@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,80 +1,80 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Stop hook: Auto-continue via signal files.
|
|
5
|
-
*
|
|
6
|
-
* When enabled (features.auto_continue: true in config.json),
|
|
7
|
-
* reads .planning/.auto-next signal file on session stop.
|
|
8
|
-
* If present, reads the next command and injects it.
|
|
9
|
-
* Signal file is ONE-SHOT: read and delete to prevent infinite loops.
|
|
10
|
-
*
|
|
11
|
-
* Hard stops (signal file NOT written):
|
|
12
|
-
* - Milestone completion
|
|
13
|
-
* - human_needed flag set
|
|
14
|
-
* - Execution errors
|
|
15
|
-
* - Gap closure attempted 3+ times
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
const { logHook } = require('./hook-logger');
|
|
21
|
-
const { configLoad } = require('./pbr-tools');
|
|
22
|
-
|
|
23
|
-
function main() {
|
|
24
|
-
try {
|
|
25
|
-
const cwd = process.cwd();
|
|
26
|
-
const planningDir = path.join(cwd, '.planning');
|
|
27
|
-
const signalPath = path.join(planningDir, '.auto-next');
|
|
28
|
-
|
|
29
|
-
// Check if auto-continue is enabled
|
|
30
|
-
const config = configLoad(planningDir);
|
|
31
|
-
if (!config || !config.features || !config.features.auto_continue) {
|
|
32
|
-
process.exit(0);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Check for signal file
|
|
36
|
-
if (!fs.existsSync(signalPath)) {
|
|
37
|
-
logHook('auto-continue', 'Stop', 'no-signal', {});
|
|
38
|
-
process.exit(0);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Read and DELETE the signal file (one-shot)
|
|
42
|
-
const nextCommand = fs.readFileSync(signalPath, 'utf8').trim();
|
|
43
|
-
// Retry unlink with exponential backoff for Windows file locking (antivirus/indexer)
|
|
44
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
45
|
-
try {
|
|
46
|
-
fs.unlinkSync(signalPath);
|
|
47
|
-
break;
|
|
48
|
-
} catch (unlinkErr) {
|
|
49
|
-
if (attempt === 2) {
|
|
50
|
-
logHook('auto-continue', 'Stop', 'unlink-failed', { error: unlinkErr.message });
|
|
51
|
-
} else {
|
|
52
|
-
// Exponential backoff: 100ms, 200ms
|
|
53
|
-
const delay = 100 * Math.pow(2, attempt);
|
|
54
|
-
const start = Date.now();
|
|
55
|
-
while (Date.now() - start < delay) { /* busy-wait */ }
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!nextCommand) {
|
|
61
|
-
logHook('auto-continue', 'Stop', 'empty-signal', {});
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
logHook('auto-continue', 'Stop', 'continue', { next: nextCommand });
|
|
66
|
-
|
|
67
|
-
// Output the next command for Claude Code to execute
|
|
68
|
-
const output = {
|
|
69
|
-
message: `Auto-continuing with: ${nextCommand}`,
|
|
70
|
-
command: nextCommand
|
|
71
|
-
};
|
|
72
|
-
process.stdout.write(JSON.stringify(output));
|
|
73
|
-
process.exit(0);
|
|
74
|
-
} catch (_e) {
|
|
75
|
-
// Don't block on errors
|
|
76
|
-
process.exit(0);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stop hook: Auto-continue via signal files.
|
|
5
|
+
*
|
|
6
|
+
* When enabled (features.auto_continue: true in config.json),
|
|
7
|
+
* reads .planning/.auto-next signal file on session stop.
|
|
8
|
+
* If present, reads the next command and injects it.
|
|
9
|
+
* Signal file is ONE-SHOT: read and delete to prevent infinite loops.
|
|
10
|
+
*
|
|
11
|
+
* Hard stops (signal file NOT written):
|
|
12
|
+
* - Milestone completion
|
|
13
|
+
* - human_needed flag set
|
|
14
|
+
* - Execution errors
|
|
15
|
+
* - Gap closure attempted 3+ times
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { logHook } = require('./hook-logger');
|
|
21
|
+
const { configLoad } = require('./pbr-tools');
|
|
22
|
+
|
|
23
|
+
function main() {
|
|
24
|
+
try {
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
const planningDir = path.join(cwd, '.planning');
|
|
27
|
+
const signalPath = path.join(planningDir, '.auto-next');
|
|
28
|
+
|
|
29
|
+
// Check if auto-continue is enabled
|
|
30
|
+
const config = configLoad(planningDir);
|
|
31
|
+
if (!config || !config.features || !config.features.auto_continue) {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check for signal file
|
|
36
|
+
if (!fs.existsSync(signalPath)) {
|
|
37
|
+
logHook('auto-continue', 'Stop', 'no-signal', {});
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Read and DELETE the signal file (one-shot)
|
|
42
|
+
const nextCommand = fs.readFileSync(signalPath, 'utf8').trim();
|
|
43
|
+
// Retry unlink with exponential backoff for Windows file locking (antivirus/indexer)
|
|
44
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
45
|
+
try {
|
|
46
|
+
fs.unlinkSync(signalPath);
|
|
47
|
+
break;
|
|
48
|
+
} catch (unlinkErr) {
|
|
49
|
+
if (attempt === 2) {
|
|
50
|
+
logHook('auto-continue', 'Stop', 'unlink-failed', { error: unlinkErr.message });
|
|
51
|
+
} else {
|
|
52
|
+
// Exponential backoff: 100ms, 200ms
|
|
53
|
+
const delay = 100 * Math.pow(2, attempt);
|
|
54
|
+
const start = Date.now();
|
|
55
|
+
while (Date.now() - start < delay) { /* busy-wait */ }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!nextCommand) {
|
|
61
|
+
logHook('auto-continue', 'Stop', 'empty-signal', {});
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logHook('auto-continue', 'Stop', 'continue', { next: nextCommand });
|
|
66
|
+
|
|
67
|
+
// Output the next command for Claude Code to execute
|
|
68
|
+
const output = {
|
|
69
|
+
message: `Auto-continuing with: ${nextCommand}`,
|
|
70
|
+
command: nextCommand
|
|
71
|
+
};
|
|
72
|
+
process.stdout.write(JSON.stringify(output));
|
|
73
|
+
process.exit(0);
|
|
74
|
+
} catch (_e) {
|
|
75
|
+
// Don't block on errors
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main();
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PreToolUse hook: Blocks dangerous Bash commands that could destroy
|
|
5
|
-
* planning state or repository integrity.
|
|
6
|
-
*
|
|
7
|
-
* Patterns blocked (exit 2):
|
|
8
|
-
* - rm -rf .planning (or any rm that targets .planning/)
|
|
9
|
-
* - git reset --hard
|
|
10
|
-
* - git push --force / -f to main/master
|
|
11
|
-
* - git clean -fd / -fxd (removes untracked files including .planning/)
|
|
12
|
-
*
|
|
13
|
-
* Patterns warned (exit 0, additionalContext):
|
|
14
|
-
* - Large rm operations (rm -rf on project directories)
|
|
15
|
-
* - git checkout -- . (discards all unstaged changes)
|
|
16
|
-
*
|
|
17
|
-
* Exit codes:
|
|
18
|
-
* 0 = not a dangerous command, or a warning-only match
|
|
19
|
-
* 2 = blocked (destructive command detected)
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
const { logHook } = require('./hook-logger');
|
|
23
|
-
|
|
24
|
-
// Commands that are outright blocked
|
|
25
|
-
const BLOCK_PATTERNS = [
|
|
26
|
-
{
|
|
27
|
-
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s+.*\.planning\b/,
|
|
28
|
-
reason: 'rm -rf targeting .planning/ directory — this would destroy all project state.'
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s+.*\.planning[/\\]/,
|
|
32
|
-
reason: 'rm -rf targeting files inside .planning/ — this would destroy project state.'
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
pattern: /\bgit\s+reset\s+--hard\b/,
|
|
36
|
-
reason: 'git reset --hard discards all uncommitted changes. Use git stash or git checkout for specific files instead.'
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
pattern: /\bgit\s+push\s+.*(-f|--force)\b.*\b(main|master)\b/,
|
|
40
|
-
reason: 'Force-pushing to main/master can destroy shared history. This is almost never what you want.'
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
pattern: /\bgit\s+push\s+.*\b(main|master)\b.*(-f|--force)\b/,
|
|
44
|
-
reason: 'Force-pushing to main/master can destroy shared history. This is almost never what you want.'
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
pattern: /\bgit\s+clean\s+(-[a-zA-Z]*f[a-zA-Z]*d|-[a-zA-Z]*d[a-zA-Z]*f)\b/,
|
|
48
|
-
reason: 'git clean -fd removes untracked files including .planning/ contents. Use specific file paths instead.'
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
// Commands that produce warnings but are not blocked
|
|
53
|
-
const WARN_PATTERNS = [
|
|
54
|
-
{
|
|
55
|
-
pattern: /\bgit\s+checkout\s+--\s+\.\s*$/,
|
|
56
|
-
message: 'git checkout -- . discards ALL unstaged changes. Consider targeting specific files.'
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
pattern: /\bgit\s+push\s+.*(-f|--force)\b/,
|
|
60
|
-
message: 'Force-pushing can overwrite remote history. Ensure this is intentional.'
|
|
61
|
-
}
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Check a parsed hook data object for dangerous commands.
|
|
66
|
-
* Returns { output, exitCode } if the command should be blocked/warned, or null if allowed.
|
|
67
|
-
* Used by pre-bash-dispatch.js for consolidated hook execution.
|
|
68
|
-
*/
|
|
69
|
-
function checkDangerous(data) {
|
|
70
|
-
const command = data.tool_input?.command || '';
|
|
71
|
-
|
|
72
|
-
// Skip empty commands
|
|
73
|
-
if (!command.trim()) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Check block patterns
|
|
78
|
-
for (const { pattern, reason } of BLOCK_PATTERNS) {
|
|
79
|
-
if (pattern.test(command)) {
|
|
80
|
-
logHook('check-dangerous-commands', 'PreToolUse', 'block', {
|
|
81
|
-
command: command.substring(0, 200),
|
|
82
|
-
reason
|
|
83
|
-
});
|
|
84
|
-
return {
|
|
85
|
-
output: {
|
|
86
|
-
decision: 'block',
|
|
87
|
-
reason: `Dangerous command blocked.\n\n${reason}\n\nCommand: ${command.substring(0, 150)}`
|
|
88
|
-
},
|
|
89
|
-
exitCode: 2
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Check warn patterns
|
|
95
|
-
for (const { pattern, message } of WARN_PATTERNS) {
|
|
96
|
-
if (pattern.test(command)) {
|
|
97
|
-
logHook('check-dangerous-commands', 'PreToolUse', 'warn', {
|
|
98
|
-
command: command.substring(0, 200),
|
|
99
|
-
warning: message
|
|
100
|
-
});
|
|
101
|
-
return {
|
|
102
|
-
output: {
|
|
103
|
-
additionalContext: `Warning: ${message}`
|
|
104
|
-
},
|
|
105
|
-
exitCode: 0
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// No match — allow
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function main() {
|
|
115
|
-
let input = '';
|
|
116
|
-
|
|
117
|
-
process.stdin.setEncoding('utf8');
|
|
118
|
-
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
119
|
-
process.stdin.on('end', () => {
|
|
120
|
-
try {
|
|
121
|
-
const data = JSON.parse(input);
|
|
122
|
-
const result = checkDangerous(data);
|
|
123
|
-
if (result) {
|
|
124
|
-
process.stdout.write(JSON.stringify(result.output));
|
|
125
|
-
process.exit(result.exitCode);
|
|
126
|
-
}
|
|
127
|
-
process.exit(0);
|
|
128
|
-
} catch (_e) {
|
|
129
|
-
// Parse error — don't block
|
|
130
|
-
process.exit(0);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
module.exports = { BLOCK_PATTERNS, WARN_PATTERNS, checkDangerous };
|
|
136
|
-
if (require.main === module) { main(); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse hook: Blocks dangerous Bash commands that could destroy
|
|
5
|
+
* planning state or repository integrity.
|
|
6
|
+
*
|
|
7
|
+
* Patterns blocked (exit 2):
|
|
8
|
+
* - rm -rf .planning (or any rm that targets .planning/)
|
|
9
|
+
* - git reset --hard
|
|
10
|
+
* - git push --force / -f to main/master
|
|
11
|
+
* - git clean -fd / -fxd (removes untracked files including .planning/)
|
|
12
|
+
*
|
|
13
|
+
* Patterns warned (exit 0, additionalContext):
|
|
14
|
+
* - Large rm operations (rm -rf on project directories)
|
|
15
|
+
* - git checkout -- . (discards all unstaged changes)
|
|
16
|
+
*
|
|
17
|
+
* Exit codes:
|
|
18
|
+
* 0 = not a dangerous command, or a warning-only match
|
|
19
|
+
* 2 = blocked (destructive command detected)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { logHook } = require('./hook-logger');
|
|
23
|
+
|
|
24
|
+
// Commands that are outright blocked
|
|
25
|
+
const BLOCK_PATTERNS = [
|
|
26
|
+
{
|
|
27
|
+
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s+.*\.planning\b/,
|
|
28
|
+
reason: 'rm -rf targeting .planning/ directory — this would destroy all project state.'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s+.*\.planning[/\\]/,
|
|
32
|
+
reason: 'rm -rf targeting files inside .planning/ — this would destroy project state.'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /\bgit\s+reset\s+--hard\b/,
|
|
36
|
+
reason: 'git reset --hard discards all uncommitted changes. Use git stash or git checkout for specific files instead.'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /\bgit\s+push\s+.*(-f|--force)\b.*\b(main|master)\b/,
|
|
40
|
+
reason: 'Force-pushing to main/master can destroy shared history. This is almost never what you want.'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pattern: /\bgit\s+push\s+.*\b(main|master)\b.*(-f|--force)\b/,
|
|
44
|
+
reason: 'Force-pushing to main/master can destroy shared history. This is almost never what you want.'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /\bgit\s+clean\s+(-[a-zA-Z]*f[a-zA-Z]*d|-[a-zA-Z]*d[a-zA-Z]*f)\b/,
|
|
48
|
+
reason: 'git clean -fd removes untracked files including .planning/ contents. Use specific file paths instead.'
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Commands that produce warnings but are not blocked
|
|
53
|
+
const WARN_PATTERNS = [
|
|
54
|
+
{
|
|
55
|
+
pattern: /\bgit\s+checkout\s+--\s+\.\s*$/,
|
|
56
|
+
message: 'git checkout -- . discards ALL unstaged changes. Consider targeting specific files.'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
pattern: /\bgit\s+push\s+.*(-f|--force)\b/,
|
|
60
|
+
message: 'Force-pushing can overwrite remote history. Ensure this is intentional.'
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check a parsed hook data object for dangerous commands.
|
|
66
|
+
* Returns { output, exitCode } if the command should be blocked/warned, or null if allowed.
|
|
67
|
+
* Used by pre-bash-dispatch.js for consolidated hook execution.
|
|
68
|
+
*/
|
|
69
|
+
function checkDangerous(data) {
|
|
70
|
+
const command = data.tool_input?.command || '';
|
|
71
|
+
|
|
72
|
+
// Skip empty commands
|
|
73
|
+
if (!command.trim()) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check block patterns
|
|
78
|
+
for (const { pattern, reason } of BLOCK_PATTERNS) {
|
|
79
|
+
if (pattern.test(command)) {
|
|
80
|
+
logHook('check-dangerous-commands', 'PreToolUse', 'block', {
|
|
81
|
+
command: command.substring(0, 200),
|
|
82
|
+
reason
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
output: {
|
|
86
|
+
decision: 'block',
|
|
87
|
+
reason: `Dangerous command blocked.\n\n${reason}\n\nCommand: ${command.substring(0, 150)}`
|
|
88
|
+
},
|
|
89
|
+
exitCode: 2
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check warn patterns
|
|
95
|
+
for (const { pattern, message } of WARN_PATTERNS) {
|
|
96
|
+
if (pattern.test(command)) {
|
|
97
|
+
logHook('check-dangerous-commands', 'PreToolUse', 'warn', {
|
|
98
|
+
command: command.substring(0, 200),
|
|
99
|
+
warning: message
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
output: {
|
|
103
|
+
additionalContext: `Warning: ${message}`
|
|
104
|
+
},
|
|
105
|
+
exitCode: 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// No match — allow
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main() {
|
|
115
|
+
let input = '';
|
|
116
|
+
|
|
117
|
+
process.stdin.setEncoding('utf8');
|
|
118
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
119
|
+
process.stdin.on('end', () => {
|
|
120
|
+
try {
|
|
121
|
+
const data = JSON.parse(input);
|
|
122
|
+
const result = checkDangerous(data);
|
|
123
|
+
if (result) {
|
|
124
|
+
process.stdout.write(JSON.stringify(result.output));
|
|
125
|
+
process.exit(result.exitCode);
|
|
126
|
+
}
|
|
127
|
+
process.exit(0);
|
|
128
|
+
} catch (_e) {
|
|
129
|
+
// Parse error — don't block
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { BLOCK_PATTERNS, WARN_PATTERNS, checkDangerous };
|
|
136
|
+
if (require.main === module) { main(); }
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PreToolUse check: Blocks creation of new .md/.txt documentation files
|
|
5
|
-
* outside a known allowlist, preventing doc sprawl during builds.
|
|
6
|
-
*
|
|
7
|
-
* Opt-in via .planning/config.json: { "hooks": { "blockDocSprawl": true } }
|
|
8
|
-
*
|
|
9
|
-
* Allowlist:
|
|
10
|
-
* - README.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, LICENSE.md, LICENSE, LICENSE.txt
|
|
11
|
-
* - Any file under .planning/, .claude/, node_modules/, .git/
|
|
12
|
-
* - Any file that already exists (edits to existing docs are always allowed)
|
|
13
|
-
*
|
|
14
|
-
* Called by pre-write-dispatch.js — not wired directly in hooks.json.
|
|
15
|
-
*
|
|
16
|
-
* Exit codes (when standalone):
|
|
17
|
-
* 0 = pass (allowed)
|
|
18
|
-
* 2 = block (not on allowlist)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const { logHook } = require('./hook-logger');
|
|
24
|
-
|
|
25
|
-
const ALLOWED_DOC_BASENAMES = new Set([
|
|
26
|
-
'readme.md',
|
|
27
|
-
'claude.md',
|
|
28
|
-
'contributing.md',
|
|
29
|
-
'changelog.md',
|
|
30
|
-
'license.md',
|
|
31
|
-
'license',
|
|
32
|
-
'license.txt',
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
const ALLOWED_DIR_SEGMENTS = [
|
|
36
|
-
'.planning',
|
|
37
|
-
'.claude',
|
|
38
|
-
'node_modules',
|
|
39
|
-
'.git',
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
const DOC_EXTENSIONS = new Set(['.md', '.txt']);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Check if a Write operation would create a disallowed documentation file.
|
|
46
|
-
* @param {Object} data - Parsed hook input (tool_input, etc.)
|
|
47
|
-
* @param {string} [cwd] - Working directory override (defaults to process.cwd())
|
|
48
|
-
* @returns {null|{exitCode: number, output: Object}} null if allowed, block result otherwise
|
|
49
|
-
*/
|
|
50
|
-
function checkDocSprawl(data, cwd) {
|
|
51
|
-
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
52
|
-
if (!filePath) return null;
|
|
53
|
-
|
|
54
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
55
|
-
if (!DOC_EXTENSIONS.has(ext)) return null;
|
|
56
|
-
|
|
57
|
-
// Only block NEW file creation — existing docs can always be edited
|
|
58
|
-
if (fs.existsSync(filePath)) return null;
|
|
59
|
-
|
|
60
|
-
// Check config — disabled by default
|
|
61
|
-
const effectiveCwd = cwd || process.cwd();
|
|
62
|
-
if (!isBlockDocSprawlEnabled(effectiveCwd)) return null;
|
|
63
|
-
|
|
64
|
-
// Check basename allowlist
|
|
65
|
-
const basename = path.basename(filePath).toLowerCase();
|
|
66
|
-
if (ALLOWED_DOC_BASENAMES.has(basename)) return null;
|
|
67
|
-
|
|
68
|
-
// Check if file is in an allowed directory
|
|
69
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
70
|
-
for (const seg of ALLOWED_DIR_SEGMENTS) {
|
|
71
|
-
if (normalized.includes(`/${seg}/`)) return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
logHook('check-doc-sprawl', 'PreToolUse', 'block', {
|
|
75
|
-
file: path.basename(filePath),
|
|
76
|
-
ext
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
exitCode: 2,
|
|
81
|
-
output: {
|
|
82
|
-
decision: 'block',
|
|
83
|
-
reason: `[Doc Sprawl] Blocked creation of ${path.basename(filePath)}. ` +
|
|
84
|
-
'Only known docs (README.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, LICENSE.md) ' +
|
|
85
|
-
'and .planning/ files are allowed when blockDocSprawl is enabled. ' +
|
|
86
|
-
'Add content to an existing file instead, or disable hooks.blockDocSprawl in config.'
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function isBlockDocSprawlEnabled(cwd) {
|
|
92
|
-
try {
|
|
93
|
-
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
94
|
-
if (!fs.existsSync(configPath)) return false;
|
|
95
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
96
|
-
return !!config.hooks?.blockDocSprawl;
|
|
97
|
-
} catch (_e) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
module.exports = { checkDocSprawl, isBlockDocSprawlEnabled, ALLOWED_DOC_BASENAMES, ALLOWED_DIR_SEGMENTS, DOC_EXTENSIONS };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse check: Blocks creation of new .md/.txt documentation files
|
|
5
|
+
* outside a known allowlist, preventing doc sprawl during builds.
|
|
6
|
+
*
|
|
7
|
+
* Opt-in via .planning/config.json: { "hooks": { "blockDocSprawl": true } }
|
|
8
|
+
*
|
|
9
|
+
* Allowlist:
|
|
10
|
+
* - README.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, LICENSE.md, LICENSE, LICENSE.txt
|
|
11
|
+
* - Any file under .planning/, .claude/, node_modules/, .git/
|
|
12
|
+
* - Any file that already exists (edits to existing docs are always allowed)
|
|
13
|
+
*
|
|
14
|
+
* Called by pre-write-dispatch.js — not wired directly in hooks.json.
|
|
15
|
+
*
|
|
16
|
+
* Exit codes (when standalone):
|
|
17
|
+
* 0 = pass (allowed)
|
|
18
|
+
* 2 = block (not on allowlist)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { logHook } = require('./hook-logger');
|
|
24
|
+
|
|
25
|
+
const ALLOWED_DOC_BASENAMES = new Set([
|
|
26
|
+
'readme.md',
|
|
27
|
+
'claude.md',
|
|
28
|
+
'contributing.md',
|
|
29
|
+
'changelog.md',
|
|
30
|
+
'license.md',
|
|
31
|
+
'license',
|
|
32
|
+
'license.txt',
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const ALLOWED_DIR_SEGMENTS = [
|
|
36
|
+
'.planning',
|
|
37
|
+
'.claude',
|
|
38
|
+
'node_modules',
|
|
39
|
+
'.git',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const DOC_EXTENSIONS = new Set(['.md', '.txt']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a Write operation would create a disallowed documentation file.
|
|
46
|
+
* @param {Object} data - Parsed hook input (tool_input, etc.)
|
|
47
|
+
* @param {string} [cwd] - Working directory override (defaults to process.cwd())
|
|
48
|
+
* @returns {null|{exitCode: number, output: Object}} null if allowed, block result otherwise
|
|
49
|
+
*/
|
|
50
|
+
function checkDocSprawl(data, cwd) {
|
|
51
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
52
|
+
if (!filePath) return null;
|
|
53
|
+
|
|
54
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
55
|
+
if (!DOC_EXTENSIONS.has(ext)) return null;
|
|
56
|
+
|
|
57
|
+
// Only block NEW file creation — existing docs can always be edited
|
|
58
|
+
if (fs.existsSync(filePath)) return null;
|
|
59
|
+
|
|
60
|
+
// Check config — disabled by default
|
|
61
|
+
const effectiveCwd = cwd || process.cwd();
|
|
62
|
+
if (!isBlockDocSprawlEnabled(effectiveCwd)) return null;
|
|
63
|
+
|
|
64
|
+
// Check basename allowlist
|
|
65
|
+
const basename = path.basename(filePath).toLowerCase();
|
|
66
|
+
if (ALLOWED_DOC_BASENAMES.has(basename)) return null;
|
|
67
|
+
|
|
68
|
+
// Check if file is in an allowed directory
|
|
69
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
70
|
+
for (const seg of ALLOWED_DIR_SEGMENTS) {
|
|
71
|
+
if (normalized.includes(`/${seg}/`)) return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logHook('check-doc-sprawl', 'PreToolUse', 'block', {
|
|
75
|
+
file: path.basename(filePath),
|
|
76
|
+
ext
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
exitCode: 2,
|
|
81
|
+
output: {
|
|
82
|
+
decision: 'block',
|
|
83
|
+
reason: `[Doc Sprawl] Blocked creation of ${path.basename(filePath)}. ` +
|
|
84
|
+
'Only known docs (README.md, CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md, LICENSE.md) ' +
|
|
85
|
+
'and .planning/ files are allowed when blockDocSprawl is enabled. ' +
|
|
86
|
+
'Add content to an existing file instead, or disable hooks.blockDocSprawl in config.'
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isBlockDocSprawlEnabled(cwd) {
|
|
92
|
+
try {
|
|
93
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
94
|
+
if (!fs.existsSync(configPath)) return false;
|
|
95
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
96
|
+
return !!config.hooks?.blockDocSprawl;
|
|
97
|
+
} catch (_e) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { checkDocSprawl, isBlockDocSprawlEnabled, ALLOWED_DOC_BASENAMES, ALLOWED_DIR_SEGMENTS, DOC_EXTENSIONS };
|