@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.
- package/.agent-context/prompts/bootstrap-design.md +38 -12
- package/.agent-context/prompts/init-project.md +3 -3
- package/.agent-context/prompts/refactor.md +1 -1
- package/.agent-context/prompts/review-code.md +1 -1
- package/.agent-context/review-checklists/pr-checklist.md +1 -1
- package/.agent-context/rules/architecture.md +1 -1
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +2 -2
- package/.gemini/instructions.md +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.windsurfrules +2 -2
- package/AGENTS.md +1 -1
- package/README.md +5 -5
- package/lib/cli/commands/init.mjs +97 -2
- package/lib/cli/commands/upgrade.mjs +98 -2
- package/lib/cli/compiler.mjs +6 -2
- package/lib/cli/detector.mjs +101 -0
- package/lib/cli/init-architecture-flow.mjs +2 -0
- package/lib/cli/project-scaffolder.mjs +240 -223
- package/package.json +1 -1
- package/scripts/validate.mjs +41 -4
|
@@ -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,
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
154
|
+
projectName = (await userInterface.question(projectNamePrompt)).trim();
|
|
266
155
|
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
|
|
388
|
-
|
|
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
|
|
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
|
|
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
|
-
'
|
|
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
|
-
'##
|
|
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.
|
|
594
|
-
'3.
|
|
595
|
-
'4.
|
|
596
|
-
'5.
|
|
597
|
-
'6.
|
|
598
|
-
'7.
|
|
599
|
-
'8.
|
|
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.
|
|
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.
|
|
625
|
-
'3.
|
|
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
|
-
|
|
681
|
-
|
|
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,
|