@specsafe/cli 0.4.0 ā 0.6.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/dist/commands/apply.d.ts +3 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +182 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/constitution.d.ts +3 -0
- package/dist/commands/constitution.d.ts.map +1 -0
- package/dist/commands/constitution.js +192 -0
- package/dist/commands/constitution.js.map +1 -0
- package/dist/commands/delta.d.ts +3 -0
- package/dist/commands/delta.d.ts.map +1 -0
- package/dist/commands/delta.js +82 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +102 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/extend.d.ts +6 -0
- package/dist/commands/extend.d.ts.map +1 -0
- package/dist/commands/extend.js +167 -0
- package/dist/commands/extend.js.map +1 -0
- package/dist/commands/init-old.d.ts +3 -0
- package/dist/commands/init-old.d.ts.map +1 -0
- package/dist/commands/init-old.js +146 -0
- package/dist/commands/init-old.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +195 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +26 -8
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/qa.d.ts.map +1 -1
- package/dist/commands/qa.js +37 -1
- package/dist/commands/qa.js.map +1 -1
- package/dist/commands/rules.d.ts.map +1 -1
- package/dist/commands/rules.js +182 -97
- package/dist/commands/rules.js.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,YAAY,SAqMrB,CAAC"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { readFile, writeFile, readdir, copyFile, mkdir, unlink } from 'fs/promises';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { DeltaParser, SemanticMerger } from '@specsafe/core';
|
|
7
|
+
import { confirm } from '@inquirer/prompts';
|
|
8
|
+
export const applyCommand = new Command('apply')
|
|
9
|
+
.description('Apply pending delta specs to a base spec')
|
|
10
|
+
.argument('<spec-id>', 'Base spec ID (e.g., SPEC-20260101-001)')
|
|
11
|
+
.option('--no-backup', 'Skip creating backup before applying')
|
|
12
|
+
.option('-f, --force', 'Apply even if conflicts exist')
|
|
13
|
+
.action(async (specId, options) => {
|
|
14
|
+
const spinner = ora('Loading delta specs...').start();
|
|
15
|
+
try {
|
|
16
|
+
// Validate specId to prevent path traversal
|
|
17
|
+
if (!/^[A-Za-z0-9_-]+$/.test(specId)) {
|
|
18
|
+
spinner.fail(chalk.red('Invalid spec ID. Use only alphanumeric characters, hyphens, and underscores.'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Load base spec
|
|
22
|
+
const baseSpecPath = join('specs/active', `${specId}.md`);
|
|
23
|
+
let baseContent;
|
|
24
|
+
try {
|
|
25
|
+
baseContent = await readFile(baseSpecPath, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
spinner.fail(chalk.red(`Base spec not found: ${specId}`));
|
|
29
|
+
console.log(chalk.gray('š” Tip: Check that the spec exists in specs/active/'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// Find all delta specs for this base spec
|
|
33
|
+
const deltasDir = 'specs/deltas';
|
|
34
|
+
let deltaFiles = [];
|
|
35
|
+
try {
|
|
36
|
+
const files = await readdir(deltasDir);
|
|
37
|
+
deltaFiles = files.filter(f => f.startsWith(`DELTA-${specId}-`) && f.endsWith('.md'));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
spinner.fail(chalk.red('No deltas directory found'));
|
|
41
|
+
console.log(chalk.gray('š” Tip: Create a delta spec first with: specsafe delta <spec-id>'));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (deltaFiles.length === 0) {
|
|
45
|
+
spinner.fail(chalk.red(`No delta specs found for ${specId}`));
|
|
46
|
+
console.log(chalk.gray('š” Tip: Create a delta spec first with: specsafe delta <spec-id>'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
spinner.text = `Found ${deltaFiles.length} delta spec(s)`;
|
|
50
|
+
// Parse all delta specs
|
|
51
|
+
const parser = new DeltaParser();
|
|
52
|
+
const deltaSpecs = [];
|
|
53
|
+
for (const file of deltaFiles) {
|
|
54
|
+
const deltaPath = join(deltasDir, file);
|
|
55
|
+
const deltaContent = await readFile(deltaPath, 'utf-8');
|
|
56
|
+
const deltaId = file.replace('.md', '');
|
|
57
|
+
try {
|
|
58
|
+
const deltaSpec = parser.parse(deltaContent, deltaId, specId);
|
|
59
|
+
const validation = parser.validate(deltaSpec);
|
|
60
|
+
if (!validation.valid) {
|
|
61
|
+
spinner.warn(chalk.yellow(`Validation errors in ${deltaId}:`));
|
|
62
|
+
for (const error of validation.errors) {
|
|
63
|
+
console.log(chalk.yellow(` - ${error}`));
|
|
64
|
+
}
|
|
65
|
+
if (!options.force) {
|
|
66
|
+
const proceed = await confirm({
|
|
67
|
+
message: 'Continue with other deltas?',
|
|
68
|
+
default: false
|
|
69
|
+
});
|
|
70
|
+
if (!proceed) {
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
spinner.start('Continuing with other deltas...');
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
spinner.start('Continuing...');
|
|
77
|
+
}
|
|
78
|
+
deltaSpecs.push(deltaSpec);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
spinner.warn(chalk.yellow(`Failed to parse ${deltaId}: ${err.message}`));
|
|
82
|
+
spinner.start('Continuing...');
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (deltaSpecs.length === 0) {
|
|
87
|
+
spinner.fail(chalk.red('No valid delta specs to apply'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
spinner.text = `Applying ${deltaSpecs.length} delta spec(s)...`;
|
|
91
|
+
// Apply all deltas in sequence
|
|
92
|
+
const merger = new SemanticMerger();
|
|
93
|
+
let currentContent = baseContent;
|
|
94
|
+
let totalStats = {
|
|
95
|
+
added: 0,
|
|
96
|
+
modified: 0,
|
|
97
|
+
removed: 0,
|
|
98
|
+
conflicts: 0
|
|
99
|
+
};
|
|
100
|
+
for (const deltaSpec of deltaSpecs) {
|
|
101
|
+
const result = merger.merge(currentContent, deltaSpec);
|
|
102
|
+
// Accumulate stats
|
|
103
|
+
totalStats.added += result.stats.added;
|
|
104
|
+
totalStats.modified += result.stats.modified;
|
|
105
|
+
totalStats.removed += result.stats.removed;
|
|
106
|
+
totalStats.conflicts += result.stats.conflicts;
|
|
107
|
+
if (result.conflicts.length > 0) {
|
|
108
|
+
spinner.warn(chalk.yellow(`Conflicts in ${deltaSpec.id}:`));
|
|
109
|
+
for (const conflict of result.conflicts) {
|
|
110
|
+
console.log(chalk.yellow(` - ${conflict.message}`));
|
|
111
|
+
}
|
|
112
|
+
if (!options.force) {
|
|
113
|
+
const proceed = await confirm({
|
|
114
|
+
message: 'Apply anyway (may result in incomplete merge)?',
|
|
115
|
+
default: false
|
|
116
|
+
});
|
|
117
|
+
if (!proceed) {
|
|
118
|
+
spinner.fail(chalk.red('Apply cancelled due to conflicts'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
spinner.start();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
currentContent = result.content;
|
|
125
|
+
}
|
|
126
|
+
spinner.stop();
|
|
127
|
+
// Show preview of changes
|
|
128
|
+
console.log(chalk.blue('\nš Merge Summary:'));
|
|
129
|
+
console.log(chalk.green(` ā Added: ${totalStats.added} requirements`));
|
|
130
|
+
console.log(chalk.yellow(` ~ Modified: ${totalStats.modified} requirements`));
|
|
131
|
+
console.log(chalk.red(` - Removed: ${totalStats.removed} requirements`));
|
|
132
|
+
if (totalStats.conflicts > 0) {
|
|
133
|
+
console.log(chalk.red(` ā Conflicts: ${totalStats.conflicts}`));
|
|
134
|
+
}
|
|
135
|
+
// Confirm before writing
|
|
136
|
+
const proceed = await confirm({
|
|
137
|
+
message: 'Apply these changes to the base spec?',
|
|
138
|
+
default: true
|
|
139
|
+
});
|
|
140
|
+
if (!proceed) {
|
|
141
|
+
console.log(chalk.yellow('\nApply cancelled.'));
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
spinner.start('Applying changes...');
|
|
145
|
+
// Create backup if requested
|
|
146
|
+
if (options.backup) {
|
|
147
|
+
const backupDir = join('specs', 'backups');
|
|
148
|
+
await mkdir(backupDir, { recursive: true });
|
|
149
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
150
|
+
const backupPath = join(backupDir, `${specId}-${timestamp}.md`);
|
|
151
|
+
await copyFile(baseSpecPath, backupPath);
|
|
152
|
+
spinner.text = `Created backup: ${backupPath}`;
|
|
153
|
+
}
|
|
154
|
+
// Write merged content
|
|
155
|
+
await writeFile(baseSpecPath, currentContent);
|
|
156
|
+
// Archive applied deltas (only those that were successfully applied)
|
|
157
|
+
const archiveDir = join('specs', 'deltas', 'applied');
|
|
158
|
+
await mkdir(archiveDir, { recursive: true });
|
|
159
|
+
for (const deltaSpec of deltaSpecs) {
|
|
160
|
+
const deltaFile = `${deltaSpec.id}.md`;
|
|
161
|
+
const deltaPath = join(deltasDir, deltaFile);
|
|
162
|
+
const archivePath = join(archiveDir, deltaFile);
|
|
163
|
+
await copyFile(deltaPath, archivePath);
|
|
164
|
+
await unlink(deltaPath); // Remove original after archiving
|
|
165
|
+
}
|
|
166
|
+
spinner.succeed(chalk.green(`Successfully applied ${deltaSpecs.length} delta spec(s) to ${specId}`));
|
|
167
|
+
console.log(chalk.blue(` Updated: ${baseSpecPath}`));
|
|
168
|
+
if (options.backup) {
|
|
169
|
+
console.log(chalk.blue(` Backup: specs/backups/`));
|
|
170
|
+
}
|
|
171
|
+
console.log(chalk.blue(` Archived deltas: specs/deltas/applied/`));
|
|
172
|
+
console.log(chalk.gray('\n Next steps:'));
|
|
173
|
+
console.log(chalk.gray(` 1. Review changes: git diff ${baseSpecPath}`));
|
|
174
|
+
console.log(chalk.gray(` 2. Update tests if needed`));
|
|
175
|
+
console.log(chalk.gray(` 3. Run: specsafe verify ${specId}`));
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
spinner.fail(chalk.red(`Failed to apply deltas: ${error.message}`));
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constitution.d.ts","sourceRoot":"","sources":["../../src/commands/constitution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,mBAAmB,SAgM7B,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { writeFile, mkdir, readFile } from 'fs/promises';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { ConstitutionManager, generateConstitution, generateMinimalConstitution, generateStrictConstitution, BUILTIN_PRINCIPLES } from '@specsafe/core';
|
|
8
|
+
async function loadSpec(specId, cwd = process.cwd()) {
|
|
9
|
+
const specsDir = join(cwd, 'specs');
|
|
10
|
+
const specPath = join(specsDir, `${specId}.json`);
|
|
11
|
+
if (!existsSync(specPath))
|
|
12
|
+
throw new Error(`Spec file not found: ${specPath}`);
|
|
13
|
+
const content = await readFile(specPath, 'utf-8');
|
|
14
|
+
const spec = JSON.parse(content);
|
|
15
|
+
spec.createdAt = new Date(spec.createdAt);
|
|
16
|
+
spec.updatedAt = new Date(spec.updatedAt);
|
|
17
|
+
if (spec.completedAt)
|
|
18
|
+
spec.completedAt = new Date(spec.completedAt);
|
|
19
|
+
return spec;
|
|
20
|
+
}
|
|
21
|
+
export const constitutionCommand = new Command('constitution')
|
|
22
|
+
.description('Manage constitutional governance')
|
|
23
|
+
.addCommand(new Command('init')
|
|
24
|
+
.description('Initialize constitution')
|
|
25
|
+
.option('--minimal', 'Minimal constitution')
|
|
26
|
+
.option('--strict', 'Strict constitution')
|
|
27
|
+
.option('--force', 'Overwrite existing')
|
|
28
|
+
.action(async (options) => {
|
|
29
|
+
const spinner = ora('Initializing...').start();
|
|
30
|
+
try {
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const specsafeDir = join(cwd, '.specsafe');
|
|
33
|
+
const constitutionPath = join(specsafeDir, 'constitution.md');
|
|
34
|
+
if (existsSync(constitutionPath) && !options.force) {
|
|
35
|
+
spinner.fail(chalk.red('Constitution exists. Use --force'));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
await mkdir(specsafeDir, { recursive: true });
|
|
39
|
+
let projectName = 'My Project';
|
|
40
|
+
try {
|
|
41
|
+
const pkgPath = join(cwd, 'package.json');
|
|
42
|
+
if (existsSync(pkgPath)) {
|
|
43
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
|
|
44
|
+
projectName = pkg.name || projectName;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch { }
|
|
48
|
+
let content;
|
|
49
|
+
if (options.minimal)
|
|
50
|
+
content = generateMinimalConstitution(projectName);
|
|
51
|
+
else if (options.strict)
|
|
52
|
+
content = generateStrictConstitution(projectName);
|
|
53
|
+
else
|
|
54
|
+
content = generateConstitution({ projectName });
|
|
55
|
+
await writeFile(constitutionPath, content);
|
|
56
|
+
spinner.succeed(chalk.green('Constitution initialized'));
|
|
57
|
+
console.log(chalk.gray(`\n${constitutionPath}`));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
spinner.fail(chalk.red(error.message));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}))
|
|
64
|
+
.addCommand(new Command('list')
|
|
65
|
+
.description('List principles')
|
|
66
|
+
.option('--builtin-only', 'Show built-ins only')
|
|
67
|
+
.action(async (options) => {
|
|
68
|
+
const spinner = ora('Loading...').start();
|
|
69
|
+
try {
|
|
70
|
+
if (options.builtinOnly) {
|
|
71
|
+
spinner.stop();
|
|
72
|
+
console.log(chalk.bold('\nš Built-in Principles:\n'));
|
|
73
|
+
for (const p of BUILTIN_PRINCIPLES) {
|
|
74
|
+
const lock = p.immutable ? 'š' : 'š';
|
|
75
|
+
const sev = p.severity === 'error' ? 'š«' : 'ā ļø';
|
|
76
|
+
console.log(`${lock} ${sev} ${chalk.cyan(p.id.padEnd(30))} ${p.name}`);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const mgr = new ConstitutionManager();
|
|
81
|
+
await mgr.load({ includeBuiltins: true });
|
|
82
|
+
const principles = mgr.listPrinciples();
|
|
83
|
+
spinner.stop();
|
|
84
|
+
console.log(chalk.bold('\nš Principles:\n'));
|
|
85
|
+
const errors = principles.filter(p => p.severity === 'error');
|
|
86
|
+
const warnings = principles.filter(p => p.severity === 'warning');
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
console.log(chalk.bold('š« Errors:'));
|
|
89
|
+
for (const p of errors) {
|
|
90
|
+
const lock = p.immutable ? 'š' : 'š';
|
|
91
|
+
console.log(` ${lock} ${chalk.red(p.id.padEnd(30))} ${p.name}`);
|
|
92
|
+
}
|
|
93
|
+
console.log();
|
|
94
|
+
}
|
|
95
|
+
if (warnings.length > 0) {
|
|
96
|
+
console.log(chalk.bold('ā ļø Warnings:'));
|
|
97
|
+
for (const p of warnings) {
|
|
98
|
+
const lock = p.immutable ? 'š' : 'š';
|
|
99
|
+
console.log(` ${lock} ${chalk.yellow(p.id.padEnd(30))} ${p.name}`);
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
console.log(chalk.gray(`Total: ${principles.length}`));
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
spinner.fail(chalk.red(error.message));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}))
|
|
110
|
+
.addCommand(new Command('check')
|
|
111
|
+
.description('Check spec governance')
|
|
112
|
+
.argument('<spec-id>', 'Spec ID')
|
|
113
|
+
.action(async (specId) => {
|
|
114
|
+
const spinner = ora('Checking...').start();
|
|
115
|
+
try {
|
|
116
|
+
const spec = await loadSpec(specId);
|
|
117
|
+
const mgr = new ConstitutionManager();
|
|
118
|
+
await mgr.load({ includeBuiltins: true });
|
|
119
|
+
const results = await mgr.validate(spec);
|
|
120
|
+
spinner.stop();
|
|
121
|
+
console.log(chalk.bold(`\nš ${spec.name}\n`));
|
|
122
|
+
let totalErrors = 0;
|
|
123
|
+
let totalWarnings = 0;
|
|
124
|
+
for (const result of results) {
|
|
125
|
+
const icon = result.passed ? 'ā
' : 'ā';
|
|
126
|
+
const color = result.passed ? chalk.green : chalk.red;
|
|
127
|
+
console.log(`${icon} ${color(result.gate.name)}`);
|
|
128
|
+
if (result.violations.length > 0) {
|
|
129
|
+
for (const v of result.violations) {
|
|
130
|
+
const vIcon = v.severity === 'error' ? 'š«' : 'ā ļø';
|
|
131
|
+
const vColor = v.severity === 'error' ? chalk.red : chalk.yellow;
|
|
132
|
+
console.log(` ${vIcon} ${vColor(v.principle.name)}`);
|
|
133
|
+
console.log(` ${chalk.gray(v.message)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
totalErrors += result.violations.filter(v => v.severity === 'error').length;
|
|
137
|
+
totalWarnings += result.violations.filter(v => v.severity === 'warning').length;
|
|
138
|
+
}
|
|
139
|
+
console.log(chalk.bold('\nSummary:'));
|
|
140
|
+
console.log(chalk.gray(`Gates: ${results.length}`));
|
|
141
|
+
if (totalErrors > 0)
|
|
142
|
+
console.log(chalk.red(`Errors: ${totalErrors}`));
|
|
143
|
+
if (totalWarnings > 0)
|
|
144
|
+
console.log(chalk.yellow(`Warnings: ${totalWarnings}`));
|
|
145
|
+
if (totalErrors === 0) {
|
|
146
|
+
console.log(chalk.green('\n⨠Passed!'));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.log(chalk.red('\nā Failed'));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
spinner.fail(chalk.red(error.message));
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}))
|
|
158
|
+
.addCommand(new Command('add')
|
|
159
|
+
.description('Add custom principle')
|
|
160
|
+
.argument('<id>', 'Principle ID')
|
|
161
|
+
.requiredOption('--name <name>', 'Name')
|
|
162
|
+
.requiredOption('--description <desc>', 'Description')
|
|
163
|
+
.option('--severity <level>', 'error or warning', 'warning')
|
|
164
|
+
.option('--immutable', 'Immutable', false)
|
|
165
|
+
.action(async (id, options) => {
|
|
166
|
+
const spinner = ora('Adding...').start();
|
|
167
|
+
try {
|
|
168
|
+
if (options.severity !== 'error' && options.severity !== 'warning') {
|
|
169
|
+
throw new Error('Severity must be error or warning');
|
|
170
|
+
}
|
|
171
|
+
const mgr = new ConstitutionManager();
|
|
172
|
+
await mgr.load({ includeBuiltins: false });
|
|
173
|
+
const principle = {
|
|
174
|
+
id,
|
|
175
|
+
name: options.name,
|
|
176
|
+
description: options.description,
|
|
177
|
+
severity: options.severity,
|
|
178
|
+
immutable: options.immutable,
|
|
179
|
+
metadata: { createdAt: new Date() },
|
|
180
|
+
};
|
|
181
|
+
mgr.addPrinciple(principle);
|
|
182
|
+
spinner.text = 'Saving...';
|
|
183
|
+
await mgr.save();
|
|
184
|
+
spinner.succeed(chalk.green('Added and saved'));
|
|
185
|
+
console.log(chalk.gray(`\n${principle.id}: ${principle.name}`));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
spinner.fail(chalk.red(error.message));
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}));
|
|
192
|
+
//# sourceMappingURL=constitution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constitution.js","sourceRoot":"","sources":["../../src/commands/constitution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/commands/delta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,YAAY,SA6ErB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { writeFile, mkdir, readFile } from 'fs/promises';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { generateDeltaTemplate } from '@specsafe/core';
|
|
7
|
+
import { confirm } from '@inquirer/prompts';
|
|
8
|
+
import { execFileSync } from 'child_process';
|
|
9
|
+
export const deltaCommand = new Command('delta')
|
|
10
|
+
.description('Create a new delta spec for an existing spec (brownfield changes)')
|
|
11
|
+
.argument('<spec-id>', 'Base spec ID (e.g., SPEC-20260101-001)')
|
|
12
|
+
.option('-a, --author <author>', 'Author name', 'developer')
|
|
13
|
+
.option('--no-edit', 'Create without opening in editor')
|
|
14
|
+
.action(async (specId, options) => {
|
|
15
|
+
const spinner = ora('Creating delta spec...').start();
|
|
16
|
+
try {
|
|
17
|
+
// Validate specId to prevent path traversal
|
|
18
|
+
if (!/^[A-Za-z0-9_-]+$/.test(specId)) {
|
|
19
|
+
spinner.fail(chalk.red('Invalid spec ID. Use only alphanumeric characters, hyphens, and underscores.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
// Validate base spec exists
|
|
23
|
+
const baseSpecPath = join('specs/active', `${specId}.md`);
|
|
24
|
+
try {
|
|
25
|
+
await readFile(baseSpecPath, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
spinner.fail(chalk.red(`Base spec not found: ${specId}`));
|
|
29
|
+
console.log(chalk.gray('š” Tip: Check that the spec exists in specs/active/'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// Generate delta spec ID
|
|
33
|
+
const date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
34
|
+
const deltaId = `DELTA-${specId}-${date}`;
|
|
35
|
+
// Create deltas directory if needed
|
|
36
|
+
await mkdir('specs/deltas', { recursive: true });
|
|
37
|
+
const deltaPath = join('specs/deltas', `${deltaId}.md`);
|
|
38
|
+
// Check if delta already exists
|
|
39
|
+
try {
|
|
40
|
+
await readFile(deltaPath, 'utf-8');
|
|
41
|
+
spinner.stop();
|
|
42
|
+
const overwrite = await confirm({
|
|
43
|
+
message: `Delta spec ${deltaId} already exists. Overwrite?`,
|
|
44
|
+
default: false
|
|
45
|
+
});
|
|
46
|
+
if (!overwrite) {
|
|
47
|
+
spinner.stop();
|
|
48
|
+
console.log(chalk.yellow('Delta creation cancelled.'));
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// File doesn't exist, proceed
|
|
54
|
+
}
|
|
55
|
+
// Generate template
|
|
56
|
+
const template = generateDeltaTemplate(deltaId, specId, options.author);
|
|
57
|
+
await writeFile(deltaPath, template);
|
|
58
|
+
spinner.succeed(chalk.green(`Created delta spec: ${deltaId}`));
|
|
59
|
+
console.log(chalk.blue(` Location: ${deltaPath}`));
|
|
60
|
+
console.log(chalk.gray('\n Next steps:'));
|
|
61
|
+
console.log(chalk.gray(` 1. Edit ${deltaPath} to describe changes`));
|
|
62
|
+
console.log(chalk.gray(` 2. Run: specsafe diff ${specId}`));
|
|
63
|
+
console.log(chalk.gray(` 3. Run: specsafe apply ${specId}`));
|
|
64
|
+
// Open in editor if requested
|
|
65
|
+
if (options.edit) {
|
|
66
|
+
spinner.start('Opening in editor...');
|
|
67
|
+
try {
|
|
68
|
+
const editor = process.env.EDITOR || 'nano';
|
|
69
|
+
execFileSync(editor, [deltaPath], { stdio: 'inherit' });
|
|
70
|
+
spinner.stop();
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
spinner.warn(chalk.yellow('Could not open editor. Edit the file manually.'));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
spinner.fail(chalk.red(`Failed to create delta spec: ${error.message}`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/commands/delta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,WAAW,SA2GpB,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { readFile, readdir } from 'fs/promises';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { DeltaParser, SemanticMerger } from '@specsafe/core';
|
|
7
|
+
export const diffCommand = new Command('diff')
|
|
8
|
+
.description('Show what would change when applying delta specs')
|
|
9
|
+
.argument('<spec-id>', 'Base spec ID (e.g., SPEC-20260101-001)')
|
|
10
|
+
.option('-v, --verbose', 'Show detailed diff')
|
|
11
|
+
.action(async (specId, options) => {
|
|
12
|
+
const spinner = ora('Loading delta specs...').start();
|
|
13
|
+
try {
|
|
14
|
+
// Validate specId to prevent path traversal
|
|
15
|
+
if (!/^[A-Za-z0-9_-]+$/.test(specId)) {
|
|
16
|
+
spinner.fail(chalk.red('Invalid spec ID. Use only alphanumeric characters, hyphens, and underscores.'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
// Load base spec
|
|
20
|
+
const baseSpecPath = join('specs/active', `${specId}.md`);
|
|
21
|
+
let baseContent;
|
|
22
|
+
try {
|
|
23
|
+
baseContent = await readFile(baseSpecPath, 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
spinner.fail(chalk.red(`Base spec not found: ${specId}`));
|
|
27
|
+
console.log(chalk.gray('š” Tip: Check that the spec exists in specs/active/'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Find all delta specs for this base spec
|
|
31
|
+
const deltasDir = 'specs/deltas';
|
|
32
|
+
let deltaFiles = [];
|
|
33
|
+
try {
|
|
34
|
+
const files = await readdir(deltasDir);
|
|
35
|
+
deltaFiles = files.filter(f => f.startsWith(`DELTA-${specId}-`) && f.endsWith('.md'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
spinner.fail(chalk.red('No deltas directory found'));
|
|
39
|
+
console.log(chalk.gray('š” Tip: Create a delta spec first with: specsafe delta <spec-id>'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
if (deltaFiles.length === 0) {
|
|
43
|
+
spinner.fail(chalk.red(`No delta specs found for ${specId}`));
|
|
44
|
+
console.log(chalk.gray('š” Tip: Create a delta spec first with: specsafe delta <spec-id>'));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
spinner.succeed(chalk.green(`Found ${deltaFiles.length} delta spec(s)`));
|
|
48
|
+
// Parse and display each delta
|
|
49
|
+
const parser = new DeltaParser();
|
|
50
|
+
const merger = new SemanticMerger();
|
|
51
|
+
for (const file of deltaFiles) {
|
|
52
|
+
const deltaPath = join(deltasDir, file);
|
|
53
|
+
const deltaContent = await readFile(deltaPath, 'utf-8');
|
|
54
|
+
const deltaId = file.replace(/\.md$/, '');
|
|
55
|
+
console.log(chalk.blue(`\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`));
|
|
56
|
+
console.log(chalk.blue.bold(`š ${deltaId}`));
|
|
57
|
+
console.log(chalk.blue(`āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n`));
|
|
58
|
+
try {
|
|
59
|
+
const deltaSpec = parser.parse(deltaContent, deltaId, specId);
|
|
60
|
+
// Validate
|
|
61
|
+
const validation = parser.validate(deltaSpec);
|
|
62
|
+
if (!validation.valid) {
|
|
63
|
+
console.log(chalk.red('ā ļø Validation Errors:'));
|
|
64
|
+
for (const error of validation.errors) {
|
|
65
|
+
console.log(chalk.red(` ⢠${error}`));
|
|
66
|
+
}
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
69
|
+
// Show diff preview
|
|
70
|
+
const diffPreview = merger.diff(baseContent, deltaSpec);
|
|
71
|
+
console.log(diffPreview);
|
|
72
|
+
// Show detailed changes if verbose
|
|
73
|
+
if (options.verbose) {
|
|
74
|
+
const mergeResult = merger.merge(baseContent, deltaSpec);
|
|
75
|
+
console.log(chalk.blue('\nš Merge Statistics:'));
|
|
76
|
+
console.log(chalk.green(` ā Added: ${mergeResult.stats.added}`));
|
|
77
|
+
console.log(chalk.yellow(` ~ Modified: ${mergeResult.stats.modified}`));
|
|
78
|
+
console.log(chalk.red(` - Removed: ${mergeResult.stats.removed}`));
|
|
79
|
+
if (mergeResult.conflicts.length > 0) {
|
|
80
|
+
console.log(chalk.red(`\nā ļø Conflicts (${mergeResult.conflicts.length}):`));
|
|
81
|
+
for (const conflict of mergeResult.conflicts) {
|
|
82
|
+
console.log(chalk.red(` ⢠[${conflict.type}] ${conflict.message}`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.log(chalk.red(`ā Failed to parse: ${err.message}`));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.blue(`\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n`));
|
|
92
|
+
console.log(chalk.gray(' Next steps:'));
|
|
93
|
+
console.log(chalk.gray(` ⢠Review changes above`));
|
|
94
|
+
console.log(chalk.gray(` ⢠Edit delta specs if needed`));
|
|
95
|
+
console.log(chalk.gray(` ⢠Run: specsafe apply ${specId}`));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
spinner.fail(chalk.red(`Failed to generate diff: ${error.message}`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"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"}
|