@ryuenn3123/agentic-senior-core 3.0.6 → 3.0.8

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
- );
254
-
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: ';
150
+ const projectNamePrompt = defaultProjectName
151
+ ? `Project name (press Enter to use folder name: ${defaultProjectName}): `
152
+ : 'Project name: ';
264
153
 
265
- projectName = (await userInterface.question(projectNamePrompt)).trim();
154
+ projectName = (await userInterface.question(projectNamePrompt)).trim();
266
155
 
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,160 @@ 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
+ export function buildDesignIntentSeedFromSignals({
377
+ projectName,
378
+ projectDescription,
379
+ primaryDomain,
380
+ features = [],
381
+ initContext,
382
+ architectureRecommendation = null,
383
+ status = 'seed-needs-design-synthesis',
384
+ supplementalFields = {},
385
+ }) {
386
+ const inferredKeywords = inferDesignKeywords({
387
+ projectDescription,
388
+ primaryDomain,
389
+ features,
390
+ });
391
+ const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
392
+
393
+ return `${JSON.stringify({
394
+ mode: 'dynamic',
395
+ status,
396
+ project: {
397
+ name: projectName,
398
+ context: projectDescription,
399
+ domain: primaryDomain,
400
+ stack: toTitleCase(initContext.stackFileName),
401
+ blueprint: toTitleCase(initContext.blueprintFileName),
402
+ },
403
+ brandAdjectives: inferredKeywords.brandAdjectives,
404
+ antiAdjectives: inferredKeywords.antiAdjectives,
405
+ visualDirection: {
406
+ trendStance: 'trend-aware-not-trend-chasing',
407
+ distinctiveMoves: inferredKeywords.distinctiveMoves,
408
+ copiedReferenceAllowed: false,
409
+ },
410
+ experiencePrinciples: [
411
+ 'Design must feel project-specific, not interchangeable with generic SaaS templates.',
412
+ 'Major interface decisions must be explainable in product and user terms.',
413
+ 'Accessibility, responsiveness, and implementation realism are non-negotiable.',
414
+ ],
415
+ forbiddenPatterns: [
416
+ 'generic-saas-hero',
417
+ 'copycat-brand-system',
418
+ 'unjustified-default-gradients',
419
+ 'placeholder-design-language',
420
+ ],
421
+ requiredDesignSections: [
422
+ 'Design Intent and Product Personality',
423
+ 'Audience and Use-Context Signals',
424
+ 'Visual Direction and Distinctive Moves',
425
+ 'Color System and Semantic Roles',
426
+ 'Typography System and Hierarchy',
427
+ 'Spacing, Layout Rhythm, and Density Strategy',
428
+ 'Interaction, Motion, and Feedback Rules',
429
+ 'Component Language and Shared Patterns',
430
+ 'Accessibility Non-Negotiables',
431
+ 'Responsive Strategy',
432
+ 'Anti-Patterns to Avoid',
433
+ 'Implementation Notes for Future UI Tasks',
434
+ ],
435
+ implementation: {
436
+ requiredDeliverables: ['docs/DESIGN.md', 'docs/design-intent.json'],
437
+ requireDesignRationale: true,
438
+ requireDistinctVisualDirection: true,
439
+ requireMachineReadableContract: true,
440
+ bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
441
+ },
442
+ architectSignals: designSignals,
443
+ ...supplementalFields,
444
+ }, null, 2)}\n`;
445
+ }
446
+
447
+ function buildDesignIntentSeed({
448
+ discoveryAnswers,
449
+ initContext,
450
+ architectureRecommendation,
451
+ }) {
452
+ return buildDesignIntentSeedFromSignals({
453
+ projectName: discoveryAnswers.projectName,
454
+ projectDescription: discoveryAnswers.projectDescription,
455
+ primaryDomain: discoveryAnswers.primaryDomain,
456
+ features: discoveryAnswers.features,
457
+ initContext,
458
+ architectureRecommendation,
459
+ status: 'seed-needs-design-synthesis',
460
+ });
461
+ }
462
+
493
463
  function buildProjectContextBootstrapPrompt({
494
464
  discoveryAnswers,
495
465
  initContext,
@@ -576,35 +546,61 @@ function buildDesignBootstrapPrompt({
576
546
  }) {
577
547
  const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
578
548
  const designSignalsJson = JSON.stringify(designSignals, null, 2);
549
+ const designIntentSeed = buildDesignIntentSeed({
550
+ discoveryAnswers,
551
+ initContext,
552
+ architectureRecommendation,
553
+ });
579
554
 
580
555
  return [
581
- '# Bootstrap Prompt: Dynamic DESIGN.md Synthesis',
556
+ '# Bootstrap Prompt: Dynamic Design Contract Synthesis',
582
557
  '',
583
558
  `Protocol version: ${PROJECT_DOC_SYNTHESIS_PROMPT_VERSION}`,
584
559
  '',
585
560
  'You are the Lead UI/UX Art Director for this project.',
586
- 'Write docs/DESIGN.md from zero. Do not use generic template prose.',
561
+ 'Create a dynamic design contract, not a fixed stylistic template.',
587
562
  '',
588
563
  '## Mission',
589
564
  `Author docs/DESIGN.md in ${docsLanguage.toUpperCase()} language with strong art direction and engineering-ready guidance.`,
565
+ 'Keep docs/design-intent.json synchronized as the machine-readable source of design intent.',
590
566
  '',
591
- '## Required Sections',
567
+ '## Deliverables',
568
+ '1. docs/DESIGN.md',
569
+ '2. docs/design-intent.json',
570
+ '',
571
+ '## Required DESIGN.md Sections',
592
572
  '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',
573
+ '2. Audience and Use-Context Signals',
574
+ '3. Visual Direction and Distinctive Moves',
575
+ '4. Color System (tokens, semantic roles, accessibility rationale)',
576
+ '5. Typography System (pairing, scale, usage rules)',
577
+ '6. Spacing, Layout Rhythm, and Density Strategy',
578
+ '7. Motion and Interaction Principles',
579
+ '8. Component Language (cards, forms, nav, states)',
600
580
  '9. Accessibility Non-Negotiables',
601
- '10. Redesign Protocol (how to evolve without breaking design identity)',
581
+ '10. Responsive Strategy',
582
+ '11. Anti-Patterns to Avoid',
583
+ '12. Implementation Notes for Future UI Tasks',
584
+ '',
585
+ '## Required design-intent.json Fields',
586
+ '1. mode',
587
+ '2. status',
588
+ '3. project',
589
+ '4. brandAdjectives',
590
+ '5. antiAdjectives',
591
+ '6. visualDirection',
592
+ '7. experiencePrinciples',
593
+ '8. forbiddenPatterns',
594
+ '9. requiredDesignSections',
595
+ '10. implementation',
602
596
  '',
603
597
  '## Hard Rules',
604
598
  '1. No copy-paste from external style guides.',
605
599
  '2. Every major decision must include psychological/product rationale.',
606
600
  '3. Keep implementation feasible for the selected stack and blueprint.',
607
601
  '4. Keep tone decisive like an art director, not generic AI boilerplate.',
602
+ '5. Do not anchor the final design language to a famous brand reference. Translate inspiration into original project-specific principles.',
603
+ '6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
608
604
  '',
609
605
  '## Project Inputs',
610
606
  `- Project name: ${discoveryAnswers.projectName}`,
@@ -619,10 +615,17 @@ function buildDesignBootstrapPrompt({
619
615
  designSignalsJson || 'null',
620
616
  '```',
621
617
  '',
618
+ '## Seed Machine Contract',
619
+ 'Refine this seed instead of discarding it. Keep the final JSON aligned with the markdown design system.',
620
+ '```json',
621
+ designIntentSeed.trim(),
622
+ '```',
623
+ '',
622
624
  '## 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.',
625
+ '1. Create or update docs/DESIGN.md with complete content.',
626
+ '2. Create or update docs/design-intent.json with machine-readable design intent.',
627
+ '3. Ensure both files stay project-specific, dynamic, and practical for implementation and review.',
628
+ '4. After the contract exists, use it as a first-class source for future UI tasks.',
626
629
  '',
627
630
  ].join('\n');
628
631
  }
@@ -643,12 +646,14 @@ export async function generateProjectDocumentation(
643
646
 
644
647
  const docsDirectoryPath = path.join(targetDirectoryPath, 'docs');
645
648
  const promptsDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'prompts');
649
+ await ensureDirectory(docsDirectoryPath);
646
650
  await ensureDirectory(promptsDirectoryPath);
647
651
 
648
652
  const synthesisContext = buildSynthesisContext(discoveryAnswers, initContext);
649
653
  const { requiredDocFileNames } = resolveProjectDocTargets(discoveryAnswers);
650
654
  const expectedDocFileNames = [...requiredDocFileNames];
651
655
  const generatedPromptFileNames = [];
656
+ const materializedFileNames = [];
652
657
 
653
658
  const projectContextPromptFileName = 'bootstrap-project-context.md';
654
659
  const architectureRecommendation = initContext.architectureRecommendation || null;
@@ -677,8 +682,19 @@ export async function generateProjectDocumentation(
677
682
  await fs.writeFile(path.join(promptsDirectoryPath, designPromptFileName), designPromptContent, 'utf8');
678
683
  generatedPromptFileNames.push(designPromptFileName);
679
684
 
680
- if (!expectedDocFileNames.includes('DESIGN.md')) {
681
- expectedDocFileNames.push('DESIGN.md');
685
+ const designIntentSeedFileName = 'design-intent.json';
686
+ const designIntentSeedContent = buildDesignIntentSeed({
687
+ discoveryAnswers,
688
+ initContext: synthesisContext,
689
+ architectureRecommendation,
690
+ });
691
+ await fs.writeFile(path.join(docsDirectoryPath, designIntentSeedFileName), designIntentSeedContent, 'utf8');
692
+ materializedFileNames.push(designIntentSeedFileName);
693
+
694
+ for (const designContractFileName of UI_DESIGN_CONTRACT_FILE_NAMES) {
695
+ if (!expectedDocFileNames.includes(designContractFileName)) {
696
+ expectedDocFileNames.push(designContractFileName);
697
+ }
682
698
  }
683
699
  }
684
700
 
@@ -686,6 +702,7 @@ export async function generateProjectDocumentation(
686
702
  docsDirectoryPath,
687
703
  generatedFileNames: expectedDocFileNames,
688
704
  generatedPromptFileNames,
705
+ materializedFileNames,
689
706
  bootstrapMode: 'ai-synthesis',
690
707
  synthesisPromptVersion: PROJECT_DOC_SYNTHESIS_PROMPT_VERSION,
691
708
  templateVersion: PROJECT_DOC_TEMPLATE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "type": "module",
5
5
  "description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
6
6
  "bin": {