musubi-sdd 5.1.0 → 5.6.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/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- package/src/validators/traceability-validator.js +7 -11
package/src/gui/server.js
CHANGED
|
@@ -60,17 +60,17 @@ class GUIServer {
|
|
|
60
60
|
*/
|
|
61
61
|
setupReplanningEvents() {
|
|
62
62
|
// Forward replanning events to WebSocket clients
|
|
63
|
-
this.replanningService.on('state:updated',
|
|
64
|
-
this.broadcast({
|
|
65
|
-
type: 'replanning:state',
|
|
66
|
-
data: state
|
|
63
|
+
this.replanningService.on('state:updated', state => {
|
|
64
|
+
this.broadcast({
|
|
65
|
+
type: 'replanning:state',
|
|
66
|
+
data: state,
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
this.replanningService.on('replan:recorded',
|
|
71
|
-
this.broadcast({
|
|
72
|
-
type: 'replanning:event',
|
|
73
|
-
data: event
|
|
70
|
+
this.replanningService.on('replan:recorded', event => {
|
|
71
|
+
this.broadcast({
|
|
72
|
+
type: 'replanning:event',
|
|
73
|
+
data: event,
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
}
|
|
@@ -286,9 +286,9 @@ class GUIServer {
|
|
|
286
286
|
}
|
|
287
287
|
try {
|
|
288
288
|
const { spawn } = require('child_process');
|
|
289
|
-
const
|
|
289
|
+
const _child = spawn('npx', ['musubi-requirements', 'create'], {
|
|
290
290
|
cwd: this.projectPath,
|
|
291
|
-
shell: true
|
|
291
|
+
shell: true,
|
|
292
292
|
});
|
|
293
293
|
res.json({ success: true, message: 'Requirements wizard started' });
|
|
294
294
|
} catch (error) {
|
|
@@ -303,20 +303,20 @@ class GUIServer {
|
|
|
303
303
|
}
|
|
304
304
|
try {
|
|
305
305
|
const { id, title, type, priority, description, feature } = req.body;
|
|
306
|
-
|
|
306
|
+
|
|
307
307
|
if (!id || !title || !description || !feature) {
|
|
308
308
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
const fs = require('fs').promises;
|
|
312
312
|
const specsDir = path.join(this.projectPath, 'storage', 'specs');
|
|
313
|
-
|
|
313
|
+
|
|
314
314
|
// Ensure directory exists
|
|
315
315
|
await fs.mkdir(specsDir, { recursive: true });
|
|
316
|
-
|
|
316
|
+
|
|
317
317
|
const filename = `${feature}-requirements.md`;
|
|
318
318
|
const filepath = path.join(specsDir, filename);
|
|
319
|
-
|
|
319
|
+
|
|
320
320
|
// Check if file exists
|
|
321
321
|
let content = '';
|
|
322
322
|
try {
|
|
@@ -346,9 +346,9 @@ ${description}
|
|
|
346
346
|
|
|
347
347
|
`;
|
|
348
348
|
content += reqContent;
|
|
349
|
-
|
|
349
|
+
|
|
350
350
|
await fs.writeFile(filepath, content, 'utf-8');
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
res.json({ success: true, file: filepath });
|
|
353
353
|
} catch (error) {
|
|
354
354
|
res.status(500).json({ error: error.message });
|
|
@@ -359,26 +359,26 @@ ${description}
|
|
|
359
359
|
try {
|
|
360
360
|
const { spawn } = require('child_process');
|
|
361
361
|
const result = { stdout: '', stderr: '' };
|
|
362
|
-
|
|
362
|
+
|
|
363
363
|
const child = spawn('node', [path.join(__dirname, '../../bin/musubi-validate.js'), 'all'], {
|
|
364
364
|
cwd: this.projectPath,
|
|
365
365
|
shell: false,
|
|
366
|
-
env: { ...process.env, FORCE_COLOR: '0' }
|
|
366
|
+
env: { ...process.env, FORCE_COLOR: '0' },
|
|
367
367
|
});
|
|
368
|
-
|
|
369
|
-
child.stdout.on('data',
|
|
370
|
-
child.stderr.on('data',
|
|
371
|
-
|
|
372
|
-
child.on('error',
|
|
368
|
+
|
|
369
|
+
child.stdout.on('data', data => (result.stdout += data.toString()));
|
|
370
|
+
child.stderr.on('data', data => (result.stderr += data.toString()));
|
|
371
|
+
|
|
372
|
+
child.on('error', error => {
|
|
373
373
|
res.status(500).json({ success: false, error: error.message });
|
|
374
374
|
});
|
|
375
|
-
|
|
376
|
-
child.on('close',
|
|
377
|
-
res.json({
|
|
378
|
-
success: code === 0,
|
|
379
|
-
output: result.stdout || 'Validation completed',
|
|
375
|
+
|
|
376
|
+
child.on('close', code => {
|
|
377
|
+
res.json({
|
|
378
|
+
success: code === 0,
|
|
379
|
+
output: result.stdout || 'Validation completed',
|
|
380
380
|
errors: result.stderr,
|
|
381
|
-
exitCode: code
|
|
381
|
+
exitCode: code,
|
|
382
382
|
});
|
|
383
383
|
});
|
|
384
384
|
} catch (error) {
|
|
@@ -412,20 +412,20 @@ ${description}
|
|
|
412
412
|
setupWebSocket() {
|
|
413
413
|
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
414
414
|
|
|
415
|
-
this.wss.on('connection',
|
|
415
|
+
this.wss.on('connection', ws => {
|
|
416
416
|
this.clients.add(ws);
|
|
417
417
|
|
|
418
418
|
ws.on('close', () => {
|
|
419
419
|
this.clients.delete(ws);
|
|
420
420
|
});
|
|
421
421
|
|
|
422
|
-
ws.on('error',
|
|
422
|
+
ws.on('error', error => {
|
|
423
423
|
console.error('WebSocket error:', error);
|
|
424
424
|
this.clients.delete(ws);
|
|
425
425
|
});
|
|
426
426
|
|
|
427
427
|
// Send initial project state
|
|
428
|
-
this.projectScanner.scan().then(
|
|
428
|
+
this.projectScanner.scan().then(project => {
|
|
429
429
|
ws.send(JSON.stringify({ type: 'project:init', data: project }));
|
|
430
430
|
});
|
|
431
431
|
});
|
|
@@ -438,7 +438,8 @@ ${description}
|
|
|
438
438
|
broadcast(message) {
|
|
439
439
|
const data = JSON.stringify(message);
|
|
440
440
|
for (const client of this.clients) {
|
|
441
|
-
if (client.readyState === 1) {
|
|
441
|
+
if (client.readyState === 1) {
|
|
442
|
+
// WebSocket.OPEN
|
|
442
443
|
client.send(data);
|
|
443
444
|
}
|
|
444
445
|
}
|
|
@@ -449,10 +450,10 @@ ${description}
|
|
|
449
450
|
*/
|
|
450
451
|
setupFileWatcher() {
|
|
451
452
|
this.fileWatcher = new FileWatcher(this.projectPath, {
|
|
452
|
-
ignored: /(^|[
|
|
453
|
+
ignored: /(^|[/\\])\.|node_modules/,
|
|
453
454
|
});
|
|
454
455
|
|
|
455
|
-
this.fileWatcher.on('change', async
|
|
456
|
+
this.fileWatcher.on('change', async filePath => {
|
|
456
457
|
try {
|
|
457
458
|
const project = await this.projectScanner.scan();
|
|
458
459
|
this.broadcast({ type: 'file:changed', data: { path: filePath, project } });
|
|
@@ -461,7 +462,7 @@ ${description}
|
|
|
461
462
|
}
|
|
462
463
|
});
|
|
463
464
|
|
|
464
|
-
this.fileWatcher.on('add', async
|
|
465
|
+
this.fileWatcher.on('add', async filePath => {
|
|
465
466
|
try {
|
|
466
467
|
const project = await this.projectScanner.scan();
|
|
467
468
|
this.broadcast({ type: 'file:added', data: { path: filePath, project } });
|
|
@@ -470,7 +471,7 @@ ${description}
|
|
|
470
471
|
}
|
|
471
472
|
});
|
|
472
473
|
|
|
473
|
-
this.fileWatcher.on('unlink', async
|
|
474
|
+
this.fileWatcher.on('unlink', async filePath => {
|
|
474
475
|
try {
|
|
475
476
|
const project = await this.projectScanner.scan();
|
|
476
477
|
this.broadcast({ type: 'file:removed', data: { path: filePath, project } });
|
|
@@ -487,11 +488,11 @@ ${description}
|
|
|
487
488
|
async start() {
|
|
488
489
|
return new Promise((resolve, reject) => {
|
|
489
490
|
this.httpServer = http.createServer(this.app);
|
|
490
|
-
|
|
491
|
+
|
|
491
492
|
this.setupWebSocket();
|
|
492
493
|
this.setupFileWatcher();
|
|
493
494
|
|
|
494
|
-
this.httpServer.listen(this.port,
|
|
495
|
+
this.httpServer.listen(this.port, err => {
|
|
495
496
|
if (err) {
|
|
496
497
|
reject(err);
|
|
497
498
|
return;
|
|
@@ -537,7 +538,7 @@ ${description}
|
|
|
537
538
|
}
|
|
538
539
|
|
|
539
540
|
if (this.httpServer) {
|
|
540
|
-
return new Promise(
|
|
541
|
+
return new Promise(resolve => {
|
|
541
542
|
this.httpServer.close(resolve);
|
|
542
543
|
});
|
|
543
544
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const chokidar = require('chokidar');
|
|
7
7
|
const EventEmitter = require('events');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* File Watcher - Watches for file changes in the project
|
|
@@ -18,10 +19,10 @@ class FileWatcher extends EventEmitter {
|
|
|
18
19
|
*/
|
|
19
20
|
constructor(projectPath, options = {}) {
|
|
20
21
|
super();
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
this.projectPath = projectPath;
|
|
23
24
|
this.options = {
|
|
24
|
-
ignored: options.ignored || /(^|[
|
|
25
|
+
ignored: options.ignored || /(^|[/\\])\.|node_modules/,
|
|
25
26
|
persistent: true,
|
|
26
27
|
ignoreInitial: true,
|
|
27
28
|
awaitWriteFinish: {
|
|
@@ -42,26 +43,40 @@ class FileWatcher extends EventEmitter {
|
|
|
42
43
|
* Start watching
|
|
43
44
|
*/
|
|
44
45
|
start() {
|
|
45
|
-
const
|
|
46
|
+
const potentialPaths = [
|
|
46
47
|
path.join(this.projectPath, 'steering'),
|
|
47
48
|
path.join(this.projectPath, 'storage'),
|
|
48
49
|
];
|
|
49
50
|
|
|
51
|
+
// Only watch paths that exist to avoid ENOENT errors
|
|
52
|
+
const watchPaths = potentialPaths.filter(p => {
|
|
53
|
+
try {
|
|
54
|
+
return fs.existsSync(p);
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// If no paths exist, watch the project path itself
|
|
61
|
+
if (watchPaths.length === 0) {
|
|
62
|
+
watchPaths.push(this.projectPath);
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
this.watcher = chokidar.watch(watchPaths, this.options);
|
|
51
66
|
|
|
52
|
-
this.watcher.on('change',
|
|
67
|
+
this.watcher.on('change', filePath => {
|
|
53
68
|
this.debounce('change', filePath);
|
|
54
69
|
});
|
|
55
70
|
|
|
56
|
-
this.watcher.on('add',
|
|
71
|
+
this.watcher.on('add', filePath => {
|
|
57
72
|
this.debounce('add', filePath);
|
|
58
73
|
});
|
|
59
74
|
|
|
60
|
-
this.watcher.on('unlink',
|
|
75
|
+
this.watcher.on('unlink', filePath => {
|
|
61
76
|
this.debounce('unlink', filePath);
|
|
62
77
|
});
|
|
63
78
|
|
|
64
|
-
this.watcher.on('error',
|
|
79
|
+
this.watcher.on('error', error => {
|
|
65
80
|
this.emit('error', error);
|
|
66
81
|
});
|
|
67
82
|
|
|
@@ -77,7 +92,7 @@ class FileWatcher extends EventEmitter {
|
|
|
77
92
|
*/
|
|
78
93
|
debounce(event, filePath) {
|
|
79
94
|
const key = `${event}:${filePath}`;
|
|
80
|
-
|
|
95
|
+
|
|
81
96
|
if (this.debounceTimers.has(key)) {
|
|
82
97
|
clearTimeout(this.debounceTimers.get(key));
|
|
83
98
|
}
|
|
@@ -83,8 +83,8 @@ class ProjectScanner {
|
|
|
83
83
|
*/
|
|
84
84
|
async getSteering() {
|
|
85
85
|
const steeringPath = path.join(this.projectPath, 'steering');
|
|
86
|
-
|
|
87
|
-
if (!await fs.pathExists(steeringPath)) {
|
|
86
|
+
|
|
87
|
+
if (!(await fs.pathExists(steeringPath))) {
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -104,8 +104,8 @@ class ProjectScanner {
|
|
|
104
104
|
*/
|
|
105
105
|
async getConstitution() {
|
|
106
106
|
const constitutionPath = path.join(this.projectPath, 'steering', 'rules', 'constitution.md');
|
|
107
|
-
|
|
108
|
-
if (!await fs.pathExists(constitutionPath)) {
|
|
107
|
+
|
|
108
|
+
if (!(await fs.pathExists(constitutionPath))) {
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -146,8 +146,8 @@ class ProjectScanner {
|
|
|
146
146
|
*/
|
|
147
147
|
async getSpecs() {
|
|
148
148
|
const specsPath = path.join(this.projectPath, 'storage', 'specs');
|
|
149
|
-
|
|
150
|
-
if (!await fs.pathExists(specsPath)) {
|
|
149
|
+
|
|
150
|
+
if (!(await fs.pathExists(specsPath))) {
|
|
151
151
|
return [];
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -174,6 +174,11 @@ class ProjectScanner {
|
|
|
174
174
|
*/
|
|
175
175
|
async getSpec(id) {
|
|
176
176
|
const specsPath = path.join(this.projectPath, 'storage', 'specs');
|
|
177
|
+
|
|
178
|
+
if (!(await fs.pathExists(specsPath))) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
177
182
|
const files = await fs.readdir(specsPath);
|
|
178
183
|
|
|
179
184
|
for (const file of files) {
|
|
@@ -181,7 +186,7 @@ class ProjectScanner {
|
|
|
181
186
|
|
|
182
187
|
const filePath = path.join(specsPath, file);
|
|
183
188
|
const spec = await this.parseSpec(filePath);
|
|
184
|
-
|
|
189
|
+
|
|
185
190
|
if (spec && spec.id === id) {
|
|
186
191
|
return spec;
|
|
187
192
|
}
|
|
@@ -232,16 +237,17 @@ class ProjectScanner {
|
|
|
232
237
|
*/
|
|
233
238
|
parseRequirements(content) {
|
|
234
239
|
const requirements = [];
|
|
235
|
-
const reqRegex =
|
|
240
|
+
const reqRegex =
|
|
241
|
+
/#{2,3}\s+(REQ[-\w]+)[:\s]*(.+?)(?=\n#{2,3}\s+REQ|\n#{1,2}\s+[^#]|\n#{1,2}$|$)/gs;
|
|
236
242
|
|
|
237
243
|
let match;
|
|
238
244
|
while ((match = reqRegex.exec(content)) !== null) {
|
|
239
245
|
const id = match[1];
|
|
240
246
|
const body = match[2].trim();
|
|
241
|
-
|
|
247
|
+
|
|
242
248
|
// Try to extract EARS pattern
|
|
243
249
|
const pattern = this.detectEARSPattern(body);
|
|
244
|
-
|
|
250
|
+
|
|
245
251
|
requirements.push({
|
|
246
252
|
id,
|
|
247
253
|
body,
|
|
@@ -387,8 +393,8 @@ class ProjectScanner {
|
|
|
387
393
|
*/
|
|
388
394
|
async getWorkflow() {
|
|
389
395
|
const workflowPath = path.join(this.projectPath, 'steering', 'rules', 'workflow.md');
|
|
390
|
-
|
|
391
|
-
if (!await fs.pathExists(workflowPath)) {
|
|
396
|
+
|
|
397
|
+
if (!(await fs.pathExists(workflowPath))) {
|
|
392
398
|
// Return default workflow stages
|
|
393
399
|
return {
|
|
394
400
|
stages: [
|
|
@@ -427,7 +433,7 @@ class ProjectScanner {
|
|
|
427
433
|
name: match[2].trim(),
|
|
428
434
|
status,
|
|
429
435
|
});
|
|
430
|
-
|
|
436
|
+
|
|
431
437
|
if (status === 'active') {
|
|
432
438
|
currentStage = parseInt(match[1], 10);
|
|
433
439
|
}
|
|
@@ -448,7 +454,7 @@ class ProjectScanner {
|
|
|
448
454
|
};
|
|
449
455
|
|
|
450
456
|
// Check for steering directory
|
|
451
|
-
if (!await this.hasSteering()) {
|
|
457
|
+
if (!(await this.hasSteering())) {
|
|
452
458
|
results.errors.push({
|
|
453
459
|
type: 'missing-steering',
|
|
454
460
|
message: 'steering/ directory not found',
|
|
@@ -495,13 +501,13 @@ class ProjectScanner {
|
|
|
495
501
|
*/
|
|
496
502
|
async readMarkdownFile(filePath) {
|
|
497
503
|
try {
|
|
498
|
-
if (!await fs.pathExists(filePath)) {
|
|
504
|
+
if (!(await fs.pathExists(filePath))) {
|
|
499
505
|
return null;
|
|
500
506
|
}
|
|
501
|
-
|
|
507
|
+
|
|
502
508
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
503
509
|
const { data, content: body } = matter(content);
|
|
504
|
-
|
|
510
|
+
|
|
505
511
|
return {
|
|
506
512
|
path: filePath,
|
|
507
513
|
metadata: data,
|
|
@@ -519,13 +525,13 @@ class ProjectScanner {
|
|
|
519
525
|
*/
|
|
520
526
|
async readYamlFile(filePath) {
|
|
521
527
|
try {
|
|
522
|
-
if (!await fs.pathExists(filePath)) {
|
|
528
|
+
if (!(await fs.pathExists(filePath))) {
|
|
523
529
|
return null;
|
|
524
530
|
}
|
|
525
|
-
|
|
531
|
+
|
|
526
532
|
const yaml = require('js-yaml');
|
|
527
533
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
528
|
-
|
|
534
|
+
|
|
529
535
|
return {
|
|
530
536
|
path: filePath,
|
|
531
537
|
data: yaml.load(content),
|
|
@@ -35,7 +35,7 @@ class ReplanningService extends EventEmitter {
|
|
|
35
35
|
super();
|
|
36
36
|
this.projectPath = projectPath;
|
|
37
37
|
this.storagePath = path.join(projectPath, 'storage', 'replanning');
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// In-memory state (will be populated from storage or engine)
|
|
40
40
|
this.state = {
|
|
41
41
|
status: 'idle',
|
|
@@ -49,8 +49,8 @@ class ReplanningService extends EventEmitter {
|
|
|
49
49
|
successfulReplans: 0,
|
|
50
50
|
averageConfidence: 0,
|
|
51
51
|
goalCompletionRate: 0,
|
|
52
|
-
optimizationsApplied: 0
|
|
53
|
-
}
|
|
52
|
+
optimizationsApplied: 0,
|
|
53
|
+
},
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -106,10 +106,13 @@ class ReplanningService extends EventEmitter {
|
|
|
106
106
|
try {
|
|
107
107
|
const historyDir = path.join(this.storagePath, 'history');
|
|
108
108
|
await fs.mkdir(historyDir, { recursive: true });
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
const files = await fs.readdir(historyDir);
|
|
111
|
-
const jsonFiles = files
|
|
112
|
-
|
|
111
|
+
const jsonFiles = files
|
|
112
|
+
.filter(f => f.endsWith('.json'))
|
|
113
|
+
.sort()
|
|
114
|
+
.reverse();
|
|
115
|
+
|
|
113
116
|
this.state.history = [];
|
|
114
117
|
for (const file of jsonFiles.slice(0, 50)) {
|
|
115
118
|
try {
|
|
@@ -148,7 +151,7 @@ class ReplanningService extends EventEmitter {
|
|
|
148
151
|
overallProgress: 0,
|
|
149
152
|
activeGoals: 0,
|
|
150
153
|
completedGoals: 0,
|
|
151
|
-
milestones: []
|
|
154
|
+
milestones: [],
|
|
152
155
|
};
|
|
153
156
|
}
|
|
154
157
|
}
|
|
@@ -168,7 +171,7 @@ class ReplanningService extends EventEmitter {
|
|
|
168
171
|
currentPath: null,
|
|
169
172
|
optimizedPath: null,
|
|
170
173
|
potentialSavings: 0,
|
|
171
|
-
suggestions: []
|
|
174
|
+
suggestions: [],
|
|
172
175
|
};
|
|
173
176
|
}
|
|
174
177
|
}
|
|
@@ -214,26 +217,26 @@ class ReplanningService extends EventEmitter {
|
|
|
214
217
|
async recordReplan(event) {
|
|
215
218
|
const historyDir = path.join(this.storagePath, 'history');
|
|
216
219
|
await fs.mkdir(historyDir, { recursive: true });
|
|
217
|
-
|
|
220
|
+
|
|
218
221
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
219
222
|
const filename = `replan-${timestamp}.json`;
|
|
220
|
-
|
|
223
|
+
|
|
221
224
|
const entry = {
|
|
222
225
|
id: `replan-${Date.now()}`,
|
|
223
226
|
timestamp: new Date().toISOString(),
|
|
224
|
-
...event
|
|
227
|
+
...event,
|
|
225
228
|
};
|
|
226
|
-
|
|
229
|
+
|
|
227
230
|
await fs.writeFile(path.join(historyDir, filename), JSON.stringify(entry, null, 2));
|
|
228
|
-
|
|
231
|
+
|
|
229
232
|
this.state.history.unshift(entry);
|
|
230
233
|
this.state.lastReplan = entry;
|
|
231
234
|
this.state.metrics.totalReplans++;
|
|
232
|
-
|
|
235
|
+
|
|
233
236
|
if (event.success) {
|
|
234
237
|
this.state.metrics.successfulReplans++;
|
|
235
238
|
}
|
|
236
|
-
|
|
239
|
+
|
|
237
240
|
await this.saveState();
|
|
238
241
|
this.emit('replan:recorded', entry);
|
|
239
242
|
}
|
|
@@ -247,7 +250,7 @@ class ReplanningService extends EventEmitter {
|
|
|
247
250
|
this.getState(),
|
|
248
251
|
this.getGoalProgress(),
|
|
249
252
|
this.getPathOptimization(),
|
|
250
|
-
this.getMetrics()
|
|
253
|
+
this.getMetrics(),
|
|
251
254
|
]);
|
|
252
255
|
|
|
253
256
|
return {
|
|
@@ -255,20 +258,21 @@ class ReplanningService extends EventEmitter {
|
|
|
255
258
|
goalProgress: {
|
|
256
259
|
overall: goalProgress.overallProgress || 0,
|
|
257
260
|
active: goalProgress.activeGoals || 0,
|
|
258
|
-
completed: goalProgress.completedGoals || 0
|
|
261
|
+
completed: goalProgress.completedGoals || 0,
|
|
259
262
|
},
|
|
260
263
|
optimization: {
|
|
261
264
|
status: pathOptimization.status,
|
|
262
|
-
suggestions: pathOptimization.suggestions?.length || 0
|
|
265
|
+
suggestions: pathOptimization.suggestions?.length || 0,
|
|
263
266
|
},
|
|
264
267
|
metrics: {
|
|
265
268
|
totalReplans: metrics.totalReplans || 0,
|
|
266
|
-
successRate:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
successRate:
|
|
270
|
+
metrics.totalReplans > 0
|
|
271
|
+
? Math.round((metrics.successfulReplans / metrics.totalReplans) * 100)
|
|
272
|
+
: 100,
|
|
273
|
+
optimizationsApplied: metrics.optimizationsApplied || 0,
|
|
270
274
|
},
|
|
271
|
-
recentHistory: state.history.slice(0, 5)
|
|
275
|
+
recentHistory: state.history.slice(0, 5),
|
|
272
276
|
};
|
|
273
277
|
}
|
|
274
278
|
}
|
|
@@ -132,7 +132,7 @@ class TraceabilityService {
|
|
|
132
132
|
|
|
133
133
|
try {
|
|
134
134
|
const files = await fs.readdir(specsPath);
|
|
135
|
-
const reqFiles = files.filter(
|
|
135
|
+
const reqFiles = files.filter(f => f.endsWith('-requirements.md'));
|
|
136
136
|
|
|
137
137
|
for (const file of reqFiles) {
|
|
138
138
|
const filePath = path.join(specsPath, file);
|
|
@@ -156,7 +156,7 @@ class TraceabilityService {
|
|
|
156
156
|
// Also check frontmatter for requirements list
|
|
157
157
|
if (frontmatter.requirements) {
|
|
158
158
|
for (const req of frontmatter.requirements) {
|
|
159
|
-
if (!requirements.find(
|
|
159
|
+
if (!requirements.find(r => r.id === req.id)) {
|
|
160
160
|
requirements.push({
|
|
161
161
|
id: req.id,
|
|
162
162
|
title: req.title || req.id,
|
|
@@ -185,7 +185,7 @@ class TraceabilityService {
|
|
|
185
185
|
|
|
186
186
|
try {
|
|
187
187
|
const files = await fs.readdir(specsPath);
|
|
188
|
-
const designFiles = files.filter(
|
|
188
|
+
const designFiles = files.filter(f => f.endsWith('-design.md'));
|
|
189
189
|
|
|
190
190
|
for (const file of designFiles) {
|
|
191
191
|
const filePath = path.join(specsPath, file);
|
|
@@ -216,7 +216,7 @@ class TraceabilityService {
|
|
|
216
216
|
|
|
217
217
|
try {
|
|
218
218
|
const files = await fs.readdir(specsPath);
|
|
219
|
-
const taskFiles = files.filter(
|
|
219
|
+
const taskFiles = files.filter(f => f.endsWith('-tasks.md'));
|
|
220
220
|
|
|
221
221
|
for (const file of taskFiles) {
|
|
222
222
|
const filePath = path.join(specsPath, file);
|
|
@@ -255,10 +255,10 @@ class TraceabilityService {
|
|
|
255
255
|
const implementations = [];
|
|
256
256
|
|
|
257
257
|
try {
|
|
258
|
-
await this.walkDirectory(srcPath, async
|
|
258
|
+
await this.walkDirectory(srcPath, async filePath => {
|
|
259
259
|
if (filePath.endsWith('.js') || filePath.endsWith('.ts')) {
|
|
260
260
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
261
|
-
|
|
261
|
+
|
|
262
262
|
// Look for @implements or @requirement tags
|
|
263
263
|
const tagRegex = /@(?:implements|requirement)\s+(REQ-\d+[^\s]*)/g;
|
|
264
264
|
let match;
|
|
@@ -292,10 +292,10 @@ class TraceabilityService {
|
|
|
292
292
|
async walkDirectory(dir, callback) {
|
|
293
293
|
try {
|
|
294
294
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
295
|
-
|
|
295
|
+
|
|
296
296
|
for (const entry of entries) {
|
|
297
297
|
const fullPath = path.join(dir, entry.name);
|
|
298
|
-
|
|
298
|
+
|
|
299
299
|
if (entry.isDirectory()) {
|
|
300
300
|
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
301
301
|
await this.walkDirectory(fullPath, callback);
|
|
@@ -46,7 +46,7 @@ class WorkflowService {
|
|
|
46
46
|
// Check steering
|
|
47
47
|
const steeringPath = path.join(this.projectPath, 'steering');
|
|
48
48
|
const constitutionPath = path.join(steeringPath, 'rules', 'constitution.md');
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
try {
|
|
51
51
|
await fs.access(constitutionPath);
|
|
52
52
|
state.completedStages.push('steering');
|
|
@@ -61,7 +61,12 @@ class WorkflowService {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// Check requirements
|
|
64
|
-
const requirementsPath = path.join(
|
|
64
|
+
const requirementsPath = path.join(
|
|
65
|
+
this.projectPath,
|
|
66
|
+
'storage',
|
|
67
|
+
'specs',
|
|
68
|
+
`${featureId}-requirements.md`
|
|
69
|
+
);
|
|
65
70
|
if (await this.fileExists(requirementsPath)) {
|
|
66
71
|
state.completedStages.push('requirements');
|
|
67
72
|
state.currentStage = 'design';
|
|
@@ -100,7 +105,7 @@ class WorkflowService {
|
|
|
100
105
|
|
|
101
106
|
try {
|
|
102
107
|
const files = await fs.readdir(specsPath);
|
|
103
|
-
|
|
108
|
+
|
|
104
109
|
for (const file of files) {
|
|
105
110
|
const match = file.match(/^(.+)-(requirements|design|tasks)\.md$/);
|
|
106
111
|
if (match) {
|
|
@@ -133,7 +138,9 @@ class WorkflowService {
|
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
if (targetIndex > currentIndex + 1) {
|
|
136
|
-
throw new Error(
|
|
141
|
+
throw new Error(
|
|
142
|
+
`Cannot skip stages. Current: ${currentState.currentStage}, Target: ${targetStage}`
|
|
143
|
+
);
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
// The actual transition is done by creating the artifact
|
|
@@ -190,7 +197,7 @@ class WorkflowService {
|
|
|
190
197
|
|
|
191
198
|
return {
|
|
192
199
|
featureId,
|
|
193
|
-
valid: issues.filter(
|
|
200
|
+
valid: issues.filter(i => i.type === 'error').length === 0,
|
|
194
201
|
issues,
|
|
195
202
|
state,
|
|
196
203
|
};
|
|
@@ -219,11 +226,11 @@ class WorkflowService {
|
|
|
219
226
|
try {
|
|
220
227
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
221
228
|
const { data: frontmatter, content: body } = matter(content);
|
|
222
|
-
|
|
229
|
+
|
|
223
230
|
const sections = [];
|
|
224
231
|
const sectionRegex = /^##\s+(.+)$/gm;
|
|
225
232
|
let match;
|
|
226
|
-
|
|
233
|
+
|
|
227
234
|
while ((match = sectionRegex.exec(body)) !== null) {
|
|
228
235
|
sections.push(match[1]);
|
|
229
236
|
}
|