@specsafe/cli 0.1.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/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/commands/archive.d.ts +3 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +99 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/code.d.ts +3 -0
- package/dist/commands/code.d.ts.map +1 -0
- package/dist/commands/code.js +53 -0
- package/dist/commands/code.js.map +1 -0
- package/dist/commands/complete.d.ts +3 -0
- package/dist/commands/complete.d.ts.map +1 -0
- package/dist/commands/complete.js +137 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +204 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +80 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +122 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +141 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/qa.d.ts +3 -0
- package/dist/commands/qa.d.ts.map +1 -0
- package/dist/commands/qa.js +143 -0
- package/dist/commands/qa.js.map +1 -0
- package/dist/commands/spec.d.ts +3 -0
- package/dist/commands/spec.d.ts.map +1 -0
- package/dist/commands/spec.js +70 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +47 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +134 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,aAAa,SA4MtB,CAAC"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { access, readFile } from 'fs/promises';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
export const doctorCommand = new Command('doctor')
|
|
6
|
+
.description('Validate project setup and report issues')
|
|
7
|
+
.action(async () => {
|
|
8
|
+
console.log(chalk.bold('\nSpecSafe Doctor 🩺\n'));
|
|
9
|
+
const results = [];
|
|
10
|
+
// Check 1: Node.js version
|
|
11
|
+
const nodeVersion = process.version;
|
|
12
|
+
const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
13
|
+
if (nodeMajor >= 18) {
|
|
14
|
+
results.push({
|
|
15
|
+
status: 'pass',
|
|
16
|
+
message: `Node.js ${nodeVersion} (>= 18 required)`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
results.push({
|
|
21
|
+
status: 'error',
|
|
22
|
+
message: `Node.js ${nodeVersion} (< 18 required)`,
|
|
23
|
+
fix: 'Upgrade to Node.js 18 or higher'
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// Check 2: Project initialized (PROJECT_STATE.md exists)
|
|
27
|
+
const projectStatePath = join(process.cwd(), 'PROJECT_STATE.md');
|
|
28
|
+
try {
|
|
29
|
+
await access(projectStatePath);
|
|
30
|
+
results.push({
|
|
31
|
+
status: 'pass',
|
|
32
|
+
message: 'PROJECT_STATE.md found'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
results.push({
|
|
37
|
+
status: 'error',
|
|
38
|
+
message: 'PROJECT_STATE.md not found',
|
|
39
|
+
fix: "Run 'specsafe init' to initialize the project"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Check 3: Directory structure (specs/, tests/)
|
|
43
|
+
const specsDir = join(process.cwd(), 'specs');
|
|
44
|
+
const testsDir = join(process.cwd(), 'tests');
|
|
45
|
+
try {
|
|
46
|
+
await access(specsDir);
|
|
47
|
+
results.push({
|
|
48
|
+
status: 'pass',
|
|
49
|
+
message: 'specs/ directory found'
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
results.push({
|
|
54
|
+
status: 'warn',
|
|
55
|
+
message: 'specs/ directory missing',
|
|
56
|
+
fix: "Run 'specsafe init' to create"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await access(testsDir);
|
|
61
|
+
results.push({
|
|
62
|
+
status: 'pass',
|
|
63
|
+
message: 'tests/ directory found'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
results.push({
|
|
68
|
+
status: 'warn',
|
|
69
|
+
message: 'tests/ directory missing',
|
|
70
|
+
fix: "Run 'specsafe init' to create"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Check 4: Config file validation
|
|
74
|
+
const configPath = join(process.cwd(), 'specsafe.config.json');
|
|
75
|
+
try {
|
|
76
|
+
await access(configPath);
|
|
77
|
+
const configContent = await readFile(configPath, 'utf-8');
|
|
78
|
+
try {
|
|
79
|
+
const config = JSON.parse(configContent);
|
|
80
|
+
// Validate known fields
|
|
81
|
+
const knownFields = ['projectName', 'version', 'stages', 'testFramework', 'language'];
|
|
82
|
+
const unknownFields = Object.keys(config).filter(key => !knownFields.includes(key));
|
|
83
|
+
if (unknownFields.length > 0) {
|
|
84
|
+
results.push({
|
|
85
|
+
status: 'warn',
|
|
86
|
+
message: `Config file has unknown fields: ${unknownFields.join(', ')}`
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
results.push({
|
|
91
|
+
status: 'pass',
|
|
92
|
+
message: 'Config file valid'
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (parseError) {
|
|
97
|
+
results.push({
|
|
98
|
+
status: 'error',
|
|
99
|
+
message: 'Config file has invalid JSON',
|
|
100
|
+
fix: 'Fix syntax errors in specsafe.config.json'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Config file is optional, so this is just info
|
|
106
|
+
results.push({
|
|
107
|
+
status: 'pass',
|
|
108
|
+
message: 'No config file (using defaults)'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// Check 5: Dependencies (@specsafe/core and @specsafe/test-gen)
|
|
112
|
+
const nodeModulesPath = join(process.cwd(), 'node_modules');
|
|
113
|
+
const corePath = join(nodeModulesPath, '@specsafe', 'core');
|
|
114
|
+
const testGenPath = join(nodeModulesPath, '@specsafe', 'test-gen');
|
|
115
|
+
try {
|
|
116
|
+
await access(corePath);
|
|
117
|
+
results.push({
|
|
118
|
+
status: 'pass',
|
|
119
|
+
message: '@specsafe/core installed'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
results.push({
|
|
124
|
+
status: 'error',
|
|
125
|
+
message: '@specsafe/core not found',
|
|
126
|
+
fix: "Run 'npm install @specsafe/core'"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await access(testGenPath);
|
|
131
|
+
results.push({
|
|
132
|
+
status: 'pass',
|
|
133
|
+
message: '@specsafe/test-gen installed'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
results.push({
|
|
138
|
+
status: 'error',
|
|
139
|
+
message: '@specsafe/test-gen not found',
|
|
140
|
+
fix: "Run 'npm install @specsafe/test-gen'"
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Check 6: Git repo (recommended but not required)
|
|
144
|
+
const gitPath = join(process.cwd(), '.git');
|
|
145
|
+
try {
|
|
146
|
+
await access(gitPath);
|
|
147
|
+
results.push({
|
|
148
|
+
status: 'pass',
|
|
149
|
+
message: 'Git repository detected'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
results.push({
|
|
154
|
+
status: 'warn',
|
|
155
|
+
message: 'No Git repository found',
|
|
156
|
+
fix: "Run 'git init' to initialize (recommended)"
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// Print results
|
|
160
|
+
let passed = 0;
|
|
161
|
+
let warnings = 0;
|
|
162
|
+
let errors = 0;
|
|
163
|
+
for (const result of results) {
|
|
164
|
+
let icon;
|
|
165
|
+
let color;
|
|
166
|
+
switch (result.status) {
|
|
167
|
+
case 'pass':
|
|
168
|
+
icon = '✅';
|
|
169
|
+
color = chalk.green;
|
|
170
|
+
passed++;
|
|
171
|
+
break;
|
|
172
|
+
case 'warn':
|
|
173
|
+
icon = '⚠️';
|
|
174
|
+
color = chalk.yellow;
|
|
175
|
+
warnings++;
|
|
176
|
+
break;
|
|
177
|
+
case 'error':
|
|
178
|
+
icon = '❌';
|
|
179
|
+
color = chalk.red;
|
|
180
|
+
errors++;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
console.log(` ${icon} ${color(result.message)}`);
|
|
184
|
+
if (result.fix) {
|
|
185
|
+
console.log(` ${chalk.gray(result.fix)}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Summary
|
|
189
|
+
console.log('');
|
|
190
|
+
const parts = [];
|
|
191
|
+
if (passed > 0)
|
|
192
|
+
parts.push(chalk.green(`${passed} passed`));
|
|
193
|
+
if (warnings > 0)
|
|
194
|
+
parts.push(chalk.yellow(`${warnings} warning${warnings > 1 ? 's' : ''}`));
|
|
195
|
+
if (errors > 0)
|
|
196
|
+
parts.push(chalk.red(`${errors} error${errors > 1 ? 's' : ''}`));
|
|
197
|
+
console.log(` ${parts.join(', ')}`);
|
|
198
|
+
console.log('');
|
|
199
|
+
// Exit with error code if there are errors
|
|
200
|
+
if (errors > 0) {
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SA8EpB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
5
|
+
import { ProjectTracker } from '@specsafe/core';
|
|
6
|
+
export const initCommand = new Command('init')
|
|
7
|
+
.description('Initialize a new SpecSafe project')
|
|
8
|
+
.argument('[name]', 'Project name', 'my-project')
|
|
9
|
+
.action(async (name) => {
|
|
10
|
+
const spinner = ora('Initializing SpecSafe project...').start();
|
|
11
|
+
try {
|
|
12
|
+
// Create directory structure
|
|
13
|
+
await mkdir('specs/active', { recursive: true });
|
|
14
|
+
await mkdir('specs/completed', { recursive: true });
|
|
15
|
+
await mkdir('specs/archive', { recursive: true });
|
|
16
|
+
await mkdir('src', { recursive: true });
|
|
17
|
+
await mkdir('tests', { recursive: true });
|
|
18
|
+
// Create PROJECT_STATE.md
|
|
19
|
+
const tracker = new ProjectTracker(process.cwd());
|
|
20
|
+
await tracker.initialize(name);
|
|
21
|
+
// Create spec template
|
|
22
|
+
const template = `# Spec Template
|
|
23
|
+
|
|
24
|
+
## Metadata
|
|
25
|
+
- **ID**: SPEC-{YYYYMMDD}-{NNN}
|
|
26
|
+
- **Status**: draft
|
|
27
|
+
- **Priority**: P1
|
|
28
|
+
|
|
29
|
+
## 1. Purpose (WHY)
|
|
30
|
+
|
|
31
|
+
## 2. Scope (WHAT)
|
|
32
|
+
### In Scope
|
|
33
|
+
-
|
|
34
|
+
|
|
35
|
+
### Out of Scope
|
|
36
|
+
-
|
|
37
|
+
|
|
38
|
+
## 3. Requirements
|
|
39
|
+
| ID | Requirement | Priority | Acceptance Criteria |
|
|
40
|
+
|----|-------------|----------|---------------------|
|
|
41
|
+
| | | | |
|
|
42
|
+
|
|
43
|
+
## 4. Technical Approach (HOW)
|
|
44
|
+
|
|
45
|
+
## 5. Test Strategy
|
|
46
|
+
|
|
47
|
+
## 6. Implementation Plan
|
|
48
|
+
|
|
49
|
+
## 7. Success Criteria
|
|
50
|
+
- [ ]
|
|
51
|
+
|
|
52
|
+
## 8. Risks & Mitigations
|
|
53
|
+
|
|
54
|
+
## 9. Notes & References
|
|
55
|
+
`;
|
|
56
|
+
await writeFile('specs/template.md', template);
|
|
57
|
+
// Create config file
|
|
58
|
+
const config = {
|
|
59
|
+
projectName: name,
|
|
60
|
+
version: '1.0.0',
|
|
61
|
+
stages: ['spec', 'test', 'code', 'qa', 'complete'],
|
|
62
|
+
testFramework: 'vitest',
|
|
63
|
+
language: 'typescript'
|
|
64
|
+
};
|
|
65
|
+
await writeFile('specsafe.config.json', JSON.stringify(config, null, 2));
|
|
66
|
+
spinner.succeed(chalk.green(`Initialized SpecSafe project: ${name}`));
|
|
67
|
+
console.log('\n' + chalk.blue('Next steps:'));
|
|
68
|
+
console.log(' 1. specsafe new <spec-name> - Create a new spec');
|
|
69
|
+
console.log(' 2. specsafe status - View project status');
|
|
70
|
+
console.log(' 3. Edit specs/active/ to define requirements');
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
spinner.fail(chalk.red(`Failed to initialize: ${error.message}`));
|
|
74
|
+
if (error.message.includes('EEXIST')) {
|
|
75
|
+
console.log(chalk.gray('💡 Tip: Directory already exists. Use \'specsafe init <name>\' to create elsewhere.'));
|
|
76
|
+
}
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,mCAAmC,CAAC;KAChD,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,YAAY,CAAC;KAChD,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;IAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,kCAAkC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEhE,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,KAAK,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE/B,uBAAuB;QACvB,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCtB,CAAC;QACI,MAAM,SAAS,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QAE/C,qBAAqB;QACrB,MAAM,MAAM,GAAG;YACb,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;YAClD,aAAa,EAAE,QAAQ;YACvB,QAAQ,EAAE,YAAY;SACvB,CAAC;QACF,MAAM,SAAS,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEzE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEtE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC,CAAC;QACjH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,WAAW,SA+GpB,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ProjectTracker } from '@specsafe/core';
|
|
5
|
+
import { loadConfig } from '../config.js';
|
|
6
|
+
export const listCommand = new Command('list')
|
|
7
|
+
.description('List all specs from PROJECT_STATE.md')
|
|
8
|
+
.option('--stage <stage>', 'Filter by stage (spec, test, code, qa, complete, archived)')
|
|
9
|
+
.option('--json', 'Output as JSON')
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
const spinner = ora('Loading specs...').start();
|
|
12
|
+
try {
|
|
13
|
+
const config = await loadConfig();
|
|
14
|
+
const tracker = new ProjectTracker(process.cwd());
|
|
15
|
+
const state = await tracker.readState();
|
|
16
|
+
if (!state) {
|
|
17
|
+
spinner.stop();
|
|
18
|
+
console.log(chalk.yellow('No SpecSafe project found.'));
|
|
19
|
+
console.log(chalk.gray('💡 Tip: Run "specsafe init" to initialize a new project.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
spinner.stop();
|
|
23
|
+
// Filter specs if stage option is provided
|
|
24
|
+
let specs = state.specs;
|
|
25
|
+
if (options.stage) {
|
|
26
|
+
const stage = options.stage.toLowerCase();
|
|
27
|
+
const validStages = ['spec', 'test', 'code', 'qa', 'complete', 'archived'];
|
|
28
|
+
if (!validStages.includes(stage)) {
|
|
29
|
+
console.error(chalk.red(`Invalid stage: ${options.stage}`));
|
|
30
|
+
console.log(chalk.gray(`Valid stages: ${validStages.join(', ')}`));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
specs = specs.filter(s => s.stage === stage);
|
|
34
|
+
}
|
|
35
|
+
// Sort by last updated (most recent first)
|
|
36
|
+
specs = specs.sort((a, b) => b.lastUpdated.getTime() - a.lastUpdated.getTime());
|
|
37
|
+
// Output as JSON if requested
|
|
38
|
+
if (options.json) {
|
|
39
|
+
const output = specs.map(s => ({
|
|
40
|
+
id: s.id,
|
|
41
|
+
name: s.name,
|
|
42
|
+
stage: s.stage,
|
|
43
|
+
progress: s.progress,
|
|
44
|
+
lastUpdated: s.lastUpdated.toISOString(),
|
|
45
|
+
createdAt: s.createdAt?.toISOString(),
|
|
46
|
+
completedAt: s.completedAt?.toISOString()
|
|
47
|
+
}));
|
|
48
|
+
console.log(JSON.stringify(output, null, 2));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Table output
|
|
52
|
+
if (specs.length === 0) {
|
|
53
|
+
console.log(chalk.yellow('\nNo specs found.'));
|
|
54
|
+
if (options.stage) {
|
|
55
|
+
console.log(chalk.gray(`No specs in stage: ${options.stage.toUpperCase()}`));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(chalk.gray('Create your first spec with: specsafe new <name>'));
|
|
59
|
+
}
|
|
60
|
+
console.log();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
console.log(chalk.bold.blue(`\n📋 ${config.projectName} - Specs (${specs.length})\n`));
|
|
64
|
+
// Print table header
|
|
65
|
+
console.log(chalk.bold('ID Name Stage Progress Last Updated'));
|
|
66
|
+
console.log(chalk.gray('─'.repeat(90)));
|
|
67
|
+
// Print specs
|
|
68
|
+
specs.forEach((spec) => {
|
|
69
|
+
const stageColor = getStageColor(spec.stage);
|
|
70
|
+
const id = spec.id.padEnd(18);
|
|
71
|
+
const name = spec.name.slice(0, 28).padEnd(30);
|
|
72
|
+
const stage = stageColor(spec.stage.toUpperCase().padEnd(10));
|
|
73
|
+
const progress = `${spec.progress}%`.padEnd(8);
|
|
74
|
+
const lastUpdated = spec.lastUpdated.toISOString().split('T')[0];
|
|
75
|
+
console.log(`${id} ${name} ${stage} ${progress} ${lastUpdated}`);
|
|
76
|
+
});
|
|
77
|
+
console.log();
|
|
78
|
+
// Print summary by stage
|
|
79
|
+
const stages = ['spec', 'test', 'code', 'qa', 'complete', 'archived'];
|
|
80
|
+
const byStage = stages.map(s => ({
|
|
81
|
+
stage: s,
|
|
82
|
+
count: state.specs.filter(spec => spec.stage === s).length
|
|
83
|
+
})).filter(s => s.count > 0);
|
|
84
|
+
if (byStage.length > 0 && !options.stage) {
|
|
85
|
+
console.log(chalk.bold('Summary:'));
|
|
86
|
+
byStage.forEach(({ stage, count }) => {
|
|
87
|
+
const color = getStageColor(stage);
|
|
88
|
+
console.log(` ${color(stage.toUpperCase().padEnd(10))} ${count}`);
|
|
89
|
+
});
|
|
90
|
+
console.log();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
spinner.fail(chalk.red(error.message));
|
|
95
|
+
if (error.message.includes('PROJECT_STATE') || error.message.includes('not found')) {
|
|
96
|
+
console.log(chalk.gray('💡 Tip: Run "specsafe init" to initialize the project.'));
|
|
97
|
+
}
|
|
98
|
+
else if (error.message.includes('Invalid stage')) {
|
|
99
|
+
console.log(chalk.gray('💡 Tip: Valid stages: spec, test, code, qa, complete, archived'));
|
|
100
|
+
}
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
function getStageColor(stage) {
|
|
105
|
+
switch (stage) {
|
|
106
|
+
case 'complete':
|
|
107
|
+
return chalk.green;
|
|
108
|
+
case 'archived':
|
|
109
|
+
return chalk.gray;
|
|
110
|
+
case 'qa':
|
|
111
|
+
return chalk.magenta;
|
|
112
|
+
case 'code':
|
|
113
|
+
return chalk.yellow;
|
|
114
|
+
case 'test':
|
|
115
|
+
return chalk.cyan;
|
|
116
|
+
case 'spec':
|
|
117
|
+
return chalk.blue;
|
|
118
|
+
default:
|
|
119
|
+
return chalk.white;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,UAAU,SAmJnB,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { writeFile, mkdir, readdir } from 'fs/promises';
|
|
5
|
+
import { join, basename } from 'path';
|
|
6
|
+
import { Workflow, ProjectTracker } from '@specsafe/core';
|
|
7
|
+
export const newCommand = new Command('new')
|
|
8
|
+
.description('Create a new spec')
|
|
9
|
+
.argument('<name>', 'Spec name (kebab-case)')
|
|
10
|
+
.option('-d, --description <desc>', 'Spec description')
|
|
11
|
+
.option('-a, --author <author>', 'Author name', 'developer')
|
|
12
|
+
.option('-n, --dry-run', 'Preview changes without writing files')
|
|
13
|
+
.action(async (name, options) => {
|
|
14
|
+
const spinner = ora('Creating new spec...').start();
|
|
15
|
+
try {
|
|
16
|
+
const workflow = new Workflow();
|
|
17
|
+
const tracker = new ProjectTracker(process.cwd());
|
|
18
|
+
// Generate spec ID with auto-increment to avoid collisions
|
|
19
|
+
const date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
20
|
+
// Check if specs/active directory exists
|
|
21
|
+
try {
|
|
22
|
+
await readdir('specs/active');
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (err.code === 'ENOENT') {
|
|
26
|
+
throw new Error('specs/active/ directory not found. Run "specsafe init" first to initialize the project.');
|
|
27
|
+
}
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
// List existing specs for today and find max suffix
|
|
31
|
+
let maxSuffix = 0;
|
|
32
|
+
try {
|
|
33
|
+
const files = await readdir('specs/active');
|
|
34
|
+
const todayPrefix = `SPEC-${date}-`;
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
if (file.startsWith(todayPrefix) && file.endsWith('.md')) {
|
|
37
|
+
const suffix = parseInt(file.replace(todayPrefix, '').replace('.md', ''), 10);
|
|
38
|
+
if (!isNaN(suffix) && suffix > maxSuffix) {
|
|
39
|
+
maxSuffix = suffix;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Directory doesn't exist yet, that's fine
|
|
46
|
+
}
|
|
47
|
+
const id = `SPEC-${date}-${String(maxSuffix + 1).padStart(3, '0')}`;
|
|
48
|
+
// Create spec
|
|
49
|
+
const spec = workflow.createSpec(id, name, options.description || `Spec for ${name}`, options.author, basename(process.cwd()));
|
|
50
|
+
// Create spec content
|
|
51
|
+
const specContent = `# ${name} Specification
|
|
52
|
+
|
|
53
|
+
**ID:** ${id}
|
|
54
|
+
**Status:** SPEC
|
|
55
|
+
**Created:** ${new Date().toISOString().split('T')[0]}
|
|
56
|
+
**Author:** ${options.author}
|
|
57
|
+
|
|
58
|
+
## 1. Purpose (WHY)
|
|
59
|
+
<!-- Why are we building this? -->
|
|
60
|
+
|
|
61
|
+
## 2. Scope (WHAT)
|
|
62
|
+
### In Scope
|
|
63
|
+
-
|
|
64
|
+
|
|
65
|
+
### Out of Scope
|
|
66
|
+
-
|
|
67
|
+
|
|
68
|
+
## 3. Requirements
|
|
69
|
+
### Functional Requirements
|
|
70
|
+
| ID | Requirement | Priority | Acceptance Criteria |
|
|
71
|
+
|----|-------------|----------|---------------------|
|
|
72
|
+
| FR-1 | | P0 | |
|
|
73
|
+
|
|
74
|
+
### Non-Functional Requirements
|
|
75
|
+
| ID | Requirement | Metric |
|
|
76
|
+
|----|-------------|--------|
|
|
77
|
+
| NFR-1 | | |
|
|
78
|
+
|
|
79
|
+
## 4. Technical Approach (HOW)
|
|
80
|
+
|
|
81
|
+
## 5. Test Strategy (TDD)
|
|
82
|
+
### Unit Tests
|
|
83
|
+
-
|
|
84
|
+
|
|
85
|
+
### Integration Tests
|
|
86
|
+
-
|
|
87
|
+
|
|
88
|
+
## 6. Implementation Plan
|
|
89
|
+
| Phase | Task | Est. Time | Dependencies |
|
|
90
|
+
|-------|------|-----------|--------------|
|
|
91
|
+
| 1 | | | |
|
|
92
|
+
|
|
93
|
+
## 7. Success Criteria
|
|
94
|
+
- [ ] All P0 requirements met
|
|
95
|
+
- [ ] All tests passing
|
|
96
|
+
- [ ] Documentation complete
|
|
97
|
+
|
|
98
|
+
## 8. Risks & Mitigations
|
|
99
|
+
| Risk | Probability | Impact | Mitigation |
|
|
100
|
+
|------|-------------|--------|------------|
|
|
101
|
+
| | | | |
|
|
102
|
+
|
|
103
|
+
## 9. Notes & References
|
|
104
|
+
-
|
|
105
|
+
`;
|
|
106
|
+
const specPath = join('specs/active', `${id}.md`);
|
|
107
|
+
// Handle dry-run mode
|
|
108
|
+
if (options.dryRun) {
|
|
109
|
+
spinner.stop();
|
|
110
|
+
console.log(chalk.cyan('[DRY RUN] Would create the following files:\n'));
|
|
111
|
+
console.log(chalk.cyan(` ${specPath}`));
|
|
112
|
+
console.log(chalk.cyan(`\nContent preview (first 20 lines):\n`));
|
|
113
|
+
const previewLines = specContent.split('\n').slice(0, 20).join('\n');
|
|
114
|
+
console.log(chalk.gray(previewLines));
|
|
115
|
+
if (specContent.split('\n').length > 20) {
|
|
116
|
+
console.log(chalk.gray(' ... (truncated)'));
|
|
117
|
+
}
|
|
118
|
+
console.log(chalk.cyan(`\nWould update PROJECT_STATE.md with spec: ${id}`));
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
// Create spec file
|
|
122
|
+
await mkdir('specs/active', { recursive: true });
|
|
123
|
+
await writeFile(specPath, specContent);
|
|
124
|
+
// Update project state
|
|
125
|
+
await tracker.addSpec(spec);
|
|
126
|
+
spinner.succeed(chalk.green(`Created spec: ${id}`));
|
|
127
|
+
console.log(chalk.blue(` Location: ${specPath}`));
|
|
128
|
+
console.log(chalk.gray(' Edit the spec to add requirements, then run: specsafe spec <id>'));
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
spinner.fail(chalk.red(`Failed to create spec: ${error.message}`));
|
|
132
|
+
if (error.message.includes('specs/active/')) {
|
|
133
|
+
console.log(chalk.gray('💡 Tip: Run "specsafe init" to initialize the project first.'));
|
|
134
|
+
}
|
|
135
|
+
else if (error.message.includes('already exists')) {
|
|
136
|
+
console.log(chalk.gray('💡 Tip: Use a different spec name or delete the existing spec first.'));
|
|
137
|
+
}
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa.d.ts","sourceRoot":"","sources":["../../src/commands/qa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,eAAO,MAAM,SAAS,SAuIlB,CAAC"}
|