popeye-cli 1.9.5 → 2.0.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 (318) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/CONTRIBUTING.md +15 -1
  3. package/README.md +57 -0
  4. package/cheatsheet.md +65 -0
  5. package/dist/cli/commands/debug-context.d.ts +64 -0
  6. package/dist/cli/commands/debug-context.d.ts.map +1 -0
  7. package/dist/cli/commands/debug-context.js +221 -0
  8. package/dist/cli/commands/debug-context.js.map +1 -0
  9. package/dist/cli/commands/debug-prompts.d.ts +25 -0
  10. package/dist/cli/commands/debug-prompts.d.ts.map +1 -0
  11. package/dist/cli/commands/debug-prompts.js +80 -0
  12. package/dist/cli/commands/debug-prompts.js.map +1 -0
  13. package/dist/cli/commands/debug.d.ts +68 -0
  14. package/dist/cli/commands/debug.d.ts.map +1 -0
  15. package/dist/cli/commands/debug.js +543 -0
  16. package/dist/cli/commands/debug.js.map +1 -0
  17. package/dist/cli/commands/index.d.ts +1 -0
  18. package/dist/cli/commands/index.d.ts.map +1 -1
  19. package/dist/cli/commands/index.js +1 -0
  20. package/dist/cli/commands/index.js.map +1 -1
  21. package/dist/cli/index.d.ts.map +1 -1
  22. package/dist/cli/index.js +2 -1
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/interactive.d.ts.map +1 -1
  25. package/dist/cli/interactive.js +25 -0
  26. package/dist/cli/interactive.js.map +1 -1
  27. package/dist/generators/all.d.ts.map +1 -1
  28. package/dist/generators/all.js +2 -0
  29. package/dist/generators/all.js.map +1 -1
  30. package/dist/generators/templates/database-docker.d.ts.map +1 -1
  31. package/dist/generators/templates/database-docker.js +10 -0
  32. package/dist/generators/templates/database-docker.js.map +1 -1
  33. package/dist/generators/templates/fullstack.d.ts +4 -1
  34. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  35. package/dist/generators/templates/fullstack.js +6 -2
  36. package/dist/generators/templates/fullstack.js.map +1 -1
  37. package/dist/pipeline/artifact-manager.d.ts +47 -0
  38. package/dist/pipeline/artifact-manager.d.ts.map +1 -0
  39. package/dist/pipeline/artifact-manager.js +251 -0
  40. package/dist/pipeline/artifact-manager.js.map +1 -0
  41. package/dist/pipeline/artifact-validators.d.ts +29 -0
  42. package/dist/pipeline/artifact-validators.d.ts.map +1 -0
  43. package/dist/pipeline/artifact-validators.js +173 -0
  44. package/dist/pipeline/artifact-validators.js.map +1 -0
  45. package/dist/pipeline/change-request.d.ts +47 -0
  46. package/dist/pipeline/change-request.d.ts.map +1 -0
  47. package/dist/pipeline/change-request.js +91 -0
  48. package/dist/pipeline/change-request.js.map +1 -0
  49. package/dist/pipeline/check-runner.d.ts +47 -0
  50. package/dist/pipeline/check-runner.d.ts.map +1 -0
  51. package/dist/pipeline/check-runner.js +417 -0
  52. package/dist/pipeline/check-runner.js.map +1 -0
  53. package/dist/pipeline/command-resolver.d.ts +9 -0
  54. package/dist/pipeline/command-resolver.d.ts.map +1 -0
  55. package/dist/pipeline/command-resolver.js +140 -0
  56. package/dist/pipeline/command-resolver.js.map +1 -0
  57. package/dist/pipeline/consensus/consensus-runner.d.ts +44 -0
  58. package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -0
  59. package/dist/pipeline/consensus/consensus-runner.js +212 -0
  60. package/dist/pipeline/consensus/consensus-runner.js.map +1 -0
  61. package/dist/pipeline/constitution.d.ts +45 -0
  62. package/dist/pipeline/constitution.d.ts.map +1 -0
  63. package/dist/pipeline/constitution.js +82 -0
  64. package/dist/pipeline/constitution.js.map +1 -0
  65. package/dist/pipeline/gate-engine.d.ts +55 -0
  66. package/dist/pipeline/gate-engine.d.ts.map +1 -0
  67. package/dist/pipeline/gate-engine.js +270 -0
  68. package/dist/pipeline/gate-engine.js.map +1 -0
  69. package/dist/pipeline/index.d.ts +26 -0
  70. package/dist/pipeline/index.d.ts.map +1 -0
  71. package/dist/pipeline/index.js +35 -0
  72. package/dist/pipeline/index.js.map +1 -0
  73. package/dist/pipeline/migration.d.ts +15 -0
  74. package/dist/pipeline/migration.d.ts.map +1 -0
  75. package/dist/pipeline/migration.js +76 -0
  76. package/dist/pipeline/migration.js.map +1 -0
  77. package/dist/pipeline/orchestrator.d.ts +28 -0
  78. package/dist/pipeline/orchestrator.d.ts.map +1 -0
  79. package/dist/pipeline/orchestrator.js +238 -0
  80. package/dist/pipeline/orchestrator.js.map +1 -0
  81. package/dist/pipeline/packets/audit-report-builder.d.ts +11 -0
  82. package/dist/pipeline/packets/audit-report-builder.d.ts.map +1 -0
  83. package/dist/pipeline/packets/audit-report-builder.js +32 -0
  84. package/dist/pipeline/packets/audit-report-builder.js.map +1 -0
  85. package/dist/pipeline/packets/consensus-packet-builder.d.ts +35 -0
  86. package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -0
  87. package/dist/pipeline/packets/consensus-packet-builder.js +80 -0
  88. package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -0
  89. package/dist/pipeline/packets/index.d.ts +12 -0
  90. package/dist/pipeline/packets/index.d.ts.map +1 -0
  91. package/dist/pipeline/packets/index.js +8 -0
  92. package/dist/pipeline/packets/index.js.map +1 -0
  93. package/dist/pipeline/packets/plan-packet-builder.d.ts +21 -0
  94. package/dist/pipeline/packets/plan-packet-builder.d.ts.map +1 -0
  95. package/dist/pipeline/packets/plan-packet-builder.js +27 -0
  96. package/dist/pipeline/packets/plan-packet-builder.js.map +1 -0
  97. package/dist/pipeline/packets/rca-packet-builder.d.ts +19 -0
  98. package/dist/pipeline/packets/rca-packet-builder.d.ts.map +1 -0
  99. package/dist/pipeline/packets/rca-packet-builder.js +22 -0
  100. package/dist/pipeline/packets/rca-packet-builder.js.map +1 -0
  101. package/dist/pipeline/phases/architecture.d.ts +7 -0
  102. package/dist/pipeline/phases/architecture.d.ts.map +1 -0
  103. package/dist/pipeline/phases/architecture.js +60 -0
  104. package/dist/pipeline/phases/architecture.js.map +1 -0
  105. package/dist/pipeline/phases/audit.d.ts +8 -0
  106. package/dist/pipeline/phases/audit.d.ts.map +1 -0
  107. package/dist/pipeline/phases/audit.js +144 -0
  108. package/dist/pipeline/phases/audit.js.map +1 -0
  109. package/dist/pipeline/phases/consensus-architecture.d.ts +7 -0
  110. package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -0
  111. package/dist/pipeline/phases/consensus-architecture.js +84 -0
  112. package/dist/pipeline/phases/consensus-architecture.js.map +1 -0
  113. package/dist/pipeline/phases/consensus-master-plan.d.ts +7 -0
  114. package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -0
  115. package/dist/pipeline/phases/consensus-master-plan.js +81 -0
  116. package/dist/pipeline/phases/consensus-master-plan.js.map +1 -0
  117. package/dist/pipeline/phases/consensus-role-plans.d.ts +7 -0
  118. package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -0
  119. package/dist/pipeline/phases/consensus-role-plans.js +85 -0
  120. package/dist/pipeline/phases/consensus-role-plans.js.map +1 -0
  121. package/dist/pipeline/phases/done.d.ts +7 -0
  122. package/dist/pipeline/phases/done.d.ts.map +1 -0
  123. package/dist/pipeline/phases/done.js +45 -0
  124. package/dist/pipeline/phases/done.js.map +1 -0
  125. package/dist/pipeline/phases/implementation.d.ts +8 -0
  126. package/dist/pipeline/phases/implementation.d.ts.map +1 -0
  127. package/dist/pipeline/phases/implementation.js +42 -0
  128. package/dist/pipeline/phases/implementation.js.map +1 -0
  129. package/dist/pipeline/phases/index.d.ts +20 -0
  130. package/dist/pipeline/phases/index.d.ts.map +1 -0
  131. package/dist/pipeline/phases/index.js +19 -0
  132. package/dist/pipeline/phases/index.js.map +1 -0
  133. package/dist/pipeline/phases/intake.d.ts +8 -0
  134. package/dist/pipeline/phases/intake.d.ts.map +1 -0
  135. package/dist/pipeline/phases/intake.js +40 -0
  136. package/dist/pipeline/phases/intake.js.map +1 -0
  137. package/dist/pipeline/phases/phase-context.d.ts +30 -0
  138. package/dist/pipeline/phases/phase-context.d.ts.map +1 -0
  139. package/dist/pipeline/phases/phase-context.js +33 -0
  140. package/dist/pipeline/phases/phase-context.js.map +1 -0
  141. package/dist/pipeline/phases/production-gate.d.ts +8 -0
  142. package/dist/pipeline/phases/production-gate.d.ts.map +1 -0
  143. package/dist/pipeline/phases/production-gate.js +84 -0
  144. package/dist/pipeline/phases/production-gate.js.map +1 -0
  145. package/dist/pipeline/phases/qa-validation.d.ts +7 -0
  146. package/dist/pipeline/phases/qa-validation.d.ts.map +1 -0
  147. package/dist/pipeline/phases/qa-validation.js +50 -0
  148. package/dist/pipeline/phases/qa-validation.js.map +1 -0
  149. package/dist/pipeline/phases/recovery-loop.d.ts +7 -0
  150. package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -0
  151. package/dist/pipeline/phases/recovery-loop.js +91 -0
  152. package/dist/pipeline/phases/recovery-loop.js.map +1 -0
  153. package/dist/pipeline/phases/review.d.ts +8 -0
  154. package/dist/pipeline/phases/review.d.ts.map +1 -0
  155. package/dist/pipeline/phases/review.js +127 -0
  156. package/dist/pipeline/phases/review.js.map +1 -0
  157. package/dist/pipeline/phases/role-planning.d.ts +7 -0
  158. package/dist/pipeline/phases/role-planning.d.ts.map +1 -0
  159. package/dist/pipeline/phases/role-planning.js +75 -0
  160. package/dist/pipeline/phases/role-planning.js.map +1 -0
  161. package/dist/pipeline/phases/stuck.d.ts +7 -0
  162. package/dist/pipeline/phases/stuck.d.ts.map +1 -0
  163. package/dist/pipeline/phases/stuck.js +51 -0
  164. package/dist/pipeline/phases/stuck.js.map +1 -0
  165. package/dist/pipeline/repo-snapshot.d.ts +24 -0
  166. package/dist/pipeline/repo-snapshot.d.ts.map +1 -0
  167. package/dist/pipeline/repo-snapshot.js +343 -0
  168. package/dist/pipeline/repo-snapshot.js.map +1 -0
  169. package/dist/pipeline/role-execution-adapter.d.ts +59 -0
  170. package/dist/pipeline/role-execution-adapter.d.ts.map +1 -0
  171. package/dist/pipeline/role-execution-adapter.js +159 -0
  172. package/dist/pipeline/role-execution-adapter.js.map +1 -0
  173. package/dist/pipeline/skill-loader.d.ts +34 -0
  174. package/dist/pipeline/skill-loader.d.ts.map +1 -0
  175. package/dist/pipeline/skill-loader.js +156 -0
  176. package/dist/pipeline/skill-loader.js.map +1 -0
  177. package/dist/pipeline/skills/defaults.d.ts +16 -0
  178. package/dist/pipeline/skills/defaults.d.ts.map +1 -0
  179. package/dist/pipeline/skills/defaults.js +189 -0
  180. package/dist/pipeline/skills/defaults.js.map +1 -0
  181. package/dist/pipeline/type-defs/artifacts.d.ts +202 -0
  182. package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -0
  183. package/dist/pipeline/type-defs/artifacts.js +66 -0
  184. package/dist/pipeline/type-defs/artifacts.js.map +1 -0
  185. package/dist/pipeline/type-defs/audit.d.ts +256 -0
  186. package/dist/pipeline/type-defs/audit.d.ts.map +1 -0
  187. package/dist/pipeline/type-defs/audit.js +54 -0
  188. package/dist/pipeline/type-defs/audit.js.map +1 -0
  189. package/dist/pipeline/type-defs/checks.d.ts +81 -0
  190. package/dist/pipeline/type-defs/checks.d.ts.map +1 -0
  191. package/dist/pipeline/type-defs/checks.js +38 -0
  192. package/dist/pipeline/type-defs/checks.js.map +1 -0
  193. package/dist/pipeline/type-defs/enums.d.ts +43 -0
  194. package/dist/pipeline/type-defs/enums.d.ts.map +1 -0
  195. package/dist/pipeline/type-defs/enums.js +55 -0
  196. package/dist/pipeline/type-defs/enums.js.map +1 -0
  197. package/dist/pipeline/type-defs/index.d.ts +12 -0
  198. package/dist/pipeline/type-defs/index.d.ts.map +1 -0
  199. package/dist/pipeline/type-defs/index.js +12 -0
  200. package/dist/pipeline/type-defs/index.js.map +1 -0
  201. package/dist/pipeline/type-defs/packets.d.ts +806 -0
  202. package/dist/pipeline/type-defs/packets.d.ts.map +1 -0
  203. package/dist/pipeline/type-defs/packets.js +109 -0
  204. package/dist/pipeline/type-defs/packets.js.map +1 -0
  205. package/dist/pipeline/type-defs/snapshot.d.ts +52 -0
  206. package/dist/pipeline/type-defs/snapshot.d.ts.map +1 -0
  207. package/dist/pipeline/type-defs/snapshot.js +35 -0
  208. package/dist/pipeline/type-defs/snapshot.js.map +1 -0
  209. package/dist/pipeline/type-defs/state.d.ts +449 -0
  210. package/dist/pipeline/type-defs/state.d.ts.map +1 -0
  211. package/dist/pipeline/type-defs/state.js +88 -0
  212. package/dist/pipeline/type-defs/state.js.map +1 -0
  213. package/dist/pipeline/types.d.ts +16 -0
  214. package/dist/pipeline/types.d.ts.map +1 -0
  215. package/dist/pipeline/types.js +16 -0
  216. package/dist/pipeline/types.js.map +1 -0
  217. package/dist/types/audit.d.ts +6 -6
  218. package/dist/workflow/index.d.ts.map +1 -1
  219. package/dist/workflow/index.js +48 -0
  220. package/dist/workflow/index.js.map +1 -1
  221. package/package.json +1 -1
  222. package/skills/ARBITRATOR.md +137 -0
  223. package/skills/ARCHITECT.md +167 -0
  224. package/skills/AUDITOR.md +128 -0
  225. package/skills/AUDIT_REPORT_SCHEMA.md +20 -0
  226. package/skills/BACKEND_PROGRAMMER.md +95 -0
  227. package/skills/CONSENSUS_PACKET_SCHEMA.md +166 -0
  228. package/skills/DB_EXPERT.md +106 -0
  229. package/skills/DEBUGGER.md +286 -0
  230. package/skills/DISPATCHER.md +157 -0
  231. package/skills/FRONTEND_PROGRAMMER.md +84 -0
  232. package/skills/JOURNALIST.md +247 -0
  233. package/skills/MARKETING_EXPERT.md +23 -0
  234. package/skills/PHASE_GATE_ENGINE_SPEC.md +171 -0
  235. package/skills/PLAN_PACKET_SCHEMA.md +222 -0
  236. package/skills/POPEYE_CONSTITUTION.md +177 -0
  237. package/skills/POPEYE_FULL_AUTONOMY_PIPELINE.md +537 -0
  238. package/skills/PRODUCTION_READINESS_SCHEMA.md +19 -0
  239. package/skills/QA_TESTER.md +40 -0
  240. package/skills/RCA_PACKET_SCHEMA.md +22 -0
  241. package/skills/RELEASE_MANAGER.md +60 -0
  242. package/skills/REVIEWER.md +133 -0
  243. package/skills/SOCIAL_EXPERT.md +22 -0
  244. package/skills/UI_UX_SPECIALIST.md +22 -0
  245. package/skills/WEBSITE_PROGRAMMER.md +37 -0
  246. package/src/cli/commands/debug-context.ts +265 -0
  247. package/src/cli/commands/debug-prompts.ts +91 -0
  248. package/src/cli/commands/debug.ts +662 -0
  249. package/src/cli/commands/index.ts +1 -0
  250. package/src/cli/index.ts +2 -0
  251. package/src/cli/interactive.ts +27 -0
  252. package/src/generators/all.ts +2 -0
  253. package/src/generators/templates/database-docker.ts +10 -0
  254. package/src/generators/templates/fullstack.ts +6 -2
  255. package/src/pipeline/artifact-manager.ts +339 -0
  256. package/src/pipeline/artifact-validators.ts +224 -0
  257. package/src/pipeline/change-request.ts +119 -0
  258. package/src/pipeline/check-runner.ts +504 -0
  259. package/src/pipeline/command-resolver.ts +168 -0
  260. package/src/pipeline/consensus/consensus-runner.ts +317 -0
  261. package/src/pipeline/constitution.ts +109 -0
  262. package/src/pipeline/gate-engine.ts +347 -0
  263. package/src/pipeline/index.ts +82 -0
  264. package/src/pipeline/migration.ts +91 -0
  265. package/src/pipeline/orchestrator.ts +314 -0
  266. package/src/pipeline/packets/audit-report-builder.ts +47 -0
  267. package/src/pipeline/packets/consensus-packet-builder.ts +112 -0
  268. package/src/pipeline/packets/index.ts +15 -0
  269. package/src/pipeline/packets/plan-packet-builder.ts +52 -0
  270. package/src/pipeline/packets/rca-packet-builder.ts +38 -0
  271. package/src/pipeline/phases/architecture.ts +73 -0
  272. package/src/pipeline/phases/audit.ts +193 -0
  273. package/src/pipeline/phases/consensus-architecture.ts +104 -0
  274. package/src/pipeline/phases/consensus-master-plan.ts +100 -0
  275. package/src/pipeline/phases/consensus-role-plans.ts +105 -0
  276. package/src/pipeline/phases/done.ts +68 -0
  277. package/src/pipeline/phases/implementation.ts +48 -0
  278. package/src/pipeline/phases/index.ts +21 -0
  279. package/src/pipeline/phases/intake.ts +54 -0
  280. package/src/pipeline/phases/phase-context.ts +86 -0
  281. package/src/pipeline/phases/production-gate.ts +113 -0
  282. package/src/pipeline/phases/qa-validation.ts +63 -0
  283. package/src/pipeline/phases/recovery-loop.ts +118 -0
  284. package/src/pipeline/phases/review.ts +149 -0
  285. package/src/pipeline/phases/role-planning.ts +92 -0
  286. package/src/pipeline/phases/stuck.ts +62 -0
  287. package/src/pipeline/repo-snapshot.ts +395 -0
  288. package/src/pipeline/role-execution-adapter.ts +238 -0
  289. package/src/pipeline/skill-loader.ts +192 -0
  290. package/src/pipeline/skills/defaults.ts +215 -0
  291. package/src/pipeline/type-defs/artifacts.ts +81 -0
  292. package/src/pipeline/type-defs/audit.ts +67 -0
  293. package/src/pipeline/type-defs/checks.ts +47 -0
  294. package/src/pipeline/type-defs/enums.ts +62 -0
  295. package/src/pipeline/type-defs/index.ts +12 -0
  296. package/src/pipeline/type-defs/packets.ts +131 -0
  297. package/src/pipeline/type-defs/snapshot.ts +55 -0
  298. package/src/pipeline/type-defs/state.ts +165 -0
  299. package/src/pipeline/types.ts +16 -0
  300. package/src/workflow/index.ts +48 -0
  301. package/tests/cli/commands/debug.test.ts +376 -0
  302. package/tests/pipeline/artifact-manager.test.ts +183 -0
  303. package/tests/pipeline/artifact-validators.test.ts +207 -0
  304. package/tests/pipeline/change-request.test.ts +180 -0
  305. package/tests/pipeline/check-runner.test.ts +157 -0
  306. package/tests/pipeline/command-resolver.test.ts +159 -0
  307. package/tests/pipeline/consensus-runner.test.ts +206 -0
  308. package/tests/pipeline/consensus-scoring.test.ts +163 -0
  309. package/tests/pipeline/constitution.test.ts +122 -0
  310. package/tests/pipeline/gate-engine.test.ts +195 -0
  311. package/tests/pipeline/migration.test.ts +133 -0
  312. package/tests/pipeline/orchestrator.test.ts +614 -0
  313. package/tests/pipeline/packets/builders.test.ts +347 -0
  314. package/tests/pipeline/repo-snapshot.test.ts +189 -0
  315. package/tests/pipeline/role-execution-adapter.test.ts +299 -0
  316. package/tests/pipeline/skill-loader.test.ts +186 -0
  317. package/tests/pipeline/start-env-checks.test.ts +123 -0
  318. package/tests/pipeline/types.test.ts +156 -0
@@ -847,6 +847,7 @@ function showHelp(): void {
847
847
  ['/db [action]', 'Database management (status/configure/apply)'],
848
848
  ['/doctor', 'Run database and project readiness checks'],
849
849
  ['/review', 'Run post-build audit/review with findings and recovery'],
850
+ ['/debug', 'Start interactive debugging session (use /back to return)'],
850
851
  ['/clear', 'Clear screen'],
851
852
  ['/exit', 'Exit Popeye'],
852
853
  ];
@@ -1028,6 +1029,11 @@ async function handleInput(input: string, state: SessionState): Promise<boolean>
1028
1029
  await handleReviewSlashCommand(state, args);
1029
1030
  break;
1030
1031
 
1032
+ case '/debug':
1033
+ case '/dbg':
1034
+ await handleDebugSlashCommand(state);
1035
+ break;
1036
+
1031
1037
  default:
1032
1038
  printError(`Unknown command: ${cmd}`);
1033
1039
  printInfo('Type /help for available commands');
@@ -1208,6 +1214,27 @@ async function handleReviewSlashCommand(state: SessionState, args: string[] = []
1208
1214
  }
1209
1215
  }
1210
1216
 
1217
+ /**
1218
+ * Handle /debug slash command - start interactive debugging session
1219
+ */
1220
+ async function handleDebugSlashCommand(state: SessionState): Promise<void> {
1221
+ if (!state.projectDir) {
1222
+ printError('No active project. Create or resume a project first.');
1223
+ return;
1224
+ }
1225
+
1226
+ try {
1227
+ const { runDebugSession } = await import('./commands/debug.js');
1228
+ await runDebugSession({
1229
+ projectDir: state.projectDir,
1230
+ language: state.language,
1231
+ });
1232
+ printInfo('Returned to main Popeye session.');
1233
+ } catch (err) {
1234
+ printError(err instanceof Error ? err.message : 'Debug session failed');
1235
+ }
1236
+ }
1237
+
1211
1238
  /**
1212
1239
  * Handle /overview command - full project plan and milestone review
1213
1240
  */
@@ -198,6 +198,8 @@ export function generateAllDockerCompose(projectName: string): string {
198
198
  dockerfile: Dockerfile
199
199
  ports:
200
200
  - "8000:8000"
201
+ env_file:
202
+ - ./apps/backend/.env
201
203
  environment:
202
204
  - DEBUG=false
203
205
  - FRONTEND_URL=http://frontend:80
@@ -150,6 +150,8 @@ export function generateAllDockerComposeWithDb(projectName: string): string {
150
150
  dockerfile: Dockerfile
151
151
  ports:
152
152
  - "8000:8000"
153
+ env_file:
154
+ - ./apps/backend/.env
153
155
  environment:
154
156
  - DEBUG=false
155
157
  - FRONTEND_URL=http://frontend:80
@@ -211,5 +213,13 @@ DB_VECTOR_REQUIRED=true
211
213
 
212
214
  # Admin Wizard
213
215
  ADMIN_SETUP_TOKEN=change-me-to-a-random-string
216
+
217
+ # JWT Configuration
218
+ SECRET_KEY=change-me-in-production
219
+
220
+ # Google OAuth2 (optional - uncomment to enable)
221
+ # GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
222
+ # GOOGLE_CLIENT_SECRET=your-client-secret
223
+ # GOOGLE_REDIRECT_URI=http://localhost:8000/api/v1/auth/google/callback
214
224
  `;
215
225
  }
@@ -495,15 +495,19 @@ DATABASE_URL=sqlite:///./data/app.db
495
495
 
496
496
  /**
497
497
  * Generate UI spec placeholder for fullstack project
498
+ *
499
+ * @param projectName - The project name
500
+ * @param brandColor - Optional brand primary color hex (e.g. '#2563EB')
498
501
  */
499
- export function generateUiSpec(projectName: string): string {
502
+ export function generateUiSpec(projectName: string, brandColor?: string): string {
503
+ const primary = brandColor || '#3B82F6';
500
504
  return JSON.stringify(
501
505
  {
502
506
  name: projectName,
503
507
  version: '1.0',
504
508
  theme: {
505
509
  colors: {
506
- primary: '#3B82F6',
510
+ primary,
507
511
  secondary: '#6B7280',
508
512
  accent: '#10B981',
509
513
  },
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Artifact Manager — manages immutable versioned artifacts under /docs/.
3
+ * Supports both Markdown and JSON content types (P0-C).
4
+ * Implements version chains via group_id + previous_id (P1-2).
5
+ */
6
+
7
+ import { randomUUID } from 'node:crypto';
8
+ import { createHash } from 'node:crypto';
9
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'node:fs';
10
+ import { join, relative } from 'node:path';
11
+
12
+ import type {
13
+ ArtifactType,
14
+ ArtifactEntry,
15
+ ArtifactRef,
16
+ ContentType,
17
+ PipelinePhase,
18
+ } from './types.js';
19
+
20
+ // ─── Constants ───────────────────────────────────────────
21
+
22
+ /** Directory mappings: artifact type -> subdirectory under /docs/ */
23
+ const ARTIFACT_DIRS: Record<string, string> = {
24
+ master_plan: 'master-plan',
25
+ architecture: 'architecture',
26
+ role_plan: 'role-plans',
27
+ consensus: 'consensus',
28
+ arbitration: 'arbitration',
29
+ audit_report: 'audit',
30
+ rca_report: 'incidents',
31
+ production_readiness: 'production',
32
+ release_notes: 'release',
33
+ deployment: 'release',
34
+ rollback: 'release',
35
+ repo_snapshot: 'snapshots',
36
+ build_check: 'checks',
37
+ test_check: 'checks',
38
+ lint_check: 'checks',
39
+ typecheck_check: 'checks',
40
+ placeholder_scan: 'checks',
41
+ qa_validation: 'role-plans',
42
+ review_decision: 'consensus',
43
+ stuck_report: 'incidents',
44
+ journalist_trace: 'journal',
45
+ resolved_commands: 'checks',
46
+ constitution: 'governance',
47
+ change_request: 'governance',
48
+ };
49
+
50
+ /** All required subdirectories under /docs/ */
51
+ const DOCS_SUBDIRS = [
52
+ 'master-plan',
53
+ 'architecture',
54
+ 'role-plans',
55
+ 'consensus',
56
+ 'arbitration',
57
+ 'audit',
58
+ 'incidents',
59
+ 'production',
60
+ 'release',
61
+ 'snapshots',
62
+ 'checks',
63
+ 'journal',
64
+ 'governance',
65
+ ];
66
+
67
+ // ─── Helper Functions ────────────────────────────────────
68
+
69
+ function computeSha256(content: string): string {
70
+ return createHash('sha256').update(content, 'utf-8').digest('hex');
71
+ }
72
+
73
+ function shortId(): string {
74
+ return randomUUID().split('-')[0];
75
+ }
76
+
77
+ function formatDate(): string {
78
+ return new Date().toISOString().split('T')[0];
79
+ }
80
+
81
+ function getExtension(contentType: ContentType): string {
82
+ return contentType === 'json' ? '.json' : '.md';
83
+ }
84
+
85
+ // ─── Artifact Manager ────────────────────────────────────
86
+
87
+ export interface ArtifactManagerOptions {
88
+ projectDir: string;
89
+ }
90
+
91
+ export class ArtifactManager {
92
+ private readonly projectDir: string;
93
+ private readonly docsDir: string;
94
+
95
+ constructor(options: ArtifactManagerOptions) {
96
+ this.projectDir = options.projectDir;
97
+ this.docsDir = join(options.projectDir, 'docs');
98
+ }
99
+
100
+ /** Ensure all /docs/ subdirectories exist */
101
+ ensureDocsStructure(): void {
102
+ if (!existsSync(this.docsDir)) {
103
+ mkdirSync(this.docsDir, { recursive: true });
104
+ }
105
+ for (const subdir of DOCS_SUBDIRS) {
106
+ const dirPath = join(this.docsDir, subdir);
107
+ if (!existsSync(dirPath)) {
108
+ mkdirSync(dirPath, { recursive: true });
109
+ }
110
+ }
111
+ }
112
+
113
+ /** Create an artifact from Markdown content */
114
+ createArtifactText(
115
+ type: ArtifactType,
116
+ markdown: string,
117
+ phase: PipelinePhase,
118
+ groupId?: string,
119
+ ): ArtifactEntry {
120
+ return this.createArtifact(type, markdown, phase, 'markdown', groupId);
121
+ }
122
+
123
+ /** Create an artifact from a JSON-serializable object */
124
+ createArtifactJson(
125
+ type: ArtifactType,
126
+ jsonObject: unknown,
127
+ phase: PipelinePhase,
128
+ groupId?: string,
129
+ ): ArtifactEntry {
130
+ const content = JSON.stringify(jsonObject, null, 2);
131
+ return this.createArtifact(type, content, phase, 'json', groupId);
132
+ }
133
+
134
+ /** Core artifact creation logic */
135
+ private createArtifact(
136
+ type: ArtifactType,
137
+ content: string,
138
+ phase: PipelinePhase,
139
+ contentType: ContentType,
140
+ groupId?: string,
141
+ ): ArtifactEntry {
142
+ this.ensureDocsStructure();
143
+
144
+ const resolvedGroupId = groupId ?? randomUUID();
145
+ const existingArtifacts = this.listArtifacts(type);
146
+ const version = this.getNextVersion(resolvedGroupId, existingArtifacts);
147
+ const previousEntry = this.getLatestInGroup(resolvedGroupId, existingArtifacts);
148
+
149
+ const id = randomUUID();
150
+ const date = formatDate();
151
+ const sid = shortId();
152
+ const ext = getExtension(contentType);
153
+ const filename = `${type}_${sid}_v${version}_${date}${ext}`;
154
+
155
+ const subdir = ARTIFACT_DIRS[type] ?? 'misc';
156
+ const dirPath = join(this.docsDir, subdir);
157
+ if (!existsSync(dirPath)) {
158
+ mkdirSync(dirPath, { recursive: true });
159
+ }
160
+
161
+ const filePath = join(dirPath, filename);
162
+ const sha256 = computeSha256(content);
163
+
164
+ writeFileSync(filePath, content, 'utf-8');
165
+
166
+ const relativePath = relative(this.projectDir, filePath);
167
+
168
+ const entry: ArtifactEntry = {
169
+ id,
170
+ type,
171
+ phase,
172
+ version,
173
+ path: relativePath,
174
+ sha256,
175
+ timestamp: new Date().toISOString(),
176
+ immutable: true,
177
+ content_type: contentType,
178
+ group_id: resolvedGroupId,
179
+ previous_id: previousEntry?.id,
180
+ };
181
+
182
+ return entry;
183
+ }
184
+
185
+ /** Get the file path for a given artifact type and naming components */
186
+ getArtifactPath(
187
+ type: ArtifactType,
188
+ sid: string,
189
+ version: number,
190
+ date: string,
191
+ contentType: ContentType,
192
+ ): string {
193
+ const ext = getExtension(contentType);
194
+ const subdir = ARTIFACT_DIRS[type] ?? 'misc';
195
+ return join(this.docsDir, subdir, `${type}_${sid}_v${version}_${date}${ext}`);
196
+ }
197
+
198
+ /** List all artifacts, optionally filtered by type */
199
+ listArtifacts(type?: ArtifactType): ArtifactEntry[] {
200
+ // Scan for artifact JSON metadata files in a .artifacts/ dir
201
+ const metaDir = join(this.docsDir, '.artifacts');
202
+ if (!existsSync(metaDir)) {
203
+ return [];
204
+ }
205
+
206
+ const entries: ArtifactEntry[] = [];
207
+ const files = readdirSync(metaDir).filter((f) => f.endsWith('.json'));
208
+
209
+ for (const file of files) {
210
+ try {
211
+ const raw = readFileSync(join(metaDir, file), 'utf-8');
212
+ const parsed = JSON.parse(raw) as ArtifactEntry;
213
+ if (!type || parsed.type === type) {
214
+ entries.push(parsed);
215
+ }
216
+ } catch {
217
+ // Skip malformed metadata files
218
+ }
219
+ }
220
+
221
+ return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
222
+ }
223
+
224
+ /** Verify an artifact's SHA-256 matches its stored content */
225
+ verifyArtifact(entry: ArtifactEntry): boolean {
226
+ const fullPath = join(this.projectDir, entry.path);
227
+ if (!existsSync(fullPath)) {
228
+ return false;
229
+ }
230
+
231
+ const content = readFileSync(fullPath, 'utf-8');
232
+ const currentHash = computeSha256(content);
233
+ return currentHash === entry.sha256;
234
+ }
235
+
236
+ /** Get the latest artifact of a given type */
237
+ getLatestArtifact(type: ArtifactType): ArtifactEntry | null {
238
+ const all = this.listArtifacts(type);
239
+ if (all.length === 0) return null;
240
+ return all[all.length - 1];
241
+ }
242
+
243
+ /** Get next version number for a group across existing artifacts */
244
+ getNextVersion(groupId: string, existingArtifacts: ArtifactEntry[]): number {
245
+ const groupArtifacts = existingArtifacts.filter((a) => a.group_id === groupId);
246
+ if (groupArtifacts.length === 0) return 1;
247
+ const maxVersion = Math.max(...groupArtifacts.map((a) => a.version));
248
+ return maxVersion + 1;
249
+ }
250
+
251
+ /** Convert an ArtifactEntry to an ArtifactRef */
252
+ toArtifactRef(entry: ArtifactEntry): ArtifactRef {
253
+ return {
254
+ artifact_id: entry.id,
255
+ path: entry.path,
256
+ sha256: entry.sha256,
257
+ version: entry.version,
258
+ type: entry.type,
259
+ };
260
+ }
261
+
262
+ /** Store artifact metadata for later retrieval */
263
+ storeArtifactMetadata(entry: ArtifactEntry): void {
264
+ const metaDir = join(this.docsDir, '.artifacts');
265
+ if (!existsSync(metaDir)) {
266
+ mkdirSync(metaDir, { recursive: true });
267
+ }
268
+
269
+ const metaPath = join(metaDir, `${entry.id}.json`);
270
+ writeFileSync(metaPath, JSON.stringify(entry, null, 2), 'utf-8');
271
+ }
272
+
273
+ /** Create an artifact and store its metadata in one step */
274
+ createAndStoreText(
275
+ type: ArtifactType,
276
+ markdown: string,
277
+ phase: PipelinePhase,
278
+ groupId?: string,
279
+ ): ArtifactEntry {
280
+ const entry = this.createArtifactText(type, markdown, phase, groupId);
281
+ this.storeArtifactMetadata(entry);
282
+ return entry;
283
+ }
284
+
285
+ /** Create a JSON artifact and store its metadata in one step */
286
+ createAndStoreJson(
287
+ type: ArtifactType,
288
+ jsonObject: unknown,
289
+ phase: PipelinePhase,
290
+ groupId?: string,
291
+ ): ArtifactEntry {
292
+ const entry = this.createArtifactJson(type, jsonObject, phase, groupId);
293
+ this.storeArtifactMetadata(entry);
294
+ return entry;
295
+ }
296
+
297
+ /** Update /docs/INDEX.md with current artifact listing */
298
+ updateIndex(artifacts: ArtifactEntry[]): void {
299
+ this.ensureDocsStructure();
300
+
301
+ const lines: string[] = [
302
+ '# Documentation Index',
303
+ '',
304
+ `> Auto-generated by Popeye Pipeline — ${new Date().toISOString()}`,
305
+ '',
306
+ '## Artifacts',
307
+ '',
308
+ '| Type | Version | Path | Phase | Timestamp |',
309
+ '|------|---------|------|-------|-----------|',
310
+ ];
311
+
312
+ const sorted = [...artifacts].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
313
+
314
+ for (const a of sorted) {
315
+ lines.push(`| ${a.type} | v${a.version} | ${a.path} | ${a.phase} | ${a.timestamp} |`);
316
+ }
317
+
318
+ lines.push('');
319
+ const indexPath = join(this.docsDir, 'INDEX.md');
320
+ writeFileSync(indexPath, lines.join('\n'), 'utf-8');
321
+ }
322
+
323
+ /** Get the latest artifact entry in a specific group */
324
+ private getLatestInGroup(
325
+ groupId: string,
326
+ existingArtifacts: ArtifactEntry[],
327
+ ): ArtifactEntry | null {
328
+ const groupArtifacts = existingArtifacts
329
+ .filter((a) => a.group_id === groupId)
330
+ .sort((a, b) => a.version - b.version);
331
+ if (groupArtifacts.length === 0) return null;
332
+ return groupArtifacts[groupArtifacts.length - 1];
333
+ }
334
+ }
335
+
336
+ /** Factory function */
337
+ export function createArtifactManager(projectDir: string): ArtifactManager {
338
+ return new ArtifactManager({ projectDir });
339
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Artifact Completeness Validators — deterministic structural checks.
3
+ * Runs BEFORE LLM review in consensus phases to catch obvious issues.
4
+ * Each validator checks for required sections, minimum content length,
5
+ * and structural integrity specific to its artifact type.
6
+ */
7
+
8
+ import type { ArtifactType } from './types.js';
9
+
10
+ // ─── Types ───────────────────────────────────────────────
11
+
12
+ export interface ValidationResult {
13
+ valid: boolean;
14
+ errors: string[];
15
+ warnings: string[];
16
+ }
17
+
18
+ // ─── Section Patterns ────────────────────────────────────
19
+
20
+ /** Regex patterns for detecting markdown sections (case-insensitive) */
21
+ function hasSection(content: string, patterns: RegExp[]): boolean {
22
+ return patterns.some((p) => p.test(content));
23
+ }
24
+
25
+ function findMissingSections(
26
+ content: string,
27
+ required: { name: string; patterns: RegExp[] }[],
28
+ ): string[] {
29
+ const missing: string[] = [];
30
+ for (const { name, patterns } of required) {
31
+ if (!hasSection(content, patterns)) {
32
+ missing.push(name);
33
+ }
34
+ }
35
+ return missing;
36
+ }
37
+
38
+ // ─── Per-Type Validators ─────────────────────────────────
39
+
40
+ function validateMasterPlan(content: string): ValidationResult {
41
+ const errors: string[] = [];
42
+ const warnings: string[] = [];
43
+
44
+ if (content.length < 200) {
45
+ errors.push('Master plan is too short (min 200 characters)');
46
+ }
47
+
48
+ const missing = findMissingSections(content, [
49
+ { name: 'Goals/Objectives', patterns: [/#+\s*(goals?|objectives?)/i, /\bgoals?\b.*:/i] },
50
+ { name: 'Milestones', patterns: [/#+\s*milestones?/i, /\bmilestone\s+\d/i] },
51
+ { name: 'Success Criteria', patterns: [/#+\s*success\s+criteria/i, /\bsuccess\s+criteria\b/i, /#+\s*acceptance\s+criteria/i] },
52
+ ]);
53
+
54
+ for (const section of missing) {
55
+ errors.push(`Missing required section: ${section}`);
56
+ }
57
+
58
+ // Check for empty sections (heading followed by another heading or end)
59
+ const emptyHeadings = content.match(/^(#+\s+.+)\n(?=#+\s+|\s*$)/gm);
60
+ if (emptyHeadings && emptyHeadings.length > 2) {
61
+ warnings.push(`${emptyHeadings.length} potentially empty sections detected`);
62
+ }
63
+
64
+ return { valid: errors.length === 0, errors, warnings };
65
+ }
66
+
67
+ function validateArchitecture(content: string): ValidationResult {
68
+ const errors: string[] = [];
69
+ const warnings: string[] = [];
70
+
71
+ if (content.length < 200) {
72
+ errors.push('Architecture document is too short (min 200 characters)');
73
+ }
74
+
75
+ const missing = findMissingSections(content, [
76
+ { name: 'Components/Modules', patterns: [/#+\s*(components?|modules?|services?)/i, /\bcomponent\b/i] },
77
+ { name: 'Data Flow/Contracts', patterns: [/#+\s*(data\s+flow|contracts?|api|interfaces?)/i, /\bcontract\b/i, /\bdata\s+flow\b/i] },
78
+ { name: 'Tech Stack', patterns: [/#+\s*(tech\s+stack|technology|stack)/i, /\btech\s+stack\b/i] },
79
+ ]);
80
+
81
+ for (const section of missing) {
82
+ errors.push(`Missing required section: ${section}`);
83
+ }
84
+
85
+ // Must reference at least one file path
86
+ const hasFilePath = /(?:src\/|app\/|pages\/|lib\/|\.ts|\.js|\.py|\.go)/.test(content);
87
+ if (!hasFilePath) {
88
+ warnings.push('Architecture should reference at least one file path');
89
+ }
90
+
91
+ return { valid: errors.length === 0, errors, warnings };
92
+ }
93
+
94
+ function validateRolePlan(content: string): ValidationResult {
95
+ const errors: string[] = [];
96
+ const warnings: string[] = [];
97
+
98
+ if (content.length < 100) {
99
+ errors.push('Role plan is too short (min 100 characters)');
100
+ }
101
+
102
+ const missing = findMissingSections(content, [
103
+ { name: 'Tasks/Responsibilities', patterns: [/#+\s*(tasks?|responsibilities?|work\s+items?)/i, /\btask\b/i] },
104
+ { name: 'Dependencies', patterns: [/#+\s*(dependenc|prerequisites?|requires?)/i, /\bdepend/i] },
105
+ { name: 'Acceptance Criteria', patterns: [/#+\s*(acceptance|done\s+when|completion)/i, /\bacceptance\b/i, /\bdone\s+when\b/i] },
106
+ ]);
107
+
108
+ for (const section of missing) {
109
+ errors.push(`Missing required section: ${section}`);
110
+ }
111
+
112
+ // Should reference a role name
113
+ const rolePatterns = /\b(DISPATCHER|ARCHITECT|DB_EXPERT|BACKEND|FRONTEND|WEBSITE|QA_TESTER|REVIEWER|AUDITOR|JOURNALIST)\b/i;
114
+ if (!rolePatterns.test(content)) {
115
+ warnings.push('Role plan should reference the role name');
116
+ }
117
+
118
+ return { valid: errors.length === 0, errors, warnings };
119
+ }
120
+
121
+ function validateQaValidation(content: string): ValidationResult {
122
+ const errors: string[] = [];
123
+ const warnings: string[] = [];
124
+
125
+ const missing = findMissingSections(content, [
126
+ { name: 'Test Results', patterns: [/#+\s*(test\s+results?|results?)/i, /\btest\s+results?\b/i, /\bpass(?:ed|ing)?\b/i] },
127
+ { name: 'Coverage', patterns: [/#+\s*coverage/i, /\bcoverage\b/i, /\d+\s*%/] },
128
+ ]);
129
+
130
+ for (const section of missing) {
131
+ errors.push(`Missing required section: ${section}`);
132
+ }
133
+
134
+ // Should contain pass/fail counts
135
+ const hasPassFail = /\b\d+\s*(pass|fail|error|skip)/i.test(content);
136
+ if (!hasPassFail) {
137
+ warnings.push('QA validation should include pass/fail counts');
138
+ }
139
+
140
+ return { valid: errors.length === 0, errors, warnings };
141
+ }
142
+
143
+ function validateAuditReport(content: string): ValidationResult {
144
+ const errors: string[] = [];
145
+ const warnings: string[] = [];
146
+
147
+ // Try JSON parsing
148
+ try {
149
+ const parsed = JSON.parse(content);
150
+ if (!Array.isArray(parsed.findings)) {
151
+ errors.push('Audit report must have a "findings" array');
152
+ }
153
+ if (typeof parsed.overall_status !== 'string') {
154
+ errors.push('Audit report must have "overall_status"');
155
+ }
156
+ if (typeof parsed.system_risk_score !== 'number') {
157
+ errors.push('Audit report must have "system_risk_score"');
158
+ }
159
+ } catch {
160
+ // Not JSON — check for markdown-style audit
161
+ if (!content.includes('findings') && !content.includes('finding')) {
162
+ errors.push('Audit report must contain findings');
163
+ }
164
+ if (!content.includes('status') && !content.includes('PASS') && !content.includes('FAIL')) {
165
+ errors.push('Audit report must contain overall status');
166
+ }
167
+ if (!content.includes('risk') && !content.includes('score')) {
168
+ warnings.push('Audit report should include risk score');
169
+ }
170
+ }
171
+
172
+ return { valid: errors.length === 0, errors, warnings };
173
+ }
174
+
175
+ // ─── Validator Registry ──────────────────────────────────
176
+
177
+ const VALIDATORS: Partial<Record<ArtifactType, (content: string) => ValidationResult>> = {
178
+ master_plan: validateMasterPlan,
179
+ architecture: validateArchitecture,
180
+ role_plan: validateRolePlan,
181
+ qa_validation: validateQaValidation,
182
+ audit_report: validateAuditReport,
183
+ };
184
+
185
+ // ─── Public API ──────────────────────────────────────────
186
+
187
+ /**
188
+ * Validate artifact content completeness based on type-specific rules.
189
+ * Returns a ValidationResult with errors (blocking) and warnings (non-blocking).
190
+ *
191
+ * Args:
192
+ * type: The artifact type to validate against.
193
+ * content: The artifact content string.
194
+ *
195
+ * Returns:
196
+ * ValidationResult with valid flag, errors, and warnings.
197
+ */
198
+ export function validateArtifactCompleteness(
199
+ type: ArtifactType,
200
+ content: string,
201
+ ): ValidationResult {
202
+ const validator = VALIDATORS[type];
203
+ if (!validator) {
204
+ // No validator for this type — pass by default
205
+ return { valid: true, errors: [], warnings: [] };
206
+ }
207
+
208
+ if (!content || content.trim().length === 0) {
209
+ return {
210
+ valid: false,
211
+ errors: [`${type} artifact has empty content`],
212
+ warnings: [],
213
+ };
214
+ }
215
+
216
+ return validator(content);
217
+ }
218
+
219
+ /**
220
+ * Get all artifact types that have validators.
221
+ */
222
+ export function getValidatableArtifactTypes(): ArtifactType[] {
223
+ return Object.keys(VALIDATORS) as ArtifactType[];
224
+ }