@theglitchking/semantic-memory 1.6.0 → 1.7.0

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.
Files changed (149) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +90 -0
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/README.md +49 -29
  6. package/THIRD_PARTY_NOTICES.md +17 -0
  7. package/commands/jev-mode.md +18 -0
  8. package/commands/skill-loop.md +23 -0
  9. package/dist/{chunk-7PNF3AP2.js → chunk-34VN47IW.js} +2 -2
  10. package/dist/chunk-3FPUBAEW.js +113 -0
  11. package/dist/chunk-3FPUBAEW.js.map +1 -0
  12. package/dist/{chunk-WZEKIUYG.js → chunk-6TIKWOY4.js} +10 -8
  13. package/dist/{chunk-WZEKIUYG.js.map → chunk-6TIKWOY4.js.map} +1 -1
  14. package/dist/{chunk-NBY4TRP7.js → chunk-7M34JDTH.js} +3 -3
  15. package/dist/{chunk-FTRXH2WC.js → chunk-7UAVDOI6.js} +15 -12
  16. package/dist/chunk-7UAVDOI6.js.map +1 -0
  17. package/dist/{chunk-BBP3DG2U.js → chunk-AIXQFTSB.js} +25 -26
  18. package/dist/{chunk-BBP3DG2U.js.map → chunk-AIXQFTSB.js.map} +1 -1
  19. package/dist/chunk-BEACL25M.js +224 -0
  20. package/dist/chunk-BEACL25M.js.map +1 -0
  21. package/dist/chunk-CATPGINC.js +186 -0
  22. package/dist/chunk-CATPGINC.js.map +1 -0
  23. package/dist/{chunk-IGHCVAAL.js → chunk-CJUY55GG.js} +6 -3
  24. package/dist/{chunk-IGHCVAAL.js.map → chunk-CJUY55GG.js.map} +1 -1
  25. package/dist/chunk-DBFD3MTT.js +130 -0
  26. package/dist/chunk-DBFD3MTT.js.map +1 -0
  27. package/dist/{chunk-XAXK7NM4.js → chunk-EXAUVIGW.js} +9 -7
  28. package/dist/{chunk-XAXK7NM4.js.map → chunk-EXAUVIGW.js.map} +1 -1
  29. package/dist/{chunk-LGQPP5FQ.js → chunk-EYVFC745.js} +2 -2
  30. package/dist/chunk-GYR7U6KA.js +80 -0
  31. package/dist/chunk-GYR7U6KA.js.map +1 -0
  32. package/dist/{chunk-OEF5U5GV.js → chunk-H2BES6FU.js} +10 -2
  33. package/dist/{chunk-OEF5U5GV.js.map → chunk-H2BES6FU.js.map} +1 -1
  34. package/dist/chunk-IEZVFKUT.js +157 -0
  35. package/dist/chunk-IEZVFKUT.js.map +1 -0
  36. package/dist/chunk-J7LYDRML.js +9 -0
  37. package/dist/chunk-J7LYDRML.js.map +1 -0
  38. package/dist/chunk-MQS7JEBD.js +137 -0
  39. package/dist/chunk-MQS7JEBD.js.map +1 -0
  40. package/dist/chunk-NCGM36NX.js +118 -0
  41. package/dist/chunk-NCGM36NX.js.map +1 -0
  42. package/dist/chunk-NIZLBNH4.js +165 -0
  43. package/dist/chunk-NIZLBNH4.js.map +1 -0
  44. package/dist/chunk-OT3MV7XF.js +123 -0
  45. package/dist/chunk-OT3MV7XF.js.map +1 -0
  46. package/dist/chunk-P3GLY42T.js +291 -0
  47. package/dist/chunk-P3GLY42T.js.map +1 -0
  48. package/dist/chunk-PJXYCCI2.js +177 -0
  49. package/dist/chunk-PJXYCCI2.js.map +1 -0
  50. package/dist/chunk-R6GNP7DS.js +87 -0
  51. package/dist/chunk-R6GNP7DS.js.map +1 -0
  52. package/dist/{chunk-44BQDRXD.js → chunk-SHSOCFJH.js} +4 -1
  53. package/dist/chunk-SHSOCFJH.js.map +1 -0
  54. package/dist/chunk-TKHWVISV.js +71 -0
  55. package/dist/chunk-TKHWVISV.js.map +1 -0
  56. package/dist/{chunk-CAX46XLY.js → chunk-VBLCUWT4.js} +23 -12
  57. package/dist/chunk-VBLCUWT4.js.map +1 -0
  58. package/dist/chunk-VQCNWRTL.js +121 -0
  59. package/dist/chunk-VQCNWRTL.js.map +1 -0
  60. package/dist/{chunk-SX427M2R.js → chunk-XKGK643R.js} +2 -2
  61. package/dist/{chunk-FURCIJBO.js → chunk-Y7H76Q7F.js} +6 -3
  62. package/dist/chunk-Y7H76Q7F.js.map +1 -0
  63. package/dist/chunk-ZSWI3Y4Y.js +232 -0
  64. package/dist/chunk-ZSWI3Y4Y.js.map +1 -0
  65. package/dist/cli/index.js +389 -60
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/core/features.d.ts +38 -0
  68. package/dist/core/features.js +28 -0
  69. package/dist/core/index.d.ts +285 -2
  70. package/dist/core/index.js +170 -20
  71. package/dist/core/index.js.map +1 -1
  72. package/dist/core/jev.d.ts +35 -0
  73. package/dist/core/jev.js +20 -0
  74. package/dist/core/redact.js +1 -1
  75. package/dist/core/skill-curator.d.ts +1 -0
  76. package/dist/core/skill-curator.js +41 -0
  77. package/dist/core/skill-evidence.d.ts +25 -0
  78. package/dist/core/skill-evidence.js +23 -0
  79. package/dist/core/skill-queue.d.ts +40 -0
  80. package/dist/core/skill-queue.js +38 -0
  81. package/dist/core/skill-review.d.ts +3 -0
  82. package/dist/core/skill-review.js +29 -0
  83. package/dist/core/skill-worker.d.ts +21 -0
  84. package/dist/core/skill-worker.js +70 -0
  85. package/dist/core/skill-worker.js.map +1 -0
  86. package/dist/{dossier-RWTIMGZN.js → dossier-RXBLQOXC.js} +5 -4
  87. package/dist/failure-eval-IUJO47UC.js +18 -0
  88. package/dist/healthcheck-S7D6YNTA.js +21 -0
  89. package/dist/indexer-RZPH2PVL.js +9 -0
  90. package/dist/{lexicon-IVNJTMWE.js → lexicon-SEJSLBHO.js} +6 -5
  91. package/dist/lexicon-SEJSLBHO.js.map +1 -0
  92. package/dist/lint-3JNLRM5U.js +18 -0
  93. package/dist/lint-3JNLRM5U.js.map +1 -0
  94. package/dist/log-RM2NDNPH.js +11 -0
  95. package/dist/log-RM2NDNPH.js.map +1 -0
  96. package/dist/matter-SNJ2WDL6.js +8 -0
  97. package/dist/matter-SNJ2WDL6.js.map +1 -0
  98. package/dist/mcp/server.js +43 -38
  99. package/dist/mcp/server.js.map +1 -1
  100. package/dist/{profile-UE4YVTDC.js → profile-SNULTJ27.js} +6 -5
  101. package/dist/profile-SNULTJ27.js.map +1 -0
  102. package/dist/{schema-LY6533NJ.js → schema-RDYUK6CC.js} +3 -2
  103. package/dist/schema-RDYUK6CC.js.map +1 -0
  104. package/dist/{search-MZC4DC7H.js → search-325ULY57.js} +4 -3
  105. package/dist/{search-MZC4DC7H.js.map → search-325ULY57.js.map} +1 -1
  106. package/dist/skill-curator-D7HHZaUI.d.ts +71 -0
  107. package/dist/skill-land-XU2APPBJ.js +31 -0
  108. package/dist/skill-land-XU2APPBJ.js.map +1 -0
  109. package/dist/skill-ledger-AYXVNFX7.js +18 -0
  110. package/dist/skill-ledger-AYXVNFX7.js.map +1 -0
  111. package/dist/skill-merge-GXBQLBZA.js +24 -0
  112. package/dist/skill-merge-GXBQLBZA.js.map +1 -0
  113. package/dist/skill-review-BaW5n0Zg.d.ts +179 -0
  114. package/dist/skill-schema-RVB3EHEF.js +30 -0
  115. package/dist/skill-schema-RVB3EHEF.js.map +1 -0
  116. package/dist/{telemetry-B4KP3UVE.js → telemetry-YAG3VA4Y.js} +4 -4
  117. package/dist/telemetry-YAG3VA4Y.js.map +1 -0
  118. package/docs/regression-snapshot.md +5 -5
  119. package/hooks/hooks.json +26 -1
  120. package/hooks/jev-bridge.py +109 -0
  121. package/hooks/session-start.js +5 -0
  122. package/hooks/skill-loop.js +65 -0
  123. package/hooks/vault-context.js +104 -6
  124. package/package.json +4 -2
  125. package/dist/chunk-44BQDRXD.js.map +0 -1
  126. package/dist/chunk-5F5ZIAW2.js +0 -79
  127. package/dist/chunk-5F5ZIAW2.js.map +0 -1
  128. package/dist/chunk-CAX46XLY.js.map +0 -1
  129. package/dist/chunk-FTRXH2WC.js.map +0 -1
  130. package/dist/chunk-FURCIJBO.js.map +0 -1
  131. package/dist/failure-eval-37JRNTIW.js +0 -10
  132. package/dist/healthcheck-3FSNL2U7.js +0 -20
  133. package/dist/indexer-RAKJ7JR3.js +0 -8
  134. package/dist/lint-36WTW762.js +0 -17
  135. package/dist/log-YIXF6CYM.js +0 -11
  136. /package/dist/{chunk-7PNF3AP2.js.map → chunk-34VN47IW.js.map} +0 -0
  137. /package/dist/{chunk-NBY4TRP7.js.map → chunk-7M34JDTH.js.map} +0 -0
  138. /package/dist/{chunk-LGQPP5FQ.js.map → chunk-EYVFC745.js.map} +0 -0
  139. /package/dist/{chunk-SX427M2R.js.map → chunk-XKGK643R.js.map} +0 -0
  140. /package/dist/{dossier-RWTIMGZN.js.map → core/features.js.map} +0 -0
  141. /package/dist/{failure-eval-37JRNTIW.js.map → core/jev.js.map} +0 -0
  142. /package/dist/{healthcheck-3FSNL2U7.js.map → core/skill-curator.js.map} +0 -0
  143. /package/dist/{indexer-RAKJ7JR3.js.map → core/skill-evidence.js.map} +0 -0
  144. /package/dist/{lexicon-IVNJTMWE.js.map → core/skill-queue.js.map} +0 -0
  145. /package/dist/{lint-36WTW762.js.map → core/skill-review.js.map} +0 -0
  146. /package/dist/{log-YIXF6CYM.js.map → dossier-RXBLQOXC.js.map} +0 -0
  147. /package/dist/{profile-UE4YVTDC.js.map → failure-eval-IUJO47UC.js.map} +0 -0
  148. /package/dist/{schema-LY6533NJ.js.map → healthcheck-S7D6YNTA.js.map} +0 -0
  149. /package/dist/{telemetry-B4KP3UVE.js.map → indexer-RZPH2PVL.js.map} +0 -0
@@ -5,10 +5,11 @@ import {
5
5
  readSpeakerProfile,
6
6
  speakerProfileTemplate,
7
7
  updateProfileSection
8
- } from "./chunk-WZEKIUYG.js";
9
- import "./chunk-XAXK7NM4.js";
10
- import "./chunk-LGQPP5FQ.js";
11
- import "./chunk-OEF5U5GV.js";
8
+ } from "./chunk-6TIKWOY4.js";
9
+ import "./chunk-EXAUVIGW.js";
10
+ import "./chunk-EYVFC745.js";
11
+ import "./chunk-J7LYDRML.js";
12
+ import "./chunk-H2BES6FU.js";
12
13
  import "./chunk-PZ5AY32C.js";
13
14
  export {
14
15
  PROFILE_SECTIONS,
@@ -18,4 +19,4 @@ export {
18
19
  speakerProfileTemplate,
19
20
  updateProfileSection
20
21
  };
21
- //# sourceMappingURL=profile-UE4YVTDC.js.map
22
+ //# sourceMappingURL=profile-SNULTJ27.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -2,11 +2,12 @@ import {
2
2
  installDefaultSchema,
3
3
  loadSchema,
4
4
  validateNote
5
- } from "./chunk-FURCIJBO.js";
5
+ } from "./chunk-Y7H76Q7F.js";
6
+ import "./chunk-J7LYDRML.js";
6
7
  import "./chunk-PZ5AY32C.js";
7
8
  export {
8
9
  installDefaultSchema,
9
10
  loadSchema,
10
11
  validateNote
11
12
  };
12
- //# sourceMappingURL=schema-LY6533NJ.js.map
13
+ //# sourceMappingURL=schema-RDYUK6CC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -3,10 +3,11 @@ import {
3
3
  GraphBuilder,
4
4
  TextSearch,
5
5
  VectorIndex
6
- } from "./chunk-44BQDRXD.js";
6
+ } from "./chunk-SHSOCFJH.js";
7
7
  import {
8
8
  Indexer
9
- } from "./chunk-IGHCVAAL.js";
9
+ } from "./chunk-CJUY55GG.js";
10
+ import "./chunk-J7LYDRML.js";
10
11
  import "./chunk-PZ5AY32C.js";
11
12
 
12
13
  // src/cli/search.ts
@@ -210,4 +211,4 @@ export {
210
211
  openSearch,
211
212
  runSearch
212
213
  };
213
- //# sourceMappingURL=search-MZC4DC7H.js.map
214
+ //# sourceMappingURL=search-325ULY57.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/search.ts"],"sourcesContent":["import { join, resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { Indexer } from \"../core/indexer.js\";\nimport { Embedder } from \"../core/embedder.js\";\nimport { GraphBuilder } from \"../core/graph.js\";\nimport { VectorIndex } from \"../core/vector.js\";\nimport { TextSearch } from \"../core/search-text.js\";\nimport type { IndexedDocument } from \"../core/types.js\";\n\nexport interface SearchCliOptions {\n notes: string;\n query: string;\n limit?: number;\n textOnly?: boolean;\n json?: boolean;\n}\n\nexport interface SearchHit {\n path: string;\n title?: string;\n score: number;\n snippet?: string;\n mtime?: string;\n}\n\nexport async function runSearch(opts: SearchCliOptions): Promise<SearchHit[]> {\n const searcher = await openSearch(opts.notes, { textOnly: opts.textOnly });\n return searcher.search(opts.query, opts.limit ?? 8);\n}\n\nexport interface Searcher {\n search(query: string, limit?: number): Promise<SearchHit[]>;\n}\n\n/**\n * Load a notes folder once and search it many times — the same scoring the hook's CLI search uses.\n * The hook spawns `search` per prompt; the failure eval reuses one Searcher per corpus.\n */\nexport async function openSearch(notes: string, opts: { textOnly?: boolean } = {}): Promise<Searcher> {\n const notesPath = resolve(notes);\n if (!existsSync(notesPath)) {\n throw new Error(`notes directory not found: ${notesPath}`);\n }\n\n const indexer = new Indexer(notesPath);\n const indexPath = join(notesPath, \".semantic-sidekick-index\");\n\n // Prefer the parsed-doc cache written by reindex — avoids re-parsing 500+ files\n // on every hook call. Falls back to a full parse if the cache is missing.\n let documents = existsSync(indexPath) ? await indexer.loadDocsCache(indexPath) : null;\n if (!documents) {\n documents = await indexer.indexAll();\n }\n if (documents.length === 0) return { search: async () => [] };\n const docs = documents;\n\n if (opts.textOnly) {\n const textSearch = new TextSearch();\n textSearch.setDocuments(docs);\n return { search: async (query, limit = 8) => textHits(textSearch, docs, query, limit) };\n }\n\n // Hybrid path: load cached index, embed query, search + graph rerank.\n if (!existsSync(indexPath)) {\n throw new Error(`index not built at ${indexPath} — run: semantic-sidekick --notes ${notesPath} --reindex`);\n }\n\n const embedder = new Embedder();\n await embedder.init();\n\n const metaPath = join(indexPath, \"meta.json\");\n if (existsSync(metaPath)) {\n try {\n const meta = JSON.parse(await readFile(metaPath, \"utf-8\")) as { model?: string };\n if (meta.model && meta.model !== embedder.getModel()) {\n throw new Error(`model mismatch (cached=${meta.model}, current=${embedder.getModel()}) — run --reindex`);\n }\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"model mismatch\")) throw e;\n }\n }\n\n const vectorIndex = new VectorIndex(embedder.getDimensions());\n const loaded = await vectorIndex.load(indexPath);\n if (!loaded) {\n throw new Error(`could not load vector index at ${indexPath} — run --reindex`);\n }\n\n const graph = new GraphBuilder();\n await graph.load(indexPath);\n\n const docByPath = new Map(docs.map((d) => [d.path, d]));\n return {\n async search(query, limit = 8) {\n const queryEmbed = await embedder.embed(query);\n const semantic = vectorIndex.search(queryEmbed, limit * 3);\n const graphResults = graph.searchGraph(query, 2);\n const graphPaths = new Set(graphResults.map((r) => r.path));\n\n const hybrid = semantic\n .map((r) => {\n const doc = docByPath.get(r.path);\n let score = graphPaths.has(r.path) ? r.score * 1.3 : r.score;\n if (doc?.loadPriority !== undefined) {\n score *= 1 + (doc.loadPriority - 5) * 0.04;\n }\n return { ...r, score };\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n\n return hybrid.map((r) => {\n const doc = docByPath.get(r.path);\n return {\n path: r.path,\n title: (r as any).title ?? doc?.title,\n score: r.score,\n snippet: (r as any).snippet,\n mtime: doc?.mtime,\n };\n });\n },\n };\n}\n\nfunction textHits(textSearch: TextSearch, documents: IndexedDocument[], query: string, limit: number): SearchHit[] {\n const keywords = extractKeywords(query);\n if (keywords.length === 0) return [];\n const scoreByPath = new Map<string, { hits: number; firstSnippet?: string; title?: string; mtime: string }>();\n for (const kw of keywords) {\n const results = textSearch.search({ pattern: kw, limit: 50 });\n for (const r of results) {\n const existing = scoreByPath.get(r.path);\n const doc = documents.find((d) => d.path === r.path);\n if (existing) {\n existing.hits += 1;\n } else {\n scoreByPath.set(r.path, {\n hits: 1,\n firstSnippet: r.snippet,\n title: r.title,\n mtime: doc?.mtime ?? \"\",\n });\n }\n }\n }\n return Array.from(scoreByPath.entries())\n .map(([path, v]) => ({\n path,\n title: v.title,\n score: v.hits / keywords.length,\n snippet: v.firstSnippet,\n mtime: v.mtime,\n }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n}\n\nconst STOPWORDS = new Set([\n \"a\", \"an\", \"and\", \"are\", \"as\", \"at\", \"be\", \"by\", \"can\", \"do\", \"does\", \"for\", \"from\",\n \"how\", \"i\", \"if\", \"in\", \"is\", \"it\", \"its\", \"of\", \"on\", \"or\", \"our\", \"should\", \"that\",\n \"the\", \"this\", \"to\", \"was\", \"we\", \"what\", \"when\", \"where\", \"which\", \"why\", \"will\",\n \"with\", \"you\", \"your\", \"have\", \"has\", \"had\", \"but\", \"not\", \"so\", \"any\", \"all\",\n \"about\", \"there\", \"their\", \"they\", \"them\", \"these\", \"those\", \"would\", \"could\",\n \"might\", \"may\", \"really\", \"just\", \"like\", \"some\", \"more\", \"than\", \"into\", \"out\",\n \"up\", \"down\", \"need\", \"want\", \"get\", \"got\", \"also\", \"very\", \"much\",\n]);\n\nfunction extractKeywords(query: string): string[] {\n const tokens = query\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \" \")\n .split(/\\s+/)\n .filter((t) => t.length > 2 && !STOPWORDS.has(t));\n const seen = new Set<string>();\n const out: string[] = [];\n for (const t of tokens) {\n if (!seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n if (out.length >= 6) break;\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AAwBzB,eAAsB,UAAU,MAA8C;AAC5E,QAAM,WAAW,MAAM,WAAW,KAAK,OAAO,EAAE,UAAU,KAAK,SAAS,CAAC;AACzE,SAAO,SAAS,OAAO,KAAK,OAAO,KAAK,SAAS,CAAC;AACpD;AAUA,eAAsB,WAAW,OAAe,OAA+B,CAAC,GAAsB;AACpG,QAAM,YAAY,QAAQ,KAAK;AAC/B,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAM,IAAI,MAAM,8BAA8B,SAAS,EAAE;AAAA,EAC3D;AAEA,QAAM,UAAU,IAAI,QAAQ,SAAS;AACrC,QAAM,YAAY,KAAK,WAAW,0BAA0B;AAI5D,MAAI,YAAY,WAAW,SAAS,IAAI,MAAM,QAAQ,cAAc,SAAS,IAAI;AACjF,MAAI,CAAC,WAAW;AACd,gBAAY,MAAM,QAAQ,SAAS;AAAA,EACrC;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,QAAQ,YAAY,CAAC,EAAE;AAC5D,QAAM,OAAO;AAEb,MAAI,KAAK,UAAU;AACjB,UAAM,aAAa,IAAI,WAAW;AAClC,eAAW,aAAa,IAAI;AAC5B,WAAO,EAAE,QAAQ,OAAO,OAAO,QAAQ,MAAM,SAAS,YAAY,MAAM,OAAO,KAAK,EAAE;AAAA,EACxF;AAGA,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAM,IAAI,MAAM,sBAAsB,SAAS,0CAAqC,SAAS,YAAY;AAAA,EAC3G;AAEA,QAAM,WAAW,IAAI,SAAS;AAC9B,QAAM,SAAS,KAAK;AAEpB,QAAM,WAAW,KAAK,WAAW,WAAW;AAC5C,MAAI,WAAW,QAAQ,GAAG;AACxB,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,MAAM,SAAS,UAAU,OAAO,CAAC;AACzD,UAAI,KAAK,SAAS,KAAK,UAAU,SAAS,SAAS,GAAG;AACpD,cAAM,IAAI,MAAM,0BAA0B,KAAK,KAAK,aAAa,SAAS,SAAS,CAAC,wBAAmB;AAAA,MACzG;AAAA,IACF,SAAS,GAAG;AACV,UAAI,aAAa,SAAS,EAAE,QAAQ,WAAW,gBAAgB,EAAG,OAAM;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,YAAY,SAAS,cAAc,CAAC;AAC5D,QAAM,SAAS,MAAM,YAAY,KAAK,SAAS;AAC/C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,kCAAkC,SAAS,uBAAkB;AAAA,EAC/E;AAEA,QAAM,QAAQ,IAAI,aAAa;AAC/B,QAAM,MAAM,KAAK,SAAS;AAE1B,QAAM,YAAY,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACtD,SAAO;AAAA,IACL,MAAM,OAAO,OAAO,QAAQ,GAAG;AAC7B,YAAM,aAAa,MAAM,SAAS,MAAM,KAAK;AAC7C,YAAM,WAAW,YAAY,OAAO,YAAY,QAAQ,CAAC;AACzD,YAAM,eAAe,MAAM,YAAY,OAAO,CAAC;AAC/C,YAAM,aAAa,IAAI,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAE1D,YAAM,SAAS,SACZ,IAAI,CAAC,MAAM;AACV,cAAM,MAAM,UAAU,IAAI,EAAE,IAAI;AAChC,YAAI,QAAQ,WAAW,IAAI,EAAE,IAAI,IAAI,EAAE,QAAQ,MAAM,EAAE;AACvD,YAAI,KAAK,iBAAiB,QAAW;AACnC,mBAAS,KAAK,IAAI,eAAe,KAAK;AAAA,QACxC;AACA,eAAO,EAAE,GAAG,GAAG,MAAM;AAAA,MACvB,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,KAAK;AAEjB,aAAO,OAAO,IAAI,CAAC,MAAM;AACvB,cAAM,MAAM,UAAU,IAAI,EAAE,IAAI;AAChC,eAAO;AAAA,UACL,MAAM,EAAE;AAAA,UACR,OAAQ,EAAU,SAAS,KAAK;AAAA,UAChC,OAAO,EAAE;AAAA,UACT,SAAU,EAAU;AAAA,UACpB,OAAO,KAAK;AAAA,QACd;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,SAAS,YAAwB,WAA8B,OAAe,OAA4B;AACjH,QAAM,WAAW,gBAAgB,KAAK;AACtC,MAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AACnC,QAAM,cAAc,oBAAI,IAAoF;AAC5G,aAAW,MAAM,UAAU;AACzB,UAAM,UAAU,WAAW,OAAO,EAAE,SAAS,IAAI,OAAO,GAAG,CAAC;AAC5D,eAAW,KAAK,SAAS;AACvB,YAAM,WAAW,YAAY,IAAI,EAAE,IAAI;AACvC,YAAM,MAAM,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI;AACnD,UAAI,UAAU;AACZ,iBAAS,QAAQ;AAAA,MACnB,OAAO;AACL,oBAAY,IAAI,EAAE,MAAM;AAAA,UACtB,MAAM;AAAA,UACN,cAAc,EAAE;AAAA,UAChB,OAAO,EAAE;AAAA,UACT,OAAO,KAAK,SAAS;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,YAAY,QAAQ,CAAC,EACpC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO;AAAA,IACnB;AAAA,IACA,OAAO,EAAE;AAAA,IACT,OAAO,EAAE,OAAO,SAAS;AAAA,IACzB,SAAS,EAAE;AAAA,IACX,OAAO,EAAE;AAAA,EACX,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,KAAK;AACnB;AAEA,IAAM,YAAY,oBAAI,IAAI;AAAA,EACxB;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC7E;AAAA,EAAO;AAAA,EAAK;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAU;AAAA,EAC9E;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAO;AAAA,EAC3E;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EACxE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EACtE;AAAA,EAAS;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAC1E;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAC9D,CAAC;AAED,SAAS,gBAAgB,OAAyB;AAChD,QAAM,SAAS,MACZ,YAAY,EACZ,QAAQ,aAAa,GAAG,EACxB,MAAM,KAAK,EACX,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;AAClD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,KAAK,IAAI,CAAC,GAAG;AAChB,WAAK,IAAI,CAAC;AACV,UAAI,KAAK,CAAC;AAAA,IACZ;AACA,QAAI,IAAI,UAAU,EAAG;AAAA,EACvB;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/cli/search.ts"],"sourcesContent":["import { join, resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { Indexer } from \"../core/indexer.js\";\nimport { Embedder } from \"../core/embedder.js\";\nimport { GraphBuilder } from \"../core/graph.js\";\nimport { VectorIndex } from \"../core/vector.js\";\nimport { TextSearch } from \"../core/search-text.js\";\nimport type { IndexedDocument } from \"../core/types.js\";\n\nexport interface SearchCliOptions {\n notes: string;\n query: string;\n limit?: number;\n textOnly?: boolean;\n json?: boolean;\n}\n\nexport interface SearchHit {\n path: string;\n title?: string;\n score: number;\n snippet?: string;\n mtime?: string;\n}\n\nexport async function runSearch(opts: SearchCliOptions): Promise<SearchHit[]> {\n const searcher = await openSearch(opts.notes, { textOnly: opts.textOnly });\n return searcher.search(opts.query, opts.limit ?? 8);\n}\n\nexport interface Searcher {\n search(query: string, limit?: number): Promise<SearchHit[]>;\n}\n\n/**\n * Load a notes folder once and search it many times — the same scoring the hook's CLI search uses.\n * The hook spawns `search` per prompt; the failure eval reuses one Searcher per corpus.\n */\nexport async function openSearch(notes: string, opts: { textOnly?: boolean } = {}): Promise<Searcher> {\n const notesPath = resolve(notes);\n if (!existsSync(notesPath)) {\n throw new Error(`notes directory not found: ${notesPath}`);\n }\n\n const indexer = new Indexer(notesPath);\n const indexPath = join(notesPath, \".semantic-sidekick-index\");\n\n // Prefer the parsed-doc cache written by reindex — avoids re-parsing 500+ files\n // on every hook call. Falls back to a full parse if the cache is missing.\n let documents = existsSync(indexPath) ? await indexer.loadDocsCache(indexPath) : null;\n if (!documents) {\n documents = await indexer.indexAll();\n }\n if (documents.length === 0) return { search: async () => [] };\n const docs = documents;\n\n if (opts.textOnly) {\n const textSearch = new TextSearch();\n textSearch.setDocuments(docs);\n return { search: async (query, limit = 8) => textHits(textSearch, docs, query, limit) };\n }\n\n // Hybrid path: load cached index, embed query, search + graph rerank.\n if (!existsSync(indexPath)) {\n throw new Error(`index not built at ${indexPath} — run: semantic-sidekick --notes ${notesPath} --reindex`);\n }\n\n const embedder = new Embedder();\n await embedder.init();\n\n const metaPath = join(indexPath, \"meta.json\");\n if (existsSync(metaPath)) {\n try {\n const meta = JSON.parse(await readFile(metaPath, \"utf-8\")) as { model?: string };\n if (meta.model && meta.model !== embedder.getModel()) {\n throw new Error(`model mismatch (cached=${meta.model}, current=${embedder.getModel()}) — run --reindex`);\n }\n } catch (e) {\n if (e instanceof Error && e.message.startsWith(\"model mismatch\")) throw e;\n }\n }\n\n const vectorIndex = new VectorIndex(embedder.getDimensions());\n const loaded = await vectorIndex.load(indexPath);\n if (!loaded) {\n throw new Error(`could not load vector index at ${indexPath} — run --reindex`);\n }\n\n const graph = new GraphBuilder();\n await graph.load(indexPath);\n\n const docByPath = new Map(docs.map((d) => [d.path, d]));\n return {\n async search(query, limit = 8) {\n const queryEmbed = await embedder.embed(query);\n const semantic = vectorIndex.search(queryEmbed, limit * 3);\n const graphResults = graph.searchGraph(query, 2);\n const graphPaths = new Set(graphResults.map((r) => r.path));\n\n const hybrid = semantic\n .map((r) => {\n const doc = docByPath.get(r.path);\n let score = graphPaths.has(r.path) ? r.score * 1.3 : r.score;\n if (doc?.loadPriority !== undefined) {\n score *= 1 + (doc.loadPriority - 5) * 0.04;\n }\n return { ...r, score };\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n\n return hybrid.map((r) => {\n const doc = docByPath.get(r.path);\n return {\n path: r.path,\n title: (r as any).title ?? doc?.title,\n score: r.score,\n snippet: (r as any).snippet,\n mtime: doc?.mtime,\n };\n });\n },\n };\n}\n\nfunction textHits(textSearch: TextSearch, documents: IndexedDocument[], query: string, limit: number): SearchHit[] {\n const keywords = extractKeywords(query);\n if (keywords.length === 0) return [];\n const scoreByPath = new Map<string, { hits: number; firstSnippet?: string; title?: string; mtime: string }>();\n for (const kw of keywords) {\n const results = textSearch.search({ pattern: kw, limit: 50 });\n for (const r of results) {\n const existing = scoreByPath.get(r.path);\n const doc = documents.find((d) => d.path === r.path);\n if (existing) {\n existing.hits += 1;\n } else {\n scoreByPath.set(r.path, {\n hits: 1,\n firstSnippet: r.snippet,\n title: r.title,\n mtime: doc?.mtime ?? \"\",\n });\n }\n }\n }\n return Array.from(scoreByPath.entries())\n .map(([path, v]) => ({\n path,\n title: v.title,\n score: v.hits / keywords.length,\n snippet: v.firstSnippet,\n mtime: v.mtime,\n }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n}\n\nconst STOPWORDS = new Set([\n \"a\", \"an\", \"and\", \"are\", \"as\", \"at\", \"be\", \"by\", \"can\", \"do\", \"does\", \"for\", \"from\",\n \"how\", \"i\", \"if\", \"in\", \"is\", \"it\", \"its\", \"of\", \"on\", \"or\", \"our\", \"should\", \"that\",\n \"the\", \"this\", \"to\", \"was\", \"we\", \"what\", \"when\", \"where\", \"which\", \"why\", \"will\",\n \"with\", \"you\", \"your\", \"have\", \"has\", \"had\", \"but\", \"not\", \"so\", \"any\", \"all\",\n \"about\", \"there\", \"their\", \"they\", \"them\", \"these\", \"those\", \"would\", \"could\",\n \"might\", \"may\", \"really\", \"just\", \"like\", \"some\", \"more\", \"than\", \"into\", \"out\",\n \"up\", \"down\", \"need\", \"want\", \"get\", \"got\", \"also\", \"very\", \"much\",\n]);\n\nfunction extractKeywords(query: string): string[] {\n const tokens = query\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \" \")\n .split(/\\s+/)\n .filter((t) => t.length > 2 && !STOPWORDS.has(t));\n const seen = new Set<string>();\n const out: string[] = [];\n for (const t of tokens) {\n if (!seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n if (out.length >= 6) break;\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AAwBzB,eAAsB,UAAU,MAA8C;AAC5E,QAAM,WAAW,MAAM,WAAW,KAAK,OAAO,EAAE,UAAU,KAAK,SAAS,CAAC;AACzE,SAAO,SAAS,OAAO,KAAK,OAAO,KAAK,SAAS,CAAC;AACpD;AAUA,eAAsB,WAAW,OAAe,OAA+B,CAAC,GAAsB;AACpG,QAAM,YAAY,QAAQ,KAAK;AAC/B,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAM,IAAI,MAAM,8BAA8B,SAAS,EAAE;AAAA,EAC3D;AAEA,QAAM,UAAU,IAAI,QAAQ,SAAS;AACrC,QAAM,YAAY,KAAK,WAAW,0BAA0B;AAI5D,MAAI,YAAY,WAAW,SAAS,IAAI,MAAM,QAAQ,cAAc,SAAS,IAAI;AACjF,MAAI,CAAC,WAAW;AACd,gBAAY,MAAM,QAAQ,SAAS;AAAA,EACrC;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,QAAQ,YAAY,CAAC,EAAE;AAC5D,QAAM,OAAO;AAEb,MAAI,KAAK,UAAU;AACjB,UAAM,aAAa,IAAI,WAAW;AAClC,eAAW,aAAa,IAAI;AAC5B,WAAO,EAAE,QAAQ,OAAO,OAAO,QAAQ,MAAM,SAAS,YAAY,MAAM,OAAO,KAAK,EAAE;AAAA,EACxF;AAGA,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAM,IAAI,MAAM,sBAAsB,SAAS,0CAAqC,SAAS,YAAY;AAAA,EAC3G;AAEA,QAAM,WAAW,IAAI,SAAS;AAC9B,QAAM,SAAS,KAAK;AAEpB,QAAM,WAAW,KAAK,WAAW,WAAW;AAC5C,MAAI,WAAW,QAAQ,GAAG;AACxB,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,MAAM,SAAS,UAAU,OAAO,CAAC;AACzD,UAAI,KAAK,SAAS,KAAK,UAAU,SAAS,SAAS,GAAG;AACpD,cAAM,IAAI,MAAM,0BAA0B,KAAK,KAAK,aAAa,SAAS,SAAS,CAAC,wBAAmB;AAAA,MACzG;AAAA,IACF,SAAS,GAAG;AACV,UAAI,aAAa,SAAS,EAAE,QAAQ,WAAW,gBAAgB,EAAG,OAAM;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,YAAY,SAAS,cAAc,CAAC;AAC5D,QAAM,SAAS,MAAM,YAAY,KAAK,SAAS;AAC/C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,kCAAkC,SAAS,uBAAkB;AAAA,EAC/E;AAEA,QAAM,QAAQ,IAAI,aAAa;AAC/B,QAAM,MAAM,KAAK,SAAS;AAE1B,QAAM,YAAY,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACtD,SAAO;AAAA,IACL,MAAM,OAAO,OAAO,QAAQ,GAAG;AAC7B,YAAM,aAAa,MAAM,SAAS,MAAM,KAAK;AAC7C,YAAM,WAAW,YAAY,OAAO,YAAY,QAAQ,CAAC;AACzD,YAAM,eAAe,MAAM,YAAY,OAAO,CAAC;AAC/C,YAAM,aAAa,IAAI,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAE1D,YAAM,SAAS,SACZ,IAAI,CAAC,MAAM;AACV,cAAM,MAAM,UAAU,IAAI,EAAE,IAAI;AAChC,YAAI,QAAQ,WAAW,IAAI,EAAE,IAAI,IAAI,EAAE,QAAQ,MAAM,EAAE;AACvD,YAAI,KAAK,iBAAiB,QAAW;AACnC,mBAAS,KAAK,IAAI,eAAe,KAAK;AAAA,QACxC;AACA,eAAO,EAAE,GAAG,GAAG,MAAM;AAAA,MACvB,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,KAAK;AAEjB,aAAO,OAAO,IAAI,CAAC,MAAM;AACvB,cAAM,MAAM,UAAU,IAAI,EAAE,IAAI;AAChC,eAAO;AAAA,UACL,MAAM,EAAE;AAAA,UACR,OAAQ,EAAU,SAAS,KAAK;AAAA,UAChC,OAAO,EAAE;AAAA,UACT,SAAU,EAAU;AAAA,UACpB,OAAO,KAAK;AAAA,QACd;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,SAAS,YAAwB,WAA8B,OAAe,OAA4B;AACjH,QAAM,WAAW,gBAAgB,KAAK;AACtC,MAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AACnC,QAAM,cAAc,oBAAI,IAAoF;AAC5G,aAAW,MAAM,UAAU;AACzB,UAAM,UAAU,WAAW,OAAO,EAAE,SAAS,IAAI,OAAO,GAAG,CAAC;AAC5D,eAAW,KAAK,SAAS;AACvB,YAAM,WAAW,YAAY,IAAI,EAAE,IAAI;AACvC,YAAM,MAAM,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI;AACnD,UAAI,UAAU;AACZ,iBAAS,QAAQ;AAAA,MACnB,OAAO;AACL,oBAAY,IAAI,EAAE,MAAM;AAAA,UACtB,MAAM;AAAA,UACN,cAAc,EAAE;AAAA,UAChB,OAAO,EAAE;AAAA,UACT,OAAO,KAAK,SAAS;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,YAAY,QAAQ,CAAC,EACpC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO;AAAA,IACnB;AAAA,IACA,OAAO,EAAE;AAAA,IACT,OAAO,EAAE,OAAO,SAAS;AAAA,IACzB,SAAS,EAAE;AAAA,IACX,OAAO,EAAE;AAAA,EACX,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,KAAK;AACnB;AAEA,IAAM,YAAY,oBAAI,IAAI;AAAA,EACxB;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC7E;AAAA,EAAO;AAAA,EAAK;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAO;AAAA,EAAU;AAAA,EAC9E;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAO;AAAA,EAC3E;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EACxE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EACtE;AAAA,EAAS;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAC1E;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAC9D,CAAC;AAED,SAAS,gBAAgB,OAAyB;AAChD,QAAM,SAAS,MACZ,YAAY,EACZ,QAAQ,aAAa,GAAG,EACxB,MAAM,KAAK,EACX,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;AAClD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,KAAK,IAAI,CAAC,GAAG;AAChB,WAAK,IAAI,CAAC;AACV,UAAI,KAAK,CAAC;AAAA,IACZ;AACA,QAAI,IAAI,UAAU,EAAG;AAAA,EACvB;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,71 @@
1
+ type LedgerAction = "land" | "patch" | "stale" | "archive" | "restore" | "promote" | "merge" | "reject" | "rollback";
2
+ interface FileChange {
3
+ path: string;
4
+ /** sha256 of the bytes before (null: the file did not exist) */
5
+ before: string | null;
6
+ after: string | null;
7
+ }
8
+ interface LedgerEntry {
9
+ id: string;
10
+ ts: string;
11
+ action: LedgerAction;
12
+ actor: "user" | "curator" | "reviewer";
13
+ skill: string;
14
+ why: string;
15
+ files: FileChange[];
16
+ /** rollback: the entry it reversed */
17
+ reverts?: string;
18
+ }
19
+ declare function readLedger(repo: string): LedgerEntry[];
20
+ /** Undo one entry: every file it touched goes back to its exact bytes before (or aside, if it did not exist). */
21
+ declare function rollback(repo: string, id: string): LedgerEntry;
22
+
23
+ declare const STALE_DAYS = 14;
24
+ declare const ARCHIVE_DAYS = 30;
25
+ declare const CURATOR_INTERVAL_DAYS = 7;
26
+ declare const IDLE_HOURS = 2;
27
+ declare const curatorStatePath: (repo: string) => string;
28
+ interface CuratorState {
29
+ first_seen?: string;
30
+ last_run?: string;
31
+ last_report?: string;
32
+ flagged?: {
33
+ name: string;
34
+ reason: string;
35
+ at: string;
36
+ }[];
37
+ }
38
+ declare function readState(repo: string): CuratorState;
39
+ interface CuratorAction {
40
+ skill: string;
41
+ path: string;
42
+ action: "stale" | "archive" | "reactivate" | "keep" | "skip";
43
+ why: string;
44
+ }
45
+ declare function plan(repo: string, now?: number, env?: NodeJS.ProcessEnv): CuratorAction[];
46
+ /** Run the curator. Dry run: report only. Returns the actions (and the ledger ids of the changes made). */
47
+ declare function curate(repo: string, opts?: {
48
+ dryRun?: boolean;
49
+ now?: number;
50
+ env?: NodeJS.ProcessEnv;
51
+ }): {
52
+ actions: CuratorAction[];
53
+ ledger: string[];
54
+ };
55
+ /** Whether SessionStart should start the curator now (and records first sight of the repo). */
56
+ declare function curatorDue(repo: string, now?: number): boolean;
57
+ /** Bring an archived skill back where it was, active again. */
58
+ declare function restore(repo: string, name: string): LedgerEntry;
59
+ declare function listArchived(repo: string): string[];
60
+ /** Run each landed learned skill's Verify command (they were approved by a person); flag failures, archive nothing. */
61
+ declare function verifySkills(repo: string, opts?: {
62
+ timeoutMs?: number;
63
+ env?: NodeJS.ProcessEnv;
64
+ }): {
65
+ skill: string;
66
+ result: "pass" | "fail" | "prose" | "none";
67
+ detail: string;
68
+ }[];
69
+ declare const curatorState: typeof readState;
70
+
71
+ export { ARCHIVE_DAYS as A, CURATOR_INTERVAL_DAYS as C, IDLE_HOURS as I, type LedgerEntry as L, STALE_DAYS as S, restore as a, rollback as b, curate as c, type CuratorAction as d, curatorDue as e, curatorState as f, curatorStatePath as g, listArchived as l, plan as p, readLedger as r, verifySkills as v };
@@ -0,0 +1,31 @@
1
+ import {
2
+ approveDraft,
3
+ describeDrafts,
4
+ destinationFor,
5
+ evalDraft,
6
+ rejectDraft,
7
+ reviewedPrompts
8
+ } from "./chunk-BEACL25M.js";
9
+ import "./chunk-P3GLY42T.js";
10
+ import "./chunk-TKHWVISV.js";
11
+ import "./chunk-3FPUBAEW.js";
12
+ import "./chunk-MQS7JEBD.js";
13
+ import "./chunk-DBFD3MTT.js";
14
+ import "./chunk-IEZVFKUT.js";
15
+ import "./chunk-ZSWI3Y4Y.js";
16
+ import "./chunk-J7LYDRML.js";
17
+ import "./chunk-VQCNWRTL.js";
18
+ import "./chunk-GYR7U6KA.js";
19
+ import "./chunk-NIZLBNH4.js";
20
+ import "./chunk-R6GNP7DS.js";
21
+ import "./chunk-H2BES6FU.js";
22
+ import "./chunk-PZ5AY32C.js";
23
+ export {
24
+ approveDraft,
25
+ describeDrafts,
26
+ destinationFor,
27
+ evalDraft,
28
+ rejectDraft,
29
+ reviewedPrompts
30
+ };
31
+ //# sourceMappingURL=skill-land-XU2APPBJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,18 @@
1
+ import {
2
+ applyChange,
3
+ archiveDir,
4
+ ledgerPath,
5
+ readLedger,
6
+ rollback
7
+ } from "./chunk-TKHWVISV.js";
8
+ import "./chunk-R6GNP7DS.js";
9
+ import "./chunk-H2BES6FU.js";
10
+ import "./chunk-PZ5AY32C.js";
11
+ export {
12
+ applyChange,
13
+ archiveDir,
14
+ ledgerPath,
15
+ readLedger,
16
+ rollback
17
+ };
18
+ //# sourceMappingURL=skill-ledger-AYXVNFX7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,24 @@
1
+ import {
2
+ MERGE_MIN_OVERLAP,
3
+ consolidateOn,
4
+ mergeCandidates,
5
+ proposeMerges
6
+ } from "./chunk-NCGM36NX.js";
7
+ import "./chunk-P3GLY42T.js";
8
+ import "./chunk-3FPUBAEW.js";
9
+ import "./chunk-IEZVFKUT.js";
10
+ import "./chunk-ZSWI3Y4Y.js";
11
+ import "./chunk-J7LYDRML.js";
12
+ import "./chunk-VQCNWRTL.js";
13
+ import "./chunk-GYR7U6KA.js";
14
+ import "./chunk-NIZLBNH4.js";
15
+ import "./chunk-R6GNP7DS.js";
16
+ import "./chunk-H2BES6FU.js";
17
+ import "./chunk-PZ5AY32C.js";
18
+ export {
19
+ MERGE_MIN_OVERLAP,
20
+ consolidateOn,
21
+ mergeCandidates,
22
+ proposeMerges
23
+ };
24
+ //# sourceMappingURL=skill-merge-GXBQLBZA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,179 @@
1
+ import { ReviewJob } from './core/skill-queue.js';
2
+ import { ReviewOutcome } from './core/skill-worker.js';
3
+
4
+ declare const SKILL_SCOPES: readonly ["directory", "project", "global"];
5
+ declare const SKILL_STATUSES: readonly ["draft", "active", "stale", "archived"];
6
+ declare const SKILL_SECTIONS: readonly ["When to use", "Steps", "Pitfalls", "Verify"];
7
+ type SkillScope = (typeof SKILL_SCOPES)[number];
8
+ type SkillStatus = (typeof SKILL_STATUSES)[number];
9
+ interface SkillLoopMeta {
10
+ schema: 1;
11
+ scope: SkillScope;
12
+ status: SkillStatus;
13
+ pinned: boolean;
14
+ triggers: string[];
15
+ applies_to: {
16
+ paths: string[];
17
+ commands: string[];
18
+ };
19
+ provenance: {
20
+ sessions: string[];
21
+ failures: string[];
22
+ corrections: string[];
23
+ };
24
+ cites: string[];
25
+ supersedes: string[];
26
+ }
27
+ interface ParsedSkill {
28
+ name: string;
29
+ description: string;
30
+ /** null when the file has no `skill_loop:` block: not a learned skill */
31
+ meta: SkillLoopMeta | null;
32
+ /** the raw `skill_loop` value, for lint messages on a malformed block */
33
+ rawMeta: unknown;
34
+ body: string;
35
+ /** `## ` headings in the body, in order */
36
+ sections: string[];
37
+ frontmatter: Record<string, unknown>;
38
+ }
39
+ declare function parseSkill(raw: string): ParsedSkill;
40
+ /** A learned skill's SKILL.md text. Field order is fixed so a re-render of an unchanged skill is byte-identical. */
41
+ declare function renderSkill(skill: {
42
+ name: string;
43
+ description: string;
44
+ meta: SkillLoopMeta;
45
+ body: string;
46
+ extra?: Record<string, unknown>;
47
+ }): string;
48
+ interface SkillFile {
49
+ path: string;
50
+ /** directory holding SKILL.md; its basename is the skill's name in Claude Code */
51
+ dir: string;
52
+ scope: SkillScope;
53
+ learned: boolean;
54
+ skill: ParsedSkill;
55
+ }
56
+ /** Every skill Claude Code would load for this repo: directory, project and global scope. */
57
+ declare function findSkills(projectRoot: string, opts?: {
58
+ home?: string;
59
+ global?: boolean;
60
+ }): SkillFile[];
61
+ interface SkillFinding {
62
+ path: string;
63
+ rule: "frontmatter" | "name" | "description" | "skill_loop" | "provenance" | "sections" | "cites" | "applies_to" | "secrets" | "name_clash";
64
+ severity: "error" | "warn";
65
+ message: string;
66
+ }
67
+ interface SkillLintContext {
68
+ /** repo root the skill belongs to (applies_to paths resolve against it) */
69
+ projectRoot: string;
70
+ facts?: Set<string>;
71
+ /** every other skill Claude Code would load alongside (for name clashes) */
72
+ others?: SkillFile[];
73
+ }
74
+ /** Lint one learned skill. `dirName` is the folder name Claude Code will use (defaults to the skill's name). */
75
+ declare function lintSkill(path: string, raw: string, ctx: SkillLintContext, dirName?: string): SkillFinding[];
76
+ /** Lint every learned skill this repo would load (and its drafts, when given). */
77
+ declare function lintSkills(projectRoot: string, opts?: {
78
+ home?: string;
79
+ extra?: string[];
80
+ }): {
81
+ files: number;
82
+ findings: SkillFinding[];
83
+ };
84
+
85
+ interface ScopeDecision {
86
+ scope: Exclude<SkillScope, "global">;
87
+ /** directory scope: the subdirectory (repo-relative) */
88
+ directory?: string;
89
+ /** Jev said this holds in any repo: offer promotion once another repo has it too */
90
+ globalCandidate: boolean;
91
+ why: string;
92
+ }
93
+ declare function decideScope(repo: string, draft: {
94
+ name: string;
95
+ description: string;
96
+ body: string;
97
+ applies_to?: {
98
+ paths?: string[];
99
+ };
100
+ }, env?: NodeJS.ProcessEnv): Promise<ScopeDecision>;
101
+
102
+ declare const draftsDir: (repo: string) => string;
103
+ interface DraftRecord {
104
+ id: string;
105
+ kind: "new" | "patch" | "merge";
106
+ name: string;
107
+ scope: SkillScope;
108
+ /** patch: the SKILL.md it replaces, and that file's sha256 when the draft was made */
109
+ target?: string;
110
+ base_sha256?: string;
111
+ session_id: string;
112
+ created: string;
113
+ warnings: string[];
114
+ /** directory scope: where it lands (repo-relative subdirectory) */
115
+ directory?: string;
116
+ scope_why?: string;
117
+ /** Jev thinks it holds in any repo */
118
+ global_candidate?: boolean;
119
+ /** other repos with the same lesson: review offers one global skill replacing the copies */
120
+ promotion?: {
121
+ repo: string;
122
+ path: string;
123
+ }[];
124
+ /** merge: the SKILL.md files the merged skill replaces (archived when it lands) */
125
+ supersedes?: string[];
126
+ /** filled by skill evals (Phase 14) when they run at approval time */
127
+ evals?: Record<string, unknown>;
128
+ }
129
+ /** The session's timeline from its transcript: prompts, tool calls, and the errors they returned. Fail-soft. */
130
+ declare function transcriptTimeline(path: string | null, max?: number): string[];
131
+ declare function buildBrief(job: ReviewJob, skills?: SkillFile[]): string;
132
+ interface ReviewerDraft {
133
+ kind: "new" | "patch";
134
+ target?: string;
135
+ name: string;
136
+ description: string;
137
+ scope: string;
138
+ applies_to?: {
139
+ paths?: string[];
140
+ commands?: string[];
141
+ };
142
+ triggers?: string[];
143
+ provenance?: {
144
+ sessions?: string[];
145
+ failures?: string[];
146
+ corrections?: string[];
147
+ };
148
+ cites?: string[];
149
+ body: string;
150
+ }
151
+ /** The JSON object in the reviewer's answer (bare, or inside a ```json fence). */
152
+ declare function parseReviewerJson(text: string): {
153
+ drafts: ReviewerDraft[];
154
+ none_reason?: string;
155
+ } | null;
156
+ type ScopeResolver = (d: ReviewerDraft) => Promise<ScopeDecision>;
157
+ /**
158
+ * Turn one reviewer draft into a draft folder, or say why not. `resolveScope` decides the final scope
159
+ * (skill-scope.ts: deterministic rules, then Jev); the reviewer's own choice is never trusted for it.
160
+ */
161
+ declare function materializeDraft(repo: string, job: ReviewJob, d: ReviewerDraft, skills: SkillFile[], resolveScope?: ScopeResolver, env?: NodeJS.ProcessEnv, merge?: {
162
+ supersedes: string[];
163
+ }): Promise<{
164
+ id?: string;
165
+ rejected?: string;
166
+ }>;
167
+ declare function listDrafts(repo: string): {
168
+ dir: string;
169
+ record: DraftRecord;
170
+ }[];
171
+ interface ReviewOptions {
172
+ env?: NodeJS.ProcessEnv;
173
+ resolveScope?: ScopeResolver;
174
+ maxTurns?: number;
175
+ }
176
+ /** Review one queued session (the worker's review function). */
177
+ declare function reviewSession(job: ReviewJob, opts?: ReviewOptions): Promise<ReviewOutcome>;
178
+
179
+ export { type DraftRecord as D, type ParsedSkill as P, type ReviewOptions as R, type SkillLoopMeta as S, SKILL_SCOPES as a, SKILL_SECTIONS as b, SKILL_STATUSES as c, type SkillFile as d, type SkillFinding as e, type SkillScope as f, type SkillStatus as g, buildBrief as h, decideScope as i, draftsDir as j, findSkills as k, lintSkill as l, lintSkills as m, listDrafts as n, reviewSession as o, parseSkill as p, type ReviewerDraft as q, renderSkill as r, type ScopeResolver as s, materializeDraft as t, parseReviewerJson as u, transcriptTimeline as v };
@@ -0,0 +1,30 @@
1
+ import {
2
+ GENERATED_BY,
3
+ SKILL_SCOPES,
4
+ SKILL_SECTIONS,
5
+ SKILL_STATUSES,
6
+ factIds,
7
+ findSkills,
8
+ isLearned,
9
+ lintSkill,
10
+ lintSkills,
11
+ parseSkill,
12
+ renderSkill
13
+ } from "./chunk-ZSWI3Y4Y.js";
14
+ import "./chunk-J7LYDRML.js";
15
+ import "./chunk-H2BES6FU.js";
16
+ import "./chunk-PZ5AY32C.js";
17
+ export {
18
+ GENERATED_BY,
19
+ SKILL_SCOPES,
20
+ SKILL_SECTIONS,
21
+ SKILL_STATUSES,
22
+ factIds,
23
+ findSkills,
24
+ isLearned,
25
+ lintSkill,
26
+ lintSkills,
27
+ parseSkill,
28
+ renderSkill
29
+ };
30
+ //# sourceMappingURL=skill-schema-RVB3EHEF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -6,9 +6,9 @@ import {
6
6
  selectionLogPath,
7
7
  telemetryEnabled,
8
8
  wasRecentlySearched
9
- } from "./chunk-NBY4TRP7.js";
10
- import "./chunk-LGQPP5FQ.js";
11
- import "./chunk-OEF5U5GV.js";
9
+ } from "./chunk-7M34JDTH.js";
10
+ import "./chunk-EYVFC745.js";
11
+ import "./chunk-H2BES6FU.js";
12
12
  import "./chunk-PZ5AY32C.js";
13
13
  export {
14
14
  appendEvent,
@@ -19,4 +19,4 @@ export {
19
19
  telemetryEnabled,
20
20
  wasRecentlySearched
21
21
  };
22
- //# sourceMappingURL=telemetry-B4KP3UVE.js.map
22
+ //# sourceMappingURL=telemetry-YAG3VA4Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -27,13 +27,13 @@ Three snapshots:
27
27
  | Snapshot | Captures | Size |
28
28
  |---|---|---|
29
29
  | `tool-surface 1` | Full list of registered tools — name, description, complete JSON Schema for inputs | ~1000 lines |
30
- | `tool-name list (compact) 1` | Sorted list of tool names in default mode (33 entries) | ~35 lines |
31
- | `tool-name list in read-only mode 1` | Sorted list of tool names in `--read-only` mode (21 entries) | ~22 lines |
30
+ | `tool-name list (compact) 1` | Sorted list of tool names in default mode (44 entries as of v1.6.1; 33 at baseline) | ~46 lines |
31
+ | `tool-name list in read-only mode 1` | Sorted list of tool names in `--read-only` mode (20 entries as of v1.6.1; 21 at baseline) | ~21 lines |
32
32
 
33
33
  Plus structural assertions:
34
- - Default mode exposes exactly 33 tools
35
- - `--read-only` mode exposes exactly 21 tools
36
- - Read-only mode never exposes any of the 12 write tools (explicit name list)
34
+ - Default mode exposes exactly 44 tools (33 at baseline)
35
+ - `--read-only` mode exposes exactly 20 tools (21 at baseline; `log_event` became write-gated in v1.6.1)
36
+ - Read-only mode never exposes any of the 13 listed write tools (explicit name list, `log_event` added in v1.6.1)
37
37
 
38
38
  ### `test/regression/tool-outputs.test.ts` — output contract
39
39
 
package/hooks/hooks.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "semantic-sidekick activation + mode hooks. SessionStart reconciles .mcp.json + resets mode + preloads vault context. UserPromptSubmit injects vault search results (mode-aware, fingerprinted). Stop emits mode-specific transition-capture prompts (research\u2192synthesis, outage\u2192postmortem, vault-first\u2192generic capture). PostToolUseFailure (opt-in, SEMANTIC_MEMORY_FAILURE_HOOK=1) looks up a knowledge-base fact for a failed Bash/MCP call.",
2
+ "description": "semantic-sidekick activation + mode hooks. SessionStart reconciles .mcp.json + resets mode + preloads vault context. UserPromptSubmit injects vault search results (mode-aware, fingerprinted). Stop emits mode-specific transition-capture prompts (research\u2192synthesis, outage\u2192postmortem, vault-first\u2192generic capture). PostToolUseFailure (opt-in, SEMANTIC_MEMORY_FAILURE_HOOK=1) looks up a knowledge-base fact for a failed Bash/MCP call. PostToolUse (Skill) and SessionEnd feed the skill loop (v1.7, off by default: SEMANTIC_MEMORY_SKILL_LOOP=1).",
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
@@ -11,6 +11,10 @@
11
11
  {
12
12
  "type": "command",
13
13
  "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/vault-context.js\""
14
+ },
15
+ {
16
+ "type": "command",
17
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/skill-loop.js\""
14
18
  }
15
19
  ]
16
20
  }
@@ -45,6 +49,27 @@
45
49
  }
46
50
  ]
47
51
  }
52
+ ],
53
+ "PostToolUse": [
54
+ {
55
+ "matcher": "Skill",
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/skill-loop.js\""
60
+ }
61
+ ]
62
+ }
63
+ ],
64
+ "SessionEnd": [
65
+ {
66
+ "hooks": [
67
+ {
68
+ "type": "command",
69
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/skill-loop.js\""
70
+ }
71
+ ]
72
+ }
48
73
  ]
49
74
  }
50
75
  }