musubi-sdd 5.9.2 ā 5.9.3
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/bin/musubi-config.js +3 -5
- package/bin/musubi-release.js +55 -54
- package/bin/musubi-workflow.js +15 -8
- package/package.json +1 -1
- package/src/generators/changelog-generator.js +2 -4
- package/src/index.js +10 -2
- package/src/managers/package-manager.js +3 -5
- package/src/managers/workflow.js +4 -4
- package/src/orchestration/builtin-skills.js +30 -5
- package/src/orchestration/skill-registry.js +2 -2
- package/src/schemas/project-schema.json +23 -7
- package/src/validators/constitution-level-manager.js +2 -2
- package/src/validators/constitutional-validator.js +9 -14
package/bin/musubi-config.js
CHANGED
|
@@ -15,10 +15,7 @@ const chalk = require('chalk');
|
|
|
15
15
|
|
|
16
16
|
const program = new Command();
|
|
17
17
|
|
|
18
|
-
program
|
|
19
|
-
.name('musubi-config')
|
|
20
|
-
.description('MUSUBI Project Configuration Manager')
|
|
21
|
-
.version('1.0.0');
|
|
18
|
+
program.name('musubi-config').description('MUSUBI Project Configuration Manager').version('1.0.0');
|
|
22
19
|
|
|
23
20
|
program
|
|
24
21
|
.command('validate')
|
|
@@ -57,7 +54,8 @@ program
|
|
|
57
54
|
console.log(chalk.green('\nā
Configuration is valid'));
|
|
58
55
|
}
|
|
59
56
|
|
|
60
|
-
const exitCode =
|
|
57
|
+
const exitCode =
|
|
58
|
+
result.errors.length > 0 || (options.strict && result.warnings.length > 0) ? 1 : 0;
|
|
61
59
|
process.exit(exitCode);
|
|
62
60
|
} catch (error) {
|
|
63
61
|
console.error(chalk.red(`Error: ${error.message}`));
|
package/bin/musubi-release.js
CHANGED
|
@@ -56,7 +56,7 @@ async function getCurrentVersion() {
|
|
|
56
56
|
*/
|
|
57
57
|
function bumpVersion(version, type) {
|
|
58
58
|
const parts = version.split('.').map(Number);
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
switch (type) {
|
|
61
61
|
case 'major':
|
|
62
62
|
return `${parts[0] + 1}.0.0`;
|
|
@@ -78,19 +78,19 @@ function bumpVersion(version, type) {
|
|
|
78
78
|
*/
|
|
79
79
|
async function preReleaseChecks(options) {
|
|
80
80
|
const checks = [];
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
// Check for uncommitted changes
|
|
83
83
|
if (!options.skipGitCheck) {
|
|
84
84
|
try {
|
|
85
|
-
const status = execSync('git status --porcelain', {
|
|
86
|
-
cwd: projectRoot,
|
|
87
|
-
encoding: 'utf8'
|
|
85
|
+
const status = execSync('git status --porcelain', {
|
|
86
|
+
cwd: projectRoot,
|
|
87
|
+
encoding: 'utf8',
|
|
88
88
|
});
|
|
89
89
|
if (status.trim()) {
|
|
90
|
-
checks.push({
|
|
91
|
-
name: 'Uncommitted changes',
|
|
92
|
-
passed: false,
|
|
93
|
-
message: 'You have uncommitted changes'
|
|
90
|
+
checks.push({
|
|
91
|
+
name: 'Uncommitted changes',
|
|
92
|
+
passed: false,
|
|
93
|
+
message: 'You have uncommitted changes',
|
|
94
94
|
});
|
|
95
95
|
} else {
|
|
96
96
|
checks.push({ name: 'Git status', passed: true });
|
|
@@ -99,7 +99,7 @@ async function preReleaseChecks(options) {
|
|
|
99
99
|
checks.push({ name: 'Git status', passed: false, message: 'Git not available' });
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
// Run tests
|
|
104
104
|
if (!options.skipTests) {
|
|
105
105
|
try {
|
|
@@ -110,7 +110,7 @@ async function preReleaseChecks(options) {
|
|
|
110
110
|
checks.push({ name: 'Tests', passed: false, message: 'Tests failed' });
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
// Check coverage (if available)
|
|
115
115
|
if (!options.skipCoverage) {
|
|
116
116
|
try {
|
|
@@ -119,18 +119,18 @@ async function preReleaseChecks(options) {
|
|
|
119
119
|
const coverage = JSON.parse(await fs.readFile(coveragePath, 'utf8'));
|
|
120
120
|
const totalCoverage = coverage.total?.lines?.pct || 0;
|
|
121
121
|
const threshold = options.coverageThreshold || 70;
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
if (totalCoverage >= threshold) {
|
|
124
|
-
checks.push({
|
|
125
|
-
name: 'Coverage',
|
|
126
|
-
passed: true,
|
|
127
|
-
message: `${totalCoverage}% >= ${threshold}
|
|
124
|
+
checks.push({
|
|
125
|
+
name: 'Coverage',
|
|
126
|
+
passed: true,
|
|
127
|
+
message: `${totalCoverage}% >= ${threshold}%`,
|
|
128
128
|
});
|
|
129
129
|
} else {
|
|
130
|
-
checks.push({
|
|
131
|
-
name: 'Coverage',
|
|
132
|
-
passed: false,
|
|
133
|
-
message: `${totalCoverage}% < ${threshold}
|
|
130
|
+
checks.push({
|
|
131
|
+
name: 'Coverage',
|
|
132
|
+
passed: false,
|
|
133
|
+
message: `${totalCoverage}% < ${threshold}%`,
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -138,7 +138,7 @@ async function preReleaseChecks(options) {
|
|
|
138
138
|
// Coverage check optional
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
return checks;
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -147,9 +147,9 @@ async function preReleaseChecks(options) {
|
|
|
147
147
|
*/
|
|
148
148
|
function createGitTag(version, message) {
|
|
149
149
|
try {
|
|
150
|
-
execSync(`git tag -a v${version} -m "${message}"`, {
|
|
151
|
-
cwd: projectRoot,
|
|
152
|
-
encoding: 'utf8'
|
|
150
|
+
execSync(`git tag -a v${version} -m "${message}"`, {
|
|
151
|
+
cwd: projectRoot,
|
|
152
|
+
encoding: 'utf8',
|
|
153
153
|
});
|
|
154
154
|
return true;
|
|
155
155
|
} catch (error) {
|
|
@@ -184,7 +184,7 @@ function publishToNpm(options = {}) {
|
|
|
184
184
|
if (options.access) cmd += ` --access ${options.access}`;
|
|
185
185
|
if (options.tag) cmd += ` --tag ${options.tag}`;
|
|
186
186
|
if (options.dryRun) cmd += ' --dry-run';
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
execSync(cmd, { cwd: projectRoot, stdio: 'inherit' });
|
|
189
189
|
return true;
|
|
190
190
|
} catch (error) {
|
|
@@ -212,41 +212,43 @@ program
|
|
|
212
212
|
.action(async (type, options) => {
|
|
213
213
|
try {
|
|
214
214
|
console.log(chalk.bold('\nš MUSUBI Release Manager\n'));
|
|
215
|
-
|
|
215
|
+
|
|
216
216
|
// Get current version
|
|
217
217
|
const currentVersion = await getCurrentVersion();
|
|
218
218
|
const newVersion = bumpVersion(currentVersion, type);
|
|
219
|
-
|
|
219
|
+
|
|
220
220
|
console.log(chalk.white(` Current version: ${chalk.gray(currentVersion)}`));
|
|
221
221
|
console.log(chalk.white(` New version: ${chalk.cyan(newVersion)}`));
|
|
222
222
|
console.log();
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
// Pre-release checks
|
|
225
225
|
console.log(chalk.bold('š Pre-release checks:\n'));
|
|
226
226
|
const checks = await preReleaseChecks(options);
|
|
227
|
-
|
|
227
|
+
|
|
228
228
|
let allPassed = true;
|
|
229
229
|
for (const check of checks) {
|
|
230
230
|
if (check.passed) {
|
|
231
|
-
console.log(
|
|
231
|
+
console.log(
|
|
232
|
+
chalk.green(` ā
${check.name}${check.message ? `: ${check.message}` : ''}`)
|
|
233
|
+
);
|
|
232
234
|
} else {
|
|
233
235
|
console.log(chalk.red(` ā ${check.name}: ${check.message}`));
|
|
234
236
|
allPassed = false;
|
|
235
237
|
}
|
|
236
238
|
}
|
|
237
|
-
|
|
239
|
+
|
|
238
240
|
if (!allPassed && !options.dryRun) {
|
|
239
241
|
console.log(chalk.red('\nā Pre-release checks failed. Use --skip-* options to bypass.'));
|
|
240
242
|
process.exit(1);
|
|
241
243
|
}
|
|
242
|
-
|
|
244
|
+
|
|
243
245
|
if (options.dryRun) {
|
|
244
246
|
console.log(chalk.yellow('\nš Dry run - no changes made'));
|
|
245
247
|
return;
|
|
246
248
|
}
|
|
247
|
-
|
|
249
|
+
|
|
248
250
|
console.log();
|
|
249
|
-
|
|
251
|
+
|
|
250
252
|
// Update package.json
|
|
251
253
|
console.log(chalk.white(' š¦ Updating package.json...'));
|
|
252
254
|
const pkg = await readPackageJson();
|
|
@@ -255,7 +257,7 @@ program
|
|
|
255
257
|
await writePackageJson(pkg);
|
|
256
258
|
console.log(chalk.green(' Done'));
|
|
257
259
|
}
|
|
258
|
-
|
|
260
|
+
|
|
259
261
|
// Update CHANGELOG
|
|
260
262
|
if (!options.skipChangelog) {
|
|
261
263
|
console.log(chalk.white(' š Updating CHANGELOG...'));
|
|
@@ -263,7 +265,7 @@ program
|
|
|
263
265
|
const result = await generator.update(newVersion);
|
|
264
266
|
console.log(chalk.green(` Added ${result.commitCount} commits`));
|
|
265
267
|
}
|
|
266
|
-
|
|
268
|
+
|
|
267
269
|
// Git commit
|
|
268
270
|
console.log(chalk.white(' š¾ Committing changes...'));
|
|
269
271
|
execSync(`git add -A && git commit -m "chore(release): v${newVersion}"`, {
|
|
@@ -271,23 +273,22 @@ program
|
|
|
271
273
|
encoding: 'utf8',
|
|
272
274
|
});
|
|
273
275
|
console.log(chalk.green(' Done'));
|
|
274
|
-
|
|
276
|
+
|
|
275
277
|
// Create tag
|
|
276
278
|
if (options.tag !== false) {
|
|
277
279
|
console.log(chalk.white(' š·ļø Creating git tag...'));
|
|
278
280
|
createGitTag(newVersion, `Release v${newVersion}`);
|
|
279
281
|
console.log(chalk.green(' Done'));
|
|
280
282
|
}
|
|
281
|
-
|
|
283
|
+
|
|
282
284
|
// Push
|
|
283
285
|
if (options.push !== false) {
|
|
284
286
|
console.log(chalk.white(' ā¬ļø Pushing to remote...'));
|
|
285
287
|
pushToRemote(options.tag !== false);
|
|
286
288
|
console.log(chalk.green(' Done'));
|
|
287
289
|
}
|
|
288
|
-
|
|
290
|
+
|
|
289
291
|
console.log(chalk.green(`\nā
Released v${newVersion}!\n`));
|
|
290
|
-
|
|
291
292
|
} catch (error) {
|
|
292
293
|
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
293
294
|
process.exit(1);
|
|
@@ -300,9 +301,9 @@ program
|
|
|
300
301
|
.option('--access <access>', 'Package access level (public/restricted)')
|
|
301
302
|
.option('--tag <tag>', 'npm dist-tag')
|
|
302
303
|
.option('--dry-run', 'Run npm publish --dry-run')
|
|
303
|
-
.action(async
|
|
304
|
+
.action(async options => {
|
|
304
305
|
console.log(chalk.bold('\nš¦ Publishing to npm...\n'));
|
|
305
|
-
|
|
306
|
+
|
|
306
307
|
const success = publishToNpm(options);
|
|
307
308
|
if (success) {
|
|
308
309
|
console.log(chalk.green('\nā
Published successfully!'));
|
|
@@ -316,25 +317,25 @@ program
|
|
|
316
317
|
.description('Generate or update CHANGELOG')
|
|
317
318
|
.option('-v, --version <version>', 'Version for the changelog entry')
|
|
318
319
|
.option('-f, --from <tag>', 'Starting tag for commit range')
|
|
319
|
-
.action(async
|
|
320
|
+
.action(async options => {
|
|
320
321
|
try {
|
|
321
322
|
console.log(chalk.bold('\nš Generating CHANGELOG...\n'));
|
|
322
|
-
|
|
323
|
+
|
|
323
324
|
const generator = new ChangelogGenerator(projectRoot);
|
|
324
325
|
const version = options.version || (await getCurrentVersion());
|
|
325
326
|
const result = await generator.update(version, options.from);
|
|
326
|
-
|
|
327
|
+
|
|
327
328
|
console.log(chalk.white(` Version: ${result.version}`));
|
|
328
329
|
console.log(chalk.white(` Commits: ${result.commitCount}`));
|
|
329
330
|
console.log();
|
|
330
|
-
|
|
331
|
+
|
|
331
332
|
// Show category breakdown
|
|
332
333
|
for (const [category, commits] of Object.entries(result.categories)) {
|
|
333
334
|
if (commits.length > 0) {
|
|
334
335
|
console.log(chalk.gray(` ${category}: ${commits.length}`));
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
|
-
|
|
338
|
+
|
|
338
339
|
console.log(chalk.green('\nā
CHANGELOG updated!'));
|
|
339
340
|
} catch (error) {
|
|
340
341
|
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
@@ -348,12 +349,12 @@ program
|
|
|
348
349
|
.option('-v, --version <version>', 'Version for the release notes')
|
|
349
350
|
.option('-f, --from <tag>', 'Starting tag for commit range')
|
|
350
351
|
.option('-o, --output <file>', 'Output file (default: stdout)')
|
|
351
|
-
.action(async
|
|
352
|
+
.action(async options => {
|
|
352
353
|
try {
|
|
353
354
|
const generator = new ChangelogGenerator(projectRoot);
|
|
354
355
|
const version = options.version || (await getCurrentVersion());
|
|
355
356
|
const notes = generator.generateReleaseNotes(version, options.from);
|
|
356
|
-
|
|
357
|
+
|
|
357
358
|
if (options.output) {
|
|
358
359
|
await fs.writeFile(options.output, notes, 'utf8');
|
|
359
360
|
console.log(chalk.green(`ā
Release notes written to ${options.output}`));
|
|
@@ -372,10 +373,10 @@ program
|
|
|
372
373
|
.action(async () => {
|
|
373
374
|
try {
|
|
374
375
|
console.log(chalk.bold('\nš Release Status\n'));
|
|
375
|
-
|
|
376
|
+
|
|
376
377
|
const currentVersion = await getCurrentVersion();
|
|
377
378
|
console.log(chalk.white(` Current version: ${chalk.cyan(currentVersion)}`));
|
|
378
|
-
|
|
379
|
+
|
|
379
380
|
// Get last tag
|
|
380
381
|
try {
|
|
381
382
|
const lastTag = execSync('git describe --tags --abbrev=0', {
|
|
@@ -383,7 +384,7 @@ program
|
|
|
383
384
|
encoding: 'utf8',
|
|
384
385
|
}).trim();
|
|
385
386
|
console.log(chalk.white(` Last tag: ${chalk.gray(lastTag)}`));
|
|
386
|
-
|
|
387
|
+
|
|
387
388
|
// Count commits since last tag
|
|
388
389
|
const commitCount = execSync(`git rev-list ${lastTag}..HEAD --count`, {
|
|
389
390
|
cwd: projectRoot,
|
|
@@ -393,7 +394,7 @@ program
|
|
|
393
394
|
} catch {
|
|
394
395
|
console.log(chalk.gray(' No tags found'));
|
|
395
396
|
}
|
|
396
|
-
|
|
397
|
+
|
|
397
398
|
// Check for uncommitted changes
|
|
398
399
|
const status = execSync('git status --porcelain', {
|
|
399
400
|
cwd: projectRoot,
|
|
@@ -404,7 +405,7 @@ program
|
|
|
404
405
|
} else {
|
|
405
406
|
console.log(chalk.green(' Uncommitted changes: No'));
|
|
406
407
|
}
|
|
407
|
-
|
|
408
|
+
|
|
408
409
|
console.log();
|
|
409
410
|
} catch (error) {
|
|
410
411
|
console.error(chalk.red(`\nā Error: ${error.message}`));
|
package/bin/musubi-workflow.js
CHANGED
|
@@ -58,10 +58,14 @@ async function showStatus() {
|
|
|
58
58
|
|
|
59
59
|
console.log(chalk.bold('\nš Workflow Status\n'));
|
|
60
60
|
console.log(chalk.white(`Feature: ${chalk.cyan(state.feature)}`));
|
|
61
|
-
console.log(
|
|
61
|
+
console.log(
|
|
62
|
+
chalk.white(
|
|
63
|
+
`Mode: ${chalk.magenta(state.mode || 'default')} (${getModeDescription(state.mode || 'medium')})`
|
|
64
|
+
)
|
|
65
|
+
);
|
|
62
66
|
console.log(chalk.white(`Current Stage: ${formatStage(state.currentStage)}`));
|
|
63
67
|
console.log(chalk.white(`Started: ${new Date(state.startedAt).toLocaleString()}`));
|
|
64
|
-
|
|
68
|
+
|
|
65
69
|
// Show mode config
|
|
66
70
|
if (state.config) {
|
|
67
71
|
console.log(chalk.bold('\nāļø Mode Configuration:'));
|
|
@@ -83,7 +87,7 @@ async function showStatus() {
|
|
|
83
87
|
} else {
|
|
84
88
|
stagesToShow = Object.keys(WORKFLOW_STAGES);
|
|
85
89
|
}
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
const currentIndex = stagesToShow.indexOf(state.currentStage);
|
|
88
92
|
|
|
89
93
|
stagesToShow.forEach((stage, index) => {
|
|
@@ -200,19 +204,22 @@ program
|
|
|
200
204
|
.command('init <feature>')
|
|
201
205
|
.description('Initialize a new workflow for a feature')
|
|
202
206
|
.option('-s, --stage <stage>', 'Starting stage (auto-detected based on mode)')
|
|
203
|
-
.option(
|
|
207
|
+
.option(
|
|
208
|
+
'-m, --mode <mode>',
|
|
209
|
+
'Workflow mode: small, medium, or large (auto-detected from feature name)'
|
|
210
|
+
)
|
|
204
211
|
.action(async (feature, options) => {
|
|
205
212
|
try {
|
|
206
213
|
const initOptions = {};
|
|
207
214
|
if (options.stage) initOptions.startStage = options.stage;
|
|
208
215
|
if (options.mode) initOptions.mode = options.mode;
|
|
209
|
-
|
|
216
|
+
|
|
210
217
|
const state = await engine.initWorkflow(feature, initOptions);
|
|
211
218
|
console.log(chalk.green(`\nā
Workflow initialized for "${feature}"`));
|
|
212
219
|
console.log(chalk.cyan(` Mode: ${state.mode} (${getModeDescription(state.mode)})`));
|
|
213
220
|
console.log(chalk.cyan(` Starting: ${formatStage(state.currentStage)}`));
|
|
214
221
|
console.log(chalk.gray(` Coverage: ${state.config.coverageThreshold}%`));
|
|
215
|
-
|
|
222
|
+
|
|
216
223
|
// Show stages for this mode
|
|
217
224
|
const modeManager = engine.getModeManager();
|
|
218
225
|
const stages = await modeManager.getStages(state.mode);
|
|
@@ -235,9 +242,9 @@ program
|
|
|
235
242
|
.action(async () => {
|
|
236
243
|
const modeManager = engine.getModeManager();
|
|
237
244
|
const modes = await modeManager.compareModes();
|
|
238
|
-
|
|
245
|
+
|
|
239
246
|
console.log(chalk.bold('\nš Available Workflow Modes\n'));
|
|
240
|
-
|
|
247
|
+
|
|
241
248
|
modes.forEach(mode => {
|
|
242
249
|
console.log(chalk.bold.cyan(` ${mode.name.toUpperCase()}`));
|
|
243
250
|
console.log(chalk.white(` ${mode.description}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musubi-sdd",
|
|
3
|
-
"version": "5.9.
|
|
3
|
+
"version": "5.9.3",
|
|
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": {
|
|
@@ -270,10 +270,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
270
270
|
const existingContent = await this.readChangelog();
|
|
271
271
|
|
|
272
272
|
// Find insertion point (after header, before first version)
|
|
273
|
-
const headerEndMatch = existingContent.match(
|
|
274
|
-
|
|
275
|
-
);
|
|
276
|
-
|
|
273
|
+
const headerEndMatch = existingContent.match(/# Changelog[\s\S]*?(?=\n## \[|$)/);
|
|
274
|
+
|
|
277
275
|
let newContent;
|
|
278
276
|
if (headerEndMatch) {
|
|
279
277
|
const headerEnd = headerEndMatch[0].length;
|
package/src/index.js
CHANGED
|
@@ -61,7 +61,11 @@ const { ConstitutionalValidator } = require('./validators/constitutional-validat
|
|
|
61
61
|
const { Constitution } = require('./validators/constitution');
|
|
62
62
|
const { DeltaFormatValidator } = require('./validators/delta-format');
|
|
63
63
|
const { TraceabilityValidator } = require('./validators/traceability-validator');
|
|
64
|
-
const {
|
|
64
|
+
const {
|
|
65
|
+
ConstitutionLevelManager,
|
|
66
|
+
EnforcementLevel,
|
|
67
|
+
ArticleId,
|
|
68
|
+
} = require('./validators/constitution-level-manager');
|
|
65
69
|
const { ProjectValidator, DEFAULT_PROJECT_CONFIG } = require('./validators/project-validator');
|
|
66
70
|
|
|
67
71
|
// Orchestration
|
|
@@ -78,7 +82,11 @@ const { DeltaSpecManager } = require('./managers/delta-spec');
|
|
|
78
82
|
const { MemoryCondenser } = require('./managers/memory-condenser');
|
|
79
83
|
const { SkillLoader } = require('./managers/skill-loader');
|
|
80
84
|
const { WorkflowModeManager, DEFAULT_MODES } = require('./managers/workflow-mode-manager');
|
|
81
|
-
const {
|
|
85
|
+
const {
|
|
86
|
+
PackageManager,
|
|
87
|
+
PackageType,
|
|
88
|
+
DEFAULT_COVERAGE_TARGETS,
|
|
89
|
+
} = require('./managers/package-manager');
|
|
82
90
|
|
|
83
91
|
// Monitoring
|
|
84
92
|
const { CostTracker } = require('./monitoring/cost-tracker');
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const fs = require('fs-extra');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const yaml = require('js-yaml');
|
|
11
|
-
const { execSync } = require('child_process');
|
|
11
|
+
const { execSync: _execSync } = require('child_process');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Package types
|
|
@@ -195,7 +195,7 @@ class PackageManager {
|
|
|
195
195
|
if (this._packages) return this._packages;
|
|
196
196
|
|
|
197
197
|
const config = await this.loadConfig();
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
if (config.packages && config.packages.length > 0) {
|
|
200
200
|
this._packages = config.packages;
|
|
201
201
|
} else {
|
|
@@ -283,9 +283,7 @@ class PackageManager {
|
|
|
283
283
|
// Add edges for internal dependencies
|
|
284
284
|
const deps = [
|
|
285
285
|
...(pkg.dependencies || []),
|
|
286
|
-
...(config.dependency_graph?.ignore_dev_dependencies
|
|
287
|
-
? []
|
|
288
|
-
: (pkg.devDependencies || [])),
|
|
286
|
+
...(config.dependency_graph?.ignore_dev_dependencies ? [] : pkg.devDependencies || []),
|
|
289
287
|
];
|
|
290
288
|
|
|
291
289
|
for (const dep of deps) {
|
package/src/managers/workflow.js
CHANGED
|
@@ -131,13 +131,13 @@ class WorkflowEngine {
|
|
|
131
131
|
// Normalize stage name (handle aliases)
|
|
132
132
|
const normalizedTarget = STAGE_ALIASES[targetStage] || targetStage;
|
|
133
133
|
const currentStage = state.currentStage;
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
// Get valid transitions based on mode
|
|
136
136
|
let validTransitions;
|
|
137
137
|
if (state.mode) {
|
|
138
138
|
validTransitions = await this.modeManager.getValidTransitions(state.mode, currentStage);
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
// Fall back to full transitions if mode-specific not available
|
|
142
142
|
if (!validTransitions || validTransitions.length === 0) {
|
|
143
143
|
validTransitions = WORKFLOW_STAGES[currentStage]?.next || [];
|
|
@@ -400,7 +400,7 @@ class WorkflowEngine {
|
|
|
400
400
|
async getValidTransitions() {
|
|
401
401
|
const state = await this.getState();
|
|
402
402
|
if (!state) return [];
|
|
403
|
-
|
|
403
|
+
|
|
404
404
|
// Use mode-specific transitions if available
|
|
405
405
|
if (state.mode) {
|
|
406
406
|
const modeTransitions = await this.modeManager.getValidTransitions(
|
|
@@ -411,7 +411,7 @@ class WorkflowEngine {
|
|
|
411
411
|
return modeTransitions;
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
|
-
|
|
414
|
+
|
|
415
415
|
return WORKFLOW_STAGES[state.currentStage]?.next || [];
|
|
416
416
|
}
|
|
417
417
|
|
|
@@ -66,7 +66,12 @@ const workflowModeSkill = {
|
|
|
66
66
|
inputs: [
|
|
67
67
|
{ name: 'action', type: 'string', description: 'get|detect|compare', required: true },
|
|
68
68
|
{ name: 'mode', type: 'string', description: 'small|medium|large', required: false },
|
|
69
|
-
{
|
|
69
|
+
{
|
|
70
|
+
name: 'featureName',
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Feature name for auto-detection',
|
|
73
|
+
required: false,
|
|
74
|
+
},
|
|
70
75
|
{ name: 'projectPath', type: 'string', description: 'Project root path', required: false },
|
|
71
76
|
],
|
|
72
77
|
outputs: [
|
|
@@ -113,7 +118,12 @@ const packageManagerSkill = {
|
|
|
113
118
|
tags: ['package', 'monorepo', 'dependency', 'coverage', 'graph'],
|
|
114
119
|
inputs: [
|
|
115
120
|
{ name: 'action', type: 'string', description: 'list|graph|validate|coverage', required: true },
|
|
116
|
-
{
|
|
121
|
+
{
|
|
122
|
+
name: 'packageName',
|
|
123
|
+
type: 'string',
|
|
124
|
+
description: 'Package name for coverage lookup',
|
|
125
|
+
required: false,
|
|
126
|
+
},
|
|
117
127
|
{ name: 'projectPath', type: 'string', description: 'Project root path', required: false },
|
|
118
128
|
],
|
|
119
129
|
outputs: [
|
|
@@ -163,8 +173,18 @@ const constitutionLevelSkill = {
|
|
|
163
173
|
tags: ['constitution', 'governance', 'critical', 'advisory', 'flexible', 'articles'],
|
|
164
174
|
inputs: [
|
|
165
175
|
{ name: 'action', type: 'string', description: 'summary|level|validate', required: true },
|
|
166
|
-
{
|
|
167
|
-
|
|
176
|
+
{
|
|
177
|
+
name: 'articleId',
|
|
178
|
+
type: 'string',
|
|
179
|
+
description: 'Article ID (e.g., CONST-001)',
|
|
180
|
+
required: false,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'validation',
|
|
184
|
+
type: 'object',
|
|
185
|
+
description: 'Validation results object',
|
|
186
|
+
required: false,
|
|
187
|
+
},
|
|
168
188
|
{ name: 'projectPath', type: 'string', description: 'Project root path', required: false },
|
|
169
189
|
],
|
|
170
190
|
outputs: [
|
|
@@ -240,7 +260,12 @@ const projectConfigSkill = {
|
|
|
240
260
|
if (!input.dryRun && result.migrated) {
|
|
241
261
|
await validator.saveConfig(result.config);
|
|
242
262
|
}
|
|
243
|
-
return {
|
|
263
|
+
return {
|
|
264
|
+
success: true,
|
|
265
|
+
migrated: result.migrated,
|
|
266
|
+
config: result.config,
|
|
267
|
+
dryRun: input.dryRun,
|
|
268
|
+
};
|
|
244
269
|
}
|
|
245
270
|
case 'show': {
|
|
246
271
|
const report = await validator.generateReport();
|
|
@@ -107,9 +107,9 @@ const SkillCategory = {
|
|
|
107
107
|
VALIDATION: 'validation',
|
|
108
108
|
INTEGRATION: 'integration',
|
|
109
109
|
MONITORING: 'monitoring',
|
|
110
|
-
RELEASE: 'release',
|
|
110
|
+
RELEASE: 'release', // Added for release management
|
|
111
111
|
CONFIGURATION: 'configuration', // Added for project configuration
|
|
112
|
-
WORKFLOW: 'workflow',
|
|
112
|
+
WORKFLOW: 'workflow', // Added for workflow management
|
|
113
113
|
GENERAL: 'general',
|
|
114
114
|
};
|
|
115
115
|
|
|
@@ -39,7 +39,20 @@
|
|
|
39
39
|
"description": "Programming languages used",
|
|
40
40
|
"items": {
|
|
41
41
|
"type": "string",
|
|
42
|
-
"enum": [
|
|
42
|
+
"enum": [
|
|
43
|
+
"javascript",
|
|
44
|
+
"typescript",
|
|
45
|
+
"python",
|
|
46
|
+
"rust",
|
|
47
|
+
"go",
|
|
48
|
+
"java",
|
|
49
|
+
"ruby",
|
|
50
|
+
"markdown",
|
|
51
|
+
"yaml",
|
|
52
|
+
"json",
|
|
53
|
+
"cobol",
|
|
54
|
+
"shell"
|
|
55
|
+
]
|
|
43
56
|
}
|
|
44
57
|
},
|
|
45
58
|
"frameworks": {
|
|
@@ -73,7 +86,10 @@
|
|
|
73
86
|
"naming_conventions": {
|
|
74
87
|
"type": "object",
|
|
75
88
|
"properties": {
|
|
76
|
-
"files": {
|
|
89
|
+
"files": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"enum": ["kebab-case", "camelCase", "PascalCase", "snake_case"]
|
|
92
|
+
},
|
|
77
93
|
"classes": { "type": "string", "enum": ["PascalCase", "camelCase"] },
|
|
78
94
|
"functions": { "type": "string", "enum": ["camelCase", "snake_case"] },
|
|
79
95
|
"constants": { "type": "string", "enum": ["UPPER_SNAKE_CASE", "camelCase"] }
|
|
@@ -183,7 +199,10 @@
|
|
|
183
199
|
"format": { "type": "string", "enum": ["keep-a-changelog", "conventional"] },
|
|
184
200
|
"include_types": {
|
|
185
201
|
"type": "array",
|
|
186
|
-
"items": {
|
|
202
|
+
"items": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"enum": ["feat", "fix", "docs", "style", "refactor", "test", "chore"]
|
|
205
|
+
}
|
|
187
206
|
}
|
|
188
207
|
}
|
|
189
208
|
},
|
|
@@ -208,10 +227,7 @@
|
|
|
208
227
|
"properties": {
|
|
209
228
|
"coverage_threshold": { "type": "integer", "minimum": 50, "maximum": 100 },
|
|
210
229
|
"mock_allowed": {
|
|
211
|
-
"oneOf": [
|
|
212
|
-
{ "type": "boolean" },
|
|
213
|
-
{ "type": "array", "items": { "type": "string" } }
|
|
214
|
-
]
|
|
230
|
+
"oneOf": [{ "type": "boolean" }, { "type": "array", "items": { "type": "string" } }]
|
|
215
231
|
},
|
|
216
232
|
"ears_required": { "type": "boolean" },
|
|
217
233
|
"adr_required": { "type": "boolean" }
|
|
@@ -13,8 +13,8 @@ const yaml = require('js-yaml');
|
|
|
13
13
|
* Enforcement levels
|
|
14
14
|
*/
|
|
15
15
|
const EnforcementLevel = {
|
|
16
|
-
BLOCK: 'block',
|
|
17
|
-
WARN: 'warn',
|
|
16
|
+
BLOCK: 'block', // Violations block progress
|
|
17
|
+
WARN: 'warn', // Violations show warnings
|
|
18
18
|
CONFIGURE: 'configurable', // Can be overridden per project
|
|
19
19
|
};
|
|
20
20
|
|
|
@@ -15,7 +15,7 @@ const glob = require('glob');
|
|
|
15
15
|
const {
|
|
16
16
|
ConstitutionLevelManager,
|
|
17
17
|
ArticleId,
|
|
18
|
-
EnforcementLevel,
|
|
18
|
+
EnforcementLevel: _EnforcementLevel,
|
|
19
19
|
} = require('./constitution-level-manager');
|
|
20
20
|
|
|
21
21
|
class ConstitutionalValidator {
|
|
@@ -165,10 +165,8 @@ class ConstitutionalValidator {
|
|
|
165
165
|
for (const lib of subDirs) {
|
|
166
166
|
// Check multiple possible test directory names
|
|
167
167
|
const testDirs = ['tests', 'test', '__tests__'];
|
|
168
|
-
const hasTestDir = testDirs.some(dir =>
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
|
|
168
|
+
const hasTestDir = testDirs.some(dir => fs.existsSync(path.join(libPath, lib, dir)));
|
|
169
|
+
|
|
172
170
|
// Check for test files in the library root or test subdirectories
|
|
173
171
|
const testFile = glob.sync(path.join(libPath, lib, '**/*.test.{js,ts}'));
|
|
174
172
|
const specFile = glob.sync(path.join(libPath, lib, '**/*.spec.{js,ts}'));
|
|
@@ -427,13 +425,7 @@ class ConstitutionalValidator {
|
|
|
427
425
|
`Create steering/${file}`
|
|
428
426
|
);
|
|
429
427
|
} else {
|
|
430
|
-
this._recordFinding(
|
|
431
|
-
articleId,
|
|
432
|
-
articleName,
|
|
433
|
-
true,
|
|
434
|
-
`Found steering/${file}`,
|
|
435
|
-
null
|
|
436
|
-
);
|
|
428
|
+
this._recordFinding(articleId, articleName, true, `Found steering/${file}`, null);
|
|
437
429
|
}
|
|
438
430
|
}
|
|
439
431
|
}
|
|
@@ -565,7 +557,8 @@ class ConstitutionalValidator {
|
|
|
565
557
|
|
|
566
558
|
for (const file of testFiles) {
|
|
567
559
|
const content = fs.readFileSync(file, 'utf-8');
|
|
568
|
-
const mockMatches =
|
|
560
|
+
const mockMatches =
|
|
561
|
+
content.match(/\b(jest\.mock|sinon\.stub|vi\.mock|mock\()\s*\(['"]([^'"]+)['"]\)/g) || [];
|
|
569
562
|
|
|
570
563
|
for (const match of mockMatches) {
|
|
571
564
|
const isAllowed = allowedMockPatterns.some(pattern =>
|
|
@@ -667,7 +660,9 @@ class ConstitutionalValidator {
|
|
|
667
660
|
console.log(
|
|
668
661
|
`Passes: ${report.summary.passes} | Warnings: ${report.summary.warnings} | Violations: ${report.summary.violations}`
|
|
669
662
|
);
|
|
670
|
-
console.log(
|
|
663
|
+
console.log(
|
|
664
|
+
`Critical: ${criticalViolations.length} | Advisory: ${nonBlockingViolations.length}`
|
|
665
|
+
);
|
|
671
666
|
|
|
672
667
|
if (criticalViolations.length > 0) {
|
|
673
668
|
console.log('\nš« CRITICAL VIOLATIONS (blocking):');
|