musubi-sdd 6.2.0 → 6.2.2

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.
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MUSUBI Upgrade Script
5
+ *
6
+ * Upgrades existing MUSUBI projects to newer versions:
7
+ * - Updates steering files
8
+ * - Adds new prompts/skills
9
+ * - Migrates configuration
10
+ * - Preserves user customizations
11
+ */
12
+
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const chalk = require('chalk');
16
+ const { Command } = require('commander');
17
+
18
+ const program = new Command();
19
+ const packageJson = require('../package.json');
20
+
21
+ // Version migration definitions
22
+ const MIGRATIONS = {
23
+ '6.2.0': {
24
+ description: 'Review Gate Engine, Dashboard, Traceability',
25
+ changes: [
26
+ 'Add Review Gate prompts to AGENTS.md',
27
+ 'Create storage/reviews/, storage/dashboard/, storage/traceability/ directories',
28
+ 'Update steering/project.yml with reviewGate settings',
29
+ ],
30
+ migrate: migrateToV620,
31
+ },
32
+ '6.2.1': {
33
+ description: 'Bug fixes and improvements',
34
+ changes: ['Minor fixes'],
35
+ migrate: migrateToV621,
36
+ },
37
+ };
38
+
39
+ // ============================================================================
40
+ // Migration Functions
41
+ // ============================================================================
42
+
43
+ async function migrateToV620(projectDir, _options) {
44
+ const results = { success: [], failed: [], skipped: [] };
45
+
46
+ // 1. Create new storage directories
47
+ const newDirs = [
48
+ 'storage/reviews',
49
+ 'storage/dashboard',
50
+ 'storage/traceability',
51
+ 'storage/transitions',
52
+ ];
53
+
54
+ for (const dir of newDirs) {
55
+ const dirPath = path.join(projectDir, dir);
56
+ try {
57
+ if (!fs.existsSync(dirPath)) {
58
+ await fs.ensureDir(dirPath);
59
+ await fs.writeFile(path.join(dirPath, '.gitkeep'), '');
60
+ results.success.push(`Created ${dir}/`);
61
+ } else {
62
+ results.skipped.push(`${dir}/ already exists`);
63
+ }
64
+ } catch (err) {
65
+ results.failed.push(`Failed to create ${dir}/: ${err.message}`);
66
+ }
67
+ }
68
+
69
+ // 2. Update AGENTS.md with new review prompts
70
+ const agentsPath = path.join(projectDir, 'AGENTS.md');
71
+ if (fs.existsSync(agentsPath)) {
72
+ try {
73
+ let content = await fs.readFile(agentsPath, 'utf8');
74
+
75
+ const reviewPrompts = `
76
+ ### Review Gate Prompts (v6.2.0)
77
+
78
+ - \`#sdd-review-requirements <feature>\` - Review requirements (EARS, stakeholders, acceptance criteria)
79
+ - \`#sdd-review-design <feature>\` - Review design (C4, ADR, Constitutional Articles)
80
+ - \`#sdd-review-implementation <feature>\` - Review implementation (coverage, lint, traceability)
81
+ - \`#sdd-review-all <feature>\` - Full review cycle for all phases
82
+ `;
83
+
84
+ if (!content.includes('#sdd-review-requirements')) {
85
+ // Find a good insertion point (after existing prompts section)
86
+ const promptsIndex = content.indexOf('### Prompts');
87
+ if (promptsIndex !== -1) {
88
+ const nextSectionIndex = content.indexOf('###', promptsIndex + 10);
89
+ if (nextSectionIndex !== -1) {
90
+ content =
91
+ content.slice(0, nextSectionIndex) + reviewPrompts + '\n' + content.slice(nextSectionIndex);
92
+ } else {
93
+ content += '\n' + reviewPrompts;
94
+ }
95
+ } else {
96
+ content += '\n' + reviewPrompts;
97
+ }
98
+
99
+ await fs.writeFile(agentsPath, content);
100
+ results.success.push('Added Review Gate prompts to AGENTS.md');
101
+ } else {
102
+ results.skipped.push('Review Gate prompts already in AGENTS.md');
103
+ }
104
+ } catch (err) {
105
+ results.failed.push(`Failed to update AGENTS.md: ${err.message}`);
106
+ }
107
+ } else {
108
+ results.skipped.push('AGENTS.md not found (not a MUSUBI project?)');
109
+ }
110
+
111
+ // 3. Update steering/project.yml with reviewGate settings
112
+ const projectYmlPath = path.join(projectDir, 'steering', 'project.yml');
113
+ if (fs.existsSync(projectYmlPath)) {
114
+ try {
115
+ let content = await fs.readFile(projectYmlPath, 'utf8');
116
+
117
+ const reviewGateConfig = `
118
+ # Review Gate Settings (v6.2.0)
119
+ reviewGate:
120
+ requirements:
121
+ earsCheck: true
122
+ stakeholderCoverage: true
123
+ acceptanceCriteriaRequired: true
124
+ design:
125
+ c4Required: ['context', 'container', 'component']
126
+ adrRequired: true
127
+ constitutionalArticles: [1, 2, 7, 8]
128
+ implementation:
129
+ minCoverage: 80
130
+ coverageType: 'line'
131
+ lintStrict: true
132
+
133
+ # Traceability Settings (v6.2.0)
134
+ traceability:
135
+ patterns:
136
+ - 'REQ-[A-Z0-9]+-\\\\d{3}'
137
+ - 'IMP-\\\\d+\\\\.\\\\d+-\\\\d{3}(?:-\\\\d{2})?'
138
+ extractFrom:
139
+ - 'src/**/*.{ts,js}'
140
+ - 'tests/**/*.test.{ts,js}'
141
+ outputPath: 'storage/traceability/matrix.yml'
142
+ `;
143
+
144
+ if (!content.includes('reviewGate:')) {
145
+ content += '\n' + reviewGateConfig;
146
+ await fs.writeFile(projectYmlPath, content);
147
+ results.success.push('Added reviewGate settings to steering/project.yml');
148
+ } else {
149
+ results.skipped.push('reviewGate settings already in steering/project.yml');
150
+ }
151
+ } catch (err) {
152
+ results.failed.push(`Failed to update steering/project.yml: ${err.message}`);
153
+ }
154
+ } else {
155
+ results.skipped.push('steering/project.yml not found');
156
+ }
157
+
158
+ // 4. Create version marker
159
+ const versionPath = path.join(projectDir, 'steering', '.musubi-version');
160
+ try {
161
+ await fs.writeFile(versionPath, '6.2.0\n');
162
+ results.success.push('Created version marker');
163
+ } catch (err) {
164
+ results.failed.push(`Failed to create version marker: ${err.message}`);
165
+ }
166
+
167
+ return results;
168
+ }
169
+
170
+ async function migrateToV621(projectDir, _options) {
171
+ const results = { success: [], failed: [], skipped: [] };
172
+
173
+ // v6.2.1 is mainly bug fixes, just update version marker
174
+ const versionPath = path.join(projectDir, 'steering', '.musubi-version');
175
+ try {
176
+ await fs.writeFile(versionPath, '6.2.1\n');
177
+ results.success.push('Updated version marker to 6.2.1');
178
+ } catch (err) {
179
+ results.failed.push(`Failed to update version marker: ${err.message}`);
180
+ }
181
+
182
+ return results;
183
+ }
184
+
185
+ // ============================================================================
186
+ // Utility Functions
187
+ // ============================================================================
188
+
189
+ function getCurrentVersion(projectDir) {
190
+ const versionPath = path.join(projectDir, 'steering', '.musubi-version');
191
+ if (fs.existsSync(versionPath)) {
192
+ return fs.readFileSync(versionPath, 'utf8').trim();
193
+ }
194
+
195
+ // Fallback: check if MUSUBI is initialized
196
+ const steeringDir = path.join(projectDir, 'steering');
197
+ if (fs.existsSync(steeringDir)) {
198
+ return '6.0.0'; // Assume pre-version-tracking
199
+ }
200
+
201
+ return null;
202
+ }
203
+
204
+ function compareVersions(v1, v2) {
205
+ const parts1 = v1.split('.').map(Number);
206
+ const parts2 = v2.split('.').map(Number);
207
+
208
+ for (let i = 0; i < 3; i++) {
209
+ if (parts1[i] > parts2[i]) return 1;
210
+ if (parts1[i] < parts2[i]) return -1;
211
+ }
212
+ return 0;
213
+ }
214
+
215
+ function getMigrationPath(fromVersion, toVersion) {
216
+ const versions = Object.keys(MIGRATIONS).sort(compareVersions);
217
+ const path = [];
218
+
219
+ for (const version of versions) {
220
+ if (compareVersions(version, fromVersion) > 0 && compareVersions(version, toVersion) <= 0) {
221
+ path.push(version);
222
+ }
223
+ }
224
+
225
+ return path;
226
+ }
227
+
228
+ // ============================================================================
229
+ // CLI Commands
230
+ // ============================================================================
231
+
232
+ program
233
+ .name('musubi-upgrade')
234
+ .description('Upgrade MUSUBI project to a newer version')
235
+ .version(packageJson.version)
236
+ .option('--to <version>', 'Target version to upgrade to', 'latest')
237
+ .option('--dry-run', 'Preview changes without applying')
238
+ .option('--force', 'Force upgrade even if already at target version')
239
+ .action(async options => {
240
+ const projectDir = process.cwd();
241
+
242
+ console.log(chalk.blue.bold('\nšŸ”„ MUSUBI Upgrade\n'));
243
+
244
+ // Check if MUSUBI is initialized
245
+ const currentVersion = getCurrentVersion(projectDir);
246
+ if (!currentVersion) {
247
+ console.log(chalk.red('āŒ MUSUBI is not initialized in this directory.'));
248
+ console.log(chalk.gray('\nRun: npx musubi-sdd init\n'));
249
+ process.exit(1);
250
+ }
251
+
252
+ // Determine target version
253
+ let targetVersion = options.to;
254
+ if (targetVersion === 'latest') {
255
+ targetVersion = packageJson.version;
256
+ }
257
+
258
+ // Validate target version
259
+ const availableVersions = Object.keys(MIGRATIONS);
260
+ if (!availableVersions.includes(targetVersion) && targetVersion !== packageJson.version) {
261
+ console.log(chalk.red(`āŒ Unknown target version: ${targetVersion}`));
262
+ console.log(chalk.gray(`\nAvailable versions: ${availableVersions.join(', ')}\n`));
263
+ process.exit(1);
264
+ }
265
+
266
+ console.log(chalk.white(`Current version: ${chalk.yellow(currentVersion)}`));
267
+ console.log(chalk.white(`Target version: ${chalk.green(targetVersion)}`));
268
+
269
+ // Check if upgrade is needed
270
+ const comparison = compareVersions(currentVersion, targetVersion);
271
+ if (comparison >= 0 && !options.force) {
272
+ console.log(chalk.green('\nāœ… Already at target version or newer.\n'));
273
+ console.log(chalk.gray('Use --force to re-run migrations.\n'));
274
+ process.exit(0);
275
+ }
276
+
277
+ // Get migration path
278
+ const migrationPath = getMigrationPath(currentVersion, targetVersion);
279
+
280
+ if (migrationPath.length === 0) {
281
+ console.log(chalk.yellow('\nāš ļø No migrations needed.\n'));
282
+ process.exit(0);
283
+ }
284
+
285
+ console.log(chalk.white(`\nMigrations to apply: ${migrationPath.join(' → ')}\n`));
286
+
287
+ // Show migration details
288
+ for (const version of migrationPath) {
289
+ const migration = MIGRATIONS[version];
290
+ console.log(chalk.cyan(`šŸ“¦ v${version}: ${migration.description}`));
291
+ for (const change of migration.changes) {
292
+ console.log(chalk.gray(` - ${change}`));
293
+ }
294
+ }
295
+
296
+ if (options.dryRun) {
297
+ console.log(chalk.yellow('\nšŸ” Dry run mode - no changes applied.\n'));
298
+ process.exit(0);
299
+ }
300
+
301
+ // Confirm upgrade
302
+ console.log('');
303
+
304
+ // Apply migrations
305
+ let totalSuccess = 0;
306
+ let totalFailed = 0;
307
+ let totalSkipped = 0;
308
+
309
+ for (const version of migrationPath) {
310
+ console.log(chalk.blue(`\nšŸ“¦ Applying migration to v${version}...\n`));
311
+
312
+ const migration = MIGRATIONS[version];
313
+ const results = await migration.migrate(projectDir, options);
314
+
315
+ for (const msg of results.success) {
316
+ console.log(chalk.green(` āœ… ${msg}`));
317
+ totalSuccess++;
318
+ }
319
+ for (const msg of results.skipped) {
320
+ console.log(chalk.yellow(` ā­ļø ${msg}`));
321
+ totalSkipped++;
322
+ }
323
+ for (const msg of results.failed) {
324
+ console.log(chalk.red(` āŒ ${msg}`));
325
+ totalFailed++;
326
+ }
327
+ }
328
+
329
+ // Summary
330
+ console.log(chalk.blue.bold('\nšŸ“Š Upgrade Summary\n'));
331
+ console.log(chalk.green(` āœ… Success: ${totalSuccess}`));
332
+ console.log(chalk.yellow(` ā­ļø Skipped: ${totalSkipped}`));
333
+ console.log(chalk.red(` āŒ Failed: ${totalFailed}`));
334
+
335
+ if (totalFailed > 0) {
336
+ console.log(chalk.red('\nāš ļø Some migrations failed. Please check the errors above.\n'));
337
+ process.exit(1);
338
+ }
339
+
340
+ console.log(chalk.green(`\nāœ… Successfully upgraded to v${targetVersion}!\n`));
341
+ });
342
+
343
+ // List available migrations
344
+ program
345
+ .command('list')
346
+ .description('List available migrations')
347
+ .action(() => {
348
+ console.log(chalk.blue.bold('\nšŸ“‹ Available MUSUBI Migrations\n'));
349
+
350
+ const versions = Object.keys(MIGRATIONS).sort(compareVersions);
351
+ for (const version of versions) {
352
+ const migration = MIGRATIONS[version];
353
+ console.log(chalk.cyan(`v${version}: ${migration.description}`));
354
+ for (const change of migration.changes) {
355
+ console.log(chalk.gray(` - ${change}`));
356
+ }
357
+ console.log('');
358
+ }
359
+
360
+ console.log(chalk.white(`Current package version: ${packageJson.version}\n`));
361
+ });
362
+
363
+ // Check current version
364
+ program
365
+ .command('check')
366
+ .description('Check current project version')
367
+ .action(() => {
368
+ const projectDir = process.cwd();
369
+ const currentVersion = getCurrentVersion(projectDir);
370
+
371
+ console.log(chalk.blue.bold('\nšŸ” MUSUBI Version Check\n'));
372
+
373
+ if (!currentVersion) {
374
+ console.log(chalk.red('āŒ MUSUBI is not initialized in this directory.\n'));
375
+ process.exit(1);
376
+ }
377
+
378
+ console.log(chalk.white(`Project version: ${chalk.yellow(currentVersion)}`));
379
+ console.log(chalk.white(`Package version: ${chalk.green(packageJson.version)}`));
380
+
381
+ const comparison = compareVersions(currentVersion, packageJson.version);
382
+ if (comparison < 0) {
383
+ console.log(chalk.yellow(`\nāš ļø Upgrade available: ${currentVersion} → ${packageJson.version}`));
384
+ console.log(chalk.gray('\nRun: npx musubi-sdd upgrade\n'));
385
+ } else {
386
+ console.log(chalk.green('\nāœ… Project is up to date.\n'));
387
+ }
388
+ });
389
+
390
+ program.parse(process.argv);
391
+
392
+ // Show help if no arguments
393
+ if (!process.argv.slice(2).length) {
394
+ program.outputHelp();
395
+ }
package/bin/musubi.js CHANGED
@@ -392,6 +392,21 @@ program
392
392
  }
393
393
  });
394
394
 
395
+ // ============================================================================
396
+ // Command: upgrade
397
+ // ============================================================================
398
+ program
399
+ .command('upgrade')
400
+ .description('Upgrade MUSUBI project to a newer version')
401
+ .option('--to <version>', 'Target version to upgrade to', 'latest')
402
+ .option('--dry-run', 'Preview changes without applying')
403
+ .option('--force', 'Force upgrade even if already at target version')
404
+ .action(async _options => {
405
+ // Delegate to musubi-upgrade.js
406
+ process.argv = ['node', 'musubi-upgrade', ...process.argv.slice(3)];
407
+ require('./musubi-upgrade.js');
408
+ });
409
+
395
410
  // ============================================================================
396
411
  // Command: info
397
412
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubi-sdd",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
4
4
  "description": "Ultimate Specification Driven Development Tool with 27 Agents for 7 AI Coding Platforms + MCP Integration (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -26,7 +26,8 @@
26
26
  "musubi-gui": "bin/musubi-gui.js",
27
27
  "musubi-orchestrate": "bin/musubi-orchestrate.js",
28
28
  "musubi-release": "bin/musubi-release.js",
29
- "musubi-config": "bin/musubi-config.js"
29
+ "musubi-config": "bin/musubi-config.js",
30
+ "musubi-upgrade": "bin/musubi-upgrade.js"
30
31
  },
31
32
  "scripts": {
32
33
  "test": "jest",
@@ -73,7 +74,8 @@
73
74
  "js-yaml": "^4.1.0",
74
75
  "open": "^10.1.0",
75
76
  "playwright": "^1.40.0",
76
- "ws": "^8.14.2"
77
+ "ws": "^8.14.2",
78
+ "yaml": "^2.8.2"
77
79
  },
78
80
  "devDependencies": {
79
81
  "eslint": "^8.50.0",