@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.
Files changed (221) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CLAUDE.md +149 -0
  3. package/LICENSE +21 -0
  4. package/README.md +247 -0
  5. package/dashboard/bin/cli.js +25 -0
  6. package/dashboard/package.json +34 -0
  7. package/dashboard/public/.gitkeep +0 -0
  8. package/dashboard/public/css/layout.css +406 -0
  9. package/dashboard/public/css/status-colors.css +98 -0
  10. package/dashboard/public/js/htmx-title.js +5 -0
  11. package/dashboard/public/js/sidebar-toggle.js +20 -0
  12. package/dashboard/src/app.js +78 -0
  13. package/dashboard/src/middleware/errorHandler.js +52 -0
  14. package/dashboard/src/middleware/notFoundHandler.js +9 -0
  15. package/dashboard/src/repositories/planning.repository.js +128 -0
  16. package/dashboard/src/routes/events.routes.js +40 -0
  17. package/dashboard/src/routes/index.routes.js +31 -0
  18. package/dashboard/src/routes/pages.routes.js +195 -0
  19. package/dashboard/src/server.js +42 -0
  20. package/dashboard/src/services/dashboard.service.js +222 -0
  21. package/dashboard/src/services/phase.service.js +167 -0
  22. package/dashboard/src/services/project.service.js +57 -0
  23. package/dashboard/src/services/roadmap.service.js +171 -0
  24. package/dashboard/src/services/sse.service.js +58 -0
  25. package/dashboard/src/services/todo.service.js +254 -0
  26. package/dashboard/src/services/watcher.service.js +48 -0
  27. package/dashboard/src/views/coming-soon.ejs +11 -0
  28. package/dashboard/src/views/error.ejs +13 -0
  29. package/dashboard/src/views/index.ejs +5 -0
  30. package/dashboard/src/views/layout.ejs +1 -0
  31. package/dashboard/src/views/partials/dashboard-content.ejs +77 -0
  32. package/dashboard/src/views/partials/footer.ejs +3 -0
  33. package/dashboard/src/views/partials/head.ejs +21 -0
  34. package/dashboard/src/views/partials/header.ejs +12 -0
  35. package/dashboard/src/views/partials/layout-bottom.ejs +15 -0
  36. package/dashboard/src/views/partials/layout-top.ejs +8 -0
  37. package/dashboard/src/views/partials/phase-content.ejs +181 -0
  38. package/dashboard/src/views/partials/phases-content.ejs +117 -0
  39. package/dashboard/src/views/partials/roadmap-content.ejs +142 -0
  40. package/dashboard/src/views/partials/sidebar.ejs +38 -0
  41. package/dashboard/src/views/partials/todo-create-content.ejs +53 -0
  42. package/dashboard/src/views/partials/todo-detail-content.ejs +38 -0
  43. package/dashboard/src/views/partials/todos-content.ejs +53 -0
  44. package/dashboard/src/views/phase-detail.ejs +5 -0
  45. package/dashboard/src/views/phases.ejs +5 -0
  46. package/dashboard/src/views/roadmap.ejs +5 -0
  47. package/dashboard/src/views/todo-create.ejs +5 -0
  48. package/dashboard/src/views/todo-detail.ejs +5 -0
  49. package/dashboard/src/views/todos.ejs +5 -0
  50. package/package.json +57 -0
  51. package/plugins/pbr/.claude-plugin/plugin.json +13 -0
  52. package/plugins/pbr/UI-CONSISTENCY-GAPS.md +61 -0
  53. package/plugins/pbr/agents/codebase-mapper.md +271 -0
  54. package/plugins/pbr/agents/debugger.md +281 -0
  55. package/plugins/pbr/agents/executor.md +407 -0
  56. package/plugins/pbr/agents/general.md +164 -0
  57. package/plugins/pbr/agents/integration-checker.md +141 -0
  58. package/plugins/pbr/agents/plan-checker.md +280 -0
  59. package/plugins/pbr/agents/planner.md +358 -0
  60. package/plugins/pbr/agents/researcher.md +363 -0
  61. package/plugins/pbr/agents/synthesizer.md +230 -0
  62. package/plugins/pbr/agents/verifier.md +454 -0
  63. package/plugins/pbr/commands/begin.md +5 -0
  64. package/plugins/pbr/commands/build.md +5 -0
  65. package/plugins/pbr/commands/config.md +5 -0
  66. package/plugins/pbr/commands/continue.md +5 -0
  67. package/plugins/pbr/commands/debug.md +5 -0
  68. package/plugins/pbr/commands/discuss.md +5 -0
  69. package/plugins/pbr/commands/explore.md +5 -0
  70. package/plugins/pbr/commands/health.md +5 -0
  71. package/plugins/pbr/commands/help.md +5 -0
  72. package/plugins/pbr/commands/import.md +5 -0
  73. package/plugins/pbr/commands/milestone.md +5 -0
  74. package/plugins/pbr/commands/note.md +5 -0
  75. package/plugins/pbr/commands/pause.md +5 -0
  76. package/plugins/pbr/commands/plan.md +5 -0
  77. package/plugins/pbr/commands/quick.md +5 -0
  78. package/plugins/pbr/commands/resume.md +5 -0
  79. package/plugins/pbr/commands/review.md +5 -0
  80. package/plugins/pbr/commands/scan.md +5 -0
  81. package/plugins/pbr/commands/setup.md +5 -0
  82. package/plugins/pbr/commands/status.md +5 -0
  83. package/plugins/pbr/commands/todo.md +5 -0
  84. package/plugins/pbr/contexts/dev.md +27 -0
  85. package/plugins/pbr/contexts/research.md +28 -0
  86. package/plugins/pbr/contexts/review.md +36 -0
  87. package/plugins/pbr/hooks/hooks.json +183 -0
  88. package/plugins/pbr/references/agent-anti-patterns.md +24 -0
  89. package/plugins/pbr/references/agent-interactions.md +134 -0
  90. package/plugins/pbr/references/agent-teams.md +54 -0
  91. package/plugins/pbr/references/checkpoints.md +157 -0
  92. package/plugins/pbr/references/common-bug-patterns.md +13 -0
  93. package/plugins/pbr/references/continuation-format.md +212 -0
  94. package/plugins/pbr/references/deviation-rules.md +112 -0
  95. package/plugins/pbr/references/git-integration.md +226 -0
  96. package/plugins/pbr/references/integration-patterns.md +117 -0
  97. package/plugins/pbr/references/model-profiles.md +99 -0
  98. package/plugins/pbr/references/model-selection.md +31 -0
  99. package/plugins/pbr/references/pbr-rules.md +193 -0
  100. package/plugins/pbr/references/plan-authoring.md +181 -0
  101. package/plugins/pbr/references/plan-format.md +283 -0
  102. package/plugins/pbr/references/planning-config.md +213 -0
  103. package/plugins/pbr/references/questioning.md +214 -0
  104. package/plugins/pbr/references/reading-verification.md +127 -0
  105. package/plugins/pbr/references/stub-patterns.md +160 -0
  106. package/plugins/pbr/references/subagent-coordination.md +119 -0
  107. package/plugins/pbr/references/ui-formatting.md +399 -0
  108. package/plugins/pbr/references/verification-patterns.md +198 -0
  109. package/plugins/pbr/references/wave-execution.md +95 -0
  110. package/plugins/pbr/scripts/auto-continue.js +80 -0
  111. package/plugins/pbr/scripts/check-dangerous-commands.js +136 -0
  112. package/plugins/pbr/scripts/check-doc-sprawl.js +102 -0
  113. package/plugins/pbr/scripts/check-phase-boundary.js +196 -0
  114. package/plugins/pbr/scripts/check-plan-format.js +270 -0
  115. package/plugins/pbr/scripts/check-roadmap-sync.js +252 -0
  116. package/plugins/pbr/scripts/check-skill-workflow.js +262 -0
  117. package/plugins/pbr/scripts/check-state-sync.js +476 -0
  118. package/plugins/pbr/scripts/check-subagent-output.js +144 -0
  119. package/plugins/pbr/scripts/config-schema.json +251 -0
  120. package/plugins/pbr/scripts/context-budget-check.js +287 -0
  121. package/plugins/pbr/scripts/event-handler.js +151 -0
  122. package/plugins/pbr/scripts/event-logger.js +92 -0
  123. package/plugins/pbr/scripts/hook-logger.js +76 -0
  124. package/plugins/pbr/scripts/hooks-schema.json +79 -0
  125. package/plugins/pbr/scripts/log-subagent.js +152 -0
  126. package/plugins/pbr/scripts/log-tool-failure.js +88 -0
  127. package/plugins/pbr/scripts/pbr-tools.js +1301 -0
  128. package/plugins/pbr/scripts/post-write-dispatch.js +66 -0
  129. package/plugins/pbr/scripts/post-write-quality.js +207 -0
  130. package/plugins/pbr/scripts/pre-bash-dispatch.js +56 -0
  131. package/plugins/pbr/scripts/pre-write-dispatch.js +62 -0
  132. package/plugins/pbr/scripts/progress-tracker.js +228 -0
  133. package/plugins/pbr/scripts/session-cleanup.js +254 -0
  134. package/plugins/pbr/scripts/status-line.js +285 -0
  135. package/plugins/pbr/scripts/suggest-compact.js +119 -0
  136. package/plugins/pbr/scripts/task-completed.js +45 -0
  137. package/plugins/pbr/scripts/track-context-budget.js +119 -0
  138. package/plugins/pbr/scripts/validate-commit.js +200 -0
  139. package/plugins/pbr/scripts/validate-plugin-structure.js +172 -0
  140. package/plugins/pbr/skills/begin/SKILL.md +545 -0
  141. package/plugins/pbr/skills/begin/templates/PROJECT.md.tmpl +33 -0
  142. package/plugins/pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +18 -0
  143. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +49 -0
  144. package/plugins/pbr/skills/begin/templates/config.json.tmpl +63 -0
  145. package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +19 -0
  146. package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +30 -0
  147. package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +16 -0
  148. package/plugins/pbr/skills/build/SKILL.md +962 -0
  149. package/plugins/pbr/skills/config/SKILL.md +241 -0
  150. package/plugins/pbr/skills/continue/SKILL.md +127 -0
  151. package/plugins/pbr/skills/debug/SKILL.md +489 -0
  152. package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +16 -0
  153. package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +27 -0
  154. package/plugins/pbr/skills/discuss/SKILL.md +338 -0
  155. package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +61 -0
  156. package/plugins/pbr/skills/discuss/templates/decision-categories.md +9 -0
  157. package/plugins/pbr/skills/explore/SKILL.md +362 -0
  158. package/plugins/pbr/skills/health/SKILL.md +186 -0
  159. package/plugins/pbr/skills/health/templates/check-pattern.md.tmpl +30 -0
  160. package/plugins/pbr/skills/health/templates/output-format.md.tmpl +63 -0
  161. package/plugins/pbr/skills/help/SKILL.md +140 -0
  162. package/plugins/pbr/skills/import/SKILL.md +490 -0
  163. package/plugins/pbr/skills/milestone/SKILL.md +673 -0
  164. package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +48 -0
  165. package/plugins/pbr/skills/milestone/templates/stats-file.md.tmpl +30 -0
  166. package/plugins/pbr/skills/note/SKILL.md +212 -0
  167. package/plugins/pbr/skills/pause/SKILL.md +235 -0
  168. package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +71 -0
  169. package/plugins/pbr/skills/plan/SKILL.md +628 -0
  170. package/plugins/pbr/skills/plan/decimal-phase-calc.md +98 -0
  171. package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +21 -0
  172. package/plugins/pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +32 -0
  173. package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +38 -0
  174. package/plugins/pbr/skills/plan/templates/researcher-prompt.md.tmpl +19 -0
  175. package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +23 -0
  176. package/plugins/pbr/skills/quick/SKILL.md +335 -0
  177. package/plugins/pbr/skills/resume/SKILL.md +388 -0
  178. package/plugins/pbr/skills/review/SKILL.md +652 -0
  179. package/plugins/pbr/skills/review/templates/debugger-prompt.md.tmpl +60 -0
  180. package/plugins/pbr/skills/review/templates/gap-planner-prompt.md.tmpl +40 -0
  181. package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +115 -0
  182. package/plugins/pbr/skills/scan/SKILL.md +269 -0
  183. package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +201 -0
  184. package/plugins/pbr/skills/setup/SKILL.md +227 -0
  185. package/plugins/pbr/skills/shared/commit-planning-docs.md +35 -0
  186. package/plugins/pbr/skills/shared/config-loading.md +102 -0
  187. package/plugins/pbr/skills/shared/context-budget.md +40 -0
  188. package/plugins/pbr/skills/shared/context-loader-task.md +86 -0
  189. package/plugins/pbr/skills/shared/digest-select.md +79 -0
  190. package/plugins/pbr/skills/shared/domain-probes.md +125 -0
  191. package/plugins/pbr/skills/shared/error-reporting.md +79 -0
  192. package/plugins/pbr/skills/shared/gate-prompts.md +388 -0
  193. package/plugins/pbr/skills/shared/phase-argument-parsing.md +45 -0
  194. package/plugins/pbr/skills/shared/progress-display.md +53 -0
  195. package/plugins/pbr/skills/shared/revision-loop.md +81 -0
  196. package/plugins/pbr/skills/shared/state-loading.md +62 -0
  197. package/plugins/pbr/skills/shared/state-update.md +161 -0
  198. package/plugins/pbr/skills/shared/universal-anti-patterns.md +33 -0
  199. package/plugins/pbr/skills/status/SKILL.md +353 -0
  200. package/plugins/pbr/skills/todo/SKILL.md +181 -0
  201. package/plugins/pbr/templates/CONTEXT.md.tmpl +52 -0
  202. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +151 -0
  203. package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +97 -0
  204. package/plugins/pbr/templates/ROADMAP.md.tmpl +40 -0
  205. package/plugins/pbr/templates/SUMMARY.md.tmpl +81 -0
  206. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +116 -0
  207. package/plugins/pbr/templates/codebase/ARCHITECTURE.md.tmpl +98 -0
  208. package/plugins/pbr/templates/codebase/CONCERNS.md.tmpl +93 -0
  209. package/plugins/pbr/templates/codebase/CONVENTIONS.md.tmpl +104 -0
  210. package/plugins/pbr/templates/codebase/INTEGRATIONS.md.tmpl +78 -0
  211. package/plugins/pbr/templates/codebase/STACK.md.tmpl +78 -0
  212. package/plugins/pbr/templates/codebase/STRUCTURE.md.tmpl +80 -0
  213. package/plugins/pbr/templates/codebase/TESTING.md.tmpl +107 -0
  214. package/plugins/pbr/templates/continue-here.md.tmpl +73 -0
  215. package/plugins/pbr/templates/prompt-partials/phase-project-context.md.tmpl +37 -0
  216. package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +124 -0
  217. package/plugins/pbr/templates/research/STACK.md.tmpl +71 -0
  218. package/plugins/pbr/templates/research/SUMMARY.md.tmpl +112 -0
  219. package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +81 -0
  220. package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +99 -0
  221. package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +36 -0
@@ -0,0 +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 };
@@ -0,0 +1,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 = {}) {
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
+ try {
55
+ let lines = [];
56
+ if (fs.existsSync(logPath)) {
57
+ const content = fs.readFileSync(logPath, 'utf8').trim();
58
+ if (content) {
59
+ lines = content.split('\n');
60
+ }
61
+ }
62
+
63
+ lines.push(JSON.stringify(entry));
64
+
65
+ // Keep only last MAX_ENTRIES
66
+ if (lines.length > MAX_ENTRIES) {
67
+ lines = lines.slice(lines.length - MAX_ENTRIES);
68
+ }
69
+
70
+ fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf8');
71
+ } catch (_e) {
72
+ // Best-effort logging — never fail the hook
73
+ }
74
+ }
75
+
76
+ module.exports = { logHook };
@@ -0,0 +1,79 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Plan-Build-Run Hooks",
4
+ "description": "Schema for Claude Code hooks.json configuration",
5
+ "type": "object",
6
+ "properties": {
7
+ "$schema": { "type": "string" },
8
+ "description": { "type": "string" },
9
+ "hooks": {
10
+ "type": "object",
11
+ "properties": {
12
+ "SessionStart": { "$ref": "#/definitions/hookEntryList" },
13
+ "PreToolUse": { "$ref": "#/definitions/hookEntryList" },
14
+ "PostToolUse": { "$ref": "#/definitions/hookEntryList" },
15
+ "PostToolUseFailure": { "$ref": "#/definitions/hookEntryList" },
16
+ "PreCompact": { "$ref": "#/definitions/hookEntryList" },
17
+ "Stop": { "$ref": "#/definitions/hookEntryList" },
18
+ "SubagentStart": { "$ref": "#/definitions/hookEntryList" },
19
+ "SubagentStop": { "$ref": "#/definitions/hookEntryList" },
20
+ "TaskCompleted": { "$ref": "#/definitions/hookEntryList" },
21
+ "SessionEnd": { "$ref": "#/definitions/hookEntryList" }
22
+ },
23
+ "additionalProperties": false
24
+ }
25
+ },
26
+ "required": ["hooks"],
27
+ "additionalProperties": true,
28
+ "definitions": {
29
+ "hookEntryList": {
30
+ "type": "array",
31
+ "items": { "$ref": "#/definitions/hookEntry" }
32
+ },
33
+ "hookEntry": {
34
+ "type": "object",
35
+ "properties": {
36
+ "matcher": {
37
+ "type": "string",
38
+ "description": "Regex tested against the tool name (e.g. 'Write|Edit', 'Bash', 'Task')"
39
+ },
40
+ "hooks": {
41
+ "type": "array",
42
+ "items": { "$ref": "#/definitions/hookCommand" }
43
+ }
44
+ },
45
+ "required": ["hooks"],
46
+ "additionalProperties": false
47
+ },
48
+ "hookCommand": {
49
+ "type": "object",
50
+ "properties": {
51
+ "type": {
52
+ "type": "string",
53
+ "enum": ["command"],
54
+ "description": "Hook type — currently only 'command' is supported"
55
+ },
56
+ "command": {
57
+ "type": "string",
58
+ "description": "Shell command to execute (use ${CLAUDE_PLUGIN_ROOT} for script paths)"
59
+ },
60
+ "statusMessage": {
61
+ "type": "string",
62
+ "description": "Status message shown in Claude Code UI while hook runs"
63
+ },
64
+ "async": {
65
+ "type": "boolean",
66
+ "description": "Run in background without blocking (stdout not captured)",
67
+ "default": false
68
+ },
69
+ "timeout": {
70
+ "type": "number",
71
+ "minimum": 1,
72
+ "description": "Maximum seconds before hook is killed (required when async: true)"
73
+ }
74
+ },
75
+ "required": ["type", "command"],
76
+ "additionalProperties": false
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SubagentStart / SubagentStop logging hook.
5
+ *
6
+ * Usage:
7
+ * node log-subagent.js start — called on SubagentStart
8
+ * node log-subagent.js stop — called on SubagentStop
9
+ *
10
+ * On start: logs spawn event and injects project context via additionalContext.
11
+ * On stop: logs completion event.
12
+ *
13
+ * Non-blocking — exits 0 always.
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { logHook } = require('./hook-logger');
19
+ const { logEvent } = require('./event-logger');
20
+ const { configLoad } = require('./pbr-tools');
21
+
22
+ function readStdin() {
23
+ try {
24
+ const input = fs.readFileSync(0, 'utf8').trim();
25
+ if (input) return JSON.parse(input);
26
+ } catch (_e) {
27
+ // empty or non-JSON stdin
28
+ }
29
+ return {};
30
+ }
31
+
32
+ function main() {
33
+ const action = process.argv[2]; // 'start' or 'stop'
34
+ const data = readStdin();
35
+
36
+ if (action === 'start') {
37
+ logHook('log-subagent', 'SubagentStart', 'spawned', {
38
+ agent_id: data.agent_id || null,
39
+ agent_type: data.agent_type || data.subagent_type || null,
40
+ description: data.description || null
41
+ });
42
+ logEvent('agent', 'spawn', {
43
+ agent_id: data.agent_id || null,
44
+ agent_type: data.agent_type || data.subagent_type || null,
45
+ description: data.description || null
46
+ });
47
+
48
+ // Write .active-agent signal so other hooks know a subagent is running
49
+ writeActiveAgent(data.agent_type || data.subagent_type || 'unknown');
50
+
51
+ // Inject project context into subagent
52
+ const context = buildAgentContext();
53
+ if (context) {
54
+ const output = {
55
+ hookSpecificOutput: {
56
+ hookEventName: 'SubagentStart',
57
+ additionalContext: context
58
+ }
59
+ };
60
+ process.stdout.write(JSON.stringify(output));
61
+ }
62
+ } else if (action === 'stop') {
63
+ // Remove .active-agent signal
64
+ removeActiveAgent();
65
+ logHook('log-subagent', 'SubagentStop', 'completed', {
66
+ agent_id: data.agent_id || null,
67
+ agent_type: data.agent_type || data.subagent_type || null,
68
+ duration_ms: data.duration_ms || null
69
+ });
70
+ logEvent('agent', 'complete', {
71
+ agent_id: data.agent_id || null,
72
+ agent_type: data.agent_type || data.subagent_type || null,
73
+ duration_ms: data.duration_ms || null
74
+ });
75
+ }
76
+
77
+ process.exit(0);
78
+ }
79
+
80
+ function writeActiveAgent(agentType) {
81
+ try {
82
+ const cwd = process.cwd();
83
+ const filePath = path.join(cwd, '.planning', '.active-agent');
84
+ if (fs.existsSync(path.join(cwd, '.planning'))) {
85
+ fs.writeFileSync(filePath, agentType, 'utf8');
86
+ }
87
+ } catch (_e) {
88
+ // Best-effort
89
+ }
90
+ }
91
+
92
+ function removeActiveAgent() {
93
+ try {
94
+ const cwd = process.cwd();
95
+ const filePath = path.join(cwd, '.planning', '.active-agent');
96
+ if (fs.existsSync(filePath)) {
97
+ fs.unlinkSync(filePath);
98
+ }
99
+ } catch (_e) {
100
+ // Best-effort
101
+ }
102
+ }
103
+
104
+ function buildAgentContext() {
105
+ const cwd = process.cwd();
106
+ const planningDir = path.join(cwd, '.planning');
107
+
108
+ if (!fs.existsSync(planningDir)) return '';
109
+
110
+ const parts = [];
111
+
112
+ // Current phase and status from STATE.md
113
+ const stateFile = path.join(planningDir, 'STATE.md');
114
+ if (fs.existsSync(stateFile)) {
115
+ try {
116
+ const state = fs.readFileSync(stateFile, 'utf8');
117
+ const phaseMatch = state.match(/Phase:\s*(\d+)\s+of\s+(\d+)/);
118
+ const statusMatch = state.match(/\*{0,2}(?:Phase\s+)?Status\*{0,2}:\s*["']?(\w+)["']?/i);
119
+ if (phaseMatch) {
120
+ parts.push(`Phase ${phaseMatch[1]} of ${phaseMatch[2]}${statusMatch ? ' (' + statusMatch[1] + ')' : ''}`);
121
+ }
122
+ } catch (_e) {
123
+ // skip
124
+ }
125
+ }
126
+
127
+ // Active skill context
128
+ const activeSkillFile = path.join(planningDir, '.active-skill');
129
+ if (fs.existsSync(activeSkillFile)) {
130
+ try {
131
+ const skill = fs.readFileSync(activeSkillFile, 'utf8').trim();
132
+ if (skill) parts.push(`Active skill: /pbr:${skill}`);
133
+ } catch (_e) {
134
+ // skip
135
+ }
136
+ }
137
+
138
+ // Config highlights
139
+ const config = configLoad(planningDir);
140
+ if (config) {
141
+ const configParts = [];
142
+ if (config.depth) configParts.push(`depth=${config.depth}`);
143
+ if (config.git && config.git.auto_commit !== undefined) configParts.push(`auto_commit=${config.git.auto_commit}`);
144
+ if (configParts.length > 0) parts.push(`Config: ${configParts.join(', ')}`);
145
+ }
146
+
147
+ if (parts.length === 0) return '';
148
+ return '[Plan-Build-Run Project Context] ' + parts.join(' | ');
149
+ }
150
+
151
+ module.exports = { buildAgentContext };
152
+ if (require.main === module) { main(); }
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PostToolUseFailure hook: Logs tool failures to events.jsonl
5
+ * and provides recovery hints via additionalContext.
6
+ *
7
+ * Fires when any tool execution fails. Captures tool name, error,
8
+ * and session context for debugging.
9
+ *
10
+ * Exit codes:
11
+ * 0 = always (informational hook, never blocks)
12
+ */
13
+
14
+ const { logHook } = require('./hook-logger');
15
+ const { logEvent } = require('./event-logger');
16
+
17
+ function readStdin() {
18
+ return new Promise((resolve) => {
19
+ let input = '';
20
+ process.stdin.setEncoding('utf8');
21
+ process.stdin.on('data', (chunk) => { input += chunk; });
22
+ process.stdin.on('end', () => {
23
+ try {
24
+ resolve(JSON.parse(input));
25
+ } catch (_e) {
26
+ resolve({});
27
+ }
28
+ });
29
+ });
30
+ }
31
+
32
+ async function main() {
33
+ const data = await readStdin();
34
+
35
+ const toolName = data.tool_name || 'unknown';
36
+ const error = data.error || 'unknown error';
37
+ const isInterrupt = data.is_interrupt || false;
38
+ const toolInput = data.tool_input || {};
39
+
40
+ // Log to hooks.jsonl
41
+ logHook('log-tool-failure', 'PostToolUseFailure', 'logged', {
42
+ tool: toolName,
43
+ error: typeof error === 'string' ? error.substring(0, 200) : JSON.stringify(error).substring(0, 200),
44
+ interrupt: isInterrupt
45
+ });
46
+
47
+ // Log to events.jsonl with more detail
48
+ logEvent('tool', 'failure', {
49
+ tool: toolName,
50
+ error: typeof error === 'string' ? error.substring(0, 500) : JSON.stringify(error).substring(0, 500),
51
+ interrupt: isInterrupt,
52
+ input_summary: summarizeInput(toolName, toolInput)
53
+ });
54
+
55
+ // Provide recovery hints for Bash failures (most common actionable failure)
56
+ if (toolName === 'Bash' && !isInterrupt) {
57
+ const output = {
58
+ hookSpecificOutput: {
59
+ hookEventName: 'PostToolUseFailure',
60
+ additionalContext: 'Bash command failed. If this is a recurring issue, consider using /pbr:debug for systematic investigation.'
61
+ }
62
+ };
63
+ process.stdout.write(JSON.stringify(output));
64
+ }
65
+
66
+ process.exit(0);
67
+ }
68
+
69
+ function summarizeInput(toolName, toolInput) {
70
+ switch (toolName) {
71
+ case 'Bash':
72
+ return (toolInput.command || '').substring(0, 100);
73
+ case 'Write':
74
+ case 'Read':
75
+ case 'Edit':
76
+ return toolInput.file_path || '';
77
+ case 'Glob':
78
+ return toolInput.pattern || '';
79
+ case 'Grep':
80
+ return toolInput.pattern || '';
81
+ case 'Task':
82
+ return (toolInput.description || '').substring(0, 100);
83
+ default:
84
+ return '';
85
+ }
86
+ }
87
+
88
+ main();