popeye-cli 2.1.0 → 2.7.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 (356) hide show
  1. package/dist/adapters/gemini.d.ts +14 -0
  2. package/dist/adapters/gemini.d.ts.map +1 -1
  3. package/dist/adapters/gemini.js +41 -6
  4. package/dist/adapters/gemini.js.map +1 -1
  5. package/dist/adapters/grok.d.ts +14 -0
  6. package/dist/adapters/grok.d.ts.map +1 -1
  7. package/dist/adapters/grok.js +42 -6
  8. package/dist/adapters/grok.js.map +1 -1
  9. package/dist/adapters/openai.d.ts +10 -0
  10. package/dist/adapters/openai.d.ts.map +1 -1
  11. package/dist/adapters/openai.js +44 -5
  12. package/dist/adapters/openai.js.map +1 -1
  13. package/dist/cli/commands/create.js +1 -1
  14. package/dist/cli/commands/create.js.map +1 -1
  15. package/dist/cli/interactive.d.ts.map +1 -1
  16. package/dist/cli/interactive.js +328 -21
  17. package/dist/cli/interactive.js.map +1 -1
  18. package/dist/generators/all.d.ts.map +1 -1
  19. package/dist/generators/all.js +25 -2
  20. package/dist/generators/all.js.map +1 -1
  21. package/dist/generators/doc-parser.d.ts +21 -6
  22. package/dist/generators/doc-parser.d.ts.map +1 -1
  23. package/dist/generators/doc-parser.js +55 -4
  24. package/dist/generators/doc-parser.js.map +1 -1
  25. package/dist/generators/templates/fullstack.js +1 -1
  26. package/dist/generators/templates/website-components.js +1 -1
  27. package/dist/generators/templates/website-components.js.map +1 -1
  28. package/dist/generators/templates/website-config.d.ts +4 -1
  29. package/dist/generators/templates/website-config.d.ts.map +1 -1
  30. package/dist/generators/templates/website-config.js +17 -11
  31. package/dist/generators/templates/website-config.js.map +1 -1
  32. package/dist/generators/templates/website-conversion.js +1 -1
  33. package/dist/generators/templates/website-conversion.js.map +1 -1
  34. package/dist/generators/templates/website-landing.js +1 -1
  35. package/dist/generators/templates/website-landing.js.map +1 -1
  36. package/dist/generators/templates/website-layout.d.ts +36 -4
  37. package/dist/generators/templates/website-layout.d.ts.map +1 -1
  38. package/dist/generators/templates/website-layout.js +466 -23
  39. package/dist/generators/templates/website-layout.js.map +1 -1
  40. package/dist/generators/templates/website-pricing.js +1 -1
  41. package/dist/generators/templates/website-pricing.js.map +1 -1
  42. package/dist/generators/templates/website-sections.js +1 -1
  43. package/dist/generators/templates/website-sections.js.map +1 -1
  44. package/dist/generators/templates/website-seo.d.ts.map +1 -1
  45. package/dist/generators/templates/website-seo.js +4 -1
  46. package/dist/generators/templates/website-seo.js.map +1 -1
  47. package/dist/generators/templates/website.d.ts +1 -1
  48. package/dist/generators/templates/website.d.ts.map +1 -1
  49. package/dist/generators/templates/website.js +1 -1
  50. package/dist/generators/templates/website.js.map +1 -1
  51. package/dist/generators/website-content-ai.d.ts +52 -0
  52. package/dist/generators/website-content-ai.d.ts.map +1 -0
  53. package/dist/generators/website-content-ai.js +141 -0
  54. package/dist/generators/website-content-ai.js.map +1 -0
  55. package/dist/generators/website-content-scanner.d.ts +1 -1
  56. package/dist/generators/website-content-scanner.d.ts.map +1 -1
  57. package/dist/generators/website-content-scanner.js +98 -1
  58. package/dist/generators/website-content-scanner.js.map +1 -1
  59. package/dist/generators/website-context.d.ts +34 -1
  60. package/dist/generators/website-context.d.ts.map +1 -1
  61. package/dist/generators/website-context.js +131 -9
  62. package/dist/generators/website-context.js.map +1 -1
  63. package/dist/generators/website-debug.d.ts +12 -0
  64. package/dist/generators/website-debug.d.ts.map +1 -1
  65. package/dist/generators/website-debug.js +16 -0
  66. package/dist/generators/website-debug.js.map +1 -1
  67. package/dist/generators/website.d.ts.map +1 -1
  68. package/dist/generators/website.js +26 -4
  69. package/dist/generators/website.js.map +1 -1
  70. package/dist/pipeline/artifact-manager.d.ts.map +1 -1
  71. package/dist/pipeline/artifact-manager.js +3 -0
  72. package/dist/pipeline/artifact-manager.js.map +1 -1
  73. package/dist/pipeline/auto-recovery.d.ts +56 -0
  74. package/dist/pipeline/auto-recovery.d.ts.map +1 -0
  75. package/dist/pipeline/auto-recovery.js +185 -0
  76. package/dist/pipeline/auto-recovery.js.map +1 -0
  77. package/dist/pipeline/change-request.d.ts +39 -0
  78. package/dist/pipeline/change-request.d.ts.map +1 -1
  79. package/dist/pipeline/change-request.js +40 -1
  80. package/dist/pipeline/change-request.js.map +1 -1
  81. package/dist/pipeline/check-runner.d.ts +30 -1
  82. package/dist/pipeline/check-runner.d.ts.map +1 -1
  83. package/dist/pipeline/check-runner.js +122 -1
  84. package/dist/pipeline/check-runner.js.map +1 -1
  85. package/dist/pipeline/command-resolver.d.ts.map +1 -1
  86. package/dist/pipeline/command-resolver.js +33 -2
  87. package/dist/pipeline/command-resolver.js.map +1 -1
  88. package/dist/pipeline/consensus/arbitrator-query.d.ts +22 -0
  89. package/dist/pipeline/consensus/arbitrator-query.d.ts.map +1 -0
  90. package/dist/pipeline/consensus/arbitrator-query.js +70 -0
  91. package/dist/pipeline/consensus/arbitrator-query.js.map +1 -0
  92. package/dist/pipeline/consensus/consensus-runner.d.ts +131 -7
  93. package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -1
  94. package/dist/pipeline/consensus/consensus-runner.js +809 -35
  95. package/dist/pipeline/consensus/consensus-runner.js.map +1 -1
  96. package/dist/pipeline/cr-lifecycle.d.ts +42 -0
  97. package/dist/pipeline/cr-lifecycle.d.ts.map +1 -0
  98. package/dist/pipeline/cr-lifecycle.js +89 -0
  99. package/dist/pipeline/cr-lifecycle.js.map +1 -0
  100. package/dist/pipeline/gate-engine.d.ts +1 -0
  101. package/dist/pipeline/gate-engine.d.ts.map +1 -1
  102. package/dist/pipeline/gate-engine.js +27 -8
  103. package/dist/pipeline/gate-engine.js.map +1 -1
  104. package/dist/pipeline/migration.d.ts.map +1 -1
  105. package/dist/pipeline/migration.js +3 -26
  106. package/dist/pipeline/migration.js.map +1 -1
  107. package/dist/pipeline/orchestrator.d.ts +1 -1
  108. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  109. package/dist/pipeline/orchestrator.js +311 -16
  110. package/dist/pipeline/orchestrator.js.map +1 -1
  111. package/dist/pipeline/packets/consensus-packet-builder.d.ts +15 -4
  112. package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -1
  113. package/dist/pipeline/packets/consensus-packet-builder.js +29 -17
  114. package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -1
  115. package/dist/pipeline/phases/architecture.d.ts.map +1 -1
  116. package/dist/pipeline/phases/architecture.js +5 -3
  117. package/dist/pipeline/phases/architecture.js.map +1 -1
  118. package/dist/pipeline/phases/audit.d.ts.map +1 -1
  119. package/dist/pipeline/phases/audit.js +5 -3
  120. package/dist/pipeline/phases/audit.js.map +1 -1
  121. package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -1
  122. package/dist/pipeline/phases/consensus-architecture.js +10 -1
  123. package/dist/pipeline/phases/consensus-architecture.js.map +1 -1
  124. package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -1
  125. package/dist/pipeline/phases/consensus-master-plan.js +10 -3
  126. package/dist/pipeline/phases/consensus-master-plan.js.map +1 -1
  127. package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -1
  128. package/dist/pipeline/phases/consensus-role-plans.js +10 -1
  129. package/dist/pipeline/phases/consensus-role-plans.js.map +1 -1
  130. package/dist/pipeline/phases/done.d.ts.map +1 -1
  131. package/dist/pipeline/phases/done.js +9 -4
  132. package/dist/pipeline/phases/done.js.map +1 -1
  133. package/dist/pipeline/phases/intake.d.ts +1 -0
  134. package/dist/pipeline/phases/intake.d.ts.map +1 -1
  135. package/dist/pipeline/phases/intake.js +56 -13
  136. package/dist/pipeline/phases/intake.js.map +1 -1
  137. package/dist/pipeline/phases/phase-context.d.ts +2 -0
  138. package/dist/pipeline/phases/phase-context.d.ts.map +1 -1
  139. package/dist/pipeline/phases/phase-context.js +3 -1
  140. package/dist/pipeline/phases/phase-context.js.map +1 -1
  141. package/dist/pipeline/phases/production-gate.d.ts.map +1 -1
  142. package/dist/pipeline/phases/production-gate.js +28 -3
  143. package/dist/pipeline/phases/production-gate.js.map +1 -1
  144. package/dist/pipeline/phases/qa-validation.d.ts.map +1 -1
  145. package/dist/pipeline/phases/qa-validation.js +38 -5
  146. package/dist/pipeline/phases/qa-validation.js.map +1 -1
  147. package/dist/pipeline/phases/recovery-loop.d.ts +2 -0
  148. package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -1
  149. package/dist/pipeline/phases/recovery-loop.js +200 -6
  150. package/dist/pipeline/phases/recovery-loop.js.map +1 -1
  151. package/dist/pipeline/phases/review.d.ts.map +1 -1
  152. package/dist/pipeline/phases/review.js +58 -28
  153. package/dist/pipeline/phases/review.js.map +1 -1
  154. package/dist/pipeline/phases/role-planning.d.ts.map +1 -1
  155. package/dist/pipeline/phases/role-planning.js +20 -5
  156. package/dist/pipeline/phases/role-planning.js.map +1 -1
  157. package/dist/pipeline/phases/stuck.d.ts.map +1 -1
  158. package/dist/pipeline/phases/stuck.js +10 -0
  159. package/dist/pipeline/phases/stuck.js.map +1 -1
  160. package/dist/pipeline/repo-snapshot.d.ts.map +1 -1
  161. package/dist/pipeline/repo-snapshot.js +3 -0
  162. package/dist/pipeline/repo-snapshot.js.map +1 -1
  163. package/dist/pipeline/role-execution-adapter.d.ts +2 -1
  164. package/dist/pipeline/role-execution-adapter.d.ts.map +1 -1
  165. package/dist/pipeline/role-execution-adapter.js +22 -7
  166. package/dist/pipeline/role-execution-adapter.js.map +1 -1
  167. package/dist/pipeline/skill-loader.d.ts +19 -0
  168. package/dist/pipeline/skill-loader.d.ts.map +1 -1
  169. package/dist/pipeline/skill-loader.js +22 -0
  170. package/dist/pipeline/skill-loader.js.map +1 -1
  171. package/dist/pipeline/skills/constitution-generator.d.ts +51 -0
  172. package/dist/pipeline/skills/constitution-generator.d.ts.map +1 -0
  173. package/dist/pipeline/skills/constitution-generator.js +210 -0
  174. package/dist/pipeline/skills/constitution-generator.js.map +1 -0
  175. package/dist/pipeline/skills/coverage-gate.d.ts +44 -0
  176. package/dist/pipeline/skills/coverage-gate.d.ts.map +1 -0
  177. package/dist/pipeline/skills/coverage-gate.js +143 -0
  178. package/dist/pipeline/skills/coverage-gate.js.map +1 -0
  179. package/dist/pipeline/skills/generator.d.ts +65 -0
  180. package/dist/pipeline/skills/generator.d.ts.map +1 -0
  181. package/dist/pipeline/skills/generator.js +221 -0
  182. package/dist/pipeline/skills/generator.js.map +1 -0
  183. package/dist/pipeline/skills/role-map.d.ts +38 -0
  184. package/dist/pipeline/skills/role-map.d.ts.map +1 -0
  185. package/dist/pipeline/skills/role-map.js +234 -0
  186. package/dist/pipeline/skills/role-map.js.map +1 -0
  187. package/dist/pipeline/skills/types.d.ts +47 -0
  188. package/dist/pipeline/skills/types.d.ts.map +1 -0
  189. package/dist/pipeline/skills/types.js +5 -0
  190. package/dist/pipeline/skills/types.js.map +1 -0
  191. package/dist/pipeline/skills/usage-registry.d.ts +48 -0
  192. package/dist/pipeline/skills/usage-registry.d.ts.map +1 -0
  193. package/dist/pipeline/skills/usage-registry.js +55 -0
  194. package/dist/pipeline/skills/usage-registry.js.map +1 -0
  195. package/dist/pipeline/strategy-context.d.ts +20 -0
  196. package/dist/pipeline/strategy-context.d.ts.map +1 -0
  197. package/dist/pipeline/strategy-context.js +55 -0
  198. package/dist/pipeline/strategy-context.js.map +1 -0
  199. package/dist/pipeline/type-defs/artifacts.d.ts +30 -5
  200. package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
  201. package/dist/pipeline/type-defs/artifacts.js +5 -0
  202. package/dist/pipeline/type-defs/artifacts.js.map +1 -1
  203. package/dist/pipeline/type-defs/audit.d.ts +28 -13
  204. package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
  205. package/dist/pipeline/type-defs/checks.d.ts +19 -8
  206. package/dist/pipeline/type-defs/checks.d.ts.map +1 -1
  207. package/dist/pipeline/type-defs/checks.js +4 -0
  208. package/dist/pipeline/type-defs/checks.js.map +1 -1
  209. package/dist/pipeline/type-defs/packets.d.ts +119 -18
  210. package/dist/pipeline/type-defs/packets.d.ts.map +1 -1
  211. package/dist/pipeline/type-defs/packets.js +17 -1
  212. package/dist/pipeline/type-defs/packets.js.map +1 -1
  213. package/dist/pipeline/type-defs/state.d.ts +165 -16
  214. package/dist/pipeline/type-defs/state.d.ts.map +1 -1
  215. package/dist/pipeline/type-defs/state.js +26 -1
  216. package/dist/pipeline/type-defs/state.js.map +1 -1
  217. package/dist/shared/text-utils.d.ts +23 -0
  218. package/dist/shared/text-utils.d.ts.map +1 -0
  219. package/dist/shared/text-utils.js +66 -0
  220. package/dist/shared/text-utils.js.map +1 -0
  221. package/dist/shared/website-strategy-format.d.ts +18 -0
  222. package/dist/shared/website-strategy-format.d.ts.map +1 -0
  223. package/dist/shared/website-strategy-format.js +47 -0
  224. package/dist/shared/website-strategy-format.js.map +1 -0
  225. package/dist/state/index.d.ts +2 -0
  226. package/dist/state/index.d.ts.map +1 -1
  227. package/dist/state/index.js +57 -8
  228. package/dist/state/index.js.map +1 -1
  229. package/dist/types/consensus.d.ts +1 -0
  230. package/dist/types/consensus.d.ts.map +1 -1
  231. package/dist/types/consensus.js.map +1 -1
  232. package/dist/types/website-strategy.d.ts +1 -1
  233. package/dist/types/workflow.d.ts +447 -0
  234. package/dist/types/workflow.d.ts.map +1 -1
  235. package/dist/types/workflow.js +3 -0
  236. package/dist/types/workflow.js.map +1 -1
  237. package/dist/upgrade/handlers.d.ts.map +1 -1
  238. package/dist/upgrade/handlers.js +6 -3
  239. package/dist/upgrade/handlers.js.map +1 -1
  240. package/dist/workflow/consensus.d.ts.map +1 -1
  241. package/dist/workflow/consensus.js +1 -0
  242. package/dist/workflow/consensus.js.map +1 -1
  243. package/dist/workflow/website-strategy.d.ts.map +1 -1
  244. package/dist/workflow/website-strategy.js +2 -29
  245. package/dist/workflow/website-strategy.js.map +1 -1
  246. package/dist/workflow/website-updater.d.ts.map +1 -1
  247. package/dist/workflow/website-updater.js +3 -2
  248. package/dist/workflow/website-updater.js.map +1 -1
  249. package/package.json +1 -1
  250. package/src/adapters/gemini.ts +51 -6
  251. package/src/adapters/grok.ts +51 -6
  252. package/src/adapters/openai.ts +53 -5
  253. package/src/cli/commands/create.ts +1 -1
  254. package/src/cli/interactive.ts +337 -20
  255. package/src/generators/all.ts +25 -2
  256. package/src/generators/doc-parser.ts +75 -15
  257. package/src/generators/templates/fullstack.ts +1 -1
  258. package/src/generators/templates/website-components.ts +1 -1
  259. package/src/generators/templates/website-config.ts +23 -11
  260. package/src/generators/templates/website-conversion.ts +1 -1
  261. package/src/generators/templates/website-landing.ts +1 -1
  262. package/src/generators/templates/website-layout.ts +491 -23
  263. package/src/generators/templates/website-pricing.ts +1 -1
  264. package/src/generators/templates/website-sections.ts +1 -1
  265. package/src/generators/templates/website-seo.ts +4 -1
  266. package/src/generators/templates/website.ts +3 -0
  267. package/src/generators/website-content-ai.ts +186 -0
  268. package/src/generators/website-content-scanner.ts +113 -1
  269. package/src/generators/website-context.ts +151 -12
  270. package/src/generators/website-debug.ts +26 -0
  271. package/src/generators/website.ts +28 -3
  272. package/src/pipeline/artifact-manager.ts +3 -0
  273. package/src/pipeline/auto-recovery.ts +283 -0
  274. package/src/pipeline/change-request.ts +63 -1
  275. package/src/pipeline/check-runner.ts +141 -2
  276. package/src/pipeline/command-resolver.ts +34 -2
  277. package/src/pipeline/consensus/arbitrator-query.ts +101 -0
  278. package/src/pipeline/consensus/consensus-runner.ts +1099 -42
  279. package/src/pipeline/cr-lifecycle.ts +103 -0
  280. package/src/pipeline/gate-engine.ts +36 -8
  281. package/src/pipeline/migration.ts +5 -30
  282. package/src/pipeline/orchestrator.ts +367 -16
  283. package/src/pipeline/packets/consensus-packet-builder.ts +44 -18
  284. package/src/pipeline/phases/architecture.ts +6 -3
  285. package/src/pipeline/phases/audit.ts +6 -3
  286. package/src/pipeline/phases/consensus-architecture.ts +10 -1
  287. package/src/pipeline/phases/consensus-master-plan.ts +10 -3
  288. package/src/pipeline/phases/consensus-role-plans.ts +10 -1
  289. package/src/pipeline/phases/done.ts +15 -4
  290. package/src/pipeline/phases/intake.ts +67 -14
  291. package/src/pipeline/phases/phase-context.ts +6 -1
  292. package/src/pipeline/phases/production-gate.ts +41 -3
  293. package/src/pipeline/phases/qa-validation.ts +51 -5
  294. package/src/pipeline/phases/recovery-loop.ts +229 -7
  295. package/src/pipeline/phases/review.ts +73 -30
  296. package/src/pipeline/phases/role-planning.ts +23 -5
  297. package/src/pipeline/phases/stuck.ts +10 -0
  298. package/src/pipeline/repo-snapshot.ts +3 -0
  299. package/src/pipeline/role-execution-adapter.ts +30 -4
  300. package/src/pipeline/skill-loader.ts +33 -0
  301. package/src/pipeline/skills/constitution-generator.ts +236 -0
  302. package/src/pipeline/skills/coverage-gate.ts +199 -0
  303. package/src/pipeline/skills/generator.ts +287 -0
  304. package/src/pipeline/skills/role-map.ts +248 -0
  305. package/src/pipeline/skills/types.ts +53 -0
  306. package/src/pipeline/skills/usage-registry.ts +87 -0
  307. package/src/pipeline/strategy-context.ts +60 -0
  308. package/src/pipeline/type-defs/artifacts.ts +5 -0
  309. package/src/pipeline/type-defs/checks.ts +4 -0
  310. package/src/pipeline/type-defs/packets.ts +18 -1
  311. package/src/pipeline/type-defs/state.ts +26 -1
  312. package/src/shared/text-utils.ts +70 -0
  313. package/src/shared/website-strategy-format.ts +56 -0
  314. package/src/state/index.ts +60 -8
  315. package/src/types/consensus.ts +1 -0
  316. package/src/types/workflow.ts +6 -0
  317. package/src/upgrade/handlers.ts +9 -3
  318. package/src/workflow/consensus.ts +1 -0
  319. package/src/workflow/website-strategy.ts +2 -36
  320. package/src/workflow/website-updater.ts +4 -2
  321. package/tests/adapters/gemini.test.ts +165 -0
  322. package/tests/adapters/grok.test.ts +137 -0
  323. package/tests/adapters/openai.test.ts +128 -0
  324. package/tests/generators/doc-parser.test.ts +88 -9
  325. package/tests/generators/quality-gate.test.ts +19 -3
  326. package/tests/generators/website-components.test.ts +34 -0
  327. package/tests/generators/website-content-ai.test.ts +308 -0
  328. package/tests/generators/website-content-scanner.test.ts +86 -0
  329. package/tests/generators/website-context.test.ts +3 -2
  330. package/tests/integration/smokestack-scaffold.test.ts +385 -0
  331. package/tests/pipeline/auto-recovery.test.ts +337 -0
  332. package/tests/pipeline/change-request.test.ts +70 -0
  333. package/tests/pipeline/command-resolver.test.ts +42 -0
  334. package/tests/pipeline/consensus/arbitrator-query.test.ts +107 -0
  335. package/tests/pipeline/consensus-runner.test.ts +1333 -10
  336. package/tests/pipeline/consensus-scoring.test.ts +602 -18
  337. package/tests/pipeline/gate-engine.test.ts +34 -0
  338. package/tests/pipeline/install-check.test.ts +261 -0
  339. package/tests/pipeline/migration.test.ts +4 -3
  340. package/tests/pipeline/orchestrator.test.ts +1506 -15
  341. package/tests/pipeline/packets/builders.test.ts +29 -6
  342. package/tests/pipeline/phases/role-planning.strategy.test.ts +204 -0
  343. package/tests/pipeline/pipeline-persistence.test.ts +230 -0
  344. package/tests/pipeline/recovery-loop-guidance.test.ts +280 -0
  345. package/tests/pipeline/role-execution-adapter.test.ts +88 -0
  346. package/tests/pipeline/skills/constitution-generator.test.ts +201 -0
  347. package/tests/pipeline/skills/coverage-gate.test.ts +370 -0
  348. package/tests/pipeline/skills/generator.test.ts +213 -0
  349. package/tests/pipeline/skills/role-map.test.ts +198 -0
  350. package/tests/pipeline/skills/usage-registry.test.ts +114 -0
  351. package/tests/pipeline/strategy-context.test.ts +148 -0
  352. package/tests/shared/text-utils.test.ts +155 -0
  353. package/tests/state/progress-analysis.test.ts +375 -0
  354. package/tests/upgrade/handlers.test.ts +33 -2
  355. package/tests/workflow/consensus.test.ts +6 -0
  356. package/tsconfig.json +1 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Strategy context loader for pipeline roles.
3
+ * Loads website strategy from disk and formats it for injection
4
+ * into role planning and execution prompts.
5
+ */
6
+
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+
10
+ import type { PipelineRole } from './types.js';
11
+ import { WebsiteStrategySchema } from '../types/website-strategy.js';
12
+ import { formatWebsiteStrategy } from '../shared/website-strategy-format.js';
13
+
14
+ /** Roles that should receive website strategy context */
15
+ export const STRATEGY_ROLES: readonly PipelineRole[] = [
16
+ 'WEBSITE_PROGRAMMER',
17
+ 'MARKETING_EXPERT',
18
+ 'SOCIAL_EXPERT',
19
+ ];
20
+
21
+ /** Known strategy file locations, checked in order */
22
+ const STRATEGY_PATHS = [
23
+ '.popeye/website-strategy.json',
24
+ '.popeye/website-strategy.md',
25
+ ] as const;
26
+
27
+ /**
28
+ * Load website strategy from disk and format for prompt injection.
29
+ * Checks known paths in order. Returns undefined if no valid strategy found.
30
+ *
31
+ * Args:
32
+ * projectDir: Root project directory containing .popeye/.
33
+ *
34
+ * Returns:
35
+ * string | undefined: Formatted strategy text, or undefined if not found/invalid.
36
+ */
37
+ export function loadStrategyForRole(projectDir: string): string | undefined {
38
+ for (const relPath of STRATEGY_PATHS) {
39
+ const fullPath = join(projectDir, relPath);
40
+ if (!existsSync(fullPath)) continue;
41
+
42
+ try {
43
+ const raw = readFileSync(fullPath, 'utf-8');
44
+
45
+ // .md files: return raw content directly
46
+ if (relPath.endsWith('.md')) return raw;
47
+
48
+ // .json files: parse, validate, format
49
+ const parsed = JSON.parse(raw);
50
+ const strategyData = parsed.strategy ?? parsed;
51
+ const result = WebsiteStrategySchema.safeParse(strategyData);
52
+ if (!result.success) continue;
53
+
54
+ return formatWebsiteStrategy(result.data);
55
+ } catch {
56
+ continue; // Malformed file, try next path
57
+ }
58
+ }
59
+ return undefined;
60
+ }
@@ -32,7 +32,12 @@ export const ArtifactTypeSchema = z.enum([
32
32
  'resolved_commands',
33
33
  'constitution',
34
34
  'change_request',
35
+ 'recovery_fix_plan',
36
+ 'auto_recovery_guidance',
35
37
  'additional_context',
38
+ 'skill_generation_log',
39
+ 'skill_usage_log',
40
+ 'install_check',
36
41
  ]);
37
42
  export type ArtifactType = z.infer<typeof ArtifactTypeSchema>;
38
43
 
@@ -16,6 +16,8 @@ export const GateCheckTypeSchema = z.enum([
16
16
  'placeholder_scan',
17
17
  'start',
18
18
  'env_check',
19
+ 'skill_coverage',
20
+ 'install',
19
21
  ]);
20
22
  export type GateCheckType = z.infer<typeof GateCheckTypeSchema>;
21
23
 
@@ -42,6 +44,8 @@ export const ResolvedCommandsSchema = z.object({
42
44
  typecheck: z.string().optional(),
43
45
  migrations: z.string().optional(),
44
46
  start: z.string().optional(),
47
+ install: z.string().optional(),
48
+ install_cwd: z.string().optional(),
45
49
  resolved_from: z.string(),
46
50
  });
47
51
  export type ResolvedCommands = z.infer<typeof ResolvedCommandsSchema>;
@@ -51,8 +51,10 @@ export const ReviewerVoteSchema = z.object({
51
51
  vote: z.enum(['APPROVE', 'REJECT', 'CONDITIONAL']),
52
52
  confidence: z.number().min(0).max(1),
53
53
  blocking_issues: z.array(z.string()),
54
+ required_changes: z.array(z.string()).optional(),
54
55
  suggestions: z.array(z.string()),
55
56
  evidence_refs: z.array(ArtifactRefSchema),
57
+ reviewer_inconsistency: z.boolean().optional(),
56
58
  });
57
59
  export type ReviewerVote = z.infer<typeof ReviewerVoteSchema>;
58
60
 
@@ -69,6 +71,7 @@ export const ConsensusResultSchema = z.object({
69
71
  score: z.number().min(0).max(1),
70
72
  weighted_score: z.number().min(0).max(1),
71
73
  participating_reviewers: z.number().int(),
74
+ has_true_blockers: z.boolean().default(false),
72
75
  });
73
76
 
74
77
  export const ArbitratorResultSchema = z.object({
@@ -77,6 +80,13 @@ export const ArbitratorResultSchema = z.object({
77
80
  artifact_ref: ArtifactRefSchema.optional(),
78
81
  });
79
82
 
83
+ export const NormalizationMovesSchema = z.object({
84
+ tagged_blockers_demoted_to_suggestions: z.number(),
85
+ tagged_blockers_demoted_to_required: z.number(),
86
+ untagged_from_blocking_routed_to_required: z.number(),
87
+ forced_rejects: z.number(),
88
+ }).optional();
89
+
80
90
  export const ConsensusPacketSchema = z.object({
81
91
  metadata: z.object({
82
92
  packet_id: z.string(),
@@ -89,6 +99,7 @@ export const ConsensusPacketSchema = z.object({
89
99
  consensus_result: ConsensusResultSchema,
90
100
  arbitrator_result: ArbitratorResultSchema.optional(),
91
101
  final_status: z.enum(['APPROVED', 'REJECTED', 'ARBITRATED']),
102
+ normalization_moves: NormalizationMovesSchema,
92
103
  });
93
104
  export type ConsensusPacket = z.infer<typeof ConsensusPacketSchema>;
94
105
 
@@ -125,7 +136,13 @@ export const ChangeRequestSchema = z.object({
125
136
  affected_phases: z.array(PipelinePhaseSchema),
126
137
  risk_level: z.enum(['low', 'medium', 'high']),
127
138
  }),
128
- status: z.enum(['proposed', 'approved', 'rejected']),
139
+ status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
129
140
  approval_artifact: ArtifactRefSchema.optional(),
141
+ /** Deterministic drift fingerprint for CR deduplication (v2.4.9) */
142
+ drift_key: z.string().optional(),
143
+ /** ISO timestamp when the CR was resolved (v2.4.9) */
144
+ resolved_at: z.string().optional(),
145
+ /** How the CR was resolved (v2.4.9) */
146
+ resolution: z.enum(['accepted_baseline', 'reverted', 'manual_override']).optional(),
130
147
  });
131
148
  export type ChangeRequest = z.infer<typeof ChangeRequestSchema>;
@@ -9,6 +9,7 @@ import { GateCheckTypeSchema, GateCheckResultSchema, ResolvedCommandsSchema, typ
9
9
  import type { ArtifactType } from './artifacts.js';
10
10
  import type { ConsensusPacket } from './packets.js';
11
11
  import type { RCAPacket } from './packets.js';
12
+ // Reason: SkillUsageEvent type is defined inline in the Zod schema to keep state serialization self-contained
12
13
 
13
14
  // ─── Gate Definition ─────────────────────────────────────
14
15
 
@@ -88,6 +89,7 @@ export const PipelineStateSchema = z.object({
88
89
  missingArtifacts: z.array(ArtifactTypeSchema),
89
90
  failedChecks: z.array(GateCheckTypeSchema),
90
91
  consensusScore: z.number().optional(),
92
+ finalStatus: z.string().optional(), // v2.4.3: 'APPROVED' | 'REJECTED' | 'ARBITRATED'
91
93
  timestamp: z.string(),
92
94
  })),
93
95
  gateChecks: z.record(z.string(), z.array(GateCheckResultSchema)),
@@ -97,6 +99,12 @@ export const PipelineStateSchema = z.object({
97
99
  resolvedCommands: ResolvedCommandsSchema.optional(),
98
100
  /** Tracks which phase failed, for recovery routing */
99
101
  failedPhase: PipelinePhaseSchema.optional(),
102
+ /** Last rewind target from recovery — detects repeated same-target rewinds (v2.4.6) */
103
+ lastRewindTarget: PipelinePhaseSchema.optional(),
104
+ /** v2.6.0: Auto-recovery result — tracks whether arbitrator guidance was attempted before STUCK */
105
+ autoRecoveryResult: z.enum(['success', 'timeout', 'invalid', 'error']).optional(),
106
+ /** v2.7.0: Baseline failure count before recovery — for regression detection */
107
+ recoveryBaselineFailedCheckCount: z.number().int().min(0).optional(),
100
108
  /** Session guidance: user steering, upgrade context, or resume instructions */
101
109
  sessionGuidance: z.string().optional(),
102
110
  /** Pending change requests that force re-routing to consensus phases (v1.1) */
@@ -104,7 +112,23 @@ export const PipelineStateSchema = z.object({
104
112
  cr_id: z.string(),
105
113
  change_type: z.enum(['scope', 'architecture', 'dependency', 'config', 'requirement']),
106
114
  target_phase: PipelinePhaseSchema,
107
- status: z.enum(['proposed', 'approved', 'rejected']),
115
+ status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
116
+ drift_key: z.string().optional(),
117
+ })).optional(),
118
+ /** ID of the CR currently being processed by the routed phase (v2.4.9) */
119
+ activeChangeRequestId: z.string().optional(),
120
+ /** Snapshot override: after config CR resolved, REVIEW uses this instead of CONSENSUS_ROLE_PLANS baseline (v2.4.9) */
121
+ baselineSnapshotOverride: ArtifactRefSchema.optional(),
122
+ /** Rolling loop signatures for stagnation detection (v2.4.9) */
123
+ lastSignatures: z.array(z.string()).optional(),
124
+ /** Skill usage events for coverage enforcement (v2.2.1) */
125
+ skillUsageEvents: z.array(z.object({
126
+ role: PipelineRoleSchema,
127
+ phase: PipelinePhaseSchema,
128
+ used_as: z.enum(['system_prompt', 'review_prompt', 'arbitration_prompt', 'role_context', 'planning_prompt', 'other']),
129
+ skill_source: z.enum(['project_override', 'defaults']),
130
+ skill_version: z.string().optional(),
131
+ timestamp: z.string(),
108
132
  })).optional(),
109
133
  });
110
134
  export type PipelineState = z.infer<typeof PipelineStateSchema>;
@@ -163,5 +187,6 @@ export function createDefaultPipelineState(): PipelineState {
163
187
  gateChecks: {},
164
188
  activeRoles: [],
165
189
  constitutionHash: '',
190
+ skillUsageEvents: [],
166
191
  };
167
192
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Text utilities for normalizing LLM-generated issue lists.
3
+ * Detects false-positive "no issues" responses that LLMs produce in
4
+ * BLOCKING_ISSUES fields (e.g. "No blocking issues found", "N/A", "None identified").
5
+ */
6
+
7
+ /** Exact-match tokens that mean "nothing" after trim+lowercase */
8
+ const NONE_EXACT = new Set(['none', 'n/a', 'na', 'nil', 'nothing']);
9
+
10
+ /**
11
+ * Anchored phrase patterns that indicate "no issues".
12
+ * Tested against the cleaned text (bullet-stripped, trimmed, lowercased,
13
+ * trailing punctuation removed).
14
+ */
15
+ const NONE_PHRASES: RegExp[] = [
16
+ /^no\s+(?:(?:blocking|critical|significant|major)\s+)*(?:issues?|concerns?|problems?|blockers?|showstoppers?)\b/,
17
+ /^none\s+(?:identified|found|detected|noted|observed|reported|applicable|at this time)\b/,
18
+ /^there\s+are\s+no\s+(?:(?:significant|major|critical|blocking)\s+)*(?:issues|concerns|problems|blockers)\b/,
19
+ ];
20
+
21
+ /**
22
+ * Determine whether a text string is a "none-variant" — an LLM's way of
23
+ * saying "no blocking issues" rather than an actual issue description.
24
+ *
25
+ * @param text - Raw text from a blocking_issues list item
26
+ * @returns true if the text is a none-variant (should be filtered out)
27
+ */
28
+ export function isNoneVariant(text: string): boolean {
29
+ // Empty / whitespace-only
30
+ const trimmed = text.trim();
31
+ if (trimmed.length === 0) return true;
32
+
33
+ // Strip leading bullet prefixes: "- ", "* ", "+ ", "1) ", "1. ", etc.
34
+ const stripped = trimmed.replace(/^[-*+\d.)\s]+/, '').trim();
35
+ if (stripped.length === 0) return true;
36
+
37
+ // Strip trailing punctuation
38
+ const cleaned = stripped.replace(/[.,;!]+$/, '').trim();
39
+ if (cleaned.length === 0) return true;
40
+
41
+ const lower = cleaned.toLowerCase();
42
+
43
+ // Exact match check
44
+ if (NONE_EXACT.has(lower)) return true;
45
+
46
+ // Anchored phrase check
47
+ for (const pattern of NONE_PHRASES) {
48
+ if (pattern.test(lower)) return true;
49
+ }
50
+
51
+ // Length guard: >80 chars and no phrase match means it is likely a real issue
52
+ if (cleaned.length > 80) return false;
53
+
54
+ return false;
55
+ }
56
+
57
+ /**
58
+ * Normalize an issue list by filtering out empty strings and none-variants.
59
+ * Intended as a drop-in replacement for the weak `.filter(i => i.toLowerCase() !== 'none')`
60
+ * used across adapters.
61
+ *
62
+ * @param items - Raw list items from parseList()
63
+ * @returns Filtered list with only genuine issues
64
+ */
65
+ export function normalizeIssueList(items: string[]): string[] {
66
+ return items
67
+ .map((i) => i.trim())
68
+ .filter((i) => i.length > 0)
69
+ .filter((i) => !isNoneVariant(i));
70
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Shared website strategy formatter.
3
+ * Formats a WebsiteStrategyDocument as structured text for prompt injection.
4
+ * Used by both workflow (plan-mode consensus) and pipeline (role planning/execution).
5
+ */
6
+
7
+ import type { WebsiteStrategyDocument } from '../types/website-strategy.js';
8
+
9
+ /**
10
+ * Format a WebsiteStrategyDocument as structured text for prompt injection.
11
+ *
12
+ * Args:
13
+ * strategy: The validated strategy document to format.
14
+ *
15
+ * Returns:
16
+ * string: Multi-line formatted text with ICP, Positioning, Messaging,
17
+ * SEO Keywords, Site Architecture, and Conversion Strategy sections.
18
+ */
19
+ export function formatWebsiteStrategy(strategy: WebsiteStrategyDocument): string {
20
+ const lines: string[] = [];
21
+
22
+ lines.push(`### Target Customer`);
23
+ lines.push(`- Persona: ${strategy.icp.primaryPersona}`);
24
+ lines.push(`- Pain points: ${strategy.icp.painPoints.join(', ')}`);
25
+ lines.push('');
26
+
27
+ lines.push(`### Positioning`);
28
+ lines.push(`- Category: ${strategy.positioning.category}`);
29
+ lines.push(`- Value proposition: ${strategy.positioning.valueProposition}`);
30
+ lines.push(`- Differentiators: ${strategy.positioning.differentiators.join(', ')}`);
31
+ lines.push('');
32
+
33
+ lines.push(`### Messaging`);
34
+ lines.push(`- Headline: ${strategy.messaging.headline}`);
35
+ lines.push(`- Subheadline: ${strategy.messaging.subheadline}`);
36
+ lines.push('');
37
+
38
+ lines.push(`### SEO Keywords`);
39
+ lines.push(`- Primary: ${strategy.seoStrategy.primaryKeywords.join(', ')}`);
40
+ lines.push(`- Secondary: ${strategy.seoStrategy.secondaryKeywords.join(', ')}`);
41
+ lines.push('');
42
+
43
+ lines.push(`### Site Architecture`);
44
+ for (const page of strategy.siteArchitecture.pages) {
45
+ lines.push(`- ${page.path} (${page.pageType}): ${page.purpose}`);
46
+ }
47
+ lines.push('');
48
+
49
+ lines.push(`### Conversion Strategy`);
50
+ lines.push(`- Primary CTA: "${strategy.conversionStrategy.primaryCta.text}" -> ${strategy.conversionStrategy.primaryCta.href}`);
51
+ lines.push(`- Secondary CTA: "${strategy.conversionStrategy.secondaryCta.text}" -> ${strategy.conversionStrategy.secondaryCta.href}`);
52
+ lines.push(`- Trust signals: ${strategy.conversionStrategy.trustSignals.join(', ')}`);
53
+ lines.push(`- Lead capture: ${strategy.conversionStrategy.leadCapture}`);
54
+
55
+ return lines.join('\n');
56
+ }
@@ -578,6 +578,10 @@ export interface ProjectProgressAnalysis {
578
578
  statusMismatch: boolean; // true if status='complete' but work is incomplete
579
579
  planMismatch: boolean; // true if plan file has more tasks than state
580
580
 
581
+ // Pipeline state (when applicable)
582
+ pipelinePhase?: string; // Current pipeline phase (e.g. 'DONE', 'STUCK', 'IMPLEMENTATION')
583
+ pipelineTerminal: boolean; // true if pipeline is in DONE or STUCK state
584
+
581
585
  // Milestone breakdown (from state)
582
586
  totalMilestones: number;
583
587
  completedMilestones: number;
@@ -780,14 +784,42 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
780
784
  : 0;
781
785
 
782
786
  // Determine if actually complete - must match plan if plan has more tasks
783
- const isActuallyComplete = totalMilestones > 0 &&
784
- completedMilestones === totalMilestones &&
785
- completedTasks === totalTasks &&
786
- !planMismatch; // Can't be complete if plan has more tasks
787
+ // v2.4.6: When no milestones/tasks exist in state but project was explicitly
788
+ // marked complete (status='complete' + phase='complete'), trust the status.
789
+ // completeProject() only sets both to 'complete' after build verification passes.
790
+ const noTrackingData = totalMilestones === 0 && totalTasks === 0;
791
+ const explicitlyCompleted = state.status === 'complete' && state.phase === 'complete';
792
+
793
+ // Pipeline state detection — pipeline truth overrides milestone truth.
794
+ // RECOVERY_LOOP is treated as stuck-like: the pipeline was interrupted mid-recovery
795
+ // and will just re-fail without new guidance.
796
+ const pipelinePhase = state.pipeline?.pipelinePhase;
797
+ const pipelineStuckLike = pipelinePhase === 'STUCK' || pipelinePhase === 'RECOVERY_LOOP';
798
+ const pipelineTerminal = pipelinePhase === 'DONE' || pipelineStuckLike;
799
+ const pipelineDone = pipelinePhase === 'DONE';
800
+
801
+ let isActuallyComplete: boolean;
802
+ if (noTrackingData && pipelinePhase) {
803
+ // Pipeline project: pipeline state is the source of truth.
804
+ // DONE = complete, STUCK = not complete.
805
+ // planMismatch is irrelevant — pipeline projects don't track milestones.
806
+ isActuallyComplete = pipelineDone;
807
+ } else if (noTrackingData) {
808
+ // Legacy project with no tracking: trust explicit completion only
809
+ isActuallyComplete = explicitlyCompleted && !planMismatch;
810
+ } else {
811
+ // Standard milestone-based: all milestones + tasks must be complete
812
+ isActuallyComplete = totalMilestones > 0 &&
813
+ completedMilestones === totalMilestones &&
814
+ completedTasks === totalTasks &&
815
+ !planMismatch; // Can't be complete if plan has more tasks
816
+ }
787
817
 
788
- // Check for status mismatch
818
+ // Check for status mismatch.
819
+ // Pipeline terminal states are handled separately — not "mismatches".
789
820
  const statusMismatch = (state.status === 'complete' || state.phase === 'complete') &&
790
- (!isActuallyComplete || planMismatch);
821
+ (!isActuallyComplete || planMismatch) &&
822
+ !pipelineTerminal;
791
823
 
792
824
  // Find next items to work on
793
825
  let nextMilestone: { id: string; name: string } | undefined;
@@ -836,6 +868,15 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
836
868
  if (planMismatch) {
837
869
  progressSummary = `PLAN MISMATCH: State has ${completedTasks}/${totalTasks} tasks but plan has ${planTaskCount} tasks. ` +
838
870
  `Only ${percentComplete}% of plan completed.`;
871
+ } else if (isActuallyComplete && pipelineDone && noTrackingData) {
872
+ progressSummary = 'Project completed via pipeline';
873
+ } else if (isActuallyComplete && noTrackingData) {
874
+ progressSummary = 'Project completed (no milestone tracking data)';
875
+ } else if (pipelineStuckLike) {
876
+ const failedAt = state.pipeline?.failedPhase ?? 'unknown';
877
+ const recoveryCount = state.pipeline?.recoveryCount ?? 0;
878
+ const label = pipelinePhase === 'RECOVERY_LOOP' ? 'PIPELINE STUCK (in recovery)' : 'PIPELINE STUCK';
879
+ progressSummary = `${label}: Failed at ${failedAt} after ${recoveryCount} recovery attempts`;
839
880
  } else if (isActuallyComplete) {
840
881
  progressSummary = `All ${totalTasks} tasks complete across ${totalMilestones} milestones`;
841
882
  } else if (statusMismatch) {
@@ -848,6 +889,8 @@ export async function analyzeProjectProgress(projectDir: string): Promise<Projec
848
889
  isActuallyComplete,
849
890
  statusMismatch,
850
891
  planMismatch,
892
+ pipelinePhase,
893
+ pipelineTerminal,
851
894
  totalMilestones,
852
895
  completedMilestones,
853
896
  inProgressMilestones,
@@ -927,6 +970,15 @@ export async function verifyProjectCompletion(projectDir: string): Promise<{
927
970
  * @returns Updated state
928
971
  */
929
972
  export async function resetIncompleteProject(projectDir: string): Promise<ProjectState> {
973
+ const current = await loadProject(projectDir);
974
+
975
+ // Pipeline projects should not be reset via legacy path —
976
+ // legacy reset changes phase/status but not pipeline.pipelinePhase,
977
+ // creating inconsistent state. Pipeline resume handles its own recovery.
978
+ if (current.pipeline?.pipelinePhase) {
979
+ return current;
980
+ }
981
+
930
982
  const verification = await verifyProjectCompletion(projectDir);
931
983
 
932
984
  if (verification.isComplete) {
@@ -955,8 +1007,8 @@ export async function resetIncompleteProject(projectDir: string): Promise<Projec
955
1007
  }
956
1008
 
957
1009
  // Reset any failed tasks to pending for retry
958
- const current = await loadProject(projectDir);
959
- const updatedMilestones = current.milestones.map(m => ({
1010
+ const latestState = await loadProject(projectDir);
1011
+ const updatedMilestones = latestState.milestones.map(m => ({
960
1012
  ...m,
961
1013
  // Reset milestone status if it was incorrectly marked complete
962
1014
  status: m.tasks.every(t => t.status === 'complete')
@@ -35,6 +35,7 @@ export interface ConsensusResult {
35
35
  analysis: string;
36
36
  strengths: string[];
37
37
  concerns: string[];
38
+ blockingIssues: string[];
38
39
  recommendations: string[];
39
40
  approved: boolean;
40
41
  rawResponse: string;
@@ -11,6 +11,8 @@ import type { TestPlanOutput } from './tester.js';
11
11
  import { TestPlanOutputSchema, TestVerdictSchema } from './tester.js';
12
12
  import type { DbConfig } from './database.js';
13
13
  import { DbConfigSchema } from './database.js';
14
+ import { PipelineStateSchema } from '../pipeline/types.js';
15
+ import type { PipelineState } from '../pipeline/types.js';
14
16
 
15
17
  /**
16
18
  * Workflow phases
@@ -244,6 +246,8 @@ export interface ProjectState {
244
246
  auditLastRunAt?: string;
245
247
  /** Unique identifier for the audit run */
246
248
  auditRunId?: string;
249
+ /** Pipeline execution state — persisted for resume across sessions (v2.4.5) */
250
+ pipeline?: PipelineState;
247
251
  }
248
252
 
249
253
  /**
@@ -271,6 +275,7 @@ export const ProjectStateSchema = z.object({
271
275
  analysis: z.string(),
272
276
  strengths: z.array(z.string()),
273
277
  concerns: z.array(z.string()),
278
+ blockingIssues: z.array(z.string()).default([]),
274
279
  recommendations: z.array(z.string()),
275
280
  approved: z.boolean(),
276
281
  rawResponse: z.string(),
@@ -296,6 +301,7 @@ export const ProjectStateSchema = z.object({
296
301
  auditRecoveryInProgress: z.boolean().optional(),
297
302
  auditLastRunAt: z.string().optional(),
298
303
  auditRunId: z.string().optional(),
304
+ pipeline: PipelineStateSchema.optional(),
299
305
  });
300
306
 
301
307
  /**
@@ -21,7 +21,7 @@ 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';
24
+ import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext, extractDocPathsFromText } from '../generators/website-context.js';
25
25
  import type { WebsiteContentContext } from '../generators/website-context.js';
26
26
  import { resolveWorkspaceRoot } from '../generators/workspace-root.js';
27
27
  import { loadWebsiteStrategy } from '../workflow/website-strategy.js';
@@ -61,11 +61,17 @@ export async function buildUpgradeContentContext(
61
61
  projectName: string,
62
62
  ): Promise<{ context?: WebsiteContentContext; warning?: string }> {
63
63
  try {
64
+ // Load state once for idea text + brand context
65
+ const state = await loadState(projectDir);
66
+
64
67
  // Build context from user docs (scans projectDir + parent via getScanDirectories)
65
- const context = await buildWebsiteContext(projectDir, projectName);
68
+ // Extract extra doc paths from state idea text if available
69
+ const extraDocPaths = state?.idea ? extractDocPathsFromText(state.idea) : [];
70
+ const context = await buildWebsiteContext(
71
+ projectDir, projectName, state?.specification ?? state?.idea, extraDocPaths,
72
+ );
66
73
 
67
74
  // Apply brand context from state if available
68
- const state = await loadState(projectDir);
69
75
  if (state?.brandContext?.primaryColor) {
70
76
  context.brand = { ...context.brand, primaryColor: state.brandContext.primaryColor };
71
77
  }
@@ -1176,6 +1176,7 @@ export async function runOptimizedConsensusProcess(
1176
1176
  score: combinedScore,
1177
1177
  analysis: combinedAnalysis,
1178
1178
  concerns: allConcerns,
1179
+ blockingIssues: [],
1179
1180
  recommendations: allRecommendations,
1180
1181
  approved: combinedScore >= threshold,
1181
1182
  strengths: [],
@@ -13,6 +13,7 @@ import type {
13
13
  BrandAssetsContract,
14
14
  } from '../types/website-strategy.js';
15
15
  import { WebsiteStrategySchema } from '../types/website-strategy.js';
16
+ import { formatWebsiteStrategy } from '../shared/website-strategy-format.js';
16
17
  import { createClient } from '../adapters/openai.js';
17
18
 
18
19
  /** File name for persisted strategy */
@@ -187,42 +188,7 @@ Respond with ONLY valid JSON, no markdown code fences or explanation.`;
187
188
  export function formatStrategyForPlanContext(
188
189
  strategy: WebsiteStrategyDocument
189
190
  ): string {
190
- const lines: string[] = [];
191
-
192
- lines.push(`### Target Customer`);
193
- lines.push(`- Persona: ${strategy.icp.primaryPersona}`);
194
- lines.push(`- Pain points: ${strategy.icp.painPoints.join(', ')}`);
195
- lines.push('');
196
-
197
- lines.push(`### Positioning`);
198
- lines.push(`- Category: ${strategy.positioning.category}`);
199
- lines.push(`- Value proposition: ${strategy.positioning.valueProposition}`);
200
- lines.push(`- Differentiators: ${strategy.positioning.differentiators.join(', ')}`);
201
- lines.push('');
202
-
203
- lines.push(`### Messaging`);
204
- lines.push(`- Headline: ${strategy.messaging.headline}`);
205
- lines.push(`- Subheadline: ${strategy.messaging.subheadline}`);
206
- lines.push('');
207
-
208
- lines.push(`### SEO Keywords`);
209
- lines.push(`- Primary: ${strategy.seoStrategy.primaryKeywords.join(', ')}`);
210
- lines.push(`- Secondary: ${strategy.seoStrategy.secondaryKeywords.join(', ')}`);
211
- lines.push('');
212
-
213
- lines.push(`### Site Architecture`);
214
- for (const page of strategy.siteArchitecture.pages) {
215
- lines.push(`- ${page.path} (${page.pageType}): ${page.purpose}`);
216
- }
217
- lines.push('');
218
-
219
- lines.push(`### Conversion Strategy`);
220
- lines.push(`- Primary CTA: "${strategy.conversionStrategy.primaryCta.text}" -> ${strategy.conversionStrategy.primaryCta.href}`);
221
- lines.push(`- Secondary CTA: "${strategy.conversionStrategy.secondaryCta.text}" -> ${strategy.conversionStrategy.secondaryCta.href}`);
222
- lines.push(`- Trust signals: ${strategy.conversionStrategy.trustSignals.join(', ')}`);
223
- lines.push(`- Lead capture: ${strategy.conversionStrategy.leadCapture}`);
224
-
225
- return lines.join('\n');
191
+ return formatWebsiteStrategy(strategy);
226
192
  }
227
193
 
228
194
  /**
@@ -9,7 +9,7 @@ import path from 'node:path';
9
9
  import type { ProjectState } from '../types/workflow.js';
10
10
  import type { OutputLanguage } from '../types/project.js';
11
11
  import { isWorkspace } from '../types/project.js';
12
- import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext } from '../generators/website-context.js';
12
+ import { buildWebsiteContext, resolveBrandAssets, validateWebsiteContext, extractDocPathsFromText } from '../generators/website-context.js';
13
13
  import { resolveWorkspaceRoot } from '../generators/workspace-root.js';
14
14
  import { generateWebsiteLandingPage } from '../generators/templates/website-landing.js';
15
15
  import { generateWebsitePricingPage } from '../generators/templates/website-pricing.js';
@@ -52,10 +52,12 @@ export async function updateWebsiteContent(
52
52
 
53
53
  // Build content context from user docs and specification
54
54
  const parentDir = path.dirname(projectDir);
55
+ const extraDocPaths = state.idea ? extractDocPathsFromText(state.idea) : [];
55
56
  const context = await buildWebsiteContext(
56
57
  parentDir,
57
58
  state.name,
58
- state.specification
59
+ state.specification,
60
+ extraDocPaths,
59
61
  );
60
62
 
61
63
  // Apply brand context from state if available