File size: 8,695 Bytes
bdf5f4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/* context_store.c — Universal pattern-triggered context injection.
 *
 *   The regex-triggered skill loading pattern generalizes: ANY context
 *   (skills, tool docs, error recovery, codebase summaries, few-shot examples)
 *   is a (trigger, text) pair. The store matches all triggers against the
 *   current input and returns matching entries in priority order.
 *
 *   This replaces static Pri0 with a dynamic lookup: instead of stuffing the
 *   system prompt with all possible instructions, only inject what matches
 *   the current request. Information capacity goes from "fits in system
 *   prompt" to "fits on disk."
 *
 *   Triggers support: exact match, substring, regex (via callback if linked
 *   with PCRE2; otherwise substring fallback), and keyword intersection.
 *
 *   Compile:
 *     cc -O3 -std=c11 -fPIC -shared context_store.c -o libcontext_store.so
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>

#define MAX_ENTRIES   128
#define MAX_TEXT_LEN  2048
#define MAX_TRIG_LEN  256
#define MATCH_EXACT   0
#define MATCH_SUBSTR  1
#define MATCH_KEYWORD 2

typedef struct {
    char id[32];
    char trigger[MAX_TRIG_LEN];
    char text[MAX_TEXT_LEN];
    int  match_type;   /* MATCH_EXACT, MATCH_SUBSTR, MATCH_KEYWORD */
    int  priority;     /* 0=highest (error recovery), 9=lowest (general knowledge) */
    int  cooldown;     /* skip this entry for N more calls after a hit */
} entry_t;

static entry_t g_store[MAX_ENTRIES];
static int     g_count = 0;
static int     g_hit_counts[MAX_ENTRIES]; /* for cooldown tracking */

/* ---- Public API ---- */

/* Add or update an entry. Returns index or -1 if store full. */
int store_add(const char *id, const char *trigger, const char *text,
              int match_type, int priority) {
    /* Update existing */
    for (int i = 0; i < g_count; i++) {
        if (strcmp(g_store[i].id, id) == 0) {
            strncpy(g_store[i].trigger, trigger, MAX_TRIG_LEN-1);
            strncpy(g_store[i].text, text, MAX_TEXT_LEN-1);
            g_store[i].match_type = match_type;
            g_store[i].priority = priority;
            return i;
        }
    }
    if (g_count >= MAX_ENTRIES) return -1;
    int i = g_count++;
    strncpy(g_store[i].id, id, 31);
    strncpy(g_store[i].trigger, trigger, MAX_TRIG_LEN-1);
    strncpy(g_store[i].text, text, MAX_TEXT_LEN-1);
    g_store[i].match_type = match_type;
    g_store[i].priority = priority;
    g_store[i].cooldown = 0;
    g_hit_counts[i] = 0;
    return i;
}

void store_remove(const char *id) {
    for (int i = 0; i < g_count; i++) {
        if (strcmp(g_store[i].id, id) == 0) {
            /* Shift remaining entries down */
            memmove(&g_store[i], &g_store[i+1], (g_count - i - 1) * sizeof(entry_t));
            memmove(&g_hit_counts[i], &g_hit_counts[i+1], (g_count - i - 1) * sizeof(int));
            g_count--;
            return;
        }
    }
}

/* Case-insensitive substring match */
static int ci_substr(const char *haystack, const char *needle) {
    if (!*needle) return 1;
    while (*haystack) {
        const char *h = haystack, *n = needle;
        while (*h && *n && tolower(*h) == tolower(*n)) { h++; n++; }
        if (!*n) return 1;
        haystack++;
    }
    return 0;
}

/* Check if a word from needle appears in haystack */
static int keyword_match(const char *haystack, const char *trigger) {
    char word[64];
    const char *p = trigger;
    while (*p) {
        while (*p == ' ' || *p == ',') p++;
        if (!*p) break;
        int len = 0;
        while (p[len] && p[len] != ' ' && p[len] != ',' && len < 63) len++;
        memcpy(word, p, len); word[len] = '\0';
        p += len;
        /* Check if this word appears in haystack */
        const char *h = haystack;
        while (*h) {
            while (*h == ' ') h++;
            const char *w = word;
            const char *s = h;
            while (*s && *w && tolower(*s) == tolower(*w)) { s++; w++; }
            if (!*w && (!*s || *s == ' ' || *s == '\n')) return 1;
            while (*h && *h != ' ') h++;
        }
    }
    return 0;
}

/* Match an entry against input. Returns 1 if match. */
static int entry_matches(const entry_t *e, const char *input) {
    switch (e->match_type) {
        case MATCH_EXACT:   return strcmp(input, e->trigger) == 0;
        case MATCH_SUBSTR:  return ci_substr(input, e->trigger);
        case MATCH_KEYWORD: return keyword_match(input, e->trigger);
        default:            return 0;
    }
}

/* Query the store. Returns matching entries in priority order as a
 * concatenated string. max_tokens limits total output (est. 4 chars/tok).
 * excluded_ids is a | separated list of ids to skip (already active).
 * Copies result to buf (caller-owned, at least bufsz bytes). */
int store_query(const char *input, int max_tokens, const char *excluded_ids,
                char *buf, int bufsz) {
    int budget = max_tokens * 4;
    int total = 0;
    int included = 0;

    buf[0] = '\0';

    /* Collect matching indices, sorted by priority */
    int matches[MAX_ENTRIES];
    int nm = 0;
    for (int i = 0; i < g_count; i++) {
        if (g_store[i].cooldown > 0) { g_store[i].cooldown--; continue; }
        if (!entry_matches(&g_store[i], input)) continue;
        /* Check exclusion */
        if (excluded_ids && *excluded_ids) {
            char ex_copy[512];
            strncpy(ex_copy, excluded_ids, 511);
            char *tok = strtok(ex_copy, "|");
            int skip = 0;
            while (tok) {
                if (strcmp(tok, g_store[i].id) == 0) { skip = 1; break; }
                tok = strtok(NULL, "|");
            }
            if (skip) continue;
        }
        /* Insert sorted by priority */
        int j;
        for (j = nm; j > 0 && g_store[matches[j-1]].priority > g_store[i].priority; j--)
            matches[j] = matches[j-1];
        matches[j] = i;
        nm++;
    }

    /* Write matching entries */
    for (int m = 0; m < nm; m++) {
        int i = matches[m];
        int len = strlen(g_store[i].text);
        if (total + len + 2 > budget && included > 0) break;
        int to_write = len;
        if (total + to_write + 2 > budget) to_write = budget - total - 2;
        if (to_write <= 0) break;

        if (included > 0) { buf[total++] = '\n'; buf[total] = '\0'; }
        memcpy(buf + total, g_store[i].text, to_write);
        total += to_write;
        buf[total] = '\0';
        included++;

        /* Set cooldown to prevent re-triggering */
        g_store[i].cooldown = 3;
        g_hit_counts[i]++;
    }

    return included;
}

/* Get store stats */
int store_count(void) { return g_count; }

/* Seed the store with default entries. Call once at startup. */
void store_init_defaults(void) {
    store_add("default-terminate",
        "quit exit stop shutdown",
        "To exit: type /exit or say 'quit'.",
        MATCH_KEYWORD, 9);

    store_add("error-oom",
        "out of memory OOM killed memory error allocation failed",
        "MEMORY FULL. Free RAM by: closing unused programs, reducing batch size, or using a smaller model. Current model: Hermes 3B Q4.",
        MATCH_KEYWORD, 0);

    store_add("error-timeout",
        "timed out timeout hang frozen stuck",
        "TIMEOUT DETECTED. The last operation took too long. Try: --no-mmap for faster loading, reduce max_tokens, or simplify the request.",
        MATCH_KEYWORD, 0);

    store_add("error-syntax",
        "syntax error compilation error parse error unexpected token",
        "SYNTAX ERROR. Check: missing semicolons, unmatched braces, wrong variable types. Compile with -Wall to see all warnings.",
        MATCH_KEYWORD, 1);

    store_add("tool-terminal",
        "terminal shell bash command cmd execute run",
        "TERMINAL: use terminal('command') tool. Always quote paths with spaces. Set timeout for long operations. Check exit code before proceeding.",
        MATCH_KEYWORD, 3);

    store_add("tool-file",
        "file read write save open edit",
        "FILES: use read_file(path) and write_file(path,content). Never write outside the project directory. Check file exists before reading.",
        MATCH_KEYWORD, 3);

    store_add("tool-git",
        "git commit push pull branch merge rebase",
        "GIT: use terminal with git commands. NEVER force push to main. Create feature branches. Commit messages should be descriptive.",
        MATCH_KEYWORD, 3);

    store_add("pattern-search",
        "search find grep locate look for",
        "SEARCH: use search_files(pattern) for content, or terminal('find ...') for filenames. Be specific in patterns to reduce noise.",
        MATCH_KEYWORD, 4);
}