specweave 1.0.588 → 1.0.590
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/.claude-plugin/marketplace.json +2 -2
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.js +9 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -1
- 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 +75 -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/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 +22 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +227 -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/package.json +6 -4
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/hooks/hooks.json +5 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.590",
|
|
4
4
|
"description": "SpecWeave - Spec-Driven Development Framework. PM-led planning, intelligent model selection, living documentation, multi-tool support.",
|
|
5
5
|
"owner": {
|
|
6
6
|
"name": "Anton Abyzov",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"description": "SpecWeave unified plugin — increment lifecycle, living docs, PM-led planning, GitHub/JIRA/ADO sync, docs generation, release management, diagrams, AI media",
|
|
13
13
|
"source": "./plugins/specweave",
|
|
14
14
|
"category": "development",
|
|
15
|
-
"version": "1.0.
|
|
15
|
+
"version": "1.0.590",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Anton Abyzov",
|
|
18
18
|
"email": "anton.abyzov@gmail.com"
|
|
@@ -1 +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;
|
|
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;AAyBxD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CAwFrB"}
|
|
@@ -19,6 +19,15 @@ const HANDLERS = {
|
|
|
19
19
|
// Gated Stop variant — fires only under an active auto session (AC-US7-02).
|
|
20
20
|
// Maps the module's `handleStop` export onto the router's `handle` contract.
|
|
21
21
|
'stop': () => import('./pre-compact.js').then((m) => ({ handle: m.handleStop })),
|
|
22
|
+
// Restored in 0870 — these were dead since 0f81519b1 (hooks.json invoked them
|
|
23
|
+
// but the router never registered them, so they silently no-op'd).
|
|
24
|
+
'session-start': () => import('./session-start.js'),
|
|
25
|
+
'post-tool-use': () => import('./post-tool-use.js'),
|
|
26
|
+
'post-tool-use-analytics': () => import('./post-tool-use-analytics.js'),
|
|
27
|
+
'stop-reflect': () => import('./stop-reflect.js'),
|
|
28
|
+
'stop-sync': () => import('./stop-sync.js'),
|
|
29
|
+
// Real blocking auto-loop driver (0870 rewrite — see stop-auto.ts).
|
|
30
|
+
'stop-auto': () => import('./stop-auto.js'),
|
|
22
31
|
};
|
|
23
32
|
/**
|
|
24
33
|
* Route a hook event to the correct handler.
|
|
@@ -1 +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;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,+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,qEAAqE;IACrE,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC;IAC/C,4EAA4E;IAC5E,6EAA6E;IAC7E,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-router.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/hook-router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse analytics handler — tracks Skill and Task (subagent) usage.
|
|
3
|
+
*
|
|
4
|
+
* Appends events to `<stateDir>/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;AA0BvD,eAAO,MAAM,MAAM,EAAE,SAoCpB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse analytics handler — tracks Skill and Task (subagent) usage.
|
|
3
|
+
*
|
|
4
|
+
* Appends events to `<stateDir>/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
|
+
timestamp: context.timestamp,
|
|
43
|
+
type: 'skill',
|
|
44
|
+
name: skillName,
|
|
45
|
+
success: true,
|
|
46
|
+
plugin,
|
|
47
|
+
};
|
|
48
|
+
appendEvent(context.stateDir, event);
|
|
49
|
+
return CONTINUE;
|
|
50
|
+
}
|
|
51
|
+
if (toolName === 'Task') {
|
|
52
|
+
const agentType = (ti.subagent_type ?? 'general');
|
|
53
|
+
const event = {
|
|
54
|
+
timestamp: context.timestamp,
|
|
55
|
+
type: 'agent',
|
|
56
|
+
name: agentType,
|
|
57
|
+
success: true,
|
|
58
|
+
plugin: 'specweave',
|
|
59
|
+
};
|
|
60
|
+
appendEvent(context.stateDir, event);
|
|
61
|
+
return CONTINUE;
|
|
62
|
+
}
|
|
63
|
+
return CONTINUE;
|
|
64
|
+
};
|
|
65
|
+
function appendEvent(stateDir, event) {
|
|
66
|
+
try {
|
|
67
|
+
const analyticsDir = path.join(stateDir, 'analytics');
|
|
68
|
+
fs.mkdirSync(analyticsDir, { recursive: true });
|
|
69
|
+
fs.appendFileSync(path.join(analyticsDir, 'events.jsonl'), JSON.stringify(event) + '\n');
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Never throw from analytics
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# 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,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-start hook handler — session initialization.
|
|
3
|
+
*
|
|
4
|
+
* Clears stale auto-mode files, resets context pressure,
|
|
5
|
+
* and performs baseline prompt health check.
|
|
6
|
+
*/
|
|
7
|
+
import type { HandlerFn } from './types.js';
|
|
8
|
+
export declare const handle: HandlerFn;
|
|
9
|
+
//# sourceMappingURL=session-start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-start.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/session-start.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAkC5C,eAAO,MAAM,MAAM,EAAE,SA6EpB,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-start hook handler — session initialization.
|
|
3
|
+
*
|
|
4
|
+
* Clears stale auto-mode files, resets context pressure,
|
|
5
|
+
* and performs baseline prompt health check.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
10
|
+
const WARNING_THRESHOLD = 80000;
|
|
11
|
+
const CRITICAL_THRESHOLD = 120000;
|
|
12
|
+
const SYSTEM_ESTIMATE = 12000;
|
|
13
|
+
const SKILL_BUDGET = 15000;
|
|
14
|
+
const HOOK_PER_TURN = 3000;
|
|
15
|
+
function isStale(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const stat = fs.statSync(filePath);
|
|
18
|
+
return Date.now() - stat.mtimeMs > STALE_THRESHOLD_MS;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function safeRemove(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
fs.unlinkSync(filePath);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function fileSize(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return fs.statSync(filePath).size;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const handle = async (_input, context) => {
|
|
41
|
+
const { projectRoot, stateDir, logsDir, timestamp } = context;
|
|
42
|
+
try {
|
|
43
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { continue: true };
|
|
47
|
+
}
|
|
48
|
+
// --- Auto-mode cleanup: remove stale session files (>24h) ---
|
|
49
|
+
const autoFile = path.join(stateDir, 'auto-mode.json');
|
|
50
|
+
if (fs.existsSync(autoFile) && isStale(autoFile)) {
|
|
51
|
+
safeRemove(autoFile);
|
|
52
|
+
safeRemove(path.join(stateDir, '.stop-auto-dedup'));
|
|
53
|
+
safeRemove(path.join(stateDir, '.stop-auto-dedup-prev'));
|
|
54
|
+
safeRemove(path.join(stateDir, '.stop-auto-turns'));
|
|
55
|
+
try {
|
|
56
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
57
|
+
fs.appendFileSync(path.join(logsDir, 'session.log'), `[${timestamp}] SessionStart: Cleared stale auto-mode session files (>24h)\n`);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Never throw from logging
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// --- Context pressure reset ---
|
|
64
|
+
safeRemove(path.join(stateDir, 'context-pressure.json'));
|
|
65
|
+
safeRemove(path.join(stateDir, 'prompt-health-alert.json'));
|
|
66
|
+
// --- Baseline health check ---
|
|
67
|
+
try {
|
|
68
|
+
const claudeMdSize = fileSize(path.join(projectRoot, 'CLAUDE.md'));
|
|
69
|
+
const memoryDir = path.join(projectRoot, '.claude', 'projects');
|
|
70
|
+
let memoryMdSize = 0;
|
|
71
|
+
try {
|
|
72
|
+
// Find MEMORY.md in any project subdirectory
|
|
73
|
+
if (fs.existsSync(memoryDir)) {
|
|
74
|
+
const entries = fs.readdirSync(memoryDir, { withFileTypes: true });
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
const memPath = path.join(memoryDir, entry.name, 'memory', 'MEMORY.md');
|
|
78
|
+
memoryMdSize += fileSize(memPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Best-effort
|
|
85
|
+
}
|
|
86
|
+
const baseline = claudeMdSize + memoryMdSize + SYSTEM_ESTIMATE + SKILL_BUDGET + HOOK_PER_TURN;
|
|
87
|
+
let warningLevel = 'normal';
|
|
88
|
+
if (baseline > CRITICAL_THRESHOLD) {
|
|
89
|
+
warningLevel = 'critical';
|
|
90
|
+
}
|
|
91
|
+
else if (baseline > WARNING_THRESHOLD) {
|
|
92
|
+
warningLevel = 'warning';
|
|
93
|
+
}
|
|
94
|
+
const health = {
|
|
95
|
+
baseline,
|
|
96
|
+
claudeMdSize,
|
|
97
|
+
memoryMdSize,
|
|
98
|
+
skillBudget: SKILL_BUDGET,
|
|
99
|
+
systemEstimate: SYSTEM_ESTIMATE,
|
|
100
|
+
hookPerTurn: HOOK_PER_TURN,
|
|
101
|
+
warningLevel,
|
|
102
|
+
checkedAt: timestamp,
|
|
103
|
+
};
|
|
104
|
+
fs.writeFileSync(path.join(stateDir, 'prompt-health.json'), JSON.stringify(health));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Health check is best-effort
|
|
108
|
+
}
|
|
109
|
+
return { continue: true };
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=session-start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/session-start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-auto hook handler — the autonomous-loop driver for `/sw:auto`.
|
|
3
|
+
*
|
|
4
|
+
* Fires when a session ends. Unlike the other Stop hooks, this one is allowed to
|
|
5
|
+
* BLOCK so that Claude Code re-prompts the model and the auto loop continues
|
|
6
|
+
* (see plugins/specweave/skills/auto/SKILL.md "Core Loop"):
|
|
7
|
+
*
|
|
8
|
+
* IMPLEMENT -> TEST -> FIX -> PASS -> NEXT -> ... -> ALL DONE -> sw:done --auto
|
|
9
|
+
*
|
|
10
|
+
* Decision table (any throw short-circuits to `approve` so ordinary sessions are
|
|
11
|
+
* never trapped):
|
|
12
|
+
*
|
|
13
|
+
* no auto-mode.json | active !== true | stale -> reset .stop-auto-turns; { approve }
|
|
14
|
+
* turns > auto.maxTurns (default 20) -> clear counter; { approve } // safety stop
|
|
15
|
+
* 0 pending tasks AND all ACs satisfied -> clear counter; { block: all_complete_needs_closure }
|
|
16
|
+
* otherwise (work remains) -> increment counter; { block: <P> task(s) / <A> AC(s) remain }
|
|
17
|
+
*
|
|
18
|
+
* @module core/hooks/handlers/stop-auto
|
|
19
|
+
*/
|
|
20
|
+
import type { HandlerFn } from './types.js';
|
|
21
|
+
export declare const handle: HandlerFn;
|
|
22
|
+
//# sourceMappingURL=stop-auto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-auto.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAsJzD,eAAO,MAAM,MAAM,EAAE,SAiEpB,CAAC"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-auto hook handler — the autonomous-loop driver for `/sw:auto`.
|
|
3
|
+
*
|
|
4
|
+
* Fires when a session ends. Unlike the other Stop hooks, this one is allowed to
|
|
5
|
+
* BLOCK so that Claude Code re-prompts the model and the auto loop continues
|
|
6
|
+
* (see plugins/specweave/skills/auto/SKILL.md "Core Loop"):
|
|
7
|
+
*
|
|
8
|
+
* IMPLEMENT -> TEST -> FIX -> PASS -> NEXT -> ... -> ALL DONE -> sw:done --auto
|
|
9
|
+
*
|
|
10
|
+
* Decision table (any throw short-circuits to `approve` so ordinary sessions are
|
|
11
|
+
* never trapped):
|
|
12
|
+
*
|
|
13
|
+
* no auto-mode.json | active !== true | stale -> reset .stop-auto-turns; { approve }
|
|
14
|
+
* turns > auto.maxTurns (default 20) -> clear counter; { approve } // safety stop
|
|
15
|
+
* 0 pending tasks AND all ACs satisfied -> clear counter; { block: all_complete_needs_closure }
|
|
16
|
+
* otherwise (work remains) -> increment counter; { block: <P> task(s) / <A> AC(s) remain }
|
|
17
|
+
*
|
|
18
|
+
* @module core/hooks/handlers/stop-auto
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from 'fs';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import { logHook } from './utils.js';
|
|
23
|
+
import { calculateProgressFromTasksFile } from '../../../progress/us-progress-tracker.js';
|
|
24
|
+
/** Read JSON safely, return null on error */
|
|
25
|
+
function readJsonSafe(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the auto-mode session file (per-session if present, else global). */
|
|
34
|
+
function getSessionPath(stateDir) {
|
|
35
|
+
const sessionId = process.env.CLAUDE_SESSION_ID;
|
|
36
|
+
if (sessionId) {
|
|
37
|
+
const perSession = path.join(stateDir, 'sessions', sessionId, 'auto-mode.json');
|
|
38
|
+
if (fs.existsSync(perSession))
|
|
39
|
+
return perSession;
|
|
40
|
+
}
|
|
41
|
+
return path.join(stateDir, 'auto-mode.json');
|
|
42
|
+
}
|
|
43
|
+
/** True when the session file's mtime is older than maxSessionAge seconds. */
|
|
44
|
+
function isSessionStale(sessionPath, maxSessionAge) {
|
|
45
|
+
try {
|
|
46
|
+
const ageMs = Date.now() - fs.statSync(sessionPath).mtimeMs;
|
|
47
|
+
return ageMs > maxSessionAge * 1000;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const TURNS_FILE = '.stop-auto-turns';
|
|
54
|
+
function readTurns(stateDir) {
|
|
55
|
+
try {
|
|
56
|
+
const n = parseInt(fs.readFileSync(path.join(stateDir, TURNS_FILE), 'utf8').trim(), 10);
|
|
57
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function writeTurns(stateDir, n) {
|
|
64
|
+
try {
|
|
65
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
66
|
+
fs.writeFileSync(path.join(stateDir, TURNS_FILE), String(n));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Never throw from counter bookkeeping
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function resetTurns(stateDir) {
|
|
73
|
+
try {
|
|
74
|
+
fs.unlinkSync(path.join(stateDir, TURNS_FILE));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Already absent — fine
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Count satisfied/total ACs from a spec.md by `- [ ]`/`- [x]` AC lines. */
|
|
81
|
+
function countAcs(specPath) {
|
|
82
|
+
let total = 0;
|
|
83
|
+
let satisfied = 0;
|
|
84
|
+
try {
|
|
85
|
+
const text = fs.readFileSync(specPath, 'utf8');
|
|
86
|
+
for (const line of text.split('\n')) {
|
|
87
|
+
// Match an AC checkbox line: `- [ ] **AC-US1-01**: ...` (id optional but must mention AC).
|
|
88
|
+
const m = line.match(/^\s*-\s*\[([ xX])\]\s*\*{0,2}AC[-\w]/);
|
|
89
|
+
if (!m)
|
|
90
|
+
continue;
|
|
91
|
+
total++;
|
|
92
|
+
if (m[1].toLowerCase() === 'x')
|
|
93
|
+
satisfied++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// No spec / unreadable — treat as no ACs.
|
|
98
|
+
}
|
|
99
|
+
return { total, satisfied };
|
|
100
|
+
}
|
|
101
|
+
/** Increment ids from the session marker, falling back to an active-increment scan. */
|
|
102
|
+
function resolveIncrementIds(session, projectRoot) {
|
|
103
|
+
const fromMarker = Array.isArray(session.incrementIds)
|
|
104
|
+
? session.incrementIds.filter((x) => typeof x === 'string')
|
|
105
|
+
: [];
|
|
106
|
+
if (fromMarker.length > 0)
|
|
107
|
+
return fromMarker;
|
|
108
|
+
const incDir = path.join(projectRoot, '.specweave', 'increments');
|
|
109
|
+
const active = [];
|
|
110
|
+
try {
|
|
111
|
+
for (const dir of fs.readdirSync(incDir)) {
|
|
112
|
+
const meta = readJsonSafe(path.join(incDir, dir, 'metadata.json'));
|
|
113
|
+
if (meta && (meta.status === 'active' || meta.status === 'in-progress')) {
|
|
114
|
+
active.push(dir);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// No increments dir — empty.
|
|
120
|
+
}
|
|
121
|
+
return active;
|
|
122
|
+
}
|
|
123
|
+
/** Resolve the on-disk increment directory for an id (exact, else prefix match). */
|
|
124
|
+
function findIncrementDir(projectRoot, id) {
|
|
125
|
+
const incDir = path.join(projectRoot, '.specweave', 'increments');
|
|
126
|
+
const exact = path.join(incDir, id);
|
|
127
|
+
if (fs.existsSync(exact))
|
|
128
|
+
return exact;
|
|
129
|
+
try {
|
|
130
|
+
const prefix = id.match(/^\d{4}/)?.[0] ?? id;
|
|
131
|
+
for (const dir of fs.readdirSync(incDir)) {
|
|
132
|
+
if (dir.startsWith(prefix))
|
|
133
|
+
return path.join(incDir, dir);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// fall through
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/** Aggregate remaining work (pending tasks + unsatisfied ACs) across increments. */
|
|
142
|
+
async function computeRemaining(projectRoot, incrementIds) {
|
|
143
|
+
let pendingTasks = 0;
|
|
144
|
+
let pendingAcs = 0;
|
|
145
|
+
let primaryId = incrementIds[0] ?? '';
|
|
146
|
+
for (const id of incrementIds) {
|
|
147
|
+
const dir = findIncrementDir(projectRoot, id);
|
|
148
|
+
if (!dir)
|
|
149
|
+
continue;
|
|
150
|
+
// Tasks — prefer the shared parser for accurate same-line/status handling.
|
|
151
|
+
const tasksPath = path.join(dir, 'tasks.md');
|
|
152
|
+
if (fs.existsSync(tasksPath)) {
|
|
153
|
+
try {
|
|
154
|
+
const progress = await calculateProgressFromTasksFile(tasksPath);
|
|
155
|
+
pendingTasks += progress.pendingTasks + progress.inProgressTasks;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Unparseable tasks file — skip its tasks.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ACs — parse spec.md checkbox lines.
|
|
162
|
+
const { total, satisfied } = countAcs(path.join(dir, 'spec.md'));
|
|
163
|
+
pendingAcs += total - satisfied;
|
|
164
|
+
}
|
|
165
|
+
return { pendingTasks, pendingAcs, primaryId };
|
|
166
|
+
}
|
|
167
|
+
export const handle = async (_input, context) => {
|
|
168
|
+
const { stateDir, projectRoot, configPath } = context;
|
|
169
|
+
try {
|
|
170
|
+
// 1. No auto session -> approve (and make sure the counter is reset).
|
|
171
|
+
const sessionPath = getSessionPath(stateDir);
|
|
172
|
+
const session = fs.existsSync(sessionPath) ? readJsonSafe(sessionPath) : null;
|
|
173
|
+
if (!session || session.active !== true) {
|
|
174
|
+
resetTurns(stateDir);
|
|
175
|
+
logHook(context, 'stop-auto', 'No active auto session — approve');
|
|
176
|
+
return { decision: 'approve' };
|
|
177
|
+
}
|
|
178
|
+
// 2. Stale session -> approve.
|
|
179
|
+
const config = readJsonSafe(configPath);
|
|
180
|
+
const maxSessionAge = config?.auto?.maxSessionAge ?? 7200;
|
|
181
|
+
if (isSessionStale(sessionPath, maxSessionAge)) {
|
|
182
|
+
resetTurns(stateDir);
|
|
183
|
+
logHook(context, 'stop-auto', 'Stale auto session — approve');
|
|
184
|
+
return { decision: 'approve' };
|
|
185
|
+
}
|
|
186
|
+
// 3. Turn-counter safety stop -> approve.
|
|
187
|
+
const maxTurns = config?.auto?.maxTurns ?? 20;
|
|
188
|
+
const turns = readTurns(stateDir);
|
|
189
|
+
if (turns > maxTurns) {
|
|
190
|
+
resetTurns(stateDir);
|
|
191
|
+
logHook(context, 'stop-auto', `Safety stop: turns ${turns} > maxTurns ${maxTurns} — approve`);
|
|
192
|
+
return { decision: 'approve' };
|
|
193
|
+
}
|
|
194
|
+
// 4. Compute remaining work.
|
|
195
|
+
const incrementIds = resolveIncrementIds(session, projectRoot);
|
|
196
|
+
// No increment to act on (empty marker + none active) -> approve rather than
|
|
197
|
+
// spuriously blocking for closure on a 0/0 count.
|
|
198
|
+
if (incrementIds.length === 0) {
|
|
199
|
+
resetTurns(stateDir);
|
|
200
|
+
logHook(context, 'stop-auto', 'Auto session active but no increment to work on — approve');
|
|
201
|
+
return { decision: 'approve' };
|
|
202
|
+
}
|
|
203
|
+
const { pendingTasks, pendingAcs, primaryId } = await computeRemaining(projectRoot, incrementIds);
|
|
204
|
+
const idLabel = primaryId || incrementIds[0] || 'active increment';
|
|
205
|
+
// 5. All complete -> block for closure (the trigger sw:auto consumes).
|
|
206
|
+
if (pendingTasks === 0 && pendingAcs === 0) {
|
|
207
|
+
resetTurns(stateDir);
|
|
208
|
+
logHook(context, 'stop-auto', `${idLabel}: all_complete_needs_closure`);
|
|
209
|
+
return {
|
|
210
|
+
decision: 'block',
|
|
211
|
+
reason: `${idLabel}: all_complete_needs_closure — run sw:done --auto ${idLabel}`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// 6. Work remains -> block to continue + increment the counter.
|
|
215
|
+
writeTurns(stateDir, turns + 1);
|
|
216
|
+
const userGoal = typeof session.userGoal === 'string' && session.userGoal ? session.userGoal : '';
|
|
217
|
+
const reason = `${idLabel}: ${pendingTasks} task(s) / ${pendingAcs} AC(s) remain. Run sw:do to continue.` +
|
|
218
|
+
(userGoal ? ` Goal: ${userGoal}` : '');
|
|
219
|
+
logHook(context, 'stop-auto', reason);
|
|
220
|
+
return { decision: 'block', reason };
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Never throw — any failure must not trap the session.
|
|
224
|
+
return { decision: 'approve' };
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=stop-auto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-auto.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-auto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-reflect hook handler.
|
|
3
|
+
*
|
|
4
|
+
* Fires when a session ends. Checks if reflection is enabled in config.
|
|
5
|
+
* If enabled, logs that reflection was requested (actual reflection logic
|
|
6
|
+
* will call `specweave reflect-stop` CLI in a follow-up).
|
|
7
|
+
*
|
|
8
|
+
* NEVER blocks — always returns approve.
|
|
9
|
+
*
|
|
10
|
+
* @module core/hooks/handlers/stop-reflect
|
|
11
|
+
*/
|
|
12
|
+
import type { HandlerFn } from './types.js';
|
|
13
|
+
export declare const handle: HandlerFn;
|
|
14
|
+
//# sourceMappingURL=stop-reflect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-reflect.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-reflect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAe5C,eAAO,MAAM,MAAM,EAAE,SAgBpB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-reflect hook handler.
|
|
3
|
+
*
|
|
4
|
+
* Fires when a session ends. Checks if reflection is enabled in config.
|
|
5
|
+
* If enabled, logs that reflection was requested (actual reflection logic
|
|
6
|
+
* will call `specweave reflect-stop` CLI in a follow-up).
|
|
7
|
+
*
|
|
8
|
+
* NEVER blocks — always returns approve.
|
|
9
|
+
*
|
|
10
|
+
* @module core/hooks/handlers/stop-reflect
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import { logHook } from './utils.js';
|
|
14
|
+
/** Check if reflect is enabled in config. Default: true */
|
|
15
|
+
function isReflectEnabled(configPath) {
|
|
16
|
+
try {
|
|
17
|
+
if (!fs.existsSync(configPath))
|
|
18
|
+
return true;
|
|
19
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
20
|
+
// Note: explicit false check — missing key defaults to enabled
|
|
21
|
+
return config.reflect?.enabled !== false;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return true; // Default enabled on error
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const handle = async (input, context) => {
|
|
28
|
+
try {
|
|
29
|
+
const enabled = isReflectEnabled(context.configPath);
|
|
30
|
+
if (!enabled) {
|
|
31
|
+
logHook(context, 'stop-reflect', 'Reflection disabled in config');
|
|
32
|
+
return { decision: 'approve' };
|
|
33
|
+
}
|
|
34
|
+
// Reflection is enabled — log intent
|
|
35
|
+
logHook(context, 'stop-reflect', 'Reflection requested at session end');
|
|
36
|
+
return { decision: 'approve' };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Never throw — always approve
|
|
40
|
+
return { decision: 'approve' };
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=stop-reflect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-reflect.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-reflect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,2DAA2D;AAC3D,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/D,+DAA+D;QAC/D,OAAO,MAAM,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,2BAA2B;IAC1C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAc,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAErD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,+BAA+B,CAAC,CAAC;YAClE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,qCAAqC;QACrC,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,qCAAqC,CAAC,CAAC;QACxE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-sync hook handler.
|
|
3
|
+
*
|
|
4
|
+
* Fires at session end (after stop-reflect and stop-auto). Reads pending
|
|
5
|
+
* sync events, deduplicates by increment ID, logs sync intent, and clears
|
|
6
|
+
* the event queue. Actual sync calls (LivingDocsSync, GitHub/JIRA/ADO)
|
|
7
|
+
* will be added in a follow-up.
|
|
8
|
+
*
|
|
9
|
+
* NEVER blocks — always returns approve.
|
|
10
|
+
*
|
|
11
|
+
* @module core/hooks/handlers/stop-sync
|
|
12
|
+
*/
|
|
13
|
+
import type { HandlerFn } from './types.js';
|
|
14
|
+
export declare const handle: HandlerFn;
|
|
15
|
+
//# sourceMappingURL=stop-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-sync.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoB5C,eAAO,MAAM,MAAM,EAAE,SA+CpB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop-sync hook handler.
|
|
3
|
+
*
|
|
4
|
+
* Fires at session end (after stop-reflect and stop-auto). Reads pending
|
|
5
|
+
* sync events, deduplicates by increment ID, logs sync intent, and clears
|
|
6
|
+
* the event queue. Actual sync calls (LivingDocsSync, GitHub/JIRA/ADO)
|
|
7
|
+
* will be added in a follow-up.
|
|
8
|
+
*
|
|
9
|
+
* NEVER blocks — always returns approve.
|
|
10
|
+
*
|
|
11
|
+
* @module core/hooks/handlers/stop-sync
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { logHook } from './utils.js';
|
|
16
|
+
/** Parse a JSONL line safely */
|
|
17
|
+
function parseEventLine(line) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(line);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export const handle = async (input, context) => {
|
|
26
|
+
try {
|
|
27
|
+
const pendingPath = path.join(context.stateDir, 'event-queue', 'pending.jsonl');
|
|
28
|
+
// 1. Check if pending events exist
|
|
29
|
+
if (!fs.existsSync(pendingPath)) {
|
|
30
|
+
logHook(context, 'stop-sync', 'No pending events');
|
|
31
|
+
return { decision: 'approve' };
|
|
32
|
+
}
|
|
33
|
+
const content = fs.readFileSync(pendingPath, 'utf8').trim();
|
|
34
|
+
if (!content) {
|
|
35
|
+
logHook(context, 'stop-sync', 'No pending events');
|
|
36
|
+
return { decision: 'approve' };
|
|
37
|
+
}
|
|
38
|
+
// 2. Parse and deduplicate events by increment ID
|
|
39
|
+
const lines = content.split('\n').filter((l) => l.trim());
|
|
40
|
+
const seenIncrements = new Set();
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
const event = parseEventLine(line);
|
|
43
|
+
if (!event?.incrementId)
|
|
44
|
+
continue;
|
|
45
|
+
const incId = event.incrementId;
|
|
46
|
+
if (seenIncrements.has(incId))
|
|
47
|
+
continue;
|
|
48
|
+
seenIncrements.add(incId);
|
|
49
|
+
logHook(context, 'stop-sync', `Sync requested for increment: ${incId} (event: ${event.event ?? 'unknown'})`);
|
|
50
|
+
}
|
|
51
|
+
// 3. Clear processed events
|
|
52
|
+
try {
|
|
53
|
+
fs.writeFileSync(pendingPath, '');
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Non-critical — events may be re-processed next time
|
|
57
|
+
}
|
|
58
|
+
if (seenIncrements.size > 0) {
|
|
59
|
+
logHook(context, 'stop-sync', `Processed ${seenIncrements.size} unique increment(s)`);
|
|
60
|
+
}
|
|
61
|
+
return { decision: 'approve' };
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Never throw — always approve
|
|
65
|
+
return { decision: 'approve' };
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=stop-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-sync.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAUrC,gCAAgC;AAChC,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAc,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;QAEhF,mCAAmC;QACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,kDAAkD;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,WAAW;gBAAE,SAAS;YAElC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC;YAChC,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YAExC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,iCAAiC,KAAK,YAAY,KAAK,CAAC,KAAK,IAAI,SAAS,GAAG,CAAC,CAAC;QAC/G,CAAC;QAED,4BAA4B;QAC5B,IAAI,CAAC;YACH,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,sDAAsD;QACxD,CAAC;QAED,IAAI,cAAc,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,cAAc,CAAC,IAAI,sBAAsB,CAAC,CAAC;QACxF,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.590",
|
|
4
4
|
"description": "100+ domain-expert AI skills — PM, Architect, Frontend, QA, Security and more. Skills learn your team's patterns permanently. Spec-first planning, autonomous execution, multi-agent teams, synced to GitHub/JIRA. Claude Code, Cursor, Copilot & more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"preinstall": "node scripts/check-node-version.js || exit 0",
|
|
12
12
|
"preuninstall": "node scripts/preuninstall.cjs",
|
|
13
|
-
"version": "node scripts/build/auto-changelog.js && git add CHANGELOG.md",
|
|
13
|
+
"version": "node scripts/build/auto-changelog.js && node scripts/build/stamp-plugin-version.cjs && git add CHANGELOG.md plugins/specweave/.claude-plugin/plugin.json .claude-plugin/marketplace.json",
|
|
14
14
|
"postversion": "echo '✅ Version bumped with CHANGELOG entry'",
|
|
15
15
|
"clean": "rm -rf dist/",
|
|
16
|
-
"build": "tsc && npm run build:dashboard && npm run copy:locales && npm run copy:plugins && npm run copy:hook-deps && npm run copy:adapters",
|
|
16
|
+
"build": "tsc && npm run build:dashboard && npm run copy:locales && npm run copy:plugins && npm run copy:hook-deps && npm run copy:adapters && npm run stamp:plugin-version",
|
|
17
|
+
"stamp:plugin-version": "node scripts/build/stamp-plugin-version.cjs",
|
|
18
|
+
"validate:versions": "node scripts/validation/validate-versions.cjs",
|
|
17
19
|
"build:dashboard": "npx vite build --config src/dashboard/client/vite.config.ts",
|
|
18
20
|
"copy:adapters": "node scripts/build/copy-adapters.js",
|
|
19
21
|
"rebuild": "npm run clean && npm run build",
|
|
@@ -21,7 +23,7 @@
|
|
|
21
23
|
"copy:plugins": "node scripts/build/copy-plugin-js.js",
|
|
22
24
|
"copy:hook-deps": "node scripts/build/copy-hook-dependencies.js",
|
|
23
25
|
"dev": "tsc --watch",
|
|
24
|
-
"prepublishOnly": "npm run rebuild",
|
|
26
|
+
"prepublishOnly": "npm run rebuild && npm run validate:versions",
|
|
25
27
|
"test:unit": "vitest run tests/unit --coverage",
|
|
26
28
|
"test:unit:fast": "vitest run --config vitest.unit.config.ts",
|
|
27
29
|
"test:integration": "vitest run tests/integration --coverage",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sw",
|
|
3
3
|
"description": "SpecWeave unified plugin. Provides increment planning (PM, Architect, Tech Lead agents), specification generation, TDD workflow, living docs sync, brownfield support, GitHub/JIRA/ADO bidirectional sync, documentation generation, release management, architecture diagrams, and AI media generation.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.590",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SpecWeave Team",
|
|
7
7
|
"url": "https://spec-weave.com"
|
|
@@ -89,6 +89,11 @@
|
|
|
89
89
|
"type": "command",
|
|
90
90
|
"command": "bash -c 'command -v specweave &>/dev/null && specweave hook stop-sync || { cat >/dev/null; echo \"{\\\"decision\\\":\\\"approve\\\"}\"; }'",
|
|
91
91
|
"timeout": 15000
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "command",
|
|
95
|
+
"command": "bash -c 'command -v specweave &>/dev/null && specweave hook stop || { cat >/dev/null; echo \"{\\\"decision\\\":\\\"approve\\\"}\"; }'",
|
|
96
|
+
"timeout": 15000
|
|
92
97
|
}
|
|
93
98
|
]
|
|
94
99
|
}
|