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
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Command Resolver — detects project-type-specific build/test/lint/typecheck
3
+ * commands from a RepoSnapshot. Used by CheckRunner and ProductionGate.
4
+ */
5
+
6
+ import type { RepoSnapshot, ResolvedCommands } from './types.js';
7
+
8
+ // ─── Project Type Detection ──────────────────────────────
9
+
10
+ export type ProjectType = 'node' | 'python' | 'mixed' | 'unknown';
11
+
12
+ export function detectProjectType(snapshot: RepoSnapshot): ProjectType {
13
+ const hasNode = snapshot.config_files.some((c) => c.type === 'package.json');
14
+ const hasPython = snapshot.config_files.some(
15
+ (c) => c.type === 'pyproject.toml' || c.type === 'requirements.txt' || c.type === 'setup.py',
16
+ );
17
+
18
+ if (hasNode && hasPython) return 'mixed';
19
+ if (hasNode) return 'node';
20
+ if (hasPython) return 'python';
21
+ return 'unknown';
22
+ }
23
+
24
+ // ─── Command Resolution ──────────────────────────────────
25
+
26
+ export function resolveCommands(
27
+ snapshot: RepoSnapshot,
28
+ overrides?: Partial<ResolvedCommands>,
29
+ ): ResolvedCommands {
30
+ const projectType = detectProjectType(snapshot);
31
+ const pm = snapshot.package_manager ?? 'npm';
32
+ const scripts = snapshot.scripts;
33
+
34
+ let resolved: ResolvedCommands;
35
+
36
+ switch (projectType) {
37
+ case 'node':
38
+ resolved = resolveNodeCommands(pm, scripts, snapshot);
39
+ break;
40
+ case 'python':
41
+ resolved = resolvePythonCommands(snapshot);
42
+ break;
43
+ case 'mixed':
44
+ // Prefer Node commands, augment with Python where Node is missing
45
+ resolved = resolveNodeCommands(pm, scripts, snapshot);
46
+ if (!resolved.test) {
47
+ const pyResolved = resolvePythonCommands(snapshot);
48
+ resolved.test = pyResolved.test;
49
+ }
50
+ break;
51
+ default:
52
+ resolved = { resolved_from: 'none' };
53
+ }
54
+
55
+ // Apply overrides
56
+ if (overrides) {
57
+ if (overrides.build) resolved.build = overrides.build;
58
+ if (overrides.test) resolved.test = overrides.test;
59
+ if (overrides.lint) resolved.lint = overrides.lint;
60
+ if (overrides.typecheck) resolved.typecheck = overrides.typecheck;
61
+ if (overrides.migrations) resolved.migrations = overrides.migrations;
62
+ if (overrides.start) resolved.start = overrides.start;
63
+ }
64
+
65
+ return resolved;
66
+ }
67
+
68
+ // ─── Node Resolution ─────────────────────────────────────
69
+
70
+ function resolveNodeCommands(
71
+ pm: string,
72
+ scripts: Record<string, string>,
73
+ snapshot: RepoSnapshot,
74
+ ): ResolvedCommands {
75
+ const run = pm === 'yarn' ? 'yarn' : pm === 'pnpm' ? 'pnpm' : `${pm} run`;
76
+ const npx = pm === 'pnpm' ? 'pnpm exec' : pm === 'yarn' ? 'yarn' : 'npx';
77
+
78
+ const resolved: ResolvedCommands = {
79
+ resolved_from: 'package.json',
80
+ };
81
+
82
+ // Build
83
+ if (scripts.build) {
84
+ resolved.build = `${run} build`;
85
+ }
86
+
87
+ // Test
88
+ if (scripts.test) {
89
+ resolved.test = `${run} test`;
90
+ } else if (snapshot.test_framework === 'vitest') {
91
+ resolved.test = `${npx} vitest run`;
92
+ } else if (snapshot.test_framework === 'jest') {
93
+ resolved.test = `${npx} jest`;
94
+ }
95
+
96
+ // Lint
97
+ if (scripts.lint) {
98
+ resolved.lint = `${run} lint`;
99
+ }
100
+
101
+ // Typecheck
102
+ if (scripts.typecheck) {
103
+ resolved.typecheck = `${run} typecheck`;
104
+ } else if (snapshot.languages_detected.includes('typescript')) {
105
+ resolved.typecheck = `${npx} tsc --noEmit`;
106
+ }
107
+
108
+ // Migrations
109
+ const hasPrisma = snapshot.config_files.some(
110
+ (c) => c.type === 'prisma/schema.prisma',
111
+ );
112
+ if (hasPrisma) {
113
+ resolved.migrations = `${npx} prisma migrate deploy`;
114
+ }
115
+
116
+ // Start
117
+ if (scripts.start) {
118
+ resolved.start = `${run} start`;
119
+ } else if (scripts.dev) {
120
+ resolved.start = `${run} dev`;
121
+ }
122
+
123
+ return resolved;
124
+ }
125
+
126
+ // ─── Python Resolution ───────────────────────────────────
127
+
128
+ function resolvePythonCommands(snapshot: RepoSnapshot): ResolvedCommands {
129
+ const resolved: ResolvedCommands = {
130
+ resolved_from: snapshot.config_files
131
+ .find((c) => c.type === 'pyproject.toml' || c.type === 'requirements.txt')
132
+ ?.path ?? 'python-defaults',
133
+ };
134
+
135
+ // Test
136
+ if (snapshot.test_framework === 'pytest') {
137
+ resolved.test = 'pytest tests/';
138
+ } else {
139
+ resolved.test = 'pytest tests/'; // default for Python
140
+ }
141
+
142
+ // Lint
143
+ const hasPyproject = snapshot.config_files.some((c) => c.type === 'pyproject.toml');
144
+ if (hasPyproject) {
145
+ resolved.lint = 'ruff check .';
146
+ } else {
147
+ resolved.lint = 'flake8 src/';
148
+ }
149
+
150
+ // Typecheck
151
+ if (snapshot.languages_detected.includes('python')) {
152
+ resolved.typecheck = 'mypy src/';
153
+ }
154
+
155
+ // Build
156
+ resolved.build = 'python -m build';
157
+
158
+ // Migrations
159
+ const hasAlembic = snapshot.config_files.some((c) => c.type === 'alembic.ini');
160
+ if (hasAlembic) {
161
+ resolved.migrations = 'alembic upgrade head';
162
+ }
163
+
164
+ // Start
165
+ resolved.start = 'uvicorn main:app --host 0.0.0.0 --port 8000';
166
+
167
+ return resolved;
168
+ }
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Consensus Runner — adapter layer between structured packets
3
+ * and existing consensus machinery.
4
+ *
5
+ * Two modes (P1-D):
6
+ * 1. Independent Review (DEFAULT): N reviewers review simultaneously,
7
+ * no reviewer sees other reviewers' output.
8
+ * 2. Iterative Consensus (optional): for recovery plan iteration.
9
+ */
10
+
11
+ import { createHash } from 'node:crypto';
12
+
13
+ import type {
14
+ PlanPacket,
15
+ ConsensusPacket,
16
+ ReviewerVote,
17
+ } from '../types.js';
18
+ import type { GateDefinition } from '../gate-engine.js';
19
+ import { buildConsensusPacket } from '../packets/consensus-packet-builder.js';
20
+ import type { ConsensusRules } from '../packets/consensus-packet-builder.js';
21
+
22
+ // Re-use existing consensus infrastructure
23
+ import { iterateUntilConsensus } from '../../workflow/consensus.js';
24
+ import type { ConsensusConfig, ConsensusResult } from '../../types/consensus.js';
25
+
26
+ // ─── Types ───────────────────────────────────────────────
27
+
28
+ export interface ConsensusRunnerConfig {
29
+ mode: 'independent' | 'iterative';
30
+ minReviewers: number;
31
+ threshold: number;
32
+ quorum: number;
33
+ projectDir: string;
34
+ consensusConfig?: Partial<ConsensusConfig>;
35
+ /** Provider configurations for multi-LLM review */
36
+ reviewerProviders?: ReviewerProviderConfig[];
37
+ }
38
+
39
+ export interface ReviewerProviderConfig {
40
+ provider: string; // 'openai' | 'gemini' | 'grok'
41
+ model: string;
42
+ temperature: number;
43
+ }
44
+
45
+ const DEFAULT_PROVIDERS: ReviewerProviderConfig[] = [
46
+ { provider: 'openai', model: 'gpt-4o', temperature: 0.3 },
47
+ { provider: 'gemini', model: 'gemini-2.0-flash', temperature: 0.3 },
48
+ ];
49
+
50
+ // ─── Consensus Runner ────────────────────────────────────
51
+
52
+ export class ConsensusRunner {
53
+ private readonly config: ConsensusRunnerConfig;
54
+
55
+ constructor(config: ConsensusRunnerConfig) {
56
+ this.config = config;
57
+ }
58
+
59
+ /** Run structured consensus on a plan packet */
60
+ async runStructuredConsensus(
61
+ planPacket: PlanPacket,
62
+ gateDefinition: GateDefinition,
63
+ ): Promise<ConsensusPacket> {
64
+ const rules: ConsensusRules = {
65
+ threshold: gateDefinition.consensusThreshold ?? this.config.threshold,
66
+ quorum: this.config.quorum,
67
+ min_reviewers: gateDefinition.minReviewers ?? this.config.minReviewers,
68
+ };
69
+
70
+ let votes: ReviewerVote[];
71
+
72
+ if (this.config.mode === 'independent') {
73
+ votes = await this.runIndependentReview(planPacket);
74
+ } else {
75
+ votes = await this.runIterativeReview(planPacket);
76
+ }
77
+
78
+ // Build consensus packet from votes
79
+ const packet = buildConsensusPacket({
80
+ planPacketRef: {
81
+ artifact_id: planPacket.metadata.packet_id,
82
+ path: '',
83
+ sha256: '',
84
+ version: planPacket.metadata.version,
85
+ type: 'consensus',
86
+ },
87
+ votes,
88
+ rules,
89
+ });
90
+
91
+ return packet;
92
+ }
93
+
94
+ /** Independent review: spawn N reviewers, each reviews independently */
95
+ async runIndependentReview(planPacket: PlanPacket): Promise<ReviewerVote[]> {
96
+ const providers = this.config.reviewerProviders ?? DEFAULT_PROVIDERS;
97
+ const numReviewers = Math.max(
98
+ this.config.minReviewers,
99
+ providers.length,
100
+ );
101
+
102
+ // Build the review prompt from the plan packet
103
+ const prompt = buildReviewPrompt(planPacket);
104
+ const promptHash = createHash('sha256').update(prompt).digest('hex');
105
+
106
+ // Spawn reviewers in parallel
107
+ const reviewPromises: Promise<ReviewerVote>[] = [];
108
+ for (let i = 0; i < numReviewers; i++) {
109
+ const provider = providers[i % providers.length];
110
+ reviewPromises.push(
111
+ this.spawnSingleReviewer(
112
+ prompt,
113
+ promptHash,
114
+ provider,
115
+ `reviewer-${provider.provider}-${i}`,
116
+ ),
117
+ );
118
+ }
119
+
120
+ return Promise.all(reviewPromises);
121
+ }
122
+
123
+ /** Iterative review: wraps existing iterateUntilConsensus */
124
+ async runIterativeReview(planPacket: PlanPacket): Promise<ReviewerVote[]> {
125
+ const prompt = buildReviewPrompt(planPacket);
126
+
127
+ try {
128
+ const result = await iterateUntilConsensus(
129
+ prompt,
130
+ `Phase: ${planPacket.metadata.phase}`,
131
+ {
132
+ projectDir: this.config.projectDir,
133
+ config: this.config.consensusConfig,
134
+ },
135
+ );
136
+
137
+ // Convert legacy result to ReviewerVote format
138
+ const vote: ReviewerVote = {
139
+ reviewer_id: 'iterative-reviewer',
140
+ provider: 'openai',
141
+ model: this.config.consensusConfig?.openaiModel ?? 'gpt-4o',
142
+ temperature: this.config.consensusConfig?.temperature ?? 0.3,
143
+ prompt_hash: createHash('sha256').update(prompt).digest('hex'),
144
+ vote: result.approved ? 'APPROVE' : 'REJECT',
145
+ confidence: result.finalScore ?? 0.5,
146
+ blocking_issues: result.finalConcerns ?? [],
147
+ suggestions: result.finalRecommendations ?? [],
148
+ evidence_refs: [],
149
+ };
150
+
151
+ return [vote];
152
+ } catch {
153
+ return [{
154
+ reviewer_id: 'iterative-reviewer-error',
155
+ provider: 'openai',
156
+ model: 'unknown',
157
+ temperature: 0,
158
+ prompt_hash: '',
159
+ vote: 'REJECT',
160
+ confidence: 0,
161
+ blocking_issues: ['Iterative consensus failed'],
162
+ suggestions: [],
163
+ evidence_refs: [],
164
+ }];
165
+ }
166
+ }
167
+
168
+ /** Spawn a single independent reviewer */
169
+ private async spawnSingleReviewer(
170
+ prompt: string,
171
+ promptHash: string,
172
+ provider: ReviewerProviderConfig,
173
+ reviewerId: string,
174
+ ): Promise<ReviewerVote> {
175
+ try {
176
+ const result = await this.callProviderForReview(prompt, provider);
177
+
178
+ return {
179
+ reviewer_id: reviewerId,
180
+ provider: provider.provider,
181
+ model: provider.model,
182
+ temperature: provider.temperature,
183
+ prompt_hash: promptHash,
184
+ vote: result.approved ? 'APPROVE' : 'REJECT',
185
+ confidence: result.confidence,
186
+ blocking_issues: result.blockingIssues,
187
+ suggestions: result.suggestions,
188
+ evidence_refs: [],
189
+ };
190
+ } catch {
191
+ return {
192
+ reviewer_id: reviewerId,
193
+ provider: provider.provider,
194
+ model: provider.model,
195
+ temperature: provider.temperature,
196
+ prompt_hash: promptHash,
197
+ vote: 'REJECT',
198
+ confidence: 0,
199
+ blocking_issues: [`Review failed for ${provider.provider}`],
200
+ suggestions: [],
201
+ evidence_refs: [],
202
+ };
203
+ }
204
+ }
205
+
206
+ /** Call the appropriate provider adapter for a review */
207
+ private async callProviderForReview(
208
+ prompt: string,
209
+ provider: ReviewerProviderConfig,
210
+ ): Promise<ProviderReviewResult> {
211
+ switch (provider.provider) {
212
+ case 'openai': {
213
+ const { requestConsensus } = await import('../../adapters/openai.js');
214
+ const result = await requestConsensus(prompt, '', {
215
+ openaiModel: provider.model,
216
+ temperature: provider.temperature,
217
+ } as Partial<ConsensusConfig>);
218
+ return parseConsensusResult(result);
219
+ }
220
+ case 'gemini': {
221
+ const { requestConsensus } = await import('../../adapters/gemini.js');
222
+ const result = await requestConsensus(prompt, '', {
223
+ model: provider.model as never,
224
+ temperature: provider.temperature,
225
+ });
226
+ return parseConsensusResult(result);
227
+ }
228
+ case 'grok': {
229
+ const { requestConsensus } = await import('../../adapters/grok.js');
230
+ const result = await requestConsensus(prompt, '', {
231
+ model: provider.model,
232
+ temperature: provider.temperature,
233
+ });
234
+ return parseConsensusResult(result);
235
+ }
236
+ default:
237
+ throw new Error(`Unknown provider: ${provider.provider}`);
238
+ }
239
+ }
240
+ }
241
+
242
+ // ─── Helper Types ────────────────────────────────────────
243
+
244
+ interface ProviderReviewResult {
245
+ approved: boolean;
246
+ confidence: number;
247
+ blockingIssues: string[];
248
+ suggestions: string[];
249
+ }
250
+
251
+ function parseConsensusResult(result: ConsensusResult): ProviderReviewResult {
252
+ return {
253
+ approved: result.approved,
254
+ confidence: result.score / 100, // score is 0-100, confidence is 0-1
255
+ blockingIssues: result.concerns ?? [],
256
+ suggestions: result.recommendations ?? [],
257
+ };
258
+ }
259
+
260
+ // ─── Prompt Builder ──────────────────────────────────────
261
+
262
+ export function buildReviewPrompt(planPacket: PlanPacket): string {
263
+ const lines: string[] = [
264
+ `# Independent Plan Review`,
265
+ ``,
266
+ `## Phase: ${planPacket.metadata.phase}`,
267
+ `## Submitted by: ${planPacket.metadata.submitted_by}`,
268
+ `## Version: ${planPacket.metadata.version}`,
269
+ ``,
270
+ `## Acceptance Criteria`,
271
+ ...planPacket.acceptance_criteria.map((c) => `- ${c}`),
272
+ ``,
273
+ `## Constraints`,
274
+ ...planPacket.constraints.map((c) => `- [${c.type}] ${c.description}`),
275
+ ``,
276
+ ];
277
+
278
+ if (planPacket.open_questions?.length) {
279
+ lines.push(`## Open Questions`);
280
+ lines.push(...planPacket.open_questions.map((q) => `- ${q}`));
281
+ lines.push('');
282
+ }
283
+
284
+ lines.push(
285
+ `## Review Instructions`,
286
+ ``,
287
+ `You are an independent reviewer. Evaluate this plan for:`,
288
+ `1. Completeness — are all required artifacts defined?`,
289
+ `2. Consistency — do acceptance criteria match constraints?`,
290
+ `3. Feasibility — can this be implemented as described?`,
291
+ `4. Constitution compliance — does it follow governance rules?`,
292
+ ``,
293
+ `Respond with:`,
294
+ `- APPROVE, REJECT, or CONDITIONAL`,
295
+ `- Confidence score (0-1)`,
296
+ `- Blocking issues (if any)`,
297
+ `- Suggestions for improvement`,
298
+ );
299
+
300
+ return lines.join('\n');
301
+ }
302
+
303
+ // ─── Factory ─────────────────────────────────────────────
304
+
305
+ export function createConsensusRunner(
306
+ projectDir: string,
307
+ consensusConfig?: Partial<ConsensusConfig>,
308
+ ): ConsensusRunner {
309
+ return new ConsensusRunner({
310
+ mode: 'independent',
311
+ minReviewers: 2,
312
+ threshold: 0.95,
313
+ quorum: 2,
314
+ projectDir,
315
+ consensusConfig,
316
+ });
317
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Constitution management — artifact creation, hashing, and verification.
3
+ * The constitution (skills/POPEYE_CONSTITUTION.md) is the immutable governance
4
+ * document for the pipeline. This module ensures it is tracked as an artifact
5
+ * and its integrity is verified at every gate.
6
+ */
7
+
8
+ import { createHash } from 'node:crypto';
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+
12
+ import type { ArtifactEntry, PipelineState } from './types.js';
13
+ import type { ArtifactManager } from './artifact-manager.js';
14
+
15
+ // ─── Constants ───────────────────────────────────────────
16
+
17
+ const CONSTITUTION_FILENAME = 'POPEYE_CONSTITUTION.md';
18
+ const SKILLS_DIR = 'skills';
19
+
20
+ // ─── Hash Computation ────────────────────────────────────
21
+
22
+ /**
23
+ * Compute SHA-256 hash of constitution file content.
24
+ *
25
+ * Args:
26
+ * projectDir: Root project directory containing skills/ folder.
27
+ *
28
+ * Returns:
29
+ * Hex-encoded SHA-256 hash, or empty string if file not found.
30
+ */
31
+ export function computeConstitutionHash(projectDir: string): string {
32
+ const constitutionPath = join(projectDir, SKILLS_DIR, CONSTITUTION_FILENAME);
33
+ if (!existsSync(constitutionPath)) {
34
+ return '';
35
+ }
36
+
37
+ const content = readFileSync(constitutionPath, 'utf-8');
38
+ return createHash('sha256').update(content, 'utf-8').digest('hex');
39
+ }
40
+
41
+ // ─── Artifact Creation ───────────────────────────────────
42
+
43
+ /**
44
+ * Create a constitution artifact from the skills/POPEYE_CONSTITUTION.md file.
45
+ *
46
+ * Args:
47
+ * projectDir: Root project directory.
48
+ * artifactManager: The artifact manager instance.
49
+ *
50
+ * Returns:
51
+ * The created ArtifactEntry, or null if constitution file not found.
52
+ */
53
+ export function createConstitutionArtifact(
54
+ projectDir: string,
55
+ artifactManager: ArtifactManager,
56
+ ): ArtifactEntry | null {
57
+ const constitutionPath = join(projectDir, SKILLS_DIR, CONSTITUTION_FILENAME);
58
+ if (!existsSync(constitutionPath)) {
59
+ return null;
60
+ }
61
+
62
+ const content = readFileSync(constitutionPath, 'utf-8');
63
+ return artifactManager.createAndStoreText(
64
+ 'constitution',
65
+ content,
66
+ 'INTAKE',
67
+ );
68
+ }
69
+
70
+ // ─── Verification ────────────────────────────────────────
71
+
72
+ /**
73
+ * Verify the constitution file has not been modified since pipeline start.
74
+ * Compares current file hash against the hash stored in pipeline state.
75
+ *
76
+ * Args:
77
+ * pipeline: Current pipeline state (contains constitutionHash).
78
+ * projectDir: Root project directory.
79
+ *
80
+ * Returns:
81
+ * Object with valid=true if hash matches, or valid=false with reason.
82
+ */
83
+ export function verifyConstitution(
84
+ pipeline: PipelineState,
85
+ projectDir: string,
86
+ ): { valid: boolean; reason?: string } {
87
+ // If no hash stored yet (pre-INTAKE), skip verification
88
+ if (!pipeline.constitutionHash) {
89
+ return { valid: true };
90
+ }
91
+
92
+ const currentHash = computeConstitutionHash(projectDir);
93
+
94
+ if (!currentHash) {
95
+ return {
96
+ valid: false,
97
+ reason: 'Constitution file not found — may have been deleted',
98
+ };
99
+ }
100
+
101
+ if (currentHash !== pipeline.constitutionHash) {
102
+ return {
103
+ valid: false,
104
+ reason: 'Constitution has been modified since pipeline start',
105
+ };
106
+ }
107
+
108
+ return { valid: true };
109
+ }