spec-starter 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/bin/index.js +83 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# spec-starter
|
|
2
2
|
|
|
3
|
-
A Claude Code project template for structured feature development.
|
|
3
|
+
A Claude Code project template for structured feature development. Run `npx spec-starter` in any project to get a repeatable workflow for taking features from raw idea to tested implementation.
|
|
4
4
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ Each stage has a matching slash command. Commands are interactive: called withou
|
|
|
21
21
|
| Command | No args | With arg |
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| `/task` | List backlog tasks | Add idea to `.claude/tasks.md` |
|
|
24
|
-
| `/feature:start` | List backlog tasks | Create feature folder + `2-brief.md` |
|
|
24
|
+
| `/feature:start` | List backlog tasks | Create feature folder + `1-feature.md` + `2-brief.md` |
|
|
25
25
|
| `/feature:review` | List `[?]` features | Review current state and take next action |
|
|
26
26
|
| `/feature:blueprint` | List `[x]` Brief features ready to blueprint | Generate `3-blueprint.md` |
|
|
27
27
|
| `/feature:implement` | List `[x]` Blueprint features ready to implement | Implement + generate `4-e2e-checklist.md` |
|
|
@@ -41,10 +41,11 @@ Backlog idea (.claude/tasks.md)
|
|
|
41
41
|
/feature:blueprint <slug> → .claude/_features/<slug>/3-blueprint.md [x] Blueprint
|
|
42
42
|
↓
|
|
43
43
|
/feature:implement <slug> → writes code (TDD), generates 4-e2e-checklist.md [x] Implement
|
|
44
|
+
↓ [o] Review
|
|
45
|
+
· manually work through 4-e2e-checklist.md
|
|
44
46
|
↓
|
|
45
|
-
|
|
46
|
-
↓
|
|
47
|
-
/feature:finish <slug> → push branch, PR into dev, merge, mark Done [x] Done
|
|
47
|
+
/feature:finish <slug> → push branch, PR into dev, merge, mark Done [x] Review
|
|
48
|
+
↓ [x] Done
|
|
48
49
|
↓
|
|
49
50
|
/feature:test <slug> → walk e2e checklist interactively (post-merge QA)
|
|
50
51
|
```
|
|
@@ -91,9 +92,23 @@ You can also create or edit `.claude/testing.md` by hand at any time.
|
|
|
91
92
|
|
|
92
93
|
## Getting started
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
In your project root:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx spec-starter
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then open Claude Code — `project-init` runs automatically on first open, generating `CLAUDE.md` and `.claude/testing.md`.
|
|
102
|
+
|
|
103
|
+
Add ideas to `.claude/tasks.md` (one per line), then run `/feature:start <idea>` to turn one into a feature.
|
|
104
|
+
|
|
105
|
+
## Updating
|
|
106
|
+
|
|
107
|
+
Run the same command again to sync the latest engine files. Your project data (`_features/`, `tasks.md`, `testing.md`, `CLAUDE.md`) is never touched. Claude will announce the update next time you open the project.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx spec-starter
|
|
111
|
+
```
|
|
97
112
|
|
|
98
113
|
## Design principle
|
|
99
114
|
|
package/bin/index.js
CHANGED
|
@@ -3,32 +3,26 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const PKG_DIR = path.join(__dirname, '..');
|
|
7
|
+
const TARGET_DIR = process.cwd();
|
|
8
|
+
const VERSION = require('../package.json').version;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
const engineDirs = [
|
|
10
|
+
const SCAFFOLD_DIRS = [
|
|
11
11
|
'.claude/commands',
|
|
12
12
|
'.claude/skills',
|
|
13
13
|
'.claude/_templates',
|
|
14
14
|
'.claude/hooks',
|
|
15
15
|
];
|
|
16
|
-
const engineFiles = [
|
|
17
|
-
'.claude/settings.json',
|
|
18
|
-
];
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
['.claude/tasks.md', ''],
|
|
17
|
+
const SCAFFOLD_FILES = [
|
|
18
|
+
'.claude/settings.json',
|
|
23
19
|
];
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
31
|
-
const destPath = path.join(destDir, entry.name);
|
|
21
|
+
function copyDir(src, dest) {
|
|
22
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
23
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
24
|
+
const srcPath = path.join(src, entry.name);
|
|
25
|
+
const destPath = path.join(dest, entry.name);
|
|
32
26
|
if (entry.isDirectory()) {
|
|
33
27
|
copyDir(srcPath, destPath);
|
|
34
28
|
} else {
|
|
@@ -37,33 +31,85 @@ function copyDir(srcDir, destDir) {
|
|
|
37
31
|
}
|
|
38
32
|
}
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
// Copy scaffold
|
|
35
|
+
for (const dir of SCAFFOLD_DIRS) {
|
|
36
|
+
const src = path.join(PKG_DIR, dir);
|
|
37
|
+
const dest = path.join(TARGET_DIR, dir);
|
|
38
|
+
if (fs.existsSync(src)) copyDir(src, dest);
|
|
39
|
+
}
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
for (const file of SCAFFOLD_FILES) {
|
|
42
|
+
const src = path.join(PKG_DIR, file);
|
|
43
|
+
const dest = path.join(TARGET_DIR, file);
|
|
44
|
+
if (fs.existsSync(src)) {
|
|
45
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
46
|
+
fs.copyFileSync(src, dest);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
// Version tracking
|
|
51
|
+
const VERSION_FILE = path.join(TARGET_DIR, '.claude', '.spec-starter-version');
|
|
52
|
+
const CLAUDE_MD = path.join(TARGET_DIR, 'CLAUDE.md');
|
|
53
|
+
|
|
54
|
+
function readInstalledVersion() {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(fs.readFileSync(VERSION_FILE, 'utf8')).version;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
fs.
|
|
52
|
-
fs.copyFileSync(path.join(src, file), path.join(dest, file));
|
|
53
|
-
console.log(` ✓ ${file}`);
|
|
62
|
+
function writeInstalledVersion() {
|
|
63
|
+
fs.writeFileSync(VERSION_FILE, JSON.stringify({ version: VERSION }, null, 2));
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
// Bootstrap CLAUDE.md for fresh installs
|
|
67
|
+
function writeBootstrapClaudeMd() {
|
|
68
|
+
const content = `<!-- spec-starter:bootstrap -->
|
|
69
|
+
Run the \`project-init\` skill now. Do not do anything else first.
|
|
70
|
+
<!-- /spec-starter:bootstrap -->
|
|
71
|
+
`;
|
|
72
|
+
fs.writeFileSync(CLAUDE_MD, content);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Inject update notice into existing CLAUDE.md
|
|
76
|
+
const NOTICE_START = '<!-- spec-starter:notice -->';
|
|
77
|
+
const NOTICE_END = '<!-- /spec-starter:notice -->';
|
|
78
|
+
|
|
79
|
+
function injectUpdateNotice() {
|
|
80
|
+
const notice = `${NOTICE_START}
|
|
81
|
+
spec-starter was updated to v${VERSION}. Let the user know what changed, then remove this notice block from CLAUDE.md.
|
|
82
|
+
${NOTICE_END}`;
|
|
83
|
+
|
|
84
|
+
const existing = fs.readFileSync(CLAUDE_MD, 'utf8');
|
|
85
|
+
|
|
86
|
+
if (existing.includes(NOTICE_START)) {
|
|
87
|
+
// Replace existing notice
|
|
88
|
+
const updated = existing.replace(
|
|
89
|
+
new RegExp(`${NOTICE_START}[\\s\\S]*?${NOTICE_END}`),
|
|
90
|
+
notice
|
|
91
|
+
);
|
|
92
|
+
fs.writeFileSync(CLAUDE_MD, updated);
|
|
61
93
|
} else {
|
|
62
|
-
|
|
94
|
+
// Prepend notice
|
|
95
|
+
fs.writeFileSync(CLAUDE_MD, notice + '\n\n' + existing);
|
|
63
96
|
}
|
|
64
97
|
}
|
|
65
98
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
// Main
|
|
100
|
+
const installedVersion = readInstalledVersion();
|
|
101
|
+
const claudeMdExists = fs.existsSync(CLAUDE_MD);
|
|
102
|
+
|
|
103
|
+
if (!claudeMdExists) {
|
|
104
|
+
writeBootstrapClaudeMd();
|
|
105
|
+
console.log(`spec-starter v${VERSION} installed.`);
|
|
106
|
+
console.log('Open Claude Code — project-init will run automatically.');
|
|
107
|
+
} else if (installedVersion !== VERSION) {
|
|
108
|
+
injectUpdateNotice();
|
|
109
|
+
console.log(`spec-starter updated to v${VERSION}.`);
|
|
110
|
+
console.log('Claude will announce the update next time you open this project.');
|
|
111
|
+
} else {
|
|
112
|
+
console.log(`spec-starter v${VERSION} refreshed.`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
writeInstalledVersion();
|