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.
- package/README.ja.md +60 -1
- package/README.md +60 -1
- package/bin/musubi-dashboard.js +340 -0
- package/bin/musubi-upgrade.js +395 -0
- package/bin/musubi.js +15 -0
- package/package.json +5 -3
- package/src/cli/dashboard-cli.js +536 -0
- package/src/constitutional/checker.js +633 -0
- package/src/constitutional/ci-reporter.js +338 -0
- package/src/constitutional/index.js +22 -0
- package/src/constitutional/phase-minus-one.js +404 -0
- package/src/constitutional/steering-sync.js +473 -0
- package/src/dashboard/index.js +20 -0
- package/src/dashboard/sprint-planner.js +361 -0
- package/src/dashboard/sprint-reporter.js +378 -0
- package/src/dashboard/transition-recorder.js +209 -0
- package/src/dashboard/workflow-dashboard.js +434 -0
- package/src/enterprise/error-recovery.js +524 -0
- package/src/enterprise/experiment-report.js +573 -0
- package/src/enterprise/index.js +57 -4
- package/src/enterprise/rollback-manager.js +584 -0
- package/src/enterprise/tech-article.js +509 -0
- package/src/traceability/extractor.js +294 -0
- package/src/traceability/gap-detector.js +230 -0
- package/src/traceability/index.js +15 -0
- package/src/traceability/matrix-storage.js +368 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard CLI
|
|
3
|
+
*
|
|
4
|
+
* Command-line interface for dashboard operations.
|
|
5
|
+
*
|
|
6
|
+
* Requirement: IMP-6.2-003-05
|
|
7
|
+
* Design: Section 4.5
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { WorkflowDashboard } = require('../dashboard/workflow-dashboard');
|
|
11
|
+
const { TransitionRecorder } = require('../dashboard/transition-recorder');
|
|
12
|
+
const { SprintPlanner, PRIORITY } = require('../dashboard/sprint-planner');
|
|
13
|
+
const { SprintReporter } = require('../dashboard/sprint-reporter');
|
|
14
|
+
const { TraceabilityExtractor } = require('../traceability/extractor');
|
|
15
|
+
const { GapDetector } = require('../traceability/gap-detector');
|
|
16
|
+
const { MatrixStorage } = require('../traceability/matrix-storage');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* DashboardCLI
|
|
20
|
+
*
|
|
21
|
+
* Provides CLI commands for dashboard operations.
|
|
22
|
+
*/
|
|
23
|
+
class DashboardCLI {
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} config - Configuration options
|
|
26
|
+
*/
|
|
27
|
+
constructor(config = {}) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.dashboard = new WorkflowDashboard(config.dashboardConfig);
|
|
30
|
+
this.recorder = new TransitionRecorder(config.recorderConfig);
|
|
31
|
+
this.planner = new SprintPlanner(config.plannerConfig);
|
|
32
|
+
this.reporter = new SprintReporter(config.reporterConfig);
|
|
33
|
+
this.extractor = new TraceabilityExtractor(config.extractorConfig);
|
|
34
|
+
this.gapDetector = new GapDetector(config.gapDetectorConfig);
|
|
35
|
+
this.matrixStorage = new MatrixStorage(config.matrixConfig);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Execute CLI command
|
|
40
|
+
* @param {string} command - Command name
|
|
41
|
+
* @param {Array} args - Command arguments
|
|
42
|
+
* @param {Object} options - Command options
|
|
43
|
+
* @returns {Promise<Object>} Command result
|
|
44
|
+
*/
|
|
45
|
+
async execute(command, args = [], options = {}) {
|
|
46
|
+
const commands = {
|
|
47
|
+
// Workflow commands
|
|
48
|
+
'workflow:create': () => this.createWorkflow(args[0], options),
|
|
49
|
+
'workflow:status': () => this.getWorkflowStatus(args[0]),
|
|
50
|
+
'workflow:advance': () => this.advanceWorkflow(args[0], options),
|
|
51
|
+
'workflow:list': () => this.listWorkflows(),
|
|
52
|
+
|
|
53
|
+
// Sprint commands
|
|
54
|
+
'sprint:create': () => this.createSprint(options),
|
|
55
|
+
'sprint:start': () => this.startSprint(args[0]),
|
|
56
|
+
'sprint:complete': () => this.completeSprint(args[0]),
|
|
57
|
+
'sprint:status': () => this.getSprintStatus(args[0]),
|
|
58
|
+
'sprint:add-task': () => this.addSprintTask(args[0], options),
|
|
59
|
+
'sprint:report': () => this.generateSprintReport(args[0]),
|
|
60
|
+
|
|
61
|
+
// Traceability commands
|
|
62
|
+
'trace:scan': () => this.scanTraceability(args[0], options),
|
|
63
|
+
'trace:gaps': () => this.detectGaps(args[0]),
|
|
64
|
+
'trace:matrix': () => this.showMatrix(args[0]),
|
|
65
|
+
'trace:save': () => this.saveMatrix(args[0], options),
|
|
66
|
+
|
|
67
|
+
// Summary commands
|
|
68
|
+
'summary': () => this.getSummary(args[0]),
|
|
69
|
+
'help': () => this.showHelp()
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handler = commands[command];
|
|
73
|
+
if (!handler) {
|
|
74
|
+
throw new Error(`Unknown command: ${command}. Use 'help' to see available commands.`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return await handler();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create a new workflow
|
|
82
|
+
* @param {string} featureId - Feature ID
|
|
83
|
+
* @param {Object} options - Options
|
|
84
|
+
* @returns {Promise<Object>} Created workflow
|
|
85
|
+
*/
|
|
86
|
+
async createWorkflow(featureId, options = {}) {
|
|
87
|
+
if (!featureId) {
|
|
88
|
+
throw new Error('Feature ID is required');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const workflow = await this.dashboard.createWorkflow(featureId, {
|
|
92
|
+
title: options.name || featureId,
|
|
93
|
+
description: options.description || ''
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
message: `Workflow created: ${workflow.featureId}`,
|
|
99
|
+
workflow
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get workflow status
|
|
105
|
+
* @param {string} workflowId - Workflow ID
|
|
106
|
+
* @returns {Promise<Object>} Workflow status
|
|
107
|
+
*/
|
|
108
|
+
async getWorkflowStatus(workflowId) {
|
|
109
|
+
if (!workflowId) {
|
|
110
|
+
throw new Error('Workflow ID is required');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const workflow = await this.dashboard.getWorkflow(workflowId);
|
|
114
|
+
if (!workflow) {
|
|
115
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const summary = await this.dashboard.getSummary(workflowId);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
workflow,
|
|
123
|
+
summary
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Advance workflow to next stage
|
|
129
|
+
* @param {string} workflowId - Workflow ID
|
|
130
|
+
* @param {Object} options - Options
|
|
131
|
+
* @returns {Promise<Object>} Updated workflow
|
|
132
|
+
*/
|
|
133
|
+
async advanceWorkflow(workflowId, options = {}) {
|
|
134
|
+
if (!workflowId) {
|
|
135
|
+
throw new Error('Workflow ID is required');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const workflow = await this.dashboard.getWorkflow(workflowId);
|
|
139
|
+
if (!workflow) {
|
|
140
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const nextStage = options.toStage || this.getNextStage(workflow.currentStage);
|
|
144
|
+
|
|
145
|
+
// Record transition
|
|
146
|
+
await this.recorder.recordTransition(workflowId, {
|
|
147
|
+
fromStage: workflow.currentStage,
|
|
148
|
+
toStage: nextStage,
|
|
149
|
+
reviewer: options.reviewer,
|
|
150
|
+
status: 'approved'
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Complete current stage
|
|
154
|
+
await this.dashboard.updateStage(
|
|
155
|
+
workflowId,
|
|
156
|
+
workflow.currentStage,
|
|
157
|
+
'completed'
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Start next stage
|
|
161
|
+
const updated = await this.dashboard.updateStage(
|
|
162
|
+
workflowId,
|
|
163
|
+
nextStage,
|
|
164
|
+
'in-progress'
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
success: true,
|
|
169
|
+
message: `Workflow advanced to ${updated.currentStage}`,
|
|
170
|
+
workflow: updated
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* List all workflows
|
|
176
|
+
* @returns {Promise<Object>} Workflow list
|
|
177
|
+
*/
|
|
178
|
+
async listWorkflows() {
|
|
179
|
+
const workflows = await this.dashboard.listWorkflows();
|
|
180
|
+
const workflowSummaries = [];
|
|
181
|
+
|
|
182
|
+
for (const w of workflows) {
|
|
183
|
+
const completion = await this.dashboard.calculateCompletion(w.featureId);
|
|
184
|
+
workflowSummaries.push({
|
|
185
|
+
id: w.featureId,
|
|
186
|
+
name: w.title,
|
|
187
|
+
currentStage: w.currentStage,
|
|
188
|
+
status: w.status || 'active',
|
|
189
|
+
completion
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
count: workflows.length,
|
|
196
|
+
workflows: workflowSummaries
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create a new sprint
|
|
202
|
+
* @param {Object} options - Sprint options
|
|
203
|
+
* @returns {Promise<Object>} Created sprint
|
|
204
|
+
*/
|
|
205
|
+
async createSprint(options = {}) {
|
|
206
|
+
const sprint = await this.planner.createSprint({
|
|
207
|
+
sprintId: options.id,
|
|
208
|
+
name: options.name,
|
|
209
|
+
featureId: options.featureId,
|
|
210
|
+
goal: options.goal,
|
|
211
|
+
velocity: options.velocity ? parseInt(options.velocity) : undefined
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
message: `Sprint created: ${sprint.id}`,
|
|
217
|
+
sprint
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Start a sprint
|
|
223
|
+
* @param {string} sprintId - Sprint ID
|
|
224
|
+
* @returns {Promise<Object>} Started sprint
|
|
225
|
+
*/
|
|
226
|
+
async startSprint(sprintId) {
|
|
227
|
+
if (!sprintId) {
|
|
228
|
+
throw new Error('Sprint ID is required');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const sprint = await this.planner.startSprint(sprintId);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
message: `Sprint started: ${sprint.id}`,
|
|
236
|
+
sprint
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Complete a sprint
|
|
242
|
+
* @param {string} sprintId - Sprint ID
|
|
243
|
+
* @returns {Promise<Object>} Completed sprint
|
|
244
|
+
*/
|
|
245
|
+
async completeSprint(sprintId) {
|
|
246
|
+
if (!sprintId) {
|
|
247
|
+
throw new Error('Sprint ID is required');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const sprint = await this.planner.completeSprint(sprintId);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
message: `Sprint completed: ${sprint.id}`,
|
|
255
|
+
sprint
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get sprint status
|
|
261
|
+
* @param {string} sprintId - Sprint ID
|
|
262
|
+
* @returns {Promise<Object>} Sprint status
|
|
263
|
+
*/
|
|
264
|
+
async getSprintStatus(sprintId) {
|
|
265
|
+
if (!sprintId) {
|
|
266
|
+
throw new Error('Sprint ID is required');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const sprint = await this.planner.getSprint(sprintId);
|
|
270
|
+
if (!sprint) {
|
|
271
|
+
throw new Error(`Sprint not found: ${sprintId}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const metrics = await this.planner.getMetrics(sprintId);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
sprint,
|
|
279
|
+
metrics
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Add task to sprint
|
|
285
|
+
* @param {string} sprintId - Sprint ID
|
|
286
|
+
* @param {Object} options - Task options
|
|
287
|
+
* @returns {Promise<Object>} Updated sprint
|
|
288
|
+
*/
|
|
289
|
+
async addSprintTask(sprintId, options = {}) {
|
|
290
|
+
if (!sprintId) {
|
|
291
|
+
throw new Error('Sprint ID is required');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!options.title) {
|
|
295
|
+
throw new Error('Task title is required');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const sprint = await this.planner.addTasks(sprintId, [{
|
|
299
|
+
id: options.taskId,
|
|
300
|
+
title: options.title,
|
|
301
|
+
description: options.description,
|
|
302
|
+
requirementId: options.requirementId,
|
|
303
|
+
storyPoints: options.points ? parseInt(options.points) : 1,
|
|
304
|
+
priority: options.priority || PRIORITY.MEDIUM
|
|
305
|
+
}]);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
success: true,
|
|
309
|
+
message: 'Task added to sprint',
|
|
310
|
+
sprint
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Generate sprint report
|
|
316
|
+
* @param {string} sprintId - Sprint ID
|
|
317
|
+
* @returns {Promise<Object>} Sprint report
|
|
318
|
+
*/
|
|
319
|
+
async generateSprintReport(sprintId) {
|
|
320
|
+
if (!sprintId) {
|
|
321
|
+
throw new Error('Sprint ID is required');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const sprint = await this.planner.getSprint(sprintId);
|
|
325
|
+
if (!sprint) {
|
|
326
|
+
throw new Error(`Sprint not found: ${sprintId}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const markdown = await this.reporter.generateMarkdownReport(sprint);
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
message: 'Report generated',
|
|
334
|
+
report: markdown
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Scan for traceability
|
|
340
|
+
* @param {string} directory - Directory to scan
|
|
341
|
+
* @param {Object} options - Scan options
|
|
342
|
+
* @returns {Promise<Object>} Scan results
|
|
343
|
+
*/
|
|
344
|
+
async scanTraceability(directory = '.', options = {}) {
|
|
345
|
+
const artifacts = await this.extractor.scanDirectory(directory, {
|
|
346
|
+
extensions: options.extensions ? options.extensions.split(',') : undefined
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
success: true,
|
|
351
|
+
directory,
|
|
352
|
+
artifacts: artifacts.length,
|
|
353
|
+
requirements: new Set(artifacts.map(a => a.requirementId)).size,
|
|
354
|
+
results: artifacts
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Detect traceability gaps
|
|
360
|
+
* @param {string} directory - Directory to analyze
|
|
361
|
+
* @returns {Promise<Object>} Gap detection results
|
|
362
|
+
*/
|
|
363
|
+
async detectGaps(directory = '.') {
|
|
364
|
+
// Scan artifacts
|
|
365
|
+
const codeArtifacts = await this.extractor.scanDirectory(directory, {
|
|
366
|
+
extensions: ['.js', '.ts']
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const testArtifacts = await this.extractor.scanDirectory(directory, {
|
|
370
|
+
extensions: ['.test.js', '.spec.js', '.test.ts', '.spec.ts']
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Build matrix in format expected by GapDetector
|
|
374
|
+
const matrix = {};
|
|
375
|
+
const allRequirements = new Set();
|
|
376
|
+
|
|
377
|
+
for (const artifact of codeArtifacts) {
|
|
378
|
+
allRequirements.add(artifact.requirementId);
|
|
379
|
+
if (!matrix[artifact.requirementId]) {
|
|
380
|
+
matrix[artifact.requirementId] = {
|
|
381
|
+
requirementId: artifact.requirementId,
|
|
382
|
+
code: [],
|
|
383
|
+
tests: [],
|
|
384
|
+
design: [],
|
|
385
|
+
commits: []
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
matrix[artifact.requirementId].code.push(artifact);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
for (const artifact of testArtifacts) {
|
|
392
|
+
allRequirements.add(artifact.requirementId);
|
|
393
|
+
if (!matrix[artifact.requirementId]) {
|
|
394
|
+
matrix[artifact.requirementId] = {
|
|
395
|
+
requirementId: artifact.requirementId,
|
|
396
|
+
code: [],
|
|
397
|
+
tests: [],
|
|
398
|
+
design: [],
|
|
399
|
+
commits: []
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
matrix[artifact.requirementId].tests.push(artifact);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Detect gaps using getGapReport which calls detectGaps internally
|
|
406
|
+
const links = Object.values(matrix);
|
|
407
|
+
const report = this.gapDetector.getGapReport(links);
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
success: true,
|
|
411
|
+
requirements: allRequirements.size,
|
|
412
|
+
gaps: report.gaps.length,
|
|
413
|
+
report
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Show traceability matrix
|
|
419
|
+
* @param {string} matrixId - Matrix ID to load
|
|
420
|
+
* @returns {Promise<Object>} Matrix data
|
|
421
|
+
*/
|
|
422
|
+
async showMatrix(matrixId) {
|
|
423
|
+
if (matrixId) {
|
|
424
|
+
const matrix = await this.matrixStorage.load(matrixId);
|
|
425
|
+
if (!matrix) {
|
|
426
|
+
throw new Error(`Matrix not found: ${matrixId}`);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
success: true,
|
|
430
|
+
matrix
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const matrices = await this.matrixStorage.list();
|
|
435
|
+
return {
|
|
436
|
+
success: true,
|
|
437
|
+
matrices
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Save traceability matrix
|
|
443
|
+
* @param {string} directory - Directory to scan
|
|
444
|
+
* @param {Object} options - Save options
|
|
445
|
+
* @returns {Promise<Object>} Save result
|
|
446
|
+
*/
|
|
447
|
+
async saveMatrix(directory = '.', options = {}) {
|
|
448
|
+
const codeArtifacts = await this.extractor.scanDirectory(directory);
|
|
449
|
+
|
|
450
|
+
const entries = this.extractor.groupByRequirement(codeArtifacts);
|
|
451
|
+
|
|
452
|
+
const featureId = options.id || `MATRIX-${Date.now()}`;
|
|
453
|
+
const matrix = {
|
|
454
|
+
name: options.name || 'Traceability Matrix',
|
|
455
|
+
entries,
|
|
456
|
+
createdAt: new Date().toISOString()
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const filePath = await this.matrixStorage.save(featureId, matrix);
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
success: true,
|
|
463
|
+
message: `Matrix saved: ${featureId}`,
|
|
464
|
+
path: filePath
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Get overall summary
|
|
470
|
+
* @param {string} featureId - Feature ID
|
|
471
|
+
* @returns {Promise<Object>} Summary
|
|
472
|
+
*/
|
|
473
|
+
async getSummary(featureId) {
|
|
474
|
+
const workflows = await this.dashboard.listWorkflows();
|
|
475
|
+
const filteredWorkflows = featureId
|
|
476
|
+
? workflows.filter(w => w.featureId === featureId)
|
|
477
|
+
: workflows;
|
|
478
|
+
|
|
479
|
+
const summary = {
|
|
480
|
+
workflows: {
|
|
481
|
+
total: filteredWorkflows.length,
|
|
482
|
+
active: filteredWorkflows.filter(w => w.status === 'active').length,
|
|
483
|
+
completed: filteredWorkflows.filter(w => w.status === 'completed').length
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
success: true,
|
|
489
|
+
summary
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Show help
|
|
495
|
+
* @returns {Object} Help information
|
|
496
|
+
*/
|
|
497
|
+
showHelp() {
|
|
498
|
+
return {
|
|
499
|
+
success: true,
|
|
500
|
+
commands: {
|
|
501
|
+
'workflow:create <featureId>': 'Create a new workflow',
|
|
502
|
+
'workflow:status <workflowId>': 'Get workflow status',
|
|
503
|
+
'workflow:advance <workflowId>': 'Advance workflow to next stage',
|
|
504
|
+
'workflow:list': 'List all workflows',
|
|
505
|
+
'sprint:create': 'Create a new sprint',
|
|
506
|
+
'sprint:start <sprintId>': 'Start a sprint',
|
|
507
|
+
'sprint:complete <sprintId>': 'Complete a sprint',
|
|
508
|
+
'sprint:status <sprintId>': 'Get sprint status',
|
|
509
|
+
'sprint:add-task <sprintId>': 'Add task to sprint',
|
|
510
|
+
'sprint:report <sprintId>': 'Generate sprint report',
|
|
511
|
+
'trace:scan [directory]': 'Scan for traceability',
|
|
512
|
+
'trace:gaps [directory]': 'Detect traceability gaps',
|
|
513
|
+
'trace:matrix [matrixId]': 'Show traceability matrix',
|
|
514
|
+
'trace:save [directory]': 'Save traceability matrix',
|
|
515
|
+
'summary [featureId]': 'Get overall summary',
|
|
516
|
+
'help': 'Show this help'
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get next stage in workflow
|
|
523
|
+
* @param {string} currentStage - Current stage
|
|
524
|
+
* @returns {string} Next stage
|
|
525
|
+
*/
|
|
526
|
+
getNextStage(currentStage) {
|
|
527
|
+
const stageOrder = ['steering', 'requirements', 'design', 'implementation', 'validation'];
|
|
528
|
+
const currentIndex = stageOrder.indexOf(currentStage);
|
|
529
|
+
if (currentIndex === -1 || currentIndex === stageOrder.length - 1) {
|
|
530
|
+
return currentStage;
|
|
531
|
+
}
|
|
532
|
+
return stageOrder[currentIndex + 1];
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
module.exports = { DashboardCLI };
|