@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,119 +1,119 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PostToolUse hook on Write|Edit: Tracks tool call count per session
|
|
5
|
-
* and suggests /compact when approaching context limits.
|
|
6
|
-
*
|
|
7
|
-
* Counter stored in .planning/.compact-counter (JSON).
|
|
8
|
-
* Threshold configurable via config.json hooks.compactThreshold (default: 50).
|
|
9
|
-
* After first suggestion, re-suggests every 25 calls.
|
|
10
|
-
* Counter resets on SessionStart (via progress-tracker.js).
|
|
11
|
-
*
|
|
12
|
-
* Exit codes:
|
|
13
|
-
* 0 = always (advisory only)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const { logHook } = require('./hook-logger');
|
|
19
|
-
const { configLoad } = require('./pbr-tools');
|
|
20
|
-
|
|
21
|
-
const DEFAULT_THRESHOLD = 50;
|
|
22
|
-
const REMINDER_INTERVAL = 25;
|
|
23
|
-
|
|
24
|
-
function main() {
|
|
25
|
-
process.stdin.setEncoding('utf8');
|
|
26
|
-
process.stdin.resume();
|
|
27
|
-
process.stdin.on('end', () => {
|
|
28
|
-
try {
|
|
29
|
-
const cwd = process.cwd();
|
|
30
|
-
const planningDir = path.join(cwd, '.planning');
|
|
31
|
-
if (!fs.existsSync(planningDir)) {
|
|
32
|
-
process.exit(0);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const result = checkCompaction(planningDir, cwd);
|
|
36
|
-
if (result) {
|
|
37
|
-
process.stdout.write(JSON.stringify(result));
|
|
38
|
-
}
|
|
39
|
-
process.exit(0);
|
|
40
|
-
} catch (_e) {
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Increment tool call counter and return a suggestion if threshold is reached.
|
|
48
|
-
* @param {string} planningDir - Path to .planning/ directory
|
|
49
|
-
* @param {string} cwd - Current working directory (for config loading)
|
|
50
|
-
* @returns {Object|null} Hook output with additionalContext, or null
|
|
51
|
-
*/
|
|
52
|
-
function checkCompaction(planningDir, cwd) {
|
|
53
|
-
const counterPath = path.join(planningDir, '.compact-counter');
|
|
54
|
-
const counter = loadCounter(counterPath);
|
|
55
|
-
const threshold = getThreshold(cwd);
|
|
56
|
-
|
|
57
|
-
counter.count += 1;
|
|
58
|
-
saveCounter(counterPath, counter);
|
|
59
|
-
|
|
60
|
-
if (counter.count < threshold) return null;
|
|
61
|
-
|
|
62
|
-
const isFirstSuggestion = !counter.lastSuggested;
|
|
63
|
-
const callsSinceSuggestion = counter.count - (counter.lastSuggested || 0);
|
|
64
|
-
|
|
65
|
-
if (isFirstSuggestion || callsSinceSuggestion >= REMINDER_INTERVAL) {
|
|
66
|
-
counter.lastSuggested = counter.count;
|
|
67
|
-
saveCounter(counterPath, counter);
|
|
68
|
-
|
|
69
|
-
logHook('suggest-compact', 'PostToolUse', 'suggest', {
|
|
70
|
-
count: counter.count,
|
|
71
|
-
threshold
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
additionalContext: `[Context Budget] ${counter.count} tool calls this session (threshold: ${threshold}). Consider running /compact to free context space before quality degrades.`
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function loadCounter(counterPath) {
|
|
83
|
-
try {
|
|
84
|
-
const content = fs.readFileSync(counterPath, 'utf8');
|
|
85
|
-
const data = JSON.parse(content);
|
|
86
|
-
return { count: data.count || 0, lastSuggested: data.lastSuggested || 0 };
|
|
87
|
-
} catch (_e) {
|
|
88
|
-
return { count: 0, lastSuggested: 0 };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function saveCounter(counterPath, counter) {
|
|
93
|
-
try {
|
|
94
|
-
fs.writeFileSync(counterPath, JSON.stringify(counter), 'utf8');
|
|
95
|
-
} catch (_e) {
|
|
96
|
-
// Best-effort
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function getThreshold(cwd) {
|
|
101
|
-
const planningDir = path.join(cwd, '.planning');
|
|
102
|
-
const config = configLoad(planningDir);
|
|
103
|
-
if (!config) return DEFAULT_THRESHOLD;
|
|
104
|
-
return config.hooks?.compactThreshold || DEFAULT_THRESHOLD;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function resetCounter(planningDir) {
|
|
108
|
-
const counterPath = path.join(planningDir, '.compact-counter');
|
|
109
|
-
try {
|
|
110
|
-
if (fs.existsSync(counterPath)) {
|
|
111
|
-
fs.unlinkSync(counterPath);
|
|
112
|
-
}
|
|
113
|
-
} catch (_e) {
|
|
114
|
-
// Best-effort
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
module.exports = { checkCompaction, loadCounter, saveCounter, getThreshold, resetCounter, DEFAULT_THRESHOLD, REMINDER_INTERVAL };
|
|
119
|
-
if (require.main === module) { main(); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse hook on Write|Edit: Tracks tool call count per session
|
|
5
|
+
* and suggests /compact when approaching context limits.
|
|
6
|
+
*
|
|
7
|
+
* Counter stored in .planning/.compact-counter (JSON).
|
|
8
|
+
* Threshold configurable via config.json hooks.compactThreshold (default: 50).
|
|
9
|
+
* After first suggestion, re-suggests every 25 calls.
|
|
10
|
+
* Counter resets on SessionStart (via progress-tracker.js).
|
|
11
|
+
*
|
|
12
|
+
* Exit codes:
|
|
13
|
+
* 0 = always (advisory only)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { logHook } = require('./hook-logger');
|
|
19
|
+
const { configLoad } = require('./pbr-tools');
|
|
20
|
+
|
|
21
|
+
const DEFAULT_THRESHOLD = 50;
|
|
22
|
+
const REMINDER_INTERVAL = 25;
|
|
23
|
+
|
|
24
|
+
function main() {
|
|
25
|
+
process.stdin.setEncoding('utf8');
|
|
26
|
+
process.stdin.resume();
|
|
27
|
+
process.stdin.on('end', () => {
|
|
28
|
+
try {
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
const planningDir = path.join(cwd, '.planning');
|
|
31
|
+
if (!fs.existsSync(planningDir)) {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const result = checkCompaction(planningDir, cwd);
|
|
36
|
+
if (result) {
|
|
37
|
+
process.stdout.write(JSON.stringify(result));
|
|
38
|
+
}
|
|
39
|
+
process.exit(0);
|
|
40
|
+
} catch (_e) {
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Increment tool call counter and return a suggestion if threshold is reached.
|
|
48
|
+
* @param {string} planningDir - Path to .planning/ directory
|
|
49
|
+
* @param {string} cwd - Current working directory (for config loading)
|
|
50
|
+
* @returns {Object|null} Hook output with additionalContext, or null
|
|
51
|
+
*/
|
|
52
|
+
function checkCompaction(planningDir, cwd) {
|
|
53
|
+
const counterPath = path.join(planningDir, '.compact-counter');
|
|
54
|
+
const counter = loadCounter(counterPath);
|
|
55
|
+
const threshold = getThreshold(cwd);
|
|
56
|
+
|
|
57
|
+
counter.count += 1;
|
|
58
|
+
saveCounter(counterPath, counter);
|
|
59
|
+
|
|
60
|
+
if (counter.count < threshold) return null;
|
|
61
|
+
|
|
62
|
+
const isFirstSuggestion = !counter.lastSuggested;
|
|
63
|
+
const callsSinceSuggestion = counter.count - (counter.lastSuggested || 0);
|
|
64
|
+
|
|
65
|
+
if (isFirstSuggestion || callsSinceSuggestion >= REMINDER_INTERVAL) {
|
|
66
|
+
counter.lastSuggested = counter.count;
|
|
67
|
+
saveCounter(counterPath, counter);
|
|
68
|
+
|
|
69
|
+
logHook('suggest-compact', 'PostToolUse', 'suggest', {
|
|
70
|
+
count: counter.count,
|
|
71
|
+
threshold
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
additionalContext: `[Context Budget] ${counter.count} tool calls this session (threshold: ${threshold}). Consider running /compact to free context space before quality degrades.`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function loadCounter(counterPath) {
|
|
83
|
+
try {
|
|
84
|
+
const content = fs.readFileSync(counterPath, 'utf8');
|
|
85
|
+
const data = JSON.parse(content);
|
|
86
|
+
return { count: data.count || 0, lastSuggested: data.lastSuggested || 0 };
|
|
87
|
+
} catch (_e) {
|
|
88
|
+
return { count: 0, lastSuggested: 0 };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function saveCounter(counterPath, counter) {
|
|
93
|
+
try {
|
|
94
|
+
fs.writeFileSync(counterPath, JSON.stringify(counter), 'utf8');
|
|
95
|
+
} catch (_e) {
|
|
96
|
+
// Best-effort
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getThreshold(cwd) {
|
|
101
|
+
const planningDir = path.join(cwd, '.planning');
|
|
102
|
+
const config = configLoad(planningDir);
|
|
103
|
+
if (!config) return DEFAULT_THRESHOLD;
|
|
104
|
+
return config.hooks?.compactThreshold || DEFAULT_THRESHOLD;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function resetCounter(planningDir) {
|
|
108
|
+
const counterPath = path.join(planningDir, '.compact-counter');
|
|
109
|
+
try {
|
|
110
|
+
if (fs.existsSync(counterPath)) {
|
|
111
|
+
fs.unlinkSync(counterPath);
|
|
112
|
+
}
|
|
113
|
+
} catch (_e) {
|
|
114
|
+
// Best-effort
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { checkCompaction, loadCounter, saveCounter, getThreshold, resetCounter, DEFAULT_THRESHOLD, REMINDER_INTERVAL };
|
|
119
|
+
if (require.main === module) { main(); }
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* TaskCompleted hook: Logs agent task completion with output summary.
|
|
5
|
-
*
|
|
6
|
-
* Fires when a Task() sub-agent finishes (distinct from SubagentStop).
|
|
7
|
-
* Logs the completion event and agent type for workflow tracking.
|
|
8
|
-
*
|
|
9
|
-
* Non-blocking — exits 0 always.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const { logHook } = require('./hook-logger');
|
|
14
|
-
const { logEvent } = require('./event-logger');
|
|
15
|
-
|
|
16
|
-
function readStdin() {
|
|
17
|
-
try {
|
|
18
|
-
const input = fs.readFileSync(0, 'utf8').trim();
|
|
19
|
-
if (input) return JSON.parse(input);
|
|
20
|
-
} catch (_e) {
|
|
21
|
-
// empty or non-JSON stdin
|
|
22
|
-
}
|
|
23
|
-
return {};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function main() {
|
|
27
|
-
const data = readStdin();
|
|
28
|
-
|
|
29
|
-
logHook('task-completed', 'TaskCompleted', 'completed', {
|
|
30
|
-
agent_type: data.agent_type || data.subagent_type || null,
|
|
31
|
-
agent_id: data.agent_id || null,
|
|
32
|
-
duration_ms: data.duration_ms || null
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
logEvent('agent', 'task-completed', {
|
|
36
|
-
agent_type: data.agent_type || data.subagent_type || null,
|
|
37
|
-
agent_id: data.agent_id || null,
|
|
38
|
-
duration_ms: data.duration_ms || null
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (require.main === module) { main(); }
|
|
45
|
-
module.exports = { main };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TaskCompleted hook: Logs agent task completion with output summary.
|
|
5
|
+
*
|
|
6
|
+
* Fires when a Task() sub-agent finishes (distinct from SubagentStop).
|
|
7
|
+
* Logs the completion event and agent type for workflow tracking.
|
|
8
|
+
*
|
|
9
|
+
* Non-blocking — exits 0 always.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const { logHook } = require('./hook-logger');
|
|
14
|
+
const { logEvent } = require('./event-logger');
|
|
15
|
+
|
|
16
|
+
function readStdin() {
|
|
17
|
+
try {
|
|
18
|
+
const input = fs.readFileSync(0, 'utf8').trim();
|
|
19
|
+
if (input) return JSON.parse(input);
|
|
20
|
+
} catch (_e) {
|
|
21
|
+
// empty or non-JSON stdin
|
|
22
|
+
}
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function main() {
|
|
27
|
+
const data = readStdin();
|
|
28
|
+
|
|
29
|
+
logHook('task-completed', 'TaskCompleted', 'completed', {
|
|
30
|
+
agent_type: data.agent_type || data.subagent_type || null,
|
|
31
|
+
agent_id: data.agent_id || null,
|
|
32
|
+
duration_ms: data.duration_ms || null
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
logEvent('agent', 'task-completed', {
|
|
36
|
+
agent_type: data.agent_type || data.subagent_type || null,
|
|
37
|
+
agent_id: data.agent_id || null,
|
|
38
|
+
duration_ms: data.duration_ms || null
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (require.main === module) { main(); }
|
|
45
|
+
module.exports = { main };
|
|
@@ -1,119 +1,149 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* PostToolUse hook on Read: Tracks cumulative file reads per skill invocation.
|
|
5
|
-
*
|
|
6
|
-
* Maintains a session-scoped counter in .planning/.context-tracker.
|
|
7
|
-
* Warns
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse hook on Read: Tracks cumulative file reads per skill invocation.
|
|
5
|
+
*
|
|
6
|
+
* Maintains a session-scoped counter in .planning/.context-tracker.
|
|
7
|
+
* Warns only at meaningful thresholds to reduce noise:
|
|
8
|
+
* - Unique files read crosses milestone (10, 20, 30, ...)
|
|
9
|
+
* - Total chars read crosses milestone (50k, 100k, 150k, ...)
|
|
10
|
+
* - A single file read is unusually large (> 5,000 chars)
|
|
11
|
+
* Resets when .active-skill changes (new skill invocation).
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 = always (PostToolUse hook, advisory only)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { logHook } = require('./hook-logger');
|
|
20
|
+
|
|
21
|
+
const UNIQUE_FILE_MILESTONE = 10; // warn every 10 unique files
|
|
22
|
+
const CHAR_MILESTONE = 50000; // warn every 50k chars
|
|
23
|
+
const LARGE_FILE_THRESHOLD = 5000; // warn if single read > 5k chars
|
|
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 cwd = process.cwd();
|
|
33
|
+
const planningDir = path.join(cwd, '.planning');
|
|
34
|
+
if (!fs.existsSync(planningDir)) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const data = JSON.parse(input);
|
|
39
|
+
const filePath = data.tool_input?.file_path || '';
|
|
40
|
+
if (!filePath) {
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Skip plugin-internal files — these are loaded by the plugin system,
|
|
45
|
+
// not by the orchestrator, so they shouldn't count against context budget
|
|
46
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || '';
|
|
47
|
+
if (pluginRoot) {
|
|
48
|
+
const normalizedFile = path.resolve(filePath);
|
|
49
|
+
const normalizedPlugin = path.resolve(pluginRoot);
|
|
50
|
+
if (normalizedFile.startsWith(normalizedPlugin + path.sep) || normalizedFile === normalizedPlugin) {
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Estimate chars read (use limit if provided, otherwise assume ~2000 lines × 40 chars avg)
|
|
56
|
+
const limit = data.tool_input?.limit;
|
|
57
|
+
const estimatedChars = limit ? limit * 40 : 80000;
|
|
58
|
+
// Use actual output length if available
|
|
59
|
+
const actualChars = data.tool_output ? String(data.tool_output).length : estimatedChars;
|
|
60
|
+
|
|
61
|
+
const trackerPath = path.join(planningDir, '.context-tracker');
|
|
62
|
+
const skillPath = path.join(planningDir, '.active-skill');
|
|
63
|
+
|
|
64
|
+
// Check if active skill changed (reset tracker)
|
|
65
|
+
const currentSkill = readFileSafe(skillPath);
|
|
66
|
+
let tracker = loadTracker(trackerPath);
|
|
67
|
+
|
|
68
|
+
if (tracker.skill !== currentSkill) {
|
|
69
|
+
tracker = { skill: currentSkill, reads: 0, total_chars: 0, files: [] };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Update tracker
|
|
73
|
+
const prevFileCount = tracker.files.length;
|
|
74
|
+
tracker.reads += 1;
|
|
75
|
+
tracker.total_chars += actualChars;
|
|
76
|
+
if (!tracker.files.includes(filePath)) {
|
|
77
|
+
tracker.files.push(filePath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Save tracker
|
|
81
|
+
try {
|
|
82
|
+
fs.writeFileSync(trackerPath, JSON.stringify(tracker), 'utf8');
|
|
83
|
+
} catch (_e) {
|
|
84
|
+
// Best-effort
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check thresholds — only warn at milestone crossings, not every read
|
|
88
|
+
const warnings = [];
|
|
89
|
+
|
|
90
|
+
// Milestone: unique files read crosses a multiple of UNIQUE_FILE_MILESTONE
|
|
91
|
+
const curUniqueFiles = tracker.files.length;
|
|
92
|
+
if (curUniqueFiles >= UNIQUE_FILE_MILESTONE &&
|
|
93
|
+
Math.floor(curUniqueFiles / UNIQUE_FILE_MILESTONE) > Math.floor(prevFileCount / UNIQUE_FILE_MILESTONE)) {
|
|
94
|
+
warnings.push(`${curUniqueFiles} unique files read (milestone: every ${UNIQUE_FILE_MILESTONE})`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Milestone: total chars crosses a multiple of CHAR_MILESTONE
|
|
98
|
+
const prevChars = tracker.total_chars - actualChars;
|
|
99
|
+
if (tracker.total_chars >= CHAR_MILESTONE &&
|
|
100
|
+
Math.floor(tracker.total_chars / CHAR_MILESTONE) > Math.floor(prevChars / CHAR_MILESTONE)) {
|
|
101
|
+
const kChars = Math.round(tracker.total_chars / 1000);
|
|
102
|
+
warnings.push(`~${kChars}k chars read (milestone: every ${CHAR_MILESTONE / 1000}k)`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Single large file warning
|
|
106
|
+
if (actualChars >= LARGE_FILE_THRESHOLD) {
|
|
107
|
+
const kChars = Math.round(actualChars / 1000);
|
|
108
|
+
warnings.push(`large file read (~${kChars}k chars): ${path.basename(filePath)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (warnings.length > 0) {
|
|
112
|
+
logHook('track-context-budget', 'PostToolUse', 'warn', {
|
|
113
|
+
reads: tracker.reads,
|
|
114
|
+
total_chars: tracker.total_chars,
|
|
115
|
+
unique_files: tracker.files.length,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const output = {
|
|
119
|
+
additionalContext: `[Context Budget Warning] ${warnings.join(', ')}. ${tracker.files.length} unique files read. Consider delegating remaining reads to a Task() subagent to protect orchestrator context.`
|
|
120
|
+
};
|
|
121
|
+
process.stdout.write(JSON.stringify(output));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
process.exit(0);
|
|
125
|
+
} catch (_e) {
|
|
126
|
+
// Never block on tracking errors
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function readFileSafe(filePath) {
|
|
133
|
+
try {
|
|
134
|
+
return fs.readFileSync(filePath, 'utf8').trim();
|
|
135
|
+
} catch (_e) {
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function loadTracker(trackerPath) {
|
|
141
|
+
try {
|
|
142
|
+
const content = fs.readFileSync(trackerPath, 'utf8');
|
|
143
|
+
return JSON.parse(content);
|
|
144
|
+
} catch (_e) {
|
|
145
|
+
return { skill: '', reads: 0, total_chars: 0, files: [] };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
main();
|