claude-alfred 0.3.2 → 0.3.4

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 (30) hide show
  1. package/README.ja.md +10 -8
  2. package/README.md +10 -8
  3. package/dist/audit-g6phLGMg.mjs +18 -0
  4. package/dist/cli.mjs +7 -7
  5. package/dist/{directives-ko2sX5BW.mjs → directives-DyHIJo9p.mjs} +1 -1
  6. package/dist/{dispatcher-BnBtA4bo.mjs → dispatcher-DE-9lhIv.mjs} +7 -7
  7. package/dist/{audit-6NSlW0Ut.mjs → epic-D9ksT1k7.mjs} +3 -16
  8. package/dist/{fts-QWdC_4Zv.mjs → fts-Buk8fkl1.mjs} +2 -2
  9. package/dist/{helpers-BnPVn8Nv.mjs → helpers-CvI9bVCq.mjs} +3 -3
  10. package/dist/knowledge-BgWoLpv7.mjs +172 -0
  11. package/dist/knowledge-extractor-DTO74tU1.mjs +456 -0
  12. package/dist/living-spec-jepeb0NQ.mjs +218 -0
  13. package/dist/{post-tool-gG8qG5Ee.mjs → post-tool-Dg6-F0_1.mjs} +31 -37
  14. package/dist/{pre-compact-CwvBi5op.mjs → pre-compact-B2G-Khc9.mjs} +7 -5
  15. package/dist/pre-tool-BwjDjdA-.mjs +54 -0
  16. package/dist/{spec-guard-BGwtCxgH.mjs → review-gate-BvV9OUnG.mjs} +46 -2
  17. package/dist/{server-Df8fib2F.mjs → server-BaPbUbPa.mjs} +7 -5
  18. package/dist/{server-CHdcikXe.mjs → server-DjHlrHQd.mjs} +663 -431
  19. package/dist/{session-start-dPl4lfZw.mjs → session-start-B036zTZK.mjs} +7 -6
  20. package/dist/{state-CdHO66SV.mjs → state-CM6iakfR.mjs} +25 -1
  21. package/dist/stop-_E6Cn7T8.mjs +30 -0
  22. package/dist/{knowledge-B0fsJ9SI.mjs → types-DFsKNXVY.mjs} +1 -172
  23. package/dist/{user-prompt-o9rlzB2I.mjs → user-prompt-B-FXw-Q3.mjs} +10 -8
  24. package/dist/{vectors-BDQP0gaa.mjs → vectors-DtWMZUgk.mjs} +1 -1
  25. package/package.json +1 -1
  26. package/dist/pre-tool-BMCYjtHT.mjs +0 -29
  27. package/dist/stop-NL5GdPNy.mjs +0 -21
  28. /package/dist/{embedder-BRG2Xpr4.mjs → embedder-CFkDPOku.mjs} +0 -0
  29. /package/dist/{project-Djp8-Djz.mjs → project-Cz-yOhrW.mjs} +0 -0
  30. /package/dist/{store-CuN3_dWu.mjs → store-D4fokoGA.mjs} +0 -0
package/README.ja.md CHANGED
@@ -22,7 +22,7 @@ alfred は3つとも潰す。
22
22
 
23
23
  **消えない仕様。** 要件、設計、決定、セッション状態 — 構造化されたMarkdownファイルが Compact を跨いで生き残る。コンテキストは一度も失われない。
24
24
 
25
- **適応する仕様。** 小さなバグ修正なら3ファイル。中規模なら5。大規模なら7。タスクに応じてスペックの深さを自動調整。bugfix専用テンプレート(再現手順、原因分析、修正戦略)も。
25
+ **適応する仕様。** 小さなバグ修正なら3ファイル。中規模なら5。大規模なら6。タスクに応じてスペックの深さを自動調整。bugfix専用テンプレート(再現手順、原因分析、修正戦略)も。
26
26
 
27
27
  **積み重なる記憶。** 「あの時こう決めた」「このバグ前にも見た」「Xは試したけどダメだった」— 全てが `.alfred/knowledge/` に構造化JSONとして保存される。3分類のみ: **decision**(一回きりの選択 + 理由 + 却下した代替案)、**pattern**(繰り返し実践 + 適用条件 + 期待結果)、**rule**(強制ルール + 優先度 + 根拠)。Git フレンドリーで、チームで共有できる。SQLite検索インデックスがセマンティック検索を提供。矛盾は自動検出。次に似た問題に当たったとき、聞く前に alfred が出してくる。
28
28
 
@@ -36,7 +36,9 @@ alfred は3つとも潰す。
36
36
 
37
37
  **能動的スキル提案。** alfred は呼ばれるのを待たない。調査中か、設計中か、実装中か、バグ修正中かを検出し、適切なタイミングで適切なスキルを提案する。コードを探索し続けてる?「`/alfred:survey` 使ったら?」。調査結果が出た?「`ledger` に保存しよう」。タスクが溜まってきた?「`roster` でまとめよう」。
38
38
 
39
- **バイパスできない承認ゲート。** 実装前に仕様のレビューサイクルを回す。ブラウザダッシュボードで任意の行にコメントして、承認か差し戻し — GitHub の PR レビューと同じ体験を、仕様に対してやる。ステータスの手動書き換えでは突破できない。レビューファイルの存在まで検証する。**ハード強制**: PreToolUse フックが未承認 M/L/XL スペックへの Edit/Write を物理的にブロック。Stop フックがタスク完了・セルフレビュー・spec close まで Claude の停止を阻止。
39
+ **バイパスできない承認ゲート。** 実装前に仕様のレビューサイクルを回す。ブラウザダッシュボードで任意の行にコメントして、承認か差し戻し — GitHub の PR レビューと同じ体験を、仕様に対してやる。ステータスの手動書き換えでは突破できない。**3層 enforcement**: (1) review gate が spec/wave レビュー完了まで Edit/Write をブロック、(2) approval gate が未承認 M/L/XL をブロック、(3) intent guard spec なし実装をブロック。Stop hook はレビューゲート以外ではブロックせず、リマインドのみ。
40
+
41
+ **リアルタイムナレッジ抽出。** `ledger` で保存した decision は即座に検索可能。設計パターンは spec 更新時に自動抽出。レビューエージェントの指摘(critical/high)はアンチパターンとして自動保存。ナレッジはタスク完了時だけでなく、継続的に蓄積される。
40
42
 
41
43
  **張り付くプロジェクトコンテキスト。** Steering文書(プロダクトの目的、コード構造、技術スタック)がプロジェクトから自動生成され、すべての仕様に注入される。AIは常にアーキテクチャを理解している。
42
44
 
@@ -121,7 +123,7 @@ npm update -g claude-alfred # CLI、hooks、MCP サーバー、ダッシ
121
123
 
122
124
  | ツール | 役割 |
123
125
  |--------|------|
124
- | `dossier` | 仕様のライフサイクル管理 — init(サイズ/タイプ指定可), update, status, switch, complete, delete, history, rollback, review, validate |
126
+ | `dossier` | 仕様のライフサイクル管理 — init, update, status, switch, complete, delete, history, rollback, review, validate, gate(レビューゲート管理) |
125
127
  | `roster` | エピック管理 — タスクのグループ化、依存関係、進捗追跡 |
126
128
  | `ledger` | ナレッジ — search, save(構造化JSON: decision/pattern/rule), promote(pattern→rule), reflect, audit-conventions |
127
129
 
@@ -135,8 +137,8 @@ npm update -g claude-alfred # CLI、hooks、MCP サーバー、ダッシ
135
137
  | PreCompact | スナップショット保存 + 決定抽出 + エピック進捗同期 + 調査パターン検出 |
136
138
  | UserPromptSubmit | セマンティック検索 + スキルナッジ + **spec承認ゲート**(未承認 M/L/XL に DIRECTIVE) |
137
139
  | PostToolUse | エラー検出 + Next Steps 自動チェック + ドリフト検出 + コミット時決定保存 |
138
- | **PreToolUse** | **ハード強制**: 未承認 M/L/XL スペックへの Edit/Write をブロック。`.alfred/` 編集と Read/Grep は常に許可 |
139
- | **Stop** | **品質ゲート**: Next Steps 全チェック + Wave セルフレビュー + dossier complete 完了まで停止を阻止 |
140
+ | **PreToolUse** | **3層 enforcement**: (1) review gate(spec/wave レビュー完了まで)、(2) intent guard(spec なし実装ブロック)、(3) approval gate(未承認 M/L/XL)。`.alfred/` 編集は常に許可 |
141
+ | **Stop** | review gate ブロック。その他 コンテキストリマインド(ブロックなし) |
140
142
 
141
143
  ## ブラウザダッシュボード
142
144
 
@@ -208,7 +210,7 @@ alfred dashboard --url-only # URLだけ出力
208
210
  |--------|------------|------|
209
211
  | **S** | 3: requirements, tasks, session | バグ修正、設定変更、小さな変更 |
210
212
  | **M** | 5: + design, test-specs | 新エンドポイント、リファクタ、中規模機能 |
211
- | **L/XL** | 7: + decisions, research | アーキテクチャ変更、新サブシステム |
213
+ | **L/XL** | 6: + research | アーキテクチャ変更、新サブシステム。Decision は `ledger` で直接保存 |
212
214
  | **D** (delta) | 2: delta.md(CHG-N ID + Before/After付き), session | 既存コードへのブラウンフィールド変更 |
213
215
  | **Bugfix** | 3-4: bugfix.md, tasks, session (+test-specs) | 外科的バグ修正(再現手順付き) |
214
216
 
@@ -256,8 +258,8 @@ Hook(見えない)
256
258
  |-- PreCompact -> スナップショット保存、決定抽出、エピック進捗
257
259
  |-- UserPromptSubmit -> ベクトル検索 + FTS5 + スキルナッジ + spec承認チェック
258
260
  |-- PostToolUse -> エラー検出、Next Steps自動チェック、ドリフト検出
259
- |-- PreToolUse -> 未承認 M/L/XL への Edit/Write をブロック
260
- |-- Stop -> 全タスク完了 + セルフレビュー + complete まで停止阻止
261
+ |-- PreToolUse -> review gate + intent guard + approval gate(3層 enforcement)
262
+ |-- Stop -> review gate ブロック + コンテキストリマインド(非ブロック)
261
263
  |
262
264
  v
263
265
  ストレージ
package/README.md CHANGED
@@ -22,7 +22,7 @@ alfred fixes all three.
22
22
 
23
23
  **Specs that survive.** Requirements, design, decisions, session state — structured markdown files that persist across compacts, sessions, and even project restarts. Your context is never lost.
24
24
 
25
- **Specs that adapt.** Small bug? 3 files. Medium feature? 5. Large system? All 7. alfred auto-detects the right scope — or you can pick a bugfix template with surgical precision (reproduction steps, root cause, fix strategy).
25
+ **Specs that adapt.** Small bug? 3 files. Medium feature? 5. Large system? 6. alfred auto-detects the right scope — or you can pick a bugfix template with surgical precision (reproduction steps, root cause, fix strategy).
26
26
 
27
27
  **Memory that compounds.** Every decision, every bug fix, every "we tried X and it didn't work" gets stored as structured JSON files in `.alfred/knowledge/` — three types only: **decisions** (one-time choices with reasoning and rejected alternatives), **patterns** (repeatable practices with conditions and expected outcomes), and **rules** (enforceable standards with priority and rationale). Git-friendly, human-readable, team-shareable. A SQLite search index provides semantic search across all knowledge. Contradictions are detected automatically. Next time you hit a similar problem, alfred surfaces the relevant experience — before you even ask.
28
28
 
@@ -36,7 +36,9 @@ alfred fixes all three.
36
36
 
37
37
  **Proactive skill suggestions.** alfred doesn't wait to be asked. It detects what you're doing — researching, designing, implementing, fixing bugs — and suggests the right skill at the right time. Explored code for a while? "Try `/alfred:survey`." Got research findings? "Save them with `ledger`." Three tasks piling up? "Group them with `roster`."
38
38
 
39
- **Approval gates that can't be bypassed.** Specs go through a review cycle before implementation. Comment on any line in the browser dashboard, approve or request changes — like a GitHub PR review, but for your specs. The gate verifies both the review status *and* the existence of a signed review file, so manually editing the status won't get you past it. **Hard enforcement**: PreToolUse hooks physically block Edit/Write operations on unapproved M/L/XL specs. Stop hooks prevent Claude from finishing until all tasks are checked off, self-reviews are done, and the spec is closed.
39
+ **Approval gates that can't be bypassed.** Specs go through a review cycle before implementation. Comment on any line in the browser dashboard, approve or request changes — like a GitHub PR review, but for your specs. The gate verifies both the review status *and* the existence of a signed review file, so manually editing the status won't get you past it. **Three-layer enforcement**: (1) Review gate blocks Edit/Write until spec self-review is completed, (2) Approval gate blocks Edit/Write on unapproved M/L/XL specs, (3) Intent guard blocks implementation without a spec. Stop hook reminds about incomplete items but doesn't block (except for review gates).
40
+
41
+ **Real-time knowledge extraction.** Decisions saved via `ledger` are immediately searchable. Design patterns extracted from spec components on every update. Review agent findings (critical/high severity) auto-saved as anti-patterns. Knowledge accumulates continuously, not just at task completion.
40
42
 
41
43
  **Project context that sticks.** Steering documents (product purpose, code structure, tech stack) are auto-generated from your project and injected into every spec. Your AI always knows your architecture.
42
44
 
@@ -121,7 +123,7 @@ Run `alfred doctor` to verify both are in sync.
121
123
 
122
124
  | Tool | Purpose |
123
125
  |------|---------|
124
- | `dossier` | Spec lifecycle — init (with size/type), update, status, switch, complete, delete, history, rollback, review, validate |
126
+ | `dossier` | Spec lifecycle — init (with size/type), update, status, switch, complete, delete, history, rollback, review, validate, gate (review gate management) |
125
127
  | `roster` | Epic management — group tasks with dependencies, track progress |
126
128
  | `ledger` | Knowledge — search, save (structured JSON: decision/pattern/rule), promote (pattern→rule), reflect, audit-conventions |
127
129
 
@@ -135,8 +137,8 @@ Run automatically. You don't touch these.
135
137
  | PreCompact | Extracts decisions, saves chapter snapshots, syncs epic progress, detects research patterns |
136
138
  | UserPromptSubmit | Semantic search + file context boost + **skill nudge** + **spec approval gate** (blocks implement intent on unapproved M/L/XL specs) |
137
139
  | PostToolUse | Detects Bash errors + searches memory. After commits: spec drift detection + auto-save decisions. Edit/Write: auto-check Next Steps progress |
138
- | **PreToolUse** | **Hard enforcement**: blocks Edit/Write when active M/L/XL spec is not approved. `.alfred/` edits and Read/Grep always allowed |
139
- | **Stop** | **Quality gate**: blocks Claude from stopping until all Next Steps are checked, Wave self-reviews are done, and `dossier complete` is called |
140
+ | **PreToolUse** | **Three-layer enforcement**: (1) review-gate blocks until spec/wave review done, (2) intent guard blocks implementation without a spec, (3) approval gate blocks unapproved M/L/XL. `.alfred/` edits always allowed |
141
+ | **Stop** | Review gate block. Other incomplete items context reminder (no block) |
140
142
 
141
143
  ## Browser dashboard
142
144
 
@@ -208,7 +210,7 @@ Not every task needs 7 spec files.
208
210
  |------|----------------|------|
209
211
  | **S** (small) | 3: requirements, tasks, session | Bug fix, config change, small tweak |
210
212
  | **M** (medium) | 5: + design, test-specs | New endpoint, refactor, moderate feature |
211
- | **L/XL** (large) | 7: + decisions, research | Architecture change, new subsystem |
213
+ | **L/XL** (large) | 6: + research | Architecture change, new subsystem. Decisions saved via `ledger` directly |
212
214
  | **D** (delta) | 2: delta.md (with CHG-N IDs + Before/After), session | Brownfield changes to existing code |
213
215
  | **Bugfix** | 3-4: bugfix.md, tasks, session (+ test-specs) | Surgical bug fix with reproduction steps |
214
216
 
@@ -256,8 +258,8 @@ Hooks (invisible)
256
258
  |-- PreCompact -> save snapshots, extract decisions, epic progress
257
259
  |-- UserPromptSubmit -> vector search + FTS5 + skill nudge + spec approval check
258
260
  |-- PostToolUse -> detect errors, auto-check Next Steps, drift detection
259
- |-- PreToolUse -> BLOCK Edit/Write on unapproved M/L/XL specs
260
- |-- Stop -> BLOCK stop until all tasks done + self-review + complete
261
+ |-- PreToolUse -> review-gate + intent guard + approval gate (3-layer enforcement)
262
+ |-- Stop -> review-gate block + context reminders (non-blocking)
261
263
  |
262
264
  v
263
265
  Storage
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { u as rootDir } from "./types-DFsKNXVY.mjs";
3
+ import { appendFileSync, mkdirSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ //#region src/spec/audit.ts
6
+ function appendAudit(projectPath, entry) {
7
+ try {
8
+ const dir = rootDir(projectPath);
9
+ mkdirSync(dir, { recursive: true });
10
+ const line = JSON.stringify({
11
+ ...entry,
12
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
13
+ });
14
+ appendFileSync(join(dir, "audit.jsonl"), `${line}\n`);
15
+ } catch {}
16
+ }
17
+ //#endregion
18
+ export { appendAudit as t };
package/dist/cli.mjs CHANGED
@@ -369,9 +369,9 @@ const main = defineCommand({
369
369
  serve: defineCommand({
370
370
  meta: { description: "Start MCP server (stdio)" },
371
371
  async run() {
372
- const { Store } = await import("./store-CuN3_dWu.mjs");
373
- const { Embedder } = await import("./embedder-BRG2Xpr4.mjs");
374
- const { serveMCP } = await import("./server-CHdcikXe.mjs");
372
+ const { Store } = await import("./store-D4fokoGA.mjs");
373
+ const { Embedder } = await import("./embedder-CFkDPOku.mjs");
374
+ const { serveMCP } = await import("./server-DjHlrHQd.mjs");
375
375
  const store = Store.openDefault();
376
376
  let emb = null;
377
377
  try {
@@ -397,9 +397,9 @@ const main = defineCommand({
397
397
  }
398
398
  },
399
399
  async run({ args }) {
400
- const { Store } = await import("./store-CuN3_dWu.mjs");
401
- const { Embedder } = await import("./embedder-BRG2Xpr4.mjs");
402
- const { startDashboard } = await import("./server-Df8fib2F.mjs");
400
+ const { Store } = await import("./store-D4fokoGA.mjs");
401
+ const { Embedder } = await import("./embedder-CFkDPOku.mjs");
402
+ const { startDashboard } = await import("./server-BaPbUbPa.mjs");
403
403
  const projectPath = process.cwd();
404
404
  const store = Store.openDefault();
405
405
  let emb = null;
@@ -422,7 +422,7 @@ const main = defineCommand({
422
422
  description: "Event name"
423
423
  } },
424
424
  async run({ args }) {
425
- const { runHook } = await import("./dispatcher-BnBtA4bo.mjs").then((n) => n.t);
425
+ const { runHook } = await import("./dispatcher-DE-9lhIv.mjs").then((n) => n.t);
426
426
  await runHook(args.event);
427
427
  }
428
428
  }),
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as emitAdditionalContext } from "./dispatcher-BnBtA4bo.mjs";
2
+ import { n as emitAdditionalContext } from "./dispatcher-DE-9lhIv.mjs";
3
3
  //#region src/hooks/directives.ts
4
4
  const LEVEL_ORDER = {
5
5
  DIRECTIVE: 0,
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { k as __exportAll } from "./knowledge-B0fsJ9SI.mjs";
2
+ import { g as __exportAll } from "./types-DFsKNXVY.mjs";
3
3
  import { resolve } from "node:path";
4
4
  //#region src/hooks/dispatcher.ts
5
5
  var dispatcher_exports = /* @__PURE__ */ __exportAll({
@@ -89,27 +89,27 @@ async function runHook(event) {
89
89
  }
90
90
  }
91
91
  async function handleSessionStart(ev, signal) {
92
- const { sessionStart } = await import("./session-start-dPl4lfZw.mjs");
92
+ const { sessionStart } = await import("./session-start-B036zTZK.mjs");
93
93
  await sessionStart(ev, signal);
94
94
  }
95
95
  async function handlePreCompact(ev, signal) {
96
- const { preCompact } = await import("./pre-compact-CwvBi5op.mjs");
96
+ const { preCompact } = await import("./pre-compact-B2G-Khc9.mjs");
97
97
  await preCompact(ev, signal);
98
98
  }
99
99
  async function handleUserPromptSubmit(ev, signal) {
100
- const { userPromptSubmit } = await import("./user-prompt-o9rlzB2I.mjs");
100
+ const { userPromptSubmit } = await import("./user-prompt-B-FXw-Q3.mjs");
101
101
  await userPromptSubmit(ev, signal);
102
102
  }
103
103
  async function handlePostToolUse(ev, signal) {
104
- const { postToolUse } = await import("./post-tool-gG8qG5Ee.mjs");
104
+ const { postToolUse } = await import("./post-tool-Dg6-F0_1.mjs");
105
105
  await postToolUse(ev, signal);
106
106
  }
107
107
  async function handlePreToolUse(ev, _signal) {
108
- const { preToolUse } = await import("./pre-tool-BMCYjtHT.mjs");
108
+ const { preToolUse } = await import("./pre-tool-BwjDjdA-.mjs");
109
109
  await preToolUse(ev);
110
110
  }
111
111
  async function handleStop(ev, _signal) {
112
- const { stop } = await import("./stop-NL5GdPNy.mjs");
112
+ const { stop } = await import("./stop-_E6Cn7T8.mjs");
113
113
  await stop(ev);
114
114
  }
115
115
  //#endregion
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as rootDir, D as require_dist, h as VALID_SLUG } from "./knowledge-B0fsJ9SI.mjs";
3
- import { appendFileSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { m as require_dist, n as VALID_SLUG } from "./types-DFsKNXVY.mjs";
3
+ import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  //#region src/epic/index.ts
6
6
  var import_dist = require_dist();
@@ -222,17 +222,4 @@ function writeActiveEpics(projectPath, state) {
222
222
  renameSync(tmp, path);
223
223
  }
224
224
  //#endregion
225
- //#region src/spec/audit.ts
226
- function appendAudit(projectPath, entry) {
227
- try {
228
- const dir = rootDir(projectPath);
229
- mkdirSync(dir, { recursive: true });
230
- const line = JSON.stringify({
231
- ...entry,
232
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
233
- });
234
- appendFileSync(join(dir, "audit.jsonl"), `${line}\n`);
235
- } catch {}
236
- }
237
- //#endregion
238
- export { nextActionable as a, topologicalOrder as c, listAllEpics as i, unlinkTaskFromAllEpics as l, EpicDir as n, removeEpic as o, initEpic as r, syncTaskStatus as s, appendAudit as t };
225
+ export { removeEpic as a, unlinkTaskFromAllEpics as c, nextActionable as i, initEpic as n, syncTaskStatus as o, listAllEpics as r, topologicalOrder as s, EpicDir as t };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { d as searchKnowledgeKeyword, i as getKnowledgeByIDs, l as mapRow } from "./knowledge-B0fsJ9SI.mjs";
3
- import { n as deserializeFloat32, t as cosineSimilarity } from "./vectors-BDQP0gaa.mjs";
2
+ import { d as searchKnowledgeKeyword, i as getKnowledgeByIDs, l as mapRow } from "./knowledge-BgWoLpv7.mjs";
3
+ import { n as deserializeFloat32, t as cosineSimilarity } from "./vectors-DtWMZUgk.mjs";
4
4
  //#region src/store/fts.ts
5
5
  function subTypeHalfLife(subType) {
6
6
  switch (subType) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { c as incrementHitCount, d as searchKnowledgeKeyword, i as getKnowledgeByIDs } from "./knowledge-B0fsJ9SI.mjs";
3
- import { r as vectorSearchKnowledge } from "./vectors-BDQP0gaa.mjs";
4
- import { i as subTypeHalfLife, n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-QWdC_4Zv.mjs";
2
+ import { c as incrementHitCount, d as searchKnowledgeKeyword, i as getKnowledgeByIDs } from "./knowledge-BgWoLpv7.mjs";
3
+ import { r as vectorSearchKnowledge } from "./vectors-DtWMZUgk.mjs";
4
+ import { i as subTypeHalfLife, n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-Buk8fkl1.mjs";
5
5
  //#region src/mcp/helpers.ts
6
6
  const RECENCY_FLOOR = .5;
7
7
  function truncate(s, maxLen) {
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ //#region src/store/knowledge.ts
4
+ function contentHash(content) {
5
+ return createHash("sha256").update(content).digest("hex");
6
+ }
7
+ function upsertKnowledge(store, row) {
8
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9
+ if (!row.createdAt) row.createdAt = now;
10
+ row.updatedAt = now;
11
+ row.contentHash = contentHash(row.content);
12
+ const existing = store.db.prepare("SELECT id, content_hash FROM knowledge_index WHERE project_remote = ? AND project_path = ? AND file_path = ?").get(row.projectRemote, row.projectPath, row.filePath);
13
+ if (existing && existing.content_hash === row.contentHash) {
14
+ row.id = existing.id;
15
+ return {
16
+ id: existing.id,
17
+ changed: false
18
+ };
19
+ }
20
+ const result = store.db.prepare(`
21
+ INSERT INTO knowledge_index
22
+ (file_path, content_hash, title, content, sub_type,
23
+ project_remote, project_path, project_name, branch,
24
+ created_at, updated_at, hit_count, last_accessed, enabled)
25
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, '', 1)
26
+ ON CONFLICT(project_remote, project_path, file_path) DO UPDATE SET
27
+ content_hash = excluded.content_hash,
28
+ title = excluded.title,
29
+ content = excluded.content,
30
+ sub_type = excluded.sub_type,
31
+ project_name = excluded.project_name,
32
+ branch = excluded.branch,
33
+ updated_at = excluded.updated_at
34
+ `).run(row.filePath, row.contentHash, row.title, row.content, row.subType, row.projectRemote, row.projectPath, row.projectName, row.branch, row.createdAt, row.updatedAt);
35
+ const id = Number(result.lastInsertRowid);
36
+ row.id = id;
37
+ return {
38
+ id,
39
+ changed: true
40
+ };
41
+ }
42
+ function getKnowledgeByID(store, id) {
43
+ const row = store.db.prepare(`
44
+ SELECT id, file_path, content_hash, title, content, sub_type,
45
+ project_remote, project_path, project_name, branch,
46
+ created_at, updated_at, hit_count, last_accessed, enabled
47
+ FROM knowledge_index WHERE id = ?
48
+ `).get(id);
49
+ return row ? mapRow(row) : void 0;
50
+ }
51
+ function getKnowledgeByIDs(store, ids) {
52
+ if (ids.length === 0) return [];
53
+ const placeholders = ids.map(() => "?").join(",");
54
+ return store.db.prepare(`
55
+ SELECT id, file_path, content_hash, title, content, sub_type,
56
+ project_remote, project_path, project_name, branch,
57
+ created_at, updated_at, hit_count, last_accessed, enabled
58
+ FROM knowledge_index WHERE id IN (${placeholders})
59
+ `).all(...ids).map(mapRow);
60
+ }
61
+ function setKnowledgeEnabled(store, id, enabled) {
62
+ store.db.prepare("UPDATE knowledge_index SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
63
+ }
64
+ function incrementHitCount(store, ids) {
65
+ if (ids.length === 0) return;
66
+ const now = (/* @__PURE__ */ new Date()).toISOString();
67
+ const placeholders = ids.map(() => "?").join(",");
68
+ store.db.prepare(`UPDATE knowledge_index SET hit_count = hit_count + 1, last_accessed = ?
69
+ WHERE id IN (${placeholders})`).run(now, ...ids);
70
+ }
71
+ function promoteSubType(store, id, newSubType) {
72
+ const now = (/* @__PURE__ */ new Date()).toISOString();
73
+ if (store.db.prepare("UPDATE knowledge_index SET sub_type = ?, updated_at = ? WHERE id = ? AND enabled = 1").run(newSubType, now, id).changes === 0) throw new Error(`store: promote sub_type: knowledge ${id} not found or disabled`);
74
+ }
75
+ function getPromotionCandidates(store) {
76
+ return store.db.prepare(`
77
+ SELECT id, file_path, content_hash, title, content, sub_type,
78
+ project_remote, project_path, project_name, branch,
79
+ created_at, updated_at, hit_count, last_accessed, enabled
80
+ FROM knowledge_index
81
+ WHERE enabled = 1
82
+ AND (sub_type = 'pattern' AND hit_count >= 15)
83
+ ORDER BY hit_count DESC
84
+ `).all().map(mapRow);
85
+ }
86
+ function getKnowledgeStats(store) {
87
+ const agg = store.db.prepare("SELECT COUNT(*) as total, COALESCE(AVG(hit_count), 0) as avg_hits FROM knowledge_index WHERE enabled = 1").get();
88
+ const bySubType = {};
89
+ const subtypeRows = store.db.prepare("SELECT sub_type, COUNT(*) as cnt FROM knowledge_index WHERE enabled = 1 GROUP BY sub_type").all();
90
+ for (const r of subtypeRows) bySubType[r.sub_type] = r.cnt;
91
+ const topRows = store.db.prepare(`
92
+ SELECT id, file_path, content_hash, title, content, sub_type,
93
+ project_remote, project_path, project_name, branch,
94
+ created_at, updated_at, hit_count, last_accessed, enabled
95
+ FROM knowledge_index WHERE enabled = 1
96
+ ORDER BY hit_count DESC LIMIT 5
97
+ `).all();
98
+ return {
99
+ total: agg?.total ?? 0,
100
+ avgHitCount: agg?.avg_hits ?? 0,
101
+ bySubType,
102
+ topAccessed: topRows.map(mapRow)
103
+ };
104
+ }
105
+ function searchKnowledgeKeyword(store, query, limit) {
106
+ const escaped = escapeLIKEContains(query);
107
+ return store.db.prepare(`
108
+ SELECT id, file_path, content_hash, title, content, sub_type,
109
+ project_remote, project_path, project_name, branch,
110
+ created_at, updated_at, hit_count, last_accessed, enabled
111
+ FROM knowledge_index
112
+ WHERE enabled = 1 AND (content LIKE ? ESCAPE '\\' OR title LIKE ? ESCAPE '\\')
113
+ ORDER BY hit_count DESC LIMIT ?
114
+ `).all(escaped, escaped, limit).map(mapRow);
115
+ }
116
+ function getRecentDecisions(store, projectRemote, projectPath, sinceISO, limit) {
117
+ return store.db.prepare(`
118
+ SELECT title, content, created_at FROM knowledge_index
119
+ WHERE sub_type = 'decision'
120
+ AND project_remote = ? AND project_path = ?
121
+ AND created_at > ? AND enabled = 1
122
+ ORDER BY created_at DESC LIMIT ?
123
+ `).all(projectRemote, projectPath, sinceISO, limit).map((r) => ({
124
+ title: r.title,
125
+ content: r.content,
126
+ createdAt: r.created_at
127
+ }));
128
+ }
129
+ function deleteOrphanKnowledge(store, projectRemote, projectPath, branch, validFilePaths) {
130
+ const rows = store.db.prepare("SELECT id, file_path FROM knowledge_index WHERE project_remote = ? AND project_path = ? AND branch = ?").all(projectRemote, projectPath, branch);
131
+ let deleted = 0;
132
+ const delEmbed = store.db.prepare("DELETE FROM embeddings WHERE source = 'knowledge' AND source_id = ?");
133
+ const delKnowledge = store.db.prepare("DELETE FROM knowledge_index WHERE id = ?");
134
+ store.db.transaction(() => {
135
+ for (const row of rows) if (!validFilePaths.has(row.file_path)) {
136
+ delEmbed.run(row.id);
137
+ delKnowledge.run(row.id);
138
+ deleted++;
139
+ }
140
+ })();
141
+ return deleted;
142
+ }
143
+ function countKnowledge(store, projectRemote, projectPath) {
144
+ return store.db.prepare("SELECT COUNT(*) as cnt FROM knowledge_index WHERE project_remote = ? AND project_path = ? AND enabled = 1").get(projectRemote, projectPath).cnt;
145
+ }
146
+ function escapeLIKEContains(s) {
147
+ s = s.replaceAll("\\", "\\\\");
148
+ s = s.replaceAll("%", "\\%");
149
+ s = s.replaceAll("_", "\\_");
150
+ return `%${s}%`;
151
+ }
152
+ function mapRow(r) {
153
+ return {
154
+ id: r.id,
155
+ filePath: r.file_path,
156
+ contentHash: r.content_hash,
157
+ title: r.title,
158
+ content: r.content,
159
+ subType: r.sub_type,
160
+ projectRemote: r.project_remote,
161
+ projectPath: r.project_path,
162
+ projectName: r.project_name,
163
+ branch: r.branch,
164
+ createdAt: r.created_at,
165
+ updatedAt: r.updated_at,
166
+ hitCount: r.hit_count,
167
+ lastAccessed: r.last_accessed,
168
+ enabled: r.enabled === 1
169
+ };
170
+ }
171
+ //#endregion
172
+ export { getKnowledgeStats as a, incrementHitCount as c, searchKnowledgeKeyword as d, setKnowledgeEnabled as f, getKnowledgeByIDs as i, mapRow as l, deleteOrphanKnowledge as n, getPromotionCandidates as o, upsertKnowledge as p, getKnowledgeByID as r, getRecentDecisions as s, countKnowledge as t, promoteSubType as u };