claude-alfred 0.3.3 → 0.3.5
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 +2 -2
- package/README.md +2 -2
- package/dist/{audit-09Rf9Awg.mjs → audit-g6phLGMg.mjs} +1 -1
- package/dist/cli.mjs +7 -7
- package/dist/{directives-Bm6w4JNk.mjs → directives-BnbWxhap.mjs} +1 -1
- package/dist/{dispatcher-DZUzmm1Y.mjs → dispatcher-M-Rc8GYD.mjs} +7 -7
- package/dist/{epic-Cde2RANp.mjs → epic-D9ksT1k7.mjs} +1 -1
- package/dist/{fts-Ck8wjOIE.mjs → fts-Buk8fkl1.mjs} +2 -2
- package/dist/{helpers-rMTa9O_h.mjs → helpers-CvI9bVCq.mjs} +3 -3
- package/dist/knowledge-BgWoLpv7.mjs +172 -0
- package/dist/{knowledge-extractor-b9bel965.mjs → knowledge-extractor-DTO74tU1.mjs} +6 -6
- package/dist/living-spec-jepeb0NQ.mjs +218 -0
- package/dist/{post-tool-DdovyPpK.mjs → post-tool-tKu9d-gX.mjs} +19 -10
- package/dist/{pre-compact-CEwj7z9W.mjs → pre-compact-BmW-T36K.mjs} +7 -6
- package/dist/{pre-tool-MD1OPzGy.mjs → pre-tool-DEFSi2eU.mjs} +3 -3
- package/dist/{review-gate-Cqj-CmHP.mjs → review-gate-IIPdo-3r.mjs} +2 -2
- package/dist/{server-BPnToh8m.mjs → server-B0LsUetd.mjs} +11 -10
- package/dist/{server-BwAuDMm3.mjs → server-BaPbUbPa.mjs} +6 -5
- package/dist/{session-start-CxYp9xkq.mjs → session-start-CbBbiQR5.mjs} +9 -6
- package/dist/{state-C0v-bfFb.mjs → state-Dqi1EiD0.mjs} +19 -1
- package/dist/{stop-Cq9sP57g.mjs → stop-ByE7qdsR.mjs} +9 -4
- package/dist/{knowledge-kk6LxnnT.mjs → types-DFsKNXVY.mjs} +1 -171
- package/dist/{user-prompt-uwxFPXu1.mjs → user-prompt-E1Y0wuSR.mjs} +15 -8
- package/dist/{vectors-RRTv0qO0.mjs → vectors-DtWMZUgk.mjs} +1 -1
- package/package.json +1 -1
- /package/dist/{embedder-CuUqZpze.mjs → embedder-CFkDPOku.mjs} +0 -0
- /package/dist/{project-CpgK3fwQ.mjs → project-Cz-yOhrW.mjs} +0 -0
- /package/dist/{store-DenWQkPw.mjs → store-D4fokoGA.mjs} +0 -0
package/README.ja.md
CHANGED
|
@@ -135,7 +135,7 @@ npm update -g claude-alfred # CLI、hooks、MCP サーバー、ダッシ
|
|
|
135
135
|
|----------|------|
|
|
136
136
|
| SessionStart | 仕様コンテキスト復元 + ナレッジ同期 + 1%ルール(スキル発動促進)+ 成熟度適応 |
|
|
137
137
|
| PreCompact | スナップショット保存 + 決定抽出 + エピック進捗同期 + 調査パターン検出 |
|
|
138
|
-
| UserPromptSubmit | セマンティック検索 + スキルナッジ + **spec承認ゲート**(未承認 M/L/XL に DIRECTIVE) |
|
|
138
|
+
| UserPromptSubmit | セマンティック検索 + スキルナッジ + **spec承認ゲート**(未承認 M/L/XL に DIRECTIVE)+ **並列開発ガード**(別タスク検出時に WARNING) |
|
|
139
139
|
| PostToolUse | エラー検出 + Next Steps 自動チェック + ドリフト検出 + コミット時決定保存 |
|
|
140
140
|
| **PreToolUse** | **3層 enforcement**: (1) review gate(spec/wave レビュー完了まで)、(2) intent guard(spec なし実装ブロック)、(3) approval gate(未承認 M/L/XL)。`.alfred/` 編集は常に許可 |
|
|
141
141
|
| **Stop** | review gate → ブロック。その他 → コンテキストリマインド(ブロックなし) |
|
|
@@ -256,7 +256,7 @@ alfred dashboard --url-only # URLだけ出力
|
|
|
256
256
|
Hook(見えない)
|
|
257
257
|
|-- SessionStart -> コンテキスト復元、1%ルール、成熟度適応
|
|
258
258
|
|-- PreCompact -> スナップショット保存、決定抽出、エピック進捗
|
|
259
|
-
|-- UserPromptSubmit -> ベクトル検索 + FTS5 + スキルナッジ + spec承認チェック
|
|
259
|
+
|-- UserPromptSubmit -> ベクトル検索 + FTS5 + スキルナッジ + spec承認チェック + 並列開発ガード
|
|
260
260
|
|-- PostToolUse -> エラー検出、Next Steps自動チェック、ドリフト検出
|
|
261
261
|
|-- PreToolUse -> review gate + intent guard + approval gate(3層 enforcement)
|
|
262
262
|
|-- Stop -> review gate ブロック + コンテキストリマインド(非ブロック)
|
package/README.md
CHANGED
|
@@ -135,7 +135,7 @@ Run automatically. You don't touch these.
|
|
|
135
135
|
|-------|-------------|
|
|
136
136
|
| SessionStart | Restores spec context, syncs knowledge index, adapts injection depth to project maturity, 1% rule skill activation |
|
|
137
137
|
| PreCompact | Extracts decisions, saves chapter snapshots, syncs epic progress, detects research patterns |
|
|
138
|
-
| UserPromptSubmit | Semantic search + file context boost + **skill nudge** + **spec approval gate** (blocks implement intent on unapproved M/L/XL specs) |
|
|
138
|
+
| UserPromptSubmit | Semantic search + file context boost + **skill nudge** + **spec approval gate** (blocks implement intent on unapproved M/L/XL specs) + **parallel dev guard** (WARNING when active spec exists but new task detected) |
|
|
139
139
|
| PostToolUse | Detects Bash errors + searches memory. After commits: spec drift detection + auto-save decisions. Edit/Write: auto-check Next Steps progress |
|
|
140
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
141
|
| **Stop** | Review gate → block. Other incomplete items → context reminder (no block) |
|
|
@@ -256,7 +256,7 @@ You
|
|
|
256
256
|
Hooks (invisible)
|
|
257
257
|
|-- SessionStart -> restore context, sync knowledge, 1% rule, adapt to project maturity
|
|
258
258
|
|-- PreCompact -> save snapshots, extract decisions, epic progress
|
|
259
|
-
|-- UserPromptSubmit -> vector search + FTS5 + skill nudge + spec approval check
|
|
259
|
+
|-- UserPromptSubmit -> vector search + FTS5 + skill nudge + spec approval check + parallel dev guard
|
|
260
260
|
|-- PostToolUse -> detect errors, auto-check Next Steps, drift detection
|
|
261
261
|
|-- PreToolUse -> review-gate + intent guard + approval gate (3-layer enforcement)
|
|
262
262
|
|-- Stop -> review-gate block + context reminders (non-blocking)
|
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-B0LsUetd.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-M-Rc8GYD.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-CbBbiQR5.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-BmW-T36K.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-E1Y0wuSR.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-tKu9d-gX.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-DEFSi2eU.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-ByE7qdsR.mjs");
|
|
113
113
|
await stop(ev);
|
|
114
114
|
}
|
|
115
115
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { m as require_dist, n as VALID_SLUG } from "./types-DFsKNXVY.mjs";
|
|
3
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
|
|
@@ -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 };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as detectKnowledgeConflicts } from "./fts-
|
|
5
|
-
import { t as detectProject } from "./project-
|
|
6
|
-
import { n as trackHitCounts, r as truncate, t as searchPipeline } from "./helpers-
|
|
2
|
+
import { t as appendAudit } from "./audit-g6phLGMg.mjs";
|
|
3
|
+
import { a as getKnowledgeStats, o as getPromotionCandidates, p as upsertKnowledge, r as getKnowledgeByID, u as promoteSubType } from "./knowledge-BgWoLpv7.mjs";
|
|
4
|
+
import { t as detectKnowledgeConflicts } from "./fts-Buk8fkl1.mjs";
|
|
5
|
+
import { t as detectProject } from "./project-Cz-yOhrW.mjs";
|
|
6
|
+
import { n as trackHitCounts, r as truncate, t as searchPipeline } from "./helpers-CvI9bVCq.mjs";
|
|
7
7
|
import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
@@ -245,7 +245,7 @@ async function ledgerSave(store, emb, params) {
|
|
|
245
245
|
const model = emb.model;
|
|
246
246
|
const embText = `${params.title} ${params.context_text ?? ""} ${params.decision ?? params.pattern ?? params.text ?? ""}`;
|
|
247
247
|
emb.embedForStorage(embText).then(async (vec) => {
|
|
248
|
-
const { insertEmbedding } = await import("./vectors-
|
|
248
|
+
const { insertEmbedding } = await import("./vectors-DtWMZUgk.mjs").then((n) => n.i);
|
|
249
249
|
insertEmbedding(store, "knowledge", dbId, model, vec);
|
|
250
250
|
}).catch((err) => {
|
|
251
251
|
console.error(`[alfred] embedding failed for ${dbId}: ${err}`);
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { o as readActive, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { t as appendAudit } from "./audit-g6phLGMg.mjs";
|
|
4
|
+
import { basename, dirname, extname } from "node:path";
|
|
5
|
+
import { execSync } from "node:child_process";
|
|
6
|
+
//#region src/hooks/lang-filter.ts
|
|
7
|
+
/**
|
|
8
|
+
* Language filter for Living Spec auto-append.
|
|
9
|
+
* Determines which source files should be tracked in design.md.
|
|
10
|
+
*/
|
|
11
|
+
const LANGUAGES = [
|
|
12
|
+
{
|
|
13
|
+
extensions: [
|
|
14
|
+
".js",
|
|
15
|
+
".jsx",
|
|
16
|
+
".ts",
|
|
17
|
+
".tsx",
|
|
18
|
+
".mjs",
|
|
19
|
+
".mts"
|
|
20
|
+
],
|
|
21
|
+
excludeSuffixes: [
|
|
22
|
+
".test.ts",
|
|
23
|
+
".spec.ts",
|
|
24
|
+
".test.js",
|
|
25
|
+
".spec.js",
|
|
26
|
+
".test.tsx",
|
|
27
|
+
".spec.tsx",
|
|
28
|
+
".test.mts",
|
|
29
|
+
".spec.mts",
|
|
30
|
+
".d.ts",
|
|
31
|
+
".min.js",
|
|
32
|
+
".bundle.js"
|
|
33
|
+
],
|
|
34
|
+
excludePrefixes: []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
extensions: [".py"],
|
|
38
|
+
excludeSuffixes: [
|
|
39
|
+
"_test.py",
|
|
40
|
+
"_pb2.py",
|
|
41
|
+
"_pb2_grpc.py"
|
|
42
|
+
],
|
|
43
|
+
excludePrefixes: ["test_", "conftest"]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
extensions: [".go"],
|
|
47
|
+
excludeSuffixes: [
|
|
48
|
+
"_test.go",
|
|
49
|
+
"_gen.go",
|
|
50
|
+
".pb.go",
|
|
51
|
+
"_mock.go",
|
|
52
|
+
"_string.go"
|
|
53
|
+
],
|
|
54
|
+
excludePrefixes: []
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
extensions: [".rb"],
|
|
58
|
+
excludeSuffixes: ["_test.rb", "_spec.rb"],
|
|
59
|
+
excludePrefixes: []
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
const DIR_EXCLUSIONS = [
|
|
63
|
+
"vendor/",
|
|
64
|
+
"node_modules/",
|
|
65
|
+
".alfred/",
|
|
66
|
+
"dist/",
|
|
67
|
+
"build/",
|
|
68
|
+
"__pycache__/",
|
|
69
|
+
".venv/",
|
|
70
|
+
"plugin/"
|
|
71
|
+
];
|
|
72
|
+
/** Check if a source file should be auto-appended to design.md. */
|
|
73
|
+
function shouldAutoAppend(filePath) {
|
|
74
|
+
for (const dir of DIR_EXCLUSIONS) if (filePath.startsWith(dir) || filePath.includes(`/${dir}`)) return false;
|
|
75
|
+
const ext = extname(filePath);
|
|
76
|
+
const name = basename(filePath);
|
|
77
|
+
for (const lang of LANGUAGES) {
|
|
78
|
+
if (!lang.extensions.includes(ext)) continue;
|
|
79
|
+
for (const suffix of lang.excludeSuffixes) if (filePath.endsWith(suffix)) return false;
|
|
80
|
+
for (const prefix of lang.excludePrefixes) if (name.startsWith(prefix)) return false;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/hooks/living-spec.ts
|
|
87
|
+
/**
|
|
88
|
+
* Living Spec auto-append: after git commit, detect changed source files
|
|
89
|
+
* and append them to the matching component section in design.md.
|
|
90
|
+
*
|
|
91
|
+
* Multi-language: JS/TS, Python, Go, Ruby.
|
|
92
|
+
* Fail-open: all errors silently ignored.
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* Run Living Spec auto-append after a successful git commit.
|
|
96
|
+
* Returns set of auto-appended file paths (for drift detection exclusion).
|
|
97
|
+
*/
|
|
98
|
+
function handleLivingSpec(cwd) {
|
|
99
|
+
const appended = /* @__PURE__ */ new Set();
|
|
100
|
+
try {
|
|
101
|
+
const slug = readActive(cwd);
|
|
102
|
+
const sd = new SpecDir(cwd, slug);
|
|
103
|
+
let designContent;
|
|
104
|
+
try {
|
|
105
|
+
designContent = sd.readFile("design.md");
|
|
106
|
+
} catch {
|
|
107
|
+
return appended;
|
|
108
|
+
}
|
|
109
|
+
const changedFiles = extractChangedFiles(cwd);
|
|
110
|
+
if (changedFiles.length === 0) return appended;
|
|
111
|
+
const sourceFiles = changedFiles.filter(shouldAutoAppend);
|
|
112
|
+
if (sourceFiles.length === 0) return appended;
|
|
113
|
+
const componentMap = parseDesignFileRefs(designContent);
|
|
114
|
+
if (componentMap.size === 0) return appended;
|
|
115
|
+
let updatedContent = designContent;
|
|
116
|
+
const appendedComponents = [];
|
|
117
|
+
for (const file of sourceFiles) {
|
|
118
|
+
if (designContent.includes(`\`${file}\``)) continue;
|
|
119
|
+
const component = matchComponent(file, componentMap);
|
|
120
|
+
if (!component) continue;
|
|
121
|
+
const result = appendFileToComponent(updatedContent, component, file);
|
|
122
|
+
if (result) {
|
|
123
|
+
updatedContent = result;
|
|
124
|
+
appended.add(file);
|
|
125
|
+
appendedComponents.push(`${component}:${file}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (appended.size > 0) {
|
|
129
|
+
sd.writeFile("design.md", updatedContent);
|
|
130
|
+
appendAudit(cwd, {
|
|
131
|
+
action: "living-spec.update",
|
|
132
|
+
target: slug,
|
|
133
|
+
detail: JSON.stringify({
|
|
134
|
+
files: [...appended],
|
|
135
|
+
components: appendedComponents
|
|
136
|
+
})
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
} catch {}
|
|
140
|
+
return appended;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract changed files from the last git commit.
|
|
144
|
+
* 500ms timeout, fail-open.
|
|
145
|
+
*/
|
|
146
|
+
function extractChangedFiles(cwd) {
|
|
147
|
+
try {
|
|
148
|
+
return execSync("git diff --name-only HEAD~1", {
|
|
149
|
+
cwd,
|
|
150
|
+
timeout: 500,
|
|
151
|
+
encoding: "utf-8",
|
|
152
|
+
stdio: [
|
|
153
|
+
"ignore",
|
|
154
|
+
"pipe",
|
|
155
|
+
"ignore"
|
|
156
|
+
]
|
|
157
|
+
}).split("\n").map((f) => f.trim()).filter(Boolean);
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Parse design.md for component → file references.
|
|
164
|
+
* Looks for `### Component: Name` headings with `**File**: \`path\`` lines.
|
|
165
|
+
*/
|
|
166
|
+
function parseDesignFileRefs(content) {
|
|
167
|
+
const map = /* @__PURE__ */ new Map();
|
|
168
|
+
let currentComponent = "";
|
|
169
|
+
for (const line of content.split("\n")) {
|
|
170
|
+
const compMatch = line.match(/^###\s+(?:Component:\s*)?(.+)/);
|
|
171
|
+
if (compMatch) {
|
|
172
|
+
currentComponent = compMatch[1].trim();
|
|
173
|
+
if (!map.has(currentComponent)) map.set(currentComponent, []);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (currentComponent) {
|
|
177
|
+
const fileMatch = line.match(/\*\*File\*\*:\s*`([^`]+)`/);
|
|
178
|
+
if (fileMatch) map.get(currentComponent).push(fileMatch[1]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return map;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Match a file to a component by comparing directory paths.
|
|
185
|
+
* Returns component name or null.
|
|
186
|
+
*/
|
|
187
|
+
function matchComponent(filePath, componentMap) {
|
|
188
|
+
const fileDir = dirname(filePath);
|
|
189
|
+
for (const [component, files] of componentMap) for (const existing of files) if (dirname(existing) === fileDir) return component;
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Append a file reference after the last **File**: line in a component section.
|
|
194
|
+
* Returns updated content or null if unable to insert.
|
|
195
|
+
*/
|
|
196
|
+
function appendFileToComponent(content, component, filePath) {
|
|
197
|
+
const lines = content.split("\n");
|
|
198
|
+
const newLine = `- **File**: \`${filePath}\` <!-- auto-added: ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
199
|
+
let componentIdx = -1;
|
|
200
|
+
for (let i = 0; i < lines.length; i++) {
|
|
201
|
+
const line = lines[i];
|
|
202
|
+
if (line.match(/^###\s+/) && (line.includes(`Component: ${component}`) || line.includes(component))) {
|
|
203
|
+
componentIdx = i;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (componentIdx === -1) return null;
|
|
208
|
+
let lastFileIdx = -1;
|
|
209
|
+
for (let i = componentIdx + 1; i < lines.length; i++) {
|
|
210
|
+
if (lines[i].match(/^##/)) break;
|
|
211
|
+
if (lines[i].includes("**File**:")) lastFileIdx = i;
|
|
212
|
+
}
|
|
213
|
+
if (lastFileIdx === -1) return null;
|
|
214
|
+
lines.splice(lastFileIdx + 1, 0, newLine);
|
|
215
|
+
return lines.join("\n");
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { handleLivingSpec };
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import "./audit-
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import { r as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
2
|
+
import { o as readActive, s as readActiveState, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import "./audit-g6phLGMg.mjs";
|
|
4
|
+
import { o as getPromotionCandidates, p as upsertKnowledge, u as promoteSubType } from "./knowledge-BgWoLpv7.mjs";
|
|
5
|
+
import { n as searchKnowledgeFTS, t as detectKnowledgeConflicts } from "./fts-Buk8fkl1.mjs";
|
|
6
|
+
import { t as detectProject } from "./project-Cz-yOhrW.mjs";
|
|
7
|
+
import { i as notifyUser, r as extractSection } from "./dispatcher-M-Rc8GYD.mjs";
|
|
8
|
+
import { r as truncate } from "./helpers-CvI9bVCq.mjs";
|
|
9
|
+
import { n as extractReviewFindings, r as saveKnowledgeEntries } from "./knowledge-extractor-DTO74tU1.mjs";
|
|
10
|
+
import { openDefaultCached } from "./store-D4fokoGA.mjs";
|
|
11
|
+
import { t as emitDirectives } from "./directives-BnbWxhap.mjs";
|
|
12
|
+
import { a as readStateText, n as addWorkedSlug, u as writeStateText } from "./state-Dqi1EiD0.mjs";
|
|
12
13
|
//#region src/hooks/post-tool.ts
|
|
13
14
|
function readExploreCount(cwd) {
|
|
14
15
|
return parseInt(readStateText(cwd, "explore-count", "0"), 10) || 0;
|
|
@@ -40,6 +41,10 @@ async function postToolUse(ev, signal) {
|
|
|
40
41
|
const input = ev.tool_input;
|
|
41
42
|
const filePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
42
43
|
if (filePath) autoCheckNextSteps(ev.cwd, filePath);
|
|
44
|
+
try {
|
|
45
|
+
const slug = readActive(ev.cwd);
|
|
46
|
+
addWorkedSlug(ev.cwd, slug);
|
|
47
|
+
} catch {}
|
|
43
48
|
}
|
|
44
49
|
if ([
|
|
45
50
|
"Edit",
|
|
@@ -66,6 +71,10 @@ async function handleBashResult(ev, items, signal) {
|
|
|
66
71
|
const commandStr = typeof ev.tool_input === "object" && ev.tool_input !== null ? ev.tool_input.command ?? "" : "";
|
|
67
72
|
autoCheckNextSteps(ev.cwd, `${stdout}\n${commandStr}`);
|
|
68
73
|
if (isGitCommit(stdout) && !signal.aborted) {
|
|
74
|
+
try {
|
|
75
|
+
const { handleLivingSpec } = await import("./living-spec-jepeb0NQ.mjs");
|
|
76
|
+
handleLivingSpec(ev.cwd);
|
|
77
|
+
} catch {}
|
|
69
78
|
await checkKnowledgeConflicts(items);
|
|
70
79
|
saveKnowledgeOnCommit(ev.cwd);
|
|
71
80
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { o as syncTaskStatus } from "./epic-
|
|
4
|
-
import { t as appendAudit } from "./audit-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { f as verifyReviewFile, l as reviewStatusFor, o as readActive, r as completeTask, s as readActiveState, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { o as syncTaskStatus } from "./epic-D9ksT1k7.mjs";
|
|
4
|
+
import { t as appendAudit } from "./audit-g6phLGMg.mjs";
|
|
5
|
+
import { p as upsertKnowledge } from "./knowledge-BgWoLpv7.mjs";
|
|
6
|
+
import { t as detectProject } from "./project-Cz-yOhrW.mjs";
|
|
7
|
+
import { i as notifyUser } from "./dispatcher-M-Rc8GYD.mjs";
|
|
8
|
+
import { openDefaultCached } from "./store-D4fokoGA.mjs";
|
|
8
9
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
//#region src/hooks/pre-compact.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./dispatcher-
|
|
3
|
-
import {
|
|
4
|
-
import { l as isSpecFilePath, n as isGateActive, s as denyTool, u as tryReadActiveSpec } from "./review-gate-
|
|
2
|
+
import "./dispatcher-M-Rc8GYD.mjs";
|
|
3
|
+
import { r as readLastIntent, t as IMPLEMENT_INTENTS } from "./state-Dqi1EiD0.mjs";
|
|
4
|
+
import { l as isSpecFilePath, n as isGateActive, s as denyTool, u as tryReadActiveSpec } from "./review-gate-IIPdo-3r.mjs";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
//#region src/hooks/pre-tool.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as extractSection } from "./dispatcher-
|
|
3
|
-
import {
|
|
2
|
+
import { r as extractSection } from "./dispatcher-M-Rc8GYD.mjs";
|
|
3
|
+
import { i as readStateJSON, l as writeStateJSON } from "./state-Dqi1EiD0.mjs";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
//#region src/hooks/spec-guard.ts
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as removeEpic, c as unlinkTaskFromAllEpics, i as nextActionable, n as initEpic, o as syncTaskStatus, r as listAllEpics, s as topologicalOrder, t as EpicDir } from "./epic-
|
|
4
|
-
import { t as appendAudit } from "./audit-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import "./
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import "./
|
|
11
|
-
import
|
|
2
|
+
import { _ as __toESM, a as filesForSize, c as removeTask, d as switchActive, f as verifyReviewFile, h as __commonJSMin, i as detectSize, l as reviewStatusFor, n as VALID_SLUG, o as readActive, p as writeActiveState, r as completeTask, s as readActiveState, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { a as removeEpic, c as unlinkTaskFromAllEpics, i as nextActionable, n as initEpic, o as syncTaskStatus, r as listAllEpics, s as topologicalOrder, t as EpicDir } from "./epic-D9ksT1k7.mjs";
|
|
4
|
+
import { t as appendAudit } from "./audit-g6phLGMg.mjs";
|
|
5
|
+
import { i as getKnowledgeByIDs } from "./knowledge-BgWoLpv7.mjs";
|
|
6
|
+
import { r as vectorSearchKnowledge } from "./vectors-DtWMZUgk.mjs";
|
|
7
|
+
import { n as searchKnowledgeFTS, r as subTypeBoost } from "./fts-Buk8fkl1.mjs";
|
|
8
|
+
import "./dispatcher-M-Rc8GYD.mjs";
|
|
9
|
+
import { r as truncate } from "./helpers-CvI9bVCq.mjs";
|
|
10
|
+
import { i as handleLedger, r as saveKnowledgeEntries, t as extractPatterns } from "./knowledge-extractor-DTO74tU1.mjs";
|
|
11
|
+
import "./state-Dqi1EiD0.mjs";
|
|
12
|
+
import { i as writeReviewGate, r as readReviewGate, t as clearReviewGate } from "./review-gate-IIPdo-3r.mjs";
|
|
12
13
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
14
|
import { join, resolve } from "node:path";
|
|
14
15
|
import process$1 from "node:process";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { r as listAllEpics } from "./epic-
|
|
4
|
-
import { t as appendAudit } from "./audit-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { a as filesForSize, f as verifyReviewFile, n as VALID_SLUG, p as writeActiveState, r as completeTask, s as readActiveState, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { r as listAllEpics } from "./epic-D9ksT1k7.mjs";
|
|
4
|
+
import { t as appendAudit } from "./audit-g6phLGMg.mjs";
|
|
5
|
+
import { a as getKnowledgeStats, f as setKnowledgeEnabled } from "./knowledge-BgWoLpv7.mjs";
|
|
6
|
+
import { n as searchKnowledgeFTS } from "./fts-Buk8fkl1.mjs";
|
|
7
|
+
import { t as detectProject } from "./project-Cz-yOhrW.mjs";
|
|
7
8
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
4
|
-
import {
|
|
5
|
-
import { r as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { o as readActive, s as readActiveState, t as SpecDir } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { n as deleteOrphanKnowledge, p as upsertKnowledge, s as getRecentDecisions, t as countKnowledge } from "./knowledge-BgWoLpv7.mjs";
|
|
4
|
+
import { t as detectProject } from "./project-Cz-yOhrW.mjs";
|
|
5
|
+
import { i as notifyUser, r as extractSection } from "./dispatcher-M-Rc8GYD.mjs";
|
|
6
|
+
import { r as truncate } from "./helpers-CvI9bVCq.mjs";
|
|
7
|
+
import { openDefaultCached } from "./store-D4fokoGA.mjs";
|
|
8
|
+
import { t as emitDirectives } from "./directives-BnbWxhap.mjs";
|
|
9
|
+
import { s as resetWorkedSlugs } from "./state-Dqi1EiD0.mjs";
|
|
8
10
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
9
11
|
import { join } from "node:path";
|
|
10
12
|
//#region src/hooks/session-start.ts
|
|
11
13
|
async function sessionStart(ev, _signal) {
|
|
12
14
|
if (!ev.cwd) return;
|
|
15
|
+
if (existsSync(join(ev.cwd, ".alfred"))) resetWorkedSlugs(ev.cwd);
|
|
13
16
|
let store;
|
|
14
17
|
try {
|
|
15
18
|
store = openDefaultCached();
|
|
@@ -73,5 +73,23 @@ function readLastIntent(cwd) {
|
|
|
73
73
|
if (Date.now() - data.timestamp > INTENT_EXPIRY_MS) return null;
|
|
74
74
|
return data.intent;
|
|
75
75
|
}
|
|
76
|
+
const WORKED_SLUGS_FILE = "worked-slugs.json";
|
|
77
|
+
/** Read the list of spec slugs worked on in this session. */
|
|
78
|
+
function readWorkedSlugs(cwd) {
|
|
79
|
+
const data = readStateJSON(cwd, WORKED_SLUGS_FILE, []);
|
|
80
|
+
return Array.isArray(data) ? data : [];
|
|
81
|
+
}
|
|
82
|
+
/** Add a slug to the worked-slugs list (deduplicates). */
|
|
83
|
+
function addWorkedSlug(cwd, slug) {
|
|
84
|
+
const slugs = readWorkedSlugs(cwd);
|
|
85
|
+
if (!slugs.includes(slug)) {
|
|
86
|
+
slugs.push(slug);
|
|
87
|
+
writeStateJSON(cwd, WORKED_SLUGS_FILE, slugs);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Reset worked-slugs at session start. */
|
|
91
|
+
function resetWorkedSlugs(cwd) {
|
|
92
|
+
writeStateJSON(cwd, WORKED_SLUGS_FILE, []);
|
|
93
|
+
}
|
|
76
94
|
//#endregion
|
|
77
|
-
export {
|
|
95
|
+
export { readStateText as a, writeLastIntent as c, readStateJSON as i, writeStateJSON as l, addWorkedSlug as n, readWorkedSlugs as o, readLastIntent as r, resetWorkedSlugs as s, IMPLEMENT_INTENTS as t, writeStateText as u };
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import "./state-
|
|
4
|
-
import { a as blockStop, c as hasUncheckedSelfReview, n as isGateActive, o as countUncheckedNextSteps, u as tryReadActiveSpec } from "./review-gate-
|
|
2
|
+
import "./dispatcher-M-Rc8GYD.mjs";
|
|
3
|
+
import { o as readWorkedSlugs } from "./state-Dqi1EiD0.mjs";
|
|
4
|
+
import { a as blockStop, c as hasUncheckedSelfReview, n as isGateActive, o as countUncheckedNextSteps, u as tryReadActiveSpec } from "./review-gate-IIPdo-3r.mjs";
|
|
5
5
|
//#region src/hooks/stop.ts
|
|
6
6
|
/**
|
|
7
7
|
* Stop handler:
|
|
8
8
|
* - review-gate active → BLOCK (hard enforcement)
|
|
9
9
|
* - unchecked Next Steps / self-review / incomplete spec → CONTEXT reminder (no block)
|
|
10
|
+
* - Session-scoped: only reminds about specs worked on in this session (via worked-slugs).
|
|
11
|
+
* Fallback: if no worked-slugs recorded (read-only session), uses current primary.
|
|
10
12
|
* DEC-4: stop_hook_active=true → always allow (infinite loop prevention).
|
|
11
13
|
*/
|
|
12
14
|
async function stop(ev) {
|
|
@@ -18,12 +20,15 @@ async function stop(ev) {
|
|
|
18
20
|
}
|
|
19
21
|
const spec = tryReadActiveSpec(ev.cwd);
|
|
20
22
|
if (!spec || spec.status === "completed") return;
|
|
23
|
+
const workedSlugs = ev.cwd ? readWorkedSlugs(ev.cwd) : [];
|
|
24
|
+
if (workedSlugs.length > 0 && !workedSlugs.includes(spec.slug)) return;
|
|
21
25
|
const reminders = [];
|
|
22
26
|
const unchecked = countUncheckedNextSteps(ev.cwd, spec.slug);
|
|
23
27
|
if (unchecked > 0) reminders.push(`${unchecked} unchecked Next Steps in spec '${spec.slug}'`);
|
|
24
28
|
if (hasUncheckedSelfReview(ev.cwd, spec.slug)) reminders.push("Self-review not completed");
|
|
25
29
|
reminders.push("When done, call `dossier action=complete` to close the spec");
|
|
26
|
-
|
|
30
|
+
const msg = `[CONTEXT] Spec '${spec.slug}' reminders: ${reminders.join("; ")}`;
|
|
31
|
+
process.stdout.write(`${JSON.stringify({ systemMessage: msg })}\n`);
|
|
27
32
|
}
|
|
28
33
|
//#endregion
|
|
29
34
|
export { stop };
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { basename, join } from "node:path";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
5
|
//#region \0rolldown/runtime.js
|
|
7
6
|
var __create = Object.create;
|
|
8
7
|
var __defProp = Object.defineProperty;
|
|
@@ -6872,173 +6871,4 @@ function removeTask(projectPath, taskSlug) {
|
|
|
6872
6871
|
return false;
|
|
6873
6872
|
}
|
|
6874
6873
|
//#endregion
|
|
6875
|
-
|
|
6876
|
-
function contentHash(content) {
|
|
6877
|
-
return createHash("sha256").update(content).digest("hex");
|
|
6878
|
-
}
|
|
6879
|
-
function upsertKnowledge(store, row) {
|
|
6880
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6881
|
-
if (!row.createdAt) row.createdAt = now;
|
|
6882
|
-
row.updatedAt = now;
|
|
6883
|
-
row.contentHash = contentHash(row.content);
|
|
6884
|
-
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);
|
|
6885
|
-
if (existing && existing.content_hash === row.contentHash) {
|
|
6886
|
-
row.id = existing.id;
|
|
6887
|
-
return {
|
|
6888
|
-
id: existing.id,
|
|
6889
|
-
changed: false
|
|
6890
|
-
};
|
|
6891
|
-
}
|
|
6892
|
-
const result = store.db.prepare(`
|
|
6893
|
-
INSERT INTO knowledge_index
|
|
6894
|
-
(file_path, content_hash, title, content, sub_type,
|
|
6895
|
-
project_remote, project_path, project_name, branch,
|
|
6896
|
-
created_at, updated_at, hit_count, last_accessed, enabled)
|
|
6897
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, '', 1)
|
|
6898
|
-
ON CONFLICT(project_remote, project_path, file_path) DO UPDATE SET
|
|
6899
|
-
content_hash = excluded.content_hash,
|
|
6900
|
-
title = excluded.title,
|
|
6901
|
-
content = excluded.content,
|
|
6902
|
-
sub_type = excluded.sub_type,
|
|
6903
|
-
project_name = excluded.project_name,
|
|
6904
|
-
branch = excluded.branch,
|
|
6905
|
-
updated_at = excluded.updated_at
|
|
6906
|
-
`).run(row.filePath, row.contentHash, row.title, row.content, row.subType, row.projectRemote, row.projectPath, row.projectName, row.branch, row.createdAt, row.updatedAt);
|
|
6907
|
-
const id = Number(result.lastInsertRowid);
|
|
6908
|
-
row.id = id;
|
|
6909
|
-
return {
|
|
6910
|
-
id,
|
|
6911
|
-
changed: true
|
|
6912
|
-
};
|
|
6913
|
-
}
|
|
6914
|
-
function getKnowledgeByID(store, id) {
|
|
6915
|
-
const row = store.db.prepare(`
|
|
6916
|
-
SELECT id, file_path, content_hash, title, content, sub_type,
|
|
6917
|
-
project_remote, project_path, project_name, branch,
|
|
6918
|
-
created_at, updated_at, hit_count, last_accessed, enabled
|
|
6919
|
-
FROM knowledge_index WHERE id = ?
|
|
6920
|
-
`).get(id);
|
|
6921
|
-
return row ? mapRow(row) : void 0;
|
|
6922
|
-
}
|
|
6923
|
-
function getKnowledgeByIDs(store, ids) {
|
|
6924
|
-
if (ids.length === 0) return [];
|
|
6925
|
-
const placeholders = ids.map(() => "?").join(",");
|
|
6926
|
-
return store.db.prepare(`
|
|
6927
|
-
SELECT id, file_path, content_hash, title, content, sub_type,
|
|
6928
|
-
project_remote, project_path, project_name, branch,
|
|
6929
|
-
created_at, updated_at, hit_count, last_accessed, enabled
|
|
6930
|
-
FROM knowledge_index WHERE id IN (${placeholders})
|
|
6931
|
-
`).all(...ids).map(mapRow);
|
|
6932
|
-
}
|
|
6933
|
-
function setKnowledgeEnabled(store, id, enabled) {
|
|
6934
|
-
store.db.prepare("UPDATE knowledge_index SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
|
|
6935
|
-
}
|
|
6936
|
-
function incrementHitCount(store, ids) {
|
|
6937
|
-
if (ids.length === 0) return;
|
|
6938
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6939
|
-
const placeholders = ids.map(() => "?").join(",");
|
|
6940
|
-
store.db.prepare(`UPDATE knowledge_index SET hit_count = hit_count + 1, last_accessed = ?
|
|
6941
|
-
WHERE id IN (${placeholders})`).run(now, ...ids);
|
|
6942
|
-
}
|
|
6943
|
-
function promoteSubType(store, id, newSubType) {
|
|
6944
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6945
|
-
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`);
|
|
6946
|
-
}
|
|
6947
|
-
function getPromotionCandidates(store) {
|
|
6948
|
-
return store.db.prepare(`
|
|
6949
|
-
SELECT id, file_path, content_hash, title, content, sub_type,
|
|
6950
|
-
project_remote, project_path, project_name, branch,
|
|
6951
|
-
created_at, updated_at, hit_count, last_accessed, enabled
|
|
6952
|
-
FROM knowledge_index
|
|
6953
|
-
WHERE enabled = 1
|
|
6954
|
-
AND (sub_type = 'pattern' AND hit_count >= 15)
|
|
6955
|
-
ORDER BY hit_count DESC
|
|
6956
|
-
`).all().map(mapRow);
|
|
6957
|
-
}
|
|
6958
|
-
function getKnowledgeStats(store) {
|
|
6959
|
-
const agg = store.db.prepare("SELECT COUNT(*) as total, COALESCE(AVG(hit_count), 0) as avg_hits FROM knowledge_index WHERE enabled = 1").get();
|
|
6960
|
-
const bySubType = {};
|
|
6961
|
-
const subtypeRows = store.db.prepare("SELECT sub_type, COUNT(*) as cnt FROM knowledge_index WHERE enabled = 1 GROUP BY sub_type").all();
|
|
6962
|
-
for (const r of subtypeRows) bySubType[r.sub_type] = r.cnt;
|
|
6963
|
-
const topRows = store.db.prepare(`
|
|
6964
|
-
SELECT id, file_path, content_hash, title, content, sub_type,
|
|
6965
|
-
project_remote, project_path, project_name, branch,
|
|
6966
|
-
created_at, updated_at, hit_count, last_accessed, enabled
|
|
6967
|
-
FROM knowledge_index WHERE enabled = 1
|
|
6968
|
-
ORDER BY hit_count DESC LIMIT 5
|
|
6969
|
-
`).all();
|
|
6970
|
-
return {
|
|
6971
|
-
total: agg?.total ?? 0,
|
|
6972
|
-
avgHitCount: agg?.avg_hits ?? 0,
|
|
6973
|
-
bySubType,
|
|
6974
|
-
topAccessed: topRows.map(mapRow)
|
|
6975
|
-
};
|
|
6976
|
-
}
|
|
6977
|
-
function searchKnowledgeKeyword(store, query, limit) {
|
|
6978
|
-
const escaped = escapeLIKEContains(query);
|
|
6979
|
-
return store.db.prepare(`
|
|
6980
|
-
SELECT id, file_path, content_hash, title, content, sub_type,
|
|
6981
|
-
project_remote, project_path, project_name, branch,
|
|
6982
|
-
created_at, updated_at, hit_count, last_accessed, enabled
|
|
6983
|
-
FROM knowledge_index
|
|
6984
|
-
WHERE enabled = 1 AND (content LIKE ? ESCAPE '\\' OR title LIKE ? ESCAPE '\\')
|
|
6985
|
-
ORDER BY hit_count DESC LIMIT ?
|
|
6986
|
-
`).all(escaped, escaped, limit).map(mapRow);
|
|
6987
|
-
}
|
|
6988
|
-
function getRecentDecisions(store, projectRemote, projectPath, sinceISO, limit) {
|
|
6989
|
-
return store.db.prepare(`
|
|
6990
|
-
SELECT title, content, created_at FROM knowledge_index
|
|
6991
|
-
WHERE sub_type = 'decision'
|
|
6992
|
-
AND project_remote = ? AND project_path = ?
|
|
6993
|
-
AND created_at > ? AND enabled = 1
|
|
6994
|
-
ORDER BY created_at DESC LIMIT ?
|
|
6995
|
-
`).all(projectRemote, projectPath, sinceISO, limit).map((r) => ({
|
|
6996
|
-
title: r.title,
|
|
6997
|
-
content: r.content,
|
|
6998
|
-
createdAt: r.created_at
|
|
6999
|
-
}));
|
|
7000
|
-
}
|
|
7001
|
-
function deleteOrphanKnowledge(store, projectRemote, projectPath, branch, validFilePaths) {
|
|
7002
|
-
const rows = store.db.prepare("SELECT id, file_path FROM knowledge_index WHERE project_remote = ? AND project_path = ? AND branch = ?").all(projectRemote, projectPath, branch);
|
|
7003
|
-
let deleted = 0;
|
|
7004
|
-
const delEmbed = store.db.prepare("DELETE FROM embeddings WHERE source = 'knowledge' AND source_id = ?");
|
|
7005
|
-
const delKnowledge = store.db.prepare("DELETE FROM knowledge_index WHERE id = ?");
|
|
7006
|
-
store.db.transaction(() => {
|
|
7007
|
-
for (const row of rows) if (!validFilePaths.has(row.file_path)) {
|
|
7008
|
-
delEmbed.run(row.id);
|
|
7009
|
-
delKnowledge.run(row.id);
|
|
7010
|
-
deleted++;
|
|
7011
|
-
}
|
|
7012
|
-
})();
|
|
7013
|
-
return deleted;
|
|
7014
|
-
}
|
|
7015
|
-
function countKnowledge(store, projectRemote, projectPath) {
|
|
7016
|
-
return store.db.prepare("SELECT COUNT(*) as cnt FROM knowledge_index WHERE project_remote = ? AND project_path = ? AND enabled = 1").get(projectRemote, projectPath).cnt;
|
|
7017
|
-
}
|
|
7018
|
-
function escapeLIKEContains(s) {
|
|
7019
|
-
s = s.replaceAll("\\", "\\\\");
|
|
7020
|
-
s = s.replaceAll("%", "\\%");
|
|
7021
|
-
s = s.replaceAll("_", "\\_");
|
|
7022
|
-
return `%${s}%`;
|
|
7023
|
-
}
|
|
7024
|
-
function mapRow(r) {
|
|
7025
|
-
return {
|
|
7026
|
-
id: r.id,
|
|
7027
|
-
filePath: r.file_path,
|
|
7028
|
-
contentHash: r.content_hash,
|
|
7029
|
-
title: r.title,
|
|
7030
|
-
content: r.content,
|
|
7031
|
-
subType: r.sub_type,
|
|
7032
|
-
projectRemote: r.project_remote,
|
|
7033
|
-
projectPath: r.project_path,
|
|
7034
|
-
projectName: r.project_name,
|
|
7035
|
-
branch: r.branch,
|
|
7036
|
-
createdAt: r.created_at,
|
|
7037
|
-
updatedAt: r.updated_at,
|
|
7038
|
-
hitCount: r.hit_count,
|
|
7039
|
-
lastAccessed: r.last_accessed,
|
|
7040
|
-
enabled: r.enabled === 1
|
|
7041
|
-
};
|
|
7042
|
-
}
|
|
7043
|
-
//#endregion
|
|
7044
|
-
export { __toESM as A, rootDir as C, require_dist as D, writeActiveState as E, __commonJSMin as O, reviewStatusFor as S, verifyReviewFile as T, detectSize as _, getKnowledgeStats as a, readActiveState as b, incrementHitCount as c, searchKnowledgeKeyword as d, setKnowledgeEnabled as f, completeTask as g, VALID_SLUG as h, getKnowledgeByIDs as i, __exportAll as k, mapRow as l, SpecDir as m, deleteOrphanKnowledge as n, getPromotionCandidates as o, upsertKnowledge as p, getKnowledgeByID as r, getRecentDecisions as s, countKnowledge as t, promoteSubType as u, filesForSize as v, switchActive as w, removeTask as x, readActive as y };
|
|
6874
|
+
export { __toESM as _, filesForSize as a, removeTask as c, switchActive as d, verifyReviewFile as f, __exportAll as g, __commonJSMin as h, detectSize as i, reviewStatusFor as l, require_dist as m, VALID_SLUG as n, readActive as o, writeActiveState as p, completeTask as r, readActiveState as s, SpecDir as t, rootDir as u };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { r as subTypeBoost } from "./fts-
|
|
4
|
-
import { Embedder } from "./embedder-
|
|
5
|
-
import "./dispatcher-
|
|
6
|
-
import { n as trackHitCounts, r as truncate, t as searchPipeline } from "./helpers-
|
|
7
|
-
import { openDefaultCached } from "./store-
|
|
8
|
-
import { t as emitDirectives } from "./directives-
|
|
9
|
-
import {
|
|
2
|
+
import { s as readActiveState } from "./types-DFsKNXVY.mjs";
|
|
3
|
+
import { r as subTypeBoost } from "./fts-Buk8fkl1.mjs";
|
|
4
|
+
import { Embedder } from "./embedder-CFkDPOku.mjs";
|
|
5
|
+
import "./dispatcher-M-Rc8GYD.mjs";
|
|
6
|
+
import { n as trackHitCounts, r as truncate, t as searchPipeline } from "./helpers-CvI9bVCq.mjs";
|
|
7
|
+
import { openDefaultCached } from "./store-D4fokoGA.mjs";
|
|
8
|
+
import { t as emitDirectives } from "./directives-BnbWxhap.mjs";
|
|
9
|
+
import { c as writeLastIntent, i as readStateJSON, l as writeStateJSON, o as readWorkedSlugs, t as IMPLEMENT_INTENTS } from "./state-Dqi1EiD0.mjs";
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
//#region src/hooks/user-prompt.ts
|
|
@@ -203,6 +203,7 @@ function classifyIntent(prompt) {
|
|
|
203
203
|
/**
|
|
204
204
|
* FR-5/FR-7: Check if spec is required or unapproved before implementation.
|
|
205
205
|
* Stage 1: No spec exists → DIRECTIVE to create one.
|
|
206
|
+
* Stage 1.5: Spec exists + implement intent → WARNING to confirm or create new spec.
|
|
206
207
|
* Stage 2: Spec exists but not approved (M/L/XL) → DIRECTIVE to get review.
|
|
207
208
|
*/
|
|
208
209
|
function checkSpecRequired(cwd, intent) {
|
|
@@ -244,6 +245,12 @@ function checkSpecRequired(cwd, intent) {
|
|
|
244
245
|
],
|
|
245
246
|
spiritVsLetter: true
|
|
246
247
|
};
|
|
248
|
+
if (taskSlug) {
|
|
249
|
+
if (!readWorkedSlugs(cwd).includes(taskSlug)) return {
|
|
250
|
+
level: "WARNING",
|
|
251
|
+
message: `Active spec '${taskSlug}' exists. If this is a different task, create a new spec first via /alfred:brief or dossier action=init. Use AskUserQuestion to confirm with the user whether this work is part of '${taskSlug}' or a new task.`
|
|
252
|
+
};
|
|
253
|
+
}
|
|
247
254
|
} catch {}
|
|
248
255
|
return null;
|
|
249
256
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { g as __exportAll } from "./types-DFsKNXVY.mjs";
|
|
3
3
|
//#region src/store/vectors.ts
|
|
4
4
|
var vectors_exports = /* @__PURE__ */ __exportAll({
|
|
5
5
|
cosineSimilarity: () => cosineSimilarity,
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|