@ryuenn3123/agentic-senior-core 3.0.6 → 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.
- 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 +14 -1
- package/lib/cli/commands/upgrade.mjs +125 -0
- package/lib/cli/compiler.mjs +6 -2
- package/lib/cli/init-architecture-flow.mjs +2 -0
- package/lib/cli/project-scaffolder.mjs +214 -223
- package/package.json +1 -1
- package/scripts/validate.mjs +39 -4
|
@@ -1,22 +1,48 @@
|
|
|
1
1
|
|
|
2
|
-
# Bootstrap
|
|
2
|
+
# Bootstrap Dynamic Design Contract
|
|
3
3
|
|
|
4
|
-
When a user requests frontend design or redesign, the agent should automatically synthesize a
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- User journey and interaction flows
|
|
10
|
-
- Design tokens and handoff notes for developers
|
|
4
|
+
When a user requests frontend design or redesign, the agent should automatically synthesize a dynamic design contract made of:
|
|
5
|
+
- `docs/DESIGN.md` for human-readable design direction and implementation rationale
|
|
6
|
+
- `docs/design-intent.json` for machine-readable intent, anti-generic constraints, and validation hints
|
|
7
|
+
|
|
8
|
+
This contract is a structure and reasoning system, not a fixed visual template. It must adapt to product context, user needs, platform constraints, and current design signals.
|
|
11
9
|
|
|
12
10
|
The agent must:
|
|
13
11
|
1. Read [AGENTS.md](../../AGENTS.md) for project context and team roles.
|
|
14
12
|
2. Scan all files in [.agent-context/rules/](../rules/) for UI/UX and accessibility standards.
|
|
15
13
|
3. Reference [docs/deep-dive.md](../../docs/deep-dive.md) and [docs/faq.md](../../docs/faq.md) for architecture and product background.
|
|
16
|
-
4. If [docs/DESIGN.md](../../docs/DESIGN.md) exists, check for drift and
|
|
17
|
-
5.
|
|
14
|
+
4. If [docs/DESIGN.md](../../docs/DESIGN.md) or `docs/design-intent.json` already exists, check for drift and improve them instead of rewriting blindly.
|
|
15
|
+
5. Treat any example structure or stylistic inspiration as non-normative. Use it only to judge depth and clarity, never to copy a visual language directly.
|
|
16
|
+
6. All references to docs or rules must be clickable markdown links.
|
|
17
|
+
|
|
18
|
+
Required `docs/DESIGN.md` sections:
|
|
19
|
+
1. Design Intent and Product Personality
|
|
20
|
+
2. Audience and Use-Context Signals
|
|
21
|
+
3. Visual Direction and Distinctive Moves
|
|
22
|
+
4. Color System and Semantic Roles
|
|
23
|
+
5. Typography System and Hierarchy
|
|
24
|
+
6. Spacing, Layout Rhythm, and Density Strategy
|
|
25
|
+
7. Interaction, Motion, and Feedback Rules
|
|
26
|
+
8. Component Language and Shared Patterns
|
|
27
|
+
9. Accessibility Non-Negotiables
|
|
28
|
+
10. Responsive Strategy
|
|
29
|
+
11. Anti-Patterns to Avoid
|
|
30
|
+
12. Implementation Notes for Future UI Tasks
|
|
31
|
+
|
|
32
|
+
Required `docs/design-intent.json` fields:
|
|
33
|
+
- `mode`
|
|
34
|
+
- `status`
|
|
35
|
+
- `project`
|
|
36
|
+
- `brandAdjectives`
|
|
37
|
+
- `antiAdjectives`
|
|
38
|
+
- `visualDirection`
|
|
39
|
+
- `experiencePrinciples`
|
|
40
|
+
- `forbiddenPatterns`
|
|
41
|
+
- `requiredDesignSections`
|
|
42
|
+
- `implementation`
|
|
18
43
|
|
|
19
44
|
Output:
|
|
20
|
-
-
|
|
21
|
-
-
|
|
45
|
+
- Create or update both `docs/DESIGN.md` and `docs/design-intent.json`.
|
|
46
|
+
- Keep both files synchronized: the markdown explains the why, the JSON captures the contract in machine-readable form.
|
|
47
|
+
- Use practical, modern, accessible language grounded in the project, not generic SaaS defaults.
|
|
22
48
|
- Wait for user approval before generating Figma or code assets.
|
|
@@ -35,7 +35,7 @@ If the user specifies a framework/blueprint, the agent should:
|
|
|
35
35
|
See [docs/roadmap.md](../../docs/roadmap.md) and [docs/deep-dive.md](../../docs/deep-dive.md) for the latest stack and blueprint list.
|
|
36
36
|
|
|
37
37
|
## UI/UX Bootstrap
|
|
38
|
-
When a user requests frontend or UI/UX design, the agent should automatically execute the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize DESIGN.md.
|
|
38
|
+
When a user requests frontend or UI/UX design, the agent should automatically execute the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize a dynamic design contract (`docs/DESIGN.md` + `docs/design-intent.json`).
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
@@ -97,6 +97,6 @@ See [docs/roadmap.md](../../docs/roadmap.md) and [docs/deep-dive.md](../../docs/
|
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
|
-
## Bootstrap UI/UX (
|
|
100
|
+
## Bootstrap UI/UX (Dynamic Design Contract)
|
|
101
101
|
|
|
102
|
-
To start UI/UX design from scratch, use the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize `docs/DESIGN.md`.
|
|
102
|
+
To start UI/UX design from scratch, use the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize `docs/DESIGN.md` and `docs/design-intent.json`.
|
|
@@ -15,7 +15,7 @@ Before making changes:
|
|
|
15
15
|
3. Read .agent-context/rules/error-handling.md — fix error handling patterns.
|
|
16
16
|
4. Resolve active language guidance from dynamic stack signals (TypeScript in this repository).
|
|
17
17
|
5. Enforce backend universal principles: no clever hacks, no premature abstraction, readability over brevity.
|
|
18
|
-
6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` is missing.
|
|
18
|
+
6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` or `docs/design-intent.json` is missing.
|
|
19
19
|
|
|
20
20
|
For every change you make, provide a Reasoning Chain:
|
|
21
21
|
- What was wrong (rule reference)
|
|
@@ -20,7 +20,7 @@ Use these checklists:
|
|
|
20
20
|
7. Enforce cross-session consistency guardian: session handoff must include active architecture contract summary, drift detection must warn before direction changes, and direction changes require explicit user confirmation.
|
|
21
21
|
8. Enforce explain-on-demand state visibility: default responses must avoid unnecessary state-file internals, state internals are exposed only on explicit request, and diagnostic mode must explain relevant state decisions when needed.
|
|
22
22
|
9. Enforce single-source and lazy-loading policy: canonical rule source must be explicitly enforced, language-specific guidance must load lazily based on detected scope, and conflicting duplicate rule instructions must not appear during normal flow.
|
|
23
|
-
10. Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md`).
|
|
23
|
+
10. Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md` plus `docs/design-intent.json`).
|
|
24
24
|
|
|
25
25
|
For EVERY violation found:
|
|
26
26
|
- State the exact file and line
|
|
@@ -135,4 +135,4 @@ VERDICT: PASS / FAIL (X/Y items passed)
|
|
|
135
135
|
- [ ] Backend and frontend mindset checks are both applied when scope spans API and UI boundaries
|
|
136
136
|
- [ ] Security and testing requirements remain mandatory after static template purge
|
|
137
137
|
- [ ] Coding flow is blocked if `docs/architecture-decision-record.md` (or `docs/Architecture-Decision-Record.md`) is missing
|
|
138
|
-
- [ ] UI implementation flow is blocked if `docs/DESIGN.md` is missing
|
|
138
|
+
- [ ] UI implementation flow is blocked if `docs/DESIGN.md` or `docs/design-intent.json` is missing
|
|
@@ -21,7 +21,7 @@ The `.agent-context/rules/` directory is the default guidance source for impleme
|
|
|
21
21
|
- Security and testing are non-negotiable baseline requirements.
|
|
22
22
|
- Hard block before coding:
|
|
23
23
|
- `docs/architecture-decision-record.md` (alias: `docs/Architecture-Decision-Record.md`) must exist.
|
|
24
|
-
- For UI scope, `docs/DESIGN.md` must exist.
|
|
24
|
+
- For UI scope, `docs/DESIGN.md` and `docs/design-intent.json` must exist.
|
|
25
25
|
- If required project context docs are missing, stop implementation and bootstrap docs before writing application code.
|
|
26
26
|
|
|
27
27
|
## Rules as Guardian (Cross-Session Consistency)
|
package/.cursorrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
|
|
2
2
|
|
|
3
|
-
Generated by Agentic-Senior-Core CLI v3.0.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v3.0.7
|
|
4
4
|
Timestamp: 2026-04-18T00:00:00.000Z
|
|
5
5
|
Selected profile: beginner
|
|
6
6
|
Selected policy file: .agent-context/policies/llm-judge-threshold.json
|
|
@@ -46,4 +46,4 @@ Do not claim done before checklist pass.
|
|
|
46
46
|
|
|
47
47
|
## UNIVERSAL SOP HARD GATES
|
|
48
48
|
- Stop coding if docs/architecture-decision-record.md (or docs/Architecture-Decision-Record.md) is missing.
|
|
49
|
-
- For UI scope, stop coding if docs/DESIGN.md is missing.
|
|
49
|
+
- For UI scope, stop coding if docs/DESIGN.md or docs/design-intent.json is missing.
|
package/.gemini/instructions.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Adapter Mode: thin
|
|
4
4
|
Adapter Source: .instructions.md
|
|
5
|
-
Canonical Snapshot SHA256:
|
|
5
|
+
Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
|
|
6
6
|
|
|
7
7
|
Canonical policy source: [.instructions.md](../.instructions.md).
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Adapter Mode: thin
|
|
4
4
|
Adapter Source: .instructions.md
|
|
5
|
-
Canonical Snapshot SHA256:
|
|
5
|
+
Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
|
|
6
6
|
|
|
7
7
|
The canonical policy source for this repository is [.instructions.md](../.instructions.md).
|
|
8
8
|
|
package/.windsurfrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
|
|
2
2
|
|
|
3
|
-
Generated by Agentic-Senior-Core CLI v3.0.
|
|
3
|
+
Generated by Agentic-Senior-Core CLI v3.0.7
|
|
4
4
|
Timestamp: 2026-04-18T00:00:00.000Z
|
|
5
5
|
Selected profile: beginner
|
|
6
6
|
Selected policy file: .agent-context/policies/llm-judge-threshold.json
|
|
@@ -46,4 +46,4 @@ Do not claim done before checklist pass.
|
|
|
46
46
|
|
|
47
47
|
## UNIVERSAL SOP HARD GATES
|
|
48
48
|
- Stop coding if docs/architecture-decision-record.md (or docs/Architecture-Decision-Record.md) is missing.
|
|
49
|
-
- For UI scope, stop coding if docs/DESIGN.md is missing.
|
|
49
|
+
- For UI scope, stop coding if docs/DESIGN.md or docs/design-intent.json is missing.
|
package/AGENTS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Adapter Mode: thin
|
|
4
4
|
Adapter Source: .instructions.md
|
|
5
|
-
Canonical Snapshot SHA256:
|
|
5
|
+
Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
|
|
6
6
|
|
|
7
7
|
This file is an adapter entrypoint for agent discovery.
|
|
8
8
|
The canonical policy source is [.instructions.md](.instructions.md).
|
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
**Production-grade Rules Engine (Governance Engine) for AI coding agents.**
|
|
11
11
|
Works with Cursor, Windsurf, GitHub Copilot, Claude Code, Gemini, and other LLM-powered IDE workflows.
|
|
12
12
|
|
|
13
|
-
Latest release: 3.0.
|
|
13
|
+
Latest release: 3.0.7 (2026-04-20).
|
|
14
14
|
|
|
15
|
-
Highlights in 3.0.
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- Init
|
|
15
|
+
Highlights in 3.0.7:
|
|
16
|
+
- Dynamic UI design contract bootstrap now ships paired markdown and machine-readable intent.
|
|
17
|
+
- Upgrade warns when existing UI repositories are missing required design contract files.
|
|
18
|
+
- Init discovery stays slimmer by reusing architecture context and removing redundant wizard branches.
|
|
19
19
|
|
|
20
20
|
</div>
|
|
21
21
|
|
|
@@ -399,6 +399,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
399
399
|
)?.label || 'Both (frontend + backend)';
|
|
400
400
|
|
|
401
401
|
let architectureRecommendation = null;
|
|
402
|
+
let architectureProjectDescription = '';
|
|
402
403
|
let architectPreferenceState = await readArchitectPreferenceState();
|
|
403
404
|
let architectPreferenceUpdated = false;
|
|
404
405
|
|
|
@@ -457,6 +458,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
457
458
|
selectedProjectScopeLabel = architectureSelection.selectedProjectScopeLabel;
|
|
458
459
|
selectedManualStackFileName = architectureSelection.selectedManualStackFileName;
|
|
459
460
|
selectedManualBlueprintFileName = architectureSelection.selectedManualBlueprintFileName;
|
|
461
|
+
architectureProjectDescription = architectureSelection.architectureProjectDescription;
|
|
460
462
|
architectureRecommendation = architectureSelection.architectureRecommendation;
|
|
461
463
|
architectPreferenceState = architectureSelection.architectPreferenceState;
|
|
462
464
|
architectPreferenceUpdated = architectureSelection.architectPreferenceUpdated;
|
|
@@ -607,6 +609,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
607
609
|
} else {
|
|
608
610
|
discoveryAnswers = await runProjectDiscovery(userInterface, {
|
|
609
611
|
defaultProjectName: path.basename(resolvedTargetDirectoryPath),
|
|
612
|
+
defaultProjectDescription: architectureProjectDescription,
|
|
610
613
|
});
|
|
611
614
|
}
|
|
612
615
|
|
|
@@ -645,6 +648,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
645
648
|
for (const generatedPromptFileName of scaffoldingResult.generatedPromptFileNames || []) {
|
|
646
649
|
console.log(` - .agent-context/prompts/${generatedPromptFileName}`);
|
|
647
650
|
}
|
|
651
|
+
for (const materializedFileName of scaffoldingResult.materializedFileNames || []) {
|
|
652
|
+
console.log(` - docs/${materializedFileName}`);
|
|
653
|
+
}
|
|
648
654
|
console.log('Project docs will be authored dynamically by your IDE assistant from these prompts.');
|
|
649
655
|
} else {
|
|
650
656
|
console.log(`\nProject documentation generated in docs/:`);
|
|
@@ -674,6 +680,10 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
674
680
|
selectedProfileName,
|
|
675
681
|
selectedProfilePack,
|
|
676
682
|
selectedPreset: initOptions.preset || null,
|
|
683
|
+
projectScope: {
|
|
684
|
+
key: selectedProjectScopeKey,
|
|
685
|
+
label: selectedProjectScopeLabel,
|
|
686
|
+
},
|
|
677
687
|
selectedStackFileName: selectedResolvedStackFileName,
|
|
678
688
|
selectedAdditionalStackFileNames,
|
|
679
689
|
selectedBlueprintFileName: selectedResolvedBlueprintFileName,
|
|
@@ -746,6 +756,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
746
756
|
console.log('- Generated files: .agent-instructions.md, .cursorrules, .windsurfrules, .clauderc, and .agent-context/state/onboarding-report.json');
|
|
747
757
|
if (scaffoldingResult?.bootstrapMode === 'ai-synthesis') {
|
|
748
758
|
console.log(`- Bootstrap prompts: ${(scaffoldingResult.generatedPromptFileNames || []).length} files generated in .agent-context/prompts/`);
|
|
759
|
+
if ((scaffoldingResult.materializedFileNames || []).length > 0) {
|
|
760
|
+
console.log(`- Seed docs: ${(scaffoldingResult.materializedFileNames || []).length} files generated in docs/`);
|
|
761
|
+
}
|
|
749
762
|
console.log(`- Bootstrap docs language: ${scaffoldingResult.docsLanguage}`);
|
|
750
763
|
console.log(`- Expected project docs after synthesis: ${scaffoldingResult.generatedFileNames.length} files in docs/`);
|
|
751
764
|
} else if (scaffoldingResult) {
|
|
@@ -779,7 +792,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
779
792
|
console.log('\nPrompt starter examples (copy and adapt in your IDE):');
|
|
780
793
|
console.log('- If docs/project-brief.md is missing, execute .agent-context/prompts/bootstrap-project-context.md now and create all required docs files.');
|
|
781
794
|
if ((scaffoldingResult.generatedPromptFileNames || []).includes('bootstrap-design.md')) {
|
|
782
|
-
console.log('- If docs/DESIGN.md is missing, execute .agent-context/prompts/bootstrap-design.md now before building UI components.');
|
|
795
|
+
console.log('- If docs/DESIGN.md or docs/design-intent.json is missing, execute .agent-context/prompts/bootstrap-design.md now before building UI components.');
|
|
783
796
|
}
|
|
784
797
|
console.log(`- Build an MVP for ${promptProjectName} using the newly synthesized docs as strict project context.`);
|
|
785
798
|
console.log('- When scope changes, update docs/* in the same change so future prompts stay aligned.');
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
detectProjectContext,
|
|
30
30
|
buildDetectionSummary,
|
|
31
31
|
formatDetectionCandidates,
|
|
32
|
+
collectProjectMarkers,
|
|
32
33
|
} from '../detector.mjs';
|
|
33
34
|
import {
|
|
34
35
|
buildCompiledRulesContent,
|
|
@@ -103,6 +104,102 @@ function buildExistingProjectMajorConstraints() {
|
|
|
103
104
|
];
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
async function readPackageJsonIfExists(targetDirectoryPath) {
|
|
108
|
+
const packageJsonPath = path.join(targetDirectoryPath, 'package.json');
|
|
109
|
+
if (!(await pathExists(packageJsonPath))) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function detectUiScopeSignals({
|
|
121
|
+
targetDirectoryPath,
|
|
122
|
+
existingOnboardingReport,
|
|
123
|
+
selectedStackFileName,
|
|
124
|
+
selectedBlueprintFileName,
|
|
125
|
+
}) {
|
|
126
|
+
const signalReasons = [];
|
|
127
|
+
const markerNames = await collectProjectMarkers(targetDirectoryPath);
|
|
128
|
+
const packageManifest = await readPackageJsonIfExists(targetDirectoryPath);
|
|
129
|
+
|
|
130
|
+
const persistedProjectScopeKey = String(existingOnboardingReport?.projectScope?.key || '').trim().toLowerCase();
|
|
131
|
+
if (persistedProjectScopeKey === 'frontend-only' || persistedProjectScopeKey === 'both') {
|
|
132
|
+
signalReasons.push(`onboarding project scope: ${persistedProjectScopeKey}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const selectedStackKey = String(selectedStackFileName || '').trim().toLowerCase();
|
|
136
|
+
if (selectedStackKey === 'react-native.md' || selectedStackKey === 'flutter.md') {
|
|
137
|
+
signalReasons.push(`selected stack implies UI runtime: ${selectedStackKey}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const selectedBlueprintKey = String(selectedBlueprintFileName || '').trim().toLowerCase();
|
|
141
|
+
if (selectedBlueprintKey.includes('frontend') || selectedBlueprintKey.includes('landing') || selectedBlueprintKey.includes('mobile-app')) {
|
|
142
|
+
signalReasons.push(`selected blueprint implies UI scope: ${selectedBlueprintKey}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const directUiMarkerNames = [
|
|
146
|
+
'next.config.js',
|
|
147
|
+
'next.config.mjs',
|
|
148
|
+
'next.config.ts',
|
|
149
|
+
'tailwind.config.js',
|
|
150
|
+
'tailwind.config.mjs',
|
|
151
|
+
'tailwind.config.ts',
|
|
152
|
+
'vite.config.js',
|
|
153
|
+
'vite.config.mjs',
|
|
154
|
+
'vite.config.ts',
|
|
155
|
+
'react-native.config.js',
|
|
156
|
+
'app',
|
|
157
|
+
'pages',
|
|
158
|
+
'components',
|
|
159
|
+
'public',
|
|
160
|
+
'styles',
|
|
161
|
+
'android',
|
|
162
|
+
'ios',
|
|
163
|
+
'index.html',
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const detectedUiMarkers = directUiMarkerNames.filter((markerName) => markerNames.has(markerName));
|
|
167
|
+
if (detectedUiMarkers.length > 0) {
|
|
168
|
+
signalReasons.push(`ui markers: ${detectedUiMarkers.join(', ')}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const dependencyMap = {
|
|
172
|
+
next: 'next',
|
|
173
|
+
react: 'react',
|
|
174
|
+
reactDom: 'react-dom',
|
|
175
|
+
reactNative: 'react-native',
|
|
176
|
+
expo: 'expo',
|
|
177
|
+
tailwindcss: 'tailwindcss',
|
|
178
|
+
};
|
|
179
|
+
const dependencySource = {
|
|
180
|
+
...(packageManifest?.dependencies || {}),
|
|
181
|
+
...(packageManifest?.devDependencies || {}),
|
|
182
|
+
};
|
|
183
|
+
const detectedUiDependencies = Object.values(dependencyMap).filter((dependencyName) => dependencySource[dependencyName]);
|
|
184
|
+
if (detectedUiDependencies.length > 0) {
|
|
185
|
+
signalReasons.push(`ui dependencies: ${detectedUiDependencies.join(', ')}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const hasStrongUiMarker = detectedUiMarkers.some((markerName) => (
|
|
189
|
+
markerName.startsWith('next.config')
|
|
190
|
+
|| markerName === 'react-native.config.js'
|
|
191
|
+
|| markerName === 'android'
|
|
192
|
+
|| markerName === 'ios'
|
|
193
|
+
));
|
|
194
|
+
const hasUiDependencies = detectedUiDependencies.length > 0;
|
|
195
|
+
const hasStructuralUiMarkers = detectedUiMarkers.length >= 2;
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
isUiScopeLikely: signalReasons.length > 0 && (hasStrongUiMarker || hasUiDependencies || hasStructuralUiMarkers || persistedProjectScopeKey.length > 0),
|
|
199
|
+
signalReasons,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
106
203
|
export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions = {}) {
|
|
107
204
|
const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
|
|
108
205
|
|
|
@@ -167,6 +264,21 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
167
264
|
const includeCiGuardrails = typeof existingOnboardingReport?.ciGuardrailsEnabled === 'boolean'
|
|
168
265
|
? existingOnboardingReport.ciGuardrailsEnabled
|
|
169
266
|
: true;
|
|
267
|
+
const uiScopeSignals = await detectUiScopeSignals({
|
|
268
|
+
targetDirectoryPath: resolvedTargetDirectoryPath,
|
|
269
|
+
existingOnboardingReport,
|
|
270
|
+
selectedStackFileName,
|
|
271
|
+
selectedBlueprintFileName,
|
|
272
|
+
});
|
|
273
|
+
const designContractPaths = ['docs/DESIGN.md', 'docs/design-intent.json'];
|
|
274
|
+
const missingDesignContractPaths = [];
|
|
275
|
+
|
|
276
|
+
for (const designContractPath of designContractPaths) {
|
|
277
|
+
const absoluteDesignContractPath = path.join(resolvedTargetDirectoryPath, ...designContractPath.split('/'));
|
|
278
|
+
if (!(await pathExists(absoluteDesignContractPath))) {
|
|
279
|
+
missingDesignContractPaths.push(designContractPath);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
170
282
|
|
|
171
283
|
const detectionMajorConstraints = buildExistingProjectMajorConstraints();
|
|
172
284
|
const detectionTransparency = {
|
|
@@ -288,6 +400,18 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
288
400
|
console.log('Recommendation: regenerate docs with init --scaffold-docs or update the files manually to match the latest template version.');
|
|
289
401
|
}
|
|
290
402
|
|
|
403
|
+
if (uiScopeSignals.isUiScopeLikely && missingDesignContractPaths.length > 0) {
|
|
404
|
+
console.log('\n[WARN] UI/frontend scope was detected, but the dynamic design contract is incomplete:');
|
|
405
|
+
for (const missingDesignContractPath of missingDesignContractPaths) {
|
|
406
|
+
console.log(`- Missing ${missingDesignContractPath}`);
|
|
407
|
+
}
|
|
408
|
+
if (uiScopeSignals.signalReasons.length > 0) {
|
|
409
|
+
console.log(`- Detection signals: ${uiScopeSignals.signalReasons.join('; ')}`);
|
|
410
|
+
}
|
|
411
|
+
console.log('Recommendation: create or refresh docs/DESIGN.md and docs/design-intent.json before allowing UI implementation work.');
|
|
412
|
+
console.log('Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.');
|
|
413
|
+
}
|
|
414
|
+
|
|
291
415
|
if (upgradeOptions.dryRun) {
|
|
292
416
|
console.log('\nDry run enabled. No files were modified.');
|
|
293
417
|
return;
|
|
@@ -320,6 +444,7 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
320
444
|
targetDirectoryPath: resolvedTargetDirectoryPath,
|
|
321
445
|
selectedProfileName,
|
|
322
446
|
selectedProfilePack: existingOnboardingReport?.selectedProfilePack || null,
|
|
447
|
+
projectScope: existingOnboardingReport?.projectScope || null,
|
|
323
448
|
selectedStackFileName,
|
|
324
449
|
selectedAdditionalStackFileNames,
|
|
325
450
|
selectedBlueprintFileName,
|
package/lib/cli/compiler.mjs
CHANGED
|
@@ -37,6 +37,7 @@ export async function writeOnboardingReport({
|
|
|
37
37
|
selectedProfileName,
|
|
38
38
|
selectedProfilePack,
|
|
39
39
|
selectedPreset,
|
|
40
|
+
projectScope = null,
|
|
40
41
|
selectedStackFileName,
|
|
41
42
|
selectedAdditionalStackFileNames = [],
|
|
42
43
|
selectedBlueprintFileName,
|
|
@@ -70,6 +71,7 @@ export async function writeOnboardingReport({
|
|
|
70
71
|
}
|
|
71
72
|
: null,
|
|
72
73
|
selectedPreset,
|
|
74
|
+
projectScope,
|
|
73
75
|
selectedStack: selectedStackFileName,
|
|
74
76
|
selectedAdditionalStacks: selectedAdditionalStackFileNames,
|
|
75
77
|
selectedBlueprint: selectedBlueprintFileName,
|
|
@@ -351,6 +353,8 @@ export async function buildCompiledRulesContent({
|
|
|
351
353
|
'database-schema.md',
|
|
352
354
|
'api-contract.md',
|
|
353
355
|
'flow-overview.md',
|
|
356
|
+
'DESIGN.md',
|
|
357
|
+
'design-intent.json',
|
|
354
358
|
];
|
|
355
359
|
|
|
356
360
|
for (const candidateFileName of candidateDocFileNames) {
|
|
@@ -369,7 +373,7 @@ export async function buildCompiledRulesContent({
|
|
|
369
373
|
'',
|
|
370
374
|
'Universal SOP hard block policy:',
|
|
371
375
|
'- Stop implementation if docs/architecture-decision-record.md (alias: docs/Architecture-Decision-Record.md) is missing.',
|
|
372
|
-
'- For UI scope, stop implementation if docs/DESIGN.md is missing.',
|
|
376
|
+
'- For UI scope, stop implementation if docs/DESIGN.md or docs/design-intent.json is missing.',
|
|
373
377
|
'- Materialize missing docs first, then continue coding.',
|
|
374
378
|
'',
|
|
375
379
|
'These docs were generated during project initialization and reflect the architecture,',
|
|
@@ -402,7 +406,7 @@ export async function buildCompiledRulesContent({
|
|
|
402
406
|
'- Hard block: do not write application code until docs/project-brief.md and docs/architecture-decision-record.md exist.',
|
|
403
407
|
'- If docs/project-brief.md is missing, execute bootstrap-project-context prompt immediately.',
|
|
404
408
|
hasBootstrapDesignPrompt
|
|
405
|
-
? '- For UI scope: if docs/DESIGN.md is missing, execute bootstrap-design prompt before implementing UI surfaces.'
|
|
409
|
+
? '- For UI scope: if docs/DESIGN.md or docs/design-intent.json is missing, execute bootstrap-design prompt before implementing UI surfaces.'
|
|
406
410
|
: '- For UI scope: add a design bootstrap prompt before implementing UI surfaces.',
|
|
407
411
|
'- Save generated docs under docs/ and keep them updated when feature scope changes.',
|
|
408
412
|
'Latest user prompt defines current feature scope and product direction.',
|
|
@@ -62,6 +62,7 @@ export async function resolveArchitectureSelection({
|
|
|
62
62
|
selectedProjectScopeLabel,
|
|
63
63
|
selectedManualStackFileName,
|
|
64
64
|
selectedManualBlueprintFileName,
|
|
65
|
+
architectureProjectDescription: '',
|
|
65
66
|
architectureRecommendation,
|
|
66
67
|
architectPreferenceState: nextArchitectPreferenceState,
|
|
67
68
|
architectPreferenceUpdated,
|
|
@@ -224,6 +225,7 @@ export async function resolveArchitectureSelection({
|
|
|
224
225
|
selectedProjectScopeLabel,
|
|
225
226
|
selectedManualStackFileName,
|
|
226
227
|
selectedManualBlueprintFileName,
|
|
228
|
+
architectureProjectDescription,
|
|
227
229
|
architectureRecommendation,
|
|
228
230
|
architectPreferenceState: nextArchitectPreferenceState,
|
|
229
231
|
architectPreferenceUpdated,
|
|
@@ -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
|
-
);
|
|
150
|
+
const projectNamePrompt = defaultProjectName
|
|
151
|
+
? `Project name (press Enter to use folder name: ${defaultProjectName}): `
|
|
152
|
+
: 'Project name: ';
|
|
254
153
|
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
|
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
|
-
'
|
|
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.
|
|
594
|
-
'3.
|
|
595
|
-
'4.
|
|
596
|
-
'5.
|
|
597
|
-
'6.
|
|
598
|
-
'7.
|
|
599
|
-
'8.
|
|
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.
|
|
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.
|
|
625
|
-
'3.
|
|
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
|
-
|
|
681
|
-
|
|
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,
|
package/package.json
CHANGED
package/scripts/validate.mjs
CHANGED
|
@@ -211,19 +211,19 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
|
|
|
211
211
|
snippets: [
|
|
212
212
|
'### 15. Universal SOP Consolidation',
|
|
213
213
|
'Coding flow is blocked if `docs/architecture-decision-record.md` (or `docs/Architecture-Decision-Record.md`) is missing',
|
|
214
|
-
'UI implementation flow is blocked if `docs/DESIGN.md` is missing',
|
|
214
|
+
'UI implementation flow is blocked if `docs/DESIGN.md` or `docs/design-intent.json` is missing',
|
|
215
215
|
],
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
path: '.agent-context/prompts/review-code.md',
|
|
219
219
|
snippets: [
|
|
220
|
-
'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md`).',
|
|
220
|
+
'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md` plus `docs/design-intent.json`).',
|
|
221
221
|
],
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
path: '.agent-context/prompts/refactor.md',
|
|
225
225
|
snippets: [
|
|
226
|
-
'6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` is missing.',
|
|
226
|
+
'6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` or `docs/design-intent.json` is missing.',
|
|
227
227
|
],
|
|
228
228
|
},
|
|
229
229
|
{
|
|
@@ -231,7 +231,7 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
|
|
|
231
231
|
snippets: [
|
|
232
232
|
'Universal SOP hard block policy:',
|
|
233
233
|
'Hard block: do not write application code until docs/project-brief.md and docs/architecture-decision-record.md exist.',
|
|
234
|
-
'For UI scope: if docs/DESIGN.md is missing, execute bootstrap-design prompt before implementing UI surfaces.',
|
|
234
|
+
'For UI scope: if docs/DESIGN.md or docs/design-intent.json is missing, execute bootstrap-design prompt before implementing UI surfaces.',
|
|
235
235
|
],
|
|
236
236
|
},
|
|
237
237
|
];
|
|
@@ -250,10 +250,22 @@ const REQUIRED_TEMPLATE_FREE_BOOTSTRAP_SNIPPETS = [
|
|
|
250
250
|
snippets: [
|
|
251
251
|
'Project docs will be authored dynamically by your IDE assistant from these prompts.',
|
|
252
252
|
'bootstrap-project-context.md',
|
|
253
|
+
'Seed docs:',
|
|
253
254
|
'I prepared dynamic synthesis bootstrap prompts',
|
|
254
255
|
],
|
|
255
256
|
},
|
|
256
257
|
];
|
|
258
|
+
const REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS = [
|
|
259
|
+
{
|
|
260
|
+
path: 'lib/cli/commands/upgrade.mjs',
|
|
261
|
+
snippets: [
|
|
262
|
+
'UI/frontend scope was detected, but the dynamic design contract is incomplete:',
|
|
263
|
+
'docs/design-intent.json',
|
|
264
|
+
'Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.',
|
|
265
|
+
'collectProjectMarkers',
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
];
|
|
257
269
|
const FORBIDDEN_TEMPLATE_BOOTSTRAP_SNIPPETS = [
|
|
258
270
|
{
|
|
259
271
|
path: 'lib/cli/project-scaffolder.mjs',
|
|
@@ -1021,6 +1033,28 @@ async function validateTemplateFreeBootstrapCoverage() {
|
|
|
1021
1033
|
}
|
|
1022
1034
|
}
|
|
1023
1035
|
|
|
1036
|
+
async function validateUpgradeUiContractWarningCoverage() {
|
|
1037
|
+
console.log('\nChecking upgrade UI contract warning coverage...');
|
|
1038
|
+
|
|
1039
|
+
for (const coverageRule of REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS) {
|
|
1040
|
+
const absoluteCoveragePath = join(ROOT_DIR, coverageRule.path);
|
|
1041
|
+
|
|
1042
|
+
if (!(await fileExists(absoluteCoveragePath))) {
|
|
1043
|
+
fail(`Missing upgrade UI contract warning source: ${coverageRule.path}`);
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const coverageContent = await readTextFile(absoluteCoveragePath);
|
|
1048
|
+
for (const requiredSnippet of coverageRule.snippets) {
|
|
1049
|
+
if (coverageContent.includes(requiredSnippet)) {
|
|
1050
|
+
pass(`${coverageRule.path} includes upgrade UI contract warning snippet: ${requiredSnippet}`);
|
|
1051
|
+
} else {
|
|
1052
|
+
fail(`${coverageRule.path} is missing upgrade UI contract warning snippet: ${requiredSnippet}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1024
1058
|
async function validateDeterministicBoundaryEnforcementCoverage() {
|
|
1025
1059
|
console.log('\nChecking deterministic boundary enforcement coverage...');
|
|
1026
1060
|
|
|
@@ -1289,6 +1323,7 @@ async function main() {
|
|
|
1289
1323
|
await validateStackResearchEngineCoverage();
|
|
1290
1324
|
await validateUniversalSopConsolidationCoverage();
|
|
1291
1325
|
await validateTemplateFreeBootstrapCoverage();
|
|
1326
|
+
await validateUpgradeUiContractWarningCoverage();
|
|
1292
1327
|
await validateDeterministicBoundaryEnforcementCoverage();
|
|
1293
1328
|
await validateStackResearchSnapshotState();
|
|
1294
1329
|
await validateMcpConfiguration();
|