agentic-sdlc-wizard 1.15.0 → 1.18.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 +26 -0
- package/CLAUDE_CODE_SDLC_WIZARD.md +231 -156
- package/README.md +51 -35
- package/cli/bin/sdlc-wizard.js +14 -3
- package/cli/init.js +202 -10
- package/cli/templates/hooks/instructions-loaded-check.sh +1 -1
- package/cli/templates/hooks/sdlc-prompt-check.sh +16 -5
- package/cli/templates/skills/sdlc/SKILL.md +159 -30
- package/cli/templates/skills/setup/SKILL.md +176 -0
- package/cli/templates/skills/update/SKILL.md +141 -0
- package/package.json +1 -1
- package/cli/templates/skills/testing/SKILL.md +0 -97
package/README.md
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
# Claude Code SDLC Wizard
|
|
2
2
|
|
|
3
|
-
A **self-evolving SDLC enforcement system for AI coding agents**. Makes Claude plan before coding, test before shipping, and ask when uncertain. Measures itself getting better over time.
|
|
3
|
+
A **self-evolving Software Development Life Cycle (SDLC) enforcement system for AI coding agents**. Makes Claude plan before coding, test before shipping, and ask when uncertain. Measures itself getting better over time.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
**Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview)** (Anthropic's CLI for Claude).
|
|
8
|
+
|
|
9
|
+
Run from your terminal or from inside Claude Code (`!` prefix):
|
|
10
|
+
```bash
|
|
11
|
+
npx agentic-sdlc-wizard init
|
|
12
|
+
```
|
|
13
|
+
Then start (or restart) Claude Code — type `/exit` then `claude` to reload hooks. Setup auto-invokes on first prompt — Claude reads the wizard doc, scans your project, and generates bespoke CLAUDE.md, SDLC.md, TESTING.md, and ARCHITECTURE.md. No manual commands needed.
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary>Alternative install methods</summary>
|
|
17
|
+
|
|
18
|
+
**From GitHub (no npm needed):**
|
|
19
|
+
```bash
|
|
20
|
+
npx github:BaseInfinity/agentic-ai-sdlc-wizard init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Manual:** Download `CLAUDE_CODE_SDLC_WIZARD.md` to your project and tell Claude `Run the SDLC wizard setup`.
|
|
24
|
+
</details>
|
|
25
|
+
|
|
26
|
+
<details>
|
|
27
|
+
<summary>Health check & updates</summary>
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx agentic-sdlc-wizard check # Human-readable
|
|
31
|
+
npx agentic-sdlc-wizard check --json # Machine-readable (CI-friendly)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Reports MATCH / CUSTOMIZED / MISSING / DRIFT for every installed file. Exits non-zero on MISSING or DRIFT — use in CI to catch setup regressions.
|
|
35
|
+
|
|
36
|
+
**Check for content updates:** Tell Claude `Check if the SDLC wizard has updates` — it reads [CHANGELOG.md](CHANGELOG.md), shows what's new, and offers to apply changes.
|
|
37
|
+
</details>
|
|
38
|
+
|
|
39
|
+
## Why Use This
|
|
40
|
+
|
|
41
|
+
You want Claude Code to follow engineering discipline automatically:
|
|
42
|
+
- **Plan before coding** (not guess-and-check)
|
|
43
|
+
- **Write tests first** (TDD enforced via hooks)
|
|
44
|
+
- **State confidence** (LOW = ask user, don't guess)
|
|
45
|
+
- **Track work visibly** (TaskCreate)
|
|
46
|
+
- **Self-review before presenting**
|
|
47
|
+
- **Prove it's better** (use native features unless you prove custom wins)
|
|
48
|
+
|
|
49
|
+
The wizard auto-detects your stack (package.json, test framework, deployment targets) and generates bespoke hooks + skills + docs. CI validates the generated assets; cross-stack setup-path E2E is on the [roadmap](ROADMAP.md).
|
|
4
50
|
|
|
5
51
|
## What This Actually Is
|
|
6
52
|
|
|
@@ -21,25 +67,13 @@ Layer 3: SCORING ENGINE
|
|
|
21
67
|
|
|
22
68
|
Layer 2: ENFORCEMENT
|
|
23
69
|
Hooks fire every interaction (~100 tokens).
|
|
24
|
-
PreToolUse
|
|
70
|
+
PreToolUse reminds Claude to write tests first.
|
|
25
71
|
|
|
26
72
|
Layer 1: PHILOSOPHY
|
|
27
73
|
The wizard document. KISS. TDD. Confidence levels.
|
|
28
74
|
Copy it, run setup, get a bespoke SDLC.
|
|
29
75
|
```
|
|
30
76
|
|
|
31
|
-
## Why Someone Uses This
|
|
32
|
-
|
|
33
|
-
You want Claude Code to follow engineering discipline automatically:
|
|
34
|
-
- **Plan before coding** (not guess-and-check)
|
|
35
|
-
- **Write tests first** (TDD enforced via hooks)
|
|
36
|
-
- **State confidence** (LOW = ask user, don't guess)
|
|
37
|
-
- **Track work visibly** (TaskCreate)
|
|
38
|
-
- **Self-review before presenting**
|
|
39
|
-
- **Prove it's better** (use native features unless you prove custom wins)
|
|
40
|
-
|
|
41
|
-
The wizard auto-detects your stack (package.json, test framework, deployment targets) and generates bespoke hooks + skills + docs. CI validates the generated assets; cross-stack setup-path E2E is on the [roadmap](ROADMAP.md).
|
|
42
|
-
|
|
43
77
|
## What Makes This Different
|
|
44
78
|
|
|
45
79
|
| Capability | What It Does |
|
|
@@ -48,7 +82,7 @@ The wizard auto-detects your stack (package.json, test framework, deployment tar
|
|
|
48
82
|
| **Before/after A/B testing** | Compares wizard changes against a baseline with 95% confidence intervals to prove improvements aren't noise |
|
|
49
83
|
| **SDP normalization** | Separates "the model had a bad day" from "our SDLC broke" by cross-referencing external benchmarks |
|
|
50
84
|
| **CUSUM drift detection** | Catches gradual quality decay over time — borrowed from manufacturing quality control |
|
|
51
|
-
| **Pre-tool TDD hooks** |
|
|
85
|
+
| **Pre-tool TDD hooks** | Before source edits, a hook reminds Claude to write tests first. CI scoring checks whether it actually followed TDD |
|
|
52
86
|
| **Self-evolving loop** | Weekly/monthly external research + CI friction signals from self-heal — you approve, the system gets better |
|
|
53
87
|
|
|
54
88
|
## How It Works
|
|
@@ -79,24 +113,6 @@ CI/CD PIPELINE
|
|
|
79
113
|
- Model-aware: SDP adjusts for external conditions
|
|
80
114
|
```
|
|
81
115
|
|
|
82
|
-
## Using It
|
|
83
|
-
|
|
84
|
-
**One command (recommended):**
|
|
85
|
-
```bash
|
|
86
|
-
npx agentic-sdlc-wizard init
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Installs hooks, skills, settings, and the wizard doc. Then start Claude Code and tell it "Run the SDLC wizard setup" — it'll scan your project and generate bespoke CLAUDE.md, SDLC.md, TESTING.md, and ARCHITECTURE.md.
|
|
90
|
-
|
|
91
|
-
**From GitHub (no npm needed):**
|
|
92
|
-
```bash
|
|
93
|
-
npx github:BaseInfinity/agentic-ai-sdlc-wizard init
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Manual:** Download `CLAUDE_CODE_SDLC_WIZARD.md` to your project and follow setup instructions inside.
|
|
97
|
-
|
|
98
|
-
**Check for updates:** Ask Claude "Check if the SDLC wizard has updates" — it reads [CHANGELOG.md](CHANGELOG.md), shows what's new, and offers to apply changes (opt-in each).
|
|
99
|
-
|
|
100
116
|
## Self-Evolving System
|
|
101
117
|
|
|
102
118
|
| Cadence | Source | Action |
|
|
@@ -148,7 +164,7 @@ Tests aren't just validation - they're the foundation everything else builds on.
|
|
|
148
164
|
|--------|---------|-------|
|
|
149
165
|
| `claude-md-management` | **Required** - CLAUDE.md maintenance | CLAUDE.md only |
|
|
150
166
|
| `claude-code-setup` | Recommends automations | Recommendations |
|
|
151
|
-
| `code-review` | PR review (optional) | PRs
|
|
167
|
+
| `code-review` | Local self-review and PR review (optional) | Local + PRs |
|
|
152
168
|
|
|
153
169
|
## Prove It's Better
|
|
154
170
|
|
|
@@ -170,7 +186,7 @@ This isn't the only Claude Code SDLC tool. Here's an honest comparison:
|
|
|
170
186
|
|--------|------------|----------------------|-------------|
|
|
171
187
|
| **Focus** | SDLC enforcement + measurement | Agent performance optimization | Plugin marketplace |
|
|
172
188
|
| **Hooks** | 3 (SDLC, TDD, instructions) | 12+ (dev blocker, prettier, etc.) | Webhook watcher |
|
|
173
|
-
| **Skills** | 2 (/sdlc, /
|
|
189
|
+
| **Skills** | 2 (/sdlc, /setup) | 80+ domain-specific | 13 slash commands |
|
|
174
190
|
| **Evaluation** | 95% CI, CUSUM, SDP, Tier 1/2 | Configuration testing | skilltest framework |
|
|
175
191
|
| **Self-healing** | CI auto-fix + re-trigger | No | No |
|
|
176
192
|
| **Auto-updates** | Weekly CC + community scan | No | No |
|
package/cli/bin/sdlc-wizard.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const { version } = require('../../package.json');
|
|
5
|
-
const { init } = require('../init');
|
|
5
|
+
const { init, check } = require('../init');
|
|
6
6
|
|
|
7
7
|
const args = process.argv.slice(2);
|
|
8
8
|
|
|
9
9
|
const flags = {
|
|
10
10
|
force: args.includes('--force'),
|
|
11
11
|
dryRun: args.includes('--dry-run'),
|
|
12
|
+
json: args.includes('--json'),
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const command = args.find((a) => !a.startsWith('--'));
|
|
@@ -24,10 +25,12 @@ if (args.includes('--help') || args.includes('-h') || !command) {
|
|
|
24
25
|
|
|
25
26
|
Usage:
|
|
26
27
|
sdlc-wizard init [options] Install SDLC wizard into current directory
|
|
28
|
+
sdlc-wizard check [options] Check installation health and updates
|
|
27
29
|
|
|
28
30
|
Options:
|
|
29
|
-
--force Overwrite existing files
|
|
30
|
-
--dry-run Preview changes without writing
|
|
31
|
+
--force Overwrite existing files (init only)
|
|
32
|
+
--dry-run Preview changes without writing (init only)
|
|
33
|
+
--json Output as JSON (check only)
|
|
31
34
|
--version Show version
|
|
32
35
|
--help Show this help
|
|
33
36
|
`.trim());
|
|
@@ -42,6 +45,14 @@ if (command === 'init') {
|
|
|
42
45
|
console.error(`Error: ${err.message}`);
|
|
43
46
|
process.exit(1);
|
|
44
47
|
}
|
|
48
|
+
} else if (command === 'check') {
|
|
49
|
+
try {
|
|
50
|
+
const { hasDrift } = check(process.cwd(), { json: flags.json });
|
|
51
|
+
process.exit(hasDrift ? 1 : 0);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(`Error: ${err.message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
45
56
|
} else {
|
|
46
57
|
console.error(`Unknown command: ${command}`);
|
|
47
58
|
console.error('Run "sdlc-wizard --help" for usage.');
|
package/cli/init.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
|
|
6
7
|
const RESET = '\x1b[0m';
|
|
8
|
+
const RED = '\x1b[31m';
|
|
7
9
|
const GREEN = '\x1b[32m';
|
|
8
10
|
const YELLOW = '\x1b[33m';
|
|
11
|
+
const MAGENTA = '\x1b[35m';
|
|
9
12
|
const CYAN = '\x1b[36m';
|
|
10
13
|
|
|
11
14
|
const TEMPLATES_DIR = path.join(__dirname, 'templates');
|
|
@@ -17,19 +20,86 @@ const FILES = [
|
|
|
17
20
|
{ src: 'hooks/tdd-pretool-check.sh', dest: '.claude/hooks/tdd-pretool-check.sh', executable: true },
|
|
18
21
|
{ src: 'hooks/instructions-loaded-check.sh', dest: '.claude/hooks/instructions-loaded-check.sh', executable: true },
|
|
19
22
|
{ src: 'skills/sdlc/SKILL.md', dest: '.claude/skills/sdlc/SKILL.md' },
|
|
20
|
-
{ src: 'skills/
|
|
23
|
+
{ src: 'skills/setup/SKILL.md', dest: '.claude/skills/setup/SKILL.md' },
|
|
24
|
+
{ src: 'skills/update/SKILL.md', dest: '.claude/skills/update/SKILL.md' },
|
|
21
25
|
];
|
|
22
26
|
|
|
27
|
+
const WIZARD_HOOK_MARKERS = FILES
|
|
28
|
+
.filter((f) => f.executable && f.dest.startsWith('.claude/hooks/'))
|
|
29
|
+
.map((f) => path.basename(f.src));
|
|
30
|
+
|
|
23
31
|
const GITIGNORE_ENTRIES = ['.claude/plans/', '.claude/settings.local.json'];
|
|
24
32
|
|
|
33
|
+
// Paths from previous versions that should be removed on upgrade
|
|
34
|
+
const OBSOLETE_PATHS = [
|
|
35
|
+
'.claude/skills/testing', // consolidated into /sdlc in v1.17.0
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function isWizardHookEntry(hookEntry) {
|
|
39
|
+
if (!hookEntry || !hookEntry.hooks) return false;
|
|
40
|
+
return hookEntry.hooks.some((h) =>
|
|
41
|
+
WIZARD_HOOK_MARKERS.some((marker) => h.command && h.command.includes(marker))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function mergeSettings(existingPath, templatePath, force) {
|
|
46
|
+
try {
|
|
47
|
+
const existing = JSON.parse(fs.readFileSync(existingPath, 'utf8'));
|
|
48
|
+
const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
|
|
49
|
+
|
|
50
|
+
if (!existing.hooks) existing.hooks = {};
|
|
51
|
+
|
|
52
|
+
for (const [event, templateEntries] of Object.entries(template.hooks || {})) {
|
|
53
|
+
if (!existing.hooks[event]) {
|
|
54
|
+
existing.hooks[event] = templateEntries;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Each template event has exactly one hook entry
|
|
59
|
+
const templateEntry = templateEntries[0];
|
|
60
|
+
const existingIdx = existing.hooks[event].findIndex(isWizardHookEntry);
|
|
61
|
+
|
|
62
|
+
if (existingIdx === -1) {
|
|
63
|
+
existing.hooks[event].push(templateEntry);
|
|
64
|
+
} else if (force) {
|
|
65
|
+
existing.hooks[event][existingIdx] = templateEntry;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const merged = JSON.stringify(existing, null, 2) + '\n';
|
|
70
|
+
const original = fs.readFileSync(existingPath, 'utf8');
|
|
71
|
+
return merged === original ? null : merged;
|
|
72
|
+
} catch (_) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
25
77
|
function planOperations(targetDir, { force }) {
|
|
26
78
|
const ops = [];
|
|
27
79
|
|
|
28
80
|
for (const file of FILES) {
|
|
29
81
|
const destPath = path.join(targetDir, file.dest);
|
|
82
|
+
const srcPath = path.join(TEMPLATES_DIR, file.src);
|
|
30
83
|
const exists = fs.existsSync(destPath);
|
|
84
|
+
|
|
85
|
+
if (exists && file.dest === '.claude/settings.json') {
|
|
86
|
+
const merged = mergeSettings(destPath, srcPath, force);
|
|
87
|
+
if (merged) {
|
|
88
|
+
ops.push({
|
|
89
|
+
src: srcPath,
|
|
90
|
+
dest: destPath,
|
|
91
|
+
relativeDest: file.dest,
|
|
92
|
+
action: 'MERGE',
|
|
93
|
+
mergedContent: merged,
|
|
94
|
+
executable: false,
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// Invalid JSON — fall through to normal SKIP/OVERWRITE
|
|
99
|
+
}
|
|
100
|
+
|
|
31
101
|
ops.push({
|
|
32
|
-
src:
|
|
102
|
+
src: srcPath,
|
|
33
103
|
dest: destPath,
|
|
34
104
|
relativeDest: file.dest,
|
|
35
105
|
action: exists ? (force ? 'OVERWRITE' : 'SKIP') : 'CREATE',
|
|
@@ -59,13 +129,31 @@ function executeOperations(ops) {
|
|
|
59
129
|
for (const op of ops) {
|
|
60
130
|
if (op.action === 'SKIP') continue;
|
|
61
131
|
ensureDir(op.dest);
|
|
62
|
-
|
|
132
|
+
if (op.action === 'MERGE') {
|
|
133
|
+
fs.writeFileSync(op.dest, op.mergedContent);
|
|
134
|
+
} else {
|
|
135
|
+
fs.copyFileSync(op.src, op.dest);
|
|
136
|
+
}
|
|
63
137
|
if (op.executable) {
|
|
64
138
|
fs.chmodSync(op.dest, 0o755);
|
|
65
139
|
}
|
|
66
140
|
}
|
|
67
141
|
}
|
|
68
142
|
|
|
143
|
+
function removeObsoletePaths(targetDir, { dryRun }) {
|
|
144
|
+
const removed = [];
|
|
145
|
+
for (const rel of OBSOLETE_PATHS) {
|
|
146
|
+
const fullPath = path.join(targetDir, rel);
|
|
147
|
+
if (fs.existsSync(fullPath)) {
|
|
148
|
+
if (!dryRun) {
|
|
149
|
+
fs.rmSync(fullPath, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
removed.push(rel);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return removed;
|
|
155
|
+
}
|
|
156
|
+
|
|
69
157
|
function updateGitignore(targetDir, { dryRun }) {
|
|
70
158
|
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
71
159
|
let content = '';
|
|
@@ -73,7 +161,8 @@ function updateGitignore(targetDir, { dryRun }) {
|
|
|
73
161
|
content = fs.readFileSync(gitignorePath, 'utf8');
|
|
74
162
|
}
|
|
75
163
|
|
|
76
|
-
const
|
|
164
|
+
const lines = content.split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'));
|
|
165
|
+
const toAdd = GITIGNORE_ENTRIES.filter((entry) => !lines.includes(entry));
|
|
77
166
|
if (toAdd.length === 0) return [];
|
|
78
167
|
|
|
79
168
|
if (!dryRun) {
|
|
@@ -86,7 +175,10 @@ function updateGitignore(targetDir, { dryRun }) {
|
|
|
86
175
|
|
|
87
176
|
function printOps(ops) {
|
|
88
177
|
for (const op of ops) {
|
|
89
|
-
const color = op.action === 'CREATE' ? GREEN
|
|
178
|
+
const color = op.action === 'CREATE' ? GREEN
|
|
179
|
+
: op.action === 'SKIP' ? YELLOW
|
|
180
|
+
: op.action === 'MERGE' ? MAGENTA
|
|
181
|
+
: CYAN;
|
|
90
182
|
console.log(` ${color}${op.action}${RESET} ${op.relativeDest}`);
|
|
91
183
|
}
|
|
92
184
|
}
|
|
@@ -97,6 +189,10 @@ function init(targetDir, { force = false, dryRun = false } = {}) {
|
|
|
97
189
|
if (dryRun) {
|
|
98
190
|
console.log('Dry run — no files will be written:\n');
|
|
99
191
|
printOps(ops);
|
|
192
|
+
const obsolete = removeObsoletePaths(targetDir, { dryRun: true });
|
|
193
|
+
for (const p of obsolete) {
|
|
194
|
+
console.log(` ${RED}REMOVE${RESET} ${p} (obsolete)`);
|
|
195
|
+
}
|
|
100
196
|
const gitignoreAdds = updateGitignore(targetDir, { dryRun: true });
|
|
101
197
|
if (gitignoreAdds.length > 0) {
|
|
102
198
|
console.log(` ${GREEN}APPEND${RESET} .gitignore (${gitignoreAdds.join(', ')})`);
|
|
@@ -107,7 +203,13 @@ function init(targetDir, { force = false, dryRun = false } = {}) {
|
|
|
107
203
|
console.log('');
|
|
108
204
|
printOps(ops);
|
|
109
205
|
|
|
110
|
-
|
|
206
|
+
// Always clean up obsolete paths, even when all managed files are SKIP
|
|
207
|
+
const obsolete = removeObsoletePaths(targetDir, { dryRun: false });
|
|
208
|
+
for (const p of obsolete) {
|
|
209
|
+
console.log(` ${RED}REMOVE${RESET} ${p} (obsolete)`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (ops.every((o) => o.action === 'SKIP') && obsolete.length === 0) {
|
|
111
213
|
console.log('\nAll files already exist. Use --force to overwrite.');
|
|
112
214
|
return true;
|
|
113
215
|
}
|
|
@@ -122,10 +224,14 @@ function init(targetDir, { force = false, dryRun = false } = {}) {
|
|
|
122
224
|
console.log(`
|
|
123
225
|
${GREEN}SDLC Wizard installed successfully!${RESET}
|
|
124
226
|
|
|
227
|
+
${YELLOW}Important:${RESET} Hooks and settings load at session start.
|
|
228
|
+
If Claude Code is already running, type ${CYAN}/exit${RESET} then ${CYAN}claude${RESET} to restart.
|
|
229
|
+
(Or ${CYAN}claude --continue${RESET} to keep your conversation history.)
|
|
230
|
+
|
|
125
231
|
Next steps:
|
|
126
|
-
1. Start Claude Code in this directory
|
|
127
|
-
2. Tell Claude
|
|
128
|
-
3. Claude
|
|
232
|
+
1. Start Claude Code in this directory (or restart if already running)
|
|
233
|
+
2. Tell Claude anything — setup auto-invokes when SDLC files are missing
|
|
234
|
+
3. Claude reads the wizard doc and creates CLAUDE.md, SDLC.md, TESTING.md, ARCHITECTURE.md
|
|
129
235
|
|
|
130
236
|
The wizard doc is at: CLAUDE_CODE_SDLC_WIZARD.md
|
|
131
237
|
`);
|
|
@@ -133,4 +239,90 @@ The wizard doc is at: CLAUDE_CODE_SDLC_WIZARD.md
|
|
|
133
239
|
return true;
|
|
134
240
|
}
|
|
135
241
|
|
|
136
|
-
|
|
242
|
+
function check(targetDir, { json = false } = {}) {
|
|
243
|
+
const results = [];
|
|
244
|
+
|
|
245
|
+
for (const file of FILES) {
|
|
246
|
+
const destPath = path.join(targetDir, file.dest);
|
|
247
|
+
const srcPath = path.join(TEMPLATES_DIR, file.src);
|
|
248
|
+
results.push(checkFile(srcPath, destPath, file.dest, file.executable || false));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const wizardDest = path.join(targetDir, 'CLAUDE_CODE_SDLC_WIZARD.md');
|
|
252
|
+
results.push(checkFile(WIZARD_DOC, wizardDest, 'CLAUDE_CODE_SDLC_WIZARD.md', false));
|
|
253
|
+
|
|
254
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
255
|
+
results.push(checkGitignore(gitignorePath));
|
|
256
|
+
|
|
257
|
+
let updateInfo = null;
|
|
258
|
+
try {
|
|
259
|
+
const { execSync } = require('child_process');
|
|
260
|
+
const latest = execSync('npm view agentic-sdlc-wizard version 2>/dev/null', {
|
|
261
|
+
encoding: 'utf8',
|
|
262
|
+
timeout: 5000,
|
|
263
|
+
}).trim();
|
|
264
|
+
const current = require('../package.json').version;
|
|
265
|
+
if (latest !== current) {
|
|
266
|
+
updateInfo = { current, latest };
|
|
267
|
+
}
|
|
268
|
+
} catch (_) {
|
|
269
|
+
// Offline or npm unavailable — skip update check
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const hasDrift = results.some((r) => r.status === 'MISSING' || r.status === 'DRIFT');
|
|
273
|
+
|
|
274
|
+
if (json) {
|
|
275
|
+
console.log(JSON.stringify({ files: results, update: updateInfo }, null, 2));
|
|
276
|
+
} else {
|
|
277
|
+
for (const r of results) {
|
|
278
|
+
const color = r.status === 'MATCH' ? GREEN : r.status === 'MISSING' ? RED : YELLOW;
|
|
279
|
+
console.log(` ${color}${r.status}${RESET} ${r.file}`);
|
|
280
|
+
if (r.details) console.log(` ${r.details}`);
|
|
281
|
+
}
|
|
282
|
+
if (updateInfo) {
|
|
283
|
+
console.log(`\n ${YELLOW}UPDATE${RESET} v${updateInfo.current} -> v${updateInfo.latest}`);
|
|
284
|
+
console.log(' Run: npx agentic-sdlc-wizard init --force');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return { results, updateInfo, hasDrift };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function checkFile(srcPath, destPath, relativeDest, shouldBeExecutable) {
|
|
292
|
+
if (!fs.existsSync(destPath)) {
|
|
293
|
+
return { file: relativeDest, status: 'MISSING' };
|
|
294
|
+
}
|
|
295
|
+
const srcHash = crypto.createHash('sha256').update(fs.readFileSync(srcPath)).digest('hex');
|
|
296
|
+
const destHash = crypto.createHash('sha256').update(fs.readFileSync(destPath)).digest('hex');
|
|
297
|
+
const result = {
|
|
298
|
+
file: relativeDest,
|
|
299
|
+
status: srcHash === destHash ? 'MATCH' : 'CUSTOMIZED',
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
if (shouldBeExecutable) {
|
|
303
|
+
try {
|
|
304
|
+
fs.accessSync(destPath, fs.constants.X_OK);
|
|
305
|
+
} catch (_) {
|
|
306
|
+
result.status = 'DRIFT';
|
|
307
|
+
result.details = 'Missing executable permission (chmod +x)';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function checkGitignore(gitignorePath) {
|
|
315
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
316
|
+
return { file: '.gitignore', status: 'MISSING', details: 'No .gitignore found' };
|
|
317
|
+
}
|
|
318
|
+
const lines = fs.readFileSync(gitignorePath, 'utf8').split('\n')
|
|
319
|
+
.map((l) => l.trim())
|
|
320
|
+
.filter((l) => l && !l.startsWith('#'));
|
|
321
|
+
const missing = GITIGNORE_ENTRIES.filter((e) => !lines.includes(e));
|
|
322
|
+
if (missing.length > 0) {
|
|
323
|
+
return { file: '.gitignore', status: 'DRIFT', details: `Missing entries: ${missing.join(', ')}` };
|
|
324
|
+
}
|
|
325
|
+
return { file: '.gitignore', status: 'MATCH' };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
module.exports = { init, check, planOperations, GITIGNORE_ENTRIES };
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Light SDLC hook - baseline reminder every prompt (~100 tokens)
|
|
3
|
-
# Full guidance in
|
|
3
|
+
# Full guidance in skill: .claude/skills/sdlc/
|
|
4
|
+
|
|
5
|
+
# Check if setup has been completed
|
|
6
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
7
|
+
if [ ! -s "$PROJECT_DIR/SDLC.md" ] || [ ! -s "$PROJECT_DIR/TESTING.md" ]; then
|
|
8
|
+
cat << 'SETUP'
|
|
9
|
+
SETUP NOT COMPLETE: SDLC.md and/or TESTING.md are missing.
|
|
10
|
+
|
|
11
|
+
MANDATORY FIRST ACTION: Invoke Skill tool, skill="setup-wizard"
|
|
12
|
+
Do NOT proceed with any other task until setup is complete.
|
|
13
|
+
Tell the user: "I need to run the SDLC setup wizard first to configure your project."
|
|
14
|
+
SETUP
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
4
17
|
|
|
5
18
|
cat << 'EOF'
|
|
6
19
|
SDLC BASELINE:
|
|
@@ -10,10 +23,8 @@ SDLC BASELINE:
|
|
|
10
23
|
4. FAILED 2x? STOP and ASK USER
|
|
11
24
|
5. ALL TESTS MUST PASS BEFORE COMMIT - NO EXCEPTIONS
|
|
12
25
|
|
|
13
|
-
AUTO-INVOKE
|
|
14
|
-
- implement/fix/refactor/feature/bug/build → Invoke: Skill tool, skill="sdlc"
|
|
15
|
-
- test/TDD/write test (standalone) → Invoke: Skill tool, skill="testing"
|
|
16
|
-
- If BOTH match (e.g., "fix the test") → sdlc takes precedence (includes TDD)
|
|
26
|
+
AUTO-INVOKE SKILL (Claude MUST do this FIRST):
|
|
27
|
+
- implement/fix/refactor/feature/bug/build/test/TDD → Invoke: Skill tool, skill="sdlc"
|
|
17
28
|
- DON'T invoke for: questions, explanations, reading/exploring code, simple queries
|
|
18
29
|
- DON'T wait for user to type /sdlc - AUTO-INVOKE based on task type
|
|
19
30
|
|