@bhargavvc/sdd-cc 1.30.0 → 1.35.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/README.ja-JP.md +144 -110
- package/README.ko-KR.md +143 -107
- package/README.md +183 -112
- package/README.pt-BR.md +90 -52
- package/README.zh-CN.md +141 -101
- package/agents/sdd-advisor-researcher.md +23 -0
- package/agents/sdd-ai-researcher.md +133 -0
- package/agents/sdd-code-fixer.md +516 -0
- package/agents/sdd-code-reviewer.md +355 -0
- package/agents/sdd-codebase-mapper.md +3 -3
- package/agents/sdd-debugger.md +17 -5
- package/agents/sdd-doc-verifier.md +201 -0
- package/agents/sdd-doc-writer.md +602 -0
- package/agents/sdd-domain-researcher.md +153 -0
- package/agents/sdd-eval-auditor.md +164 -0
- package/agents/sdd-eval-planner.md +154 -0
- package/agents/sdd-executor.md +87 -4
- package/agents/sdd-framework-selector.md +160 -0
- package/agents/sdd-intel-updater.md +314 -0
- package/agents/sdd-nyquist-auditor.md +1 -1
- package/agents/sdd-phase-researcher.md +71 -4
- package/agents/sdd-plan-checker.md +100 -6
- package/agents/sdd-planner.md +145 -206
- package/agents/sdd-project-researcher.md +25 -2
- package/agents/sdd-research-synthesizer.md +3 -3
- package/agents/sdd-roadmapper.md +6 -6
- package/agents/sdd-security-auditor.md +128 -0
- package/agents/sdd-ui-auditor.md +43 -3
- package/agents/sdd-ui-checker.md +5 -5
- package/agents/sdd-ui-researcher.md +27 -4
- package/agents/sdd-user-profiler.md +2 -2
- package/agents/sdd-verifier.md +142 -22
- package/bin/install.js +2151 -551
- package/commands/sdd/add-backlog.md +5 -5
- package/commands/sdd/add-tests.md +2 -2
- package/commands/sdd/ai-integration-phase.md +36 -0
- package/commands/sdd/analyze-dependencies.md +34 -0
- package/commands/sdd/audit-fix.md +33 -0
- package/commands/sdd/autonomous.md +7 -2
- package/commands/sdd/cleanup.md +5 -0
- package/commands/sdd/code-review-fix.md +52 -0
- package/commands/sdd/code-review.md +55 -0
- package/commands/sdd/complete-milestone.md +6 -6
- package/commands/sdd/debug.md +22 -9
- package/commands/sdd/discuss-phase.md +7 -2
- package/commands/sdd/do.md +1 -1
- package/commands/sdd/docs-update.md +48 -0
- package/commands/sdd/eval-review.md +32 -0
- package/commands/sdd/execute-phase.md +4 -0
- package/commands/sdd/explore.md +27 -0
- package/commands/sdd/fast.md +2 -2
- package/commands/sdd/from-sdd2.md +45 -0
- package/commands/sdd/help.md +2 -0
- package/commands/sdd/import.md +36 -0
- package/commands/sdd/intel.md +179 -0
- package/commands/sdd/join-discord.md +2 -1
- package/commands/sdd/manager.md +1 -0
- package/commands/sdd/map-codebase.md +3 -3
- package/commands/sdd/new-milestone.md +1 -1
- package/commands/sdd/new-project.md +5 -1
- package/commands/sdd/new-workspace.md +1 -1
- package/commands/sdd/next.md +2 -0
- package/commands/sdd/plan-milestone-gaps.md +2 -2
- package/commands/sdd/plan-phase.md +6 -1
- package/commands/sdd/plant-seed.md +1 -1
- package/commands/sdd/profile-user.md +1 -1
- package/commands/sdd/quick.md +5 -3
- package/commands/sdd/reapply-patches.md +230 -42
- package/commands/sdd/research-phase.md +3 -3
- package/commands/sdd/review-backlog.md +1 -0
- package/commands/sdd/review.md +6 -3
- package/commands/sdd/scan.md +26 -0
- package/commands/sdd/secure-phase.md +35 -0
- package/commands/sdd/ship.md +1 -1
- package/commands/sdd/thread.md +5 -5
- package/commands/sdd/undo.md +34 -0
- package/commands/sdd/verify-work.md +1 -1
- package/commands/sdd/workstreams.md +17 -11
- package/hooks/dist/sdd-check-update.js +33 -8
- package/hooks/dist/sdd-context-monitor.js +17 -8
- package/hooks/dist/sdd-phase-boundary.sh +27 -0
- package/hooks/dist/sdd-prompt-guard.js +1 -0
- package/hooks/dist/sdd-read-guard.js +82 -0
- package/hooks/dist/sdd-session-state.sh +33 -0
- package/hooks/dist/sdd-statusline.js +137 -15
- package/hooks/dist/sdd-validate-commit.sh +47 -0
- package/hooks/dist/sdd-workflow-guard.js +4 -4
- package/hooks/sdd-check-update.js +139 -0
- package/hooks/sdd-context-monitor.js +165 -0
- package/hooks/sdd-phase-boundary.sh +27 -0
- package/hooks/sdd-prompt-guard.js +97 -0
- package/hooks/sdd-read-guard.js +82 -0
- package/hooks/sdd-session-state.sh +33 -0
- package/hooks/sdd-statusline.js +241 -0
- package/hooks/sdd-validate-commit.sh +47 -0
- package/hooks/sdd-workflow-guard.js +94 -0
- package/package.json +3 -3
- package/scripts/build-hooks.js +18 -7
- package/scripts/prompt-injection-scan.sh +1 -0
- package/scripts/rebrand-gsd-to-sdd.sh +221 -220
- package/scripts/run-tests.cjs +5 -1
- package/scripts/sync-upstream.sh +1 -1
- package/sdd/bin/lib/commands.cjs +79 -17
- package/sdd/bin/lib/config.cjs +90 -48
- package/sdd/bin/lib/core.cjs +452 -87
- package/sdd/bin/lib/docs.cjs +267 -0
- package/sdd/bin/lib/frontmatter.cjs +381 -336
- package/sdd/bin/lib/init.cjs +110 -16
- package/sdd/bin/lib/intel.cjs +660 -0
- package/sdd/bin/lib/learnings.cjs +378 -0
- package/sdd/bin/lib/milestone.cjs +42 -11
- package/sdd/bin/lib/model-profiles.cjs +17 -15
- package/sdd/bin/lib/phase.cjs +367 -288
- package/sdd/bin/lib/profile-output.cjs +106 -10
- package/sdd/bin/lib/roadmap.cjs +146 -115
- package/sdd/bin/lib/schema-detect.cjs +238 -0
- package/sdd/bin/lib/sdd2-import.cjs +511 -0
- package/sdd/bin/lib/security.cjs +124 -3
- package/sdd/bin/lib/state.cjs +648 -264
- package/sdd/bin/lib/template.cjs +8 -4
- package/sdd/bin/lib/verify.cjs +209 -28
- package/sdd/bin/lib/workstream.cjs +7 -3
- package/sdd/bin/sdd-tools.cjs +184 -12
- package/sdd/contexts/dev.md +21 -0
- package/sdd/contexts/research.md +22 -0
- package/sdd/contexts/review.md +22 -0
- package/sdd/references/agent-contracts.md +79 -0
- package/sdd/references/ai-evals.md +156 -0
- package/sdd/references/ai-frameworks.md +186 -0
- package/sdd/references/artifact-types.md +113 -0
- package/sdd/references/common-bug-patterns.md +114 -0
- package/sdd/references/context-budget.md +49 -0
- package/sdd/references/continuation-format.md +25 -25
- package/sdd/references/domain-probes.md +125 -0
- package/sdd/references/few-shot-examples/plan-checker.md +73 -0
- package/sdd/references/few-shot-examples/verifier.md +109 -0
- package/sdd/references/gate-prompts.md +100 -0
- package/sdd/references/gates.md +70 -0
- package/sdd/references/git-integration.md +1 -1
- package/sdd/references/ios-scaffold.md +123 -0
- package/sdd/references/model-profile-resolution.md +2 -0
- package/sdd/references/model-profiles.md +24 -18
- package/sdd/references/planner-gap-closure.md +62 -0
- package/sdd/references/planner-reviews.md +39 -0
- package/sdd/references/planner-revision.md +87 -0
- package/sdd/references/planning-config.md +252 -0
- package/sdd/references/revision-loop.md +97 -0
- package/sdd/references/thinking-models-debug.md +44 -0
- package/sdd/references/thinking-models-execution.md +50 -0
- package/sdd/references/thinking-models-planning.md +62 -0
- package/sdd/references/thinking-models-research.md +50 -0
- package/sdd/references/thinking-models-verification.md +55 -0
- package/sdd/references/thinking-partner.md +96 -0
- package/sdd/references/ui-brand.md +4 -4
- package/sdd/references/universal-anti-patterns.md +63 -0
- package/sdd/references/verification-overrides.md +227 -0
- package/sdd/references/workstream-flag.md +56 -3
- package/sdd/templates/AI-SPEC.md +246 -0
- package/sdd/templates/DEBUG.md +1 -1
- package/sdd/templates/SECURITY.md +61 -0
- package/sdd/templates/UAT.md +4 -4
- package/sdd/templates/VALIDATION.md +4 -4
- package/sdd/templates/claude-md.md +32 -9
- package/sdd/templates/config.json +4 -0
- package/sdd/templates/debug-subagent-prompt.md +1 -1
- package/sdd/templates/dev-preferences.md +1 -1
- package/sdd/templates/discovery.md +2 -2
- package/sdd/templates/phase-prompt.md +1 -1
- package/sdd/templates/planner-subagent-prompt.md +3 -3
- package/sdd/templates/project.md +1 -1
- package/sdd/templates/research.md +1 -1
- package/sdd/templates/state.md +2 -2
- package/sdd/workflows/add-phase.md +8 -8
- package/sdd/workflows/add-tests.md +12 -9
- package/sdd/workflows/add-todo.md +5 -3
- package/sdd/workflows/ai-integration-phase.md +284 -0
- package/sdd/workflows/analyze-dependencies.md +96 -0
- package/sdd/workflows/audit-fix.md +157 -0
- package/sdd/workflows/audit-milestone.md +11 -11
- package/sdd/workflows/audit-uat.md +2 -2
- package/sdd/workflows/autonomous.md +195 -27
- package/sdd/workflows/check-todos.md +12 -10
- package/sdd/workflows/cleanup.md +2 -0
- package/sdd/workflows/code-review-fix.md +497 -0
- package/sdd/workflows/code-review.md +515 -0
- package/sdd/workflows/complete-milestone.md +56 -22
- package/sdd/workflows/diagnose-issues.md +10 -3
- package/sdd/workflows/discovery-phase.md +5 -3
- package/sdd/workflows/discuss-phase-assumptions.md +24 -6
- package/sdd/workflows/discuss-phase-power.md +291 -0
- package/sdd/workflows/discuss-phase.md +173 -21
- package/sdd/workflows/do.md +23 -21
- package/sdd/workflows/docs-update.md +1155 -0
- package/sdd/workflows/eval-review.md +155 -0
- package/sdd/workflows/execute-phase.md +594 -38
- package/sdd/workflows/execute-plan.md +67 -96
- package/sdd/workflows/explore.md +139 -0
- package/sdd/workflows/fast.md +5 -5
- package/sdd/workflows/forensics.md +2 -2
- package/sdd/workflows/health.md +4 -4
- package/sdd/workflows/help.md +122 -119
- package/sdd/workflows/import.md +276 -0
- package/sdd/workflows/inbox.md +387 -0
- package/sdd/workflows/insert-phase.md +7 -7
- package/sdd/workflows/list-phase-assumptions.md +4 -4
- package/sdd/workflows/list-workspaces.md +2 -2
- package/sdd/workflows/manager.md +35 -32
- package/sdd/workflows/map-codebase.md +7 -5
- package/sdd/workflows/milestone-summary.md +2 -2
- package/sdd/workflows/new-milestone.md +17 -9
- package/sdd/workflows/new-project.md +50 -25
- package/sdd/workflows/new-workspace.md +7 -5
- package/sdd/workflows/next.md +67 -11
- package/sdd/workflows/note.md +9 -7
- package/sdd/workflows/pause-work.md +75 -12
- package/sdd/workflows/plan-milestone-gaps.md +8 -8
- package/sdd/workflows/plan-phase.md +294 -42
- package/sdd/workflows/plant-seed.md +6 -3
- package/sdd/workflows/pr-branch.md +42 -14
- package/sdd/workflows/profile-user.md +9 -7
- package/sdd/workflows/progress.md +45 -45
- package/sdd/workflows/quick.md +195 -47
- package/sdd/workflows/remove-phase.md +6 -6
- package/sdd/workflows/remove-workspace.md +3 -1
- package/sdd/workflows/research-phase.md +2 -2
- package/sdd/workflows/resume-project.md +12 -12
- package/sdd/workflows/review.md +109 -9
- package/sdd/workflows/scan.md +102 -0
- package/sdd/workflows/secure-phase.md +166 -0
- package/sdd/workflows/session-report.md +2 -2
- package/sdd/workflows/settings.md +38 -12
- package/sdd/workflows/ship.md +21 -9
- package/sdd/workflows/stats.md +1 -1
- package/sdd/workflows/transition.md +23 -23
- package/sdd/workflows/ui-phase.md +15 -7
- package/sdd/workflows/ui-review.md +29 -4
- package/sdd/workflows/undo.md +314 -0
- package/sdd/workflows/update.md +171 -20
- package/sdd/workflows/validate-phase.md +6 -4
- package/sdd/workflows/verify-phase.md +210 -6
- package/sdd/workflows/verify-work.md +83 -9
- package/sdd/commands/sdd/workstreams.md +0 -63
|
@@ -11,7 +11,7 @@ const { spawn } = require('child_process');
|
|
|
11
11
|
const homeDir = os.homedir();
|
|
12
12
|
const cwd = process.cwd();
|
|
13
13
|
|
|
14
|
-
// Detect runtime config directory (supports Claude, OpenCode, Gemini)
|
|
14
|
+
// Detect runtime config directory (supports Claude, OpenCode, Kilo, Gemini)
|
|
15
15
|
// Respects CLAUDE_CONFIG_DIR for custom config directory setups
|
|
16
16
|
function detectConfigDir(baseDir) {
|
|
17
17
|
// Check env override first (supports multi-account setups)
|
|
@@ -19,7 +19,7 @@ function detectConfigDir(baseDir) {
|
|
|
19
19
|
if (envDir && fs.existsSync(path.join(envDir, 'sdd', 'VERSION'))) {
|
|
20
20
|
return envDir;
|
|
21
21
|
}
|
|
22
|
-
for (const dir of ['.config/
|
|
22
|
+
for (const dir of ['.claude', '.gemini', '.config/kilo', '.kilo', '.config/opencode', '.opencode']) {
|
|
23
23
|
if (fs.existsSync(path.join(baseDir, dir, 'sdd', 'VERSION'))) {
|
|
24
24
|
return path.join(baseDir, dir);
|
|
25
25
|
}
|
|
@@ -29,7 +29,10 @@ function detectConfigDir(baseDir) {
|
|
|
29
29
|
|
|
30
30
|
const globalConfigDir = detectConfigDir(homeDir);
|
|
31
31
|
const projectConfigDir = detectConfigDir(cwd);
|
|
32
|
-
|
|
32
|
+
// Use a shared, tool-agnostic cache directory to avoid multi-runtime
|
|
33
|
+
// resolution mismatches where check-update writes to one runtime's cache
|
|
34
|
+
// but statusline reads from another (#1421).
|
|
35
|
+
const cacheDir = path.join(homeDir, '.cache', 'sdd');
|
|
33
36
|
const cacheFile = path.join(cacheDir, 'sdd-update-check.json');
|
|
34
37
|
|
|
35
38
|
// VERSION file locations (check project first, then global)
|
|
@@ -47,6 +50,18 @@ const child = spawn(process.execPath, ['-e', `
|
|
|
47
50
|
const path = require('path');
|
|
48
51
|
const { execSync } = require('child_process');
|
|
49
52
|
|
|
53
|
+
// Compare semver: true if a > b (a is strictly newer than b)
|
|
54
|
+
// Strips pre-release suffixes (e.g. '3-beta.1' → '3') to avoid NaN from Number()
|
|
55
|
+
function isNewer(a, b) {
|
|
56
|
+
const pa = (a || '').split('.').map(s => Number(s.replace(/-.*/, '')) || 0);
|
|
57
|
+
const pb = (b || '').split('.').map(s => Number(s.replace(/-.*/, '')) || 0);
|
|
58
|
+
for (let i = 0; i < 3; i++) {
|
|
59
|
+
if (pa[i] > pb[i]) return true;
|
|
60
|
+
if (pa[i] < pb[i]) return false;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
const cacheFile = ${JSON.stringify(cacheFile)};
|
|
51
66
|
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
|
|
52
67
|
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
|
|
@@ -65,20 +80,30 @@ const child = spawn(process.execPath, ['-e', `
|
|
|
65
80
|
} catch (e) {}
|
|
66
81
|
|
|
67
82
|
// Check for stale hooks — compare hook version headers against installed VERSION
|
|
68
|
-
// Hooks
|
|
83
|
+
// Hooks are installed at configDir/hooks/ (e.g. ~/.claude/hooks/) (#1421)
|
|
84
|
+
// Only check hooks that SDD currently ships — orphaned files from removed features
|
|
85
|
+
// (e.g., sdd-intel-*.js) must be ignored to avoid permanent stale warnings (#1750)
|
|
86
|
+
const MANAGED_HOOKS = [
|
|
87
|
+
'sdd-check-update.js',
|
|
88
|
+
'sdd-context-monitor.js',
|
|
89
|
+
'sdd-prompt-guard.js',
|
|
90
|
+
'sdd-read-guard.js',
|
|
91
|
+
'sdd-statusline.js',
|
|
92
|
+
'sdd-workflow-guard.js',
|
|
93
|
+
];
|
|
69
94
|
let staleHooks = [];
|
|
70
95
|
if (configDir) {
|
|
71
|
-
const hooksDir = path.join(configDir, '
|
|
96
|
+
const hooksDir = path.join(configDir, 'hooks');
|
|
72
97
|
try {
|
|
73
98
|
if (fs.existsSync(hooksDir)) {
|
|
74
|
-
const hookFiles = fs.readdirSync(hooksDir).filter(f =>
|
|
99
|
+
const hookFiles = fs.readdirSync(hooksDir).filter(f => MANAGED_HOOKS.includes(f));
|
|
75
100
|
for (const hookFile of hookFiles) {
|
|
76
101
|
try {
|
|
77
102
|
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');
|
|
78
103
|
const versionMatch = content.match(/\\/\\/ sdd-hook-version:\\s*(.+)/);
|
|
79
104
|
if (versionMatch) {
|
|
80
105
|
const hookVersion = versionMatch[1].trim();
|
|
81
|
-
if (hookVersion
|
|
106
|
+
if (isNewer(installed, hookVersion) && !hookVersion.includes('{{')) {
|
|
82
107
|
staleHooks.push({ file: hookFile, hookVersion, installedVersion: installed });
|
|
83
108
|
}
|
|
84
109
|
} else {
|
|
@@ -97,7 +122,7 @@ const child = spawn(process.execPath, ['-e', `
|
|
|
97
122
|
} catch (e) {}
|
|
98
123
|
|
|
99
124
|
const result = {
|
|
100
|
-
update_available: latest &&
|
|
125
|
+
update_available: latest && isNewer(latest, installed),
|
|
101
126
|
installed,
|
|
102
127
|
latest: latest || 'unknown',
|
|
103
128
|
checked: Math.floor(Date.now() / 1000),
|
|
@@ -45,17 +45,26 @@ process.stdin.on('end', () => {
|
|
|
45
45
|
process.exit(0);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Reject session IDs that contain path traversal sequences or path separators.
|
|
49
|
+
// session_id is used to construct file paths in /tmp — an unsanitized value
|
|
50
|
+
// could escape the temp directory and read or write arbitrary files.
|
|
51
|
+
if (/[/\\]|\.\./.test(sessionId)) {
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if context warnings are disabled via config.
|
|
56
|
+
// Quick sentinel check: skip config read entirely for non-SDD projects (#P2.5).
|
|
49
57
|
const cwd = data.cwd || process.cwd();
|
|
50
|
-
const
|
|
51
|
-
if (fs.existsSync(
|
|
58
|
+
const planningDir = path.join(cwd, '.planning');
|
|
59
|
+
if (fs.existsSync(planningDir)) {
|
|
52
60
|
try {
|
|
61
|
+
const configPath = path.join(planningDir, 'config.json');
|
|
53
62
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
54
63
|
if (config.hooks?.context_warnings === false) {
|
|
55
64
|
process.exit(0);
|
|
56
65
|
}
|
|
57
66
|
} catch (e) {
|
|
58
|
-
// Ignore config parse errors
|
|
67
|
+
// Ignore config read/parse errors (config may not exist in .planning/)
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
|
|
@@ -117,22 +126,22 @@ process.stdin.on('end', () => {
|
|
|
117
126
|
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
118
127
|
|
|
119
128
|
// Detect if SDD is active (has .planning/STATE.md in working directory)
|
|
120
|
-
const
|
|
129
|
+
const isSddActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
|
|
121
130
|
|
|
122
131
|
// Build advisory warning message (never use imperative commands that
|
|
123
132
|
// override user preferences — see #884)
|
|
124
133
|
let message;
|
|
125
134
|
if (isCritical) {
|
|
126
|
-
message =
|
|
135
|
+
message = isSddActive
|
|
127
136
|
? `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
128
137
|
'Context is nearly exhausted. Do NOT start new complex work or write handoff files — ' +
|
|
129
138
|
'SDD state is already tracked in STATE.md. Inform the user so they can run ' +
|
|
130
|
-
'/sdd
|
|
139
|
+
'/sdd-pause-work at the next natural stopping point.'
|
|
131
140
|
: `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
132
141
|
'Context is nearly exhausted. Inform the user that context is low and ask how they ' +
|
|
133
142
|
'want to proceed. Do NOT autonomously save state or write handoff files unless the user asks.';
|
|
134
143
|
} else {
|
|
135
|
-
message =
|
|
144
|
+
message = isSddActive
|
|
136
145
|
? `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
|
|
137
146
|
'Context is getting limited. Avoid starting new complex work. If not between ' +
|
|
138
147
|
'defined plan steps, inform the user so they can prepare to pause.'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# sdd-phase-boundary.sh — PostToolUse hook: detect .planning/ file writes
|
|
3
|
+
# Outputs a reminder when planning files are modified outside normal workflow.
|
|
4
|
+
# Uses Node.js for JSON parsing (always available in SDD projects, no jq dependency).
|
|
5
|
+
#
|
|
6
|
+
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
|
7
|
+
# Enable with: "hooks": { "community": true } in .planning/config.json
|
|
8
|
+
|
|
9
|
+
# Check opt-in config — exit silently if not enabled
|
|
10
|
+
if [ -f .planning/config.json ]; then
|
|
11
|
+
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
|
12
|
+
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
|
13
|
+
else
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
# Extract file_path from JSON using Node (handles escaping correctly)
|
|
20
|
+
FILE=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{process.stdout.write(JSON.parse(d).tool_input?.file_path||'')}catch{}})" 2>/dev/null)
|
|
21
|
+
|
|
22
|
+
if [[ "$FILE" == *.planning/* ]] || [[ "$FILE" == .planning/* ]]; then
|
|
23
|
+
echo ".planning/ file modified: $FILE"
|
|
24
|
+
echo "Check: Should STATE.md be updated to reflect this change?"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
exit 0
|
|
@@ -22,6 +22,7 @@ const INJECTION_PATTERNS = [
|
|
|
22
22
|
/forget\s+(all\s+)?(your\s+)?instructions/i,
|
|
23
23
|
/override\s+(system|previous)\s+(prompt|instructions)/i,
|
|
24
24
|
/you\s+are\s+now\s+(?:a|an|the)\s+/i,
|
|
25
|
+
/act\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
|
|
25
26
|
/pretend\s+(?:you(?:'re| are)\s+|to\s+be\s+)/i,
|
|
26
27
|
/from\s+now\s+on,?\s+you\s+(?:are|will|should|must)/i,
|
|
27
28
|
/(?:print|output|reveal|show|display|repeat)\s+(?:your\s+)?(?:system\s+)?(?:prompt|instructions)/i,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// sdd-hook-version: {{SDD_VERSION}}
|
|
3
|
+
// SDD Read Guard — PreToolUse hook
|
|
4
|
+
// Injects advisory guidance when Write/Edit targets an existing file,
|
|
5
|
+
// reminding the model to Read the file first.
|
|
6
|
+
//
|
|
7
|
+
// Background: Non-Claude models (e.g. MiniMax M2.5 on OpenCode) don't
|
|
8
|
+
// natively follow the read-before-edit pattern. When they attempt to
|
|
9
|
+
// Write/Edit an existing file without reading it, the runtime rejects
|
|
10
|
+
// with "You must read file before overwriting it." The model retries
|
|
11
|
+
// without reading, creating an infinite loop that burns through usage.
|
|
12
|
+
//
|
|
13
|
+
// This hook prevents that loop by injecting clear guidance BEFORE the
|
|
14
|
+
// tool call reaches the runtime. The model sees the advisory and can
|
|
15
|
+
// issue a Read call on the next turn.
|
|
16
|
+
//
|
|
17
|
+
// Triggers on: Write and Edit tool calls
|
|
18
|
+
// Action: Advisory (does not block) — injects read-first guidance
|
|
19
|
+
// Only fires when the target file already exists on disk.
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
let input = '';
|
|
25
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
26
|
+
process.stdin.setEncoding('utf8');
|
|
27
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
28
|
+
process.stdin.on('end', () => {
|
|
29
|
+
clearTimeout(stdinTimeout);
|
|
30
|
+
try {
|
|
31
|
+
const data = JSON.parse(input);
|
|
32
|
+
const toolName = data.tool_name;
|
|
33
|
+
|
|
34
|
+
// Only intercept Write and Edit tool calls
|
|
35
|
+
if (toolName !== 'Write' && toolName !== 'Edit') {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Claude Code natively enforces read-before-edit — skip the advisory (#1984)
|
|
40
|
+
if (process.env.CLAUDE_SESSION_ID) {
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const filePath = data.tool_input?.file_path || '';
|
|
45
|
+
if (!filePath) {
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Only inject guidance when the file already exists.
|
|
50
|
+
// New files don't need a prior Read — the runtime allows creating them directly.
|
|
51
|
+
let fileExists = false;
|
|
52
|
+
try {
|
|
53
|
+
fs.accessSync(filePath, fs.constants.F_OK);
|
|
54
|
+
fileExists = true;
|
|
55
|
+
} catch {
|
|
56
|
+
// File does not exist — no guidance needed
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!fileExists) {
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const fileName = path.basename(filePath);
|
|
64
|
+
|
|
65
|
+
// Advisory guidance — does not block the operation
|
|
66
|
+
const output = {
|
|
67
|
+
hookSpecificOutput: {
|
|
68
|
+
hookEventName: 'PreToolUse',
|
|
69
|
+
additionalContext:
|
|
70
|
+
`READ-BEFORE-EDIT REMINDER: You are about to modify "${fileName}" which already exists. ` +
|
|
71
|
+
'If you have not already used the Read tool to read this file in the current session, ' +
|
|
72
|
+
'you MUST Read it first before editing. The runtime will reject edits to files that ' +
|
|
73
|
+
'have not been read. Use the Read tool on this file path, then retry your edit.',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
process.stdout.write(JSON.stringify(output));
|
|
78
|
+
} catch {
|
|
79
|
+
// Silent fail — never block tool execution
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# sdd-session-state.sh — SessionStart hook: inject project state reminder
|
|
3
|
+
# Outputs STATE.md head on every session start for orientation.
|
|
4
|
+
#
|
|
5
|
+
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
|
6
|
+
# Enable with: "hooks": { "community": true } in .planning/config.json
|
|
7
|
+
|
|
8
|
+
# Check opt-in config — exit silently if not enabled
|
|
9
|
+
if [ -f .planning/config.json ]; then
|
|
10
|
+
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
|
11
|
+
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
|
12
|
+
else
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
echo '## Project State Reminder'
|
|
17
|
+
echo ''
|
|
18
|
+
|
|
19
|
+
if [ -f .planning/STATE.md ]; then
|
|
20
|
+
echo 'STATE.md exists - check for blockers and current phase.'
|
|
21
|
+
head -20 .planning/STATE.md
|
|
22
|
+
else
|
|
23
|
+
echo 'No .planning/ found - suggest /sdd-new-project if starting new work.'
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo ''
|
|
27
|
+
|
|
28
|
+
if [ -f .planning/config.json ]; then
|
|
29
|
+
MODE=$(grep -o '"mode"[[:space:]]*:[[:space:]]*"[^"]*"' .planning/config.json 2>/dev/null || echo '"mode": "unknown"')
|
|
30
|
+
echo "Config: $MODE"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
exit 0
|
|
@@ -1,20 +1,120 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// sdd-hook-version: {{SDD_VERSION}}
|
|
3
3
|
// Claude Code Statusline - SDD Edition
|
|
4
|
-
// Shows: model | current task | directory | context usage
|
|
4
|
+
// Shows: model | current task (or SDD state) | directory | context usage
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
// --- SDD state reader -------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Walk up from dir looking for .planning/STATE.md.
|
|
14
|
+
* Returns parsed state object or null.
|
|
15
|
+
*/
|
|
16
|
+
function readSddState(dir) {
|
|
17
|
+
const home = os.homedir();
|
|
18
|
+
let current = dir;
|
|
19
|
+
for (let i = 0; i < 10; i++) {
|
|
20
|
+
const candidate = path.join(current, '.planning', 'STATE.md');
|
|
21
|
+
if (fs.existsSync(candidate)) {
|
|
22
|
+
try {
|
|
23
|
+
return parseStateMd(fs.readFileSync(candidate, 'utf8'));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const parent = path.dirname(current);
|
|
29
|
+
if (parent === current || current === home) break;
|
|
30
|
+
current = parent;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse STATE.md frontmatter + Phase line from body.
|
|
37
|
+
* Returns { status, milestone, milestoneName, phaseNum, phaseTotal, phaseName }
|
|
38
|
+
*/
|
|
39
|
+
function parseStateMd(content) {
|
|
40
|
+
const state = {};
|
|
41
|
+
|
|
42
|
+
// YAML frontmatter between --- markers
|
|
43
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
+
if (fmMatch) {
|
|
45
|
+
for (const line of fmMatch[1].split('\n')) {
|
|
46
|
+
const m = line.match(/^(\w+):\s*(.+)/);
|
|
47
|
+
if (!m) continue;
|
|
48
|
+
const [, key, val] = m;
|
|
49
|
+
const v = val.trim().replace(/^["']|["']$/g, '');
|
|
50
|
+
if (key === 'status') state.status = v === 'null' ? null : v;
|
|
51
|
+
if (key === 'milestone') state.milestone = v === 'null' ? null : v;
|
|
52
|
+
if (key === 'milestone_name') state.milestoneName = v === 'null' ? null : v;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Phase: N of M (name) or Phase: none active (...)
|
|
57
|
+
const phaseMatch = content.match(/^Phase:\s*(\d+)\s+of\s+(\d+)(?:\s+\(([^)]+)\))?/m);
|
|
58
|
+
if (phaseMatch) {
|
|
59
|
+
state.phaseNum = phaseMatch[1];
|
|
60
|
+
state.phaseTotal = phaseMatch[2];
|
|
61
|
+
state.phaseName = phaseMatch[3] || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fallback: parse Status: from body when frontmatter is absent
|
|
65
|
+
if (!state.status) {
|
|
66
|
+
const bodyStatus = content.match(/^Status:\s*(.+)/m);
|
|
67
|
+
if (bodyStatus) {
|
|
68
|
+
const raw = bodyStatus[1].trim().toLowerCase();
|
|
69
|
+
if (raw.includes('ready to plan') || raw.includes('planning')) state.status = 'planning';
|
|
70
|
+
else if (raw.includes('execut')) state.status = 'executing';
|
|
71
|
+
else if (raw.includes('complet') || raw.includes('archived')) state.status = 'complete';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return state;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Format SDD state into display string.
|
|
80
|
+
* Format: "v1.9 Code Quality · executing · fix-graphiti-deployment (1/5)"
|
|
81
|
+
* Gracefully degrades when parts are missing.
|
|
82
|
+
*/
|
|
83
|
+
function formatSddState(s) {
|
|
84
|
+
const parts = [];
|
|
85
|
+
|
|
86
|
+
// Milestone: version + name (skip placeholder "milestone")
|
|
87
|
+
if (s.milestone || s.milestoneName) {
|
|
88
|
+
const ver = s.milestone || '';
|
|
89
|
+
const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
|
|
90
|
+
const ms = [ver, name].filter(Boolean).join(' ');
|
|
91
|
+
if (ms) parts.push(ms);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Status
|
|
95
|
+
if (s.status) parts.push(s.status);
|
|
96
|
+
|
|
97
|
+
// Phase
|
|
98
|
+
if (s.phaseNum && s.phaseTotal) {
|
|
99
|
+
const phase = s.phaseName
|
|
100
|
+
? `${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
|
|
101
|
+
: `ph ${s.phaseNum}/${s.phaseTotal}`;
|
|
102
|
+
parts.push(phase);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return parts.join(' · ');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- stdin ------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
function runStatusline() {
|
|
111
|
+
let input = '';
|
|
112
|
+
// Timeout guard: if stdin doesn't close within 3s (e.g. pipe issues on
|
|
113
|
+
// Windows/Git Bash), exit silently instead of hanging. See #775.
|
|
114
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
115
|
+
process.stdin.setEncoding('utf8');
|
|
116
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
117
|
+
process.stdin.on('end', () => {
|
|
18
118
|
clearTimeout(stdinTimeout);
|
|
19
119
|
try {
|
|
20
120
|
const data = JSON.parse(input);
|
|
@@ -35,7 +135,10 @@ process.stdin.on('end', () => {
|
|
|
35
135
|
|
|
36
136
|
// Write context metrics to bridge file for the context-monitor PostToolUse hook.
|
|
37
137
|
// The monitor reads this file to inject agent-facing warnings when context is low.
|
|
38
|
-
|
|
138
|
+
// Reject session IDs with path separators or traversal sequences to prevent
|
|
139
|
+
// a malicious session_id from writing files outside the temp directory.
|
|
140
|
+
const sessionSafe = session && !/[/\\]|\.\./.test(session);
|
|
141
|
+
if (sessionSafe) {
|
|
39
142
|
try {
|
|
40
143
|
const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
|
|
41
144
|
const bridgeData = JSON.stringify({
|
|
@@ -91,25 +194,38 @@ process.stdin.on('end', () => {
|
|
|
91
194
|
}
|
|
92
195
|
}
|
|
93
196
|
|
|
197
|
+
// SDD state (milestone · status · phase) — shown when no todo task
|
|
198
|
+
const sddStateStr = task ? '' : formatSddState(readSddState(dir) || {});
|
|
199
|
+
|
|
94
200
|
// SDD update available?
|
|
201
|
+
// Check shared cache first (#1421), fall back to runtime-specific cache for
|
|
202
|
+
// backward compatibility with older sdd-check-update.js versions.
|
|
95
203
|
let sddUpdate = '';
|
|
96
|
-
const
|
|
204
|
+
const sharedCacheFile = path.join(homeDir, '.cache', 'sdd', 'sdd-update-check.json');
|
|
205
|
+
const legacyCacheFile = path.join(claudeDir, 'cache', 'sdd-update-check.json');
|
|
206
|
+
const cacheFile = fs.existsSync(sharedCacheFile) ? sharedCacheFile : legacyCacheFile;
|
|
97
207
|
if (fs.existsSync(cacheFile)) {
|
|
98
208
|
try {
|
|
99
209
|
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
100
210
|
if (cache.update_available) {
|
|
101
|
-
sddUpdate = '\x1b[33m⬆ /sdd
|
|
211
|
+
sddUpdate = '\x1b[33m⬆ /sdd-update\x1b[0m │ ';
|
|
102
212
|
}
|
|
103
213
|
if (cache.stale_hooks && cache.stale_hooks.length > 0) {
|
|
104
|
-
sddUpdate += '\x1b[31m⚠ stale hooks — run /sdd
|
|
214
|
+
sddUpdate += '\x1b[31m⚠ stale hooks — run /sdd-update\x1b[0m │ ';
|
|
105
215
|
}
|
|
106
216
|
} catch (e) {}
|
|
107
217
|
}
|
|
108
218
|
|
|
109
219
|
// Output
|
|
110
220
|
const dirname = path.basename(dir);
|
|
111
|
-
|
|
112
|
-
|
|
221
|
+
const middle = task
|
|
222
|
+
? `\x1b[1m${task}\x1b[0m`
|
|
223
|
+
: sddStateStr
|
|
224
|
+
? `\x1b[2m${sddStateStr}\x1b[0m`
|
|
225
|
+
: null;
|
|
226
|
+
|
|
227
|
+
if (middle) {
|
|
228
|
+
process.stdout.write(`${sddUpdate}\x1b[2m${model}\x1b[0m │ ${middle} │ \x1b[2m${dirname}\x1b[0m${ctx}`);
|
|
113
229
|
} else {
|
|
114
230
|
process.stdout.write(`${sddUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
|
|
115
231
|
}
|
|
@@ -117,3 +233,9 @@ process.stdin.on('end', () => {
|
|
|
117
233
|
// Silent fail - don't break statusline on parse errors
|
|
118
234
|
}
|
|
119
235
|
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Export helpers for unit tests. Harmless when run as a script.
|
|
239
|
+
module.exports = { readSddState, parseStateMd, formatSddState };
|
|
240
|
+
|
|
241
|
+
if (require.main === module) runStatusline();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# sdd-validate-commit.sh — PreToolUse hook: enforce Conventional Commits format
|
|
3
|
+
# Blocks git commit commands with non-conforming messages (exit 2).
|
|
4
|
+
# Allows conforming messages and all non-commit commands (exit 0).
|
|
5
|
+
# Uses Node.js for JSON parsing (always available in SDD projects, no jq dependency).
|
|
6
|
+
#
|
|
7
|
+
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
|
8
|
+
# Enable with: "hooks": { "community": true } in .planning/config.json
|
|
9
|
+
|
|
10
|
+
# Check opt-in config — exit silently if not enabled
|
|
11
|
+
if [ -f .planning/config.json ]; then
|
|
12
|
+
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
|
13
|
+
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
|
14
|
+
else
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
20
|
+
# Extract command from JSON using Node (handles escaping correctly, no jq needed)
|
|
21
|
+
CMD=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{process.stdout.write(JSON.parse(d).tool_input?.command||'')}catch{}})" 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
# Only check git commit commands
|
|
24
|
+
if [[ "$CMD" =~ ^git[[:space:]]+commit ]]; then
|
|
25
|
+
# Extract message from -m flag
|
|
26
|
+
MSG=""
|
|
27
|
+
if [[ "$CMD" =~ -m[[:space:]]+\"([^\"]+)\" ]]; then
|
|
28
|
+
MSG="${BASH_REMATCH[1]}"
|
|
29
|
+
elif [[ "$CMD" =~ -m[[:space:]]+\'([^\']+)\' ]]; then
|
|
30
|
+
MSG="${BASH_REMATCH[1]}"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if [ -n "$MSG" ]; then
|
|
34
|
+
SUBJECT=$(echo "$MSG" | head -1)
|
|
35
|
+
# Validate Conventional Commits format
|
|
36
|
+
if ! [[ "$SUBJECT" =~ ^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?:[[:space:]].+ ]]; then
|
|
37
|
+
echo '{"decision": "block", "reason": "Commit message must follow Conventional Commits: <type>(<scope>): <subject>. Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore. Subject must be <=72 chars, lowercase, imperative mood, no trailing period."}'
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
if [ ${#SUBJECT} -gt 72 ]; then
|
|
41
|
+
echo '{"decision": "block", "reason": "Commit subject must be 72 characters or less."}'
|
|
42
|
+
exit 2
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// sdd-hook-version: {{SDD_VERSION}}
|
|
3
3
|
// SDD Workflow Guard — PreToolUse hook
|
|
4
4
|
// Detects when Claude attempts file edits outside a SDD workflow context
|
|
5
|
-
// (no active /sdd
|
|
5
|
+
// (no active /sdd- skill or Task subagent) and injects an advisory warning.
|
|
6
6
|
//
|
|
7
7
|
// This is a SOFT guard — it advises, not blocks. The edit still proceeds.
|
|
8
|
-
// The warning nudges Claude to use /sdd
|
|
8
|
+
// The warning nudges Claude to use /sdd-quick or /sdd-fast instead of
|
|
9
9
|
// making direct edits that bypass state tracking.
|
|
10
10
|
//
|
|
11
11
|
// Enable via config: hooks.workflow_guard: true (default: false)
|
|
@@ -29,7 +29,7 @@ process.stdin.on('end', () => {
|
|
|
29
29
|
process.exit(0);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// Check if we're inside a SDD workflow (Task subagent or /sdd
|
|
32
|
+
// Check if we're inside a SDD workflow (Task subagent or /sdd- skill)
|
|
33
33
|
// Subagents have a session_id that differs from the parent
|
|
34
34
|
// and typically have a description field set by the orchestrator
|
|
35
35
|
if (data.tool_input?.is_subagent || data.session_type === 'task') {
|
|
@@ -80,7 +80,7 @@ process.stdin.on('end', () => {
|
|
|
80
80
|
hookEventName: "PreToolUse",
|
|
81
81
|
additionalContext: `⚠️ WORKFLOW ADVISORY: You're editing ${path.basename(filePath)} directly without a SDD command. ` +
|
|
82
82
|
'This edit will not be tracked in STATE.md or produce a SUMMARY.md. ' +
|
|
83
|
-
'Consider using /sdd
|
|
83
|
+
'Consider using /sdd-fast for trivial fixes or /sdd-quick for larger changes ' +
|
|
84
84
|
'to maintain project state tracking. ' +
|
|
85
85
|
'If this is intentional (e.g., user explicitly asked for a direct edit), proceed normally.'
|
|
86
86
|
}
|