musubi-sdd 5.0.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 +164 -145
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +247 -125
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +83 -80
- 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 +53 -44
- package/src/monitoring/incident-manager.js +123 -103
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +82 -59
- package/src/monitoring/quality-dashboard.js +51 -39
- package/src/monitoring/release-manager.js +70 -50
- 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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Spec Kit Writer
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Writes Intermediate Representation (IR) to Spec Kit project structure
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Spec Kit structure:
|
|
7
7
|
* .specify/
|
|
8
8
|
* ├── memory/
|
|
@@ -36,22 +36,22 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
|
|
|
36
36
|
const { dryRun = false, force = false, preserveRaw = false, verbose = false } = options;
|
|
37
37
|
const warnings = [];
|
|
38
38
|
let filesWritten = 0;
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
const specifyPath = path.join(outputPath, '.specify');
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
// Create base directories
|
|
43
43
|
const dirs = [
|
|
44
44
|
path.join(specifyPath, 'memory'),
|
|
45
45
|
path.join(specifyPath, 'specs'),
|
|
46
46
|
path.join(specifyPath, 'templates'),
|
|
47
47
|
];
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
if (!dryRun) {
|
|
50
50
|
for (const dir of dirs) {
|
|
51
51
|
await fs.ensureDir(dir);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
// Write constitution
|
|
56
56
|
const constitutionPath = path.join(specifyPath, 'memory', 'constitution.md');
|
|
57
57
|
const constitution = generateConstitution(ir.constitution, preserveRaw);
|
|
@@ -60,17 +60,22 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
|
|
|
60
60
|
filesWritten++;
|
|
61
61
|
}
|
|
62
62
|
if (verbose) console.log(` Writing: ${constitutionPath}`);
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
// Write features
|
|
65
65
|
for (let i = 0; i < ir.features.length; i++) {
|
|
66
66
|
const feature = ir.features[i];
|
|
67
67
|
const featureId = formatFeatureId(i + 1, feature.id);
|
|
68
68
|
const featurePath = path.join(specifyPath, 'specs', featureId);
|
|
69
|
-
const result = await writeFeature(feature, featurePath, {
|
|
69
|
+
const result = await writeFeature(feature, featurePath, {
|
|
70
|
+
dryRun,
|
|
71
|
+
force,
|
|
72
|
+
preserveRaw,
|
|
73
|
+
verbose,
|
|
74
|
+
});
|
|
70
75
|
filesWritten += result.filesWritten;
|
|
71
76
|
warnings.push(...result.warnings);
|
|
72
77
|
}
|
|
73
|
-
|
|
78
|
+
|
|
74
79
|
// Write templates
|
|
75
80
|
for (const template of ir.templates) {
|
|
76
81
|
const templatePath = path.join(specifyPath, 'templates', `${template.name}.md`);
|
|
@@ -80,18 +85,18 @@ async function writeSpeckitProject(ir, outputPath, options = {}) {
|
|
|
80
85
|
}
|
|
81
86
|
if (verbose) console.log(` Writing: ${templatePath}`);
|
|
82
87
|
}
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
return { filesWritten, warnings };
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/**
|
|
88
93
|
* Write file with optional force overwrite
|
|
89
|
-
* @param {string} filePath
|
|
90
|
-
* @param {string} content
|
|
91
|
-
* @param {boolean} force
|
|
94
|
+
* @param {string} filePath
|
|
95
|
+
* @param {string} content
|
|
96
|
+
* @param {boolean} force
|
|
92
97
|
*/
|
|
93
98
|
async function writeFile(filePath, content, force = false) {
|
|
94
|
-
if (await fs.pathExists(filePath) && !force) {
|
|
99
|
+
if ((await fs.pathExists(filePath)) && !force) {
|
|
95
100
|
throw new Error(`File exists: ${filePath} (use --force to overwrite)`);
|
|
96
101
|
}
|
|
97
102
|
await fs.writeFile(filePath, content, 'utf-8');
|
|
@@ -99,8 +104,8 @@ async function writeFile(filePath, content, force = false) {
|
|
|
99
104
|
|
|
100
105
|
/**
|
|
101
106
|
* Format feature ID with leading zeros (e.g., "001-photo-albums")
|
|
102
|
-
* @param {number} index
|
|
103
|
-
* @param {string} originalId
|
|
107
|
+
* @param {number} index
|
|
108
|
+
* @param {string} originalId
|
|
104
109
|
* @returns {string}
|
|
105
110
|
*/
|
|
106
111
|
function formatFeatureId(index, originalId) {
|
|
@@ -108,45 +113,45 @@ function formatFeatureId(index, originalId) {
|
|
|
108
113
|
if (/^\d{3}-/.test(originalId)) {
|
|
109
114
|
return originalId;
|
|
110
115
|
}
|
|
111
|
-
|
|
116
|
+
|
|
112
117
|
// Convert to kebab-case
|
|
113
118
|
const kebabName = originalId
|
|
114
119
|
.toLowerCase()
|
|
115
120
|
.replace(/[^a-z0-9]+/g, '-')
|
|
116
121
|
.replace(/^-|-$/g, '');
|
|
117
|
-
|
|
122
|
+
|
|
118
123
|
return `${String(index).padStart(3, '0')}-${kebabName}`;
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
/**
|
|
122
127
|
* Generate constitution.md content (Spec Kit format)
|
|
123
|
-
* @param {import('../ir/types').ConstitutionIR} constitution
|
|
124
|
-
* @param {boolean} preserveRaw
|
|
128
|
+
* @param {import('../ir/types').ConstitutionIR} constitution
|
|
129
|
+
* @param {boolean} preserveRaw
|
|
125
130
|
* @returns {string}
|
|
126
131
|
*/
|
|
127
132
|
function generateConstitution(constitution, preserveRaw = false) {
|
|
128
133
|
const lines = [];
|
|
129
|
-
|
|
134
|
+
|
|
130
135
|
lines.push('# Project Constitution');
|
|
131
136
|
lines.push('');
|
|
132
137
|
lines.push('The core principles and guidelines that govern this project.');
|
|
133
138
|
lines.push('');
|
|
134
|
-
|
|
139
|
+
|
|
135
140
|
// Write Core Principles (converted from MUSUBI articles)
|
|
136
141
|
lines.push('## Core Principles');
|
|
137
142
|
lines.push('');
|
|
138
|
-
|
|
143
|
+
|
|
139
144
|
// Map articles to principles
|
|
140
145
|
if (constitution.articles && constitution.articles.length > 0) {
|
|
141
146
|
for (const article of constitution.articles) {
|
|
142
147
|
lines.push(`### ${article.name}`);
|
|
143
148
|
lines.push('');
|
|
144
|
-
|
|
149
|
+
|
|
145
150
|
if (article.description) {
|
|
146
151
|
lines.push(article.description);
|
|
147
152
|
lines.push('');
|
|
148
153
|
}
|
|
149
|
-
|
|
154
|
+
|
|
150
155
|
if (article.rules && article.rules.length > 0) {
|
|
151
156
|
for (const rule of article.rules) {
|
|
152
157
|
lines.push(`- ${rule}`);
|
|
@@ -163,14 +168,14 @@ function generateConstitution(constitution, preserveRaw = false) {
|
|
|
163
168
|
lines.push('');
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
|
-
|
|
171
|
+
|
|
167
172
|
// Write Governance
|
|
168
173
|
if (constitution.governance) {
|
|
169
174
|
lines.push('## Governance');
|
|
170
175
|
lines.push('');
|
|
171
176
|
lines.push(`**Version**: ${constitution.governance.version}`);
|
|
172
177
|
lines.push('');
|
|
173
|
-
|
|
178
|
+
|
|
174
179
|
if (constitution.governance.rules && constitution.governance.rules.length > 0) {
|
|
175
180
|
for (const rule of constitution.governance.rules) {
|
|
176
181
|
lines.push(`- ${rule}`);
|
|
@@ -178,7 +183,7 @@ function generateConstitution(constitution, preserveRaw = false) {
|
|
|
178
183
|
lines.push('');
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
|
-
|
|
186
|
+
|
|
182
187
|
// Preserve raw content if requested
|
|
183
188
|
if (preserveRaw && constitution.rawContent) {
|
|
184
189
|
lines.push('---');
|
|
@@ -189,26 +194,26 @@ function generateConstitution(constitution, preserveRaw = false) {
|
|
|
189
194
|
lines.push(constitution.rawContent);
|
|
190
195
|
lines.push('```');
|
|
191
196
|
}
|
|
192
|
-
|
|
197
|
+
|
|
193
198
|
return lines.join('\n');
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
/**
|
|
197
202
|
* Write a feature to Spec Kit format
|
|
198
|
-
* @param {import('../ir/types').FeatureIR} feature
|
|
199
|
-
* @param {string} featurePath
|
|
200
|
-
* @param {Object} options
|
|
203
|
+
* @param {import('../ir/types').FeatureIR} feature
|
|
204
|
+
* @param {string} featurePath
|
|
205
|
+
* @param {Object} options
|
|
201
206
|
* @returns {Promise<{filesWritten: number, warnings: string[]}>}
|
|
202
207
|
*/
|
|
203
208
|
async function writeFeature(feature, featurePath, options = {}) {
|
|
204
209
|
const { dryRun = false, force = false, preserveRaw = false, verbose = false } = options;
|
|
205
210
|
const warnings = [];
|
|
206
211
|
let filesWritten = 0;
|
|
207
|
-
|
|
212
|
+
|
|
208
213
|
if (!dryRun) {
|
|
209
214
|
await fs.ensureDir(featurePath);
|
|
210
215
|
}
|
|
211
|
-
|
|
216
|
+
|
|
212
217
|
// Write spec.md
|
|
213
218
|
const specPath = path.join(featurePath, 'spec.md');
|
|
214
219
|
const specContent = generateSpec(feature, preserveRaw);
|
|
@@ -217,7 +222,7 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
217
222
|
filesWritten++;
|
|
218
223
|
}
|
|
219
224
|
if (verbose) console.log(` Writing: ${specPath}`);
|
|
220
|
-
|
|
225
|
+
|
|
221
226
|
// Write plan.md
|
|
222
227
|
if (feature.plan) {
|
|
223
228
|
const planPath = path.join(featurePath, 'plan.md');
|
|
@@ -228,7 +233,7 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
228
233
|
}
|
|
229
234
|
if (verbose) console.log(` Writing: ${planPath}`);
|
|
230
235
|
}
|
|
231
|
-
|
|
236
|
+
|
|
232
237
|
// Write tasks.md
|
|
233
238
|
if (feature.tasks && feature.tasks.length > 0) {
|
|
234
239
|
const tasksPath = path.join(featurePath, 'tasks.md');
|
|
@@ -239,7 +244,7 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
239
244
|
}
|
|
240
245
|
if (verbose) console.log(` Writing: ${tasksPath}`);
|
|
241
246
|
}
|
|
242
|
-
|
|
247
|
+
|
|
243
248
|
// Write research.md
|
|
244
249
|
if (feature.research) {
|
|
245
250
|
const researchPath = path.join(featurePath, 'research.md');
|
|
@@ -250,7 +255,7 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
250
255
|
}
|
|
251
256
|
if (verbose) console.log(` Writing: ${researchPath}`);
|
|
252
257
|
}
|
|
253
|
-
|
|
258
|
+
|
|
254
259
|
// Write data-model.md
|
|
255
260
|
if (feature.dataModel) {
|
|
256
261
|
const dataModelPath = path.join(featurePath, 'data-model.md');
|
|
@@ -261,14 +266,14 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
261
266
|
}
|
|
262
267
|
if (verbose) console.log(` Writing: ${dataModelPath}`);
|
|
263
268
|
}
|
|
264
|
-
|
|
269
|
+
|
|
265
270
|
// Write contracts
|
|
266
271
|
if (feature.contracts && feature.contracts.length > 0) {
|
|
267
272
|
const contractsPath = path.join(featurePath, 'contracts');
|
|
268
273
|
if (!dryRun) {
|
|
269
274
|
await fs.ensureDir(contractsPath);
|
|
270
275
|
}
|
|
271
|
-
|
|
276
|
+
|
|
272
277
|
for (const contract of feature.contracts) {
|
|
273
278
|
const contractFile = path.join(contractsPath, `${contract.name}.md`);
|
|
274
279
|
const contractContent = contract.rawContent || generateContract(contract);
|
|
@@ -279,7 +284,7 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
279
284
|
if (verbose) console.log(` Writing: ${contractFile}`);
|
|
280
285
|
}
|
|
281
286
|
}
|
|
282
|
-
|
|
287
|
+
|
|
283
288
|
// Write quickstart.md
|
|
284
289
|
if (feature.quickstart) {
|
|
285
290
|
const quickstartPath = path.join(featurePath, 'quickstart.md');
|
|
@@ -290,47 +295,49 @@ async function writeFeature(feature, featurePath, options = {}) {
|
|
|
290
295
|
}
|
|
291
296
|
if (verbose) console.log(` Writing: ${quickstartPath}`);
|
|
292
297
|
}
|
|
293
|
-
|
|
298
|
+
|
|
294
299
|
return { filesWritten, warnings };
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
/**
|
|
298
303
|
* Generate spec.md content (Spec Kit format with User Scenarios)
|
|
299
|
-
* @param {import('../ir/types').FeatureIR} feature
|
|
300
|
-
* @param {boolean} preserveRaw
|
|
304
|
+
* @param {import('../ir/types').FeatureIR} feature
|
|
305
|
+
* @param {boolean} preserveRaw
|
|
301
306
|
* @returns {string}
|
|
302
307
|
*/
|
|
303
308
|
function generateSpec(feature, preserveRaw = false) {
|
|
304
309
|
const lines = [];
|
|
305
310
|
const spec = feature.specification;
|
|
306
|
-
|
|
311
|
+
|
|
307
312
|
lines.push(`# ${spec.title || feature.name}`);
|
|
308
313
|
lines.push('');
|
|
309
|
-
|
|
314
|
+
|
|
310
315
|
if (spec.description) {
|
|
311
316
|
lines.push(spec.description);
|
|
312
317
|
lines.push('');
|
|
313
318
|
}
|
|
314
|
-
|
|
319
|
+
|
|
315
320
|
// Convert EARS requirements to User Scenarios
|
|
316
321
|
if (spec.requirements && spec.requirements.length > 0) {
|
|
317
322
|
lines.push('## User Scenarios');
|
|
318
323
|
lines.push('');
|
|
319
|
-
|
|
324
|
+
|
|
320
325
|
let storyIndex = 1;
|
|
321
326
|
for (const req of spec.requirements) {
|
|
322
327
|
// Convert requirement to user scenario if needed
|
|
323
328
|
const storyId = `US${storyIndex++}`;
|
|
324
|
-
const scenario =
|
|
325
|
-
|| requirementToUserScenario(req, storyId);
|
|
326
|
-
|
|
329
|
+
const scenario =
|
|
330
|
+
spec.userScenarios?.find(s => s.id === storyId) || requirementToUserScenario(req, storyId);
|
|
331
|
+
|
|
327
332
|
lines.push(`### ${scenario.title || req.title || `User Story ${storyIndex}`}`);
|
|
328
333
|
lines.push('');
|
|
329
|
-
lines.push(
|
|
334
|
+
lines.push(
|
|
335
|
+
`As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`
|
|
336
|
+
);
|
|
330
337
|
lines.push('');
|
|
331
338
|
lines.push(`**Priority**: ${scenario.priority || req.priority}`);
|
|
332
339
|
lines.push('');
|
|
333
|
-
|
|
340
|
+
|
|
334
341
|
if (scenario.acceptanceCriteria && scenario.acceptanceCriteria.length > 0) {
|
|
335
342
|
lines.push('**Acceptance Criteria**:');
|
|
336
343
|
for (const ac of scenario.acceptanceCriteria) {
|
|
@@ -338,7 +345,7 @@ function generateSpec(feature, preserveRaw = false) {
|
|
|
338
345
|
}
|
|
339
346
|
lines.push('');
|
|
340
347
|
}
|
|
341
|
-
|
|
348
|
+
|
|
342
349
|
// Add original EARS statement as note
|
|
343
350
|
if (req.statement && !req.mappedFromUserStory) {
|
|
344
351
|
lines.push(`> Original EARS: ${req.statement}`);
|
|
@@ -349,15 +356,17 @@ function generateSpec(feature, preserveRaw = false) {
|
|
|
349
356
|
// Use original user scenarios
|
|
350
357
|
lines.push('## User Scenarios');
|
|
351
358
|
lines.push('');
|
|
352
|
-
|
|
359
|
+
|
|
353
360
|
for (const scenario of spec.userScenarios) {
|
|
354
361
|
lines.push(`### ${scenario.title}`);
|
|
355
362
|
lines.push('');
|
|
356
|
-
lines.push(
|
|
363
|
+
lines.push(
|
|
364
|
+
`As a ${scenario.actor}, I want to ${scenario.action} so that ${scenario.benefit}.`
|
|
365
|
+
);
|
|
357
366
|
lines.push('');
|
|
358
367
|
lines.push(`**Priority**: ${scenario.priority}`);
|
|
359
368
|
lines.push('');
|
|
360
|
-
|
|
369
|
+
|
|
361
370
|
if (scenario.acceptanceCriteria && scenario.acceptanceCriteria.length > 0) {
|
|
362
371
|
lines.push('**Acceptance Criteria**:');
|
|
363
372
|
for (const ac of scenario.acceptanceCriteria) {
|
|
@@ -367,7 +376,7 @@ function generateSpec(feature, preserveRaw = false) {
|
|
|
367
376
|
}
|
|
368
377
|
}
|
|
369
378
|
}
|
|
370
|
-
|
|
379
|
+
|
|
371
380
|
// Write success criteria
|
|
372
381
|
if (spec.successCriteria && spec.successCriteria.length > 0) {
|
|
373
382
|
lines.push('## Success Criteria');
|
|
@@ -377,7 +386,7 @@ function generateSpec(feature, preserveRaw = false) {
|
|
|
377
386
|
}
|
|
378
387
|
lines.push('');
|
|
379
388
|
}
|
|
380
|
-
|
|
389
|
+
|
|
381
390
|
// Preserve raw content if requested
|
|
382
391
|
if (preserveRaw && spec.rawContent) {
|
|
383
392
|
lines.push('---');
|
|
@@ -388,33 +397,33 @@ function generateSpec(feature, preserveRaw = false) {
|
|
|
388
397
|
lines.push(spec.rawContent);
|
|
389
398
|
lines.push('```');
|
|
390
399
|
}
|
|
391
|
-
|
|
400
|
+
|
|
392
401
|
return lines.join('\n');
|
|
393
402
|
}
|
|
394
403
|
|
|
395
404
|
/**
|
|
396
405
|
* Generate plan.md content
|
|
397
|
-
* @param {import('../ir/types').PlanIR} plan
|
|
398
|
-
* @param {boolean} preserveRaw
|
|
406
|
+
* @param {import('../ir/types').PlanIR} plan
|
|
407
|
+
* @param {boolean} preserveRaw
|
|
399
408
|
* @returns {string}
|
|
400
409
|
*/
|
|
401
410
|
function generatePlan(plan, preserveRaw = false) {
|
|
402
411
|
const lines = [];
|
|
403
|
-
|
|
412
|
+
|
|
404
413
|
lines.push('# Implementation Plan');
|
|
405
414
|
lines.push('');
|
|
406
|
-
|
|
415
|
+
|
|
407
416
|
if (plan.summary) {
|
|
408
417
|
lines.push(plan.summary);
|
|
409
418
|
lines.push('');
|
|
410
419
|
}
|
|
411
|
-
|
|
420
|
+
|
|
412
421
|
// Technical Stack
|
|
413
422
|
const tech = plan.technicalContext;
|
|
414
423
|
if (tech && (tech.language || tech.framework)) {
|
|
415
424
|
lines.push('## Technical Stack');
|
|
416
425
|
lines.push('');
|
|
417
|
-
|
|
426
|
+
|
|
418
427
|
if (tech.language) {
|
|
419
428
|
lines.push(`- **Language**: ${tech.language}${tech.version ? ` ${tech.version}` : ''}`);
|
|
420
429
|
}
|
|
@@ -429,21 +438,21 @@ function generatePlan(plan, preserveRaw = false) {
|
|
|
429
438
|
}
|
|
430
439
|
lines.push('');
|
|
431
440
|
}
|
|
432
|
-
|
|
441
|
+
|
|
433
442
|
// Implementation Phases
|
|
434
443
|
if (plan.phases && plan.phases.length > 0) {
|
|
435
444
|
lines.push('## Implementation Phases');
|
|
436
445
|
lines.push('');
|
|
437
|
-
|
|
446
|
+
|
|
438
447
|
for (const phase of plan.phases) {
|
|
439
448
|
lines.push(`### Phase ${phase.number}: ${phase.name}`);
|
|
440
449
|
lines.push('');
|
|
441
|
-
|
|
450
|
+
|
|
442
451
|
if (phase.purpose) {
|
|
443
452
|
lines.push(phase.purpose);
|
|
444
453
|
lines.push('');
|
|
445
454
|
}
|
|
446
|
-
|
|
455
|
+
|
|
447
456
|
if (phase.outputs && phase.outputs.length > 0) {
|
|
448
457
|
lines.push('**Outputs**:');
|
|
449
458
|
for (const output of phase.outputs) {
|
|
@@ -453,7 +462,7 @@ function generatePlan(plan, preserveRaw = false) {
|
|
|
453
462
|
}
|
|
454
463
|
}
|
|
455
464
|
}
|
|
456
|
-
|
|
465
|
+
|
|
457
466
|
// Preserve raw content if requested
|
|
458
467
|
if (preserveRaw && plan.rawContent) {
|
|
459
468
|
lines.push('---');
|
|
@@ -464,21 +473,21 @@ function generatePlan(plan, preserveRaw = false) {
|
|
|
464
473
|
lines.push(plan.rawContent);
|
|
465
474
|
lines.push('```');
|
|
466
475
|
}
|
|
467
|
-
|
|
476
|
+
|
|
468
477
|
return lines.join('\n');
|
|
469
478
|
}
|
|
470
479
|
|
|
471
480
|
/**
|
|
472
481
|
* Generate tasks.md content (Spec Kit format)
|
|
473
|
-
* @param {import('../ir/types').TaskIR[]} tasks
|
|
482
|
+
* @param {import('../ir/types').TaskIR[]} tasks
|
|
474
483
|
* @returns {string}
|
|
475
484
|
*/
|
|
476
485
|
function generateTasks(tasks) {
|
|
477
486
|
const lines = [];
|
|
478
|
-
|
|
487
|
+
|
|
479
488
|
lines.push('# Tasks');
|
|
480
489
|
lines.push('');
|
|
481
|
-
|
|
490
|
+
|
|
482
491
|
// Group by phase
|
|
483
492
|
const phases = {};
|
|
484
493
|
for (const task of tasks) {
|
|
@@ -488,43 +497,45 @@ function generateTasks(tasks) {
|
|
|
488
497
|
}
|
|
489
498
|
phases[phase].push(task);
|
|
490
499
|
}
|
|
491
|
-
|
|
500
|
+
|
|
492
501
|
for (const [phaseNum, phaseTasks] of Object.entries(phases)) {
|
|
493
502
|
lines.push(`## Phase ${phaseNum}`);
|
|
494
503
|
lines.push('');
|
|
495
|
-
|
|
504
|
+
|
|
496
505
|
for (const task of phaseTasks) {
|
|
497
506
|
const checkbox = task.completed ? '[x]' : '[ ]';
|
|
498
507
|
const parallelMarker = task.parallel ? '[P] ' : '';
|
|
499
508
|
const storyMarker = task.userStory ? `[${task.userStory}] ` : '';
|
|
500
509
|
const pathSuffix = task.filePath ? ` at ${task.filePath}` : '';
|
|
501
|
-
|
|
510
|
+
|
|
502
511
|
// Spec Kit format: - [ ] T001 [P] [US1] Description at path/
|
|
503
|
-
lines.push(
|
|
512
|
+
lines.push(
|
|
513
|
+
`- ${checkbox} ${task.id} ${parallelMarker}${storyMarker}${task.description}${pathSuffix}`
|
|
514
|
+
);
|
|
504
515
|
}
|
|
505
516
|
lines.push('');
|
|
506
517
|
}
|
|
507
|
-
|
|
518
|
+
|
|
508
519
|
return lines.join('\n');
|
|
509
520
|
}
|
|
510
521
|
|
|
511
522
|
/**
|
|
512
523
|
* Generate research.md content
|
|
513
|
-
* @param {import('../ir/types').ResearchIR} research
|
|
514
|
-
* @param {boolean} preserveRaw
|
|
524
|
+
* @param {import('../ir/types').ResearchIR} research
|
|
525
|
+
* @param {boolean} preserveRaw
|
|
515
526
|
* @returns {string}
|
|
516
527
|
*/
|
|
517
528
|
function generateResearch(research, preserveRaw = false) {
|
|
518
529
|
const lines = [];
|
|
519
|
-
|
|
530
|
+
|
|
520
531
|
lines.push('# Research');
|
|
521
532
|
lines.push('');
|
|
522
|
-
|
|
533
|
+
|
|
523
534
|
// Decisions
|
|
524
535
|
if (research.decisions && research.decisions.length > 0) {
|
|
525
536
|
lines.push('## Decisions');
|
|
526
537
|
lines.push('');
|
|
527
|
-
|
|
538
|
+
|
|
528
539
|
for (const decision of research.decisions) {
|
|
529
540
|
lines.push(`### ${decision.topic}`);
|
|
530
541
|
lines.push('');
|
|
@@ -535,16 +546,16 @@ function generateResearch(research, preserveRaw = false) {
|
|
|
535
546
|
lines.push('');
|
|
536
547
|
}
|
|
537
548
|
}
|
|
538
|
-
|
|
549
|
+
|
|
539
550
|
// Alternatives Considered
|
|
540
551
|
if (research.alternatives && research.alternatives.length > 0) {
|
|
541
552
|
lines.push('## Alternatives Considered');
|
|
542
553
|
lines.push('');
|
|
543
|
-
|
|
554
|
+
|
|
544
555
|
for (const alt of research.alternatives) {
|
|
545
556
|
lines.push(`### ${alt.name}`);
|
|
546
557
|
lines.push('');
|
|
547
|
-
|
|
558
|
+
|
|
548
559
|
if (alt.pros && alt.pros.length > 0) {
|
|
549
560
|
lines.push('**Pros**:');
|
|
550
561
|
for (const pro of alt.pros) {
|
|
@@ -552,7 +563,7 @@ function generateResearch(research, preserveRaw = false) {
|
|
|
552
563
|
}
|
|
553
564
|
lines.push('');
|
|
554
565
|
}
|
|
555
|
-
|
|
566
|
+
|
|
556
567
|
if (alt.cons && alt.cons.length > 0) {
|
|
557
568
|
lines.push('**Cons**:');
|
|
558
569
|
for (const con of alt.cons) {
|
|
@@ -560,7 +571,7 @@ function generateResearch(research, preserveRaw = false) {
|
|
|
560
571
|
}
|
|
561
572
|
lines.push('');
|
|
562
573
|
}
|
|
563
|
-
|
|
574
|
+
|
|
564
575
|
if (alt.rejected) {
|
|
565
576
|
lines.push(`**Status**: Rejected`);
|
|
566
577
|
if (alt.reason) {
|
|
@@ -570,7 +581,7 @@ function generateResearch(research, preserveRaw = false) {
|
|
|
570
581
|
}
|
|
571
582
|
}
|
|
572
583
|
}
|
|
573
|
-
|
|
584
|
+
|
|
574
585
|
// Preserve raw content if requested
|
|
575
586
|
if (preserveRaw && research.rawContent) {
|
|
576
587
|
lines.push('---');
|
|
@@ -581,36 +592,36 @@ function generateResearch(research, preserveRaw = false) {
|
|
|
581
592
|
lines.push(research.rawContent);
|
|
582
593
|
lines.push('```');
|
|
583
594
|
}
|
|
584
|
-
|
|
595
|
+
|
|
585
596
|
return lines.join('\n');
|
|
586
597
|
}
|
|
587
598
|
|
|
588
599
|
/**
|
|
589
600
|
* Generate data-model.md content
|
|
590
|
-
* @param {import('../ir/types').DataModelIR} dataModel
|
|
591
|
-
* @param {boolean} preserveRaw
|
|
601
|
+
* @param {import('../ir/types').DataModelIR} dataModel
|
|
602
|
+
* @param {boolean} preserveRaw
|
|
592
603
|
* @returns {string}
|
|
593
604
|
*/
|
|
594
605
|
function generateDataModel(dataModel, preserveRaw = false) {
|
|
595
606
|
const lines = [];
|
|
596
|
-
|
|
607
|
+
|
|
597
608
|
lines.push('# Data Model');
|
|
598
609
|
lines.push('');
|
|
599
|
-
|
|
610
|
+
|
|
600
611
|
// Entities
|
|
601
612
|
if (dataModel.entities && dataModel.entities.length > 0) {
|
|
602
613
|
lines.push('## Entities');
|
|
603
614
|
lines.push('');
|
|
604
|
-
|
|
615
|
+
|
|
605
616
|
for (const entity of dataModel.entities) {
|
|
606
617
|
lines.push(`### Entity: ${entity.name}`);
|
|
607
618
|
lines.push('');
|
|
608
|
-
|
|
619
|
+
|
|
609
620
|
if (entity.description) {
|
|
610
621
|
lines.push(entity.description);
|
|
611
622
|
lines.push('');
|
|
612
623
|
}
|
|
613
|
-
|
|
624
|
+
|
|
614
625
|
if (entity.fields && entity.fields.length > 0) {
|
|
615
626
|
for (const field of entity.fields) {
|
|
616
627
|
let fieldLine = `- ${field.name}: ${field.type}`;
|
|
@@ -621,19 +632,19 @@ function generateDataModel(dataModel, preserveRaw = false) {
|
|
|
621
632
|
}
|
|
622
633
|
}
|
|
623
634
|
}
|
|
624
|
-
|
|
635
|
+
|
|
625
636
|
// Relationships
|
|
626
637
|
if (dataModel.relationships && dataModel.relationships.length > 0) {
|
|
627
638
|
lines.push('## Relationships');
|
|
628
639
|
lines.push('');
|
|
629
|
-
|
|
640
|
+
|
|
630
641
|
for (const rel of dataModel.relationships) {
|
|
631
642
|
const relType = rel.type === 'one-to-many' ? 'has many' : 'has one';
|
|
632
643
|
lines.push(`- ${rel.from} ${relType} ${rel.to}`);
|
|
633
644
|
}
|
|
634
645
|
lines.push('');
|
|
635
646
|
}
|
|
636
|
-
|
|
647
|
+
|
|
637
648
|
// Preserve raw content if requested
|
|
638
649
|
if (preserveRaw && dataModel.rawContent) {
|
|
639
650
|
lines.push('---');
|
|
@@ -644,44 +655,44 @@ function generateDataModel(dataModel, preserveRaw = false) {
|
|
|
644
655
|
lines.push(dataModel.rawContent);
|
|
645
656
|
lines.push('```');
|
|
646
657
|
}
|
|
647
|
-
|
|
658
|
+
|
|
648
659
|
return lines.join('\n');
|
|
649
660
|
}
|
|
650
661
|
|
|
651
662
|
/**
|
|
652
663
|
* Generate contract content
|
|
653
|
-
* @param {import('../ir/types').ContractIR} contract
|
|
664
|
+
* @param {import('../ir/types').ContractIR} contract
|
|
654
665
|
* @returns {string}
|
|
655
666
|
*/
|
|
656
667
|
function generateContract(contract) {
|
|
657
668
|
const lines = [];
|
|
658
|
-
|
|
669
|
+
|
|
659
670
|
lines.push(`# ${contract.name}`);
|
|
660
671
|
lines.push('');
|
|
661
672
|
lines.push(`**Type**: ${contract.type.toUpperCase()}`);
|
|
662
673
|
lines.push('');
|
|
663
|
-
|
|
674
|
+
|
|
664
675
|
if (contract.rawContent) {
|
|
665
676
|
lines.push(contract.rawContent);
|
|
666
677
|
} else {
|
|
667
678
|
lines.push('> Contract details to be defined.');
|
|
668
679
|
}
|
|
669
|
-
|
|
680
|
+
|
|
670
681
|
return lines.join('\n');
|
|
671
682
|
}
|
|
672
683
|
|
|
673
684
|
/**
|
|
674
685
|
* Generate quickstart.md content
|
|
675
|
-
* @param {import('../ir/types').QuickstartIR} quickstart
|
|
676
|
-
* @param {boolean} preserveRaw
|
|
686
|
+
* @param {import('../ir/types').QuickstartIR} quickstart
|
|
687
|
+
* @param {boolean} preserveRaw
|
|
677
688
|
* @returns {string}
|
|
678
689
|
*/
|
|
679
690
|
function generateQuickstart(quickstart, preserveRaw = false) {
|
|
680
691
|
const lines = [];
|
|
681
|
-
|
|
692
|
+
|
|
682
693
|
lines.push('# Quickstart');
|
|
683
694
|
lines.push('');
|
|
684
|
-
|
|
695
|
+
|
|
685
696
|
if (quickstart.steps && quickstart.steps.length > 0) {
|
|
686
697
|
for (let i = 0; i < quickstart.steps.length; i++) {
|
|
687
698
|
const step = quickstart.steps[i];
|
|
@@ -689,7 +700,7 @@ function generateQuickstart(quickstart, preserveRaw = false) {
|
|
|
689
700
|
}
|
|
690
701
|
lines.push('');
|
|
691
702
|
}
|
|
692
|
-
|
|
703
|
+
|
|
693
704
|
// Preserve raw content if requested
|
|
694
705
|
if (preserveRaw && quickstart.rawContent) {
|
|
695
706
|
lines.push('---');
|
|
@@ -700,7 +711,7 @@ function generateQuickstart(quickstart, preserveRaw = false) {
|
|
|
700
711
|
lines.push(quickstart.rawContent);
|
|
701
712
|
lines.push('```');
|
|
702
713
|
}
|
|
703
|
-
|
|
714
|
+
|
|
704
715
|
return lines.join('\n');
|
|
705
716
|
}
|
|
706
717
|
|