@sienklogic/plan-build-run 2.0.0
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 -0
- package/CLAUDE.md +149 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/dashboard/bin/cli.js +25 -0
- package/dashboard/package.json +34 -0
- package/dashboard/public/.gitkeep +0 -0
- package/dashboard/public/css/layout.css +406 -0
- package/dashboard/public/css/status-colors.css +98 -0
- package/dashboard/public/js/htmx-title.js +5 -0
- package/dashboard/public/js/sidebar-toggle.js +20 -0
- package/dashboard/src/app.js +78 -0
- package/dashboard/src/middleware/errorHandler.js +52 -0
- package/dashboard/src/middleware/notFoundHandler.js +9 -0
- package/dashboard/src/repositories/planning.repository.js +128 -0
- package/dashboard/src/routes/events.routes.js +40 -0
- package/dashboard/src/routes/index.routes.js +31 -0
- package/dashboard/src/routes/pages.routes.js +195 -0
- package/dashboard/src/server.js +42 -0
- package/dashboard/src/services/dashboard.service.js +222 -0
- package/dashboard/src/services/phase.service.js +167 -0
- package/dashboard/src/services/project.service.js +57 -0
- package/dashboard/src/services/roadmap.service.js +171 -0
- package/dashboard/src/services/sse.service.js +58 -0
- package/dashboard/src/services/todo.service.js +254 -0
- package/dashboard/src/services/watcher.service.js +48 -0
- package/dashboard/src/views/coming-soon.ejs +11 -0
- package/dashboard/src/views/error.ejs +13 -0
- package/dashboard/src/views/index.ejs +5 -0
- package/dashboard/src/views/layout.ejs +1 -0
- package/dashboard/src/views/partials/dashboard-content.ejs +77 -0
- package/dashboard/src/views/partials/footer.ejs +3 -0
- package/dashboard/src/views/partials/head.ejs +21 -0
- package/dashboard/src/views/partials/header.ejs +12 -0
- package/dashboard/src/views/partials/layout-bottom.ejs +15 -0
- package/dashboard/src/views/partials/layout-top.ejs +8 -0
- package/dashboard/src/views/partials/phase-content.ejs +181 -0
- package/dashboard/src/views/partials/phases-content.ejs +117 -0
- package/dashboard/src/views/partials/roadmap-content.ejs +142 -0
- package/dashboard/src/views/partials/sidebar.ejs +38 -0
- package/dashboard/src/views/partials/todo-create-content.ejs +53 -0
- package/dashboard/src/views/partials/todo-detail-content.ejs +38 -0
- package/dashboard/src/views/partials/todos-content.ejs +53 -0
- package/dashboard/src/views/phase-detail.ejs +5 -0
- package/dashboard/src/views/phases.ejs +5 -0
- package/dashboard/src/views/roadmap.ejs +5 -0
- package/dashboard/src/views/todo-create.ejs +5 -0
- package/dashboard/src/views/todo-detail.ejs +5 -0
- package/dashboard/src/views/todos.ejs +5 -0
- package/package.json +57 -0
- package/plugins/pbr/.claude-plugin/plugin.json +13 -0
- package/plugins/pbr/UI-CONSISTENCY-GAPS.md +61 -0
- package/plugins/pbr/agents/codebase-mapper.md +271 -0
- package/plugins/pbr/agents/debugger.md +281 -0
- package/plugins/pbr/agents/executor.md +407 -0
- package/plugins/pbr/agents/general.md +164 -0
- package/plugins/pbr/agents/integration-checker.md +141 -0
- package/plugins/pbr/agents/plan-checker.md +280 -0
- package/plugins/pbr/agents/planner.md +358 -0
- package/plugins/pbr/agents/researcher.md +363 -0
- package/plugins/pbr/agents/synthesizer.md +230 -0
- package/plugins/pbr/agents/verifier.md +454 -0
- package/plugins/pbr/commands/begin.md +5 -0
- package/plugins/pbr/commands/build.md +5 -0
- package/plugins/pbr/commands/config.md +5 -0
- package/plugins/pbr/commands/continue.md +5 -0
- package/plugins/pbr/commands/debug.md +5 -0
- package/plugins/pbr/commands/discuss.md +5 -0
- package/plugins/pbr/commands/explore.md +5 -0
- package/plugins/pbr/commands/health.md +5 -0
- package/plugins/pbr/commands/help.md +5 -0
- package/plugins/pbr/commands/import.md +5 -0
- package/plugins/pbr/commands/milestone.md +5 -0
- package/plugins/pbr/commands/note.md +5 -0
- package/plugins/pbr/commands/pause.md +5 -0
- package/plugins/pbr/commands/plan.md +5 -0
- package/plugins/pbr/commands/quick.md +5 -0
- package/plugins/pbr/commands/resume.md +5 -0
- package/plugins/pbr/commands/review.md +5 -0
- package/plugins/pbr/commands/scan.md +5 -0
- package/plugins/pbr/commands/setup.md +5 -0
- package/plugins/pbr/commands/status.md +5 -0
- package/plugins/pbr/commands/todo.md +5 -0
- package/plugins/pbr/contexts/dev.md +27 -0
- package/plugins/pbr/contexts/research.md +28 -0
- package/plugins/pbr/contexts/review.md +36 -0
- package/plugins/pbr/hooks/hooks.json +183 -0
- package/plugins/pbr/references/agent-anti-patterns.md +24 -0
- package/plugins/pbr/references/agent-interactions.md +134 -0
- package/plugins/pbr/references/agent-teams.md +54 -0
- package/plugins/pbr/references/checkpoints.md +157 -0
- package/plugins/pbr/references/common-bug-patterns.md +13 -0
- package/plugins/pbr/references/continuation-format.md +212 -0
- package/plugins/pbr/references/deviation-rules.md +112 -0
- package/plugins/pbr/references/git-integration.md +226 -0
- package/plugins/pbr/references/integration-patterns.md +117 -0
- package/plugins/pbr/references/model-profiles.md +99 -0
- package/plugins/pbr/references/model-selection.md +31 -0
- package/plugins/pbr/references/pbr-rules.md +193 -0
- package/plugins/pbr/references/plan-authoring.md +181 -0
- package/plugins/pbr/references/plan-format.md +283 -0
- package/plugins/pbr/references/planning-config.md +213 -0
- package/plugins/pbr/references/questioning.md +214 -0
- package/plugins/pbr/references/reading-verification.md +127 -0
- package/plugins/pbr/references/stub-patterns.md +160 -0
- package/plugins/pbr/references/subagent-coordination.md +119 -0
- package/plugins/pbr/references/ui-formatting.md +399 -0
- package/plugins/pbr/references/verification-patterns.md +198 -0
- package/plugins/pbr/references/wave-execution.md +95 -0
- package/plugins/pbr/scripts/auto-continue.js +80 -0
- package/plugins/pbr/scripts/check-dangerous-commands.js +136 -0
- package/plugins/pbr/scripts/check-doc-sprawl.js +102 -0
- package/plugins/pbr/scripts/check-phase-boundary.js +196 -0
- package/plugins/pbr/scripts/check-plan-format.js +270 -0
- package/plugins/pbr/scripts/check-roadmap-sync.js +252 -0
- package/plugins/pbr/scripts/check-skill-workflow.js +262 -0
- package/plugins/pbr/scripts/check-state-sync.js +476 -0
- package/plugins/pbr/scripts/check-subagent-output.js +144 -0
- package/plugins/pbr/scripts/config-schema.json +251 -0
- package/plugins/pbr/scripts/context-budget-check.js +287 -0
- package/plugins/pbr/scripts/event-handler.js +151 -0
- package/plugins/pbr/scripts/event-logger.js +92 -0
- package/plugins/pbr/scripts/hook-logger.js +76 -0
- package/plugins/pbr/scripts/hooks-schema.json +79 -0
- package/plugins/pbr/scripts/log-subagent.js +152 -0
- package/plugins/pbr/scripts/log-tool-failure.js +88 -0
- package/plugins/pbr/scripts/pbr-tools.js +1301 -0
- package/plugins/pbr/scripts/post-write-dispatch.js +66 -0
- package/plugins/pbr/scripts/post-write-quality.js +207 -0
- package/plugins/pbr/scripts/pre-bash-dispatch.js +56 -0
- package/plugins/pbr/scripts/pre-write-dispatch.js +62 -0
- package/plugins/pbr/scripts/progress-tracker.js +228 -0
- package/plugins/pbr/scripts/session-cleanup.js +254 -0
- package/plugins/pbr/scripts/status-line.js +285 -0
- package/plugins/pbr/scripts/suggest-compact.js +119 -0
- package/plugins/pbr/scripts/task-completed.js +45 -0
- package/plugins/pbr/scripts/track-context-budget.js +119 -0
- package/plugins/pbr/scripts/validate-commit.js +200 -0
- package/plugins/pbr/scripts/validate-plugin-structure.js +172 -0
- package/plugins/pbr/skills/begin/SKILL.md +545 -0
- package/plugins/pbr/skills/begin/templates/PROJECT.md.tmpl +33 -0
- package/plugins/pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +18 -0
- package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +49 -0
- package/plugins/pbr/skills/begin/templates/config.json.tmpl +63 -0
- package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +19 -0
- package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +30 -0
- package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +16 -0
- package/plugins/pbr/skills/build/SKILL.md +962 -0
- package/plugins/pbr/skills/config/SKILL.md +241 -0
- package/plugins/pbr/skills/continue/SKILL.md +127 -0
- package/plugins/pbr/skills/debug/SKILL.md +489 -0
- package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +16 -0
- package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +27 -0
- package/plugins/pbr/skills/discuss/SKILL.md +338 -0
- package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +61 -0
- package/plugins/pbr/skills/discuss/templates/decision-categories.md +9 -0
- package/plugins/pbr/skills/explore/SKILL.md +362 -0
- package/plugins/pbr/skills/health/SKILL.md +186 -0
- package/plugins/pbr/skills/health/templates/check-pattern.md.tmpl +30 -0
- package/plugins/pbr/skills/health/templates/output-format.md.tmpl +63 -0
- package/plugins/pbr/skills/help/SKILL.md +140 -0
- package/plugins/pbr/skills/import/SKILL.md +490 -0
- package/plugins/pbr/skills/milestone/SKILL.md +673 -0
- package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +48 -0
- package/plugins/pbr/skills/milestone/templates/stats-file.md.tmpl +30 -0
- package/plugins/pbr/skills/note/SKILL.md +212 -0
- package/plugins/pbr/skills/pause/SKILL.md +235 -0
- package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +71 -0
- package/plugins/pbr/skills/plan/SKILL.md +628 -0
- package/plugins/pbr/skills/plan/decimal-phase-calc.md +98 -0
- package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +21 -0
- package/plugins/pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +32 -0
- package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +38 -0
- package/plugins/pbr/skills/plan/templates/researcher-prompt.md.tmpl +19 -0
- package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +23 -0
- package/plugins/pbr/skills/quick/SKILL.md +335 -0
- package/plugins/pbr/skills/resume/SKILL.md +388 -0
- package/plugins/pbr/skills/review/SKILL.md +652 -0
- package/plugins/pbr/skills/review/templates/debugger-prompt.md.tmpl +60 -0
- package/plugins/pbr/skills/review/templates/gap-planner-prompt.md.tmpl +40 -0
- package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +115 -0
- package/plugins/pbr/skills/scan/SKILL.md +269 -0
- package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +201 -0
- package/plugins/pbr/skills/setup/SKILL.md +227 -0
- package/plugins/pbr/skills/shared/commit-planning-docs.md +35 -0
- package/plugins/pbr/skills/shared/config-loading.md +102 -0
- package/plugins/pbr/skills/shared/context-budget.md +40 -0
- package/plugins/pbr/skills/shared/context-loader-task.md +86 -0
- package/plugins/pbr/skills/shared/digest-select.md +79 -0
- package/plugins/pbr/skills/shared/domain-probes.md +125 -0
- package/plugins/pbr/skills/shared/error-reporting.md +79 -0
- package/plugins/pbr/skills/shared/gate-prompts.md +388 -0
- package/plugins/pbr/skills/shared/phase-argument-parsing.md +45 -0
- package/plugins/pbr/skills/shared/progress-display.md +53 -0
- package/plugins/pbr/skills/shared/revision-loop.md +81 -0
- package/plugins/pbr/skills/shared/state-loading.md +62 -0
- package/plugins/pbr/skills/shared/state-update.md +161 -0
- package/plugins/pbr/skills/shared/universal-anti-patterns.md +33 -0
- package/plugins/pbr/skills/status/SKILL.md +353 -0
- package/plugins/pbr/skills/todo/SKILL.md +181 -0
- package/plugins/pbr/templates/CONTEXT.md.tmpl +52 -0
- package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +151 -0
- package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +97 -0
- package/plugins/pbr/templates/ROADMAP.md.tmpl +40 -0
- package/plugins/pbr/templates/SUMMARY.md.tmpl +81 -0
- package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +116 -0
- package/plugins/pbr/templates/codebase/ARCHITECTURE.md.tmpl +98 -0
- package/plugins/pbr/templates/codebase/CONCERNS.md.tmpl +93 -0
- package/plugins/pbr/templates/codebase/CONVENTIONS.md.tmpl +104 -0
- package/plugins/pbr/templates/codebase/INTEGRATIONS.md.tmpl +78 -0
- package/plugins/pbr/templates/codebase/STACK.md.tmpl +78 -0
- package/plugins/pbr/templates/codebase/STRUCTURE.md.tmpl +80 -0
- package/plugins/pbr/templates/codebase/TESTING.md.tmpl +107 -0
- package/plugins/pbr/templates/continue-here.md.tmpl +73 -0
- package/plugins/pbr/templates/prompt-partials/phase-project-context.md.tmpl +37 -0
- package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +124 -0
- package/plugins/pbr/templates/research/STACK.md.tmpl +71 -0
- package/plugins/pbr/templates/research/SUMMARY.md.tmpl +112 -0
- package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +81 -0
- package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +99 -0
- package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +36 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Plan-Build-Run Config",
|
|
4
|
+
"description": "Configuration schema for .planning/config.json",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"version": { "type": "integer", "enum": [1, 2] },
|
|
8
|
+
"context_strategy": { "type": "string", "enum": ["aggressive", "conservative", "balanced"] },
|
|
9
|
+
"mode": { "type": "string", "enum": ["interactive", "autonomous"] },
|
|
10
|
+
"depth": { "type": "string", "enum": ["quick", "standard", "comprehensive"], "description": "Workflow depth: quick = budget mode (fewer spawns, skip optional stages), standard = balanced mode (conditional spawns), comprehensive = thorough mode (all spawns, current default)" },
|
|
11
|
+
"features": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"structured_planning": { "type": "boolean" },
|
|
15
|
+
"goal_verification": { "type": "boolean" },
|
|
16
|
+
"integration_verification": { "type": "boolean" },
|
|
17
|
+
"context_isolation": { "type": "boolean" },
|
|
18
|
+
"atomic_commits": { "type": "boolean" },
|
|
19
|
+
"session_persistence": { "type": "boolean" },
|
|
20
|
+
"research_phase": { "type": "boolean" },
|
|
21
|
+
"plan_checking": { "type": "boolean" },
|
|
22
|
+
"tdd_mode": { "type": "boolean" },
|
|
23
|
+
"status_line": { "type": "boolean" },
|
|
24
|
+
"auto_continue": { "type": "boolean" },
|
|
25
|
+
"auto_advance": { "type": "boolean" },
|
|
26
|
+
"team_discussions": { "type": "boolean" },
|
|
27
|
+
"inline_verify": { "type": "boolean" }
|
|
28
|
+
},
|
|
29
|
+
"additionalProperties": false
|
|
30
|
+
},
|
|
31
|
+
"models": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"researcher": { "type": "string" },
|
|
35
|
+
"planner": { "type": "string" },
|
|
36
|
+
"executor": { "type": "string" },
|
|
37
|
+
"verifier": { "type": "string" },
|
|
38
|
+
"integration_checker": { "type": "string" },
|
|
39
|
+
"debugger": { "type": "string" },
|
|
40
|
+
"mapper": { "type": "string" },
|
|
41
|
+
"synthesizer": { "type": "string" },
|
|
42
|
+
"complexity_map": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"simple": { "type": "string", "default": "haiku" },
|
|
46
|
+
"medium": { "type": "string", "default": "sonnet" },
|
|
47
|
+
"complex": { "type": "string", "default": "inherit" }
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
},
|
|
54
|
+
"parallelization": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"enabled": { "type": "boolean" },
|
|
58
|
+
"plan_level": { "type": "boolean" },
|
|
59
|
+
"task_level": { "type": "boolean" },
|
|
60
|
+
"max_concurrent_agents": { "type": "integer", "minimum": 1, "maximum": 10 },
|
|
61
|
+
"min_plans_for_parallel": { "type": "integer", "minimum": 1 },
|
|
62
|
+
"use_teams": { "type": "boolean" }
|
|
63
|
+
},
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
},
|
|
66
|
+
"teams": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"planning_roles": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": { "type": "string" },
|
|
72
|
+
"default": ["architect", "security-reviewer", "test-designer"]
|
|
73
|
+
},
|
|
74
|
+
"review_roles": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": { "type": "string" },
|
|
77
|
+
"default": ["functional-reviewer", "security-auditor", "performance-analyst"]
|
|
78
|
+
},
|
|
79
|
+
"synthesis_model": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"default": "sonnet",
|
|
82
|
+
"description": "Model used for the synthesizer agent that combines team outputs"
|
|
83
|
+
},
|
|
84
|
+
"coordination": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"enum": ["file-based", "sequential"],
|
|
87
|
+
"default": "file-based",
|
|
88
|
+
"description": "How team members coordinate: file-based (parallel writes to separate files) or sequential"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"additionalProperties": false
|
|
92
|
+
},
|
|
93
|
+
"planning": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"properties": {
|
|
96
|
+
"commit_docs": { "type": "boolean" },
|
|
97
|
+
"max_tasks_per_plan": { "type": "integer", "minimum": 1, "maximum": 10 },
|
|
98
|
+
"search_gitignored": { "type": "boolean" }
|
|
99
|
+
},
|
|
100
|
+
"additionalProperties": false
|
|
101
|
+
},
|
|
102
|
+
"git": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"branching": { "type": "string", "enum": ["none", "phase", "milestone", "disabled"] },
|
|
106
|
+
"commit_format": { "type": "string" },
|
|
107
|
+
"phase_branch_template": { "type": "string" },
|
|
108
|
+
"milestone_branch_template": { "type": "string" },
|
|
109
|
+
"mode": { "type": "string", "enum": ["enabled", "disabled"] }
|
|
110
|
+
},
|
|
111
|
+
"additionalProperties": false
|
|
112
|
+
},
|
|
113
|
+
"gates": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"confirm_project": { "type": "boolean" },
|
|
117
|
+
"confirm_roadmap": { "type": "boolean" },
|
|
118
|
+
"confirm_plan": { "type": "boolean" },
|
|
119
|
+
"confirm_execute": { "type": "boolean" },
|
|
120
|
+
"confirm_transition": { "type": "boolean" },
|
|
121
|
+
"issues_review": { "type": "boolean" }
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"safety": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"properties": {
|
|
128
|
+
"always_confirm_destructive": { "type": "boolean" },
|
|
129
|
+
"always_confirm_external_services": { "type": "boolean" },
|
|
130
|
+
"enforce_phase_boundaries": { "type": "boolean" }
|
|
131
|
+
},
|
|
132
|
+
"additionalProperties": false
|
|
133
|
+
},
|
|
134
|
+
"hooks": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"properties": {
|
|
137
|
+
"autoFormat": { "type": "boolean" },
|
|
138
|
+
"typeCheck": { "type": "boolean" },
|
|
139
|
+
"detectConsoleLogs": { "type": "boolean" },
|
|
140
|
+
"blockDocSprawl": { "type": "boolean" },
|
|
141
|
+
"compactThreshold": { "type": "integer", "minimum": 10, "maximum": 200 }
|
|
142
|
+
},
|
|
143
|
+
"additionalProperties": false
|
|
144
|
+
},
|
|
145
|
+
"depth_profiles": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"description": "Depth profile overrides. Each profile maps depth value to effective feature/gate settings.",
|
|
148
|
+
"properties": {
|
|
149
|
+
"quick": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"properties": {
|
|
152
|
+
"features.research_phase": { "type": "boolean" },
|
|
153
|
+
"features.plan_checking": { "type": "boolean" },
|
|
154
|
+
"features.goal_verification": { "type": "boolean" },
|
|
155
|
+
"features.inline_verify": { "type": "boolean" },
|
|
156
|
+
"scan.mapper_count": { "type": "integer", "minimum": 1, "maximum": 4 },
|
|
157
|
+
"scan.mapper_areas": { "type": "array", "items": { "type": "string" } },
|
|
158
|
+
"debug.max_hypothesis_rounds": { "type": "integer", "minimum": 1, "maximum": 20 }
|
|
159
|
+
},
|
|
160
|
+
"additionalProperties": false
|
|
161
|
+
},
|
|
162
|
+
"standard": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"properties": {
|
|
165
|
+
"features.research_phase": { "type": "boolean" },
|
|
166
|
+
"features.plan_checking": { "type": "boolean" },
|
|
167
|
+
"features.goal_verification": { "type": "boolean" },
|
|
168
|
+
"features.inline_verify": { "type": "boolean" },
|
|
169
|
+
"scan.mapper_count": { "type": "integer", "minimum": 1, "maximum": 4 },
|
|
170
|
+
"scan.mapper_areas": { "type": "array", "items": { "type": "string" } },
|
|
171
|
+
"debug.max_hypothesis_rounds": { "type": "integer", "minimum": 1, "maximum": 20 }
|
|
172
|
+
},
|
|
173
|
+
"additionalProperties": false
|
|
174
|
+
},
|
|
175
|
+
"comprehensive": {
|
|
176
|
+
"type": "object",
|
|
177
|
+
"properties": {
|
|
178
|
+
"features.research_phase": { "type": "boolean" },
|
|
179
|
+
"features.plan_checking": { "type": "boolean" },
|
|
180
|
+
"features.goal_verification": { "type": "boolean" },
|
|
181
|
+
"features.inline_verify": { "type": "boolean" },
|
|
182
|
+
"scan.mapper_count": { "type": "integer", "minimum": 1, "maximum": 4 },
|
|
183
|
+
"scan.mapper_areas": { "type": "array", "items": { "type": "string" } },
|
|
184
|
+
"debug.max_hypothesis_rounds": { "type": "integer", "minimum": 1, "maximum": 20 }
|
|
185
|
+
},
|
|
186
|
+
"additionalProperties": false
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"additionalProperties": false
|
|
190
|
+
},
|
|
191
|
+
"debug": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"properties": {
|
|
194
|
+
"max_hypothesis_rounds": { "type": "integer", "minimum": 1, "maximum": 20 }
|
|
195
|
+
},
|
|
196
|
+
"additionalProperties": false
|
|
197
|
+
},
|
|
198
|
+
"spinner_tips": {
|
|
199
|
+
"type": "object",
|
|
200
|
+
"description": "Custom spinner tips shown during agent execution (Claude Code 2.1.45+)",
|
|
201
|
+
"properties": {
|
|
202
|
+
"tips": {
|
|
203
|
+
"type": "array",
|
|
204
|
+
"items": { "type": "string" },
|
|
205
|
+
"description": "Array of custom tip strings to display in the spinner"
|
|
206
|
+
},
|
|
207
|
+
"exclude_defaults": {
|
|
208
|
+
"type": "boolean",
|
|
209
|
+
"description": "When true, only show custom tips (no default Claude Code tips)"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"additionalProperties": false
|
|
213
|
+
},
|
|
214
|
+
"status_line": {
|
|
215
|
+
"type": "object",
|
|
216
|
+
"properties": {
|
|
217
|
+
"sections": {
|
|
218
|
+
"type": "array",
|
|
219
|
+
"items": { "type": "string", "enum": ["phase", "plan", "status", "context"] }
|
|
220
|
+
},
|
|
221
|
+
"brand_text": { "type": "string" },
|
|
222
|
+
"max_status_length": { "type": "integer", "minimum": 10, "maximum": 200 },
|
|
223
|
+
"context_bar": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"properties": {
|
|
226
|
+
"width": { "type": "integer", "minimum": 1, "maximum": 50 },
|
|
227
|
+
"thresholds": {
|
|
228
|
+
"type": "object",
|
|
229
|
+
"properties": {
|
|
230
|
+
"green": { "type": "integer", "minimum": 0, "maximum": 100 },
|
|
231
|
+
"yellow": { "type": "integer", "minimum": 0, "maximum": 100 }
|
|
232
|
+
},
|
|
233
|
+
"additionalProperties": false
|
|
234
|
+
},
|
|
235
|
+
"chars": {
|
|
236
|
+
"type": "object",
|
|
237
|
+
"properties": {
|
|
238
|
+
"filled": { "type": "string" },
|
|
239
|
+
"empty": { "type": "string" }
|
|
240
|
+
},
|
|
241
|
+
"additionalProperties": false
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"additionalProperties": false
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"additionalProperties": false
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
"additionalProperties": false
|
|
251
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreCompact hook: Preserves current state to STATE.md before
|
|
5
|
+
* lossy context compaction.
|
|
6
|
+
*
|
|
7
|
+
* Updates STATE.md with:
|
|
8
|
+
* - Timestamp of last compaction
|
|
9
|
+
* - ROADMAP progress summary (phase list, status)
|
|
10
|
+
* - Current plan context (objective from latest PLAN.md)
|
|
11
|
+
* - Config highlights (depth, mode, models, gates)
|
|
12
|
+
* - Active operation context
|
|
13
|
+
*
|
|
14
|
+
* Also outputs additionalContext for post-compaction recovery.
|
|
15
|
+
*
|
|
16
|
+
* Exit codes:
|
|
17
|
+
* 0 = always (informational hook, never blocks)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const { logHook } = require('./hook-logger');
|
|
23
|
+
const { logEvent } = require('./event-logger');
|
|
24
|
+
const { atomicWrite, configLoad, tailLines } = require('./pbr-tools');
|
|
25
|
+
|
|
26
|
+
function main() {
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
const planningDir = path.join(cwd, '.planning');
|
|
29
|
+
const stateFile = path.join(planningDir, 'STATE.md');
|
|
30
|
+
|
|
31
|
+
// Not a Plan-Build-Run project or no STATE.md
|
|
32
|
+
if (!fs.existsSync(stateFile)) {
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
let content = fs.readFileSync(stateFile, 'utf8');
|
|
38
|
+
const timestamp = new Date().toISOString();
|
|
39
|
+
|
|
40
|
+
// Gather context from multiple sources
|
|
41
|
+
const activeOp = readActiveOperation(planningDir);
|
|
42
|
+
const roadmapSummary = readRoadmapSummary(planningDir);
|
|
43
|
+
const currentPlan = readCurrentPlan(planningDir, content);
|
|
44
|
+
const configHighlights = readConfigHighlights(planningDir);
|
|
45
|
+
const recentErrors = readRecentErrors(planningDir, 3);
|
|
46
|
+
const recentAgents = readRecentAgents(planningDir, 5);
|
|
47
|
+
|
|
48
|
+
// Build continuity section
|
|
49
|
+
const continuityParts = [
|
|
50
|
+
`Last session: ${timestamp}`,
|
|
51
|
+
'Compaction occurred: context was auto-compacted at this point'
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
if (activeOp) {
|
|
55
|
+
continuityParts.push(`Active operation at compaction: ${activeOp}`);
|
|
56
|
+
}
|
|
57
|
+
if (roadmapSummary) {
|
|
58
|
+
continuityParts.push(`Roadmap progress:\n${roadmapSummary}`);
|
|
59
|
+
}
|
|
60
|
+
if (currentPlan) {
|
|
61
|
+
continuityParts.push(`Current plan: ${currentPlan}`);
|
|
62
|
+
}
|
|
63
|
+
if (configHighlights) {
|
|
64
|
+
continuityParts.push(`Config: ${configHighlights}`);
|
|
65
|
+
}
|
|
66
|
+
if (recentErrors.length > 0) {
|
|
67
|
+
continuityParts.push(`Recent errors:\n${recentErrors.map(e => ' - ' + e).join('\n')}`);
|
|
68
|
+
}
|
|
69
|
+
if (recentAgents.length > 0) {
|
|
70
|
+
continuityParts.push(`Recent agents: ${recentAgents.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
continuityParts.push('Note: Some conversation context may have been lost. Check STATE.md and SUMMARY.md files for ground truth.');
|
|
74
|
+
|
|
75
|
+
// Update or add Session Continuity section
|
|
76
|
+
const continuityHeader = '## Session Continuity';
|
|
77
|
+
const continuityContent = continuityParts.join('\n');
|
|
78
|
+
|
|
79
|
+
if (content.includes(continuityHeader)) {
|
|
80
|
+
// Replace existing section
|
|
81
|
+
content = content.replace(
|
|
82
|
+
/## Session Continuity[\s\S]*?(?=\n## |\n---|\s*$)/,
|
|
83
|
+
`${continuityHeader}\n${continuityContent}\n`
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
// Append section
|
|
87
|
+
content = content.trimEnd() + `\n\n${continuityHeader}\n${continuityContent}\n`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
atomicWrite(stateFile, content);
|
|
91
|
+
|
|
92
|
+
// Output additionalContext for post-compaction recovery
|
|
93
|
+
const recoveryContext = buildRecoveryContext(activeOp, roadmapSummary, currentPlan, configHighlights, recentErrors, recentAgents);
|
|
94
|
+
if (recoveryContext) {
|
|
95
|
+
const output = {
|
|
96
|
+
additionalContext: recoveryContext
|
|
97
|
+
};
|
|
98
|
+
process.stdout.write(JSON.stringify(output));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logHook('context-budget-check', 'PreCompact', 'saved', {
|
|
102
|
+
stateFile: 'STATE.md',
|
|
103
|
+
hasRoadmap: !!roadmapSummary,
|
|
104
|
+
hasPlan: !!currentPlan,
|
|
105
|
+
hasConfig: !!configHighlights
|
|
106
|
+
});
|
|
107
|
+
logEvent('workflow', 'compaction', { timestamp });
|
|
108
|
+
} catch (e) {
|
|
109
|
+
logHook('context-budget-check', 'PreCompact', 'error', { error: e.message });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function readActiveOperation(planningDir) {
|
|
116
|
+
const activeOpFile = path.join(planningDir, '.active-operation');
|
|
117
|
+
if (!fs.existsSync(activeOpFile)) return '';
|
|
118
|
+
try {
|
|
119
|
+
return fs.readFileSync(activeOpFile, 'utf8').trim();
|
|
120
|
+
} catch (_e) {
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function readRoadmapSummary(planningDir) {
|
|
126
|
+
const roadmapFile = path.join(planningDir, 'ROADMAP.md');
|
|
127
|
+
if (!fs.existsSync(roadmapFile)) return '';
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const roadmap = fs.readFileSync(roadmapFile, 'utf8');
|
|
131
|
+
|
|
132
|
+
// Extract progress table
|
|
133
|
+
const progressMatch = roadmap.match(/## Progress[\s\S]*?\|[\s\S]*?(?=\n##|\s*$)/);
|
|
134
|
+
if (!progressMatch) return '';
|
|
135
|
+
|
|
136
|
+
const rows = progressMatch[0].split('\n').filter(r => r.includes('|'));
|
|
137
|
+
// Skip header and separator rows
|
|
138
|
+
const dataRows = rows.filter(r => !r.includes('---') && !r.toLowerCase().includes('phase'));
|
|
139
|
+
|
|
140
|
+
if (dataRows.length === 0) return '';
|
|
141
|
+
|
|
142
|
+
// Build compact summary
|
|
143
|
+
const phases = [];
|
|
144
|
+
for (const row of dataRows) {
|
|
145
|
+
const cols = row.split('|').map(c => c.trim()).filter(Boolean);
|
|
146
|
+
if (cols.length >= 4) {
|
|
147
|
+
const num = cols[0];
|
|
148
|
+
const name = cols[1] || '';
|
|
149
|
+
const status = cols[3] || '';
|
|
150
|
+
if (num && /^\d+/.test(num)) {
|
|
151
|
+
phases.push(` Phase ${num} (${name.substring(0, 30)}): ${status}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return phases.join('\n');
|
|
157
|
+
} catch (_e) {
|
|
158
|
+
return '';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function readCurrentPlan(planningDir, stateContent) {
|
|
163
|
+
// Prefer .active-plan signal file (definitive) over directory listing (guesswork)
|
|
164
|
+
const activePlanFile = path.join(planningDir, '.active-plan');
|
|
165
|
+
if (fs.existsSync(activePlanFile)) {
|
|
166
|
+
try {
|
|
167
|
+
const activePlan = fs.readFileSync(activePlanFile, 'utf8').trim();
|
|
168
|
+
if (activePlan) return activePlan;
|
|
169
|
+
} catch (_e) { /* fall through */ }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Fallback: Extract current phase from STATE.md and find latest plan
|
|
173
|
+
const phaseMatch = stateContent.match(/Phase:\s*(\d+)\s+of\s+\d+/);
|
|
174
|
+
if (!phaseMatch) return '';
|
|
175
|
+
|
|
176
|
+
const currentPhase = phaseMatch[1].padStart(2, '0');
|
|
177
|
+
|
|
178
|
+
// Find the phase directory
|
|
179
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
180
|
+
if (!fs.existsSync(phasesDir)) return '';
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const dirs = fs.readdirSync(phasesDir).filter(d => d.startsWith(currentPhase));
|
|
184
|
+
if (dirs.length === 0) return '';
|
|
185
|
+
|
|
186
|
+
const phaseDir = path.join(phasesDir, dirs[0]);
|
|
187
|
+
|
|
188
|
+
// Find PLAN.md files
|
|
189
|
+
const planFiles = fs.readdirSync(phaseDir).filter(f => f.endsWith('PLAN.md'));
|
|
190
|
+
if (planFiles.length === 0) return 'No PLAN.md found in current phase';
|
|
191
|
+
|
|
192
|
+
// Read the last plan's objective only (frontmatter + objective tag)
|
|
193
|
+
const planFile = path.join(phaseDir, planFiles[planFiles.length - 1]);
|
|
194
|
+
const planContent = fs.readFileSync(planFile, 'utf8');
|
|
195
|
+
|
|
196
|
+
const objMatch = planContent.match(/<objective>([\s\S]*?)<\/objective>/);
|
|
197
|
+
const objective = objMatch ? objMatch[1].trim().substring(0, 150) : '';
|
|
198
|
+
|
|
199
|
+
return `${dirs[0]}/${planFiles[planFiles.length - 1]}${objective ? ' — ' + objective : ''}`;
|
|
200
|
+
} catch (_e) {
|
|
201
|
+
return '';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function readRecentErrors(planningDir, maxErrors) {
|
|
206
|
+
const count = maxErrors || 3;
|
|
207
|
+
try {
|
|
208
|
+
const eventsLog = path.join(planningDir, 'logs', 'events.jsonl');
|
|
209
|
+
// Only read the last 50 lines — errors are rare, so 50 tail lines
|
|
210
|
+
// is more than enough to find the most recent ones
|
|
211
|
+
const lines = tailLines(eventsLog, 50);
|
|
212
|
+
if (lines.length === 0) return [];
|
|
213
|
+
|
|
214
|
+
const errors = [];
|
|
215
|
+
// Read backwards for most recent
|
|
216
|
+
for (let i = lines.length - 1; i >= 0 && errors.length < count; i--) {
|
|
217
|
+
try {
|
|
218
|
+
const entry = JSON.parse(lines[i]);
|
|
219
|
+
if (entry.cat === 'error' || entry.event === 'tool-failure' || (entry.cat === 'workflow' && entry.status === 'block')) {
|
|
220
|
+
errors.push(`${entry.event || entry.cat}: ${entry.error || entry.reason || entry.message || 'unknown'}`.substring(0, 120));
|
|
221
|
+
}
|
|
222
|
+
} catch (_e) { /* skip */ }
|
|
223
|
+
}
|
|
224
|
+
return errors;
|
|
225
|
+
} catch (_e) {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function readRecentAgents(planningDir, maxAgents) {
|
|
231
|
+
const count = maxAgents || 5;
|
|
232
|
+
try {
|
|
233
|
+
const hooksLog = path.join(planningDir, 'logs', 'hooks.jsonl');
|
|
234
|
+
// Only read the last 30 lines — agent spawns are interspersed with
|
|
235
|
+
// other hook events, so 30 tail lines covers recent agents well
|
|
236
|
+
const lines = tailLines(hooksLog, 30);
|
|
237
|
+
if (lines.length === 0) return [];
|
|
238
|
+
|
|
239
|
+
const agents = [];
|
|
240
|
+
// Read backwards for most recent
|
|
241
|
+
for (let i = lines.length - 1; i >= 0 && agents.length < count; i--) {
|
|
242
|
+
try {
|
|
243
|
+
const entry = JSON.parse(lines[i]);
|
|
244
|
+
if (entry.event === 'SubagentStart' && entry.decision === 'spawned') {
|
|
245
|
+
const type = entry.agent_type || 'unknown';
|
|
246
|
+
const desc = entry.description ? ` (${entry.description.substring(0, 60)})` : '';
|
|
247
|
+
agents.push(`${type}${desc}`);
|
|
248
|
+
}
|
|
249
|
+
} catch (_e) { /* skip */ }
|
|
250
|
+
}
|
|
251
|
+
return agents.reverse(); // Chronological order
|
|
252
|
+
} catch (_e) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readConfigHighlights(planningDir) {
|
|
258
|
+
const config = configLoad(planningDir);
|
|
259
|
+
if (!config) return '';
|
|
260
|
+
|
|
261
|
+
const parts = [];
|
|
262
|
+
if (config.depth) parts.push(`depth=${config.depth}`);
|
|
263
|
+
if (config.mode) parts.push(`mode=${config.mode}`);
|
|
264
|
+
if (config.models && config.models.executor) parts.push(`executor=${config.models.executor}`);
|
|
265
|
+
if (config.gates && config.gates.verification !== undefined) parts.push(`verify=${config.gates.verification}`);
|
|
266
|
+
if (config.git && config.git.auto_commit !== undefined) parts.push(`auto_commit=${config.git.auto_commit}`);
|
|
267
|
+
return parts.join(', ');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function buildRecoveryContext(activeOp, roadmapSummary, currentPlan, configHighlights, recentErrors, recentAgents) {
|
|
271
|
+
const parts = ['[Post-Compaction Recovery] Context was auto-compacted. Key state preserved:'];
|
|
272
|
+
|
|
273
|
+
if (activeOp) parts.push(`Active operation: ${activeOp}`);
|
|
274
|
+
if (currentPlan) parts.push(`Current plan: ${currentPlan}`);
|
|
275
|
+
if (configHighlights) parts.push(`Config: ${configHighlights}`);
|
|
276
|
+
if (recentErrors && recentErrors.length > 0) parts.push(`Recent errors: ${recentErrors.join('; ')}`);
|
|
277
|
+
if (recentAgents && recentAgents.length > 0) parts.push(`Recent agents: ${recentAgents.join(', ')}`);
|
|
278
|
+
if (roadmapSummary) parts.push(`Progress:\n${roadmapSummary}`);
|
|
279
|
+
|
|
280
|
+
parts.push('Read .planning/STATE.md for full context.');
|
|
281
|
+
|
|
282
|
+
// Only return if we have something meaningful beyond header and footer
|
|
283
|
+
return parts.length > 2 ? parts.join('\n') : '';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
module.exports = { readRoadmapSummary, readCurrentPlan, readConfigHighlights, buildRecoveryContext, readRecentErrors, readRecentAgents };
|
|
287
|
+
if (require.main === module) { main(); }
|
|
@@ -0,0 +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(); }
|