musubi-sdd 3.0.1 → 3.5.1
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-change.js +623 -10
- package/bin/musubi-orchestrate.js +456 -0
- package/bin/musubi-trace.js +393 -0
- package/package.json +3 -2
- package/src/analyzers/impact-analyzer.js +682 -0
- package/src/integrations/cicd.js +782 -0
- package/src/integrations/documentation.js +740 -0
- package/src/integrations/examples.js +789 -0
- package/src/integrations/index.js +23 -0
- package/src/integrations/platforms.js +929 -0
- package/src/managers/delta-spec.js +484 -0
- package/src/monitoring/incident-manager.js +890 -0
- package/src/monitoring/index.js +633 -0
- package/src/monitoring/observability.js +938 -0
- package/src/monitoring/release-manager.js +622 -0
- package/src/orchestration/index.js +168 -0
- package/src/orchestration/orchestration-engine.js +409 -0
- package/src/orchestration/pattern-registry.js +319 -0
- package/src/orchestration/patterns/auto.js +386 -0
- package/src/orchestration/patterns/group-chat.js +395 -0
- package/src/orchestration/patterns/human-in-loop.js +506 -0
- package/src/orchestration/patterns/nested.js +322 -0
- package/src/orchestration/patterns/sequential.js +278 -0
- package/src/orchestration/patterns/swarm.js +395 -0
- package/src/orchestration/workflow-orchestrator.js +738 -0
- package/src/reporters/coverage-report.js +452 -0
- package/src/reporters/traceability-matrix-report.js +684 -0
- package/src/steering/advanced-validation.js +812 -0
- package/src/steering/auto-updater.js +670 -0
- package/src/steering/index.js +119 -0
- package/src/steering/quality-metrics.js +650 -0
- package/src/steering/template-constraints.js +789 -0
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
- package/src/templates/agents/codex/AGENTS.md +36 -1
- package/src/templates/agents/cursor/AGENTS.md +36 -1
- package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
- package/src/templates/agents/github-copilot/AGENTS.md +65 -1
- package/src/templates/agents/qwen-code/QWEN.md +36 -1
- package/src/templates/agents/windsurf/AGENTS.md +36 -1
- package/src/templates/shared/delta-spec-template.md +246 -0
- package/src/validators/delta-format.js +474 -0
- package/src/validators/traceability-validator.js +561 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delta Specification Manager
|
|
3
|
+
* Handles ADDED/MODIFIED/REMOVED/RENAMED delta specifications for brownfield projects
|
|
4
|
+
*
|
|
5
|
+
* @module managers/delta-spec
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Delta operation types
|
|
13
|
+
*/
|
|
14
|
+
const DeltaType = {
|
|
15
|
+
ADDED: 'ADDED',
|
|
16
|
+
MODIFIED: 'MODIFIED',
|
|
17
|
+
REMOVED: 'REMOVED',
|
|
18
|
+
RENAMED: 'RENAMED'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Delta specification structure
|
|
23
|
+
* @typedef {Object} DeltaSpec
|
|
24
|
+
* @property {string} id - Unique delta ID (e.g., DELTA-AUTH-001)
|
|
25
|
+
* @property {string} type - Delta type (ADDED, MODIFIED, REMOVED, RENAMED)
|
|
26
|
+
* @property {string} target - Target requirement/component ID
|
|
27
|
+
* @property {string} description - Change description
|
|
28
|
+
* @property {string} rationale - Reason for change
|
|
29
|
+
* @property {string[]} impactedAreas - Areas affected by change
|
|
30
|
+
* @property {Object} before - Previous state (for MODIFIED/RENAMED)
|
|
31
|
+
* @property {Object} after - New state (for ADDED/MODIFIED/RENAMED)
|
|
32
|
+
* @property {string} status - Status (proposed, approved, implemented, archived)
|
|
33
|
+
* @property {string} createdAt - Creation timestamp
|
|
34
|
+
* @property {string} updatedAt - Last update timestamp
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
class DeltaSpecManager {
|
|
38
|
+
constructor(projectRoot = process.cwd()) {
|
|
39
|
+
this.projectRoot = projectRoot;
|
|
40
|
+
this.changesDir = path.join(projectRoot, 'storage', 'changes');
|
|
41
|
+
this.specsDir = path.join(projectRoot, 'storage', 'specs');
|
|
42
|
+
this.archiveDir = path.join(projectRoot, 'storage', 'archive');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Initialize delta spec directories
|
|
47
|
+
*/
|
|
48
|
+
init() {
|
|
49
|
+
const dirs = [this.changesDir, this.specsDir, this.archiveDir];
|
|
50
|
+
dirs.forEach(dir => {
|
|
51
|
+
if (!fs.existsSync(dir)) {
|
|
52
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a new delta specification
|
|
59
|
+
* @param {Object} options - Delta spec options
|
|
60
|
+
* @returns {DeltaSpec} Created delta spec
|
|
61
|
+
*/
|
|
62
|
+
create(options) {
|
|
63
|
+
const {
|
|
64
|
+
id,
|
|
65
|
+
type,
|
|
66
|
+
target,
|
|
67
|
+
description,
|
|
68
|
+
rationale,
|
|
69
|
+
impactedAreas = [],
|
|
70
|
+
before = null,
|
|
71
|
+
after = null
|
|
72
|
+
} = options;
|
|
73
|
+
|
|
74
|
+
// Validate required fields
|
|
75
|
+
if (!id) throw new Error('Delta ID is required');
|
|
76
|
+
if (!type || !Object.values(DeltaType).includes(type)) {
|
|
77
|
+
throw new Error(`Invalid delta type. Must be one of: ${Object.values(DeltaType).join(', ')}`);
|
|
78
|
+
}
|
|
79
|
+
if (!target) throw new Error('Target is required');
|
|
80
|
+
if (!description) throw new Error('Description is required');
|
|
81
|
+
|
|
82
|
+
// Validate type-specific requirements
|
|
83
|
+
if (type === DeltaType.MODIFIED && !before) {
|
|
84
|
+
throw new Error('MODIFIED delta requires "before" state');
|
|
85
|
+
}
|
|
86
|
+
if (type === DeltaType.RENAMED && (!before || !after)) {
|
|
87
|
+
throw new Error('RENAMED delta requires both "before" and "after" states');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
const deltaSpec = {
|
|
92
|
+
id,
|
|
93
|
+
type,
|
|
94
|
+
target,
|
|
95
|
+
description,
|
|
96
|
+
rationale: rationale || '',
|
|
97
|
+
impactedAreas,
|
|
98
|
+
before,
|
|
99
|
+
after,
|
|
100
|
+
status: 'proposed',
|
|
101
|
+
createdAt: now,
|
|
102
|
+
updatedAt: now
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Save delta spec
|
|
106
|
+
this.save(deltaSpec);
|
|
107
|
+
|
|
108
|
+
return deltaSpec;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Save delta spec to file
|
|
113
|
+
* @param {DeltaSpec} deltaSpec - Delta spec to save
|
|
114
|
+
*/
|
|
115
|
+
save(deltaSpec) {
|
|
116
|
+
this.init();
|
|
117
|
+
|
|
118
|
+
const changeDir = path.join(this.changesDir, deltaSpec.id);
|
|
119
|
+
if (!fs.existsSync(changeDir)) {
|
|
120
|
+
fs.mkdirSync(changeDir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const content = this.formatDeltaSpec(deltaSpec);
|
|
124
|
+
const filePath = path.join(changeDir, 'delta.md');
|
|
125
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
126
|
+
|
|
127
|
+
// Also save as JSON for programmatic access
|
|
128
|
+
const jsonPath = path.join(changeDir, 'delta.json');
|
|
129
|
+
fs.writeFileSync(jsonPath, JSON.stringify(deltaSpec, null, 2), 'utf-8');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Format delta spec as markdown
|
|
134
|
+
* @param {DeltaSpec} deltaSpec - Delta spec to format
|
|
135
|
+
* @returns {string} Markdown content
|
|
136
|
+
*/
|
|
137
|
+
formatDeltaSpec(deltaSpec) {
|
|
138
|
+
const lines = [
|
|
139
|
+
`# Delta Specification: ${deltaSpec.id}`,
|
|
140
|
+
'',
|
|
141
|
+
`**Type**: ${deltaSpec.type}`,
|
|
142
|
+
`**Target**: ${deltaSpec.target}`,
|
|
143
|
+
`**Status**: ${deltaSpec.status}`,
|
|
144
|
+
`**Created**: ${deltaSpec.createdAt}`,
|
|
145
|
+
`**Updated**: ${deltaSpec.updatedAt}`,
|
|
146
|
+
'',
|
|
147
|
+
'## Description',
|
|
148
|
+
'',
|
|
149
|
+
deltaSpec.description,
|
|
150
|
+
'',
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
if (deltaSpec.rationale) {
|
|
154
|
+
lines.push('## Rationale', '', deltaSpec.rationale, '');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (deltaSpec.impactedAreas.length > 0) {
|
|
158
|
+
lines.push('## Impacted Areas', '');
|
|
159
|
+
deltaSpec.impactedAreas.forEach(area => {
|
|
160
|
+
lines.push(`- ${area}`);
|
|
161
|
+
});
|
|
162
|
+
lines.push('');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (deltaSpec.before) {
|
|
166
|
+
lines.push('## Before State', '', '```');
|
|
167
|
+
if (typeof deltaSpec.before === 'object') {
|
|
168
|
+
lines.push(JSON.stringify(deltaSpec.before, null, 2));
|
|
169
|
+
} else {
|
|
170
|
+
lines.push(String(deltaSpec.before));
|
|
171
|
+
}
|
|
172
|
+
lines.push('```', '');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (deltaSpec.after) {
|
|
176
|
+
lines.push('## After State', '', '```');
|
|
177
|
+
if (typeof deltaSpec.after === 'object') {
|
|
178
|
+
lines.push(JSON.stringify(deltaSpec.after, null, 2));
|
|
179
|
+
} else {
|
|
180
|
+
lines.push(String(deltaSpec.after));
|
|
181
|
+
}
|
|
182
|
+
lines.push('```', '');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return lines.join('\n');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Load delta spec from file
|
|
190
|
+
* @param {string} id - Delta ID
|
|
191
|
+
* @returns {DeltaSpec|null} Loaded delta spec or null
|
|
192
|
+
*/
|
|
193
|
+
load(id) {
|
|
194
|
+
const jsonPath = path.join(this.changesDir, id, 'delta.json');
|
|
195
|
+
if (!fs.existsSync(jsonPath)) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const content = fs.readFileSync(jsonPath, 'utf-8');
|
|
199
|
+
return JSON.parse(content);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* List all delta specs
|
|
204
|
+
* @param {Object} options - Filter options
|
|
205
|
+
* @returns {DeltaSpec[]} List of delta specs
|
|
206
|
+
*/
|
|
207
|
+
list(options = {}) {
|
|
208
|
+
const { status, type } = options;
|
|
209
|
+
|
|
210
|
+
if (!fs.existsSync(this.changesDir)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const entries = fs.readdirSync(this.changesDir, { withFileTypes: true });
|
|
215
|
+
const deltas = [];
|
|
216
|
+
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
if (entry.isDirectory()) {
|
|
219
|
+
const delta = this.load(entry.name);
|
|
220
|
+
if (delta) {
|
|
221
|
+
// Apply filters
|
|
222
|
+
if (status && delta.status !== status) continue;
|
|
223
|
+
if (type && delta.type !== type) continue;
|
|
224
|
+
deltas.push(delta);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return deltas.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Update delta spec status
|
|
234
|
+
* @param {string} id - Delta ID
|
|
235
|
+
* @param {string} status - New status
|
|
236
|
+
* @returns {DeltaSpec} Updated delta spec
|
|
237
|
+
*/
|
|
238
|
+
updateStatus(id, status) {
|
|
239
|
+
const validStatuses = ['proposed', 'approved', 'rejected', 'implemented', 'archived'];
|
|
240
|
+
if (!validStatuses.includes(status)) {
|
|
241
|
+
throw new Error(`Invalid status. Must be one of: ${validStatuses.join(', ')}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const delta = this.load(id);
|
|
245
|
+
if (!delta) {
|
|
246
|
+
throw new Error(`Delta not found: ${id}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
delta.status = status;
|
|
250
|
+
delta.updatedAt = new Date().toISOString();
|
|
251
|
+
this.save(delta);
|
|
252
|
+
|
|
253
|
+
return delta;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Archive a completed delta spec
|
|
258
|
+
* Merges delta into specs and moves to archive
|
|
259
|
+
* @param {string} id - Delta ID
|
|
260
|
+
* @returns {Object} Archive result
|
|
261
|
+
*/
|
|
262
|
+
archive(id) {
|
|
263
|
+
const delta = this.load(id);
|
|
264
|
+
if (!delta) {
|
|
265
|
+
throw new Error(`Delta not found: ${id}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (delta.status !== 'implemented') {
|
|
269
|
+
throw new Error('Only implemented deltas can be archived');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Create archive entry
|
|
273
|
+
this.init();
|
|
274
|
+
const archiveFile = path.join(this.archiveDir, `${id}.json`);
|
|
275
|
+
delta.status = 'archived';
|
|
276
|
+
delta.archivedAt = new Date().toISOString();
|
|
277
|
+
fs.writeFileSync(archiveFile, JSON.stringify(delta, null, 2), 'utf-8');
|
|
278
|
+
|
|
279
|
+
// Remove from active changes
|
|
280
|
+
const changeDir = path.join(this.changesDir, id);
|
|
281
|
+
if (fs.existsSync(changeDir)) {
|
|
282
|
+
fs.rmSync(changeDir, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
archived: true,
|
|
287
|
+
id,
|
|
288
|
+
archivePath: archiveFile
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Parse delta markers from markdown content
|
|
294
|
+
* @param {string} content - Markdown content
|
|
295
|
+
* @returns {Object[]} Parsed delta markers
|
|
296
|
+
*/
|
|
297
|
+
parseDeltas(content) {
|
|
298
|
+
const deltaPattern = /\[([A-Z]+)\]\s*([A-Z]+-[A-Z0-9-]+):\s*(.+)/g;
|
|
299
|
+
const deltas = [];
|
|
300
|
+
let match;
|
|
301
|
+
|
|
302
|
+
while ((match = deltaPattern.exec(content)) !== null) {
|
|
303
|
+
const [, type, id, description] = match;
|
|
304
|
+
if (Object.values(DeltaType).includes(type)) {
|
|
305
|
+
deltas.push({
|
|
306
|
+
type,
|
|
307
|
+
id,
|
|
308
|
+
description: description.trim(),
|
|
309
|
+
line: content.substring(0, match.index).split('\n').length
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return deltas;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Validate delta spec format
|
|
319
|
+
* @param {DeltaSpec} deltaSpec - Delta spec to validate
|
|
320
|
+
* @returns {Object} Validation result
|
|
321
|
+
*/
|
|
322
|
+
validate(deltaSpec) {
|
|
323
|
+
const errors = [];
|
|
324
|
+
const warnings = [];
|
|
325
|
+
|
|
326
|
+
// Required fields
|
|
327
|
+
if (!deltaSpec.id) errors.push('Missing delta ID');
|
|
328
|
+
if (!deltaSpec.type) errors.push('Missing delta type');
|
|
329
|
+
if (!deltaSpec.target) errors.push('Missing target');
|
|
330
|
+
if (!deltaSpec.description) errors.push('Missing description');
|
|
331
|
+
|
|
332
|
+
// ID format
|
|
333
|
+
if (deltaSpec.id && !/^[A-Z]+-[A-Z0-9-]+$/.test(deltaSpec.id)) {
|
|
334
|
+
errors.push('Invalid ID format. Expected: PREFIX-IDENTIFIER (e.g., DELTA-AUTH-001)');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Type validation
|
|
338
|
+
if (deltaSpec.type && !Object.values(DeltaType).includes(deltaSpec.type)) {
|
|
339
|
+
errors.push(`Invalid type: ${deltaSpec.type}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Type-specific validation
|
|
343
|
+
if (deltaSpec.type === DeltaType.MODIFIED) {
|
|
344
|
+
if (!deltaSpec.before) warnings.push('MODIFIED delta should have "before" state');
|
|
345
|
+
if (!deltaSpec.after) warnings.push('MODIFIED delta should have "after" state');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (deltaSpec.type === DeltaType.RENAMED) {
|
|
349
|
+
if (!deltaSpec.before) errors.push('RENAMED delta requires "before" state');
|
|
350
|
+
if (!deltaSpec.after) errors.push('RENAMED delta requires "after" state');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Rationale recommended
|
|
354
|
+
if (!deltaSpec.rationale) {
|
|
355
|
+
warnings.push('Rationale is recommended for traceability');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Impacted areas recommended
|
|
359
|
+
if (!deltaSpec.impactedAreas || deltaSpec.impactedAreas.length === 0) {
|
|
360
|
+
warnings.push('Impacted areas should be specified');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
valid: errors.length === 0,
|
|
365
|
+
errors,
|
|
366
|
+
warnings
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Generate impact report for a delta
|
|
372
|
+
* @param {string} id - Delta ID
|
|
373
|
+
* @returns {Object} Impact report
|
|
374
|
+
*/
|
|
375
|
+
generateImpactReport(id) {
|
|
376
|
+
const delta = this.load(id);
|
|
377
|
+
if (!delta) {
|
|
378
|
+
throw new Error(`Delta not found: ${id}`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
id: delta.id,
|
|
383
|
+
type: delta.type,
|
|
384
|
+
target: delta.target,
|
|
385
|
+
description: delta.description,
|
|
386
|
+
impactedAreas: delta.impactedAreas,
|
|
387
|
+
status: delta.status,
|
|
388
|
+
affectedFiles: this.findAffectedFiles(delta),
|
|
389
|
+
recommendations: this.generateRecommendations(delta)
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Find files affected by a delta
|
|
395
|
+
* @param {DeltaSpec} delta - Delta spec
|
|
396
|
+
* @returns {string[]} List of affected files
|
|
397
|
+
*/
|
|
398
|
+
findAffectedFiles(delta) {
|
|
399
|
+
// This would scan the codebase for references to the target
|
|
400
|
+
// Simplified implementation for now
|
|
401
|
+
const affected = [];
|
|
402
|
+
const targetPattern = new RegExp(delta.target.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
403
|
+
|
|
404
|
+
const scanDir = (dir) => {
|
|
405
|
+
if (!fs.existsSync(dir)) return;
|
|
406
|
+
|
|
407
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
408
|
+
for (const entry of entries) {
|
|
409
|
+
const fullPath = path.join(dir, entry.name);
|
|
410
|
+
|
|
411
|
+
// Skip node_modules and hidden directories
|
|
412
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
413
|
+
|
|
414
|
+
if (entry.isDirectory()) {
|
|
415
|
+
scanDir(fullPath);
|
|
416
|
+
} else if (entry.isFile() && /\.(js|ts|md|json)$/.test(entry.name)) {
|
|
417
|
+
try {
|
|
418
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
419
|
+
if (targetPattern.test(content)) {
|
|
420
|
+
affected.push(path.relative(this.projectRoot, fullPath));
|
|
421
|
+
}
|
|
422
|
+
} catch (e) {
|
|
423
|
+
// Skip files that can't be read
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
scanDir(path.join(this.projectRoot, 'src'));
|
|
430
|
+
scanDir(path.join(this.projectRoot, 'storage'));
|
|
431
|
+
|
|
432
|
+
return affected;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Generate recommendations for a delta
|
|
437
|
+
* @param {DeltaSpec} delta - Delta spec
|
|
438
|
+
* @returns {string[]} Recommendations
|
|
439
|
+
*/
|
|
440
|
+
generateRecommendations(delta) {
|
|
441
|
+
const recommendations = [];
|
|
442
|
+
|
|
443
|
+
switch (delta.type) {
|
|
444
|
+
case DeltaType.ADDED:
|
|
445
|
+
recommendations.push('Create new requirement document');
|
|
446
|
+
recommendations.push('Update traceability matrix');
|
|
447
|
+
recommendations.push('Add test cases for new functionality');
|
|
448
|
+
break;
|
|
449
|
+
|
|
450
|
+
case DeltaType.MODIFIED:
|
|
451
|
+
recommendations.push('Review existing tests for compatibility');
|
|
452
|
+
recommendations.push('Update design documents if architecture affected');
|
|
453
|
+
recommendations.push('Check for breaking changes in APIs');
|
|
454
|
+
break;
|
|
455
|
+
|
|
456
|
+
case DeltaType.REMOVED:
|
|
457
|
+
recommendations.push('Remove obsolete test cases');
|
|
458
|
+
recommendations.push('Update documentation to reflect removal');
|
|
459
|
+
recommendations.push('Check for orphaned code dependencies');
|
|
460
|
+
break;
|
|
461
|
+
|
|
462
|
+
case DeltaType.RENAMED:
|
|
463
|
+
recommendations.push('Update all references to renamed entity');
|
|
464
|
+
recommendations.push('Update import statements');
|
|
465
|
+
recommendations.push('Update documentation and comments');
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (delta.impactedAreas.includes('api')) {
|
|
470
|
+
recommendations.push('Consider API versioning for backward compatibility');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (delta.impactedAreas.includes('database')) {
|
|
474
|
+
recommendations.push('Create database migration script');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return recommendations;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
module.exports = {
|
|
482
|
+
DeltaSpecManager,
|
|
483
|
+
DeltaType
|
|
484
|
+
};
|