kirograph 0.26.0 → 0.27.1
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/dist/bin/commands/help.js +8 -3
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/hook.js +242 -0
- package/dist/bin/commands/hook.js.map +7 -0
- package/dist/bin/commands/init.js.map +2 -2
- package/dist/bin/commands/uninit.js +12 -5
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +8 -2
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/hooks.js +170 -116
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +21 -6
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/kiro.js +4 -4
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/kirograph.js +3 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/hooks/import-prompt.js +75 -0
- package/dist/hooks/import-prompt.js.map +7 -0
- package/dist/hooks/manager.js +124 -0
- package/dist/hooks/manager.js.map +7 -0
- package/dist/hooks/prompt.js +296 -0
- package/dist/hooks/prompt.js.map +7 -0
- package/package.json +3 -3
|
@@ -33,39 +33,70 @@ __export(hooks_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(hooks_exports);
|
|
34
34
|
var fs = __toESM(require("fs"));
|
|
35
35
|
var path = __toESM(require("path"));
|
|
36
|
-
|
|
37
|
-
const
|
|
36
|
+
const V2_EXT = ".json";
|
|
37
|
+
const V1_EXT = ".kiro.hook";
|
|
38
38
|
function buildWatchmenHook(synthesisMode) {
|
|
39
39
|
if (synthesisMode === "local") {
|
|
40
40
|
return {
|
|
41
|
-
|
|
42
|
-
version: "1.0.0",
|
|
41
|
+
id: "kirograph-watchmen",
|
|
43
42
|
description: "After memory capture, run local model synthesis if enough observations have accumulated.",
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
v2: {
|
|
44
|
+
version: "v1",
|
|
45
|
+
hooks: [{
|
|
46
|
+
name: "KiroGraph Watchmen",
|
|
47
|
+
trigger: "Stop",
|
|
48
|
+
action: { type: "command", command: "kirograph mem watchmen synthesize --quiet 2>&1 || true" }
|
|
49
|
+
}]
|
|
50
|
+
},
|
|
51
|
+
v1: {
|
|
52
|
+
name: "KiroGraph Watchmen",
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
description: "After memory capture, run local model synthesis if enough observations have accumulated.",
|
|
55
|
+
when: { type: "agentStop" },
|
|
56
|
+
then: { type: "runCommand", command: "kirograph mem watchmen synthesize --quiet 2>&1 || true" }
|
|
57
|
+
}
|
|
46
58
|
};
|
|
47
59
|
}
|
|
48
|
-
|
|
49
|
-
name: "KiroGraph Watchmen",
|
|
50
|
-
version: "1.0.0",
|
|
51
|
-
description: "After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.",
|
|
52
|
-
when: { type: "agentStop" },
|
|
53
|
-
then: {
|
|
54
|
-
type: "askAgent",
|
|
55
|
-
prompt: `Check if KiroGraph Watchmen synthesis should run: call kirograph_mem_store with kind='note' and content='watchmen check'. If the response does not include watchmenReady: true, do nothing and stop.
|
|
60
|
+
const watchmenPrompt = `Check if KiroGraph Watchmen synthesis should run: call kirograph_mem_store with kind='note' and content='watchmen check'. If the response does not include watchmenReady: true, do nothing and stop.
|
|
56
61
|
|
|
57
62
|
If watchmenReady: true:
|
|
58
63
|
1. Call kirograph_mem_search for each kind (decision, error, pattern, architecture, note) with limit 20.
|
|
59
64
|
2. Write or update the workspace brief: upsert the ## KiroGraph Watchmen section in each file listed in targetFiles. For .kiro/steering/kirograph-watchmen.md use inclusion: always frontmatter with sections for Decisions, Known Errors & Fixes, Recurring Patterns, and Architecture Notes. For all other files write a compact ## KiroGraph Watchmen block.
|
|
60
65
|
3. Generate skill files: if skillTargetDir is present in the response, identify recurring procedures (any procedure appearing in 3+ observations). For each, write a .kiro/steering/watchmen-<slug>.md file with inclusion: manual frontmatter, a short title, a "When to use" line with trigger phrases, and numbered steps. Prefix all generated files with watchmen- so they are distinguishable from hand-written steering files. Remove any .kiro/steering/watchmen-*.md files from previous runs that no longer match current patterns. If skillTargetDir is absent, embed a ## Recurring Procedures section in each targetFile instead, listing each procedure with trigger context and steps.
|
|
61
|
-
4. Store a kind='summary' observation briefly describing what was synthesized (e.g. "Synthesized 8 observations: updated kirograph-watchmen.md, wrote watchmen-auth-flow.md and watchmen-test-pattern.md")
|
|
66
|
+
4. Store a kind='summary' observation briefly describing what was synthesized (e.g. "Synthesized 8 observations: updated kirograph-watchmen.md, wrote watchmen-auth-flow.md and watchmen-test-pattern.md").`;
|
|
67
|
+
return {
|
|
68
|
+
id: "kirograph-watchmen",
|
|
69
|
+
description: "After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.",
|
|
70
|
+
v2: {
|
|
71
|
+
version: "v1",
|
|
72
|
+
hooks: [{
|
|
73
|
+
name: "KiroGraph Watchmen",
|
|
74
|
+
trigger: "Stop",
|
|
75
|
+
action: { type: "agent", prompt: watchmenPrompt }
|
|
76
|
+
}]
|
|
77
|
+
},
|
|
78
|
+
v1: {
|
|
79
|
+
name: "KiroGraph Watchmen",
|
|
80
|
+
version: "1.0.0",
|
|
81
|
+
description: "After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.",
|
|
82
|
+
when: { type: "agentStop" },
|
|
83
|
+
then: { type: "askAgent", prompt: watchmenPrompt }
|
|
62
84
|
}
|
|
63
85
|
};
|
|
64
86
|
}
|
|
65
87
|
const HOOKS = [
|
|
66
88
|
{
|
|
67
|
-
|
|
68
|
-
|
|
89
|
+
id: "kirograph-sync-if-dirty",
|
|
90
|
+
description: "Sync the KiroGraph index when the agent stops, picking up any file edits, creates, or deletes from the session.",
|
|
91
|
+
v2: {
|
|
92
|
+
version: "v1",
|
|
93
|
+
hooks: [{
|
|
94
|
+
name: "KiroGraph Sync on Agent Stop",
|
|
95
|
+
trigger: "Stop",
|
|
96
|
+
action: { type: "command", command: "kirograph sync --quiet 2>&1 > /dev/null" }
|
|
97
|
+
}]
|
|
98
|
+
},
|
|
99
|
+
v1: {
|
|
69
100
|
name: "KiroGraph Sync on Agent Stop",
|
|
70
101
|
version: "1.0.0",
|
|
71
102
|
description: "Sync the KiroGraph index when the agent stops, picking up any file edits, creates, or deletes from the session.",
|
|
@@ -74,11 +105,24 @@ const HOOKS = [
|
|
|
74
105
|
}
|
|
75
106
|
},
|
|
76
107
|
{
|
|
77
|
-
|
|
78
|
-
|
|
108
|
+
id: "kirograph-compress-hint",
|
|
109
|
+
description: "Remind the agent to use kirograph_exec for shell commands that benefit from token compression.",
|
|
110
|
+
v2: {
|
|
111
|
+
version: "v1",
|
|
112
|
+
hooks: [{
|
|
113
|
+
name: "KiroGraph Compression Hint",
|
|
114
|
+
trigger: "PreToolUse",
|
|
115
|
+
matcher: "execute_bash",
|
|
116
|
+
action: {
|
|
117
|
+
type: "agent",
|
|
118
|
+
prompt: "If this shell command is a git operation, GitHub CLI, test runner, linter, build tool, file listing, grep/rg, docker/kubectl, AWS CLI, or package manager command, consider using the kirograph_exec MCP tool instead for 60-90% token savings. The tool compresses output automatically while preserving error details."
|
|
119
|
+
}
|
|
120
|
+
}]
|
|
121
|
+
},
|
|
122
|
+
v1: {
|
|
79
123
|
name: "KiroGraph Compression Hint",
|
|
80
124
|
version: "1.0.0",
|
|
81
|
-
description: "Remind the agent to use kirograph_exec for shell commands that benefit from token compression
|
|
125
|
+
description: "Remind the agent to use kirograph_exec for shell commands that benefit from token compression.",
|
|
82
126
|
when: { type: "preToolUse", toolTypes: ["shell"] },
|
|
83
127
|
then: {
|
|
84
128
|
type: "askAgent",
|
|
@@ -87,8 +131,20 @@ const HOOKS = [
|
|
|
87
131
|
}
|
|
88
132
|
},
|
|
89
133
|
{
|
|
90
|
-
|
|
91
|
-
|
|
134
|
+
id: "kirograph-mem-capture",
|
|
135
|
+
description: "Prompt the agent to store important observations in memory at the end of each session.",
|
|
136
|
+
v2: {
|
|
137
|
+
version: "v1",
|
|
138
|
+
hooks: [{
|
|
139
|
+
name: "KiroGraph Memory Capture",
|
|
140
|
+
trigger: "Stop",
|
|
141
|
+
action: {
|
|
142
|
+
type: "agent",
|
|
143
|
+
prompt: "Before ending, review what happened in this session. If there were any important decisions, bug root causes, architecture insights, error patterns, or lessons learned, store them using kirograph_mem_store with the appropriate kind (decision, error, pattern, architecture, note). Keep observations concise \u2014 one fact per observation. Skip if nothing noteworthy happened."
|
|
144
|
+
}
|
|
145
|
+
}]
|
|
146
|
+
},
|
|
147
|
+
v1: {
|
|
92
148
|
name: "KiroGraph Memory Capture",
|
|
93
149
|
version: "1.0.0",
|
|
94
150
|
description: "Prompt the agent to store important observations in memory at the end of each session.",
|
|
@@ -106,135 +162,133 @@ function ensureDir(p) {
|
|
|
106
162
|
function writeJson(p, data) {
|
|
107
163
|
fs.writeFileSync(p, JSON.stringify(data, null, 2) + "\n");
|
|
108
164
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
165
|
+
function writeHookDual(hooksDir, def) {
|
|
166
|
+
writeJson(path.join(hooksDir, `${def.id}${V2_EXT}`), def.v2);
|
|
167
|
+
writeJson(path.join(hooksDir, `${def.id}${V1_EXT}`), def.v1);
|
|
168
|
+
}
|
|
169
|
+
function writeHookForFormat(hooksDir, def, format) {
|
|
170
|
+
if (format === "v2") {
|
|
171
|
+
writeJson(path.join(hooksDir, `${def.id}${V2_EXT}`), def.v2);
|
|
172
|
+
const legacyPath = path.join(hooksDir, `${def.id}${V1_EXT}`);
|
|
173
|
+
if (fs.existsSync(legacyPath)) fs.unlinkSync(legacyPath);
|
|
174
|
+
} else {
|
|
175
|
+
writeJson(path.join(hooksDir, `${def.id}${V1_EXT}`), def.v1);
|
|
176
|
+
const v2Path = path.join(hooksDir, `${def.id}${V2_EXT}`);
|
|
177
|
+
if (fs.existsSync(v2Path)) fs.unlinkSync(v2Path);
|
|
116
178
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
} catch {
|
|
123
|
-
(0, import_errors.logWarn)(`KiroGraph installer: could not read hook file ${filePath}`);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
let obj;
|
|
127
|
-
try {
|
|
128
|
-
obj = JSON.parse(raw);
|
|
129
|
-
} catch {
|
|
130
|
-
(0, import_errors.logWarn)(`KiroGraph installer: could not parse hook file ${filePath}`);
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
let changed = false;
|
|
134
|
-
if (obj?.when?.type === "onIdle") {
|
|
135
|
-
obj.when.type = "agentStop";
|
|
136
|
-
changed = true;
|
|
137
|
-
}
|
|
138
|
-
if (file.endsWith(".json") && file.startsWith("kirograph-")) {
|
|
139
|
-
const newName = file.replace(/\.json$/, HOOK_EXT);
|
|
140
|
-
const newPath = path.join(hooksDir, newName);
|
|
141
|
-
try {
|
|
142
|
-
fs.writeFileSync(newPath, JSON.stringify(obj, null, 2) + "\n");
|
|
143
|
-
fs.unlinkSync(filePath);
|
|
144
|
-
} catch {
|
|
145
|
-
(0, import_errors.logWarn)(`KiroGraph installer: could not migrate hook file ${filePath} \u2192 ${newName}`);
|
|
146
|
-
}
|
|
147
|
-
} else if (changed) {
|
|
148
|
-
try {
|
|
149
|
-
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n");
|
|
150
|
-
} catch {
|
|
151
|
-
(0, import_errors.logWarn)(`KiroGraph installer: could not write migrated hook file ${filePath}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
179
|
+
}
|
|
180
|
+
function removeHookDual(hooksDir, id) {
|
|
181
|
+
for (const ext of [V2_EXT, V1_EXT]) {
|
|
182
|
+
const p = path.join(hooksDir, `${id}${ext}`);
|
|
183
|
+
if (fs.existsSync(p)) fs.unlinkSync(p);
|
|
154
184
|
}
|
|
155
185
|
}
|
|
156
186
|
function writeHooks(kiroDir, opts) {
|
|
157
187
|
const hooksDir = path.join(kiroDir, "hooks");
|
|
158
188
|
ensureDir(hooksDir);
|
|
159
|
-
|
|
160
|
-
const
|
|
189
|
+
const format = opts?.kiroHookFormat ?? "v2";
|
|
190
|
+
const obsoleteFiles = [
|
|
161
191
|
"kirograph-sync-on-save.json",
|
|
162
192
|
"kirograph-sync-on-create.json",
|
|
163
|
-
// Legacy .json versions (migrated to .kiro.hook)
|
|
164
193
|
"kirograph-mark-dirty-on-save.json",
|
|
165
194
|
"kirograph-mark-dirty-on-create.json",
|
|
166
195
|
"kirograph-sync-on-delete.json",
|
|
167
|
-
"kirograph-
|
|
168
|
-
"kirograph-
|
|
169
|
-
"kirograph-
|
|
170
|
-
// Removed per-file hooks (consolidated into agentStop sync)
|
|
171
|
-
`kirograph-mark-dirty-on-save${HOOK_EXT}`,
|
|
172
|
-
`kirograph-mark-dirty-on-create${HOOK_EXT}`,
|
|
173
|
-
`kirograph-sync-on-delete${HOOK_EXT}`
|
|
196
|
+
"kirograph-mark-dirty-on-save.kiro.hook",
|
|
197
|
+
"kirograph-mark-dirty-on-create.kiro.hook",
|
|
198
|
+
"kirograph-sync-on-delete.kiro.hook"
|
|
174
199
|
];
|
|
175
|
-
for (const old of
|
|
200
|
+
for (const old of obsoleteFiles) {
|
|
176
201
|
const p = path.join(hooksDir, old);
|
|
177
202
|
if (fs.existsSync(p)) fs.unlinkSync(p);
|
|
178
203
|
}
|
|
179
|
-
for (const
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
if (fs.existsSync(p)) fs.unlinkSync(p);
|
|
204
|
+
for (const def of HOOKS) {
|
|
205
|
+
if (def.id === "kirograph-compress-hint" && opts?.enableCompression === false) {
|
|
206
|
+
removeHookDual(hooksDir, def.id);
|
|
183
207
|
continue;
|
|
184
208
|
}
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
if (fs.existsSync(p)) fs.unlinkSync(p);
|
|
209
|
+
if (def.id === "kirograph-mem-capture" && !opts?.enableMemory) {
|
|
210
|
+
removeHookDual(hooksDir, def.id);
|
|
188
211
|
continue;
|
|
189
212
|
}
|
|
190
|
-
|
|
213
|
+
writeHookForFormat(hooksDir, def, format);
|
|
191
214
|
}
|
|
192
|
-
const watchmenHookPath = path.join(hooksDir, `kirograph-watchmen${HOOK_EXT}`);
|
|
193
215
|
if (opts?.enableWatchmen) {
|
|
194
216
|
const mode = opts.watchmenSynthesisMode ?? "local";
|
|
195
|
-
|
|
217
|
+
writeHookForFormat(hooksDir, buildWatchmenHook(mode), format);
|
|
196
218
|
} else {
|
|
197
|
-
|
|
219
|
+
removeHookDual(hooksDir, "kirograph-watchmen");
|
|
198
220
|
}
|
|
199
|
-
const wikiIngestHookPath = path.join(hooksDir, `kirograph-wiki-ingest${HOOK_EXT}`);
|
|
200
|
-
const wikiLintHookPath = path.join(hooksDir, `kirograph-wiki-lint${HOOK_EXT}`);
|
|
201
221
|
if (opts?.enableWiki) {
|
|
202
222
|
const lintFreq = opts.wikiLintFrequency ?? 10;
|
|
203
223
|
const wikiMode = opts.wikiSynthesisMode ?? "agent";
|
|
224
|
+
const wikiIngestPrompt = `If this session produced durable knowledge (design decisions, architecture insights, API contracts, process steps, domain facts), consider updating the wiki:
|
|
225
|
+
1. Call kirograph_wiki_ingest with the key findings as source text.
|
|
226
|
+
2. The tool returns a structured prompt. Pass it to yourself to generate a WIKI_DIFF.
|
|
227
|
+
3. Call kirograph_wiki_apply_diff with your generated diff.
|
|
228
|
+
Skip if the session was trivial (simple bug fix, no new knowledge).`;
|
|
204
229
|
if (wikiMode === "local") {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
version: "1.0.0",
|
|
230
|
+
writeHookForFormat(hooksDir, {
|
|
231
|
+
id: "kirograph-wiki-ingest",
|
|
208
232
|
description: "After each session, run local-model wiki synthesis over queued sources.",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
233
|
+
v2: {
|
|
234
|
+
version: "v1",
|
|
235
|
+
hooks: [{
|
|
236
|
+
name: "KiroGraph Wiki Synthesize",
|
|
237
|
+
trigger: "Stop",
|
|
238
|
+
action: { type: "command", command: "kirograph wiki synthesize --quiet 2>&1 || true" }
|
|
239
|
+
}]
|
|
240
|
+
},
|
|
241
|
+
v1: {
|
|
242
|
+
name: "KiroGraph Wiki Synthesize",
|
|
243
|
+
version: "1.0.0",
|
|
244
|
+
description: "After each session, run local-model wiki synthesis over queued sources.",
|
|
245
|
+
when: { type: "agentStop" },
|
|
246
|
+
then: { type: "runCommand", command: "kirograph wiki synthesize --quiet 2>&1 || true" }
|
|
247
|
+
}
|
|
248
|
+
}, format);
|
|
212
249
|
} else {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
version: "1.0.0",
|
|
250
|
+
writeHookForFormat(hooksDir, {
|
|
251
|
+
id: "kirograph-wiki-ingest",
|
|
216
252
|
description: "At the end of each session, prompt the agent to ingest any relevant knowledge into the wiki.",
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
253
|
+
v2: {
|
|
254
|
+
version: "v1",
|
|
255
|
+
hooks: [{
|
|
256
|
+
name: "KiroGraph Wiki Ingest",
|
|
257
|
+
trigger: "Stop",
|
|
258
|
+
action: { type: "agent", prompt: wikiIngestPrompt }
|
|
259
|
+
}]
|
|
260
|
+
},
|
|
261
|
+
v1: {
|
|
262
|
+
name: "KiroGraph Wiki Ingest",
|
|
263
|
+
version: "1.0.0",
|
|
264
|
+
description: "At the end of each session, prompt the agent to ingest any relevant knowledge into the wiki.",
|
|
265
|
+
when: { type: "agentStop" },
|
|
266
|
+
then: { type: "askAgent", prompt: wikiIngestPrompt }
|
|
225
267
|
}
|
|
226
|
-
});
|
|
268
|
+
}, format);
|
|
227
269
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
version: "1.0.0",
|
|
270
|
+
writeHookForFormat(hooksDir, {
|
|
271
|
+
id: "kirograph-wiki-lint",
|
|
231
272
|
description: `Run wiki health check every ${lintFreq} sessions to catch broken links, orphan pages, and contradictions.`,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
273
|
+
v2: {
|
|
274
|
+
version: "v1",
|
|
275
|
+
hooks: [{
|
|
276
|
+
name: "KiroGraph Wiki Lint",
|
|
277
|
+
trigger: "Stop",
|
|
278
|
+
action: { type: "command", command: "kirograph wiki lint 2>&1 || true" }
|
|
279
|
+
}]
|
|
280
|
+
},
|
|
281
|
+
v1: {
|
|
282
|
+
name: "KiroGraph Wiki Lint",
|
|
283
|
+
version: "1.0.0",
|
|
284
|
+
description: `Run wiki health check every ${lintFreq} sessions to catch broken links, orphan pages, and contradictions.`,
|
|
285
|
+
when: { type: "agentStop" },
|
|
286
|
+
then: { type: "runCommand", command: "kirograph wiki lint 2>&1 || true" }
|
|
287
|
+
}
|
|
288
|
+
}, format);
|
|
235
289
|
} else {
|
|
236
|
-
|
|
237
|
-
|
|
290
|
+
removeHookDual(hooksDir, "kirograph-wiki-ingest");
|
|
291
|
+
removeHookDual(hooksDir, "kirograph-wiki-lint");
|
|
238
292
|
}
|
|
239
293
|
console.log(` \u2713 Auto-sync hooks written to ${hooksDir}`);
|
|
240
294
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/bin/installer/hooks.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Installer \u2014 Kiro hook file management\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { logWarn } from '../../errors';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst HOOK_EXT = '.kiro.hook';\n\n// \u2500\u2500 Hook builders \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction buildWatchmenHook(synthesisMode: 'local' | 'agent'): object {\n if (synthesisMode === 'local') {\n return {\n name: 'KiroGraph Watchmen',\n version: '1.0.0',\n description: 'After memory capture, run local model synthesis if enough observations have accumulated.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph mem watchmen synthesize --quiet 2>&1 || true' },\n };\n }\n return {\n name: 'KiroGraph Watchmen',\n version: '1.0.0',\n description: 'After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.',\n when: { type: 'agentStop' },\n then: {\n type: 'askAgent',\n prompt: `Check if KiroGraph Watchmen synthesis should run: call kirograph_mem_store with kind='note' and content='watchmen check'. If the response does not include watchmenReady: true, do nothing and stop.\n\nIf watchmenReady: true:\n1. Call kirograph_mem_search for each kind (decision, error, pattern, architecture, note) with limit 20.\n2. Write or update the workspace brief: upsert the ## KiroGraph Watchmen section in each file listed in targetFiles. For .kiro/steering/kirograph-watchmen.md use inclusion: always frontmatter with sections for Decisions, Known Errors & Fixes, Recurring Patterns, and Architecture Notes. For all other files write a compact ## KiroGraph Watchmen block.\n3. Generate skill files: if skillTargetDir is present in the response, identify recurring procedures (any procedure appearing in 3+ observations). For each, write a .kiro/steering/watchmen-<slug>.md file with inclusion: manual frontmatter, a short title, a \"When to use\" line with trigger phrases, and numbered steps. Prefix all generated files with watchmen- so they are distinguishable from hand-written steering files. Remove any .kiro/steering/watchmen-*.md files from previous runs that no longer match current patterns. If skillTargetDir is absent, embed a ## Recurring Procedures section in each targetFile instead, listing each procedure with trigger context and steps.\n4. Store a kind='summary' observation briefly describing what was synthesized (e.g. \"Synthesized 8 observations: updated kirograph-watchmen.md, wrote watchmen-auth-flow.md and watchmen-test-pattern.md\").`,\n },\n };\n}\n\nconst HOOKS: Array<{ filename: string; hook: object }> = [\n {\n filename: `kirograph-sync-if-dirty${HOOK_EXT}`,\n hook: {\n name: 'KiroGraph Sync on Agent Stop',\n version: '1.0.0',\n description: 'Sync the KiroGraph index when the agent stops, picking up any file edits, creates, or deletes from the session.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph sync --quiet 2>&1 > /dev/null' },\n },\n },\n {\n filename: `kirograph-compress-hint${HOOK_EXT}`,\n hook: {\n name: 'KiroGraph Compression Hint',\n version: '1.0.0',\n description: 'Remind the agent to use kirograph_exec for shell commands that benefit from token compression (git, gh, test, lint, build, docker, aws, grep).',\n when: { type: 'preToolUse', toolTypes: ['shell'] },\n then: {\n type: 'askAgent',\n prompt: 'If this shell command is a git operation, GitHub CLI, test runner, linter, build tool, file listing, grep/rg, docker/kubectl, AWS CLI, or package manager command, consider using the kirograph_exec MCP tool instead for 60-90% token savings. The tool compresses output automatically while preserving error details.',\n },\n },\n },\n {\n filename: `kirograph-mem-capture${HOOK_EXT}`,\n hook: {\n name: 'KiroGraph Memory Capture',\n version: '1.0.0',\n description: 'Prompt the agent to store important observations in memory at the end of each session.',\n when: { type: 'agentStop' },\n then: {\n type: 'askAgent',\n prompt: 'Before ending, review what happened in this session. If there were any important decisions, bug root causes, architecture insights, error patterns, or lessons learned, store them using kirograph_mem_store with the appropriate kind (decision, error, pattern, architecture, note). Keep observations concise \u2014 one fact per observation. Skip if nothing noteworthy happened.',\n },\n },\n },\n];\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ensureDir(p: string): void {\n fs.mkdirSync(p, { recursive: true });\n}\n\nfunction writeJson(p: string, data: unknown): void {\n fs.writeFileSync(p, JSON.stringify(data, null, 2) + '\\n');\n}\n\nfunction migrateOnIdleHooks(hooksDir: string): void {\n if (!fs.existsSync(hooksDir)) return;\n let files: string[];\n try {\n files = fs.readdirSync(hooksDir).filter(f => f.endsWith('.json') || f.endsWith(HOOK_EXT));\n } catch {\n return;\n }\n for (const file of files) {\n const filePath = path.join(hooksDir, file);\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch {\n logWarn(`KiroGraph installer: could not read hook file ${filePath}`);\n continue;\n }\n let obj: any;\n try {\n obj = JSON.parse(raw);\n } catch {\n logWarn(`KiroGraph installer: could not parse hook file ${filePath}`);\n continue;\n }\n let changed = false;\n if (obj?.when?.type === 'onIdle') {\n obj.when.type = 'agentStop';\n changed = true;\n }\n // Migrate .json \u2192 .kiro.hook extension\n if (file.endsWith('.json') && file.startsWith('kirograph-')) {\n const newName = file.replace(/\\.json$/, HOOK_EXT);\n const newPath = path.join(hooksDir, newName);\n try {\n fs.writeFileSync(newPath, JSON.stringify(obj, null, 2) + '\\n');\n fs.unlinkSync(filePath);\n } catch {\n logWarn(`KiroGraph installer: could not migrate hook file ${filePath} \u2192 ${newName}`);\n }\n } else if (changed) {\n try {\n fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + '\\n');\n } catch {\n logWarn(`KiroGraph installer: could not write migrated hook file ${filePath}`);\n }\n }\n }\n}\n\n// \u2500\u2500 Public \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function writeHooks(kiroDir: string, opts?: { enableCompression?: boolean; enableMemory?: boolean; enableWatchmen?: boolean; watchmenSynthesisMode?: 'local' | 'agent'; enableWiki?: boolean; wikiSynthesisMode?: 'local' | 'agent'; wikiLintFrequency?: number }): void {\n const hooksDir = path.join(kiroDir, 'hooks');\n ensureDir(hooksDir);\n\n migrateOnIdleHooks(hooksDir);\n\n const oldHooks = [\n 'kirograph-sync-on-save.json', 'kirograph-sync-on-create.json',\n // Legacy .json versions (migrated to .kiro.hook)\n 'kirograph-mark-dirty-on-save.json', 'kirograph-mark-dirty-on-create.json',\n 'kirograph-sync-on-delete.json', 'kirograph-sync-if-dirty.json',\n 'kirograph-compress-hint.json', 'kirograph-mem-capture.json',\n // Removed per-file hooks (consolidated into agentStop sync)\n `kirograph-mark-dirty-on-save${HOOK_EXT}`,\n `kirograph-mark-dirty-on-create${HOOK_EXT}`,\n `kirograph-sync-on-delete${HOOK_EXT}`,\n ];\n for (const old of oldHooks) {\n const p = path.join(hooksDir, old);\n if (fs.existsSync(p)) fs.unlinkSync(p);\n }\n\n for (const { filename, hook } of HOOKS) {\n // Skip compression hook if compression is disabled\n if (filename === `kirograph-compress-hint${HOOK_EXT}` && opts?.enableCompression === false) {\n const p = path.join(hooksDir, filename);\n if (fs.existsSync(p)) fs.unlinkSync(p);\n continue;\n }\n // Skip memory hook if memory is disabled\n if (filename === `kirograph-mem-capture${HOOK_EXT}` && !opts?.enableMemory) {\n const p = path.join(hooksDir, filename);\n if (fs.existsSync(p)) fs.unlinkSync(p);\n continue;\n }\n writeJson(path.join(hooksDir, filename), hook);\n }\n\n // Watchmen hook \u2014 built dynamically based on synthesis mode\n const watchmenHookPath = path.join(hooksDir, `kirograph-watchmen${HOOK_EXT}`);\n if (opts?.enableWatchmen) {\n const mode = opts.watchmenSynthesisMode ?? 'local';\n writeJson(watchmenHookPath, buildWatchmenHook(mode));\n } else {\n if (fs.existsSync(watchmenHookPath)) fs.unlinkSync(watchmenHookPath);\n }\n\n // Wiki hooks \u2014 ingest trigger + periodic lint\n const wikiIngestHookPath = path.join(hooksDir, `kirograph-wiki-ingest${HOOK_EXT}`);\n const wikiLintHookPath = path.join(hooksDir, `kirograph-wiki-lint${HOOK_EXT}`);\n if (opts?.enableWiki) {\n const lintFreq = opts.wikiLintFrequency ?? 10;\n const wikiMode = opts.wikiSynthesisMode ?? 'agent';\n\n if (wikiMode === 'local') {\n writeJson(wikiIngestHookPath, {\n name: 'KiroGraph Wiki Synthesize',\n version: '1.0.0',\n description: 'After each session, run local-model wiki synthesis over queued sources.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph wiki synthesize --quiet 2>&1 || true' },\n });\n } else {\n writeJson(wikiIngestHookPath, {\n name: 'KiroGraph Wiki Ingest',\n version: '1.0.0',\n description: 'At the end of each session, prompt the agent to ingest any relevant knowledge into the wiki.',\n when: { type: 'agentStop' },\n then: {\n type: 'askAgent',\n prompt: `If this session produced durable knowledge (design decisions, architecture insights, API contracts, process steps, domain facts), consider updating the wiki:\n1. Call kirograph_wiki_ingest with the key findings as source text.\n2. The tool returns a structured prompt. Pass it to yourself to generate a WIKI_DIFF.\n3. Call kirograph_wiki_apply_diff with your generated diff.\nSkip if the session was trivial (simple bug fix, no new knowledge).`,\n },\n });\n }\n\n writeJson(wikiLintHookPath, {\n name: 'KiroGraph Wiki Lint',\n version: '1.0.0',\n description: `Run wiki health check every ${lintFreq} sessions to catch broken links, orphan pages, and contradictions.`,\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: `kirograph wiki lint 2>&1 || true` },\n });\n } else {\n if (fs.existsSync(wikiIngestHookPath)) fs.unlinkSync(wikiIngestHookPath);\n if (fs.existsSync(wikiLintHookPath)) fs.unlinkSync(wikiLintHookPath);\n }\n\n console.log(` \u2713 Auto-sync hooks written to ${hooksDir}`);\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Installer \u2014 Kiro hook file management\n *\n * Generates hooks in BOTH formats for compatibility:\n *\n * v2 (new Kiro IDE): `.kiro/hooks/<id>.json`\n * { \"version\": \"v1\", \"hooks\": [{ \"name\", \"trigger\", \"matcher?\", \"action\": { type, command|prompt } }] }\n *\n * v1 (legacy Kiro IDE): `.kiro/hooks/<id>.kiro.hook`\n * { \"name\", \"version\", \"description\", \"when\": { type, toolTypes? }, \"then\": { type, command|prompt } }\n *\n * Old Kiro ignores .json files; new Kiro ignores .kiro.hook files.\n * Writing both ensures KiroGraph works regardless of which IDE version the user runs.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst V2_EXT = '.json';\nconst V1_EXT = '.kiro.hook';\n\n// \u2500\u2500 Hook builders \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface HookDef {\n id: string;\n description: string;\n v2: object; // new Kiro IDE format\n v1: object; // legacy Kiro IDE format\n}\n\nfunction buildWatchmenHook(synthesisMode: 'local' | 'agent'): HookDef {\n if (synthesisMode === 'local') {\n return {\n id: 'kirograph-watchmen',\n description: 'After memory capture, run local model synthesis if enough observations have accumulated.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Watchmen',\n trigger: 'Stop',\n action: { type: 'command', command: 'kirograph mem watchmen synthesize --quiet 2>&1 || true' },\n }],\n },\n v1: {\n name: 'KiroGraph Watchmen',\n version: '1.0.0',\n description: 'After memory capture, run local model synthesis if enough observations have accumulated.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph mem watchmen synthesize --quiet 2>&1 || true' },\n },\n };\n }\n\n const watchmenPrompt = `Check if KiroGraph Watchmen synthesis should run: call kirograph_mem_store with kind='note' and content='watchmen check'. If the response does not include watchmenReady: true, do nothing and stop.\n\nIf watchmenReady: true:\n1. Call kirograph_mem_search for each kind (decision, error, pattern, architecture, note) with limit 20.\n2. Write or update the workspace brief: upsert the ## KiroGraph Watchmen section in each file listed in targetFiles. For .kiro/steering/kirograph-watchmen.md use inclusion: always frontmatter with sections for Decisions, Known Errors & Fixes, Recurring Patterns, and Architecture Notes. For all other files write a compact ## KiroGraph Watchmen block.\n3. Generate skill files: if skillTargetDir is present in the response, identify recurring procedures (any procedure appearing in 3+ observations). For each, write a .kiro/steering/watchmen-<slug>.md file with inclusion: manual frontmatter, a short title, a \"When to use\" line with trigger phrases, and numbered steps. Prefix all generated files with watchmen- so they are distinguishable from hand-written steering files. Remove any .kiro/steering/watchmen-*.md files from previous runs that no longer match current patterns. If skillTargetDir is absent, embed a ## Recurring Procedures section in each targetFile instead, listing each procedure with trigger context and steps.\n4. Store a kind='summary' observation briefly describing what was synthesized (e.g. \"Synthesized 8 observations: updated kirograph-watchmen.md, wrote watchmen-auth-flow.md and watchmen-test-pattern.md\").`;\n\n return {\n id: 'kirograph-watchmen',\n description: 'After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Watchmen',\n trigger: 'Stop',\n action: { type: 'agent', prompt: watchmenPrompt },\n }],\n },\n v1: {\n name: 'KiroGraph Watchmen',\n version: '1.0.0',\n description: 'After memory capture, check if enough observations have accumulated. If so, synthesize them into workspace brief files.',\n when: { type: 'agentStop' },\n then: { type: 'askAgent', prompt: watchmenPrompt },\n },\n };\n}\n\nconst HOOKS: HookDef[] = [\n {\n id: 'kirograph-sync-if-dirty',\n description: 'Sync the KiroGraph index when the agent stops, picking up any file edits, creates, or deletes from the session.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Sync on Agent Stop',\n trigger: 'Stop',\n action: { type: 'command', command: 'kirograph sync --quiet 2>&1 > /dev/null' },\n }],\n },\n v1: {\n name: 'KiroGraph Sync on Agent Stop',\n version: '1.0.0',\n description: 'Sync the KiroGraph index when the agent stops, picking up any file edits, creates, or deletes from the session.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph sync --quiet 2>&1 > /dev/null' },\n },\n },\n {\n id: 'kirograph-compress-hint',\n description: 'Remind the agent to use kirograph_exec for shell commands that benefit from token compression.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Compression Hint',\n trigger: 'PreToolUse',\n matcher: 'execute_bash',\n action: {\n type: 'agent',\n prompt: 'If this shell command is a git operation, GitHub CLI, test runner, linter, build tool, file listing, grep/rg, docker/kubectl, AWS CLI, or package manager command, consider using the kirograph_exec MCP tool instead for 60-90% token savings. The tool compresses output automatically while preserving error details.',\n },\n }],\n },\n v1: {\n name: 'KiroGraph Compression Hint',\n version: '1.0.0',\n description: 'Remind the agent to use kirograph_exec for shell commands that benefit from token compression.',\n when: { type: 'preToolUse', toolTypes: ['shell'] },\n then: {\n type: 'askAgent',\n prompt: 'If this shell command is a git operation, GitHub CLI, test runner, linter, build tool, file listing, grep/rg, docker/kubectl, AWS CLI, or package manager command, consider using the kirograph_exec MCP tool instead for 60-90% token savings. The tool compresses output automatically while preserving error details.',\n },\n },\n },\n {\n id: 'kirograph-mem-capture',\n description: 'Prompt the agent to store important observations in memory at the end of each session.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Memory Capture',\n trigger: 'Stop',\n action: {\n type: 'agent',\n prompt: 'Before ending, review what happened in this session. If there were any important decisions, bug root causes, architecture insights, error patterns, or lessons learned, store them using kirograph_mem_store with the appropriate kind (decision, error, pattern, architecture, note). Keep observations concise \u2014 one fact per observation. Skip if nothing noteworthy happened.',\n },\n }],\n },\n v1: {\n name: 'KiroGraph Memory Capture',\n version: '1.0.0',\n description: 'Prompt the agent to store important observations in memory at the end of each session.',\n when: { type: 'agentStop' },\n then: {\n type: 'askAgent',\n prompt: 'Before ending, review what happened in this session. If there were any important decisions, bug root causes, architecture insights, error patterns, or lessons learned, store them using kirograph_mem_store with the appropriate kind (decision, error, pattern, architecture, note). Keep observations concise \u2014 one fact per observation. Skip if nothing noteworthy happened.',\n },\n },\n },\n];\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ensureDir(p: string): void {\n fs.mkdirSync(p, { recursive: true });\n}\n\nfunction writeJson(p: string, data: unknown): void {\n fs.writeFileSync(p, JSON.stringify(data, null, 2) + '\\n');\n}\n\n/** Write a hook in both v2 (.json) and v1 (.kiro.hook) formats */\nfunction writeHookDual(hooksDir: string, def: HookDef): void {\n writeJson(path.join(hooksDir, `${def.id}${V2_EXT}`), def.v2);\n writeJson(path.join(hooksDir, `${def.id}${V1_EXT}`), def.v1);\n}\n\n/** Write a hook in the specified format only, removing the other */\nfunction writeHookForFormat(hooksDir: string, def: HookDef, format: KiroHookFormat): void {\n if (format === 'v2') {\n writeJson(path.join(hooksDir, `${def.id}${V2_EXT}`), def.v2);\n // Remove legacy file if it exists\n const legacyPath = path.join(hooksDir, `${def.id}${V1_EXT}`);\n if (fs.existsSync(legacyPath)) fs.unlinkSync(legacyPath);\n } else {\n writeJson(path.join(hooksDir, `${def.id}${V1_EXT}`), def.v1);\n // Remove v2 file if it exists\n const v2Path = path.join(hooksDir, `${def.id}${V2_EXT}`);\n if (fs.existsSync(v2Path)) fs.unlinkSync(v2Path);\n }\n}\n\n/** Remove both format files for a given hook id */\nfunction removeHookDual(hooksDir: string, id: string): void {\n for (const ext of [V2_EXT, V1_EXT]) {\n const p = path.join(hooksDir, `${id}${ext}`);\n if (fs.existsSync(p)) fs.unlinkSync(p);\n }\n}\n\n// \u2500\u2500 Public \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type KiroHookFormat = 'v1-legacy' | 'v2';\n\nexport function writeHooks(kiroDir: string, opts?: { enableCompression?: boolean; enableMemory?: boolean; enableWatchmen?: boolean; watchmenSynthesisMode?: 'local' | 'agent'; enableWiki?: boolean; wikiSynthesisMode?: 'local' | 'agent'; wikiLintFrequency?: number; kiroHookFormat?: KiroHookFormat }): void {\n const hooksDir = path.join(kiroDir, 'hooks');\n ensureDir(hooksDir);\n\n const format = opts?.kiroHookFormat ?? 'v2';\n\n // Remove obsolete hook files from earlier versions\n const obsoleteFiles = [\n 'kirograph-sync-on-save.json', 'kirograph-sync-on-create.json',\n 'kirograph-mark-dirty-on-save.json', 'kirograph-mark-dirty-on-create.json',\n 'kirograph-sync-on-delete.json',\n 'kirograph-mark-dirty-on-save.kiro.hook',\n 'kirograph-mark-dirty-on-create.kiro.hook',\n 'kirograph-sync-on-delete.kiro.hook',\n ];\n for (const old of obsoleteFiles) {\n const p = path.join(hooksDir, old);\n if (fs.existsSync(p)) fs.unlinkSync(p);\n }\n\n for (const def of HOOKS) {\n // Skip compression hook if compression is disabled\n if (def.id === 'kirograph-compress-hint' && opts?.enableCompression === false) {\n removeHookDual(hooksDir, def.id);\n continue;\n }\n // Skip memory hook if memory is disabled\n if (def.id === 'kirograph-mem-capture' && !opts?.enableMemory) {\n removeHookDual(hooksDir, def.id);\n continue;\n }\n writeHookForFormat(hooksDir, def, format);\n }\n\n // Watchmen hook \u2014 built dynamically based on synthesis mode\n if (opts?.enableWatchmen) {\n const mode = opts.watchmenSynthesisMode ?? 'local';\n writeHookForFormat(hooksDir, buildWatchmenHook(mode), format);\n } else {\n removeHookDual(hooksDir, 'kirograph-watchmen');\n }\n\n // Wiki hooks \u2014 ingest trigger + periodic lint\n if (opts?.enableWiki) {\n const lintFreq = opts.wikiLintFrequency ?? 10;\n const wikiMode = opts.wikiSynthesisMode ?? 'agent';\n\n const wikiIngestPrompt = `If this session produced durable knowledge (design decisions, architecture insights, API contracts, process steps, domain facts), consider updating the wiki:\n1. Call kirograph_wiki_ingest with the key findings as source text.\n2. The tool returns a structured prompt. Pass it to yourself to generate a WIKI_DIFF.\n3. Call kirograph_wiki_apply_diff with your generated diff.\nSkip if the session was trivial (simple bug fix, no new knowledge).`;\n\n if (wikiMode === 'local') {\n writeHookForFormat(hooksDir, {\n id: 'kirograph-wiki-ingest',\n description: 'After each session, run local-model wiki synthesis over queued sources.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Wiki Synthesize',\n trigger: 'Stop',\n action: { type: 'command', command: 'kirograph wiki synthesize --quiet 2>&1 || true' },\n }],\n },\n v1: {\n name: 'KiroGraph Wiki Synthesize',\n version: '1.0.0',\n description: 'After each session, run local-model wiki synthesis over queued sources.',\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph wiki synthesize --quiet 2>&1 || true' },\n },\n }, format);\n } else {\n writeHookForFormat(hooksDir, {\n id: 'kirograph-wiki-ingest',\n description: 'At the end of each session, prompt the agent to ingest any relevant knowledge into the wiki.',\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Wiki Ingest',\n trigger: 'Stop',\n action: { type: 'agent', prompt: wikiIngestPrompt },\n }],\n },\n v1: {\n name: 'KiroGraph Wiki Ingest',\n version: '1.0.0',\n description: 'At the end of each session, prompt the agent to ingest any relevant knowledge into the wiki.',\n when: { type: 'agentStop' },\n then: { type: 'askAgent', prompt: wikiIngestPrompt },\n },\n }, format);\n }\n\n writeHookForFormat(hooksDir, {\n id: 'kirograph-wiki-lint',\n description: `Run wiki health check every ${lintFreq} sessions to catch broken links, orphan pages, and contradictions.`,\n v2: {\n version: 'v1',\n hooks: [{\n name: 'KiroGraph Wiki Lint',\n trigger: 'Stop',\n action: { type: 'command', command: 'kirograph wiki lint 2>&1 || true' },\n }],\n },\n v1: {\n name: 'KiroGraph Wiki Lint',\n version: '1.0.0',\n description: `Run wiki health check every ${lintFreq} sessions to catch broken links, orphan pages, and contradictions.`,\n when: { type: 'agentStop' },\n then: { type: 'runCommand', command: 'kirograph wiki lint 2>&1 || true' },\n },\n }, format);\n } else {\n removeHookDual(hooksDir, 'kirograph-wiki-ingest');\n removeHookDual(hooksDir, 'kirograph-wiki-lint');\n }\n\n console.log(` \u2713 Auto-sync hooks written to ${hooksDir}`);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -44,6 +44,7 @@ var import_config_prompt = require("./config-prompt");
|
|
|
44
44
|
var import_dashboard = require("./dashboard");
|
|
45
45
|
var import_qdrant_dashboard = require("./qdrant-dashboard");
|
|
46
46
|
var import_targets = require("./targets");
|
|
47
|
+
var import_import_prompt = require("../../hooks/import-prompt");
|
|
47
48
|
function _findRustc() {
|
|
48
49
|
const inPath = (0, import_child_process.spawnSync)("rustc", ["--version"], { encoding: "utf8", shell: true });
|
|
49
50
|
if (inPath.status === 0) return "rustc";
|
|
@@ -117,7 +118,14 @@ async function runInstaller(target = "kiro", opts = {}) {
|
|
|
117
118
|
console.log(` Workspace: ${cwd}
|
|
118
119
|
`);
|
|
119
120
|
console.log();
|
|
120
|
-
|
|
121
|
+
let kiroHookFormat = "v2";
|
|
122
|
+
if (target === "kiro") {
|
|
123
|
+
kiroHookFormat = await (0, import_prompts.arrowSelect)(rl, "Which version of Kiro IDE are you using?", [
|
|
124
|
+
{ value: "v1-legacy", label: "Beta Version 0.x.x", description: "Kiro IDE beta releases (uses .kiro.hook format)" },
|
|
125
|
+
{ value: "v2", label: "Version 1.x.x", description: "Kiro IDE stable releases (uses .json hook format)" }
|
|
126
|
+
], 1);
|
|
127
|
+
}
|
|
128
|
+
installer.installEarly(cwd, kiroHookFormat);
|
|
121
129
|
const alreadyInitialized = fs.existsSync(path.join(cwd, ".kirograph"));
|
|
122
130
|
let cavemanMode = "off";
|
|
123
131
|
let shellCompressionLevel = "normal";
|
|
@@ -139,6 +147,7 @@ async function runInstaller(target = "kiro", opts = {}) {
|
|
|
139
147
|
let shouldOfferIndex = false;
|
|
140
148
|
let typesenseDashboard = false;
|
|
141
149
|
let qdrantDashboard = false;
|
|
150
|
+
let hooksToImport = null;
|
|
142
151
|
try {
|
|
143
152
|
if (alreadyInitialized) {
|
|
144
153
|
const config = await (0, import_config.loadConfig)(cwd);
|
|
@@ -194,7 +203,12 @@ async function runInstaller(target = "kiro", opts = {}) {
|
|
|
194
203
|
console.log(` \u2022 enablePatterns: ${enablePatterns}`);
|
|
195
204
|
} else {
|
|
196
205
|
shouldOfferIndex = true;
|
|
197
|
-
const
|
|
206
|
+
const promptResult = await (0, import_config_prompt.promptConfigOptions)(rl, {
|
|
207
|
+
projectRoot: cwd,
|
|
208
|
+
offerHookImport: target === "kiro" && !opts.yes
|
|
209
|
+
});
|
|
210
|
+
const patch = promptResult.patch;
|
|
211
|
+
hooksToImport = promptResult.hooksToImport;
|
|
198
212
|
await (0, import_config.updateConfig)(cwd, patch);
|
|
199
213
|
cavemanMode = patch.cavemanMode ?? "off";
|
|
200
214
|
shellCompressionLevel = patch.shellCompressionLevel ?? "normal";
|
|
@@ -384,7 +398,10 @@ async function runInstaller(target = "kiro", opts = {}) {
|
|
|
384
398
|
}
|
|
385
399
|
}
|
|
386
400
|
}
|
|
387
|
-
installer.installLate(cwd, cavemanMode, shellCompressionLevel, enableMemory, enableDocs, enableData, enableSecurity, enableArchitecture, enablePatterns, enableWatchmen, watchmenSynthesisMode, enableWiki, wikiSynthesisMode, wikiLocalModel, enableCodeHealth, enableAdvancedAnalysis, enableAgentUtils, trackCallSites);
|
|
401
|
+
installer.installLate(cwd, cavemanMode, shellCompressionLevel, enableMemory, enableDocs, enableData, enableSecurity, enableArchitecture, enablePatterns, enableWatchmen, watchmenSynthesisMode, enableWiki, wikiSynthesisMode, wikiLocalModel, enableCodeHealth, enableAdvancedAnalysis, enableAgentUtils, trackCallSites, kiroHookFormat);
|
|
402
|
+
if (target === "kiro" && hooksToImport && hooksToImport.length > 0) {
|
|
403
|
+
(0, import_import_prompt.applyImportedGlobalHooks)(cwd, hooksToImport);
|
|
404
|
+
}
|
|
388
405
|
} catch (err) {
|
|
389
406
|
const reason = err instanceof Error ? err.message : String(err);
|
|
390
407
|
console.error(`
|
|
@@ -451,9 +468,7 @@ async function runInstaller(target = "kiro", opts = {}) {
|
|
|
451
468
|
}
|
|
452
469
|
});
|
|
453
470
|
});
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
if (qdrantDashboard) {
|
|
471
|
+
} else if (qdrantDashboard) {
|
|
457
472
|
await (0, import_qdrant_dashboard.openQdrantDashboard)(cwd);
|
|
458
473
|
}
|
|
459
474
|
}
|