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,385 @@
1
+ /**
2
+ * SmokeStack E2E Scaffold Test
3
+ *
4
+ * Verifies that generateAllProject() produces a correct "all" (monorepo) project.
5
+ * Uses TEST.md requirements as acceptance criteria.
6
+ * Scaffold generation is 100% template-based (no API keys needed).
7
+ */
8
+
9
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
10
+ import { generateAllProject, validateAllProject } from '../../src/generators/all.js';
11
+ import type { GenerationResult } from '../../src/generators/python.js';
12
+ import os from 'node:os';
13
+ import fs from 'node:fs/promises';
14
+ import path from 'node:path';
15
+ import { execSync } from 'node:child_process';
16
+
17
+ let tmpDir: string;
18
+ let result: GenerationResult;
19
+ let projectDir: string;
20
+
21
+ beforeAll(async () => {
22
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'popeye-smokestack-'));
23
+ const spec = {
24
+ idea: 'Build an "all" monorepo called Popeye SmokeStack with 4 parts: backend (FastAPI), frontend (React+Vite+Tailwind), website (static marketing), and database with migrations. DB: create a messages table with id, text, created_at.',
25
+ name: 'popeye-smokestack',
26
+ language: 'all' as const,
27
+ openaiModel: 'gpt-4.1',
28
+ };
29
+ result = await generateAllProject(spec, tmpDir);
30
+ projectDir = result.projectDir;
31
+ }, 30_000);
32
+
33
+ afterAll(async () => {
34
+ if (tmpDir) await fs.rm(tmpDir, { recursive: true, force: true });
35
+ });
36
+
37
+ // --- Helper ---
38
+
39
+ async function exists(relativePath: string): Promise<boolean> {
40
+ try {
41
+ await fs.access(path.join(projectDir, relativePath));
42
+ return true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ async function readText(relativePath: string): Promise<string> {
49
+ return fs.readFile(path.join(projectDir, relativePath), 'utf-8');
50
+ }
51
+
52
+ async function parseJson(relativePath: string): Promise<unknown> {
53
+ const text = await readText(relativePath);
54
+ return JSON.parse(text);
55
+ }
56
+
57
+ async function listDir(relativePath: string): Promise<string[]> {
58
+ const entries = await fs.readdir(path.join(projectDir, relativePath));
59
+ return entries;
60
+ }
61
+
62
+ async function findFiles(dir: string, ext: string): Promise<string[]> {
63
+ const results: string[] = [];
64
+ async function walk(current: string): Promise<void> {
65
+ const entries = await fs.readdir(current, { withFileTypes: true });
66
+ for (const entry of entries) {
67
+ const fullPath = path.join(current, entry.name);
68
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
69
+ await walk(fullPath);
70
+ } else if (entry.isFile() && entry.name.endsWith(ext)) {
71
+ results.push(fullPath);
72
+ }
73
+ }
74
+ }
75
+ await walk(path.join(projectDir, dir));
76
+ return results;
77
+ }
78
+
79
+ // =====================================================================
80
+ // 1. Generation succeeds
81
+ // =====================================================================
82
+ describe('1. Generation succeeds', () => {
83
+ it('result.success is true', () => {
84
+ expect(result.success).toBe(true);
85
+ });
86
+
87
+ it('created more than 50 files', () => {
88
+ expect(result.filesCreated.length).toBeGreaterThan(50);
89
+ });
90
+
91
+ it('projectDir contains "popeye-smokestack"', () => {
92
+ expect(result.projectDir).toContain('popeye-smokestack');
93
+ });
94
+ });
95
+
96
+ // =====================================================================
97
+ // 2. Required structure exists (from TEST.md)
98
+ // =====================================================================
99
+ describe('2. Required structure exists', () => {
100
+ it('apps/backend/ directory exists', async () => {
101
+ expect(await exists('apps/backend')).toBe(true);
102
+ });
103
+
104
+ it('apps/frontend/ directory exists', async () => {
105
+ expect(await exists('apps/frontend')).toBe(true);
106
+ });
107
+
108
+ it('apps/website/ directory exists', async () => {
109
+ expect(await exists('apps/website')).toBe(true);
110
+ });
111
+
112
+ it('docker-compose.yml exists', async () => {
113
+ expect(await exists('docker-compose.yml')).toBe(true);
114
+ });
115
+
116
+ it('README.md exists', async () => {
117
+ expect(await exists('README.md')).toBe(true);
118
+ });
119
+
120
+ it('.env.example or env template exists', async () => {
121
+ const backendEnv = await exists('apps/backend/.env.example');
122
+ const frontendEnv = await exists('apps/frontend/.env.example');
123
+ expect(backendEnv || frontendEnv).toBe(true);
124
+ });
125
+
126
+ it('packages/ directory exists', async () => {
127
+ expect(await exists('packages')).toBe(true);
128
+ });
129
+
130
+ it('.popeye/workspace.json exists', async () => {
131
+ expect(await exists('.popeye/workspace.json')).toBe(true);
132
+ });
133
+
134
+ it('Backend: pyproject.toml exists', async () => {
135
+ expect(await exists('apps/backend/pyproject.toml')).toBe(true);
136
+ });
137
+
138
+ it('Frontend: package.json exists', async () => {
139
+ expect(await exists('apps/frontend/package.json')).toBe(true);
140
+ });
141
+
142
+ it('Website: package.json exists', async () => {
143
+ expect(await exists('apps/website/package.json')).toBe(true);
144
+ });
145
+
146
+ it('DB: migration files exist', async () => {
147
+ const alembicDir = await exists('apps/backend/alembic.ini');
148
+ const migrationsDir = await exists('apps/backend/migrations');
149
+ expect(alembicDir || migrationsDir).toBe(true);
150
+ });
151
+
152
+ it('DB: models.py exists', async () => {
153
+ // models.py under apps/backend/src/popeye_smokestack/database/
154
+ const pyFiles = await findFiles('apps/backend', '.py');
155
+ const hasModels = pyFiles.some((f) => f.includes('models.py'));
156
+ expect(hasModels).toBe(true);
157
+ });
158
+ });
159
+
160
+ // =====================================================================
161
+ // 3. No unplanned app directories
162
+ // =====================================================================
163
+ describe('3. No unplanned app directories', () => {
164
+ it('apps/ contains ONLY backend, frontend, website', async () => {
165
+ const entries = await listDir('apps');
166
+ const allowed = new Set(['backend', 'frontend', 'website']);
167
+ const extra = entries.filter((e) => !allowed.has(e));
168
+ expect(extra).toEqual([]);
169
+ });
170
+ });
171
+
172
+ // =====================================================================
173
+ // 4. Content alignment with TEST.md
174
+ // =====================================================================
175
+ describe('4. Content alignment', () => {
176
+ it('Root package.json contains project name', async () => {
177
+ const pkg = (await parseJson('package.json')) as Record<string, unknown>;
178
+ expect(JSON.stringify(pkg)).toContain('popeye-smokestack');
179
+ });
180
+
181
+ it('Backend main.py imports FastAPI', async () => {
182
+ const pyFiles = await findFiles('apps/backend', '.py');
183
+ const mainFile = pyFiles.find((f) => f.endsWith('main.py'));
184
+ expect(mainFile).toBeDefined();
185
+ const content = await fs.readFile(mainFile!, 'utf-8');
186
+ expect(content).toContain('FastAPI');
187
+ });
188
+
189
+ it('Backend has CORS setup', async () => {
190
+ const pyFiles = await findFiles('apps/backend', '.py');
191
+ const mainFile = pyFiles.find((f) => f.endsWith('main.py'));
192
+ expect(mainFile).toBeDefined();
193
+ const content = await fs.readFile(mainFile!, 'utf-8');
194
+ expect(content.toLowerCase()).toMatch(/cors/i);
195
+ });
196
+
197
+ it('Backend has health endpoint pattern', async () => {
198
+ const pyFiles = await findFiles('apps/backend', '.py');
199
+ let foundHealth = false;
200
+ for (const f of pyFiles) {
201
+ const content = await fs.readFile(f, 'utf-8');
202
+ if (content.includes('health') || content.includes('/health')) {
203
+ foundHealth = true;
204
+ break;
205
+ }
206
+ }
207
+ expect(foundHealth).toBe(true);
208
+ });
209
+
210
+ it('Frontend package.json has react dependency', async () => {
211
+ const pkg = (await parseJson('apps/frontend/package.json')) as Record<string, unknown>;
212
+ const allDeps = JSON.stringify(pkg);
213
+ expect(allDeps).toContain('react');
214
+ });
215
+
216
+ it('Frontend has vite dependency or vite.config', async () => {
217
+ const pkg = (await parseJson('apps/frontend/package.json')) as Record<string, unknown>;
218
+ const hasViteDep = JSON.stringify(pkg).includes('vite');
219
+ const hasViteConfig = await exists('apps/frontend/vite.config.ts');
220
+ expect(hasViteDep || hasViteConfig).toBe(true);
221
+ });
222
+
223
+ it('Frontend has tailwindcss dependency or tailwind.config', async () => {
224
+ const pkg = (await parseJson('apps/frontend/package.json')) as Record<string, unknown>;
225
+ const hasTailwindDep = JSON.stringify(pkg).includes('tailwindcss');
226
+ const hasTailwindConfig =
227
+ (await exists('apps/frontend/tailwind.config.ts')) ||
228
+ (await exists('apps/frontend/tailwind.config.js'));
229
+ expect(hasTailwindDep || hasTailwindConfig).toBe(true);
230
+ });
231
+
232
+ it('Website package.json has next dependency', async () => {
233
+ const pkg = (await parseJson('apps/website/package.json')) as Record<string, unknown>;
234
+ const allDeps = JSON.stringify(pkg);
235
+ expect(allDeps).toContain('next');
236
+ });
237
+
238
+ it('Docker compose has postgres service', async () => {
239
+ const content = await readText('docker-compose.yml');
240
+ expect(content.toLowerCase()).toContain('postgres');
241
+ });
242
+
243
+ it('Env template has DATABASE_URL', async () => {
244
+ const content = await readText('apps/backend/.env.example');
245
+ expect(content).toContain('DATABASE_URL');
246
+ });
247
+ });
248
+
249
+ // =====================================================================
250
+ // 5. Syntax validation (fast, no install)
251
+ // =====================================================================
252
+ describe('5. Syntax validation', () => {
253
+ it('All package.json files are valid JSON', async () => {
254
+ const pkgFiles = [
255
+ 'package.json',
256
+ 'apps/frontend/package.json',
257
+ 'apps/website/package.json',
258
+ 'packages/design-tokens/package.json',
259
+ 'packages/ui/package.json',
260
+ ];
261
+ for (const f of pkgFiles) {
262
+ const text = await readText(f);
263
+ expect(() => JSON.parse(text), `Invalid JSON in ${f}`).not.toThrow();
264
+ }
265
+ });
266
+
267
+ it('All tsconfig.json files are valid JSON', async () => {
268
+ const tsconfigPaths = [
269
+ 'apps/frontend/tsconfig.json',
270
+ 'apps/website/tsconfig.json',
271
+ ];
272
+ for (const f of tsconfigPaths) {
273
+ if (await exists(f)) {
274
+ const text = await readText(f);
275
+ expect(() => JSON.parse(text), `Invalid JSON in ${f}`).not.toThrow();
276
+ }
277
+ }
278
+ });
279
+
280
+ it('docker-compose.yml has valid basic YAML structure', async () => {
281
+ const content = await readText('docker-compose.yml');
282
+ // Basic YAML structure check: must have services key
283
+ expect(content).toMatch(/^services:/m);
284
+ });
285
+
286
+ it('.popeye/workspace.json is valid JSON', async () => {
287
+ const text = await readText('.popeye/workspace.json');
288
+ expect(() => JSON.parse(text)).not.toThrow();
289
+ });
290
+ });
291
+
292
+ // =====================================================================
293
+ // 6. Build / Compilation verification
294
+ // =====================================================================
295
+ describe('6. Build verification', () => {
296
+ describe('Backend (Python syntax)', () => {
297
+ it('All .py files compile syntactically', async () => {
298
+ const pyFiles = await findFiles('apps/backend', '.py');
299
+ expect(pyFiles.length).toBeGreaterThan(0);
300
+
301
+ const failures: string[] = [];
302
+ for (const f of pyFiles) {
303
+ try {
304
+ execSync(
305
+ `python3 -c "compile(open('${f}').read(), '${f}', 'exec')"`,
306
+ { timeout: 10_000, stdio: 'pipe' }
307
+ );
308
+ } catch (e: unknown) {
309
+ const msg = e instanceof Error ? e.message : String(e);
310
+ failures.push(`${path.relative(projectDir, f)}: ${msg.split('\n')[0]}`);
311
+ }
312
+ }
313
+ expect(failures, `Python syntax errors:\n${failures.join('\n')}`).toEqual([]);
314
+ });
315
+ });
316
+
317
+ // Workspace install: run npm install from root so all workspace packages resolve together
318
+ describe('Workspace npm install', () => {
319
+ it('npm install from root succeeds', async () => {
320
+ execSync('npm install --ignore-scripts', {
321
+ cwd: projectDir,
322
+ timeout: 180_000,
323
+ stdio: 'pipe',
324
+ });
325
+ const feModules = await exists('apps/frontend/node_modules');
326
+ const wsModules = await exists('apps/website/node_modules');
327
+ // npm workspaces hoists to root; app dirs may or may not have node_modules
328
+ const rootModules = await exists('node_modules');
329
+ expect(rootModules || feModules || wsModules).toBe(true);
330
+ }, 200_000);
331
+ });
332
+
333
+ describe('Frontend (React+Vite+Tailwind)', () => {
334
+ it('TypeScript compiles (tsc --noEmit)', async () => {
335
+ const frontendDir = path.join(projectDir, 'apps', 'frontend');
336
+ try {
337
+ execSync('npx tsc --noEmit', {
338
+ cwd: frontendDir,
339
+ timeout: 60_000,
340
+ stdio: 'pipe',
341
+ });
342
+ } catch (e: unknown) {
343
+ const stderr =
344
+ e && typeof e === 'object' && 'stderr' in e ? String((e as { stderr: unknown }).stderr) : '';
345
+ const stdout =
346
+ e && typeof e === 'object' && 'stdout' in e ? String((e as { stdout: unknown }).stdout) : '';
347
+ throw new Error(`Frontend tsc failed:\n${stdout}\n${stderr}`);
348
+ }
349
+ }, 70_000);
350
+ });
351
+
352
+ describe('Website (Next.js)', () => {
353
+ it('TypeScript compiles (tsc --noEmit)', async () => {
354
+ const websiteDir = path.join(projectDir, 'apps', 'website');
355
+ try {
356
+ execSync('npx tsc --noEmit', {
357
+ cwd: websiteDir,
358
+ timeout: 60_000,
359
+ stdio: 'pipe',
360
+ });
361
+ } catch (e: unknown) {
362
+ const stderr =
363
+ e && typeof e === 'object' && 'stderr' in e ? String((e as { stderr: unknown }).stderr) : '';
364
+ const stdout =
365
+ e && typeof e === 'object' && 'stdout' in e ? String((e as { stdout: unknown }).stdout) : '';
366
+ throw new Error(`Website tsc failed:\n${stdout}\n${stderr}`);
367
+ }
368
+ }, 70_000);
369
+ });
370
+ });
371
+
372
+ // =====================================================================
373
+ // 7. Existing validateAllProject() (defense-in-depth)
374
+ // =====================================================================
375
+ describe('7. validateAllProject()', () => {
376
+ it('validation passes', async () => {
377
+ const validation = await validateAllProject(projectDir);
378
+ expect(validation.valid).toBe(true);
379
+ });
380
+
381
+ it('missingFiles is empty', async () => {
382
+ const validation = await validateAllProject(projectDir);
383
+ expect(validation.missingFiles).toEqual([]);
384
+ });
385
+ });