@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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// sdd-hook-version: {{SDD_VERSION}}
|
|
3
|
+
// Claude Code Statusline - SDD Edition
|
|
4
|
+
// Shows: model | current task (or SDD state) | directory | context usage
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
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', () => {
|
|
118
|
+
clearTimeout(stdinTimeout);
|
|
119
|
+
try {
|
|
120
|
+
const data = JSON.parse(input);
|
|
121
|
+
const model = data.model?.display_name || 'Claude';
|
|
122
|
+
const dir = data.workspace?.current_dir || process.cwd();
|
|
123
|
+
const session = data.session_id || '';
|
|
124
|
+
const remaining = data.context_window?.remaining_percentage;
|
|
125
|
+
|
|
126
|
+
// Context window display (shows USED percentage scaled to usable context)
|
|
127
|
+
// Claude Code reserves ~16.5% for autocompact buffer, so usable context
|
|
128
|
+
// is 83.5% of the total window. We normalize to show 100% at that point.
|
|
129
|
+
const AUTO_COMPACT_BUFFER_PCT = 16.5;
|
|
130
|
+
let ctx = '';
|
|
131
|
+
if (remaining != null) {
|
|
132
|
+
// Normalize: subtract buffer from remaining, scale to usable range
|
|
133
|
+
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
|
|
134
|
+
const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
|
|
135
|
+
|
|
136
|
+
// Write context metrics to bridge file for the context-monitor PostToolUse hook.
|
|
137
|
+
// The monitor reads this file to inject agent-facing warnings when context is low.
|
|
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) {
|
|
142
|
+
try {
|
|
143
|
+
const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
|
|
144
|
+
const bridgeData = JSON.stringify({
|
|
145
|
+
session_id: session,
|
|
146
|
+
remaining_percentage: remaining,
|
|
147
|
+
used_pct: used,
|
|
148
|
+
timestamp: Math.floor(Date.now() / 1000)
|
|
149
|
+
});
|
|
150
|
+
fs.writeFileSync(bridgePath, bridgeData);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
// Silent fail -- bridge is best-effort, don't break statusline
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Build progress bar (10 segments)
|
|
157
|
+
const filled = Math.floor(used / 10);
|
|
158
|
+
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
|
|
159
|
+
|
|
160
|
+
// Color based on usable context thresholds
|
|
161
|
+
if (used < 50) {
|
|
162
|
+
ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
|
163
|
+
} else if (used < 65) {
|
|
164
|
+
ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
|
165
|
+
} else if (used < 80) {
|
|
166
|
+
ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
|
167
|
+
} else {
|
|
168
|
+
ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Current task from todos
|
|
173
|
+
let task = '';
|
|
174
|
+
const homeDir = os.homedir();
|
|
175
|
+
// Respect CLAUDE_CONFIG_DIR for custom config directory setups (#870)
|
|
176
|
+
const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, '.claude');
|
|
177
|
+
const todosDir = path.join(claudeDir, 'todos');
|
|
178
|
+
if (session && fs.existsSync(todosDir)) {
|
|
179
|
+
try {
|
|
180
|
+
const files = fs.readdirSync(todosDir)
|
|
181
|
+
.filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
|
|
182
|
+
.map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
|
|
183
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
184
|
+
|
|
185
|
+
if (files.length > 0) {
|
|
186
|
+
try {
|
|
187
|
+
const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
|
|
188
|
+
const inProgress = todos.find(t => t.status === 'in_progress');
|
|
189
|
+
if (inProgress) task = inProgress.activeForm || '';
|
|
190
|
+
} catch (e) {}
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
// Silently fail on file system errors - don't break statusline
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// SDD state (milestone · status · phase) — shown when no todo task
|
|
198
|
+
const sddStateStr = task ? '' : formatSddState(readSddState(dir) || {});
|
|
199
|
+
|
|
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.
|
|
203
|
+
let sddUpdate = '';
|
|
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;
|
|
207
|
+
if (fs.existsSync(cacheFile)) {
|
|
208
|
+
try {
|
|
209
|
+
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
210
|
+
if (cache.update_available) {
|
|
211
|
+
sddUpdate = '\x1b[33m⬆ /sdd-update\x1b[0m │ ';
|
|
212
|
+
}
|
|
213
|
+
if (cache.stale_hooks && cache.stale_hooks.length > 0) {
|
|
214
|
+
sddUpdate += '\x1b[31m⚠ stale hooks — run /sdd-update\x1b[0m │ ';
|
|
215
|
+
}
|
|
216
|
+
} catch (e) {}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Output
|
|
220
|
+
const dirname = path.basename(dir);
|
|
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}`);
|
|
229
|
+
} else {
|
|
230
|
+
process.stdout.write(`${sddUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
// Silent fail - don't break statusline on parse errors
|
|
234
|
+
}
|
|
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
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// sdd-hook-version: {{SDD_VERSION}}
|
|
3
|
+
// SDD Workflow Guard — PreToolUse hook
|
|
4
|
+
// Detects when Claude attempts file edits outside a SDD workflow context
|
|
5
|
+
// (no active /sdd- skill or Task subagent) and injects an advisory warning.
|
|
6
|
+
//
|
|
7
|
+
// This is a SOFT guard — it advises, not blocks. The edit still proceeds.
|
|
8
|
+
// The warning nudges Claude to use /sdd-quick or /sdd-fast instead of
|
|
9
|
+
// making direct edits that bypass state tracking.
|
|
10
|
+
//
|
|
11
|
+
// Enable via config: hooks.workflow_guard: true (default: false)
|
|
12
|
+
// Only triggers on Write/Edit tool calls to non-.planning/ files.
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
let input = '';
|
|
18
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
19
|
+
process.stdin.setEncoding('utf8');
|
|
20
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
21
|
+
process.stdin.on('end', () => {
|
|
22
|
+
clearTimeout(stdinTimeout);
|
|
23
|
+
try {
|
|
24
|
+
const data = JSON.parse(input);
|
|
25
|
+
const toolName = data.tool_name;
|
|
26
|
+
|
|
27
|
+
// Only guard Write and Edit tool calls
|
|
28
|
+
if (toolName !== 'Write' && toolName !== 'Edit') {
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check if we're inside a SDD workflow (Task subagent or /sdd- skill)
|
|
33
|
+
// Subagents have a session_id that differs from the parent
|
|
34
|
+
// and typically have a description field set by the orchestrator
|
|
35
|
+
if (data.tool_input?.is_subagent || data.session_type === 'task') {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check the file being edited
|
|
40
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
41
|
+
|
|
42
|
+
// Allow edits to .planning/ files (SDD state management)
|
|
43
|
+
if (filePath.includes('.planning/') || filePath.includes('.planning\\')) {
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Allow edits to common config/docs files that don't need SDD tracking
|
|
48
|
+
const allowedPatterns = [
|
|
49
|
+
/\.gitignore$/,
|
|
50
|
+
/\.env/,
|
|
51
|
+
/CLAUDE\.md$/,
|
|
52
|
+
/AGENTS\.md$/,
|
|
53
|
+
/GEMINI\.md$/,
|
|
54
|
+
/settings\.json$/,
|
|
55
|
+
];
|
|
56
|
+
if (allowedPatterns.some(p => p.test(filePath))) {
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if workflow guard is enabled
|
|
61
|
+
const cwd = data.cwd || process.cwd();
|
|
62
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
63
|
+
if (fs.existsSync(configPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
66
|
+
if (!config.hooks?.workflow_guard) {
|
|
67
|
+
process.exit(0); // Guard disabled (default)
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
process.exit(0); // No SDD project — don't guard
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If we get here: SDD project, guard enabled, file edit outside .planning/,
|
|
77
|
+
// not in a subagent context. Inject advisory warning.
|
|
78
|
+
const output = {
|
|
79
|
+
hookSpecificOutput: {
|
|
80
|
+
hookEventName: "PreToolUse",
|
|
81
|
+
additionalContext: `⚠️ WORKFLOW ADVISORY: You're editing ${path.basename(filePath)} directly without a SDD command. ` +
|
|
82
|
+
'This edit will not be tracked in STATE.md or produce a SUMMARY.md. ' +
|
|
83
|
+
'Consider using /sdd-fast for trivial fixes or /sdd-quick for larger changes ' +
|
|
84
|
+
'to maintain project state tracking. ' +
|
|
85
|
+
'If this is intentional (e.g., user explicitly asked for a direct edit), proceed normally.'
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
process.stdout.write(JSON.stringify(output));
|
|
90
|
+
} catch (e) {
|
|
91
|
+
// Silent fail — never block tool execution
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bhargavvc/sdd-cc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sdd-cc": "bin/install.js"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"commands",
|
|
11
11
|
"sdd",
|
|
12
12
|
"agents",
|
|
13
|
-
"hooks
|
|
13
|
+
"hooks",
|
|
14
14
|
"scripts"
|
|
15
15
|
],
|
|
16
16
|
"keywords": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"url": "https://github.com/bhargavvc/sdd-cc/issues"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
39
|
+
"node": ">=22.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"c8": "^11.0.0",
|
package/scripts/build-hooks.js
CHANGED
|
@@ -18,8 +18,13 @@ const HOOKS_TO_COPY = [
|
|
|
18
18
|
'sdd-check-update.js',
|
|
19
19
|
'sdd-context-monitor.js',
|
|
20
20
|
'sdd-prompt-guard.js',
|
|
21
|
+
'sdd-read-guard.js',
|
|
21
22
|
'sdd-statusline.js',
|
|
22
|
-
'sdd-workflow-guard.js'
|
|
23
|
+
'sdd-workflow-guard.js',
|
|
24
|
+
// Community hooks (bash, opt-in via .planning/config.json hooks.community)
|
|
25
|
+
'sdd-session-state.sh',
|
|
26
|
+
'sdd-validate-commit.sh',
|
|
27
|
+
'sdd-phase-boundary.sh'
|
|
23
28
|
];
|
|
24
29
|
|
|
25
30
|
/**
|
|
@@ -59,16 +64,22 @@ function build() {
|
|
|
59
64
|
continue;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
// Validate syntax before copying
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
// Validate JS syntax before copying (.sh files skip — not Node.js)
|
|
68
|
+
if (hook.endsWith('.js')) {
|
|
69
|
+
const syntaxError = validateSyntax(src);
|
|
70
|
+
if (syntaxError) {
|
|
71
|
+
console.error(`\x1b[31m✗ ${hook}: SyntaxError — ${syntaxError}\x1b[0m`);
|
|
72
|
+
hasErrors = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
console.log(`\x1b[32m✓\x1b[0m Copying ${hook}...`);
|
|
71
78
|
fs.copyFileSync(src, dest);
|
|
79
|
+
// Preserve executable bit for shell scripts
|
|
80
|
+
if (hook.endsWith('.sh')) {
|
|
81
|
+
try { fs.chmodSync(dest, 0o755); } catch (e) { /* Windows */ }
|
|
82
|
+
}
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
if (hasErrors) {
|