@sienklogic/plan-build-run 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +1 -1
- package/plugins/copilot-pbr/agents/debugger.agent.md +2 -0
- package/plugins/copilot-pbr/agents/executor.agent.md +4 -1
- package/plugins/copilot-pbr/agents/general.agent.md +2 -1
- package/plugins/copilot-pbr/agents/integration-checker.agent.md +2 -0
- package/plugins/copilot-pbr/agents/plan-checker.agent.md +1 -1
- package/plugins/copilot-pbr/agents/planner.agent.md +3 -1
- package/plugins/copilot-pbr/agents/researcher.agent.md +5 -3
- package/plugins/copilot-pbr/agents/synthesizer.agent.md +1 -1
- package/plugins/copilot-pbr/agents/verifier.agent.md +3 -3
- package/plugins/copilot-pbr/hooks/hooks.json +37 -13
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/copilot-pbr/skills/audit/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/build/SKILL.md +7 -8
- package/plugins/copilot-pbr/skills/config/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/continue/SKILL.md +8 -2
- package/plugins/copilot-pbr/skills/debug/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/import/SKILL.md +3 -1
- package/plugins/copilot-pbr/skills/milestone/SKILL.md +2 -0
- package/plugins/copilot-pbr/skills/pause/SKILL.md +7 -1
- package/plugins/copilot-pbr/skills/plan/SKILL.md +1 -1
- package/plugins/copilot-pbr/skills/review/SKILL.md +4 -4
- package/plugins/copilot-pbr/skills/scan/SKILL.md +4 -4
- package/plugins/copilot-pbr/skills/shared/config-loading.md +1 -1
- package/plugins/copilot-pbr/skills/shared/context-budget.md +3 -3
- package/plugins/copilot-pbr/skills/shared/state-loading.md +1 -1
- package/plugins/copilot-pbr/skills/shared/state-update.md +12 -4
- package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +1 -1
- package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +7 -0
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/agents/codebase-mapper.md +1 -1
- package/plugins/cursor-pbr/agents/debugger.md +2 -0
- package/plugins/cursor-pbr/agents/executor.md +4 -1
- package/plugins/cursor-pbr/agents/general.md +2 -1
- package/plugins/cursor-pbr/agents/integration-checker.md +2 -0
- package/plugins/cursor-pbr/agents/plan-checker.md +1 -1
- package/plugins/cursor-pbr/agents/planner.md +3 -1
- package/plugins/cursor-pbr/agents/researcher.md +5 -3
- package/plugins/cursor-pbr/agents/synthesizer.md +1 -1
- package/plugins/cursor-pbr/agents/verifier.md +3 -3
- package/plugins/cursor-pbr/hooks/hooks.json +20 -0
- package/plugins/cursor-pbr/skills/audit/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/build/SKILL.md +7 -8
- package/plugins/cursor-pbr/skills/config/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/continue/SKILL.md +8 -2
- package/plugins/cursor-pbr/skills/debug/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/import/SKILL.md +3 -1
- package/plugins/cursor-pbr/skills/milestone/SKILL.md +2 -0
- package/plugins/cursor-pbr/skills/pause/SKILL.md +7 -1
- package/plugins/cursor-pbr/skills/plan/SKILL.md +1 -1
- package/plugins/cursor-pbr/skills/review/SKILL.md +4 -4
- package/plugins/cursor-pbr/skills/scan/SKILL.md +4 -4
- package/plugins/cursor-pbr/skills/shared/config-loading.md +1 -1
- package/plugins/cursor-pbr/skills/shared/context-budget.md +3 -3
- package/plugins/cursor-pbr/skills/shared/state-loading.md +1 -1
- package/plugins/cursor-pbr/skills/shared/state-update.md +12 -4
- package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +1 -1
- package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +7 -0
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/agents/codebase-mapper.md +1 -1
- package/plugins/pbr/agents/debugger.md +2 -0
- package/plugins/pbr/agents/executor.md +4 -1
- package/plugins/pbr/agents/general.md +2 -1
- package/plugins/pbr/agents/integration-checker.md +2 -0
- package/plugins/pbr/agents/plan-checker.md +0 -1
- package/plugins/pbr/agents/planner.md +2 -1
- package/plugins/pbr/agents/researcher.md +2 -0
- package/plugins/pbr/agents/synthesizer.md +0 -1
- package/plugins/pbr/agents/verifier.md +1 -1
- package/plugins/pbr/commands/do.md +5 -0
- package/plugins/pbr/hooks/hooks.json +20 -0
- package/plugins/pbr/scripts/auto-continue.js +26 -2
- package/plugins/pbr/scripts/block-skill-self-read.js +72 -0
- package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
- package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
- package/plugins/pbr/scripts/check-dangerous-commands.js +2 -2
- package/plugins/pbr/scripts/check-phase-boundary.js +2 -8
- package/plugins/pbr/scripts/check-plan-format.js +188 -24
- package/plugins/pbr/scripts/check-roadmap-sync.js +140 -1
- package/plugins/pbr/scripts/check-skill-workflow.js +3 -11
- package/plugins/pbr/scripts/check-state-sync.js +75 -11
- package/plugins/pbr/scripts/check-subagent-output.js +78 -6
- package/plugins/pbr/scripts/check-summary-gate.js +1 -1
- package/plugins/pbr/scripts/log-tool-failure.js +1 -4
- package/plugins/pbr/scripts/post-write-dispatch.js +47 -0
- package/plugins/pbr/scripts/pre-write-dispatch.js +9 -2
- package/plugins/pbr/scripts/session-cleanup.js +3 -4
- package/plugins/pbr/scripts/status-line.js +44 -11
- package/plugins/pbr/scripts/validate-commit.js +8 -7
- package/plugins/pbr/scripts/validate-skill-args.js +2 -1
- package/plugins/pbr/scripts/validate-task.js +14 -24
- package/plugins/pbr/skills/build/SKILL.md +7 -8
- package/plugins/pbr/skills/continue/SKILL.md +8 -2
- package/plugins/pbr/skills/health/SKILL.md +0 -2
- package/plugins/pbr/skills/import/SKILL.md +2 -0
- package/plugins/pbr/skills/milestone/SKILL.md +2 -0
- package/plugins/pbr/skills/pause/SKILL.md +7 -1
- package/plugins/pbr/skills/plan/SKILL.md +1 -1
- package/plugins/pbr/skills/review/SKILL.md +4 -4
- package/plugins/pbr/skills/shared/state-update.md +10 -2
- package/plugins/pbr/templates/ROADMAP.md.tmpl +2 -0
|
@@ -9,11 +9,12 @@ tools:
|
|
|
9
9
|
- Bash
|
|
10
10
|
- Glob
|
|
11
11
|
- Grep
|
|
12
|
-
- WebFetch
|
|
13
12
|
---
|
|
14
13
|
|
|
15
14
|
# Plan-Build-Run Planner
|
|
16
15
|
|
|
16
|
+
> **Memory note:** Project memory is enabled to provide planning continuity and awareness of prior phase decisions.
|
|
17
|
+
|
|
17
18
|
You are **planner**, the planning agent for the Plan-Build-Run development system. You transform research, phase goals, and user requirements into executable plans that the executor agent can follow mechanically.
|
|
18
19
|
|
|
19
20
|
## Core Principle: Context Fidelity
|
|
@@ -60,6 +60,8 @@ All claims must be attributed to a source level. Higher levels override lower le
|
|
|
60
60
|
|
|
61
61
|
**Attribution rules**: Every factual claim needs a source tag (`[S1]`, `[S2]`, etc.). Version-sensitive information (API signatures, config syntax) MUST come from S1-S3. When citing S2, note the version: `[S2-v14.2]`. Contradictions resolve in favor of higher source level.
|
|
62
62
|
|
|
63
|
+
**Offline Fallback**: If web tools are unavailable (air-gapped environment, MCP not configured), rely on local sources: codebase analysis via Glob/Grep, existing documentation, and README files. Assign these S3-S4 confidence levels. Do not attempt WebFetch or WebSearch — note in the output header that external sources were unavailable.
|
|
64
|
+
|
|
63
65
|
---
|
|
64
66
|
|
|
65
67
|
## Confidence Levels
|
|
@@ -222,7 +222,7 @@ Read `references/stub-patterns.md` for stub detection patterns by technology. Re
|
|
|
222
222
|
|
|
223
223
|
### Verifier-Specific Anti-Patterns
|
|
224
224
|
1. DO NOT trust SUMMARY.md claims without verifying the actual codebase
|
|
225
|
-
2. DO NOT attempt to fix issues — you have no
|
|
225
|
+
2. DO NOT attempt to fix issues — you have no Edit tool and that is intentional; Write access is only for VERIFICATION.md output
|
|
226
226
|
3. DO NOT mark stubs as SUBSTANTIVE — if it has a TODO, it's a stub
|
|
227
227
|
4. DO NOT mark orphaned code as WIRED — if nothing imports it, it's orphaned
|
|
228
228
|
5. DO NOT skip Level 2 or Level 3 checks — existence alone is insufficient
|
|
@@ -77,6 +77,16 @@
|
|
|
77
77
|
}
|
|
78
78
|
],
|
|
79
79
|
"PreToolUse": [
|
|
80
|
+
{
|
|
81
|
+
"matcher": "Read",
|
|
82
|
+
"hooks": [
|
|
83
|
+
{
|
|
84
|
+
"type": "command",
|
|
85
|
+
"command": "node -e \"var r=process.env.CLAUDE_PLUGIN_ROOT||'',m=r.match(/^\\/([a-zA-Z])\\/(.*)/);if(m)r=m[1]+String.fromCharCode(58)+String.fromCharCode(92)+m[2];require(require('path').resolve(r,'scripts','run-hook.js'))\" block-skill-self-read.js",
|
|
86
|
+
"statusMessage": "Checking skill self-read..."
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
},
|
|
80
90
|
{
|
|
81
91
|
"matcher": "Bash",
|
|
82
92
|
"hooks": [
|
|
@@ -87,6 +97,16 @@
|
|
|
87
97
|
}
|
|
88
98
|
]
|
|
89
99
|
},
|
|
100
|
+
{
|
|
101
|
+
"matcher": "Bash",
|
|
102
|
+
"hooks": [
|
|
103
|
+
{
|
|
104
|
+
"type": "command",
|
|
105
|
+
"command": "node -e \"var r=process.env.CLAUDE_PLUGIN_ROOT||'',m=r.match(/^\\/([a-zA-Z])\\/(.*)/);if(m)r=m[1]+String.fromCharCode(58)+String.fromCharCode(92)+m[2];require(require('path').resolve(r,'scripts','run-hook.js'))\" check-cross-plugin-sync.js",
|
|
106
|
+
"statusMessage": "Checking cross-plugin sync..."
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
90
110
|
{
|
|
91
111
|
"matcher": "Write|Edit",
|
|
92
112
|
"hooks": [
|
|
@@ -63,6 +63,9 @@ function main() {
|
|
|
63
63
|
} catch (_todoErr) {
|
|
64
64
|
// Ignore errors scanning todos
|
|
65
65
|
}
|
|
66
|
+
// Reset continue count on normal session stop (no signal)
|
|
67
|
+
const countPathNoSig = path.join(planningDir, '.continue-count');
|
|
68
|
+
try { fs.unlinkSync(countPathNoSig); } catch (_e) { /* ignore */ }
|
|
66
69
|
logHook('auto-continue', 'Stop', 'no-signal', {});
|
|
67
70
|
process.exit(0);
|
|
68
71
|
}
|
|
@@ -97,19 +100,40 @@ function main() {
|
|
|
97
100
|
process.exit(0);
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
// Track consecutive continues for session length guard
|
|
104
|
+
const countPath = path.join(planningDir, '.continue-count');
|
|
105
|
+
let continueCount = 0;
|
|
106
|
+
try {
|
|
107
|
+
continueCount = parseInt(fs.readFileSync(countPath, 'utf8').trim(), 10) || 0;
|
|
108
|
+
} catch (_e) { /* file missing — start at 0 */ }
|
|
109
|
+
continueCount++;
|
|
110
|
+
try { fs.writeFileSync(countPath, String(continueCount)); } catch (_e) { /* ignore */ }
|
|
111
|
+
|
|
112
|
+
// Hard stop after 6 consecutive continues — context is likely degraded
|
|
113
|
+
if (continueCount > 6) {
|
|
114
|
+
logHook('auto-continue', 'Stop', 'hard-stop-session-length', { count: continueCount });
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
100
118
|
// Extract last_assistant_message for richer continuation context
|
|
101
119
|
const lastMsg = hookInput.last_assistant_message || '';
|
|
102
120
|
const msgSuffix = lastMsg
|
|
103
121
|
? ` (last message excerpt: ${lastMsg.slice(0, 200)})`
|
|
104
122
|
: '';
|
|
105
123
|
|
|
106
|
-
logHook('auto-continue', 'Stop', 'continue', { next: nextCommand, hasLastMsg: !!lastMsg });
|
|
124
|
+
logHook('auto-continue', 'Stop', 'continue', { next: nextCommand, hasLastMsg: !!lastMsg, continueCount });
|
|
125
|
+
|
|
126
|
+
// Build reason string with optional advisory
|
|
127
|
+
let reasonStr = `Auto-continue: execute ${nextCommand}${msgSuffix}`;
|
|
128
|
+
if (continueCount > 3) {
|
|
129
|
+
reasonStr += `\n\n[pbr] Advisory: ${continueCount} consecutive continues. Consider /pbr:pause + fresh session to avoid context degradation.`;
|
|
130
|
+
}
|
|
107
131
|
|
|
108
132
|
// Block the stop and inject the next command as Claude's continuation reason.
|
|
109
133
|
// Claude Code Stop hooks use { decision: "block", reason: "..." } to keep going.
|
|
110
134
|
const output = {
|
|
111
135
|
decision: 'block',
|
|
112
|
-
reason:
|
|
136
|
+
reason: reasonStr
|
|
113
137
|
};
|
|
114
138
|
process.stdout.write(JSON.stringify(output));
|
|
115
139
|
process.exit(0);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse Read hook: blocks skills from re-reading their own SKILL.md.
|
|
5
|
+
*
|
|
6
|
+
* Skills are already loaded into context by Claude Code — re-reading
|
|
7
|
+
* wastes ~13k tokens. This hook checks .planning/.active-skill to
|
|
8
|
+
* determine the current skill and blocks if the Read target matches.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { logHook } = require('./hook-logger');
|
|
16
|
+
|
|
17
|
+
function main() {
|
|
18
|
+
try {
|
|
19
|
+
let hookInput = {};
|
|
20
|
+
try {
|
|
21
|
+
const stdin = fs.readFileSync(0, 'utf8').trim();
|
|
22
|
+
if (stdin) hookInput = JSON.parse(stdin);
|
|
23
|
+
} catch (_parseErr) {
|
|
24
|
+
// No stdin or invalid JSON
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cwd = hookInput.cwd || process.cwd();
|
|
28
|
+
const toolInput = hookInput.tool_input || {};
|
|
29
|
+
const filePath = toolInput.file_path || '';
|
|
30
|
+
|
|
31
|
+
if (!filePath) {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Read .active-skill to get current skill name
|
|
36
|
+
const activeSkillPath = path.join(cwd, '.planning', '.active-skill');
|
|
37
|
+
let skillName = '';
|
|
38
|
+
try {
|
|
39
|
+
skillName = fs.readFileSync(activeSkillPath, 'utf8').trim();
|
|
40
|
+
} catch (_readErr) {
|
|
41
|
+
// No .active-skill file — nothing to block
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!skillName) {
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if file_path ends with skills/{skill-name}/SKILL.md
|
|
50
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
51
|
+
const pattern = `skills/${skillName}/SKILL.md`.toLowerCase();
|
|
52
|
+
const match = normalized.toLowerCase().endsWith(pattern);
|
|
53
|
+
|
|
54
|
+
if (match) {
|
|
55
|
+
logHook('block-skill-self-read', 'PreToolUse', 'block', { skill: skillName, file: filePath });
|
|
56
|
+
const output = {
|
|
57
|
+
decision: 'block',
|
|
58
|
+
reason: `SKILL.md self-read blocked.\n\nThe active skill (${skillName}) attempted to read its own SKILL.md. Skills are already loaded into context by Claude Code \u2014 re-reading wastes ~13k tokens.\n\nNo action needed. The skill content is already available in your prompt.`
|
|
59
|
+
};
|
|
60
|
+
process.stdout.write(JSON.stringify(output));
|
|
61
|
+
} else {
|
|
62
|
+
logHook('block-skill-self-read', 'PreToolUse', 'allow', { skill: skillName, file: filePath });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
process.exit(0);
|
|
66
|
+
} catch (_e) {
|
|
67
|
+
// Don't block on errors
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
main();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse hook: Blocks subagents (except pbr:general) from writing STATE.md.
|
|
5
|
+
*
|
|
6
|
+
* STATE.md is the source of truth for project position and should only be
|
|
7
|
+
* written by skills (orchestrators) or the general agent, never by specialized
|
|
8
|
+
* agents like executor, planner, or verifier.
|
|
9
|
+
*
|
|
10
|
+
* Detection: reads .planning/.active-agent (written by log-subagent.js).
|
|
11
|
+
*
|
|
12
|
+
* Exit codes:
|
|
13
|
+
* 0 = allowed or not applicable
|
|
14
|
+
* 2 = blocked (agent not allowed to write STATE.md)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
const BLOCKED_AGENTS = [
|
|
21
|
+
'pbr:executor',
|
|
22
|
+
'pbr:planner',
|
|
23
|
+
'pbr:verifier',
|
|
24
|
+
'pbr:researcher',
|
|
25
|
+
'pbr:plan-checker',
|
|
26
|
+
'pbr:integration-checker',
|
|
27
|
+
'pbr:debugger',
|
|
28
|
+
'pbr:codebase-mapper',
|
|
29
|
+
'pbr:synthesizer',
|
|
30
|
+
'pbr:audit',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function checkAgentStateWrite(data) {
|
|
34
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
35
|
+
if (!filePath) return null;
|
|
36
|
+
|
|
37
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
38
|
+
if (!normalized.endsWith('.planning/STATE.md')) return null;
|
|
39
|
+
|
|
40
|
+
// Check if we're inside a subagent
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
const agentFile = path.join(cwd, '.planning', '.active-agent');
|
|
43
|
+
|
|
44
|
+
let agent;
|
|
45
|
+
try {
|
|
46
|
+
agent = fs.readFileSync(agentFile, 'utf8').trim();
|
|
47
|
+
} catch (_e) {
|
|
48
|
+
// No .active-agent file — not in an agent context
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!agent || !BLOCKED_AGENTS.includes(agent)) return null;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
exitCode: 2,
|
|
56
|
+
output: {
|
|
57
|
+
decision: 'block',
|
|
58
|
+
reason: `Agent write to STATE.md blocked.\n\n${agent} is not allowed to write STATE.md. Only skills (orchestrators) and pbr:general may update this file to prevent state corruption.\n\nReturn results to the calling skill and let it update STATE.md.`
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = { checkAgentStateWrite, BLOCKED_AGENTS };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse Bash hook: Advisory warning when committing pbr skill/agent
|
|
5
|
+
* changes without corresponding cursor-pbr/copilot-pbr counterparts.
|
|
6
|
+
*
|
|
7
|
+
* Only fires on `git commit` commands. Scripts directory is excluded
|
|
8
|
+
* because cursor-pbr and copilot-pbr share scripts via ../pbr/scripts/.
|
|
9
|
+
*
|
|
10
|
+
* Exit codes:
|
|
11
|
+
* 0 = always (advisory only, never blocks)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { execSync } = require('child_process');
|
|
15
|
+
const { logHook } = require('./hook-logger');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if a git commit has cross-plugin sync drift.
|
|
19
|
+
* @param {Object} data - Parsed hook input
|
|
20
|
+
* @returns {null|{additionalContext: string}} null if clean, advisory if drift
|
|
21
|
+
*/
|
|
22
|
+
function checkCrossPluginSync(data) {
|
|
23
|
+
const command = data.tool_input?.command || '';
|
|
24
|
+
|
|
25
|
+
// Only check git commit commands
|
|
26
|
+
if (!/\bgit\s+commit\b/.test(command)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let stagedFiles;
|
|
31
|
+
try {
|
|
32
|
+
stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf8' }).trim().split('\n').filter(Boolean);
|
|
33
|
+
} catch (_e) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Find pbr skill/agent files (not scripts — those are shared)
|
|
38
|
+
const pbrSyncFiles = stagedFiles.filter(f =>
|
|
39
|
+
/^plugins\/pbr\/(skills|agents)\//.test(f)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (pbrSyncFiles.length === 0) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check for missing counterparts
|
|
47
|
+
const missingCounterparts = [];
|
|
48
|
+
for (const pbrFile of pbrSyncFiles) {
|
|
49
|
+
const relativePath = pbrFile.replace(/^plugins\/pbr\//, '');
|
|
50
|
+
const cursorPath = `plugins/cursor-pbr/${relativePath}`;
|
|
51
|
+
const copilotPath = `plugins/copilot-pbr/${relativePath}`;
|
|
52
|
+
|
|
53
|
+
const hasCursor = stagedFiles.some(f => f === cursorPath);
|
|
54
|
+
const hasCopilot = stagedFiles.some(f => f === copilotPath);
|
|
55
|
+
|
|
56
|
+
if (!hasCursor || !hasCopilot) {
|
|
57
|
+
const missing = [];
|
|
58
|
+
if (!hasCursor) missing.push('cursor-pbr');
|
|
59
|
+
if (!hasCopilot) missing.push('copilot-pbr');
|
|
60
|
+
missingCounterparts.push(`${pbrFile} (missing: ${missing.join(', ')})`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (missingCounterparts.length === 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const msg = `Advisory: Cross-plugin sync may be needed. Changed pbr files without cursor-pbr/copilot-pbr counterparts:\n${missingCounterparts.map(f => ` - ${f}`).join('\n')}`;
|
|
69
|
+
logHook('check-cross-plugin-sync', 'PreToolUse', 'warn', { missingCounterparts });
|
|
70
|
+
|
|
71
|
+
return { additionalContext: msg };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function main() {
|
|
75
|
+
let input = '';
|
|
76
|
+
process.stdin.setEncoding('utf8');
|
|
77
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
78
|
+
process.stdin.on('end', () => {
|
|
79
|
+
try {
|
|
80
|
+
const data = JSON.parse(input);
|
|
81
|
+
const result = checkCrossPluginSync(data);
|
|
82
|
+
if (result) {
|
|
83
|
+
process.stdout.write(JSON.stringify(result));
|
|
84
|
+
}
|
|
85
|
+
} catch (_e) {
|
|
86
|
+
// Don't block on errors
|
|
87
|
+
}
|
|
88
|
+
process.exit(0);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { checkCrossPluginSync };
|
|
93
|
+
if (require.main === module || process.argv[1] === __filename) { main(); }
|
|
@@ -86,7 +86,7 @@ function checkDangerous(data) {
|
|
|
86
86
|
return {
|
|
87
87
|
output: {
|
|
88
88
|
decision: 'block',
|
|
89
|
-
reason: `Dangerous command blocked.\n\n${reason}
|
|
89
|
+
reason: `Dangerous command blocked.\n\n${reason} Command: ${command.substring(0, 150)}\n\nUse a safer alternative or ask the user for explicit confirmation before running destructive commands.`
|
|
90
90
|
},
|
|
91
91
|
exitCode: 2
|
|
92
92
|
};
|
|
@@ -146,7 +146,7 @@ function checkSkillSpecificBash(command) {
|
|
|
146
146
|
return {
|
|
147
147
|
output: {
|
|
148
148
|
decision: 'block',
|
|
149
|
-
reason: '
|
|
149
|
+
reason: 'JSON shell manipulation blocked.\n\nShell tools like sed, awk, and perl can corrupt JSON structure. The statusline skill must use structured tools for JSON editing.\n\nUse the Read and Write tools to modify JSON files instead of shell text manipulation.'
|
|
150
150
|
},
|
|
151
151
|
exitCode: 2
|
|
152
152
|
};
|
|
@@ -102,10 +102,7 @@ function main() {
|
|
|
102
102
|
process.exit(2);
|
|
103
103
|
} else {
|
|
104
104
|
const output = {
|
|
105
|
-
|
|
106
|
-
hookEventName: 'PreToolUse',
|
|
107
|
-
additionalContext: `Warning: editing phase ${filePhase} file but current phase is ${currentPhase}. Ensure this cross-phase edit is intentional.`
|
|
108
|
-
}
|
|
105
|
+
additionalContext: `Warning: editing phase ${filePhase} file but current phase is ${currentPhase}. Ensure this cross-phase edit is intentional.`
|
|
109
106
|
};
|
|
110
107
|
process.stdout.write(JSON.stringify(output));
|
|
111
108
|
}
|
|
@@ -184,10 +181,7 @@ function checkBoundary(data) {
|
|
|
184
181
|
return {
|
|
185
182
|
exitCode: 0,
|
|
186
183
|
output: {
|
|
187
|
-
|
|
188
|
-
hookEventName: 'PreToolUse',
|
|
189
|
-
additionalContext: `Warning: editing phase ${filePhase} file but current phase is ${currentPhase}. Ensure this cross-phase edit is intentional.`
|
|
190
|
-
}
|
|
184
|
+
additionalContext: `Warning: editing phase ${filePhase} file but current phase is ${currentPhase}. Ensure this cross-phase edit is intentional.`
|
|
191
185
|
}
|
|
192
186
|
};
|
|
193
187
|
}
|