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.
- package/README.ja.md +10 -8
- package/README.md +10 -8
- package/dist/audit-g6phLGMg.mjs +18 -0
- package/dist/cli.mjs +7 -7
- package/dist/{directives-ko2sX5BW.mjs → directives-DyHIJo9p.mjs} +1 -1
- package/dist/{dispatcher-BnBtA4bo.mjs → dispatcher-DE-9lhIv.mjs} +7 -7
- package/dist/{audit-6NSlW0Ut.mjs → epic-D9ksT1k7.mjs} +3 -16
- package/dist/{fts-QWdC_4Zv.mjs → fts-Buk8fkl1.mjs} +2 -2
- package/dist/{helpers-BnPVn8Nv.mjs → helpers-CvI9bVCq.mjs} +3 -3
- package/dist/knowledge-BgWoLpv7.mjs +172 -0
- package/dist/knowledge-extractor-DTO74tU1.mjs +456 -0
- package/dist/living-spec-jepeb0NQ.mjs +218 -0
- package/dist/{post-tool-gG8qG5Ee.mjs → post-tool-Dg6-F0_1.mjs} +31 -37
- package/dist/{pre-compact-CwvBi5op.mjs → pre-compact-B2G-Khc9.mjs} +7 -5
- package/dist/pre-tool-BwjDjdA-.mjs +54 -0
- package/dist/{spec-guard-BGwtCxgH.mjs → review-gate-BvV9OUnG.mjs} +46 -2
- package/dist/{server-Df8fib2F.mjs → server-BaPbUbPa.mjs} +7 -5
- package/dist/{server-CHdcikXe.mjs → server-DjHlrHQd.mjs} +663 -431
- package/dist/{session-start-dPl4lfZw.mjs → session-start-B036zTZK.mjs} +7 -6
- package/dist/{state-CdHO66SV.mjs → state-CM6iakfR.mjs} +25 -1
- package/dist/stop-_E6Cn7T8.mjs +30 -0
- package/dist/{knowledge-B0fsJ9SI.mjs → types-DFsKNXVY.mjs} +1 -172
- package/dist/{user-prompt-o9rlzB2I.mjs → user-prompt-B-FXw-Q3.mjs} +10 -8
- package/dist/{vectors-BDQP0gaa.mjs → vectors-DtWMZUgk.mjs} +1 -1
- package/package.json +1 -1
- package/dist/pre-tool-BMCYjtHT.mjs +0 -29
- package/dist/stop-NL5GdPNy.mjs +0 -21
- /package/dist/{embedder-BRG2Xpr4.mjs → embedder-CFkDPOku.mjs} +0 -0
- /package/dist/{project-Djp8-Djz.mjs → project-Cz-yOhrW.mjs} +0 -0
- /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。大規模なら
|
|
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
|
|
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
|
|
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** |
|
|
139
|
-
| **Stop** |
|
|
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** |
|
|
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 ->
|
|
260
|
-
|-- Stop ->
|
|
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?
|
|
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. **
|
|
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** | **
|
|
139
|
-
| **Stop** |
|
|
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) |
|
|
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 ->
|
|
260
|
-
|-- Stop ->
|
|
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-
|
|
373
|
-
const { Embedder } = await import("./embedder-
|
|
374
|
-
const { serveMCP } = await import("./server-
|
|
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-
|
|
401
|
-
const { Embedder } = await import("./embedder-
|
|
402
|
-
const { startDashboard } = await import("./server-
|
|
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-
|
|
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 {
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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 {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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-
|
|
3
|
-
import { n as deserializeFloat32, t as cosineSimilarity } from "./vectors-
|
|
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-
|
|
3
|
-
import { r as vectorSearchKnowledge } from "./vectors-
|
|
4
|
-
import { i as subTypeHalfLife, n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-
|
|
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 };
|