@ryuenn3123/agentic-senior-core 3.0.5 → 3.0.7

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.
@@ -6,7 +6,7 @@
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
8
 
9
- import { ensureDirectory, askChoice, askYesNo, toTitleCase, pathExists } from './utils.mjs';
9
+ import { ensureDirectory, askChoice, toTitleCase, pathExists } from './utils.mjs';
10
10
 
11
11
  const SUPPORTED_DOC_LANGUAGES = new Set(['en', 'id']);
12
12
  const PROJECT_DOC_FILE_NAMES = [
@@ -16,6 +16,7 @@ const PROJECT_DOC_FILE_NAMES = [
16
16
  'api-contract.md',
17
17
  'flow-overview.md',
18
18
  ];
19
+ const UI_DESIGN_CONTRACT_FILE_NAMES = ['DESIGN.md', 'design-intent.json'];
19
20
 
20
21
  // Legacy project docs may still carry this version header; keep for upgrade staleness checks.
21
22
  export const PROJECT_DOC_TEMPLATE_VERSION = '1.2.0';
@@ -54,57 +55,6 @@ const DOCKER_STRATEGY_CHOICES = [
54
55
  'Docker for both development and production',
55
56
  ];
56
57
 
57
- const DISCOVERY_MODE_CHOICES = [
58
- 'Quick mode (mostly choices, fastest)',
59
- 'Detailed mode (type your own answers)',
60
- ];
61
-
62
- const DESCRIPTION_TEMPLATE_CHOICES = [
63
- 'Marketplace / commerce platform',
64
- 'Internal operations dashboard',
65
- 'SaaS workflow product',
66
- 'Developer platform / API product',
67
- 'Content and community platform',
68
- 'Other',
69
- ];
70
-
71
- const FEATURE_PRESET_CHOICES = [
72
- 'MVP foundation (auth, core CRUD, role-based access)',
73
- 'Commerce flow (catalog, cart, checkout)',
74
- 'Operations flow (dashboard, approvals, reporting)',
75
- 'API platform flow (keys, rate-limit, webhooks)',
76
- 'Content flow (publish, moderation, search)',
77
- 'Other',
78
- ];
79
-
80
- const FEATURE_PRESET_MAP = {
81
- 'MVP foundation (auth, core CRUD, role-based access)': [
82
- 'Authentication and user profiles',
83
- 'Core CRUD workflow for primary resources',
84
- 'Role-based access control',
85
- ],
86
- 'Commerce flow (catalog, cart, checkout)': [
87
- 'Product catalog and filtering',
88
- 'Shopping cart and wishlist',
89
- 'Checkout and payment processing flow',
90
- ],
91
- 'Operations flow (dashboard, approvals, reporting)': [
92
- 'Operational dashboard with KPIs',
93
- 'Approval workflow and audit trail',
94
- 'Reporting and export functionality',
95
- ],
96
- 'API platform flow (keys, rate-limit, webhooks)': [
97
- 'API key management',
98
- 'Rate limiting and usage quotas',
99
- 'Webhook delivery and retry handling',
100
- ],
101
- 'Content flow (publish, moderation, search)': [
102
- 'Draft and publish workflow',
103
- 'Moderation queue and policy checks',
104
- 'Search and discovery experience',
105
- ],
106
- };
107
-
108
58
  function parseBooleanLikeValue(rawValue) {
109
59
  const normalizedValue = String(rawValue || '').trim().toLowerCase();
110
60
  if (['true', 'yes', 'y', '1'].includes(normalizedValue)) {
@@ -161,44 +111,6 @@ function resolveDockerStrategy({ dockerStrategy, useDocker, useDockerDevelopment
161
111
  return DOCKER_STRATEGY_CHOICES[0];
162
112
  }
163
113
 
164
- function parseDockerStrategy(dockerStrategy) {
165
- const normalizedDockerStrategy = String(dockerStrategy || '').toLowerCase();
166
-
167
- if (normalizedDockerStrategy.includes('both development and production')) {
168
- return {
169
- dockerStrategy: DOCKER_STRATEGY_CHOICES[3],
170
- hasDocker: true,
171
- useDockerDevelopment: true,
172
- useDockerProduction: true,
173
- };
174
- }
175
-
176
- if (normalizedDockerStrategy.includes('development only')) {
177
- return {
178
- dockerStrategy: DOCKER_STRATEGY_CHOICES[1],
179
- hasDocker: true,
180
- useDockerDevelopment: true,
181
- useDockerProduction: false,
182
- };
183
- }
184
-
185
- if (normalizedDockerStrategy.includes('production only')) {
186
- return {
187
- dockerStrategy: DOCKER_STRATEGY_CHOICES[2],
188
- hasDocker: true,
189
- useDockerDevelopment: false,
190
- useDockerProduction: true,
191
- };
192
- }
193
-
194
- return {
195
- dockerStrategy: DOCKER_STRATEGY_CHOICES[0],
196
- hasDocker: false,
197
- useDockerDevelopment: false,
198
- useDockerProduction: false,
199
- };
200
- }
201
-
202
114
  async function askFeatureList(userInterface) {
203
115
  console.log('\nList your key features (one per line, press Enter to finish):');
204
116
 
@@ -231,67 +143,32 @@ export async function runProjectDiscovery(userInterface, options = {}) {
231
143
  console.log('You can answer in your own language.');
232
144
  console.log('CLI prompts stay in English, but non-English answers are fully supported.\n');
233
145
 
234
- const selectedDiscoveryMode = await askChoice(
235
- 'How do you want to answer project questions?',
236
- DISCOVERY_MODE_CHOICES,
237
- userInterface
238
- );
239
-
240
- const isQuickMode = selectedDiscoveryMode === DISCOVERY_MODE_CHOICES[0];
241
-
242
146
  const defaultProjectName = (options.defaultProjectName || '').trim();
147
+ const defaultProjectDescription = String(options.defaultProjectDescription || '').trim();
243
148
  let projectName = '';
244
149
 
245
- if (isQuickMode && defaultProjectName) {
246
- const projectNameSource = await askChoice(
247
- 'Project name source:',
248
- [
249
- `Use folder name (${defaultProjectName})`,
250
- 'Type custom project name',
251
- ],
252
- userInterface
253
- );
150
+ const projectNamePrompt = defaultProjectName
151
+ ? `Project name (press Enter to use folder name: ${defaultProjectName}): `
152
+ : 'Project name: ';
254
153
 
255
- if (projectNameSource.startsWith('Use folder name')) {
256
- projectName = defaultProjectName;
257
- }
258
- }
259
-
260
- if (!projectName) {
261
- const projectNamePrompt = defaultProjectName
262
- ? `Project name (press Enter to use folder name: ${defaultProjectName}): `
263
- : 'Project name: ';
154
+ projectName = (await userInterface.question(projectNamePrompt)).trim();
264
155
 
265
- projectName = (await userInterface.question(projectNamePrompt)).trim();
266
-
267
- if (!projectName && defaultProjectName) {
268
- projectName = defaultProjectName;
269
- }
156
+ if (!projectName && defaultProjectName) {
157
+ projectName = defaultProjectName;
270
158
  }
271
159
 
272
160
  if (!projectName) {
273
161
  throw new Error('Project name is required for documentation scaffolding.');
274
162
  }
275
163
 
276
- let projectDescription = '';
277
- if (isQuickMode) {
278
- const selectedDescriptionTemplate = await askChoice(
279
- 'Project description template:',
280
- DESCRIPTION_TEMPLATE_CHOICES,
281
- userInterface
282
- );
164
+ const projectDescriptionPrompt = defaultProjectDescription
165
+ ? `One-line description (press Enter to use: ${defaultProjectDescription}): `
166
+ : 'One-line description: ';
283
167
 
284
- if (selectedDescriptionTemplate === 'Other') {
285
- projectDescription = (await userInterface.question('One-line description: ')).trim();
286
- } else {
287
- projectDescription = `${selectedDescriptionTemplate} for ${projectName}.`;
288
- }
289
- } else {
290
- projectDescription = (await userInterface.question('One-line description: ')).trim();
291
- }
168
+ let projectDescription = (await userInterface.question(projectDescriptionPrompt)).trim();
292
169
 
293
170
  if (!projectDescription) {
294
- projectDescription = `A ${projectName} project.`;
171
+ projectDescription = defaultProjectDescription || `A ${projectName} project.`;
295
172
  }
296
173
 
297
174
  const domainSelection = await askChoice(
@@ -327,79 +204,20 @@ export async function runProjectDiscovery(userInterface, options = {}) {
327
204
  authStrategy = (await userInterface.question('Describe your auth setup: ')).trim() || 'Custom auth';
328
205
  }
329
206
 
330
- let dockerStrategy = DOCKER_STRATEGY_CHOICES[0];
331
- if (isQuickMode) {
332
- dockerStrategy = await askChoice(
333
- 'Containerization strategy:',
334
- DOCKER_STRATEGY_CHOICES,
335
- userInterface
336
- );
337
- } else {
338
- const useDocker = await askYesNo(
339
- 'Use Docker for this project (development and/or production)?',
340
- userInterface,
341
- false
342
- );
343
-
344
- if (useDocker) {
345
- const useDockerDevelopment = await askYesNo(
346
- 'Use Docker for development workflow?',
347
- userInterface,
348
- true
349
- );
350
- const useDockerProduction = await askYesNo(
351
- 'Use Docker for production runtime/build?',
352
- userInterface,
353
- true
354
- );
355
-
356
- dockerStrategy = resolveDockerStrategy({
357
- useDocker,
358
- useDockerDevelopment,
359
- useDockerProduction,
360
- });
361
- }
362
- }
363
-
364
- const parsedDockerStrategy = parseDockerStrategy(dockerStrategy);
365
-
366
- let features = [];
367
- if (isQuickMode) {
368
- const selectedFeaturePreset = await askChoice(
369
- 'Feature set:',
370
- FEATURE_PRESET_CHOICES,
371
- userInterface
372
- );
207
+ const dockerStrategy = await askChoice(
208
+ 'Containerization strategy:',
209
+ DOCKER_STRATEGY_CHOICES,
210
+ userInterface
211
+ );
373
212
 
374
- if (selectedFeaturePreset === 'Other') {
375
- features = await askFeatureList(userInterface);
376
- } else {
377
- features = FEATURE_PRESET_MAP[selectedFeaturePreset] || [];
378
- }
379
- } else {
380
- features = await askFeatureList(userInterface);
381
- }
213
+ let features = await askFeatureList(userInterface);
382
214
 
383
215
  if (features.length === 0) {
384
216
  features.push('Core functionality (define during development)');
385
217
  }
386
218
 
387
- let additionalContext = 'No additional context provided.';
388
- if (isQuickMode) {
389
- const wantsAdditionalContext = await askYesNo(
390
- 'Add additional context now?',
391
- userInterface,
392
- false
393
- );
394
-
395
- if (wantsAdditionalContext) {
396
- additionalContext = (await userInterface.question('Additional context: ')).trim()
397
- || 'No additional context provided.';
398
- }
399
- } else {
400
- additionalContext = (await userInterface.question('\nAdditional context (optional, press Enter to skip): ')).trim()
401
- || 'No additional context provided.';
402
- }
219
+ const additionalContext = (await userInterface.question('\nAdditional context (optional, press Enter to skip): ')).trim()
220
+ || 'No additional context provided.';
403
221
 
404
222
  return {
405
223
  projectName,
@@ -407,9 +225,7 @@ export async function runProjectDiscovery(userInterface, options = {}) {
407
225
  primaryDomain,
408
226
  databaseChoice,
409
227
  authStrategy,
410
- dockerStrategy: parsedDockerStrategy.dockerStrategy,
411
- useDockerDevelopment: parsedDockerStrategy.useDockerDevelopment,
412
- useDockerProduction: parsedDockerStrategy.useDockerProduction,
228
+ dockerStrategy,
413
229
  features,
414
230
  additionalContext,
415
231
  };
@@ -490,6 +306,134 @@ function shouldBootstrapDesignDocument(discoveryAnswers, initContext) {
490
306
  return false;
491
307
  }
492
308
 
309
+ function inferDesignKeywords(discoveryAnswers) {
310
+ const normalizedDescription = String(discoveryAnswers.projectDescription || '').toLowerCase();
311
+ const normalizedDomain = String(discoveryAnswers.primaryDomain || '').toLowerCase();
312
+ const normalizedFeatures = Array.isArray(discoveryAnswers.features)
313
+ ? discoveryAnswers.features.map((featureValue) => String(featureValue).toLowerCase()).join(' ')
314
+ : '';
315
+ const aggregateText = `${normalizedDescription} ${normalizedDomain} ${normalizedFeatures}`;
316
+
317
+ if (aggregateText.includes('commerce') || aggregateText.includes('catalog') || aggregateText.includes('checkout')) {
318
+ return {
319
+ brandAdjectives: ['clear', 'desirable', 'confident'],
320
+ antiAdjectives: ['cluttered', 'hesitant', 'coupon-noisy'],
321
+ distinctiveMoves: [
322
+ 'Use product hierarchy and buying cues without turning the interface into a discount template.',
323
+ 'Keep decision-critical information prominent while secondary merchandising stays quiet.',
324
+ 'Let imagery and spacing create premium perception before decorative effects do.',
325
+ ],
326
+ };
327
+ }
328
+
329
+ if (aggregateText.includes('dashboard') || aggregateText.includes('operations') || aggregateText.includes('report')) {
330
+ return {
331
+ brandAdjectives: ['calm', 'precise', 'trustworthy'],
332
+ antiAdjectives: ['chaotic', 'gimmicky', 'visually exhausting'],
333
+ distinctiveMoves: [
334
+ 'Prioritize scanning clarity and status recognition over decorative density.',
335
+ 'Use visual weight to separate signal from operational noise.',
336
+ 'Reserve strong accents for alerts, decisions, and state transitions only.',
337
+ ],
338
+ };
339
+ }
340
+
341
+ if (aggregateText.includes('developer') || aggregateText.includes('api') || aggregateText.includes('platform')) {
342
+ return {
343
+ brandAdjectives: ['precise', 'technical', 'transparent'],
344
+ antiAdjectives: ['vague', 'marketing-heavy', 'template-polished'],
345
+ distinctiveMoves: [
346
+ 'Make structure and feedback feel exact without becoming sterile.',
347
+ 'Use code-adjacent rhythm and hierarchy to build trust with technical users.',
348
+ 'Keep complexity legible through spacing, grouping, and explicit interaction states.',
349
+ ],
350
+ };
351
+ }
352
+
353
+ if (aggregateText.includes('content') || aggregateText.includes('community') || aggregateText.includes('publish')) {
354
+ return {
355
+ brandAdjectives: ['editorial', 'warm', 'expressive'],
356
+ antiAdjectives: ['flat', 'anonymous', 'feed-generic'],
357
+ distinctiveMoves: [
358
+ 'Build a strong reading rhythm so content feels curated rather than dumped into cards.',
359
+ 'Use contrast and spacing to guide attention between creation, moderation, and discovery.',
360
+ 'Give key interaction moments personality without sacrificing clarity.',
361
+ ],
362
+ };
363
+ }
364
+
365
+ return {
366
+ brandAdjectives: ['clear', 'human', 'distinct'],
367
+ antiAdjectives: ['generic', 'template-like', 'trend-chasing'],
368
+ distinctiveMoves: [
369
+ 'Create a visual direction with one memorable tension instead of stacking fashionable effects.',
370
+ 'Use rhythm, hierarchy, and motion intentionally so the interface feels authored.',
371
+ 'Keep the system flexible enough to evolve with product scope without losing identity.',
372
+ ],
373
+ };
374
+ }
375
+
376
+ function buildDesignIntentSeed({
377
+ discoveryAnswers,
378
+ initContext,
379
+ architectureRecommendation,
380
+ }) {
381
+ const inferredKeywords = inferDesignKeywords(discoveryAnswers);
382
+ const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
383
+
384
+ return `${JSON.stringify({
385
+ mode: 'dynamic',
386
+ status: 'seed-needs-design-synthesis',
387
+ project: {
388
+ name: discoveryAnswers.projectName,
389
+ context: discoveryAnswers.projectDescription,
390
+ domain: discoveryAnswers.primaryDomain,
391
+ stack: toTitleCase(initContext.stackFileName),
392
+ blueprint: toTitleCase(initContext.blueprintFileName),
393
+ },
394
+ brandAdjectives: inferredKeywords.brandAdjectives,
395
+ antiAdjectives: inferredKeywords.antiAdjectives,
396
+ visualDirection: {
397
+ trendStance: 'trend-aware-not-trend-chasing',
398
+ distinctiveMoves: inferredKeywords.distinctiveMoves,
399
+ copiedReferenceAllowed: false,
400
+ },
401
+ experiencePrinciples: [
402
+ 'Design must feel project-specific, not interchangeable with generic SaaS templates.',
403
+ 'Major interface decisions must be explainable in product and user terms.',
404
+ 'Accessibility, responsiveness, and implementation realism are non-negotiable.',
405
+ ],
406
+ forbiddenPatterns: [
407
+ 'generic-saas-hero',
408
+ 'copycat-brand-system',
409
+ 'unjustified-default-gradients',
410
+ 'placeholder-design-language',
411
+ ],
412
+ requiredDesignSections: [
413
+ 'Design Intent and Product Personality',
414
+ 'Audience and Use-Context Signals',
415
+ 'Visual Direction and Distinctive Moves',
416
+ 'Color System and Semantic Roles',
417
+ 'Typography System and Hierarchy',
418
+ 'Spacing, Layout Rhythm, and Density Strategy',
419
+ 'Interaction, Motion, and Feedback Rules',
420
+ 'Component Language and Shared Patterns',
421
+ 'Accessibility Non-Negotiables',
422
+ 'Responsive Strategy',
423
+ 'Anti-Patterns to Avoid',
424
+ 'Implementation Notes for Future UI Tasks',
425
+ ],
426
+ implementation: {
427
+ requiredDeliverables: ['docs/DESIGN.md', 'docs/design-intent.json'],
428
+ requireDesignRationale: true,
429
+ requireDistinctVisualDirection: true,
430
+ requireMachineReadableContract: true,
431
+ bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
432
+ },
433
+ architectSignals: designSignals,
434
+ }, null, 2)}\n`;
435
+ }
436
+
493
437
  function buildProjectContextBootstrapPrompt({
494
438
  discoveryAnswers,
495
439
  initContext,
@@ -576,35 +520,61 @@ function buildDesignBootstrapPrompt({
576
520
  }) {
577
521
  const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
578
522
  const designSignalsJson = JSON.stringify(designSignals, null, 2);
523
+ const designIntentSeed = buildDesignIntentSeed({
524
+ discoveryAnswers,
525
+ initContext,
526
+ architectureRecommendation,
527
+ });
579
528
 
580
529
  return [
581
- '# Bootstrap Prompt: Dynamic DESIGN.md Synthesis',
530
+ '# Bootstrap Prompt: Dynamic Design Contract Synthesis',
582
531
  '',
583
532
  `Protocol version: ${PROJECT_DOC_SYNTHESIS_PROMPT_VERSION}`,
584
533
  '',
585
534
  'You are the Lead UI/UX Art Director for this project.',
586
- 'Write docs/DESIGN.md from zero. Do not use generic template prose.',
535
+ 'Create a dynamic design contract, not a fixed stylistic template.',
587
536
  '',
588
537
  '## Mission',
589
538
  `Author docs/DESIGN.md in ${docsLanguage.toUpperCase()} language with strong art direction and engineering-ready guidance.`,
539
+ 'Keep docs/design-intent.json synchronized as the machine-readable source of design intent.',
540
+ '',
541
+ '## Deliverables',
542
+ '1. docs/DESIGN.md',
543
+ '2. docs/design-intent.json',
590
544
  '',
591
- '## Required Sections',
545
+ '## Required DESIGN.md Sections',
592
546
  '1. Design Vision and Product Personality',
593
- '2. Theme and Atmosphere Direction',
594
- '3. Color System (tokens, semantic roles, accessibility rationale)',
595
- '4. Typography System (pairing, scale, usage rules)',
596
- '5. Spacing and Layout Rhythm',
597
- '6. Motion and Interaction Principles',
598
- '7. Component Language (cards, forms, nav, states)',
599
- '8. Do and Don\'t Rules',
547
+ '2. Audience and Use-Context Signals',
548
+ '3. Visual Direction and Distinctive Moves',
549
+ '4. Color System (tokens, semantic roles, accessibility rationale)',
550
+ '5. Typography System (pairing, scale, usage rules)',
551
+ '6. Spacing, Layout Rhythm, and Density Strategy',
552
+ '7. Motion and Interaction Principles',
553
+ '8. Component Language (cards, forms, nav, states)',
600
554
  '9. Accessibility Non-Negotiables',
601
- '10. Redesign Protocol (how to evolve without breaking design identity)',
555
+ '10. Responsive Strategy',
556
+ '11. Anti-Patterns to Avoid',
557
+ '12. Implementation Notes for Future UI Tasks',
558
+ '',
559
+ '## Required design-intent.json Fields',
560
+ '1. mode',
561
+ '2. status',
562
+ '3. project',
563
+ '4. brandAdjectives',
564
+ '5. antiAdjectives',
565
+ '6. visualDirection',
566
+ '7. experiencePrinciples',
567
+ '8. forbiddenPatterns',
568
+ '9. requiredDesignSections',
569
+ '10. implementation',
602
570
  '',
603
571
  '## Hard Rules',
604
572
  '1. No copy-paste from external style guides.',
605
573
  '2. Every major decision must include psychological/product rationale.',
606
574
  '3. Keep implementation feasible for the selected stack and blueprint.',
607
575
  '4. Keep tone decisive like an art director, not generic AI boilerplate.',
576
+ '5. Do not anchor the final design language to a famous brand reference. Translate inspiration into original project-specific principles.',
577
+ '6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
608
578
  '',
609
579
  '## Project Inputs',
610
580
  `- Project name: ${discoveryAnswers.projectName}`,
@@ -619,10 +589,17 @@ function buildDesignBootstrapPrompt({
619
589
  designSignalsJson || 'null',
620
590
  '```',
621
591
  '',
592
+ '## Seed Machine Contract',
593
+ 'Refine this seed instead of discarding it. Keep the final JSON aligned with the markdown design system.',
594
+ '```json',
595
+ designIntentSeed.trim(),
596
+ '```',
597
+ '',
622
598
  '## Required Execution',
623
- '1. Create docs/DESIGN.md with complete content.',
624
- '2. Ensure rules are practical for implementation and review.',
625
- '3. After DESIGN.md exists, use it as first-class source for future UI tasks.',
599
+ '1. Create or update docs/DESIGN.md with complete content.',
600
+ '2. Create or update docs/design-intent.json with machine-readable design intent.',
601
+ '3. Ensure both files stay project-specific, dynamic, and practical for implementation and review.',
602
+ '4. After the contract exists, use it as a first-class source for future UI tasks.',
626
603
  '',
627
604
  ].join('\n');
628
605
  }
@@ -643,12 +620,14 @@ export async function generateProjectDocumentation(
643
620
 
644
621
  const docsDirectoryPath = path.join(targetDirectoryPath, 'docs');
645
622
  const promptsDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'prompts');
623
+ await ensureDirectory(docsDirectoryPath);
646
624
  await ensureDirectory(promptsDirectoryPath);
647
625
 
648
626
  const synthesisContext = buildSynthesisContext(discoveryAnswers, initContext);
649
627
  const { requiredDocFileNames } = resolveProjectDocTargets(discoveryAnswers);
650
628
  const expectedDocFileNames = [...requiredDocFileNames];
651
629
  const generatedPromptFileNames = [];
630
+ const materializedFileNames = [];
652
631
 
653
632
  const projectContextPromptFileName = 'bootstrap-project-context.md';
654
633
  const architectureRecommendation = initContext.architectureRecommendation || null;
@@ -677,8 +656,19 @@ export async function generateProjectDocumentation(
677
656
  await fs.writeFile(path.join(promptsDirectoryPath, designPromptFileName), designPromptContent, 'utf8');
678
657
  generatedPromptFileNames.push(designPromptFileName);
679
658
 
680
- if (!expectedDocFileNames.includes('DESIGN.md')) {
681
- expectedDocFileNames.push('DESIGN.md');
659
+ const designIntentSeedFileName = 'design-intent.json';
660
+ const designIntentSeedContent = buildDesignIntentSeed({
661
+ discoveryAnswers,
662
+ initContext: synthesisContext,
663
+ architectureRecommendation,
664
+ });
665
+ await fs.writeFile(path.join(docsDirectoryPath, designIntentSeedFileName), designIntentSeedContent, 'utf8');
666
+ materializedFileNames.push(designIntentSeedFileName);
667
+
668
+ for (const designContractFileName of UI_DESIGN_CONTRACT_FILE_NAMES) {
669
+ if (!expectedDocFileNames.includes(designContractFileName)) {
670
+ expectedDocFileNames.push(designContractFileName);
671
+ }
682
672
  }
683
673
  }
684
674
 
@@ -686,6 +676,7 @@ export async function generateProjectDocumentation(
686
676
  docsDirectoryPath,
687
677
  generatedFileNames: expectedDocFileNames,
688
678
  generatedPromptFileNames,
679
+ materializedFileNames,
689
680
  bootstrapMode: 'ai-synthesis',
690
681
  synthesisPromptVersion: PROJECT_DOC_SYNTHESIS_PROMPT_VERSION,
691
682
  templateVersion: PROJECT_DOC_TEMPLATE_VERSION,