@llm-dev-ops/agentics-cli 1.5.33 → 1.5.35
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/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/pipeline/auto-chain.d.ts.map +1 -1
- package/dist/pipeline/auto-chain.js +189 -283
- package/dist/pipeline/auto-chain.js.map +1 -1
- package/dist/pipeline/phase3/phases/scaffold-generator.d.ts.map +1 -1
- package/dist/pipeline/phase3/phases/scaffold-generator.js +18 -79
- package/dist/pipeline/phase3/phases/scaffold-generator.js.map +1 -1
- package/dist/pipeline/phase6/phases/deployment-finalizer.d.ts.map +1 -1
- package/dist/pipeline/phase6/phases/deployment-finalizer.js +1 -11
- package/dist/pipeline/phase6/phases/deployment-finalizer.js.map +1 -1
- package/package.json +1 -1
|
@@ -663,8 +663,7 @@ The prompts must be production-grade — they will be given directly to a coding
|
|
|
663
663
|
? fs.readdirSync(promptsDir).filter(f => f.startsWith('impl-'))
|
|
664
664
|
: [];
|
|
665
665
|
if (generatedPrompts.length === 0) {
|
|
666
|
-
|
|
667
|
-
// ── Read full ADR markdown files (not just JSON index) ──
|
|
666
|
+
// ── Read all source material for prompt generation ──
|
|
668
667
|
const adrMarkdownByFile = new Map();
|
|
669
668
|
for (const f of adrFiles) {
|
|
670
669
|
try {
|
|
@@ -672,7 +671,6 @@ The prompts must be production-grade — they will be given directly to a coding
|
|
|
672
671
|
}
|
|
673
672
|
catch { /* skip */ }
|
|
674
673
|
}
|
|
675
|
-
// Read ADR index for structured data
|
|
676
674
|
const adrIndexPath = path.join(adrDir, 'adr-index.json');
|
|
677
675
|
let adrs = [];
|
|
678
676
|
if (fs.existsSync(adrIndexPath)) {
|
|
@@ -685,17 +683,9 @@ The prompts must be production-grade — they will be given directly to a coding
|
|
|
685
683
|
for (const f of adrFiles) {
|
|
686
684
|
const content = adrMarkdownByFile.get(f) ?? '';
|
|
687
685
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
688
|
-
adrs.push({
|
|
689
|
-
id: f.replace(/\.md$/, ''),
|
|
690
|
-
title: titleMatch?.[1] ?? f,
|
|
691
|
-
context: content.slice(0, 500),
|
|
692
|
-
decision: '',
|
|
693
|
-
consequences: [],
|
|
694
|
-
alternatives: [],
|
|
695
|
-
});
|
|
686
|
+
adrs.push({ id: f.replace(/\.md$/, ''), title: titleMatch?.[1] ?? f, context: content.slice(0, 500), decision: '', consequences: [], alternatives: [] });
|
|
696
687
|
}
|
|
697
688
|
}
|
|
698
|
-
// Find the full markdown for each ADR (match by ID in filename)
|
|
699
689
|
function getAdrMarkdown(adr) {
|
|
700
690
|
for (const [filename, content] of adrMarkdownByFile) {
|
|
701
691
|
if (filename.startsWith(adr.id))
|
|
@@ -703,311 +693,227 @@ The prompts must be production-grade — they will be given directly to a coding
|
|
|
703
693
|
}
|
|
704
694
|
return '';
|
|
705
695
|
}
|
|
706
|
-
|
|
707
|
-
let
|
|
708
|
-
let glossary = [];
|
|
696
|
+
// Read DDD as reference material (not as driver)
|
|
697
|
+
let dddContent = '';
|
|
709
698
|
const dddModelPath = dddDir ? path.join(dddDir, 'domain-model.json') : '';
|
|
710
699
|
if (dddModelPath && fs.existsSync(dddModelPath)) {
|
|
711
700
|
try {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
701
|
+
dddContent = fs.readFileSync(dddModelPath, 'utf-8');
|
|
702
|
+
}
|
|
703
|
+
catch { /* skip */ }
|
|
704
|
+
}
|
|
705
|
+
// Also read DDD markdown if available
|
|
706
|
+
if (dddDir) {
|
|
707
|
+
for (const f of ['domain-model.md', 'ddd-model.md']) {
|
|
708
|
+
const p = path.join(dddDir, f);
|
|
709
|
+
if (fs.existsSync(p)) {
|
|
710
|
+
try {
|
|
711
|
+
dddContent += '\n\n' + fs.readFileSync(p, 'utf-8');
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
catch { /* skip */ }
|
|
715
|
+
}
|
|
723
716
|
}
|
|
724
|
-
catch { /* empty */ }
|
|
725
717
|
}
|
|
726
|
-
//
|
|
718
|
+
// Read SPARC documents
|
|
727
719
|
let sparcSpec = '';
|
|
728
720
|
let sparcArch = '';
|
|
729
721
|
if (sparcDir) {
|
|
730
|
-
for (const [file,
|
|
722
|
+
for (const [file, setter] of [
|
|
723
|
+
['specification.md', 'spec'], ['architecture.md', 'arch'],
|
|
724
|
+
]) {
|
|
731
725
|
const p = path.join(sparcDir, file);
|
|
732
726
|
if (fs.existsSync(p)) {
|
|
733
727
|
try {
|
|
734
728
|
const content = fs.readFileSync(p, 'utf-8');
|
|
735
|
-
if (
|
|
729
|
+
if (setter === 'spec')
|
|
736
730
|
sparcSpec = content;
|
|
737
|
-
else
|
|
731
|
+
else if (setter === 'arch')
|
|
738
732
|
sparcArch = content;
|
|
739
733
|
}
|
|
740
734
|
catch { /* skip */ }
|
|
741
735
|
}
|
|
742
736
|
}
|
|
743
737
|
}
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
const
|
|
752
|
-
|
|
738
|
+
console.error(' [PROMPTS] Ruflo swarm did not produce prompts — deriving from SPARC + ADRs');
|
|
739
|
+
const derivedPhases = [];
|
|
740
|
+
// Parse SPARC architecture for service/component sections
|
|
741
|
+
if (sparcArch) {
|
|
742
|
+
// Look for ## or ### headings that describe components/services
|
|
743
|
+
const sections = sparcArch.split(/^(?=#{2,3}\s)/m).filter(s => s.trim().length > 20);
|
|
744
|
+
for (const section of sections) {
|
|
745
|
+
const headingMatch = section.match(/^#{2,3}\s+(.+)/);
|
|
746
|
+
if (!headingMatch)
|
|
747
|
+
continue;
|
|
748
|
+
const heading = headingMatch[1].trim();
|
|
749
|
+
// Skip meta-sections
|
|
750
|
+
if (/^(overview|introduction|summary|table of contents|references|constraints|non-functional)/i.test(heading))
|
|
751
|
+
continue;
|
|
752
|
+
const slug = heading.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50);
|
|
753
|
+
// Determine target folder based on content
|
|
754
|
+
const sectionLower = section.toLowerCase();
|
|
755
|
+
let folder = 'src';
|
|
756
|
+
if (/frontend|ui|dashboard|client|react|vue/.test(sectionLower))
|
|
757
|
+
folder = 'frontend';
|
|
758
|
+
else if (/erp|netsuite|sap|dynamics|oracle/.test(sectionLower))
|
|
759
|
+
folder = 'erp';
|
|
760
|
+
else if (/integration|connector|webhook|external|third.party/.test(sectionLower))
|
|
761
|
+
folder = 'integrations';
|
|
762
|
+
else if (/api|service|backend|server|handler/.test(sectionLower))
|
|
763
|
+
folder = 'backend';
|
|
764
|
+
else if (/test|quality|coverage/.test(sectionLower))
|
|
765
|
+
folder = 'tests';
|
|
766
|
+
else if (/deploy|infra|docker|cloud|ci.cd/.test(sectionLower))
|
|
767
|
+
folder = 'src';
|
|
768
|
+
else if (/doc|guide|reference/.test(sectionLower))
|
|
769
|
+
folder = 'docs';
|
|
770
|
+
// Match ADRs to this section by keyword overlap
|
|
771
|
+
const sectionWords = new Set(sectionLower.split(/\W+/).filter(w => w.length > 3));
|
|
772
|
+
const matchedAdrIds = adrs.filter(adr => {
|
|
773
|
+
const adrWords = `${adr.title} ${adr.context} ${adr.decision}`.toLowerCase().split(/\W+/).filter(w => w.length > 3);
|
|
774
|
+
const overlap = adrWords.filter(w => sectionWords.has(w)).length;
|
|
775
|
+
return overlap >= 2;
|
|
776
|
+
}).map(a => a.id);
|
|
777
|
+
derivedPhases.push({ title: heading, slug, content: section, folder, adrIds: matchedAdrIds });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// If SPARC didn't produce enough sections, fall back to ADR-derived phases
|
|
781
|
+
if (derivedPhases.length < 3) {
|
|
782
|
+
// Group ADRs into logical clusters
|
|
783
|
+
for (const adr of adrs) {
|
|
784
|
+
const slug = adr.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50);
|
|
785
|
+
const adrLower = `${adr.title} ${adr.context} ${adr.decision}`.toLowerCase();
|
|
786
|
+
let folder = 'backend';
|
|
787
|
+
if (/frontend|ui|dashboard/.test(adrLower))
|
|
788
|
+
folder = 'frontend';
|
|
789
|
+
else if (/erp|netsuite|sap|dynamics/.test(adrLower))
|
|
790
|
+
folder = 'erp';
|
|
791
|
+
else if (/integration|connector/.test(adrLower))
|
|
792
|
+
folder = 'integrations';
|
|
793
|
+
else if (/deploy|infra|docker/.test(adrLower))
|
|
794
|
+
folder = 'src';
|
|
795
|
+
else if (/test|quality/.test(adrLower))
|
|
796
|
+
folder = 'tests';
|
|
797
|
+
derivedPhases.push({
|
|
798
|
+
title: adr.title,
|
|
799
|
+
slug,
|
|
800
|
+
content: getAdrMarkdown(adr) || `**Context:** ${adr.context}\n\n**Decision:** ${adr.decision}`,
|
|
801
|
+
folder,
|
|
802
|
+
adrIds: [adr.id],
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
// Always add foundation (first) and testing/deployment (last) if not present
|
|
807
|
+
const hasTesting = derivedPhases.some(p => /test/i.test(p.title));
|
|
808
|
+
const hasDeployment = derivedPhases.some(p => /deploy|infra/i.test(p.title));
|
|
809
|
+
// Insert foundation at the beginning
|
|
810
|
+
derivedPhases.unshift({
|
|
811
|
+
title: 'Project Foundation & Core Types',
|
|
812
|
+
slug: 'foundation',
|
|
813
|
+
content: 'Set up project structure, build tooling, base configuration, and core shared types. All subsequent phases import from this.',
|
|
814
|
+
folder: 'src',
|
|
815
|
+
adrIds: adrs.map(a => a.id), // all ADRs relevant to foundation
|
|
816
|
+
});
|
|
817
|
+
if (!hasTesting) {
|
|
818
|
+
derivedPhases.push({
|
|
819
|
+
title: 'Testing & Quality Assurance',
|
|
820
|
+
slug: 'testing',
|
|
821
|
+
content: 'Write comprehensive tests for all components built in prior phases.',
|
|
822
|
+
folder: 'tests',
|
|
823
|
+
adrIds: [],
|
|
753
824
|
});
|
|
754
825
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
826
|
+
if (!hasDeployment) {
|
|
827
|
+
derivedPhases.push({
|
|
828
|
+
title: 'Deployment & Infrastructure',
|
|
829
|
+
slug: 'deployment',
|
|
830
|
+
content: 'Create deployment configs, Dockerfiles, CI/CD, and infrastructure-as-code for the target cloud platform.',
|
|
831
|
+
folder: 'src',
|
|
832
|
+
adrIds: [],
|
|
833
|
+
});
|
|
760
834
|
}
|
|
761
|
-
|
|
762
|
-
const
|
|
835
|
+
const totalSteps = derivedPhases.length;
|
|
836
|
+
const completedPhases = [];
|
|
763
837
|
for (let i = 0; i < totalSteps; i++) {
|
|
838
|
+
const phase = derivedPhases[i];
|
|
764
839
|
const order = i + 1;
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
else {
|
|
786
|
-
lines.push('> This is the foundation step. Build the core types and interfaces that later steps will import.');
|
|
787
|
-
}
|
|
840
|
+
const filename = `impl-${String(order).padStart(3, '0')}-${phase.slug}.md`;
|
|
841
|
+
const lines = [
|
|
842
|
+
`# Implementation Prompt ${order} of ${totalSteps}: ${phase.title}`,
|
|
843
|
+
'',
|
|
844
|
+
'**The implementation must be production ready, enterprise grade, commercially viable, bug and error free, without compilation errors.**',
|
|
845
|
+
'',
|
|
846
|
+
`**Target folder:** \`${phase.folder}/\``,
|
|
847
|
+
'',
|
|
848
|
+
];
|
|
849
|
+
if (order > 1) {
|
|
850
|
+
lines.push(`> This step builds on steps 1-${order - 1}. Import from previously built modules.`);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
lines.push('> This is the foundation step.');
|
|
854
|
+
}
|
|
855
|
+
lines.push('');
|
|
856
|
+
if (completedPhases.length > 0) {
|
|
857
|
+
lines.push('## Previously Completed (available for import)', '');
|
|
858
|
+
for (const prev of completedPhases)
|
|
859
|
+
lines.push(`- ${prev}`);
|
|
788
860
|
lines.push('');
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
for (const prev of completedContexts) {
|
|
794
|
-
lines.push(`- **${prev}** — completed`);
|
|
795
|
-
}
|
|
796
|
-
lines.push('');
|
|
797
|
-
}
|
|
798
|
-
// Project objective (first time only, abbreviated after)
|
|
799
|
-
if (order === 1) {
|
|
800
|
-
lines.push('## Project Objective', '', scenarioQuery, '');
|
|
801
|
-
}
|
|
802
|
-
else {
|
|
803
|
-
lines.push(`## Project Objective`, '', scenarioQuery.split('\n')[0]?.slice(0, 300) ?? scenarioQuery.slice(0, 300), '', '> See impl-001 for full project context.', '');
|
|
804
|
-
}
|
|
805
|
-
// Full DDD bounded context specification
|
|
806
|
-
lines.push(`## Bounded Context: ${ctx.name}`, '');
|
|
807
|
-
lines.push(ctx.description, '');
|
|
808
|
-
if (ctx.aggregates.length > 0) {
|
|
809
|
-
lines.push('### Aggregates', '');
|
|
810
|
-
for (const agg of ctx.aggregates) {
|
|
811
|
-
lines.push(`#### ${agg.name} (root: ${agg.root ?? agg.name})`);
|
|
812
|
-
if (agg.entities?.length)
|
|
813
|
-
lines.push(`- **Entities:** ${agg.entities.join(', ')}`);
|
|
814
|
-
if (agg.valueObjects?.length)
|
|
815
|
-
lines.push(`- **Value Objects:** ${agg.valueObjects.join(', ')}`);
|
|
816
|
-
lines.push('');
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
if (ctx.commands.length > 0) {
|
|
820
|
-
lines.push('### Commands', '');
|
|
821
|
-
for (const cmd of ctx.commands) {
|
|
822
|
-
if (typeof cmd === 'string') {
|
|
823
|
-
lines.push(`- ${cmd}`);
|
|
824
|
-
}
|
|
825
|
-
else if (cmd && typeof cmd === 'object') {
|
|
826
|
-
const c = cmd;
|
|
827
|
-
const name = c['name'] ?? c['command'] ?? 'unknown';
|
|
828
|
-
const desc = c['description'] ? ` — ${c['description']}` : '';
|
|
829
|
-
lines.push(`- **${name}**${desc}`);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
lines.push('');
|
|
833
|
-
}
|
|
834
|
-
if (ctx.queries.length > 0) {
|
|
835
|
-
lines.push('### Queries', '');
|
|
836
|
-
for (const q of ctx.queries) {
|
|
837
|
-
if (typeof q === 'string') {
|
|
838
|
-
lines.push(`- ${q}`);
|
|
839
|
-
}
|
|
840
|
-
else if (q && typeof q === 'object') {
|
|
841
|
-
const qr = q;
|
|
842
|
-
const name = qr['name'] ?? qr['query'] ?? 'unknown';
|
|
843
|
-
const desc = qr['description'] ? ` — ${qr['description']}` : '';
|
|
844
|
-
const returns = qr['returns'] ? ` → ${qr['returns']}` : '';
|
|
845
|
-
lines.push(`- **${name}**${desc}${returns}`);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
lines.push('');
|
|
849
|
-
}
|
|
850
|
-
if (ctx.domainEvents.length > 0) {
|
|
851
|
-
lines.push('### Domain Events', '');
|
|
852
|
-
for (const evt of ctx.domainEvents) {
|
|
853
|
-
if (typeof evt === 'string') {
|
|
854
|
-
lines.push(`- ${evt}`);
|
|
855
|
-
}
|
|
856
|
-
else if (evt && typeof evt === 'object') {
|
|
857
|
-
const e = evt;
|
|
858
|
-
lines.push(`- **${e['name'] ?? 'unknown'}**${e['description'] ? ` — ${e['description']}` : ''}`);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
lines.push('');
|
|
862
|
-
}
|
|
863
|
-
// Related ADRs — include FULL markdown content, not just title/decision
|
|
864
|
-
if (relatedAdrs.length > 0) {
|
|
865
|
-
lines.push(`## Architecture Decisions Governing This Context (${relatedAdrs.length} ADRs)`, '');
|
|
866
|
-
for (const adr of relatedAdrs) {
|
|
867
|
-
const fullMarkdown = getAdrMarkdown(adr);
|
|
868
|
-
if (fullMarkdown) {
|
|
869
|
-
lines.push(fullMarkdown, '');
|
|
870
|
-
}
|
|
871
|
-
else {
|
|
872
|
-
lines.push(`### ${adr.id}: ${adr.title}`, '');
|
|
873
|
-
if (adr.context)
|
|
874
|
-
lines.push(`**Context:** ${adr.context}`, '');
|
|
875
|
-
if (adr.decision)
|
|
876
|
-
lines.push(`**Decision:** ${adr.decision}`, '');
|
|
877
|
-
if (adr.consequences?.length) {
|
|
878
|
-
lines.push('**Consequences:**');
|
|
879
|
-
for (const c of adr.consequences)
|
|
880
|
-
lines.push(`- ${c}`);
|
|
881
|
-
lines.push('');
|
|
882
|
-
}
|
|
883
|
-
if (adr.alternatives?.length) {
|
|
884
|
-
lines.push('**Alternatives Considered:**');
|
|
885
|
-
for (const a of adr.alternatives)
|
|
886
|
-
lines.push(`- ${a}`);
|
|
887
|
-
lines.push('');
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
else {
|
|
893
|
-
// No keyword match — include cross-cutting ADRs (infra, security, deployment)
|
|
894
|
-
const crossCuttingAdrs = adrs.filter(a => {
|
|
895
|
-
const text = `${a.title} ${a.decision}`.toLowerCase();
|
|
896
|
-
return /security|auth|deploy|infra|database|logging|error.handling|api.design/i.test(text);
|
|
897
|
-
});
|
|
898
|
-
if (crossCuttingAdrs.length > 0) {
|
|
899
|
-
lines.push(`## Cross-Cutting Architecture Decisions (${crossCuttingAdrs.length} ADRs)`, '');
|
|
900
|
-
for (const adr of crossCuttingAdrs) {
|
|
901
|
-
const fullMarkdown = getAdrMarkdown(adr);
|
|
902
|
-
if (fullMarkdown) {
|
|
903
|
-
lines.push(fullMarkdown, '');
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
lines.push(`### ${adr.id}: ${adr.title}`, '');
|
|
907
|
-
if (adr.decision)
|
|
908
|
-
lines.push(`**Decision:** ${adr.decision}`, '');
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
// SPARC architecture excerpt for first prompt
|
|
914
|
-
if (order === 1 && sparcArch) {
|
|
915
|
-
lines.push('## SPARC Architecture Reference', '', sparcArch.slice(0, 3000), '');
|
|
916
|
-
}
|
|
917
|
-
// Relevant glossary terms
|
|
918
|
-
const ctxKeywords = ctx.name.toLowerCase().split(/[-_\s]+/);
|
|
919
|
-
const relevantTerms = glossary.filter(g => ctxKeywords.some(k => k.length > 2 && (g.term?.toLowerCase().includes(k) || g.definition?.toLowerCase().includes(k))));
|
|
920
|
-
if (relevantTerms.length > 0) {
|
|
921
|
-
lines.push('## Ubiquitous Language', '');
|
|
922
|
-
for (const g of relevantTerms) {
|
|
923
|
-
lines.push(`- **${g.term}**: ${g.definition}`);
|
|
924
|
-
}
|
|
925
|
-
lines.push('');
|
|
926
|
-
}
|
|
927
|
-
// Implementation instructions specific to this context
|
|
928
|
-
lines.push('## Implementation Requirements', '', `- Implement ALL commands, queries, and domain events listed above for the ${ctx.name} context`, '- Create aggregate root classes with proper invariant enforcement', `- Define port interfaces (e.g., \`${ctx.name}Port\`) for external dependencies`, '- Write adapter implementations for each port', '- Include London School TDD tests — mock at aggregate boundaries', '- Export public interfaces so downstream contexts can import them', '- Follow the technology stack and patterns specified in the ADRs above', '');
|
|
929
|
-
fs.writeFileSync(path.join(promptsDir, filename), lines.join('\n'), { mode: 0o600, encoding: 'utf-8' });
|
|
930
|
-
completedContexts.push(ctx.name);
|
|
861
|
+
}
|
|
862
|
+
// Full project requirements on first prompt, summary after
|
|
863
|
+
if (order === 1) {
|
|
864
|
+
lines.push('## Project Requirements', '', scenarioQuery, '');
|
|
931
865
|
}
|
|
932
866
|
else {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
lines.push('## Project Objective', '', scenarioQuery, '');
|
|
956
|
-
if (sparcSpec)
|
|
957
|
-
lines.push('## SPARC Specification', '', sparcSpec.slice(0, 4000), '');
|
|
958
|
-
if (sparcArch)
|
|
959
|
-
lines.push('## SPARC Architecture', '', sparcArch.slice(0, 3000), '');
|
|
960
|
-
}
|
|
961
|
-
else {
|
|
962
|
-
lines.push(`## Project Objective`, '', scenarioQuery.split('\n')[0]?.slice(0, 300) ?? '', '', '> See impl-001 for full project context and SPARC specification.', '');
|
|
963
|
-
}
|
|
964
|
-
// Full ADR content
|
|
965
|
-
lines.push('## Architecture Decision', '');
|
|
966
|
-
if (fullMarkdown) {
|
|
967
|
-
lines.push(fullMarkdown, '');
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
lines.push(`### ${adr.id}: ${adr.title}`, '');
|
|
971
|
-
if (adr.context)
|
|
972
|
-
lines.push(`**Context:** ${adr.context}`, '');
|
|
973
|
-
if (adr.decision)
|
|
974
|
-
lines.push(`**Decision:** ${adr.decision}`, '');
|
|
975
|
-
if (adr.consequences?.length) {
|
|
976
|
-
lines.push('**Consequences:**');
|
|
977
|
-
for (const c of adr.consequences)
|
|
978
|
-
lines.push(`- ${c}`);
|
|
979
|
-
lines.push('');
|
|
867
|
+
const firstParagraph = scenarioQuery.split(/\n\n/)[0] ?? scenarioQuery.slice(0, 400);
|
|
868
|
+
lines.push('## Project Requirements (Summary)', '', firstParagraph, '', '> See impl-001 for full project requirements.', '');
|
|
869
|
+
}
|
|
870
|
+
// The SPARC-derived content for this phase
|
|
871
|
+
lines.push(`## What to Build`, '', phase.content, '');
|
|
872
|
+
// SPARC spec on first prompt, architecture on second
|
|
873
|
+
if (order === 1 && sparcSpec) {
|
|
874
|
+
lines.push('## SPARC Specification', '', sparcSpec, '');
|
|
875
|
+
}
|
|
876
|
+
if (order <= 2 && sparcArch) {
|
|
877
|
+
lines.push('## SPARC Architecture', '', sparcArch, '');
|
|
878
|
+
}
|
|
879
|
+
// Relevant ADRs — full markdown
|
|
880
|
+
const phaseAdrs = phase.adrIds.length > 0
|
|
881
|
+
? adrs.filter(a => phase.adrIds.includes(a.id))
|
|
882
|
+
: (order === 1 ? adrs : []); // foundation gets all ADRs
|
|
883
|
+
if (phaseAdrs.length > 0) {
|
|
884
|
+
lines.push(`## Relevant Architecture Decisions (${phaseAdrs.length} ADRs)`, '');
|
|
885
|
+
for (const adr of phaseAdrs) {
|
|
886
|
+
const fullMarkdown = getAdrMarkdown(adr);
|
|
887
|
+
if (fullMarkdown) {
|
|
888
|
+
lines.push(fullMarkdown, '', '---', '');
|
|
980
889
|
}
|
|
981
|
-
|
|
982
|
-
lines.push(
|
|
983
|
-
|
|
984
|
-
lines.push(
|
|
890
|
+
else {
|
|
891
|
+
lines.push(`### ${adr.id}: ${adr.title}`, '');
|
|
892
|
+
if (adr.context)
|
|
893
|
+
lines.push(`**Context:** ${adr.context}`, '');
|
|
894
|
+
if (adr.decision)
|
|
895
|
+
lines.push(`**Decision:** ${adr.decision}`, '');
|
|
985
896
|
lines.push('');
|
|
986
897
|
}
|
|
987
898
|
}
|
|
988
|
-
lines.push('## Implementation Requirements', '', '- Implement the decision described in the ADR above', '- Write clean, modular code following the technology stack specified', '- Include London School TDD tests — mock at boundaries', '- Export public interfaces for downstream consumers', '');
|
|
989
|
-
fs.writeFileSync(path.join(promptsDir, filename), lines.join('\n'), { mode: 0o600, encoding: 'utf-8' });
|
|
990
|
-
completedContexts.push(adr.title);
|
|
991
899
|
}
|
|
900
|
+
// DDD as reference where relevant
|
|
901
|
+
if (dddContent && order <= 3) {
|
|
902
|
+
lines.push('## Domain Model Reference', '', '> Use as reference for domain concepts and entity relationships.', '', dddContent.slice(0, 6000), '');
|
|
903
|
+
}
|
|
904
|
+
lines.push('## Implementation Instructions', '', '- All code must be CUSTOM to this project — no generic boilerplate', '- Follow the technology decisions from the ADRs above', '- Write production-quality code with proper error handling', '- Include unit tests (London School TDD)', '- Export public interfaces for later build phases', `- Place files in \`${phase.folder}/\``, '');
|
|
905
|
+
fs.writeFileSync(path.join(promptsDir, filename), lines.join('\n'), { mode: 0o600, encoding: 'utf-8' });
|
|
906
|
+
completedPhases.push(`${order}. ${phase.title}`);
|
|
992
907
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
adrs: findRelatedAdrs(ctx.name).map(a => a.id),
|
|
1001
|
-
}))
|
|
1002
|
-
: adrs.map((adr, i) => ({
|
|
1003
|
-
order: i + 1,
|
|
1004
|
-
file: `impl-${String(i + 1).padStart(3, '0')}-${adr.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50)}.md`,
|
|
1005
|
-
title: adr.title,
|
|
1006
|
-
upstreams: [],
|
|
1007
|
-
adrs: [adr.id],
|
|
1008
|
-
}));
|
|
908
|
+
const planItems = derivedPhases.map((phase, i) => ({
|
|
909
|
+
order: i + 1,
|
|
910
|
+
file: `impl-${String(i + 1).padStart(3, '0')}-${phase.slug}.md`,
|
|
911
|
+
title: phase.title,
|
|
912
|
+
folder: phase.folder,
|
|
913
|
+
adrs: phase.adrIds,
|
|
914
|
+
}));
|
|
1009
915
|
fs.writeFileSync(path.join(promptsDir, 'execution-plan.json'), JSON.stringify({ totalSteps, prompts: planItems }, null, 2), { mode: 0o600, encoding: 'utf-8' });
|
|
1010
|
-
console.error(` [PROMPTS] Wrote ${totalSteps} implementation prompts from
|
|
916
|
+
console.error(` [PROMPTS] Wrote ${totalSteps} implementation prompts derived from SPARC architecture + ADRs`);
|
|
1011
917
|
}
|
|
1012
918
|
copyPlanningArtifacts(runDir);
|
|
1013
919
|
}
|