/* 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 #include #include #include #include #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); }