xtctx 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/dist/src/api/routes/config.d.ts +4 -0
- package/dist/src/api/routes/config.d.ts.map +1 -0
- package/dist/src/api/routes/config.js +61 -0
- package/dist/src/api/routes/config.js.map +1 -0
- package/dist/src/api/routes/knowledge.d.ts +4 -0
- package/dist/src/api/routes/knowledge.d.ts.map +1 -0
- package/dist/src/api/routes/knowledge.js +40 -0
- package/dist/src/api/routes/knowledge.js.map +1 -0
- package/dist/src/api/routes/search.d.ts +7 -0
- package/dist/src/api/routes/search.d.ts.map +1 -0
- package/dist/src/api/routes/search.js +108 -0
- package/dist/src/api/routes/search.js.map +1 -0
- package/dist/src/api/routes/sources.d.ts +12 -0
- package/dist/src/api/routes/sources.d.ts.map +1 -0
- package/dist/src/api/routes/sources.js +70 -0
- package/dist/src/api/routes/sources.js.map +1 -0
- package/dist/src/api/server.d.ts +26 -0
- package/dist/src/api/server.d.ts.map +1 -0
- package/dist/src/api/server.js +312 -0
- package/dist/src/api/server.js.map +1 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +58 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/ingest.d.ts +6 -0
- package/dist/src/cli/ingest.d.ts.map +1 -0
- package/dist/src/cli/ingest.js +11 -0
- package/dist/src/cli/ingest.js.map +1 -0
- package/dist/src/cli/init.d.ts +6 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +105 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/serve.d.ts +7 -0
- package/dist/src/cli/serve.d.ts.map +1 -0
- package/dist/src/cli/serve.js +100 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/sync.d.ts +5 -0
- package/dist/src/cli/sync.d.ts.map +1 -0
- package/dist/src/cli/sync.js +13 -0
- package/dist/src/cli/sync.js.map +1 -0
- package/dist/src/compaction/pipeline.d.ts +19 -0
- package/dist/src/compaction/pipeline.d.ts.map +1 -0
- package/dist/src/compaction/pipeline.js +37 -0
- package/dist/src/compaction/pipeline.js.map +1 -0
- package/dist/src/compaction/providers/cli.d.ts +13 -0
- package/dist/src/compaction/providers/cli.d.ts.map +1 -0
- package/dist/src/compaction/providers/cli.js +60 -0
- package/dist/src/compaction/providers/cli.js.map +1 -0
- package/dist/src/compaction/rule-based.d.ts +8 -0
- package/dist/src/compaction/rule-based.d.ts.map +1 -0
- package/dist/src/compaction/rule-based.js +120 -0
- package/dist/src/compaction/rule-based.js.map +1 -0
- package/dist/src/config/loader.d.ts +12 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +76 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/mappings/claude-code.d.ts +3 -0
- package/dist/src/config/mappings/claude-code.d.ts.map +1 -0
- package/dist/src/config/mappings/claude-code.js +31 -0
- package/dist/src/config/mappings/claude-code.js.map +1 -0
- package/dist/src/config/mappings/codex.d.ts +3 -0
- package/dist/src/config/mappings/codex.d.ts.map +1 -0
- package/dist/src/config/mappings/codex.js +31 -0
- package/dist/src/config/mappings/codex.js.map +1 -0
- package/dist/src/config/mappings/copilot.d.ts +3 -0
- package/dist/src/config/mappings/copilot.d.ts.map +1 -0
- package/dist/src/config/mappings/copilot.js +33 -0
- package/dist/src/config/mappings/copilot.js.map +1 -0
- package/dist/src/config/mappings/cursor.d.ts +3 -0
- package/dist/src/config/mappings/cursor.d.ts.map +1 -0
- package/dist/src/config/mappings/cursor.js +29 -0
- package/dist/src/config/mappings/cursor.js.map +1 -0
- package/dist/src/config/sync.d.ts +13 -0
- package/dist/src/config/sync.d.ts.map +1 -0
- package/dist/src/config/sync.js +96 -0
- package/dist/src/config/sync.js.map +1 -0
- package/dist/src/ingestion/coordinator.d.ts +37 -0
- package/dist/src/ingestion/coordinator.d.ts.map +1 -0
- package/dist/src/ingestion/coordinator.js +127 -0
- package/dist/src/ingestion/coordinator.js.map +1 -0
- package/dist/src/ingestion/daemon.d.ts +17 -0
- package/dist/src/ingestion/daemon.d.ts.map +1 -0
- package/dist/src/ingestion/daemon.js +35 -0
- package/dist/src/ingestion/daemon.js.map +1 -0
- package/dist/src/ingestion/watcher.d.ts +19 -0
- package/dist/src/ingestion/watcher.d.ts.map +1 -0
- package/dist/src/ingestion/watcher.js +70 -0
- package/dist/src/ingestion/watcher.js.map +1 -0
- package/dist/src/knowledge/autotag.d.ts +3 -0
- package/dist/src/knowledge/autotag.d.ts.map +1 -0
- package/dist/src/knowledge/autotag.js +24 -0
- package/dist/src/knowledge/autotag.js.map +1 -0
- package/dist/src/knowledge/dedup.d.ts +7 -0
- package/dist/src/knowledge/dedup.d.ts.map +1 -0
- package/dist/src/knowledge/dedup.js +15 -0
- package/dist/src/knowledge/dedup.js.map +1 -0
- package/dist/src/knowledge/repository.d.ts +12 -0
- package/dist/src/knowledge/repository.d.ts.map +1 -0
- package/dist/src/knowledge/repository.js +74 -0
- package/dist/src/knowledge/repository.js.map +1 -0
- package/dist/src/mcp/server.d.ts +31 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +323 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/config.d.ts +36 -0
- package/dist/src/mcp/tools/config.d.ts.map +1 -0
- package/dist/src/mcp/tools/config.js +54 -0
- package/dist/src/mcp/tools/config.js.map +1 -0
- package/dist/src/mcp/tools/knowledge.d.ts +17 -0
- package/dist/src/mcp/tools/knowledge.d.ts.map +1 -0
- package/dist/src/mcp/tools/knowledge.js +46 -0
- package/dist/src/mcp/tools/knowledge.js.map +1 -0
- package/dist/src/mcp/tools/search.d.ts +22 -0
- package/dist/src/mcp/tools/search.d.ts.map +1 -0
- package/dist/src/mcp/tools/search.js +30 -0
- package/dist/src/mcp/tools/search.js.map +1 -0
- package/dist/src/mcp/tools/sessions.d.ts +38 -0
- package/dist/src/mcp/tools/sessions.d.ts.map +1 -0
- package/dist/src/mcp/tools/sessions.js +55 -0
- package/dist/src/mcp/tools/sessions.js.map +1 -0
- package/dist/src/mcp/tools/write.d.ts +38 -0
- package/dist/src/mcp/tools/write.d.ts.map +1 -0
- package/dist/src/mcp/tools/write.js +97 -0
- package/dist/src/mcp/tools/write.js.map +1 -0
- package/dist/src/runtime/ingestion.d.ts +17 -0
- package/dist/src/runtime/ingestion.d.ts.map +1 -0
- package/dist/src/runtime/ingestion.js +106 -0
- package/dist/src/runtime/ingestion.js.map +1 -0
- package/dist/src/runtime/services.d.ts +46 -0
- package/dist/src/runtime/services.d.ts.map +1 -0
- package/dist/src/runtime/services.js +227 -0
- package/dist/src/runtime/services.js.map +1 -0
- package/dist/src/scrapers/base.d.ts +10 -0
- package/dist/src/scrapers/base.d.ts.map +1 -0
- package/dist/src/scrapers/base.js +34 -0
- package/dist/src/scrapers/base.js.map +1 -0
- package/dist/src/scrapers/claude-code.d.ts +16 -0
- package/dist/src/scrapers/claude-code.d.ts.map +1 -0
- package/dist/src/scrapers/claude-code.js +120 -0
- package/dist/src/scrapers/claude-code.js.map +1 -0
- package/dist/src/scrapers/codex.d.ts +17 -0
- package/dist/src/scrapers/codex.d.ts.map +1 -0
- package/dist/src/scrapers/codex.js +179 -0
- package/dist/src/scrapers/codex.js.map +1 -0
- package/dist/src/scrapers/copilot.d.ts +17 -0
- package/dist/src/scrapers/copilot.d.ts.map +1 -0
- package/dist/src/scrapers/copilot.js +198 -0
- package/dist/src/scrapers/copilot.js.map +1 -0
- package/dist/src/scrapers/cursor.d.ts +18 -0
- package/dist/src/scrapers/cursor.d.ts.map +1 -0
- package/dist/src/scrapers/cursor.js +218 -0
- package/dist/src/scrapers/cursor.js.map +1 -0
- package/dist/src/scrapers/gemini.d.ts +17 -0
- package/dist/src/scrapers/gemini.d.ts.map +1 -0
- package/dist/src/scrapers/gemini.js +245 -0
- package/dist/src/scrapers/gemini.js.map +1 -0
- package/dist/src/scrapers/registry.d.ts +9 -0
- package/dist/src/scrapers/registry.d.ts.map +1 -0
- package/dist/src/scrapers/registry.js +22 -0
- package/dist/src/scrapers/registry.js.map +1 -0
- package/dist/src/store/embeddings.d.ts +9 -0
- package/dist/src/store/embeddings.d.ts.map +1 -0
- package/dist/src/store/embeddings.js +28 -0
- package/dist/src/store/embeddings.js.map +1 -0
- package/dist/src/store/lance.d.ts +25 -0
- package/dist/src/store/lance.d.ts.map +1 -0
- package/dist/src/store/lance.js +74 -0
- package/dist/src/store/lance.js.map +1 -0
- package/dist/src/store/search.d.ts +18 -0
- package/dist/src/store/search.d.ts.map +1 -0
- package/dist/src/store/search.js +55 -0
- package/dist/src/store/search.js.map +1 -0
- package/dist/src/types/compaction.d.ts +29 -0
- package/dist/src/types/compaction.d.ts.map +1 -0
- package/dist/src/types/compaction.js +2 -0
- package/dist/src/types/compaction.js.map +1 -0
- package/dist/src/types/config.d.ts +45 -0
- package/dist/src/types/config.d.ts.map +1 -0
- package/dist/src/types/config.js +2 -0
- package/dist/src/types/config.js.map +1 -0
- package/dist/src/types/context.d.ts +33 -0
- package/dist/src/types/context.d.ts.map +1 -0
- package/dist/src/types/context.js +18 -0
- package/dist/src/types/context.js.map +1 -0
- package/dist/src/types/index.d.ts +5 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/scraper.d.ts +70 -0
- package/dist/src/types/scraper.d.ts.map +1 -0
- package/dist/src/types/scraper.js +2 -0
- package/dist/src/types/scraper.js.map +1 -0
- package/dist/src/utils/retry.d.ts +11 -0
- package/dist/src/utils/retry.d.ts.map +1 -0
- package/dist/src/utils/retry.js +46 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/dist/src/utils/shutdown.d.ts +25 -0
- package/dist/src/utils/shutdown.d.ts.map +1 -0
- package/dist/src/utils/shutdown.js +56 -0
- package/dist/src/utils/shutdown.js.map +1 -0
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/assets/index-Nuadr5ox.css +1 -0
- package/dist/web/assets/index-aA0u8eXA.js +3504 -0
- package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
- package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
- package/dist/web/assets/primeicons-Dr5RGzOO.svg +345 -0
- package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
- package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/favicon.svg +21 -0
- package/dist/web/index.html +21 -0
- package/dist/web/site.webmanifest +19 -0
- package/dist/web/vite.svg +1 -0
- package/package.json +69 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
|
|
6
|
+
This file is maintained automatically by Release Please.
|
|
7
|
+
|
|
8
|
+
## [0.3.2](https://github.com/fstubner/xtctx/compare/xtctx-v0.3.1...xtctx-v0.3.2) (2026-02-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* add root AGENTS guide for repository automation ([1439030](https://github.com/fstubner/xtctx/commit/1439030dfc458cf88f9a7e3abf7f925d678aa1ab))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Miscellaneous
|
|
17
|
+
|
|
18
|
+
* deploy landing site to github pages ([6ddecd1](https://github.com/fstubner/xtctx/commit/6ddecd1828e6f3e2ba60200ba8a4dc659e629e73))
|
|
19
|
+
* deploy landing site to GitHub Pages ([4fbbfdc](https://github.com/fstubner/xtctx/commit/4fbbfdc1ffadd615e444ab865afa8196ee2f6466))
|
|
20
|
+
|
|
21
|
+
## [0.3.1](https://github.com/fstubner/xtctx/compare/xtctx-v0.3.0...xtctx-v0.3.1) (2026-02-26)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Miscellaneous
|
|
25
|
+
|
|
26
|
+
* auto-enable merge for release-please prs ([8028118](https://github.com/fstubner/xtctx/commit/80281187525971817d238d8c6c93e67309e01d69))
|
|
27
|
+
* auto-enable merge for release-please PRs ([7c8535f](https://github.com/fstubner/xtctx/commit/7c8535ff7858111fa570bc528c457e29e99c5098))
|
|
28
|
+
|
|
29
|
+
## [0.3.0](https://github.com/fstubner/xtctx/compare/xtctx-v0.2.0...xtctx-v0.3.0) (2026-02-26)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
|
|
34
|
+
* make API security config file first and refresh README ([bac9c3b](https://github.com/fstubner/xtctx/commit/bac9c3bfa44e0b0c4a080f2fcf42550c1e21ef29))
|
|
35
|
+
* make API security config file first and refresh README ([8625dba](https://github.com/fstubner/xtctx/commit/8625dba6383cd416ae6d45790bfe67e5eac0eaad))
|
|
36
|
+
|
|
37
|
+
## [0.2.0](https://github.com/fstubner/xtctx/compare/xtctx-v0.1.0...xtctx-v0.2.0) (2026-02-25)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Features
|
|
41
|
+
|
|
42
|
+
* add api security hardening and owasp checklist ([5660c52](https://github.com/fstubner/xtctx/commit/5660c52591cfcacd1611cfa914e31435d2747c80))
|
|
43
|
+
* add API server and Phase 9 Vue web UI ([775543e](https://github.com/fstubner/xtctx/commit/775543e93720c24786abbeb3089e0393eeffa904))
|
|
44
|
+
* add auto-tagging for file references and domain classification ([fc27a52](https://github.com/fstubner/xtctx/commit/fc27a52de6edad9cd1b29dd3ee6685d9eb3db4b5))
|
|
45
|
+
* add Claude Code conversation scraper ([ba01b58](https://github.com/fstubner/xtctx/commit/ba01b583bfe440e6c274024961dd3472908a923c))
|
|
46
|
+
* add CLI compaction provider ([e5f38f8](https://github.com/fstubner/xtctx/commit/e5f38f87a51d5352bf46e741694fa7a70933d8dd))
|
|
47
|
+
* add CLI entrypoints and MCP serve command ([866052c](https://github.com/fstubner/xtctx/commit/866052c79c5e2d85aa7f391dcb260e83cd765e5b))
|
|
48
|
+
* add core type definitions ([c8dba56](https://github.com/fstubner/xtctx/commit/c8dba5614b83c7521638da67d7c0e664fb4434d1))
|
|
49
|
+
* add deduplication with similarity-based supercession ([7023c92](https://github.com/fstubner/xtctx/commit/7023c920fa8b294e5cc9500a456748ecc5cc06bf))
|
|
50
|
+
* add hybrid search with reciprocal rank fusion ([830dbe7](https://github.com/fstubner/xtctx/commit/830dbe78ea30dd3c5679e1b9c998150861d57a93))
|
|
51
|
+
* add ingestion coordinator, watcher, and daemon ([92a4553](https://github.com/fstubner/xtctx/commit/92a455311683f28b1a4cbdfbef6a8662cbb04faa))
|
|
52
|
+
* add knowledge repository with YAML file persistence ([52c2e5c](https://github.com/fstubner/xtctx/commit/52c2e5cbf8100364a17036daad9e24dd090a38b6))
|
|
53
|
+
* add LanceDB vector store wrapper ([4f10d46](https://github.com/fstubner/xtctx/commit/4f10d463c48dc916fe5ad8ca1eb2bea73c736927))
|
|
54
|
+
* add local embedding service using MiniLM ([dc66f51](https://github.com/fstubner/xtctx/commit/dc66f51b48200b5fb6a42c42be246edd311b7464))
|
|
55
|
+
* add MCP server scaffold with all tool definitions ([fa3554f](https://github.com/fstubner/xtctx/commit/fa3554fa5f424448eb3cab339e6a6c23707cb21e))
|
|
56
|
+
* add phase 12 landing page ([791f535](https://github.com/fstubner/xtctx/commit/791f53561146f5858ea7de458b084e9d14d6ce5b))
|
|
57
|
+
* add phase 13 multi-tool scrapers ([f81c2fb](https://github.com/fstubner/xtctx/commit/f81c2fb2406a98e554ae2246cbd96ffdd4571a78))
|
|
58
|
+
* add rule-based compaction pipeline ([7e5d58d](https://github.com/fstubner/xtctx/commit/7e5d58d4fd86cb5eb777021a5e6943b86c07cee8))
|
|
59
|
+
* add scraper registry and state management ([9859e2d](https://github.com/fstubner/xtctx/commit/9859e2d49c1678c85e0d824cd87a372aeaae9f63))
|
|
60
|
+
* automate releases and bundle web ui in npm package ([3d6ecac](https://github.com/fstubner/xtctx/commit/3d6ecac4db0e88d594e6d5c4e17d9c245d74999b))
|
|
61
|
+
* implement MCP tool handlers ([71c6ab5](https://github.com/fstubner/xtctx/commit/71c6ab5f97dde0d0dc63759d4b7014adc0ef78f3))
|
|
62
|
+
* implement phase 10 config sync ([dc2661a](https://github.com/fstubner/xtctx/commit/dc2661a58af83f4fafaa008bf8f0b796631341da))
|
|
63
|
+
* implement phase 14 hardening and release tooling ([4590f38](https://github.com/fstubner/xtctx/commit/4590f38d26d6ad80fb4737cf721796df68fae307))
|
|
64
|
+
* wire ingestion runtime into serve and ingest commands ([89dd876](https://github.com/fstubner/xtctx/commit/89dd876cd8de12617220e7a2e1167dd4bce8c1b5))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Bug Fixes
|
|
68
|
+
|
|
69
|
+
* allow release-please prs to trigger ci ([fc00a77](https://github.com/fstubner/xtctx/commit/fc00a77145dbecee8f1a4f80fa3f60b635647738))
|
|
70
|
+
* allow release-please PRs to trigger ci ([c398b12](https://github.com/fstubner/xtctx/commit/c398b12b9273baf13136c16ee7cadca6129a0d10))
|
|
71
|
+
* normalize npm package metadata ([c6cf81f](https://github.com/fstubner/xtctx/commit/c6cf81f45c737d59629b48e850a9c13b6b97566e))
|
|
72
|
+
* normalize npm package metadata ([5553c78](https://github.com/fstubner/xtctx/commit/5553c78f9cf65d4f90d72604b2608d9d5eef40b7))
|
|
73
|
+
* run ci for release-please branches ([97e5111](https://github.com/fstubner/xtctx/commit/97e5111ee098faaaa8dc4f31338183c3b5fb610a))
|
|
74
|
+
* run ci for release-please branches ([90bf917](https://github.com/fstubner/xtctx/commit/90bf9179d05fb103e52036c55648cde422317752))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### Documentation
|
|
78
|
+
|
|
79
|
+
* add open source governance and release docs ([a9e18f5](https://github.com/fstubner/xtctx/commit/a9e18f51c1c87c2a6d6da241a37d7bf28e058ea3))
|
|
80
|
+
* add phase 14 integration hardening follow-up ([56e7a6e](https://github.com/fstubner/xtctx/commit/56e7a6ee45c0e32fc48940cb319929224a657fb2))
|
|
81
|
+
* add xtctx usage skill guide ([e247258](https://github.com/fstubner/xtctx/commit/e247258de8284297af869a22fb44f1f897001d5b))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
### Miscellaneous
|
|
85
|
+
|
|
86
|
+
* ignore codex local workspace directory ([33af3cb](https://github.com/fstubner/xtctx/commit/33af3cbb99f88a9666c0858922d7984fc18b2408))
|
|
87
|
+
* ignore runtime state directory ([3a8911d](https://github.com/fstubner/xtctx/commit/3a8911dca76787ef8f6724642a85474de566815e))
|
|
88
|
+
|
|
89
|
+
## [0.1.0] - 2026-02-25
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- Core TypeScript project scaffold, tests, and CLI entrypoint.
|
|
94
|
+
- MCP server scaffold and tool handlers:
|
|
95
|
+
- `xtctx_search`
|
|
96
|
+
- `xtctx_recent_sessions`
|
|
97
|
+
- `xtctx_session_detail`
|
|
98
|
+
- `xtctx_project_knowledge`
|
|
99
|
+
- `xtctx_save_*`
|
|
100
|
+
- `xtctx_*_config` handlers
|
|
101
|
+
- Knowledge repository with autotagging and dedup/supersession behaviors.
|
|
102
|
+
- LanceDB-backed vector store + embedding service + hybrid search.
|
|
103
|
+
- Ingestion coordinator, watcher, daemon, and CLI ingestion commands.
|
|
104
|
+
- Rule-based compaction pipeline and external CLI compaction provider.
|
|
105
|
+
- API server and Vue web UI (dashboard/search/knowledge/sources/config pages).
|
|
106
|
+
- Config sync engine generating tool-native managed sections:
|
|
107
|
+
- `.cursorrules`
|
|
108
|
+
- `CLAUDE.md`
|
|
109
|
+
- `AGENTS.md`
|
|
110
|
+
- `.github/copilot-instructions.md`
|
|
111
|
+
- Additional scraper implementations:
|
|
112
|
+
- Claude Code
|
|
113
|
+
- Cursor (SQLite)
|
|
114
|
+
- Codex CLI (JSONL)
|
|
115
|
+
- GitHub Copilot (JSON)
|
|
116
|
+
- Gemini CLI (JSON)
|
|
117
|
+
- Integration hardening:
|
|
118
|
+
- Integration tests for MCP/API/web parity paths
|
|
119
|
+
- Retry and coordinated shutdown utilities
|
|
120
|
+
- CI workflow (Node 20/22)
|
|
121
|
+
- Release verification scripts and packaging allowlist
|
|
122
|
+
- Operator/runbook docs in `README.md`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felix Stubner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# xtctx
|
|
2
|
+
|
|
3
|
+
[](https://github.com/fstubner/xtctx/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/fstubner/xtctx/actions/workflows/release-please.yml)
|
|
5
|
+
[](https://github.com/fstubner/xtctx/releases)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
xtctx is a local-first context runtime for AI coding assistants.
|
|
10
|
+
|
|
11
|
+
It lets Claude Code, Codex, Cursor, Copilot, and Gemini share the same project memory so you can switch tools without re-briefing each session.
|
|
12
|
+
|
|
13
|
+
## Core Capabilities
|
|
14
|
+
|
|
15
|
+
- Cross-tool retrieval with MCP tools (`xtctx_search`, `xtctx_project_knowledge`, etc.)
|
|
16
|
+
- Durable project knowledge in `.xtctx/knowledge`
|
|
17
|
+
- File-first config sync into tool-native instruction files
|
|
18
|
+
- One runtime process that serves:
|
|
19
|
+
- MCP server
|
|
20
|
+
- HTTP API (`/api/*`)
|
|
21
|
+
- bundled web UI (`/`)
|
|
22
|
+
|
|
23
|
+
## Knowledge Types
|
|
24
|
+
|
|
25
|
+
xtctx stores structured records as:
|
|
26
|
+
|
|
27
|
+
- `decision`
|
|
28
|
+
- `error_solution`
|
|
29
|
+
- `insight`
|
|
30
|
+
- `convention`
|
|
31
|
+
- `gotcha`
|
|
32
|
+
- `faq`
|
|
33
|
+
|
|
34
|
+
## Practical Workflow (Tool Handoff)
|
|
35
|
+
|
|
36
|
+
1. Start in your repo with `xtctx init`.
|
|
37
|
+
2. Define shared rules in `.xtctx/tool-config/shared.yaml`.
|
|
38
|
+
3. Run `xtctx sync` to generate managed sections for each tool.
|
|
39
|
+
4. Start `xtctx serve`.
|
|
40
|
+
5. At the beginning of every coding session:
|
|
41
|
+
- call `xtctx_search`
|
|
42
|
+
- call `xtctx_project_knowledge`
|
|
43
|
+
6. After finishing work, write outcomes back with:
|
|
44
|
+
- `xtctx_save_decision`
|
|
45
|
+
- `xtctx_save_error_solution`
|
|
46
|
+
- `xtctx_save_insight`
|
|
47
|
+
- `xtctx_save_faq`
|
|
48
|
+
|
|
49
|
+
This gives a repeatable loop: recall -> implement -> write back.
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
Install and build:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm ci
|
|
57
|
+
npm --prefix web ci
|
|
58
|
+
npm --prefix landing ci
|
|
59
|
+
npm run build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Initialize project context:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx xtctx init
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Sync shared config into tool-native files:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx xtctx sync
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Start runtime services:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx xtctx serve
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Open:
|
|
81
|
+
|
|
82
|
+
- Runtime web app: `http://127.0.0.1:3232/`
|
|
83
|
+
- Health: `http://127.0.0.1:3232/health`
|
|
84
|
+
- API: `http://127.0.0.1:3232/api/*`
|
|
85
|
+
|
|
86
|
+
Optional full re-index:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx xtctx ingest --full
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## MCP Client Command
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"command": "npx",
|
|
97
|
+
"args": ["xtctx", "serve", "--mcp-only", "--project", "/absolute/path/to/project"]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## File-First Configuration
|
|
102
|
+
|
|
103
|
+
Primary config source is `.xtctx/config.yaml`.
|
|
104
|
+
|
|
105
|
+
Environment variables are optional explicit overrides only.
|
|
106
|
+
|
|
107
|
+
Security-related overrides:
|
|
108
|
+
|
|
109
|
+
- `XTCTX_API_TOKEN`
|
|
110
|
+
- `XTCTX_ALLOWED_ORIGINS`
|
|
111
|
+
- `XTCTX_ALLOW_LOCALHOST_ORIGINS`
|
|
112
|
+
- `XTCTX_RATE_LIMIT_WINDOW_MS`
|
|
113
|
+
- `XTCTX_RATE_LIMIT_MAX`
|
|
114
|
+
|
|
115
|
+
## UIs
|
|
116
|
+
|
|
117
|
+
- `web/`: runtime product UI served by `xtctx serve` (dark mode default with toggle)
|
|
118
|
+
- `landing/`: public landing site deployed to GitHub Pages (dark mode default with toggle)
|
|
119
|
+
|
|
120
|
+
Local previews:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run web:dev
|
|
124
|
+
npm run landing:dev
|
|
125
|
+
npm run landing:preview
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## CLI Commands
|
|
129
|
+
|
|
130
|
+
- `xtctx init [path]`
|
|
131
|
+
- `xtctx serve [--mcp-only] [--project <path>]`
|
|
132
|
+
- `xtctx ingest [--full] [--project <path>]`
|
|
133
|
+
- `xtctx sync [--project <path>]`
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
Full validation:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run verify:release
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Release automation:
|
|
144
|
+
|
|
145
|
+
- Conventional commits feed Release Please
|
|
146
|
+
- Release PR merge on `main` creates GitHub release
|
|
147
|
+
- GitHub release publication triggers npm publish via OIDC trusted publishing
|
|
148
|
+
|
|
149
|
+
See `CONTRIBUTING.md` and `SECURITY.md` for contributor and security policy details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAIL,KAAK,WAAW,EAEjB,MAAM,2BAA2B,CAAC;AAEnC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAgD7D"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { createGetConfigHandler, createListConfigsHandler, createToolPreferencesHandler, } from "../../mcp/tools/config.js";
|
|
3
|
+
export function createConfigRouter(store) {
|
|
4
|
+
const router = Router();
|
|
5
|
+
const listConfigs = createListConfigsHandler(store);
|
|
6
|
+
const getConfig = createGetConfigHandler(store);
|
|
7
|
+
const toolPreferences = createToolPreferencesHandler(store);
|
|
8
|
+
router.get("/list", async (req, res, next) => {
|
|
9
|
+
try {
|
|
10
|
+
const type = normalizeListType(req.query.type);
|
|
11
|
+
const result = await listConfigs({ type, format: "json" });
|
|
12
|
+
res.json(result);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
next(error);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
router.get("/get", async (req, res, next) => {
|
|
19
|
+
try {
|
|
20
|
+
const type = normalizeConfigType(req.query.type);
|
|
21
|
+
const name = String(req.query.name ?? "");
|
|
22
|
+
if (!type || !name) {
|
|
23
|
+
res.status(400).json({ error: "type and name are required" });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const result = await getConfig({ type, name, format: "json" });
|
|
27
|
+
res.json(result);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
next(error);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
router.get("/preferences", async (req, res, next) => {
|
|
34
|
+
try {
|
|
35
|
+
const tool = String(req.query.tool ?? "");
|
|
36
|
+
if (!tool) {
|
|
37
|
+
res.status(400).json({ error: "tool is required" });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const result = await toolPreferences({ tool, format: "json" });
|
|
41
|
+
res.json(result);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
next(error);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return router;
|
|
48
|
+
}
|
|
49
|
+
function normalizeListType(value) {
|
|
50
|
+
if (value === "skill" || value === "command" || value === "agent" || value === "all") {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
return "all";
|
|
54
|
+
}
|
|
55
|
+
function normalizeConfigType(value) {
|
|
56
|
+
if (value === "skill" || value === "command" || value === "agent") {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../src/api/routes/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/knowledge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,8BAA8B,CAAC;AAStC,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CA2BvE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { createProjectKnowledgeHandler, } from "../../mcp/tools/knowledge.js";
|
|
3
|
+
export function createKnowledgeRouter(knowledge) {
|
|
4
|
+
const router = Router();
|
|
5
|
+
const handler = createProjectKnowledgeHandler(knowledge);
|
|
6
|
+
router.get("/", async (req, res, next) => {
|
|
7
|
+
try {
|
|
8
|
+
const query = req.query;
|
|
9
|
+
const type = normalizeType(query.type);
|
|
10
|
+
const format = query.format === "markdown" ? "markdown" : "json";
|
|
11
|
+
const result = await handler({
|
|
12
|
+
type,
|
|
13
|
+
query: query.query,
|
|
14
|
+
format,
|
|
15
|
+
});
|
|
16
|
+
if (format === "markdown" && typeof result === "string") {
|
|
17
|
+
res.type("text/markdown").send(result);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
res.json(result);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
next(error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return router;
|
|
27
|
+
}
|
|
28
|
+
function normalizeType(value) {
|
|
29
|
+
if (value === "decision" ||
|
|
30
|
+
value === "error_solution" ||
|
|
31
|
+
value === "insight" ||
|
|
32
|
+
value === "convention" ||
|
|
33
|
+
value === "gotcha" ||
|
|
34
|
+
value === "faq" ||
|
|
35
|
+
value === "all") {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
return "all";
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../../../../src/api/routes/knowledge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EACL,6BAA6B,GAE9B,MAAM,8BAA8B,CAAC;AAStC,MAAM,UAAU,qBAAqB,CAAC,SAAyB;IAC7D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,6BAA6B,CAAC,SAAS,CAAC,CAAC;IAEzD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,KAAuB,CAAC;YAC1C,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;gBAC3B,IAAI;gBACJ,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,MAAM;aACP,CAAC,CAAC;YAEH,IAAI,MAAM,KAAK,UAAU,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACxD,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvC,OAAO;YACT,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IACE,KAAK,KAAK,UAAU;QACpB,KAAK,KAAK,gBAAgB;QAC1B,KAAK,KAAK,SAAS;QACnB,KAAK,KAAK,YAAY;QACtB,KAAK,KAAK,QAAQ;QAClB,KAAK,KAAK,KAAK;QACf,KAAK,KAAK,KAAK,EACf,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import type { ContextRecord } from "../../types/context.js";
|
|
3
|
+
export interface SearchRouteDependencies {
|
|
4
|
+
knowledgeRecords: () => Promise<ContextRecord[]>;
|
|
5
|
+
}
|
|
6
|
+
export declare function createSearchRouter(deps: SearchRouteDependencies): Router;
|
|
7
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAClD;AAkDD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,GAAG,MAAM,CAkCxE"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { createSearchHandler } from "../../mcp/tools/search.js";
|
|
3
|
+
class KnowledgeSearchRunner {
|
|
4
|
+
knowledgeRecords;
|
|
5
|
+
constructor(knowledgeRecords) {
|
|
6
|
+
this.knowledgeRecords = knowledgeRecords;
|
|
7
|
+
}
|
|
8
|
+
async search(_tableName, query, _mode, limit) {
|
|
9
|
+
const records = await this.knowledgeRecords();
|
|
10
|
+
const needle = query.trim().toLowerCase();
|
|
11
|
+
if (!needle) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const ranked = records
|
|
15
|
+
.map((record) => {
|
|
16
|
+
const haystack = `${record.title} ${record.body} ${record.domain_tags.join(" ")}`.toLowerCase();
|
|
17
|
+
const score = relevanceScore(needle, haystack);
|
|
18
|
+
return { record, score };
|
|
19
|
+
})
|
|
20
|
+
.filter((item) => item.score > 0)
|
|
21
|
+
.sort((a, b) => b.score - a.score)
|
|
22
|
+
.slice(0, limit);
|
|
23
|
+
return ranked.map(({ record, score }) => ({
|
|
24
|
+
id: record.id,
|
|
25
|
+
text: record.body,
|
|
26
|
+
metadata: JSON.stringify({
|
|
27
|
+
title: record.title,
|
|
28
|
+
type: record.type,
|
|
29
|
+
source_tool: record.source_tool,
|
|
30
|
+
created_at: record.created_at,
|
|
31
|
+
}),
|
|
32
|
+
score,
|
|
33
|
+
fusedScore: score,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function createSearchRouter(deps) {
|
|
38
|
+
const router = Router();
|
|
39
|
+
const runner = new KnowledgeSearchRunner(deps.knowledgeRecords);
|
|
40
|
+
const handler = createSearchHandler(runner);
|
|
41
|
+
router.get("/", async (req, res, next) => {
|
|
42
|
+
try {
|
|
43
|
+
const query = String(req.query.query ?? "");
|
|
44
|
+
const mode = normalizeMode(req.query.mode);
|
|
45
|
+
const limit = clampLimit(req.query.limit);
|
|
46
|
+
const depth = normalizeDepth(req.query.depth);
|
|
47
|
+
const format = "json";
|
|
48
|
+
const result = await handler({ query, mode, limit, depth, format });
|
|
49
|
+
res.json(result);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
next(error);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
router.post("/", async (req, res, next) => {
|
|
56
|
+
try {
|
|
57
|
+
const query = String(req.body?.query ?? "");
|
|
58
|
+
const mode = normalizeMode(req.body?.mode);
|
|
59
|
+
const limit = clampLimit(req.body?.limit);
|
|
60
|
+
const depth = normalizeDepth(req.body?.depth);
|
|
61
|
+
const format = "json";
|
|
62
|
+
const result = await handler({ query, mode, limit, depth, format });
|
|
63
|
+
res.json(result);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
next(error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return router;
|
|
70
|
+
}
|
|
71
|
+
function relevanceScore(needle, haystack) {
|
|
72
|
+
const words = needle.split(/\s+/).filter(Boolean);
|
|
73
|
+
if (words.length === 0) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
let hits = 0;
|
|
77
|
+
for (const word of words) {
|
|
78
|
+
if (haystack.includes(word)) {
|
|
79
|
+
hits += 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (hits === 0) {
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
const coverage = hits / words.length;
|
|
86
|
+
const exactBonus = haystack.includes(needle) ? 0.25 : 0;
|
|
87
|
+
return Math.min(1, coverage + exactBonus);
|
|
88
|
+
}
|
|
89
|
+
function normalizeMode(value) {
|
|
90
|
+
if (value === "semantic" || value === "keyword") {
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
return "hybrid";
|
|
94
|
+
}
|
|
95
|
+
function normalizeDepth(value) {
|
|
96
|
+
if (value === "detail" || value === "raw") {
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
return "summary";
|
|
100
|
+
}
|
|
101
|
+
function clampLimit(value) {
|
|
102
|
+
const parsed = Number(value);
|
|
103
|
+
if (!Number.isFinite(parsed)) {
|
|
104
|
+
return 10;
|
|
105
|
+
}
|
|
106
|
+
return Math.max(1, Math.min(100, Math.floor(parsed)));
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../../src/api/routes/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import type { SessionService } from "../../mcp/tools/sessions.js";
|
|
3
|
+
import type { ContextRecord } from "../../types/context.js";
|
|
4
|
+
export interface SourcesRouteDependencies {
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
knowledgeDir: string;
|
|
7
|
+
stateDir: string;
|
|
8
|
+
sessions: SessionService;
|
|
9
|
+
knowledgeRecords: () => Promise<ContextRecord[]>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createSourcesRouter(deps: SourcesRouteDependencies): Router;
|
|
12
|
+
//# sourceMappingURL=sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;IACzB,gBAAgB,EAAE,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAClD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,CAuD1E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export function createSourcesRouter(deps) {
|
|
5
|
+
const router = Router();
|
|
6
|
+
router.get("/", async (_req, res, next) => {
|
|
7
|
+
try {
|
|
8
|
+
const knowledge = await deps.knowledgeRecords();
|
|
9
|
+
const claudeProjectsDir = resolveClaudeProjectsDir();
|
|
10
|
+
const claudeDetected = await isDirectory(claudeProjectsDir);
|
|
11
|
+
const recentSessions = await deps.sessions.listRecentSessions(5);
|
|
12
|
+
res.json({
|
|
13
|
+
projectRoot: deps.projectRoot,
|
|
14
|
+
sources: [
|
|
15
|
+
{
|
|
16
|
+
name: "knowledge",
|
|
17
|
+
kind: "filesystem",
|
|
18
|
+
path: deps.knowledgeDir,
|
|
19
|
+
records: knowledge.length,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "claude-code",
|
|
23
|
+
kind: "scraper",
|
|
24
|
+
path: claudeProjectsDir,
|
|
25
|
+
detected: claudeDetected,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "scraper-state",
|
|
29
|
+
kind: "filesystem",
|
|
30
|
+
path: deps.stateDir,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
sessions: recentSessions,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
next(error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
router.get("/status", async (_req, res, next) => {
|
|
41
|
+
try {
|
|
42
|
+
const claudeProjectsDir = resolveClaudeProjectsDir();
|
|
43
|
+
const claudeDetected = await isDirectory(claudeProjectsDir);
|
|
44
|
+
const records = await deps.knowledgeRecords();
|
|
45
|
+
res.json({
|
|
46
|
+
ok: true,
|
|
47
|
+
claudeDetected,
|
|
48
|
+
knowledgeRecords: records.length,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
next(error);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return router;
|
|
56
|
+
}
|
|
57
|
+
function resolveClaudeProjectsDir() {
|
|
58
|
+
const home = process.env.USERPROFILE ?? process.env.HOME ?? "";
|
|
59
|
+
return join(home, ".claude", "projects");
|
|
60
|
+
}
|
|
61
|
+
async function isDirectory(path) {
|
|
62
|
+
try {
|
|
63
|
+
const result = await stat(path);
|
|
64
|
+
return result.isDirectory();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../../../src/api/routes/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,UAAU,mBAAmB,CAAC,IAA8B;IAChE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAChD,MAAM,iBAAiB,GAAG,wBAAwB,EAAE,CAAC;YACrD,MAAM,cAAc,GAAG,MAAM,WAAW,CAAC,iBAAiB,CAAC,CAAC;YAC5D,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;YAEjE,GAAG,CAAC,IAAI,CAAC;gBACP,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,WAAW;wBACjB,IAAI,EAAE,YAAY;wBAClB,IAAI,EAAE,IAAI,CAAC,YAAY;wBACvB,OAAO,EAAE,SAAS,CAAC,MAAM;qBAC1B;oBACD;wBACE,IAAI,EAAE,aAAa;wBACnB,IAAI,EAAE,SAAS;wBACf,IAAI,EAAE,iBAAiB;wBACvB,QAAQ,EAAE,cAAc;qBACzB;oBACD;wBACE,IAAI,EAAE,eAAe;wBACrB,IAAI,EAAE,YAAY;wBAClB,IAAI,EAAE,IAAI,CAAC,QAAQ;qBACpB;iBACF;gBACD,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9C,IAAI,CAAC;YACH,MAAM,iBAAiB,GAAG,wBAAwB,EAAE,CAAC;YACrD,MAAM,cAAc,GAAG,MAAM,WAAW,CAAC,iBAAiB,CAAC,CAAC;YAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAE9C,GAAG,CAAC,IAAI,CAAC;gBACP,EAAE,EAAE,IAAI;gBACR,cAAc;gBACd,gBAAgB,EAAE,OAAO,CAAC,MAAM;aACjC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,wBAAwB;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|