popeye-cli 1.4.7 → 1.6.0

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.
Files changed (214) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +264 -63
  3. package/dist/adapters/gemini.d.ts +1 -0
  4. package/dist/adapters/gemini.d.ts.map +1 -1
  5. package/dist/adapters/gemini.js +9 -4
  6. package/dist/adapters/gemini.js.map +1 -1
  7. package/dist/adapters/grok.d.ts +1 -0
  8. package/dist/adapters/grok.d.ts.map +1 -1
  9. package/dist/adapters/grok.js +9 -4
  10. package/dist/adapters/grok.js.map +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js +35 -9
  14. package/dist/adapters/openai.js.map +1 -1
  15. package/dist/cli/commands/create.d.ts.map +1 -1
  16. package/dist/cli/commands/create.js +54 -4
  17. package/dist/cli/commands/create.js.map +1 -1
  18. package/dist/cli/interactive.d.ts +29 -0
  19. package/dist/cli/interactive.d.ts.map +1 -1
  20. package/dist/cli/interactive.js +132 -7
  21. package/dist/cli/interactive.js.map +1 -1
  22. package/dist/generators/all.d.ts +8 -2
  23. package/dist/generators/all.d.ts.map +1 -1
  24. package/dist/generators/all.js +37 -316
  25. package/dist/generators/all.js.map +1 -1
  26. package/dist/generators/doc-parser.d.ts +64 -0
  27. package/dist/generators/doc-parser.d.ts.map +1 -0
  28. package/dist/generators/doc-parser.js +407 -0
  29. package/dist/generators/doc-parser.js.map +1 -0
  30. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  31. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  32. package/dist/generators/frontend-design-analyzer.js +208 -0
  33. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  34. package/dist/generators/shared-packages.d.ts +45 -0
  35. package/dist/generators/shared-packages.d.ts.map +1 -0
  36. package/dist/generators/shared-packages.js +456 -0
  37. package/dist/generators/shared-packages.js.map +1 -0
  38. package/dist/generators/templates/index.d.ts +8 -0
  39. package/dist/generators/templates/index.d.ts.map +1 -1
  40. package/dist/generators/templates/index.js +8 -0
  41. package/dist/generators/templates/index.js.map +1 -1
  42. package/dist/generators/templates/website-components.d.ts +33 -0
  43. package/dist/generators/templates/website-components.d.ts.map +1 -0
  44. package/dist/generators/templates/website-components.js +303 -0
  45. package/dist/generators/templates/website-components.js.map +1 -0
  46. package/dist/generators/templates/website-config.d.ts +55 -0
  47. package/dist/generators/templates/website-config.d.ts.map +1 -0
  48. package/dist/generators/templates/website-config.js +425 -0
  49. package/dist/generators/templates/website-config.js.map +1 -0
  50. package/dist/generators/templates/website-conversion.d.ts +27 -0
  51. package/dist/generators/templates/website-conversion.d.ts.map +1 -0
  52. package/dist/generators/templates/website-conversion.js +326 -0
  53. package/dist/generators/templates/website-conversion.js.map +1 -0
  54. package/dist/generators/templates/website-landing.d.ts +24 -0
  55. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  56. package/dist/generators/templates/website-landing.js +276 -0
  57. package/dist/generators/templates/website-landing.js.map +1 -0
  58. package/dist/generators/templates/website-layout.d.ts +42 -0
  59. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  60. package/dist/generators/templates/website-layout.js +408 -0
  61. package/dist/generators/templates/website-layout.js.map +1 -0
  62. package/dist/generators/templates/website-pricing.d.ts +11 -0
  63. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  64. package/dist/generators/templates/website-pricing.js +313 -0
  65. package/dist/generators/templates/website-pricing.js.map +1 -0
  66. package/dist/generators/templates/website-sections.d.ts +102 -0
  67. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  68. package/dist/generators/templates/website-sections.js +444 -0
  69. package/dist/generators/templates/website-sections.js.map +1 -0
  70. package/dist/generators/templates/website-seo.d.ts +76 -0
  71. package/dist/generators/templates/website-seo.d.ts.map +1 -0
  72. package/dist/generators/templates/website-seo.js +326 -0
  73. package/dist/generators/templates/website-seo.js.map +1 -0
  74. package/dist/generators/templates/website.d.ts +10 -83
  75. package/dist/generators/templates/website.d.ts.map +1 -1
  76. package/dist/generators/templates/website.js +12 -875
  77. package/dist/generators/templates/website.js.map +1 -1
  78. package/dist/generators/website-content-scanner.d.ts +37 -0
  79. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  80. package/dist/generators/website-content-scanner.js +165 -0
  81. package/dist/generators/website-content-scanner.js.map +1 -0
  82. package/dist/generators/website-context.d.ts +119 -0
  83. package/dist/generators/website-context.d.ts.map +1 -0
  84. package/dist/generators/website-context.js +350 -0
  85. package/dist/generators/website-context.js.map +1 -0
  86. package/dist/generators/website-debug.d.ts +68 -0
  87. package/dist/generators/website-debug.d.ts.map +1 -0
  88. package/dist/generators/website-debug.js +93 -0
  89. package/dist/generators/website-debug.js.map +1 -0
  90. package/dist/generators/website.d.ts +5 -0
  91. package/dist/generators/website.d.ts.map +1 -1
  92. package/dist/generators/website.js +136 -11
  93. package/dist/generators/website.js.map +1 -1
  94. package/dist/generators/workspace-root.d.ts +27 -0
  95. package/dist/generators/workspace-root.d.ts.map +1 -0
  96. package/dist/generators/workspace-root.js +100 -0
  97. package/dist/generators/workspace-root.js.map +1 -0
  98. package/dist/state/index.d.ts +35 -0
  99. package/dist/state/index.d.ts.map +1 -1
  100. package/dist/state/index.js +40 -0
  101. package/dist/state/index.js.map +1 -1
  102. package/dist/types/consensus.d.ts +3 -0
  103. package/dist/types/consensus.d.ts.map +1 -1
  104. package/dist/types/consensus.js +1 -0
  105. package/dist/types/consensus.js.map +1 -1
  106. package/dist/types/website-strategy.d.ts +263 -0
  107. package/dist/types/website-strategy.d.ts.map +1 -0
  108. package/dist/types/website-strategy.js +105 -0
  109. package/dist/types/website-strategy.js.map +1 -0
  110. package/dist/types/workflow.d.ts +21 -0
  111. package/dist/types/workflow.d.ts.map +1 -1
  112. package/dist/types/workflow.js +8 -0
  113. package/dist/types/workflow.js.map +1 -1
  114. package/dist/upgrade/handlers.d.ts +15 -0
  115. package/dist/upgrade/handlers.d.ts.map +1 -1
  116. package/dist/upgrade/handlers.js +52 -0
  117. package/dist/upgrade/handlers.js.map +1 -1
  118. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  119. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  120. package/dist/workflow/auto-fix-bundler.js +320 -0
  121. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  122. package/dist/workflow/auto-fix.d.ts.map +1 -1
  123. package/dist/workflow/auto-fix.js +10 -3
  124. package/dist/workflow/auto-fix.js.map +1 -1
  125. package/dist/workflow/consensus.d.ts.map +1 -1
  126. package/dist/workflow/consensus.js +2 -0
  127. package/dist/workflow/consensus.js.map +1 -1
  128. package/dist/workflow/execution-mode.d.ts.map +1 -1
  129. package/dist/workflow/execution-mode.js +18 -0
  130. package/dist/workflow/execution-mode.js.map +1 -1
  131. package/dist/workflow/index.d.ts +4 -0
  132. package/dist/workflow/index.d.ts.map +1 -1
  133. package/dist/workflow/index.js +37 -0
  134. package/dist/workflow/index.js.map +1 -1
  135. package/dist/workflow/overview.d.ts +89 -0
  136. package/dist/workflow/overview.d.ts.map +1 -0
  137. package/dist/workflow/overview.js +358 -0
  138. package/dist/workflow/overview.js.map +1 -0
  139. package/dist/workflow/plan-mode.d.ts +6 -4
  140. package/dist/workflow/plan-mode.d.ts.map +1 -1
  141. package/dist/workflow/plan-mode.js +148 -6
  142. package/dist/workflow/plan-mode.js.map +1 -1
  143. package/dist/workflow/website-strategy.d.ts +79 -0
  144. package/dist/workflow/website-strategy.d.ts.map +1 -0
  145. package/dist/workflow/website-strategy.js +310 -0
  146. package/dist/workflow/website-strategy.js.map +1 -0
  147. package/dist/workflow/website-updater.d.ts +17 -0
  148. package/dist/workflow/website-updater.d.ts.map +1 -0
  149. package/dist/workflow/website-updater.js +116 -0
  150. package/dist/workflow/website-updater.js.map +1 -0
  151. package/dist/workflow/workflow-logger.d.ts +1 -1
  152. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  153. package/dist/workflow/workflow-logger.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/adapters/gemini.ts +10 -4
  156. package/src/adapters/grok.ts +10 -4
  157. package/src/adapters/openai.ts +38 -6
  158. package/src/cli/commands/create.ts +58 -4
  159. package/src/cli/interactive.ts +143 -7
  160. package/src/generators/all.ts +49 -332
  161. package/src/generators/doc-parser.ts +449 -0
  162. package/src/generators/frontend-design-analyzer.ts +261 -0
  163. package/src/generators/shared-packages.ts +500 -0
  164. package/src/generators/templates/index.ts +8 -0
  165. package/src/generators/templates/website-components.ts +330 -0
  166. package/src/generators/templates/website-config.ts +444 -0
  167. package/src/generators/templates/website-conversion.ts +341 -0
  168. package/src/generators/templates/website-landing.ts +331 -0
  169. package/src/generators/templates/website-layout.ts +443 -0
  170. package/src/generators/templates/website-pricing.ts +330 -0
  171. package/src/generators/templates/website-sections.ts +541 -0
  172. package/src/generators/templates/website-seo.ts +370 -0
  173. package/src/generators/templates/website.ts +38 -905
  174. package/src/generators/website-content-scanner.ts +208 -0
  175. package/src/generators/website-context.ts +493 -0
  176. package/src/generators/website-debug.ts +130 -0
  177. package/src/generators/website.ts +178 -20
  178. package/src/generators/workspace-root.ts +113 -0
  179. package/src/state/index.ts +56 -0
  180. package/src/types/consensus.ts +3 -0
  181. package/src/types/website-strategy.ts +243 -0
  182. package/src/types/workflow.ts +21 -0
  183. package/src/upgrade/handlers.ts +65 -0
  184. package/src/workflow/auto-fix-bundler.ts +392 -0
  185. package/src/workflow/auto-fix.ts +11 -3
  186. package/src/workflow/consensus.ts +2 -0
  187. package/src/workflow/execution-mode.ts +21 -0
  188. package/src/workflow/index.ts +37 -0
  189. package/src/workflow/overview.ts +475 -0
  190. package/src/workflow/plan-mode.ts +193 -8
  191. package/src/workflow/website-strategy.ts +379 -0
  192. package/src/workflow/website-updater.ts +142 -0
  193. package/src/workflow/workflow-logger.ts +1 -0
  194. package/tests/adapters/persona-switching.test.ts +63 -0
  195. package/tests/cli/project-naming.test.ts +136 -0
  196. package/tests/generators/doc-parser.test.ts +121 -0
  197. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  198. package/tests/generators/quality-gate.test.ts +183 -0
  199. package/tests/generators/shared-packages.test.ts +83 -0
  200. package/tests/generators/website-components.test.ts +159 -0
  201. package/tests/generators/website-config.test.ts +84 -0
  202. package/tests/generators/website-content-scanner.test.ts +181 -0
  203. package/tests/generators/website-context.test.ts +331 -0
  204. package/tests/generators/website-debug.test.ts +77 -0
  205. package/tests/generators/website-landing.test.ts +188 -0
  206. package/tests/generators/website-pricing.test.ts +98 -0
  207. package/tests/generators/website-sections.test.ts +245 -0
  208. package/tests/generators/website-seo-quality.test.ts +246 -0
  209. package/tests/generators/workspace-root.test.ts +105 -0
  210. package/tests/upgrade/handlers.test.ts +162 -0
  211. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  212. package/tests/workflow/overview.test.ts +392 -0
  213. package/tests/workflow/plan-mode.test.ts +111 -1
  214. package/tests/workflow/website-strategy.test.ts +246 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Website strategy type definitions
3
+ * Defines the strategic marketing document, site architecture,
4
+ * design tokens, and brand assets contract for website generation
5
+ */
6
+
7
+ import { z } from 'zod';
8
+
9
+ /**
10
+ * Navigation item for site header/footer
11
+ */
12
+ export interface NavItem {
13
+ label: string;
14
+ href: string;
15
+ children?: NavItem[];
16
+ }
17
+
18
+ /**
19
+ * Footer section with links
20
+ */
21
+ export interface FooterSection {
22
+ title: string;
23
+ links: Array<{ label: string; href: string }>;
24
+ }
25
+
26
+ /**
27
+ * Page definition in site architecture
28
+ */
29
+ export interface SiteArchitecturePage {
30
+ path: string;
31
+ title: string;
32
+ purpose: string;
33
+ pageType:
34
+ | 'landing'
35
+ | 'pricing'
36
+ | 'solution'
37
+ | 'use-cases'
38
+ | 'about'
39
+ | 'contact'
40
+ | 'blog'
41
+ | 'docs'
42
+ | 'legal';
43
+ sections: string[];
44
+ seoKeywords: string[];
45
+ conversionGoal: string;
46
+ }
47
+
48
+ /**
49
+ * Complete website strategy document
50
+ * Generated by AI from product context, user docs, and market inputs
51
+ */
52
+ export interface WebsiteStrategyDocument {
53
+ icp: {
54
+ primaryPersona: string;
55
+ painPoints: string[];
56
+ goals: string[];
57
+ objections: string[];
58
+ };
59
+ positioning: {
60
+ category: string;
61
+ differentiators: string[];
62
+ valueProposition: string;
63
+ proofPoints: string[];
64
+ };
65
+ messaging: {
66
+ headline: string;
67
+ subheadline: string;
68
+ elevatorPitch: string;
69
+ longDescription: string;
70
+ };
71
+ seoStrategy: {
72
+ primaryKeywords: string[];
73
+ secondaryKeywords: string[];
74
+ longTailKeywords: string[];
75
+ titleTemplates: Record<string, string>;
76
+ metaDescriptions: Record<string, string>;
77
+ };
78
+ siteArchitecture: {
79
+ pages: SiteArchitecturePage[];
80
+ navigation: NavItem[];
81
+ footerSections: FooterSection[];
82
+ };
83
+ conversionStrategy: {
84
+ primaryCta: { text: string; href: string };
85
+ secondaryCta: { text: string; href: string };
86
+ trustSignals: string[];
87
+ socialProof: string[];
88
+ leadCapture: 'none' | 'webhook' | 'resend' | 'postmark';
89
+ };
90
+ competitiveContext: {
91
+ category: string;
92
+ competitors: string[];
93
+ differentiators: string[];
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Strategy metadata for caching and staleness detection
99
+ */
100
+ export interface StrategyMetadata {
101
+ /** SHA-256 of (docs + spec + tokens) for staleness detection */
102
+ inputHash: string;
103
+ /** ISO timestamp of generation */
104
+ generatedAt: string;
105
+ /** Schema version */
106
+ version: number;
107
+ }
108
+
109
+ /**
110
+ * Design tokens extracted from project config or brand docs
111
+ */
112
+ export interface DesignTokens {
113
+ colors: Record<string, string>;
114
+ fonts: { heading: string; body: string; mono?: string };
115
+ borderRadius: string;
116
+ source: 'tailwind-config' | 'css-variables' | 'brand-docs' | 'defaults';
117
+ }
118
+
119
+ /**
120
+ * Brand assets contract for deterministic logo/favicon placement
121
+ */
122
+ export interface BrandAssetsContract {
123
+ /** Discovered source path for logo */
124
+ logoPath?: string;
125
+ /** Canonical output path: public/brand/logo.{ext} */
126
+ logoOutputPath: string;
127
+ /** Discovered source path for favicon */
128
+ faviconPath?: string;
129
+ /** Primary brand color */
130
+ primaryColor?: string;
131
+ /** Full color scheme */
132
+ colorScheme?: Record<string, string>;
133
+ }
134
+
135
+ // --- Zod Schemas ---
136
+
137
+ export const NavItemSchema: z.ZodType<NavItem> = z.lazy(() =>
138
+ z.object({
139
+ label: z.string().min(1),
140
+ href: z.string().min(1),
141
+ children: z.array(NavItemSchema).optional(),
142
+ })
143
+ );
144
+
145
+ export const FooterSectionSchema = z.object({
146
+ title: z.string().min(1),
147
+ links: z.array(
148
+ z.object({
149
+ label: z.string().min(1),
150
+ href: z.string().min(1),
151
+ })
152
+ ),
153
+ });
154
+
155
+ export const SiteArchitecturePageSchema = z.object({
156
+ path: z.string().min(1),
157
+ title: z.string().min(1),
158
+ purpose: z.string(),
159
+ pageType: z.enum([
160
+ 'landing',
161
+ 'pricing',
162
+ 'solution',
163
+ 'use-cases',
164
+ 'about',
165
+ 'contact',
166
+ 'blog',
167
+ 'docs',
168
+ 'legal',
169
+ ]),
170
+ sections: z.array(z.string()),
171
+ seoKeywords: z.array(z.string()),
172
+ conversionGoal: z.string(),
173
+ });
174
+
175
+ export const WebsiteStrategySchema = z.object({
176
+ icp: z.object({
177
+ primaryPersona: z.string().min(1),
178
+ painPoints: z.array(z.string()),
179
+ goals: z.array(z.string()),
180
+ objections: z.array(z.string()),
181
+ }),
182
+ positioning: z.object({
183
+ category: z.string().min(1),
184
+ differentiators: z.array(z.string()),
185
+ valueProposition: z.string().min(1),
186
+ proofPoints: z.array(z.string()),
187
+ }),
188
+ messaging: z.object({
189
+ headline: z.string().min(1),
190
+ subheadline: z.string().min(1),
191
+ elevatorPitch: z.string().min(1),
192
+ longDescription: z.string().min(1),
193
+ }),
194
+ seoStrategy: z.object({
195
+ primaryKeywords: z.array(z.string()),
196
+ secondaryKeywords: z.array(z.string()),
197
+ longTailKeywords: z.array(z.string()),
198
+ titleTemplates: z.record(z.string(), z.string()),
199
+ metaDescriptions: z.record(z.string(), z.string()),
200
+ }),
201
+ siteArchitecture: z.object({
202
+ pages: z.array(SiteArchitecturePageSchema),
203
+ navigation: z.array(NavItemSchema),
204
+ footerSections: z.array(FooterSectionSchema),
205
+ }),
206
+ conversionStrategy: z.object({
207
+ primaryCta: z.object({ text: z.string().min(1), href: z.string().min(1) }),
208
+ secondaryCta: z.object({ text: z.string().min(1), href: z.string().min(1) }),
209
+ trustSignals: z.array(z.string()),
210
+ socialProof: z.array(z.string()),
211
+ leadCapture: z.enum(['none', 'webhook', 'resend', 'postmark']),
212
+ }),
213
+ competitiveContext: z.object({
214
+ category: z.string(),
215
+ competitors: z.array(z.string()),
216
+ differentiators: z.array(z.string()),
217
+ }),
218
+ });
219
+
220
+ export const StrategyMetadataSchema = z.object({
221
+ inputHash: z.string().min(1),
222
+ generatedAt: z.string().min(1),
223
+ version: z.number(),
224
+ });
225
+
226
+ export const DesignTokensSchema = z.object({
227
+ colors: z.record(z.string(), z.string()),
228
+ fonts: z.object({
229
+ heading: z.string(),
230
+ body: z.string(),
231
+ mono: z.string().optional(),
232
+ }),
233
+ borderRadius: z.string(),
234
+ source: z.enum(['tailwind-config', 'css-variables', 'brand-docs', 'defaults']),
235
+ });
236
+
237
+ export const BrandAssetsContractSchema = z.object({
238
+ logoPath: z.string().optional(),
239
+ logoOutputPath: z.string().min(1),
240
+ faviconPath: z.string().optional(),
241
+ primaryColor: z.string().optional(),
242
+ colorScheme: z.record(z.string(), z.string()).optional(),
243
+ });
@@ -192,6 +192,19 @@ export interface ProjectState {
192
192
  error?: string;
193
193
  createdAt: string;
194
194
  updatedAt: string;
195
+ /** Raw user documentation discovered from CWD */
196
+ userDocs?: string;
197
+ /** Brand context discovered from CWD */
198
+ brandContext?: {
199
+ logoPath?: string;
200
+ primaryColor?: string;
201
+ };
202
+ /** Path to website strategy JSON file (relative to .popeye/) */
203
+ websiteStrategy?: string;
204
+ /** Error message from website strategy generation (for visibility) */
205
+ strategyError?: string;
206
+ /** Absolute paths to discovered source documentation files */
207
+ sourceDocPaths?: string[];
195
208
  }
196
209
 
197
210
  /**
@@ -229,6 +242,14 @@ export const ProjectStateSchema = z.object({
229
242
  error: z.string().optional(),
230
243
  createdAt: z.string(),
231
244
  updatedAt: z.string(),
245
+ userDocs: z.string().optional(),
246
+ brandContext: z.object({
247
+ logoPath: z.string().optional(),
248
+ primaryColor: z.string().optional(),
249
+ }).optional(),
250
+ websiteStrategy: z.string().optional(),
251
+ strategyError: z.string().optional(),
252
+ sourceDocPaths: z.array(z.string()).optional(),
232
253
  });
233
254
 
234
255
  /**
@@ -21,6 +21,10 @@ import {
21
21
  generateRootDockerCompose,
22
22
  } from '../generators/templates/fullstack.js';
23
23
  import { loadState, saveState } from '../state/persistence.js';
24
+ import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext } from '../generators/website-context.js';
25
+ import type { WebsiteContentContext } from '../generators/website-context.js';
26
+ import { resolveWorkspaceRoot } from '../generators/workspace-root.js';
27
+ import { loadWebsiteStrategy } from '../workflow/website-strategy.js';
24
28
  import type { UpgradeResult } from './index.js';
25
29
 
26
30
  /**
@@ -42,6 +46,57 @@ async function pathExists(filePath: string): Promise<boolean> {
42
46
  }
43
47
  }
44
48
 
49
+ /**
50
+ * Build website content context from user docs, brand assets, and strategy
51
+ *
52
+ * Replicates the context-building pattern from website-updater.ts so that
53
+ * upgrade-generated websites get real content instead of TODO placeholders.
54
+ *
55
+ * @param projectDir - Project directory (workspace root)
56
+ * @param projectName - Project name
57
+ * @returns Content context and optional warning
58
+ */
59
+ export async function buildUpgradeContentContext(
60
+ projectDir: string,
61
+ projectName: string,
62
+ ): Promise<{ context?: WebsiteContentContext; warning?: string }> {
63
+ try {
64
+ // Build context from user docs (scans projectDir + parent via getScanDirectories)
65
+ const context = await buildWebsiteContext(projectDir, projectName);
66
+
67
+ // Apply brand context from state if available
68
+ const state = await loadState(projectDir);
69
+ if (state?.brandContext?.primaryColor) {
70
+ context.brand = { ...context.brand, primaryColor: state.brandContext.primaryColor };
71
+ }
72
+ if (state?.brandContext?.logoPath) {
73
+ context.brand = { ...context.brand, logoPath: state.brandContext.logoPath };
74
+ }
75
+
76
+ // Resolve brand assets using workspace root
77
+ const workspaceRoot = await resolveWorkspaceRoot(projectDir);
78
+ context.brandAssets = await resolveBrandAssets(workspaceRoot, context.brand);
79
+
80
+ // Load website strategy if available
81
+ const strategyData = await loadWebsiteStrategy(projectDir);
82
+ if (strategyData) {
83
+ context.strategy = strategyData.strategy;
84
+ }
85
+
86
+ // Soft validation: include quality warnings in the return value
87
+ const validation = validateWebsiteContext(context, projectName);
88
+ const validationWarnings = [...validation.issues, ...validation.warnings]
89
+ .map((w) => `[quality-gate] ${w}`);
90
+
91
+ return {
92
+ context,
93
+ warning: validationWarnings.length > 0 ? validationWarnings.join('; ') : undefined,
94
+ };
95
+ } catch (e) {
96
+ return { warning: e instanceof Error ? e.message : 'Unknown error building website context' };
97
+ }
98
+ }
99
+
45
100
  /**
46
101
  * Update state.json language field
47
102
  *
@@ -297,11 +352,21 @@ export async function upgradeFullstackToAll(
297
352
  openaiModel: 'gpt-4o',
298
353
  };
299
354
 
355
+ // Build content context from user docs, brand assets, and strategy
356
+ const { context: contentContext, warning } = await buildUpgradeContentContext(
357
+ projectDir,
358
+ projectName,
359
+ );
360
+ if (warning) {
361
+ console.warn(`[upgrade] Website context warning: ${warning}`);
362
+ }
363
+
300
364
  const result = await generateWebsiteProject(spec, projectDir, {
301
365
  baseDir: websiteDir,
302
366
  workspaceMode: true,
303
367
  skipDocker: true,
304
368
  skipReadme: true,
369
+ contentContext,
305
370
  });
306
371
 
307
372
  if (!result.success) {