Commit 8a603cd
feat(search): semantic search MCP tools + tiered context mode (B-005)
Adds three new MCP tools and an opt-in tiered context-loading mode:
axme_get_memory(slug) - full body of one memory
axme_get_decision(id_or_slug) - full body of one decision
axme_search_kb(query, type?, k?) - semantic search across both
These enable agents on KBs >100 entries to navigate without loading every
body at session start. Available in both modes (full + search) so they
also serve as fuzzy-lookup tools mid-session.
## Two context modes (config.context.mode)
- full - default. Every memory and decision body is loaded into agent
context at session start. Existing behaviour, zero breakage.
- search - only a catalog (title + 1-line description, prefixed with
[type/enforce]) is loaded. Bodies fetched on demand via the
three new tools.
Switching is user-driven only - the agent never decides:
- env var: AXME_CONTEXT_MODE=tiered claude (one-off)
- CLI: axme-code config set context.mode search
- manual: edit .axme-code/config.yaml
Default config.yaml now ships a commented hint explaining when to switch.
At >100 KB entries the axme_context output also adds a soft suggestion
without changing behaviour.
## Lazy install of @huggingface/transformers
The semantic-search runtime (~100MB node_modules + ~30MB MiniLM ONNX
model cached at ~/.cache/huggingface/) is NOT bundled. It's installed on
demand into ~/.local/share/axme-code/runtime/ by:
axme-code config set context.mode search
That command atomically:
1. npm install --prefix runtime @huggingface/transformers@^4.0.1
2. Reindex every memory + decision into .axme-code/_index/embeddings.json
3. Write context.mode = search
If either step fails, config rolls back to full and an error is printed.
The user is never left in a half-broken state.
`axme-code reindex` is also exposed for manual rebuilds (e.g. after
hand-editing .md files).
## Embedding strategy
- Brute-force cosine over Float32 (no HNSW). At <=1000 vectors this is
<10ms; HNSW only matters at 10K+. Avoids native bindings entirely.
- Synchronous embed on every axme_save_memory / axme_save_decision when
search mode is active. ~50-200ms per save once the embedder is warm;
acceptable cost for "search results are immediately consistent".
- Skips silently when mode != search OR runtime missing - never blocks a
save on something the user opted out of.
## New files
- src/storage/embeddings.ts - core: cosine, topK, JSON load/save,
loadEmbedder lazy loader, mtime
staleness, embedKbEntry helper.
- src/tools/kb-search.ts - 3 MCP handlers (get_memory, get_decision,
search_kb) with friendly fallbacks.
- src/tools/search-install.ts - installTransformers + reindexAll +
runConfigSetSearch atomic flow.
- test/embeddings.test.ts - cosine math + topK ranking + JSON
round-trip + staleness + runtime detect.
- test/kb-search.test.ts - 3 handlers' fallback paths + format
round-trip.
## Modified files
- src/types.ts - ContextMode type + ProjectConfig.contextMode
("full" default).
- src/storage/config.ts - nested context.mode parse/format.
- src/server.ts - 3 new tool registrations; saveMemory/saveDecision
handlers now await embedKbEntry post-save.
- src/tools/context.ts - branch on contextMode: full mode unchanged;
search mode emits catalog (title+desc+labels)
plus MUST instructions and soft KB-size hint.
- src/cli.ts - `config get/set <key> [<value>]` and `reindex`
subcommands.
## Verification
- npm test 536/536 pass (was 511; +25 new tests for embeddings + kb-search)
- npx tsc --noEmit clean
- npm run build clean
Phase 1 (multi-client docs) is open separately as PR #123 and will be
merged together after combined E2E.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 20e3929 commit 8a603cd
10 files changed
Lines changed: 1226 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
796 | 796 | | |
797 | 797 | | |
798 | 798 | | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
799 | 882 | | |
800 | 883 | | |
801 | 884 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
25 | 28 | | |
26 | 29 | | |
27 | 30 | | |
| |||
432 | 435 | | |
433 | 436 | | |
434 | 437 | | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
435 | 442 | | |
436 | 443 | | |
437 | 444 | | |
| |||
452 | 459 | | |
453 | 460 | | |
454 | 461 | | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
455 | 465 | | |
456 | 466 | | |
457 | 467 | | |
| |||
485 | 495 | | |
486 | 496 | | |
487 | 497 | | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
488 | 540 | | |
489 | 541 | | |
490 | 542 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
52 | 60 | | |
53 | 61 | | |
54 | 62 | | |
55 | 63 | | |
56 | 64 | | |
57 | 65 | | |
58 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
59 | 73 | | |
60 | 74 | | |
61 | 75 | | |
| |||
71 | 85 | | |
72 | 86 | | |
73 | 87 | | |
| 88 | + | |
74 | 89 | | |
75 | 90 | | |
0 commit comments