@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.
@@ -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
- console.error(' [PROMPTS] Ruflo swarm did not produce prompts writing directly from ADRs/DDDs/SPARC');
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
- let dddContexts = [];
707
- let contextMap = [];
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
- const model = JSON.parse(fs.readFileSync(dddModelPath, 'utf-8'));
713
- dddContexts = (model.contexts ?? []).map((c) => ({
714
- name: String(c['name'] ?? 'unknown'),
715
- description: String(c['description'] ?? ''),
716
- aggregates: Array.isArray(c['aggregates']) ? c['aggregates'] : [],
717
- domainEvents: Array.isArray(c['domainEvents']) ? c['domainEvents'] : [],
718
- commands: Array.isArray(c['commands']) ? c['commands'] : [],
719
- queries: Array.isArray(c['queries']) ? c['queries'] : [],
720
- }));
721
- contextMap = Array.isArray(model.contextMap) ? model.contextMap : [];
722
- glossary = Array.isArray(model.ubiquitousLanguageGlossary) ? model.ubiquitousLanguageGlossary : [];
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
- // ── Read SPARC specification + architecture markdown ──
718
+ // Read SPARC documents
727
719
  let sparcSpec = '';
728
720
  let sparcArch = '';
729
721
  if (sparcDir) {
730
- for (const [file, target] of [['specification.md', 'spec'], ['architecture.md', 'arch']]) {
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 (target === 'spec')
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
- // ── Determine build order: use DDD contexts if available, else group ADRs ──
745
- const useDDD = dddContexts.length > 0;
746
- const totalSteps = useDDD ? dddContexts.length : Math.max(adrs.length, 1);
747
- // Map ADRs to contexts by keyword matching on full text
748
- function findRelatedAdrs(contextName) {
749
- const keywords = contextName.toLowerCase().split(/[-_\s]+/).filter(k => k.length > 2);
750
- return adrs.filter(a => {
751
- const text = `${a.title} ${a.context} ${a.decision} ${(a.consequences ?? []).join(' ')}`.toLowerCase();
752
- return keywords.some(kw => text.includes(kw));
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
- // Find upstream dependencies for a context from the context map
756
- function findUpstreams(contextName) {
757
- return contextMap
758
- .filter(r => r.downstream === contextName)
759
- .map(r => `${r.upstream} (${r.type})`);
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
- // Build list of previously completed contexts for dependency tracking
762
- const completedContexts = [];
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
- if (useDDD) {
766
- // ── DDD-driven prompt: full bounded context details + related ADRs ──
767
- const ctx = dddContexts[i];
768
- const slug = ctx.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50);
769
- const filename = `impl-${String(order).padStart(3, '0')}-${slug}.md`;
770
- const relatedAdrs = findRelatedAdrs(ctx.name);
771
- const upstreams = findUpstreams(ctx.name);
772
- const lines = [
773
- `# Build Step ${order} of ${totalSteps}: ${ctx.name}`,
774
- '',
775
- '**The implementation must be production ready, enterprise grade, commercially viable, bug and error free, without compilation errors.**',
776
- '',
777
- ];
778
- // Dependencies
779
- if (upstreams.length > 0) {
780
- lines.push(`> **Dependencies:** This context depends on: ${upstreams.join(', ')}. Import from those modules.`);
781
- }
782
- else if (order > 1) {
783
- lines.push(`> This step builds on steps 1-${order - 1}. Import from previously built modules.`);
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
- // Previously built contexts
790
- if (completedContexts.length > 0) {
791
- lines.push('## Already Built (available for import)');
792
- lines.push('');
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
- // ── ADR-driven prompt (no DDD model): one prompt per ADR with full content ──
934
- const adr = adrs[i];
935
- const slug = adr.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50);
936
- const filename = `impl-${String(order).padStart(3, '0')}-${slug}.md`;
937
- const fullMarkdown = getAdrMarkdown(adr);
938
- const lines = [
939
- `# Build Step ${order} of ${totalSteps}: ${adr.title}`,
940
- '',
941
- '**The implementation must be production ready, enterprise grade, commercially viable, bug and error free, without compilation errors.**',
942
- '',
943
- order > 1
944
- ? `> This step builds on steps 1-${order - 1}. Import from previously built modules.`
945
- : '> This is the foundation step. Build the core types and interfaces that later steps will import.',
946
- '',
947
- ];
948
- if (completedContexts.length > 0) {
949
- lines.push('## Already Built (available for import)', '');
950
- for (const prev of completedContexts)
951
- lines.push(`- **${prev}** completed`);
952
- lines.push('');
953
- }
954
- if (order === 1) {
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
- if (adr.alternatives?.length) {
982
- lines.push('**Alternatives Considered:**');
983
- for (const a of adr.alternatives)
984
- lines.push(`- ${a}`);
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
- // Write execution plan
994
- const planItems = useDDD
995
- ? dddContexts.map((ctx, i) => ({
996
- order: i + 1,
997
- file: `impl-${String(i + 1).padStart(3, '0')}-${ctx.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50)}.md`,
998
- title: ctx.name,
999
- upstreams: findUpstreams(ctx.name),
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 ${useDDD ? 'DDD bounded contexts' : 'ADRs'} (${adrs.length} ADRs, ${dddContexts.length} contexts, SPARC: ${sparcSpec ? 'yes' : 'no'})`);
916
+ console.error(` [PROMPTS] Wrote ${totalSteps} implementation prompts derived from SPARC architecture + ADRs`);
1011
917
  }
1012
918
  copyPlanningArtifacts(runDir);
1013
919
  }