@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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +310 -0
  3. package/dist/commands/archive.d.ts +3 -0
  4. package/dist/commands/archive.d.ts.map +1 -0
  5. package/dist/commands/archive.js +99 -0
  6. package/dist/commands/archive.js.map +1 -0
  7. package/dist/commands/code.d.ts +3 -0
  8. package/dist/commands/code.d.ts.map +1 -0
  9. package/dist/commands/code.js +53 -0
  10. package/dist/commands/code.js.map +1 -0
  11. package/dist/commands/complete.d.ts +3 -0
  12. package/dist/commands/complete.d.ts.map +1 -0
  13. package/dist/commands/complete.js +137 -0
  14. package/dist/commands/complete.js.map +1 -0
  15. package/dist/commands/doctor.d.ts +3 -0
  16. package/dist/commands/doctor.d.ts.map +1 -0
  17. package/dist/commands/doctor.js +204 -0
  18. package/dist/commands/doctor.js.map +1 -0
  19. package/dist/commands/init.d.ts +3 -0
  20. package/dist/commands/init.d.ts.map +1 -0
  21. package/dist/commands/init.js +80 -0
  22. package/dist/commands/init.js.map +1 -0
  23. package/dist/commands/list.d.ts +3 -0
  24. package/dist/commands/list.d.ts.map +1 -0
  25. package/dist/commands/list.js +122 -0
  26. package/dist/commands/list.js.map +1 -0
  27. package/dist/commands/new.d.ts +3 -0
  28. package/dist/commands/new.d.ts.map +1 -0
  29. package/dist/commands/new.js +141 -0
  30. package/dist/commands/new.js.map +1 -0
  31. package/dist/commands/qa.d.ts +3 -0
  32. package/dist/commands/qa.d.ts.map +1 -0
  33. package/dist/commands/qa.js +143 -0
  34. package/dist/commands/qa.js.map +1 -0
  35. package/dist/commands/spec.d.ts +3 -0
  36. package/dist/commands/spec.d.ts.map +1 -0
  37. package/dist/commands/spec.js +70 -0
  38. package/dist/commands/spec.js.map +1 -0
  39. package/dist/commands/status.d.ts +3 -0
  40. package/dist/commands/status.d.ts.map +1 -0
  41. package/dist/commands/status.js +47 -0
  42. package/dist/commands/status.js.map +1 -0
  43. package/dist/commands/test.d.ts +3 -0
  44. package/dist/commands/test.d.ts.map +1 -0
  45. package/dist/commands/test.js +134 -0
  46. package/dist/commands/test.js.map +1 -0
  47. package/dist/config.d.ts +17 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/config.js +44 -0
  50. package/dist/config.js.map +1 -0
  51. package/dist/index.d.ts +7 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +49 -0
  54. package/dist/index.js.map +1 -0
  55. package/package.json +41 -0
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const doctorCommand: Command;
3
+ //# sourceMappingURL=doctor.d.ts.map
@@ -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,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const initCommand: Command;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -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,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const listCommand: Command;
3
+ //# sourceMappingURL=list.d.ts.map
@@ -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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,iBAAiB,EAAE,4DAA4D,CAAC;KACvF,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,KAAK,EAAE,OAA2C,EAAE,EAAE;IAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,CAAC;IAEhD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QAExC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC,CAAC;YACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,IAAI,EAAE,CAAC;QAEf,2CAA2C;QAC3C,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACxB,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAe,CAAC;YACvD,MAAM,WAAW,GAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YAExF,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QAC/C,CAAC;QAED,2CAA2C;QAC3C,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAEhF,8BAA8B;QAC9B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC7B,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE;gBACxC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE;gBACrC,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE;aAC1C,CAAC,CAAC,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,eAAe;QACf,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAC/C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;YAC/E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,WAAW,aAAa,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;QAEvF,qBAAqB;QACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC,CAAC;QAC/G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAExC,cAAc;QACd,KAAK,CAAC,OAAO,CAAC,CAAC,IAAiB,EAAE,EAAE;YAClC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAEjE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,KAAK,IAAI,QAAQ,IAAI,WAAW,EAAE,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,EAAE,CAAC;QAEd,yBAAyB;QACzB,MAAM,MAAM,GAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACnF,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM;SAC3D,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAE7B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;gBACnC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC;IAEH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACnF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC,CAAC;QACpF,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,SAAS,aAAa,CAAC,KAAa;IAClC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,KAAK,IAAI;YACP,OAAO,KAAK,CAAC,OAAO,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,MAAM,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB;YACE,OAAO,KAAK,CAAC,KAAK,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const newCommand: Command;
3
+ //# sourceMappingURL=new.d.ts.map
@@ -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,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const qaCommand: Command;
3
+ //# sourceMappingURL=qa.d.ts.map
@@ -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"}