specweave 1.0.551 → 1.0.552
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/bin/specweave.js +23 -1
- package/dist/src/cli/commands/hook.d.ts +15 -0
- package/dist/src/cli/commands/hook.d.ts.map +1 -0
- package/dist/src/cli/commands/hook.js +61 -0
- package/dist/src/cli/commands/hook.js.map +1 -0
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/hook-router.js +75 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
- package/dist/src/core/hooks/handlers/index.d.ts +10 -0
- package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/index.js +9 -0
- package/dist/src/core/hooks/handlers/index.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.js +111 -0
- package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
- package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
- package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +63 -0
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/types.js +27 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
- package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/utils.js +64 -0
- package/dist/src/core/hooks/handlers/utils.js.map +1 -0
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/sync/sync-target-resolver.js.map +1 -1
- package/dist/src/utils/lock-manager.d.ts.map +1 -1
- package/dist/src/utils/lock-manager.js +5 -0
- package/dist/src/utils/lock-manager.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +3 -30
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/hooks.json +10 -10
- package/plugins/specweave/hooks/README.md +0 -493
- package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
- package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
- package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
- package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
- package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
- package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
- package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
- package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
- package/plugins/specweave/hooks/log-decision.sh +0 -168
- package/plugins/specweave/hooks/pre-compact.sh +0 -64
- package/plugins/specweave/hooks/startup-health-check.sh +0 -64
- package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
- package/plugins/specweave/hooks/stop-reflect.sh +0 -336
- package/plugins/specweave/hooks/stop-sync.sh +0 -283
- package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
- package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
- package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
- package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
- package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
- package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
- package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
- package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
- package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
- package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
- package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
- package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
- package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
- package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
- package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
- package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
- package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
- package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
- package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
- package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
- package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
- package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
- package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
- package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
- package/plugins/specweave/hooks/v2/session-end.sh +0 -139
- package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
package/bin/specweave.js
CHANGED
|
@@ -1244,6 +1244,28 @@ program
|
|
|
1244
1244
|
}
|
|
1245
1245
|
});
|
|
1246
1246
|
|
|
1247
|
+
// Hook command - CLI delegation entry point for Claude Code hooks (internal)
|
|
1248
|
+
program
|
|
1249
|
+
.command('hook <event-type>')
|
|
1250
|
+
.description('Handle Claude Code hook events (internal)')
|
|
1251
|
+
.action(async (eventType) => {
|
|
1252
|
+
try {
|
|
1253
|
+
const { handleHook } = await import('../dist/src/cli/commands/hook.js');
|
|
1254
|
+
await handleHook(eventType);
|
|
1255
|
+
} catch {
|
|
1256
|
+
// Never crash — output safe default
|
|
1257
|
+
const defaults = {
|
|
1258
|
+
'user-prompt-submit': '{"decision":"approve"}',
|
|
1259
|
+
'pre-tool-use': '{"decision":"allow"}',
|
|
1260
|
+
'stop-reflect': '{"decision":"approve"}',
|
|
1261
|
+
'stop-auto': '{"decision":"approve"}',
|
|
1262
|
+
'stop-sync': '{"decision":"approve"}',
|
|
1263
|
+
};
|
|
1264
|
+
process.stdout.write(defaults[eventType] || '{"continue":true}');
|
|
1265
|
+
}
|
|
1266
|
+
process.exit(0);
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1247
1269
|
// Detect intent command - Hook helper for automatic plugin loading (internal)
|
|
1248
1270
|
program
|
|
1249
1271
|
.command('detect-intent [prompt]')
|
|
@@ -1556,7 +1578,7 @@ program
|
|
|
1556
1578
|
await checkForDuplicates();
|
|
1557
1579
|
|
|
1558
1580
|
// Hide internal-only commands from --help (still callable by hooks)
|
|
1559
|
-
for (const name of ['detect-intent', 'evaluate-completion', 'reflect-stop', 'detect-project']) {
|
|
1581
|
+
for (const name of ['hook', 'detect-intent', 'evaluate-completion', 'reflect-stop', 'detect-project']) {
|
|
1560
1582
|
const cmd = program.commands.find(c => c.name() === name);
|
|
1561
1583
|
if (cmd) cmd._hidden = true;
|
|
1562
1584
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook CLI Command — entry point for Claude Code hook delegation.
|
|
3
|
+
*
|
|
4
|
+
* Usage: specweave hook <event-type>
|
|
5
|
+
*
|
|
6
|
+
* Reads JSON from stdin, dispatches to the correct handler via hook-router,
|
|
7
|
+
* writes JSON to stdout. Always exits 0 — never crashes Claude Code.
|
|
8
|
+
*
|
|
9
|
+
* @module cli/commands/hook
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Main hook handler entry point.
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleHook(eventType: string): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsCH;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook CLI Command — entry point for Claude Code hook delegation.
|
|
3
|
+
*
|
|
4
|
+
* Usage: specweave hook <event-type>
|
|
5
|
+
*
|
|
6
|
+
* Reads JSON from stdin, dispatches to the correct handler via hook-router,
|
|
7
|
+
* writes JSON to stdout. Always exits 0 — never crashes Claude Code.
|
|
8
|
+
*
|
|
9
|
+
* @module cli/commands/hook
|
|
10
|
+
*/
|
|
11
|
+
import { hookRouter } from '../../core/hooks/handlers/hook-router.js';
|
|
12
|
+
import { getSafeDefault } from '../../core/hooks/handlers/types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Read all of stdin as a string.
|
|
15
|
+
*/
|
|
16
|
+
async function readStdin() {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
const chunks = [];
|
|
19
|
+
const timeout = setTimeout(() => {
|
|
20
|
+
process.stdin.removeAllListeners();
|
|
21
|
+
resolve(Buffer.concat(chunks).toString('utf-8'));
|
|
22
|
+
}, 5000);
|
|
23
|
+
process.stdin.on('data', (chunk) => {
|
|
24
|
+
chunks.push(chunk);
|
|
25
|
+
});
|
|
26
|
+
process.stdin.on('end', () => {
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
resolve(Buffer.concat(chunks).toString('utf-8'));
|
|
29
|
+
});
|
|
30
|
+
process.stdin.on('error', () => {
|
|
31
|
+
clearTimeout(timeout);
|
|
32
|
+
resolve('');
|
|
33
|
+
});
|
|
34
|
+
// If stdin is already ended (no pipe), resolve immediately
|
|
35
|
+
if (process.stdin.readableEnded) {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
resolve(Buffer.concat(chunks).toString('utf-8'));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Main hook handler entry point.
|
|
43
|
+
*/
|
|
44
|
+
export async function handleHook(eventType) {
|
|
45
|
+
// Crash prevention
|
|
46
|
+
process.on('unhandledRejection', () => {
|
|
47
|
+
const fallback = getSafeDefault(eventType);
|
|
48
|
+
process.stdout.write(JSON.stringify(fallback));
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
const rawStdin = await readStdin();
|
|
53
|
+
const result = await hookRouter(eventType, rawStdin);
|
|
54
|
+
process.stdout.write(JSON.stringify(result));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
const fallback = getSafeDefault(eventType);
|
|
58
|
+
process.stdout.write(JSON.stringify(fallback));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE;;GAEG;AACH,KAAK,UAAU,SAAS;IACtB,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;YACnC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YAC3B,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC7B,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,CAAC,EAAE,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,2DAA2D;QAC3D,IAAI,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;YAChC,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAAiB;IAChD,mBAAmB;IACnB,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
|
|
@@ -19,12 +19,12 @@ export declare const ResearchConfigSchema: z.ZodObject<{
|
|
|
19
19
|
iot: "iot";
|
|
20
20
|
gaming: "gaming";
|
|
21
21
|
marketplace: "marketplace";
|
|
22
|
+
blockchain: "blockchain";
|
|
22
23
|
"productivity-saas": "productivity-saas";
|
|
23
24
|
"e-commerce": "e-commerce";
|
|
24
25
|
"social-network": "social-network";
|
|
25
26
|
"enterprise-b2b": "enterprise-b2b";
|
|
26
27
|
"consumer-b2c": "consumer-b2c";
|
|
27
|
-
blockchain: "blockchain";
|
|
28
28
|
"ai-ml": "ai-ml";
|
|
29
29
|
}>;
|
|
30
30
|
competitors: z.ZodArray<z.ZodObject<{
|
|
@@ -190,12 +190,12 @@ export declare const SpecWeaveConfigSchema: z.ZodObject<{
|
|
|
190
190
|
iot: "iot";
|
|
191
191
|
gaming: "gaming";
|
|
192
192
|
marketplace: "marketplace";
|
|
193
|
+
blockchain: "blockchain";
|
|
193
194
|
"productivity-saas": "productivity-saas";
|
|
194
195
|
"e-commerce": "e-commerce";
|
|
195
196
|
"social-network": "social-network";
|
|
196
197
|
"enterprise-b2b": "enterprise-b2b";
|
|
197
198
|
"consumer-b2c": "consumer-b2c";
|
|
198
|
-
blockchain: "blockchain";
|
|
199
199
|
"ai-ml": "ai-ml";
|
|
200
200
|
}>;
|
|
201
201
|
competitors: z.ZodArray<z.ZodObject<{
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook router — the central dispatcher for all hook events.
|
|
3
|
+
*
|
|
4
|
+
* Reads stdin JSON, resolves the project root, dynamically imports the
|
|
5
|
+
* correct handler, and returns JSON. Wraps everything in try/catch so
|
|
6
|
+
* no hook can ever crash.
|
|
7
|
+
*
|
|
8
|
+
* @module core/hooks/handlers/hook-router
|
|
9
|
+
*/
|
|
10
|
+
import type { HookResult } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Route a hook event to the correct handler.
|
|
13
|
+
*
|
|
14
|
+
* @param eventType - The hook event type (e.g. 'pre-compact', 'user-prompt-submit')
|
|
15
|
+
* @param rawStdin - Raw stdin string (JSON from Claude Code)
|
|
16
|
+
* @returns HookResult — always valid JSON, never throws
|
|
17
|
+
*/
|
|
18
|
+
export declare function hookRouter(eventType: string, rawStdin: string): Promise<HookResult>;
|
|
19
|
+
//# sourceMappingURL=hook-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-router.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/hook-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,YAAY,CAAC;AAiBxD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CA+CrB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook router — the central dispatcher for all hook events.
|
|
3
|
+
*
|
|
4
|
+
* Reads stdin JSON, resolves the project root, dynamically imports the
|
|
5
|
+
* correct handler, and returns JSON. Wraps everything in try/catch so
|
|
6
|
+
* no hook can ever crash.
|
|
7
|
+
*
|
|
8
|
+
* @module core/hooks/handlers/hook-router
|
|
9
|
+
*/
|
|
10
|
+
import { getSafeDefault } from './types.js';
|
|
11
|
+
import { findProjectRoot, createContext, parseStdinJson, logHook } from './utils.js';
|
|
12
|
+
/** Dynamic import map — only the requested handler is loaded per invocation */
|
|
13
|
+
const HANDLERS = {
|
|
14
|
+
'user-prompt-submit': () => import('./user-prompt-submit.js'),
|
|
15
|
+
'pre-tool-use': () => import('./pre-tool-use.js'),
|
|
16
|
+
'post-tool-use': () => import('./post-tool-use.js'),
|
|
17
|
+
'post-tool-use-analytics': () => import('./post-tool-use-analytics.js'),
|
|
18
|
+
'session-start': () => import('./session-start.js'),
|
|
19
|
+
'pre-compact': () => import('./pre-compact.js'),
|
|
20
|
+
'stop-reflect': () => import('./stop-reflect.js'),
|
|
21
|
+
'stop-auto': () => import('./stop-auto.js'),
|
|
22
|
+
'stop-sync': () => import('./stop-sync.js'),
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Route a hook event to the correct handler.
|
|
26
|
+
*
|
|
27
|
+
* @param eventType - The hook event type (e.g. 'pre-compact', 'user-prompt-submit')
|
|
28
|
+
* @param rawStdin - Raw stdin string (JSON from Claude Code)
|
|
29
|
+
* @returns HookResult — always valid JSON, never throws
|
|
30
|
+
*/
|
|
31
|
+
export async function hookRouter(eventType, rawStdin) {
|
|
32
|
+
const safeDefault = getSafeDefault(eventType);
|
|
33
|
+
try {
|
|
34
|
+
// Global kill switch
|
|
35
|
+
if (process.env.SPECWEAVE_DISABLE_HOOKS === '1') {
|
|
36
|
+
return safeDefault;
|
|
37
|
+
}
|
|
38
|
+
// Parse stdin
|
|
39
|
+
const input = parseStdinJson(rawStdin);
|
|
40
|
+
// Resolve project root
|
|
41
|
+
const projectRoot = findProjectRoot();
|
|
42
|
+
if (!projectRoot) {
|
|
43
|
+
// Not a SpecWeave project — pass through
|
|
44
|
+
return safeDefault;
|
|
45
|
+
}
|
|
46
|
+
// Build context
|
|
47
|
+
const context = createContext(projectRoot);
|
|
48
|
+
// Find handler
|
|
49
|
+
const loader = HANDLERS[eventType];
|
|
50
|
+
if (!loader) {
|
|
51
|
+
logHook(context, 'router', `Unknown event type: ${eventType}`);
|
|
52
|
+
return safeDefault;
|
|
53
|
+
}
|
|
54
|
+
// Dynamic import + execute
|
|
55
|
+
const handlerModule = await loader();
|
|
56
|
+
const result = await handlerModule.handle(input, context);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Never crash — log and return safe default
|
|
61
|
+
try {
|
|
62
|
+
const projectRoot = findProjectRoot();
|
|
63
|
+
if (projectRoot) {
|
|
64
|
+
const context = createContext(projectRoot);
|
|
65
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
66
|
+
logHook(context, 'router', `Error in ${eventType}: ${msg}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Even logging can fail — swallow everything
|
|
71
|
+
}
|
|
72
|
+
return safeDefault;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=hook-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-router.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/hook-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErF,+EAA+E;AAC/E,MAAM,QAAQ,GAAyD;IACrE,oBAAoB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC;IAC7D,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC;IACjD,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC;IACnD,yBAAyB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,8BAA8B,CAAC;IACvE,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC;IACnD,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC;IAC/C,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC;IACjD,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC3C,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;CAC5C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,SAAiB,EACjB,QAAgB;IAEhB,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,qBAAqB;QACrB,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,EAAE,CAAC;YAChD,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,cAAc;QACd,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QAEvC,uBAAuB;QACvB,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;QACtC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,yCAAyC;YACzC,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,gBAAgB;QAChB,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;QAE3C,eAAe;QACf,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,uBAAuB,SAAS,EAAE,CAAC,CAAC;YAC/D,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,2BAA2B;QAC3B,MAAM,aAAa,GAAG,MAAM,MAAM,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4CAA4C;QAC5C,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;YACtC,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;gBAC3C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACnE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,SAAS,KAAK,GAAG,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook handlers barrel export.
|
|
3
|
+
*
|
|
4
|
+
* @module core/hooks/handlers
|
|
5
|
+
*/
|
|
6
|
+
export type { HandlerFn, HookContext, HookInput, HookResult, HookEventType } from './types.js';
|
|
7
|
+
export { getSafeDefault, SAFE_DEFAULTS } from './types.js';
|
|
8
|
+
export { hookRouter } from './hook-router.js';
|
|
9
|
+
export { findProjectRoot, createContext, parseStdinJson, logHook } from './utils.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook handlers barrel export.
|
|
3
|
+
*
|
|
4
|
+
* @module core/hooks/handlers
|
|
5
|
+
*/
|
|
6
|
+
export { getSafeDefault, SAFE_DEFAULTS } from './types.js';
|
|
7
|
+
export { hookRouter } from './hook-router.js';
|
|
8
|
+
export { findProjectRoot, createContext, parseStdinJson, logHook } from './utils.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse analytics handler — tracks Skill and Task (subagent) usage.
|
|
3
|
+
*
|
|
4
|
+
* Appends events to `.specweave/state/analytics/events.jsonl`.
|
|
5
|
+
* Non-blocking, never throws.
|
|
6
|
+
*
|
|
7
|
+
* @module core/hooks/handlers/post-tool-use-analytics
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerFn } from './types.js';
|
|
10
|
+
export declare const handle: HandlerFn;
|
|
11
|
+
//# sourceMappingURL=post-tool-use-analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use-analytics.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/post-tool-use-analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAyBvD,eAAO,MAAM,MAAM,EAAE,SAkCpB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse analytics handler — tracks Skill and Task (subagent) usage.
|
|
3
|
+
*
|
|
4
|
+
* Appends events to `.specweave/state/analytics/events.jsonl`.
|
|
5
|
+
* Non-blocking, never throws.
|
|
6
|
+
*
|
|
7
|
+
* @module core/hooks/handlers/post-tool-use-analytics
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
const CONTINUE = { continue: true };
|
|
12
|
+
function getToolInput(input) {
|
|
13
|
+
return (input.tool_input ?? input.toolInput ?? {});
|
|
14
|
+
}
|
|
15
|
+
function resolveToolName(input) {
|
|
16
|
+
const explicit = (input.tool_name ?? input.toolName ?? '');
|
|
17
|
+
if (explicit)
|
|
18
|
+
return explicit;
|
|
19
|
+
// Fallback: detect from input fields
|
|
20
|
+
const ti = getToolInput(input);
|
|
21
|
+
if (ti.skill)
|
|
22
|
+
return 'Skill';
|
|
23
|
+
if (ti.subagent_type)
|
|
24
|
+
return 'Task';
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
function extractPlugin(skillName) {
|
|
28
|
+
if (!skillName.includes(':'))
|
|
29
|
+
return 'specweave';
|
|
30
|
+
const prefix = skillName.split(':')[0];
|
|
31
|
+
return prefix === 'sw' ? 'specweave' : prefix;
|
|
32
|
+
}
|
|
33
|
+
export const handle = async (input, context) => {
|
|
34
|
+
const toolName = resolveToolName(input);
|
|
35
|
+
const ti = getToolInput(input);
|
|
36
|
+
if (toolName === 'Skill') {
|
|
37
|
+
const skillName = (ti.skill ?? '');
|
|
38
|
+
if (!skillName)
|
|
39
|
+
return CONTINUE;
|
|
40
|
+
const plugin = extractPlugin(skillName);
|
|
41
|
+
const event = {
|
|
42
|
+
type: 'skill',
|
|
43
|
+
name: skillName,
|
|
44
|
+
plugin,
|
|
45
|
+
timestamp: context.timestamp,
|
|
46
|
+
};
|
|
47
|
+
appendEvent(context.stateDir, event);
|
|
48
|
+
return CONTINUE;
|
|
49
|
+
}
|
|
50
|
+
if (toolName === 'Task') {
|
|
51
|
+
const agentType = (ti.subagent_type ?? 'general');
|
|
52
|
+
const event = {
|
|
53
|
+
type: 'agent',
|
|
54
|
+
name: agentType,
|
|
55
|
+
plugin: 'specweave',
|
|
56
|
+
timestamp: context.timestamp,
|
|
57
|
+
};
|
|
58
|
+
appendEvent(context.stateDir, event);
|
|
59
|
+
return CONTINUE;
|
|
60
|
+
}
|
|
61
|
+
return CONTINUE;
|
|
62
|
+
};
|
|
63
|
+
function appendEvent(stateDir, event) {
|
|
64
|
+
try {
|
|
65
|
+
const analyticsDir = path.join(stateDir, 'analytics');
|
|
66
|
+
fs.mkdirSync(analyticsDir, { recursive: true });
|
|
67
|
+
fs.appendFileSync(path.join(analyticsDir, 'events.jsonl'), JSON.stringify(event) + '\n');
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Never throw from analytics
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=post-tool-use-analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use-analytics.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/post-tool-use-analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse hook handler — non-blocking event detector and queue writer.
|
|
3
|
+
*
|
|
4
|
+
* Detects changes to increment files and queues events to pending.jsonl
|
|
5
|
+
* for later processing by stop-sync.
|
|
6
|
+
*
|
|
7
|
+
* @module core/hooks/handlers/post-tool-use
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerFn } from './types.js';
|
|
10
|
+
export declare const handle: HandlerFn;
|
|
11
|
+
//# sourceMappingURL=post-tool-use.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/post-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AA2CvD,eAAO,MAAM,MAAM,EAAE,SA+CpB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse hook handler — non-blocking event detector and queue writer.
|
|
3
|
+
*
|
|
4
|
+
* Detects changes to increment files and queues events to pending.jsonl
|
|
5
|
+
* for later processing by stop-sync.
|
|
6
|
+
*
|
|
7
|
+
* @module core/hooks/handlers/post-tool-use
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
const CONTINUE = { continue: true };
|
|
12
|
+
function getToolInput(input) {
|
|
13
|
+
return (input.tool_input ?? input.toolInput ?? {});
|
|
14
|
+
}
|
|
15
|
+
function getFilePath(input) {
|
|
16
|
+
const ti = getToolInput(input);
|
|
17
|
+
return (ti.file_path ?? '');
|
|
18
|
+
}
|
|
19
|
+
function extractIncrementId(filePath) {
|
|
20
|
+
const match = filePath.match(/(\d{4}-[^/]+)/);
|
|
21
|
+
return match ? match[1] : null;
|
|
22
|
+
}
|
|
23
|
+
function queueEvent(stateDir, event, incrementId, timestamp, data) {
|
|
24
|
+
try {
|
|
25
|
+
const queueDir = path.join(stateDir, 'event-queue');
|
|
26
|
+
fs.mkdirSync(queueDir, { recursive: true });
|
|
27
|
+
const entry = {
|
|
28
|
+
event,
|
|
29
|
+
incrementId,
|
|
30
|
+
timestamp,
|
|
31
|
+
...(data ? { data } : {}),
|
|
32
|
+
};
|
|
33
|
+
fs.appendFileSync(path.join(queueDir, 'pending.jsonl'), JSON.stringify(entry) + '\n');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Never throw from event queuing
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const handle = async (input, context) => {
|
|
40
|
+
const filePath = getFilePath(input);
|
|
41
|
+
// Ignore non-increment files
|
|
42
|
+
if (!filePath.includes('.specweave/increments/')) {
|
|
43
|
+
return CONTINUE;
|
|
44
|
+
}
|
|
45
|
+
const incrementId = extractIncrementId(filePath);
|
|
46
|
+
if (!incrementId)
|
|
47
|
+
return CONTINUE;
|
|
48
|
+
const basename = path.basename(filePath);
|
|
49
|
+
// metadata.json → detect status changes
|
|
50
|
+
if (basename === 'metadata.json') {
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(filePath)) {
|
|
53
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
54
|
+
const meta = JSON.parse(raw);
|
|
55
|
+
const status = meta.status ?? 'unknown';
|
|
56
|
+
queueEvent(context.stateDir, 'increment.status_changed', incrementId, context.timestamp, { field: 'status', value: status });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Read failure — skip silently
|
|
61
|
+
}
|
|
62
|
+
return CONTINUE;
|
|
63
|
+
}
|
|
64
|
+
// tasks.md → queue task.updated
|
|
65
|
+
if (basename === 'tasks.md') {
|
|
66
|
+
queueEvent(context.stateDir, 'task.updated', incrementId, context.timestamp);
|
|
67
|
+
return CONTINUE;
|
|
68
|
+
}
|
|
69
|
+
// spec.md → queue spec.updated
|
|
70
|
+
if (basename === 'spec.md') {
|
|
71
|
+
queueEvent(context.stateDir, 'spec.updated', incrementId, context.timestamp);
|
|
72
|
+
return CONTINUE;
|
|
73
|
+
}
|
|
74
|
+
return CONTINUE;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=post-tool-use.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/post-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,QAAQ,GAAG,EAAE,QAAQ,EAAE,IAAa,EAAE,CAAC;AAE7C,SAAS,YAAY,CAAC,KAAgB;IACpC,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,IAAI,EAAE,CAA4B,CAAC;AAChF,CAAC;AAED,SAAS,WAAW,CAAC,KAAgB;IACnC,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAAW,CAAC;AACxC,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,SAAS,UAAU,CACjB,QAAgB,EAChB,KAAa,EACb,WAAmB,EACnB,SAAiB,EACjB,IAA8B;IAE9B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACpD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG;YACZ,KAAK;YACL,WAAW;YACX,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1B,CAAC;QACF,EAAE,CAAC,cAAc,CACf,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EACpC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAC7B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAc,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAEpC,6BAA6B;IAC7B,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;QACjD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC;IAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEzC,wCAAwC;IACxC,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC;gBACxC,UAAU,CACR,OAAO,CAAC,QAAQ,EAChB,0BAA0B,EAC1B,WAAW,EACX,OAAO,CAAC,SAAS,EACjB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CACnC,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,gCAAgC;IAChC,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7E,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,+BAA+B;IAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7E,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-compact hook handler — context pressure signal.
|
|
3
|
+
*
|
|
4
|
+
* Fires when Claude Code approaches context limits (before compaction).
|
|
5
|
+
* Tracks compaction count, escalates pressure level, writes health alerts.
|
|
6
|
+
*
|
|
7
|
+
* Escalation: 1st=elevated, 2nd=critical, 3+=emergency
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerFn } from './types.js';
|
|
10
|
+
export declare const handle: HandlerFn;
|
|
11
|
+
//# sourceMappingURL=pre-compact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-compact.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/pre-compact.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqC5C,eAAO,MAAM,MAAM,EAAE,SA0CpB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-compact hook handler — context pressure signal.
|
|
3
|
+
*
|
|
4
|
+
* Fires when Claude Code approaches context limits (before compaction).
|
|
5
|
+
* Tracks compaction count, escalates pressure level, writes health alerts.
|
|
6
|
+
*
|
|
7
|
+
* Escalation: 1st=elevated, 2nd=critical, 3+=emergency
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
function readPressure(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
const data = JSON.parse(raw);
|
|
15
|
+
return typeof data.compactionCount === 'number' ? data.compactionCount : 0;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getLevel(count) {
|
|
22
|
+
if (count >= 3)
|
|
23
|
+
return 'emergency';
|
|
24
|
+
if (count >= 2)
|
|
25
|
+
return 'critical';
|
|
26
|
+
return 'elevated';
|
|
27
|
+
}
|
|
28
|
+
function getAdvice(level) {
|
|
29
|
+
switch (level) {
|
|
30
|
+
case 'elevated':
|
|
31
|
+
return 'Context budget reduced to compact. Session is running long.';
|
|
32
|
+
case 'critical':
|
|
33
|
+
return 'Context budget at minimal. Consider starting a new session or setting contextBudget.level to off.';
|
|
34
|
+
case 'emergency':
|
|
35
|
+
return 'Context budget stripped to off (3+ compactions). Strongly recommend starting a new session.';
|
|
36
|
+
default:
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const handle = async (_input, context) => {
|
|
41
|
+
if (process.env.SPECWEAVE_DISABLE_HOOKS === '1') {
|
|
42
|
+
return { continue: true };
|
|
43
|
+
}
|
|
44
|
+
const { stateDir, logsDir, timestamp } = context;
|
|
45
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
46
|
+
const pressurePath = path.join(stateDir, 'context-pressure.json');
|
|
47
|
+
const prevCount = readPressure(pressurePath);
|
|
48
|
+
const newCount = prevCount + 1;
|
|
49
|
+
const level = getLevel(newCount);
|
|
50
|
+
const advice = getAdvice(level);
|
|
51
|
+
// Write context-pressure.json
|
|
52
|
+
const pressure = {
|
|
53
|
+
level,
|
|
54
|
+
compactionCount: newCount,
|
|
55
|
+
lastCompaction: timestamp,
|
|
56
|
+
};
|
|
57
|
+
fs.writeFileSync(pressurePath, JSON.stringify(pressure));
|
|
58
|
+
// Write prompt-health-alert.json
|
|
59
|
+
const alertPath = path.join(stateDir, 'prompt-health-alert.json');
|
|
60
|
+
fs.writeFileSync(alertPath, JSON.stringify({
|
|
61
|
+
level,
|
|
62
|
+
compactionCount: newCount,
|
|
63
|
+
advice,
|
|
64
|
+
timestamp,
|
|
65
|
+
}));
|
|
66
|
+
// Log compaction event
|
|
67
|
+
try {
|
|
68
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
69
|
+
const logEntry = `[${timestamp}] COMPACTION #${newCount} | level=${level} | ${advice}\n`;
|
|
70
|
+
fs.appendFileSync(path.join(logsDir, 'prompt-health.log'), logEntry);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Never throw from logging
|
|
74
|
+
}
|
|
75
|
+
return { continue: true };
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=pre-compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-compact.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/pre-compact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreToolUse hook handler — inlines all guard logic:
|
|
3
|
+
* 1. Status Completion Guard (Edit metadata.json → "completed")
|
|
4
|
+
* 2. Interview Enforcement Guard (Write spec.md with strict interview)
|
|
5
|
+
* 3. Increment Existence Guard (TeamCreate without valid spec)
|
|
6
|
+
*
|
|
7
|
+
* @module core/hooks/handlers/pre-tool-use
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerFn } from './types.js';
|
|
10
|
+
export declare const handle: HandlerFn;
|
|
11
|
+
//# sourceMappingURL=pre-tool-use.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/pre-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA0B,MAAM,YAAY,CAAC;AA0UpE,eAAO,MAAM,MAAM,EAAE,SA0BpB,CAAC"}
|