@sienklogic/plan-build-run 2.0.0 → 2.0.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/CHANGELOG.md +56 -56
- package/CLAUDE.md +149 -149
- package/LICENSE +21 -21
- package/README.md +247 -247
- package/dashboard/bin/cli.js +25 -25
- package/dashboard/package.json +34 -34
- package/dashboard/public/css/layout.css +406 -406
- package/dashboard/public/css/status-colors.css +98 -98
- package/dashboard/public/js/htmx-title.js +5 -5
- package/dashboard/public/js/sidebar-toggle.js +20 -20
- package/dashboard/src/app.js +78 -78
- package/dashboard/src/middleware/errorHandler.js +52 -52
- package/dashboard/src/middleware/notFoundHandler.js +9 -9
- package/dashboard/src/repositories/planning.repository.js +128 -128
- package/dashboard/src/routes/events.routes.js +40 -40
- package/dashboard/src/routes/index.routes.js +31 -31
- package/dashboard/src/routes/pages.routes.js +245 -195
- package/dashboard/src/server.js +42 -42
- package/dashboard/src/services/dashboard.service.js +222 -222
- package/dashboard/src/services/phase.service.js +220 -167
- package/dashboard/src/services/project.service.js +57 -57
- package/dashboard/src/services/roadmap.service.js +171 -171
- package/dashboard/src/services/sse.service.js +58 -58
- package/dashboard/src/services/todo.service.js +254 -254
- package/dashboard/src/services/watcher.service.js +48 -48
- package/dashboard/src/views/coming-soon.ejs +11 -11
- package/dashboard/src/views/error.ejs +13 -13
- package/dashboard/src/views/index.ejs +5 -5
- package/dashboard/src/views/layout.ejs +1 -1
- package/dashboard/src/views/partials/dashboard-content.ejs +77 -77
- package/dashboard/src/views/partials/footer.ejs +3 -3
- package/dashboard/src/views/partials/head.ejs +21 -21
- package/dashboard/src/views/partials/header.ejs +12 -12
- package/dashboard/src/views/partials/layout-bottom.ejs +15 -15
- package/dashboard/src/views/partials/layout-top.ejs +8 -8
- package/dashboard/src/views/partials/phase-content.ejs +188 -181
- package/dashboard/src/views/partials/phase-doc-content.ejs +38 -0
- package/dashboard/src/views/partials/phases-content.ejs +117 -117
- package/dashboard/src/views/partials/roadmap-content.ejs +142 -142
- package/dashboard/src/views/partials/sidebar.ejs +38 -38
- package/dashboard/src/views/partials/todo-create-content.ejs +53 -53
- package/dashboard/src/views/partials/todo-detail-content.ejs +38 -38
- package/dashboard/src/views/partials/todos-content.ejs +53 -53
- package/dashboard/src/views/phase-detail.ejs +5 -5
- package/dashboard/src/views/phase-doc.ejs +5 -0
- package/dashboard/src/views/phases.ejs +5 -5
- package/dashboard/src/views/roadmap.ejs +5 -5
- package/dashboard/src/views/todo-create.ejs +5 -5
- package/dashboard/src/views/todo-detail.ejs +5 -5
- package/dashboard/src/views/todos.ejs +5 -5
- package/package.json +57 -57
- package/plugins/pbr/.claude-plugin/plugin.json +13 -13
- package/plugins/pbr/UI-CONSISTENCY-GAPS.md +61 -61
- package/plugins/pbr/agents/codebase-mapper.md +279 -271
- package/plugins/pbr/agents/debugger.md +281 -281
- package/plugins/pbr/agents/executor.md +428 -407
- package/plugins/pbr/agents/general.md +164 -164
- package/plugins/pbr/agents/integration-checker.md +169 -141
- package/plugins/pbr/agents/plan-checker.md +296 -280
- package/plugins/pbr/agents/planner.md +358 -358
- package/plugins/pbr/agents/researcher.md +363 -363
- package/plugins/pbr/agents/synthesizer.md +230 -230
- package/plugins/pbr/agents/verifier.md +489 -454
- package/plugins/pbr/commands/begin.md +5 -5
- package/plugins/pbr/commands/build.md +5 -5
- package/plugins/pbr/commands/config.md +5 -5
- package/plugins/pbr/commands/continue.md +5 -5
- package/plugins/pbr/commands/debug.md +5 -5
- package/plugins/pbr/commands/discuss.md +5 -5
- package/plugins/pbr/commands/explore.md +5 -5
- package/plugins/pbr/commands/health.md +5 -5
- package/plugins/pbr/commands/help.md +5 -5
- package/plugins/pbr/commands/import.md +5 -5
- package/plugins/pbr/commands/milestone.md +5 -5
- package/plugins/pbr/commands/note.md +5 -5
- package/plugins/pbr/commands/pause.md +5 -5
- package/plugins/pbr/commands/plan.md +5 -5
- package/plugins/pbr/commands/quick.md +5 -5
- package/plugins/pbr/commands/resume.md +5 -5
- package/plugins/pbr/commands/review.md +5 -5
- package/plugins/pbr/commands/scan.md +5 -5
- package/plugins/pbr/commands/setup.md +5 -5
- package/plugins/pbr/commands/status.md +5 -5
- package/plugins/pbr/commands/todo.md +5 -5
- package/plugins/pbr/contexts/dev.md +27 -27
- package/plugins/pbr/contexts/research.md +28 -28
- package/plugins/pbr/contexts/review.md +36 -36
- package/plugins/pbr/hooks/hooks.json +183 -183
- package/plugins/pbr/references/agent-anti-patterns.md +24 -24
- package/plugins/pbr/references/agent-interactions.md +134 -134
- package/plugins/pbr/references/agent-teams.md +54 -54
- package/plugins/pbr/references/checkpoints.md +157 -157
- package/plugins/pbr/references/common-bug-patterns.md +13 -13
- package/plugins/pbr/references/config-reference.md +441 -0
- package/plugins/pbr/references/continuation-format.md +212 -212
- package/plugins/pbr/references/deviation-rules.md +112 -112
- package/plugins/pbr/references/git-integration.md +226 -226
- package/plugins/pbr/references/integration-patterns.md +117 -117
- package/plugins/pbr/references/model-profiles.md +99 -99
- package/plugins/pbr/references/model-selection.md +31 -31
- package/plugins/pbr/references/pbr-rules.md +193 -193
- package/plugins/pbr/references/plan-authoring.md +181 -181
- package/plugins/pbr/references/plan-format.md +287 -283
- package/plugins/pbr/references/planning-config.md +213 -213
- package/plugins/pbr/references/questioning.md +214 -214
- package/plugins/pbr/references/reading-verification.md +127 -127
- package/plugins/pbr/references/stub-patterns.md +160 -160
- package/plugins/pbr/references/subagent-coordination.md +119 -119
- package/plugins/pbr/references/ui-formatting.md +461 -399
- package/plugins/pbr/references/verification-patterns.md +198 -198
- package/plugins/pbr/references/wave-execution.md +95 -95
- package/plugins/pbr/scripts/auto-continue.js +80 -80
- package/plugins/pbr/scripts/check-dangerous-commands.js +136 -136
- package/plugins/pbr/scripts/check-doc-sprawl.js +102 -102
- package/plugins/pbr/scripts/check-phase-boundary.js +196 -196
- package/plugins/pbr/scripts/check-plan-format.js +270 -270
- package/plugins/pbr/scripts/check-roadmap-sync.js +322 -252
- package/plugins/pbr/scripts/check-skill-workflow.js +262 -262
- package/plugins/pbr/scripts/check-state-sync.js +476 -476
- package/plugins/pbr/scripts/check-subagent-output.js +144 -144
- package/plugins/pbr/scripts/config-schema.json +251 -251
- package/plugins/pbr/scripts/context-budget-check.js +287 -287
- package/plugins/pbr/scripts/event-handler.js +151 -151
- package/plugins/pbr/scripts/event-logger.js +92 -92
- package/plugins/pbr/scripts/hook-logger.js +80 -76
- package/plugins/pbr/scripts/hooks-schema.json +79 -79
- package/plugins/pbr/scripts/log-subagent.js +164 -152
- package/plugins/pbr/scripts/log-tool-failure.js +88 -88
- package/plugins/pbr/scripts/pbr-tools.js +1378 -1301
- package/plugins/pbr/scripts/post-write-dispatch.js +66 -66
- package/plugins/pbr/scripts/post-write-quality.js +207 -207
- package/plugins/pbr/scripts/pre-bash-dispatch.js +86 -56
- package/plugins/pbr/scripts/pre-write-dispatch.js +97 -62
- package/plugins/pbr/scripts/progress-tracker.js +281 -228
- package/plugins/pbr/scripts/run-hook.js +92 -0
- package/plugins/pbr/scripts/session-cleanup.js +254 -254
- package/plugins/pbr/scripts/status-line.js +288 -285
- package/plugins/pbr/scripts/suggest-compact.js +119 -119
- package/plugins/pbr/scripts/task-completed.js +45 -45
- package/plugins/pbr/scripts/track-context-budget.js +149 -119
- package/plugins/pbr/scripts/validate-commit.js +200 -200
- package/plugins/pbr/scripts/validate-plugin-structure.js +183 -172
- package/plugins/pbr/scripts/validate-task.js +106 -0
- package/plugins/pbr/skills/begin/SKILL.md +594 -545
- package/plugins/pbr/skills/begin/templates/PROJECT.md.tmpl +33 -33
- package/plugins/pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +18 -18
- package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +49 -49
- package/plugins/pbr/skills/begin/templates/config.json.tmpl +64 -63
- package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +19 -19
- package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +30 -30
- package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +16 -16
- package/plugins/pbr/skills/build/SKILL.md +943 -962
- package/plugins/pbr/skills/config/SKILL.md +256 -241
- package/plugins/pbr/skills/continue/SKILL.md +164 -127
- package/plugins/pbr/skills/debug/SKILL.md +515 -489
- package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +16 -16
- package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +27 -27
- package/plugins/pbr/skills/discuss/SKILL.md +347 -338
- package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +61 -61
- package/plugins/pbr/skills/discuss/templates/decision-categories.md +9 -9
- package/plugins/pbr/skills/explore/SKILL.md +378 -362
- package/plugins/pbr/skills/health/SKILL.md +221 -186
- package/plugins/pbr/skills/health/templates/check-pattern.md.tmpl +30 -30
- package/plugins/pbr/skills/health/templates/output-format.md.tmpl +63 -63
- package/plugins/pbr/skills/help/SKILL.md +155 -140
- package/plugins/pbr/skills/import/SKILL.md +504 -490
- package/plugins/pbr/skills/milestone/SKILL.md +704 -673
- package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +48 -48
- package/plugins/pbr/skills/milestone/templates/stats-file.md.tmpl +30 -30
- package/plugins/pbr/skills/note/SKILL.md +231 -212
- package/plugins/pbr/skills/pause/SKILL.md +249 -235
- package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +71 -71
- package/plugins/pbr/skills/plan/SKILL.md +685 -628
- package/plugins/pbr/skills/plan/decimal-phase-calc.md +98 -98
- package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +21 -21
- package/plugins/pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +32 -32
- package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +38 -38
- package/plugins/pbr/skills/plan/templates/researcher-prompt.md.tmpl +19 -19
- package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +23 -23
- package/plugins/pbr/skills/quick/SKILL.md +354 -335
- package/plugins/pbr/skills/resume/SKILL.md +402 -388
- package/plugins/pbr/skills/review/SKILL.md +686 -652
- package/plugins/pbr/skills/review/templates/debugger-prompt.md.tmpl +60 -60
- package/plugins/pbr/skills/review/templates/gap-planner-prompt.md.tmpl +40 -40
- package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +115 -115
- package/plugins/pbr/skills/scan/SKILL.md +304 -269
- package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +201 -201
- package/plugins/pbr/skills/setup/SKILL.md +253 -227
- package/plugins/pbr/skills/shared/commit-planning-docs.md +35 -35
- package/plugins/pbr/skills/shared/config-loading.md +102 -102
- package/plugins/pbr/skills/shared/context-budget.md +40 -40
- package/plugins/pbr/skills/shared/context-loader-task.md +86 -86
- package/plugins/pbr/skills/shared/digest-select.md +79 -79
- package/plugins/pbr/skills/shared/domain-probes.md +125 -125
- package/plugins/pbr/skills/shared/error-reporting.md +79 -79
- package/plugins/pbr/skills/shared/gate-prompts.md +388 -388
- package/plugins/pbr/skills/shared/phase-argument-parsing.md +45 -45
- package/plugins/pbr/skills/shared/progress-display.md +53 -53
- package/plugins/pbr/skills/shared/revision-loop.md +81 -81
- package/plugins/pbr/skills/shared/state-loading.md +62 -62
- package/plugins/pbr/skills/shared/state-update.md +161 -161
- package/plugins/pbr/skills/shared/universal-anti-patterns.md +33 -33
- package/plugins/pbr/skills/status/SKILL.md +367 -353
- package/plugins/pbr/skills/todo/SKILL.md +198 -181
- package/plugins/pbr/templates/CONTEXT.md.tmpl +52 -52
- package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +151 -151
- package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +97 -97
- package/plugins/pbr/templates/ROADMAP.md.tmpl +40 -40
- package/plugins/pbr/templates/SUMMARY.md.tmpl +81 -81
- package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +116 -116
- package/plugins/pbr/templates/codebase/ARCHITECTURE.md.tmpl +98 -98
- package/plugins/pbr/templates/codebase/CONCERNS.md.tmpl +93 -93
- package/plugins/pbr/templates/codebase/CONVENTIONS.md.tmpl +104 -104
- package/plugins/pbr/templates/codebase/INTEGRATIONS.md.tmpl +78 -78
- package/plugins/pbr/templates/codebase/STACK.md.tmpl +78 -78
- package/plugins/pbr/templates/codebase/STRUCTURE.md.tmpl +80 -80
- package/plugins/pbr/templates/codebase/TESTING.md.tmpl +107 -107
- package/plugins/pbr/templates/continue-here.md.tmpl +73 -73
- package/plugins/pbr/templates/prompt-partials/phase-project-context.md.tmpl +37 -37
- package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +124 -124
- package/plugins/pbr/templates/research/STACK.md.tmpl +71 -71
- package/plugins/pbr/templates/research/SUMMARY.md.tmpl +112 -112
- package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +81 -81
- package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +99 -99
- package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +36 -36
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SubagentStop event handler for auto-verification triggering.
|
|
5
|
-
*
|
|
6
|
-
* Detects executor agent completion and conditionally queues verification
|
|
7
|
-
* by writing a .auto-verify signal file. Respects depth profile and
|
|
8
|
-
* config.features.goal_verification setting.
|
|
9
|
-
*
|
|
10
|
-
* Non-blocking — exits 0 always.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
const { logHook } = require('./hook-logger');
|
|
16
|
-
const { logEvent } = require('./event-logger');
|
|
17
|
-
// configLoad not used here to avoid mtime-based cache issues across directories.
|
|
18
|
-
// Config is read directly in shouldAutoVerify().
|
|
19
|
-
|
|
20
|
-
function readStdin() {
|
|
21
|
-
try {
|
|
22
|
-
const input = fs.readFileSync(0, 'utf8').trim();
|
|
23
|
-
if (input) return JSON.parse(input);
|
|
24
|
-
} catch (_e) {
|
|
25
|
-
// empty or non-JSON stdin
|
|
26
|
-
}
|
|
27
|
-
return {};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Check if the stdin data represents an executor agent completion.
|
|
32
|
-
* @param {object} data - Parsed stdin JSON from SubagentStop event
|
|
33
|
-
* @returns {boolean}
|
|
34
|
-
*/
|
|
35
|
-
function isExecutorAgent(data) {
|
|
36
|
-
const agentType = data.agent_type || data.subagent_type || null;
|
|
37
|
-
return agentType === 'pbr:executor';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Determine whether auto-verification should run based on config.
|
|
42
|
-
* @param {string} planningDir - Path to .planning directory
|
|
43
|
-
* @returns {boolean}
|
|
44
|
-
*/
|
|
45
|
-
function shouldAutoVerify(planningDir) {
|
|
46
|
-
// Read config directly instead of using configLoad to avoid mtime-based
|
|
47
|
-
// cache issues when called repeatedly with different planning directories.
|
|
48
|
-
const configPath = path.join(planningDir, 'config.json');
|
|
49
|
-
let config;
|
|
50
|
-
try {
|
|
51
|
-
if (!fs.existsSync(configPath)) return false;
|
|
52
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
53
|
-
} catch (_e) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
if (config === null) return false;
|
|
57
|
-
|
|
58
|
-
// Check explicit goal_verification toggle
|
|
59
|
-
if (config.features && config.features.goal_verification === false) return false;
|
|
60
|
-
|
|
61
|
-
// Check depth profile
|
|
62
|
-
const depth = (config.depth || 'standard').toLowerCase();
|
|
63
|
-
if (depth === 'quick') return false;
|
|
64
|
-
|
|
65
|
-
// "standard", "comprehensive", and any other depth default to true
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Parse current phase info from STATE.md.
|
|
71
|
-
* @param {string} planningDir - Path to .planning directory
|
|
72
|
-
* @returns {{ phase: number, total: number, status: string } | null}
|
|
73
|
-
*/
|
|
74
|
-
function getPhaseFromState(planningDir) {
|
|
75
|
-
const statePath = path.join(planningDir, 'STATE.md');
|
|
76
|
-
try {
|
|
77
|
-
if (!fs.existsSync(statePath)) return null;
|
|
78
|
-
const content = fs.readFileSync(statePath, 'utf8');
|
|
79
|
-
|
|
80
|
-
const phaseMatch = content.match(/Phase:\s*(\d+)\s+of\s+(\d+)/);
|
|
81
|
-
if (!phaseMatch) return null;
|
|
82
|
-
|
|
83
|
-
const statusMatch = content.match(/\*{0,2}(?:Phase\s+)?Status\*{0,2}:\s*["']?(\w+)["']?/i);
|
|
84
|
-
const status = statusMatch ? statusMatch[1] : null;
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
phase: parseInt(phaseMatch[1], 10),
|
|
88
|
-
total: parseInt(phaseMatch[2], 10),
|
|
89
|
-
status: status
|
|
90
|
-
};
|
|
91
|
-
} catch (_e) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Write signal file for orchestrator to pick up and spawn verifier.
|
|
98
|
-
* @param {string} planningDir - Path to .planning directory
|
|
99
|
-
* @param {number} phaseNumber - Current phase number
|
|
100
|
-
*/
|
|
101
|
-
function writeAutoVerifySignal(planningDir, phaseNumber) {
|
|
102
|
-
const signalPath = path.join(planningDir, '.auto-verify');
|
|
103
|
-
const payload = {
|
|
104
|
-
phase: phaseNumber,
|
|
105
|
-
timestamp: new Date().toISOString()
|
|
106
|
-
};
|
|
107
|
-
fs.writeFileSync(signalPath, JSON.stringify(payload, null, 2), 'utf8');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function main() {
|
|
111
|
-
const data = readStdin();
|
|
112
|
-
|
|
113
|
-
// Only handle executor agent completions
|
|
114
|
-
if (!isExecutorAgent(data)) {
|
|
115
|
-
process.exit(0);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const agentType = data.agent_type || data.subagent_type;
|
|
119
|
-
|
|
120
|
-
logHook('event-handler', 'SubagentStop', 'executor-complete', { agent_type: agentType });
|
|
121
|
-
logEvent('workflow', 'executor-complete', { agent_type: agentType });
|
|
122
|
-
|
|
123
|
-
const planningDir = path.join(process.cwd(), '.planning');
|
|
124
|
-
if (!fs.existsSync(planningDir)) {
|
|
125
|
-
process.exit(0);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!shouldAutoVerify(planningDir)) {
|
|
129
|
-
logHook('event-handler', 'SubagentStop', 'skip-verify', { reason: 'config/depth' });
|
|
130
|
-
process.exit(0);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const stateInfo = getPhaseFromState(planningDir);
|
|
134
|
-
if (!stateInfo || stateInfo.status !== 'building') {
|
|
135
|
-
logHook('event-handler', 'SubagentStop', 'skip-verify', {
|
|
136
|
-
reason: stateInfo ? `status=${stateInfo.status}` : 'no-state'
|
|
137
|
-
});
|
|
138
|
-
process.exit(0);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
writeAutoVerifySignal(planningDir, stateInfo.phase);
|
|
142
|
-
|
|
143
|
-
const output = {
|
|
144
|
-
message: `Executor complete. Auto-verification queued for Phase ${stateInfo.phase}.`
|
|
145
|
-
};
|
|
146
|
-
process.stdout.write(JSON.stringify(output));
|
|
147
|
-
process.exit(0);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
module.exports = { isExecutorAgent, shouldAutoVerify, getPhaseFromState };
|
|
151
|
-
if (require.main === module) { main(); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SubagentStop event handler for auto-verification triggering.
|
|
5
|
+
*
|
|
6
|
+
* Detects executor agent completion and conditionally queues verification
|
|
7
|
+
* by writing a .auto-verify signal file. Respects depth profile and
|
|
8
|
+
* config.features.goal_verification setting.
|
|
9
|
+
*
|
|
10
|
+
* Non-blocking — exits 0 always.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { logHook } = require('./hook-logger');
|
|
16
|
+
const { logEvent } = require('./event-logger');
|
|
17
|
+
// configLoad not used here to avoid mtime-based cache issues across directories.
|
|
18
|
+
// Config is read directly in shouldAutoVerify().
|
|
19
|
+
|
|
20
|
+
function readStdin() {
|
|
21
|
+
try {
|
|
22
|
+
const input = fs.readFileSync(0, 'utf8').trim();
|
|
23
|
+
if (input) return JSON.parse(input);
|
|
24
|
+
} catch (_e) {
|
|
25
|
+
// empty or non-JSON stdin
|
|
26
|
+
}
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if the stdin data represents an executor agent completion.
|
|
32
|
+
* @param {object} data - Parsed stdin JSON from SubagentStop event
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
function isExecutorAgent(data) {
|
|
36
|
+
const agentType = data.agent_type || data.subagent_type || null;
|
|
37
|
+
return agentType === 'pbr:executor';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Determine whether auto-verification should run based on config.
|
|
42
|
+
* @param {string} planningDir - Path to .planning directory
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
function shouldAutoVerify(planningDir) {
|
|
46
|
+
// Read config directly instead of using configLoad to avoid mtime-based
|
|
47
|
+
// cache issues when called repeatedly with different planning directories.
|
|
48
|
+
const configPath = path.join(planningDir, 'config.json');
|
|
49
|
+
let config;
|
|
50
|
+
try {
|
|
51
|
+
if (!fs.existsSync(configPath)) return false;
|
|
52
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
53
|
+
} catch (_e) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (config === null) return false;
|
|
57
|
+
|
|
58
|
+
// Check explicit goal_verification toggle
|
|
59
|
+
if (config.features && config.features.goal_verification === false) return false;
|
|
60
|
+
|
|
61
|
+
// Check depth profile
|
|
62
|
+
const depth = (config.depth || 'standard').toLowerCase();
|
|
63
|
+
if (depth === 'quick') return false;
|
|
64
|
+
|
|
65
|
+
// "standard", "comprehensive", and any other depth default to true
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parse current phase info from STATE.md.
|
|
71
|
+
* @param {string} planningDir - Path to .planning directory
|
|
72
|
+
* @returns {{ phase: number, total: number, status: string } | null}
|
|
73
|
+
*/
|
|
74
|
+
function getPhaseFromState(planningDir) {
|
|
75
|
+
const statePath = path.join(planningDir, 'STATE.md');
|
|
76
|
+
try {
|
|
77
|
+
if (!fs.existsSync(statePath)) return null;
|
|
78
|
+
const content = fs.readFileSync(statePath, 'utf8');
|
|
79
|
+
|
|
80
|
+
const phaseMatch = content.match(/Phase:\s*(\d+)\s+of\s+(\d+)/);
|
|
81
|
+
if (!phaseMatch) return null;
|
|
82
|
+
|
|
83
|
+
const statusMatch = content.match(/\*{0,2}(?:Phase\s+)?Status\*{0,2}:\s*["']?(\w+)["']?/i);
|
|
84
|
+
const status = statusMatch ? statusMatch[1] : null;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
phase: parseInt(phaseMatch[1], 10),
|
|
88
|
+
total: parseInt(phaseMatch[2], 10),
|
|
89
|
+
status: status
|
|
90
|
+
};
|
|
91
|
+
} catch (_e) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Write signal file for orchestrator to pick up and spawn verifier.
|
|
98
|
+
* @param {string} planningDir - Path to .planning directory
|
|
99
|
+
* @param {number} phaseNumber - Current phase number
|
|
100
|
+
*/
|
|
101
|
+
function writeAutoVerifySignal(planningDir, phaseNumber) {
|
|
102
|
+
const signalPath = path.join(planningDir, '.auto-verify');
|
|
103
|
+
const payload = {
|
|
104
|
+
phase: phaseNumber,
|
|
105
|
+
timestamp: new Date().toISOString()
|
|
106
|
+
};
|
|
107
|
+
fs.writeFileSync(signalPath, JSON.stringify(payload, null, 2), 'utf8');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function main() {
|
|
111
|
+
const data = readStdin();
|
|
112
|
+
|
|
113
|
+
// Only handle executor agent completions
|
|
114
|
+
if (!isExecutorAgent(data)) {
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const agentType = data.agent_type || data.subagent_type;
|
|
119
|
+
|
|
120
|
+
logHook('event-handler', 'SubagentStop', 'executor-complete', { agent_type: agentType });
|
|
121
|
+
logEvent('workflow', 'executor-complete', { agent_type: agentType });
|
|
122
|
+
|
|
123
|
+
const planningDir = path.join(process.cwd(), '.planning');
|
|
124
|
+
if (!fs.existsSync(planningDir)) {
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!shouldAutoVerify(planningDir)) {
|
|
129
|
+
logHook('event-handler', 'SubagentStop', 'skip-verify', { reason: 'config/depth' });
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const stateInfo = getPhaseFromState(planningDir);
|
|
134
|
+
if (!stateInfo || stateInfo.status !== 'building') {
|
|
135
|
+
logHook('event-handler', 'SubagentStop', 'skip-verify', {
|
|
136
|
+
reason: stateInfo ? `status=${stateInfo.status}` : 'no-state'
|
|
137
|
+
});
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
writeAutoVerifySignal(planningDir, stateInfo.phase);
|
|
142
|
+
|
|
143
|
+
const output = {
|
|
144
|
+
message: `Executor complete. Auto-verification queued for Phase ${stateInfo.phase}.`
|
|
145
|
+
};
|
|
146
|
+
process.stdout.write(JSON.stringify(output));
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = { isExecutorAgent, shouldAutoVerify, getPhaseFromState };
|
|
151
|
+
if (require.main === module) { main(); }
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Workflow event logger for Plan-Build-Run observability.
|
|
5
|
-
*
|
|
6
|
-
* Usage as module:
|
|
7
|
-
* const { logEvent } = require('./event-logger');
|
|
8
|
-
* logEvent('workflow', 'phase-start', { phase: 3, name: 'API' });
|
|
9
|
-
*
|
|
10
|
-
* Usage as CLI:
|
|
11
|
-
* node event-logger.js <category> <event> [JSON-details]
|
|
12
|
-
*
|
|
13
|
-
* Log file: .planning/logs/events.jsonl
|
|
14
|
-
* Format: One JSON line per entry (JSONL)
|
|
15
|
-
* Rotation: Keeps last 1000 entries max
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
|
|
21
|
-
const MAX_ENTRIES = 1000;
|
|
22
|
-
|
|
23
|
-
function getLogPath() {
|
|
24
|
-
const cwd = process.cwd();
|
|
25
|
-
const planningDir = path.join(cwd, '.planning');
|
|
26
|
-
if (!fs.existsSync(planningDir)) return null;
|
|
27
|
-
const logsDir = path.join(planningDir, 'logs');
|
|
28
|
-
if (!fs.existsSync(logsDir)) {
|
|
29
|
-
fs.mkdirSync(logsDir, { recursive: true });
|
|
30
|
-
}
|
|
31
|
-
return path.join(logsDir, 'events.jsonl');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function logEvent(category, event, details = {}) {
|
|
35
|
-
const logPath = getLogPath();
|
|
36
|
-
if (!logPath) return;
|
|
37
|
-
|
|
38
|
-
const entry = {
|
|
39
|
-
ts: new Date().toISOString(),
|
|
40
|
-
cat: category,
|
|
41
|
-
event,
|
|
42
|
-
...details
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
let lines = [];
|
|
47
|
-
if (fs.existsSync(logPath)) {
|
|
48
|
-
const content = fs.readFileSync(logPath, 'utf8').trim();
|
|
49
|
-
if (content) {
|
|
50
|
-
lines = content.split('\n');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
lines.push(JSON.stringify(entry));
|
|
55
|
-
|
|
56
|
-
if (lines.length > MAX_ENTRIES) {
|
|
57
|
-
lines = lines.slice(lines.length - MAX_ENTRIES);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf8');
|
|
61
|
-
} catch (_e) {
|
|
62
|
-
// Best-effort logging — never fail the caller
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// CLI mode
|
|
67
|
-
function main() {
|
|
68
|
-
const args = process.argv.slice(2);
|
|
69
|
-
const category = args[0];
|
|
70
|
-
const event = args[1];
|
|
71
|
-
let details = {};
|
|
72
|
-
|
|
73
|
-
if (args[2]) {
|
|
74
|
-
try {
|
|
75
|
-
details = JSON.parse(args[2]);
|
|
76
|
-
} catch (_e) {
|
|
77
|
-
details = { raw: args[2] };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!category || !event) {
|
|
82
|
-
process.stdout.write(JSON.stringify({ error: 'Usage: event-logger.js <category> <event> [JSON-details]' }));
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
logEvent(category, event, details);
|
|
87
|
-
process.stdout.write(JSON.stringify({ logged: true, category, event }));
|
|
88
|
-
process.exit(0);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (require.main === module) { main(); }
|
|
92
|
-
module.exports = { logEvent };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workflow event logger for Plan-Build-Run observability.
|
|
5
|
+
*
|
|
6
|
+
* Usage as module:
|
|
7
|
+
* const { logEvent } = require('./event-logger');
|
|
8
|
+
* logEvent('workflow', 'phase-start', { phase: 3, name: 'API' });
|
|
9
|
+
*
|
|
10
|
+
* Usage as CLI:
|
|
11
|
+
* node event-logger.js <category> <event> [JSON-details]
|
|
12
|
+
*
|
|
13
|
+
* Log file: .planning/logs/events.jsonl
|
|
14
|
+
* Format: One JSON line per entry (JSONL)
|
|
15
|
+
* Rotation: Keeps last 1000 entries max
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
const MAX_ENTRIES = 1000;
|
|
22
|
+
|
|
23
|
+
function getLogPath() {
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
const planningDir = path.join(cwd, '.planning');
|
|
26
|
+
if (!fs.existsSync(planningDir)) return null;
|
|
27
|
+
const logsDir = path.join(planningDir, 'logs');
|
|
28
|
+
if (!fs.existsSync(logsDir)) {
|
|
29
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
return path.join(logsDir, 'events.jsonl');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function logEvent(category, event, details = {}) {
|
|
35
|
+
const logPath = getLogPath();
|
|
36
|
+
if (!logPath) return;
|
|
37
|
+
|
|
38
|
+
const entry = {
|
|
39
|
+
ts: new Date().toISOString(),
|
|
40
|
+
cat: category,
|
|
41
|
+
event,
|
|
42
|
+
...details
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
let lines = [];
|
|
47
|
+
if (fs.existsSync(logPath)) {
|
|
48
|
+
const content = fs.readFileSync(logPath, 'utf8').trim();
|
|
49
|
+
if (content) {
|
|
50
|
+
lines = content.split('\n');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
lines.push(JSON.stringify(entry));
|
|
55
|
+
|
|
56
|
+
if (lines.length > MAX_ENTRIES) {
|
|
57
|
+
lines = lines.slice(lines.length - MAX_ENTRIES);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf8');
|
|
61
|
+
} catch (_e) {
|
|
62
|
+
// Best-effort logging — never fail the caller
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// CLI mode
|
|
67
|
+
function main() {
|
|
68
|
+
const args = process.argv.slice(2);
|
|
69
|
+
const category = args[0];
|
|
70
|
+
const event = args[1];
|
|
71
|
+
let details = {};
|
|
72
|
+
|
|
73
|
+
if (args[2]) {
|
|
74
|
+
try {
|
|
75
|
+
details = JSON.parse(args[2]);
|
|
76
|
+
} catch (_e) {
|
|
77
|
+
details = { raw: args[2] };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!category || !event) {
|
|
82
|
+
process.stdout.write(JSON.stringify({ error: 'Usage: event-logger.js <category> <event> [JSON-details]' }));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logEvent(category, event, details);
|
|
87
|
+
process.stdout.write(JSON.stringify({ logged: true, category, event }));
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (require.main === module) { main(); }
|
|
92
|
+
module.exports = { logEvent };
|
|
@@ -1,76 +1,80 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shared hook execution logger.
|
|
5
|
-
*
|
|
6
|
-
* Usage in hooks:
|
|
7
|
-
* const { logHook } = require('./hook-logger');
|
|
8
|
-
* logHook('validate-commit', 'PreToolUse', 'allow', { message: 'chore: ...' });
|
|
9
|
-
*
|
|
10
|
-
* Log file: .planning/logs/hooks.jsonl (in the project's .planning directory)
|
|
11
|
-
* Format: One JSON line per entry (JSONL)
|
|
12
|
-
* Rotation: Keeps last 200 entries max
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
const MAX_ENTRIES = 200;
|
|
19
|
-
|
|
20
|
-
function getLogPath() {
|
|
21
|
-
const cwd = process.cwd();
|
|
22
|
-
const planningDir = path.join(cwd, '.planning');
|
|
23
|
-
if (!fs.existsSync(planningDir)) return null;
|
|
24
|
-
|
|
25
|
-
const logsDir = path.join(planningDir, 'logs');
|
|
26
|
-
const newPath = path.join(logsDir, 'hooks.jsonl');
|
|
27
|
-
const oldPath = path.join(planningDir, '.hook-log');
|
|
28
|
-
|
|
29
|
-
// Auto-create logs/ directory if it doesn't exist
|
|
30
|
-
if (!fs.existsSync(logsDir)) {
|
|
31
|
-
fs.mkdirSync(logsDir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// One-time migration: move old .hook-log to new location
|
|
35
|
-
if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
36
|
-
fs.renameSync(oldPath, newPath);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return newPath;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function logHook(hookName, eventType, decision, details = {}) {
|
|
43
|
-
const logPath = getLogPath();
|
|
44
|
-
if (!logPath) return; // Not a Plan-Build-Run project
|
|
45
|
-
|
|
46
|
-
const entry = {
|
|
47
|
-
ts: new Date().toISOString(),
|
|
48
|
-
hook: hookName,
|
|
49
|
-
event: eventType,
|
|
50
|
-
decision,
|
|
51
|
-
...details
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared hook execution logger.
|
|
5
|
+
*
|
|
6
|
+
* Usage in hooks:
|
|
7
|
+
* const { logHook } = require('./hook-logger');
|
|
8
|
+
* logHook('validate-commit', 'PreToolUse', 'allow', { message: 'chore: ...' });
|
|
9
|
+
*
|
|
10
|
+
* Log file: .planning/logs/hooks.jsonl (in the project's .planning directory)
|
|
11
|
+
* Format: One JSON line per entry (JSONL)
|
|
12
|
+
* Rotation: Keeps last 200 entries max
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const MAX_ENTRIES = 200;
|
|
19
|
+
|
|
20
|
+
function getLogPath() {
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
const planningDir = path.join(cwd, '.planning');
|
|
23
|
+
if (!fs.existsSync(planningDir)) return null;
|
|
24
|
+
|
|
25
|
+
const logsDir = path.join(planningDir, 'logs');
|
|
26
|
+
const newPath = path.join(logsDir, 'hooks.jsonl');
|
|
27
|
+
const oldPath = path.join(planningDir, '.hook-log');
|
|
28
|
+
|
|
29
|
+
// Auto-create logs/ directory if it doesn't exist
|
|
30
|
+
if (!fs.existsSync(logsDir)) {
|
|
31
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// One-time migration: move old .hook-log to new location
|
|
35
|
+
if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
|
|
36
|
+
fs.renameSync(oldPath, newPath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return newPath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function logHook(hookName, eventType, decision, details = {}, startTime) {
|
|
43
|
+
const logPath = getLogPath();
|
|
44
|
+
if (!logPath) return; // Not a Plan-Build-Run project
|
|
45
|
+
|
|
46
|
+
const entry = {
|
|
47
|
+
ts: new Date().toISOString(),
|
|
48
|
+
hook: hookName,
|
|
49
|
+
event: eventType,
|
|
50
|
+
decision,
|
|
51
|
+
...details
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (typeof startTime === 'number' && startTime > 0) {
|
|
55
|
+
entry.duration_ms = Date.now() - startTime;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
let lines = [];
|
|
60
|
+
if (fs.existsSync(logPath)) {
|
|
61
|
+
const content = fs.readFileSync(logPath, 'utf8').trim();
|
|
62
|
+
if (content) {
|
|
63
|
+
lines = content.split('\n');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
lines.push(JSON.stringify(entry));
|
|
68
|
+
|
|
69
|
+
// Keep only last MAX_ENTRIES
|
|
70
|
+
if (lines.length > MAX_ENTRIES) {
|
|
71
|
+
lines = lines.slice(lines.length - MAX_ENTRIES);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf8');
|
|
75
|
+
} catch (_e) {
|
|
76
|
+
// Best-effort logging — never fail the hook
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { logHook };
|