@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,66 +1,66 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PostToolUse dispatcher for Write|Edit hooks.
|
|
5
|
-
*
|
|
6
|
-
* Consolidates check-plan-format.js and check-roadmap-sync.js
|
|
7
|
-
* into a single process, reading stdin once and routing to the
|
|
8
|
-
* appropriate check based on the file path. This halves the
|
|
9
|
-
* process spawns per Write/Edit call.
|
|
10
|
-
*
|
|
11
|
-
* Routing:
|
|
12
|
-
* - PLAN.md or SUMMARY*.md → plan format validation
|
|
13
|
-
* - STATE.md → roadmap sync check
|
|
14
|
-
* - SUMMARY*.md or VERIFICATION.md in .planning/phases/ → state sync (auto-update tracking files)
|
|
15
|
-
* - Other files → exit immediately (no work needed)
|
|
16
|
-
*
|
|
17
|
-
* Exit codes:
|
|
18
|
-
* 0 = always (PostToolUse hooks are advisory)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const { checkPlanWrite } = require('./check-plan-format');
|
|
22
|
-
const { checkSync } = require('./check-roadmap-sync');
|
|
23
|
-
const { checkStateSync } = require('./check-state-sync');
|
|
24
|
-
|
|
25
|
-
function main() {
|
|
26
|
-
let input = '';
|
|
27
|
-
|
|
28
|
-
process.stdin.setEncoding('utf8');
|
|
29
|
-
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
30
|
-
process.stdin.on('end', () => {
|
|
31
|
-
try {
|
|
32
|
-
const data = JSON.parse(input);
|
|
33
|
-
|
|
34
|
-
// Plan format check (PLAN.md, SUMMARY*.md)
|
|
35
|
-
// Note: SUMMARY files intentionally trigger BOTH this check AND the state-sync
|
|
36
|
-
// check below. The plan format check validates frontmatter structure, while
|
|
37
|
-
// state-sync auto-updates ROADMAP.md and STATE.md tracking fields.
|
|
38
|
-
const planResult = checkPlanWrite(data);
|
|
39
|
-
if (planResult) {
|
|
40
|
-
process.stdout.write(JSON.stringify(planResult.output));
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Roadmap sync check (STATE.md)
|
|
45
|
-
const syncResult = checkSync(data);
|
|
46
|
-
if (syncResult) {
|
|
47
|
-
process.stdout.write(JSON.stringify(syncResult.output));
|
|
48
|
-
process.exit(0);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// State sync check (SUMMARY/VERIFICATION → STATE.md + ROADMAP.md)
|
|
52
|
-
const stateSyncResult = checkStateSync(data);
|
|
53
|
-
if (stateSyncResult) {
|
|
54
|
-
process.stdout.write(JSON.stringify(stateSyncResult.output));
|
|
55
|
-
process.exit(0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
process.exit(0);
|
|
59
|
-
} catch (_e) {
|
|
60
|
-
// Don't block on parse errors
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (require.main === module) { main(); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse dispatcher for Write|Edit hooks.
|
|
5
|
+
*
|
|
6
|
+
* Consolidates check-plan-format.js and check-roadmap-sync.js
|
|
7
|
+
* into a single process, reading stdin once and routing to the
|
|
8
|
+
* appropriate check based on the file path. This halves the
|
|
9
|
+
* process spawns per Write/Edit call.
|
|
10
|
+
*
|
|
11
|
+
* Routing:
|
|
12
|
+
* - PLAN.md or SUMMARY*.md → plan format validation
|
|
13
|
+
* - STATE.md → roadmap sync check
|
|
14
|
+
* - SUMMARY*.md or VERIFICATION.md in .planning/phases/ → state sync (auto-update tracking files)
|
|
15
|
+
* - Other files → exit immediately (no work needed)
|
|
16
|
+
*
|
|
17
|
+
* Exit codes:
|
|
18
|
+
* 0 = always (PostToolUse hooks are advisory)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const { checkPlanWrite } = require('./check-plan-format');
|
|
22
|
+
const { checkSync } = require('./check-roadmap-sync');
|
|
23
|
+
const { checkStateSync } = require('./check-state-sync');
|
|
24
|
+
|
|
25
|
+
function main() {
|
|
26
|
+
let input = '';
|
|
27
|
+
|
|
28
|
+
process.stdin.setEncoding('utf8');
|
|
29
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
30
|
+
process.stdin.on('end', () => {
|
|
31
|
+
try {
|
|
32
|
+
const data = JSON.parse(input);
|
|
33
|
+
|
|
34
|
+
// Plan format check (PLAN.md, SUMMARY*.md)
|
|
35
|
+
// Note: SUMMARY files intentionally trigger BOTH this check AND the state-sync
|
|
36
|
+
// check below. The plan format check validates frontmatter structure, while
|
|
37
|
+
// state-sync auto-updates ROADMAP.md and STATE.md tracking fields.
|
|
38
|
+
const planResult = checkPlanWrite(data);
|
|
39
|
+
if (planResult) {
|
|
40
|
+
process.stdout.write(JSON.stringify(planResult.output));
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Roadmap sync check (STATE.md)
|
|
45
|
+
const syncResult = checkSync(data);
|
|
46
|
+
if (syncResult) {
|
|
47
|
+
process.stdout.write(JSON.stringify(syncResult.output));
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// State sync check (SUMMARY/VERIFICATION → STATE.md + ROADMAP.md)
|
|
52
|
+
const stateSyncResult = checkStateSync(data);
|
|
53
|
+
if (stateSyncResult) {
|
|
54
|
+
process.stdout.write(JSON.stringify(stateSyncResult.output));
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
process.exit(0);
|
|
59
|
+
} catch (_e) {
|
|
60
|
+
// Don't block on parse errors
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (require.main === module) { main(); }
|
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PostToolUse quality check hook for Write|Edit.
|
|
5
|
-
*
|
|
6
|
-
* Runs opt-in code quality checks on modified JS/TS files:
|
|
7
|
-
* 1. autoFormat: Run Prettier on the file (requires local installation)
|
|
8
|
-
* 2. typeCheck: Run tsc --noEmit filtered to the edited file (requires local typescript)
|
|
9
|
-
* 3. detectConsoleLogs: Warn about console.log statements left in the file
|
|
10
|
-
*
|
|
11
|
-
* All checks disabled by default. Enable via .planning/config.json:
|
|
12
|
-
* { "hooks": { "autoFormat": true, "typeCheck": true, "detectConsoleLogs": true } }
|
|
13
|
-
*
|
|
14
|
-
* Only processes JS/TS files (.js, .jsx, .ts, .tsx, .mjs, .cjs).
|
|
15
|
-
* Silently skips if tools (prettier, tsc) are not installed in the project.
|
|
16
|
-
*
|
|
17
|
-
* Exit codes:
|
|
18
|
-
* 0 = always (PostToolUse hook, advisory only)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const { execSync } = require('child_process');
|
|
24
|
-
const { logHook } = require('./hook-logger');
|
|
25
|
-
|
|
26
|
-
const JS_TS_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']);
|
|
27
|
-
const TS_EXTENSIONS = new Set(['.ts', '.tsx']);
|
|
28
|
-
|
|
29
|
-
function main() {
|
|
30
|
-
let input = '';
|
|
31
|
-
|
|
32
|
-
process.stdin.setEncoding('utf8');
|
|
33
|
-
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
34
|
-
process.stdin.on('end', () => {
|
|
35
|
-
try {
|
|
36
|
-
const data = JSON.parse(input);
|
|
37
|
-
const result = checkQuality(data);
|
|
38
|
-
if (result) {
|
|
39
|
-
process.stdout.write(JSON.stringify(result.output));
|
|
40
|
-
}
|
|
41
|
-
process.exit(0);
|
|
42
|
-
} catch (_e) {
|
|
43
|
-
process.exit(0);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Core quality check logic for use by dispatchers or standalone.
|
|
50
|
-
* @param {Object} data - Parsed hook input (tool_input, etc.)
|
|
51
|
-
* @returns {null|{output: Object}} null if no checks fired, result otherwise
|
|
52
|
-
*/
|
|
53
|
-
function checkQuality(data) {
|
|
54
|
-
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
55
|
-
if (!filePath) return null;
|
|
56
|
-
|
|
57
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
58
|
-
if (!JS_TS_EXTENSIONS.has(ext)) return null;
|
|
59
|
-
|
|
60
|
-
const cwd = process.cwd();
|
|
61
|
-
const config = loadHooksConfig(cwd);
|
|
62
|
-
|
|
63
|
-
// No quality hooks enabled — early exit
|
|
64
|
-
if (!config.autoFormat && !config.typeCheck && !config.detectConsoleLogs) return null;
|
|
65
|
-
|
|
66
|
-
const messages = [];
|
|
67
|
-
|
|
68
|
-
// 1. Auto-format with Prettier
|
|
69
|
-
if (config.autoFormat) {
|
|
70
|
-
const msg = runPrettier(filePath, cwd);
|
|
71
|
-
if (msg) messages.push(msg);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 2. TypeScript type-check (only for .ts/.tsx)
|
|
75
|
-
if (config.typeCheck && TS_EXTENSIONS.has(ext)) {
|
|
76
|
-
const msg = runTypeCheck(filePath, cwd);
|
|
77
|
-
if (msg) messages.push(msg);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 3. Console.log detection
|
|
81
|
-
if (config.detectConsoleLogs) {
|
|
82
|
-
const msg = detectConsoleLogs(filePath);
|
|
83
|
-
if (msg) messages.push(msg);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (messages.length === 0) return null;
|
|
87
|
-
|
|
88
|
-
logHook('post-write-quality', 'PostToolUse', 'quality-check', {
|
|
89
|
-
file: path.basename(filePath),
|
|
90
|
-
checks: messages.length
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
output: {
|
|
95
|
-
additionalContext: messages.join('\n')
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Load the hooks section from .planning/config.json.
|
|
102
|
-
* Returns {} if not found or not configured.
|
|
103
|
-
*/
|
|
104
|
-
function loadHooksConfig(cwd) {
|
|
105
|
-
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
106
|
-
if (!fs.existsSync(configPath)) return {};
|
|
107
|
-
try {
|
|
108
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
109
|
-
return config.hooks || {};
|
|
110
|
-
} catch (_e) {
|
|
111
|
-
return {};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Find a locally installed binary in node_modules/.bin/.
|
|
117
|
-
* Returns the full path or null if not found.
|
|
118
|
-
*/
|
|
119
|
-
function findLocalBin(cwd, name) {
|
|
120
|
-
const candidates = process.platform === 'win32'
|
|
121
|
-
? [path.join(cwd, 'node_modules', '.bin', name + '.cmd'),
|
|
122
|
-
path.join(cwd, 'node_modules', '.bin', name)]
|
|
123
|
-
: [path.join(cwd, 'node_modules', '.bin', name)];
|
|
124
|
-
return candidates.find(c => fs.existsSync(c)) || null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Run Prettier on the file. Returns a message string or null.
|
|
129
|
-
*/
|
|
130
|
-
function runPrettier(filePath, cwd) {
|
|
131
|
-
const bin = findLocalBin(cwd, 'prettier');
|
|
132
|
-
if (!bin) return null;
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
execSync(`"${bin}" --write "${filePath}"`, {
|
|
136
|
-
cwd,
|
|
137
|
-
timeout: 15000,
|
|
138
|
-
stdio: 'pipe',
|
|
139
|
-
});
|
|
140
|
-
return `[Auto-format] Prettier reformatted ${path.basename(filePath)}. File on disk may differ from context — re-read before further edits.`;
|
|
141
|
-
} catch (_e) {
|
|
142
|
-
// Prettier failed (syntax error, unsupported file, etc.) — skip
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Run tsc --noEmit and filter to errors in the modified file.
|
|
149
|
-
* Returns a message string or null.
|
|
150
|
-
*/
|
|
151
|
-
function runTypeCheck(filePath, cwd) {
|
|
152
|
-
const bin = findLocalBin(cwd, 'tsc');
|
|
153
|
-
if (!bin) return null;
|
|
154
|
-
if (!fs.existsSync(path.join(cwd, 'tsconfig.json'))) return null;
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
execSync(`"${bin}" --noEmit`, {
|
|
158
|
-
cwd,
|
|
159
|
-
timeout: 30000,
|
|
160
|
-
stdio: 'pipe',
|
|
161
|
-
encoding: 'utf8',
|
|
162
|
-
});
|
|
163
|
-
return null; // Clean pass — no errors
|
|
164
|
-
} catch (e) {
|
|
165
|
-
const output = (e.stdout || '').toString();
|
|
166
|
-
const basename = path.basename(filePath);
|
|
167
|
-
const relevantLines = output.split('\n')
|
|
168
|
-
.filter(line => line.includes(basename) && /error TS\d+/.test(line));
|
|
169
|
-
|
|
170
|
-
if (relevantLines.length === 0) return null;
|
|
171
|
-
const detail = relevantLines.slice(0, 3).join('\n ');
|
|
172
|
-
const extra = relevantLines.length > 3 ? `\n ...and ${relevantLines.length - 3} more` : '';
|
|
173
|
-
return `[Type Check] ${relevantLines.length} error(s) in ${basename}:\n ${detail}${extra}`;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Scan file for console.log statements. Returns a message string or null.
|
|
179
|
-
*/
|
|
180
|
-
function detectConsoleLogs(filePath) {
|
|
181
|
-
if (!fs.existsSync(filePath)) return null;
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
185
|
-
const lines = content.split('\n');
|
|
186
|
-
const matches = [];
|
|
187
|
-
|
|
188
|
-
for (let i = 0; i < lines.length; i++) {
|
|
189
|
-
const line = lines[i];
|
|
190
|
-
if (/^\s*\/\//.test(line)) continue; // skip single-line comments
|
|
191
|
-
if (/\bconsole\.log\s*\(/.test(line)) {
|
|
192
|
-
matches.push({ line: i + 1, text: line.trim().substring(0, 80) });
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (matches.length === 0) return null;
|
|
197
|
-
|
|
198
|
-
const detail = matches.slice(0, 3).map(m => ` L${m.line}: ${m.text}`).join('\n');
|
|
199
|
-
const extra = matches.length > 3 ? `\n ...and ${matches.length - 3} more` : '';
|
|
200
|
-
return `[Console.log] ${matches.length} console.log(s) in ${path.basename(filePath)}:\n${detail}${extra}`;
|
|
201
|
-
} catch (_e) {
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
module.exports = { checkQuality, loadHooksConfig, findLocalBin, runPrettier, runTypeCheck, detectConsoleLogs };
|
|
207
|
-
if (require.main === module) { main(); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse quality check hook for Write|Edit.
|
|
5
|
+
*
|
|
6
|
+
* Runs opt-in code quality checks on modified JS/TS files:
|
|
7
|
+
* 1. autoFormat: Run Prettier on the file (requires local installation)
|
|
8
|
+
* 2. typeCheck: Run tsc --noEmit filtered to the edited file (requires local typescript)
|
|
9
|
+
* 3. detectConsoleLogs: Warn about console.log statements left in the file
|
|
10
|
+
*
|
|
11
|
+
* All checks disabled by default. Enable via .planning/config.json:
|
|
12
|
+
* { "hooks": { "autoFormat": true, "typeCheck": true, "detectConsoleLogs": true } }
|
|
13
|
+
*
|
|
14
|
+
* Only processes JS/TS files (.js, .jsx, .ts, .tsx, .mjs, .cjs).
|
|
15
|
+
* Silently skips if tools (prettier, tsc) are not installed in the project.
|
|
16
|
+
*
|
|
17
|
+
* Exit codes:
|
|
18
|
+
* 0 = always (PostToolUse hook, advisory only)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { execSync } = require('child_process');
|
|
24
|
+
const { logHook } = require('./hook-logger');
|
|
25
|
+
|
|
26
|
+
const JS_TS_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']);
|
|
27
|
+
const TS_EXTENSIONS = new Set(['.ts', '.tsx']);
|
|
28
|
+
|
|
29
|
+
function main() {
|
|
30
|
+
let input = '';
|
|
31
|
+
|
|
32
|
+
process.stdin.setEncoding('utf8');
|
|
33
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
34
|
+
process.stdin.on('end', () => {
|
|
35
|
+
try {
|
|
36
|
+
const data = JSON.parse(input);
|
|
37
|
+
const result = checkQuality(data);
|
|
38
|
+
if (result) {
|
|
39
|
+
process.stdout.write(JSON.stringify(result.output));
|
|
40
|
+
}
|
|
41
|
+
process.exit(0);
|
|
42
|
+
} catch (_e) {
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Core quality check logic for use by dispatchers or standalone.
|
|
50
|
+
* @param {Object} data - Parsed hook input (tool_input, etc.)
|
|
51
|
+
* @returns {null|{output: Object}} null if no checks fired, result otherwise
|
|
52
|
+
*/
|
|
53
|
+
function checkQuality(data) {
|
|
54
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
55
|
+
if (!filePath) return null;
|
|
56
|
+
|
|
57
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
58
|
+
if (!JS_TS_EXTENSIONS.has(ext)) return null;
|
|
59
|
+
|
|
60
|
+
const cwd = process.cwd();
|
|
61
|
+
const config = loadHooksConfig(cwd);
|
|
62
|
+
|
|
63
|
+
// No quality hooks enabled — early exit
|
|
64
|
+
if (!config.autoFormat && !config.typeCheck && !config.detectConsoleLogs) return null;
|
|
65
|
+
|
|
66
|
+
const messages = [];
|
|
67
|
+
|
|
68
|
+
// 1. Auto-format with Prettier
|
|
69
|
+
if (config.autoFormat) {
|
|
70
|
+
const msg = runPrettier(filePath, cwd);
|
|
71
|
+
if (msg) messages.push(msg);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. TypeScript type-check (only for .ts/.tsx)
|
|
75
|
+
if (config.typeCheck && TS_EXTENSIONS.has(ext)) {
|
|
76
|
+
const msg = runTypeCheck(filePath, cwd);
|
|
77
|
+
if (msg) messages.push(msg);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. Console.log detection
|
|
81
|
+
if (config.detectConsoleLogs) {
|
|
82
|
+
const msg = detectConsoleLogs(filePath);
|
|
83
|
+
if (msg) messages.push(msg);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (messages.length === 0) return null;
|
|
87
|
+
|
|
88
|
+
logHook('post-write-quality', 'PostToolUse', 'quality-check', {
|
|
89
|
+
file: path.basename(filePath),
|
|
90
|
+
checks: messages.length
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
output: {
|
|
95
|
+
additionalContext: messages.join('\n')
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Load the hooks section from .planning/config.json.
|
|
102
|
+
* Returns {} if not found or not configured.
|
|
103
|
+
*/
|
|
104
|
+
function loadHooksConfig(cwd) {
|
|
105
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
106
|
+
if (!fs.existsSync(configPath)) return {};
|
|
107
|
+
try {
|
|
108
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
109
|
+
return config.hooks || {};
|
|
110
|
+
} catch (_e) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Find a locally installed binary in node_modules/.bin/.
|
|
117
|
+
* Returns the full path or null if not found.
|
|
118
|
+
*/
|
|
119
|
+
function findLocalBin(cwd, name) {
|
|
120
|
+
const candidates = process.platform === 'win32'
|
|
121
|
+
? [path.join(cwd, 'node_modules', '.bin', name + '.cmd'),
|
|
122
|
+
path.join(cwd, 'node_modules', '.bin', name)]
|
|
123
|
+
: [path.join(cwd, 'node_modules', '.bin', name)];
|
|
124
|
+
return candidates.find(c => fs.existsSync(c)) || null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Run Prettier on the file. Returns a message string or null.
|
|
129
|
+
*/
|
|
130
|
+
function runPrettier(filePath, cwd) {
|
|
131
|
+
const bin = findLocalBin(cwd, 'prettier');
|
|
132
|
+
if (!bin) return null;
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
execSync(`"${bin}" --write "${filePath}"`, {
|
|
136
|
+
cwd,
|
|
137
|
+
timeout: 15000,
|
|
138
|
+
stdio: 'pipe',
|
|
139
|
+
});
|
|
140
|
+
return `[Auto-format] Prettier reformatted ${path.basename(filePath)}. File on disk may differ from context — re-read before further edits.`;
|
|
141
|
+
} catch (_e) {
|
|
142
|
+
// Prettier failed (syntax error, unsupported file, etc.) — skip
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Run tsc --noEmit and filter to errors in the modified file.
|
|
149
|
+
* Returns a message string or null.
|
|
150
|
+
*/
|
|
151
|
+
function runTypeCheck(filePath, cwd) {
|
|
152
|
+
const bin = findLocalBin(cwd, 'tsc');
|
|
153
|
+
if (!bin) return null;
|
|
154
|
+
if (!fs.existsSync(path.join(cwd, 'tsconfig.json'))) return null;
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
execSync(`"${bin}" --noEmit`, {
|
|
158
|
+
cwd,
|
|
159
|
+
timeout: 30000,
|
|
160
|
+
stdio: 'pipe',
|
|
161
|
+
encoding: 'utf8',
|
|
162
|
+
});
|
|
163
|
+
return null; // Clean pass — no errors
|
|
164
|
+
} catch (e) {
|
|
165
|
+
const output = (e.stdout || '').toString();
|
|
166
|
+
const basename = path.basename(filePath);
|
|
167
|
+
const relevantLines = output.split('\n')
|
|
168
|
+
.filter(line => line.includes(basename) && /error TS\d+/.test(line));
|
|
169
|
+
|
|
170
|
+
if (relevantLines.length === 0) return null;
|
|
171
|
+
const detail = relevantLines.slice(0, 3).join('\n ');
|
|
172
|
+
const extra = relevantLines.length > 3 ? `\n ...and ${relevantLines.length - 3} more` : '';
|
|
173
|
+
return `[Type Check] ${relevantLines.length} error(s) in ${basename}:\n ${detail}${extra}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scan file for console.log statements. Returns a message string or null.
|
|
179
|
+
*/
|
|
180
|
+
function detectConsoleLogs(filePath) {
|
|
181
|
+
if (!fs.existsSync(filePath)) return null;
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
185
|
+
const lines = content.split('\n');
|
|
186
|
+
const matches = [];
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < lines.length; i++) {
|
|
189
|
+
const line = lines[i];
|
|
190
|
+
if (/^\s*\/\//.test(line)) continue; // skip single-line comments
|
|
191
|
+
if (/\bconsole\.log\s*\(/.test(line)) {
|
|
192
|
+
matches.push({ line: i + 1, text: line.trim().substring(0, 80) });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (matches.length === 0) return null;
|
|
197
|
+
|
|
198
|
+
const detail = matches.slice(0, 3).map(m => ` L${m.line}: ${m.text}`).join('\n');
|
|
199
|
+
const extra = matches.length > 3 ? `\n ...and ${matches.length - 3} more` : '';
|
|
200
|
+
return `[Console.log] ${matches.length} console.log(s) in ${path.basename(filePath)}:\n${detail}${extra}`;
|
|
201
|
+
} catch (_e) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = { checkQuality, loadHooksConfig, findLocalBin, runPrettier, runTypeCheck, detectConsoleLogs };
|
|
207
|
+
if (require.main === module) { main(); }
|