@cluesmith/codev 3.0.0 → 3.0.2

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 (311) hide show
  1. package/LICENSE +201 -0
  2. package/bin/af.js +2 -2
  3. package/bin/afx.js +8 -0
  4. package/bin/consult.js +2 -1
  5. package/bin/team.js +2 -1
  6. package/{dashboard/dist/assets/index-nr0cXxtf.css → dashboard-dist/assets/index-BWYZtvwm.css} +1 -1
  7. package/dashboard-dist/assets/index-Dn9zmcKp.js +195 -0
  8. package/dashboard-dist/assets/index-Dn9zmcKp.js.map +1 -0
  9. package/{dashboard/dist → dashboard-dist}/index.html +2 -2
  10. package/dist/agent-farm/cli.d.ts.map +1 -1
  11. package/dist/agent-farm/cli.js +27 -19
  12. package/dist/agent-farm/cli.js.map +1 -1
  13. package/dist/agent-farm/commands/architect.d.ts +4 -4
  14. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  15. package/dist/agent-farm/commands/architect.js +23 -12
  16. package/dist/agent-farm/commands/architect.js.map +1 -1
  17. package/dist/agent-farm/commands/attach.d.ts.map +1 -1
  18. package/dist/agent-farm/commands/attach.js +101 -11
  19. package/dist/agent-farm/commands/attach.js.map +1 -1
  20. package/dist/agent-farm/commands/bench.d.ts +1 -1
  21. package/dist/agent-farm/commands/bench.js +1 -1
  22. package/dist/agent-farm/commands/cron.js +1 -1
  23. package/dist/agent-farm/commands/cron.js.map +1 -1
  24. package/dist/agent-farm/commands/db.d.ts +3 -3
  25. package/dist/agent-farm/commands/db.js +4 -4
  26. package/dist/agent-farm/commands/db.js.map +1 -1
  27. package/dist/agent-farm/commands/open.js +2 -2
  28. package/dist/agent-farm/commands/open.js.map +1 -1
  29. package/dist/agent-farm/commands/rename.js +1 -1
  30. package/dist/agent-farm/commands/rename.js.map +1 -1
  31. package/dist/agent-farm/commands/send.js +3 -3
  32. package/dist/agent-farm/commands/send.js.map +1 -1
  33. package/dist/agent-farm/commands/shell.js +3 -3
  34. package/dist/agent-farm/commands/shell.js.map +1 -1
  35. package/dist/agent-farm/commands/spawn-roles.d.ts +9 -5
  36. package/dist/agent-farm/commands/spawn-roles.d.ts.map +1 -1
  37. package/dist/agent-farm/commands/spawn-roles.js +48 -28
  38. package/dist/agent-farm/commands/spawn-roles.js.map +1 -1
  39. package/dist/agent-farm/commands/spawn-worktree.d.ts +5 -5
  40. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  41. package/dist/agent-farm/commands/spawn-worktree.js +80 -13
  42. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  43. package/dist/agent-farm/commands/spawn.d.ts +6 -6
  44. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  45. package/dist/agent-farm/commands/spawn.js +90 -53
  46. package/dist/agent-farm/commands/spawn.js.map +1 -1
  47. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  48. package/dist/agent-farm/commands/status.js +48 -3
  49. package/dist/agent-farm/commands/status.js.map +1 -1
  50. package/dist/agent-farm/commands/team-update.d.ts +1 -1
  51. package/dist/agent-farm/commands/team-update.js +1 -1
  52. package/dist/agent-farm/commands/team.d.ts +1 -1
  53. package/dist/agent-farm/commands/team.js +1 -1
  54. package/dist/agent-farm/commands/tower-cloud.d.ts +2 -2
  55. package/dist/agent-farm/commands/tower-cloud.js +3 -3
  56. package/dist/agent-farm/commands/tower-cloud.js.map +1 -1
  57. package/dist/agent-farm/commands/tower.js +2 -2
  58. package/dist/agent-farm/commands/tower.js.map +1 -1
  59. package/dist/agent-farm/db/index.d.ts.map +1 -1
  60. package/dist/agent-farm/db/index.js +47 -0
  61. package/dist/agent-farm/db/index.js.map +1 -1
  62. package/dist/agent-farm/db/schema.d.ts +1 -1
  63. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  64. package/dist/agent-farm/db/schema.js +1 -1
  65. package/dist/agent-farm/db/types.d.ts +1 -1
  66. package/dist/agent-farm/lib/cloud-config.js +2 -2
  67. package/dist/agent-farm/lib/cloud-config.js.map +1 -1
  68. package/dist/agent-farm/lib/tower-client.d.ts +6 -230
  69. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  70. package/dist/agent-farm/lib/tower-client.js +6 -293
  71. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  72. package/dist/agent-farm/lib/tunnel-client.js +1 -1
  73. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  74. package/dist/agent-farm/servers/message-write.d.ts +24 -0
  75. package/dist/agent-farm/servers/message-write.d.ts.map +1 -0
  76. package/dist/agent-farm/servers/message-write.js +63 -0
  77. package/dist/agent-farm/servers/message-write.js.map +1 -0
  78. package/dist/agent-farm/servers/overview.d.ts +4 -1
  79. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  80. package/dist/agent-farm/servers/overview.js +38 -5
  81. package/dist/agent-farm/servers/overview.js.map +1 -1
  82. package/dist/agent-farm/servers/send-buffer.d.ts +4 -3
  83. package/dist/agent-farm/servers/send-buffer.d.ts.map +1 -1
  84. package/dist/agent-farm/servers/send-buffer.js +7 -4
  85. package/dist/agent-farm/servers/send-buffer.js.map +1 -1
  86. package/dist/agent-farm/servers/tower-cron-parser.js +1 -1
  87. package/dist/agent-farm/servers/tower-cron-parser.js.map +1 -1
  88. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  89. package/dist/agent-farm/servers/tower-cron.js +4 -5
  90. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  91. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  92. package/dist/agent-farm/servers/tower-instances.js +6 -5
  93. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  94. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  95. package/dist/agent-farm/servers/tower-routes.js +19 -16
  96. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  97. package/dist/agent-farm/servers/tower-server.js +19 -4
  98. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  99. package/dist/agent-farm/servers/tower-terminals.d.ts +2 -2
  100. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  101. package/dist/agent-farm/servers/tower-terminals.js +47 -18
  102. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  103. package/dist/agent-farm/servers/tower-tunnel.js +2 -2
  104. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  105. package/dist/agent-farm/servers/tower-utils.d.ts +9 -6
  106. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  107. package/dist/agent-farm/servers/tower-utils.js +12 -6
  108. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  109. package/dist/agent-farm/state.js +1 -1
  110. package/dist/agent-farm/state.js.map +1 -1
  111. package/dist/agent-farm/types.d.ts +11 -2
  112. package/dist/agent-farm/types.d.ts.map +1 -1
  113. package/dist/agent-farm/utils/config.d.ts +11 -0
  114. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  115. package/dist/agent-farm/utils/config.js +21 -0
  116. package/dist/agent-farm/utils/config.js.map +1 -1
  117. package/dist/agent-farm/utils/harness.d.ts +85 -0
  118. package/dist/agent-farm/utils/harness.d.ts.map +1 -0
  119. package/dist/agent-farm/utils/harness.js +212 -0
  120. package/dist/agent-farm/utils/harness.js.map +1 -0
  121. package/dist/agent-farm/utils/server-utils.d.ts +13 -0
  122. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  123. package/dist/agent-farm/utils/server-utils.js +38 -0
  124. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  125. package/dist/cli.d.ts +1 -1
  126. package/dist/cli.d.ts.map +1 -1
  127. package/dist/cli.js +19 -5
  128. package/dist/cli.js.map +1 -1
  129. package/dist/commands/adopt.js +2 -2
  130. package/dist/commands/adopt.js.map +1 -1
  131. package/dist/commands/consult/index.d.ts +37 -3
  132. package/dist/commands/consult/index.d.ts.map +1 -1
  133. package/dist/commands/consult/index.js +220 -150
  134. package/dist/commands/consult/index.js.map +1 -1
  135. package/dist/commands/doctor.d.ts.map +1 -1
  136. package/dist/commands/doctor.js +54 -8
  137. package/dist/commands/doctor.js.map +1 -1
  138. package/dist/commands/init.js +1 -1
  139. package/dist/commands/init.js.map +1 -1
  140. package/dist/commands/porch/artifacts.d.ts +69 -0
  141. package/dist/commands/porch/artifacts.d.ts.map +1 -0
  142. package/dist/commands/porch/artifacts.js +368 -0
  143. package/dist/commands/porch/artifacts.js.map +1 -0
  144. package/dist/commands/porch/checks.d.ts +8 -1
  145. package/dist/commands/porch/checks.d.ts.map +1 -1
  146. package/dist/commands/porch/checks.js +98 -1
  147. package/dist/commands/porch/checks.js.map +1 -1
  148. package/dist/commands/porch/index.d.ts +18 -7
  149. package/dist/commands/porch/index.d.ts.map +1 -1
  150. package/dist/commands/porch/index.js +299 -61
  151. package/dist/commands/porch/index.js.map +1 -1
  152. package/dist/commands/porch/next.d.ts.map +1 -1
  153. package/dist/commands/porch/next.js +72 -74
  154. package/dist/commands/porch/next.js.map +1 -1
  155. package/dist/commands/porch/notify.d.ts +2 -2
  156. package/dist/commands/porch/notify.js +5 -5
  157. package/dist/commands/porch/notify.js.map +1 -1
  158. package/dist/commands/porch/prompts.d.ts +2 -1
  159. package/dist/commands/porch/prompts.d.ts.map +1 -1
  160. package/dist/commands/porch/prompts.js +23 -14
  161. package/dist/commands/porch/prompts.js.map +1 -1
  162. package/dist/commands/porch/state.d.ts +27 -2
  163. package/dist/commands/porch/state.d.ts.map +1 -1
  164. package/dist/commands/porch/state.js +96 -9
  165. package/dist/commands/porch/state.js.map +1 -1
  166. package/dist/commands/porch/types.d.ts +8 -0
  167. package/dist/commands/porch/types.d.ts.map +1 -1
  168. package/dist/lib/config.d.ts +14 -0
  169. package/dist/lib/config.d.ts.map +1 -1
  170. package/dist/lib/config.js +7 -0
  171. package/dist/lib/config.js.map +1 -1
  172. package/dist/lib/forge-contracts.d.ts +10 -1
  173. package/dist/lib/forge-contracts.d.ts.map +1 -1
  174. package/dist/lib/forge-contracts.js.map +1 -1
  175. package/dist/lib/forge.d.ts.map +1 -1
  176. package/dist/lib/forge.js +25 -7
  177. package/dist/lib/forge.js.map +1 -1
  178. package/dist/lib/team-github.d.ts +49 -0
  179. package/dist/lib/team-github.d.ts.map +1 -1
  180. package/dist/lib/team-github.js +115 -6
  181. package/dist/lib/team-github.js.map +1 -1
  182. package/dist/terminal/pty-manager.d.ts.map +1 -1
  183. package/dist/terminal/pty-manager.js +2 -0
  184. package/dist/terminal/pty-manager.js.map +1 -1
  185. package/dist/terminal/ws-protocol.d.ts +10 -8
  186. package/dist/terminal/ws-protocol.d.ts.map +1 -1
  187. package/dist/terminal/ws-protocol.js +3 -0
  188. package/dist/terminal/ws-protocol.js.map +1 -1
  189. package/package.json +28 -25
  190. package/scripts/forge/gitea/auth-status.sh +0 -0
  191. package/scripts/forge/gitea/issue-comment.sh +0 -0
  192. package/scripts/forge/gitea/issue-list.sh +0 -0
  193. package/scripts/forge/gitea/issue-view.sh +0 -0
  194. package/scripts/forge/gitea/pr-exists.sh +4 -1
  195. package/scripts/forge/gitea/pr-list.sh +0 -0
  196. package/scripts/forge/gitea/pr-merge.sh +0 -0
  197. package/scripts/forge/gitea/pr-view.sh +0 -0
  198. package/scripts/forge/gitea/recently-closed.sh +0 -0
  199. package/scripts/forge/gitea/recently-merged.sh +0 -0
  200. package/scripts/forge/gitea/repo-archive.sh +0 -0
  201. package/scripts/forge/gitea/user-identity.sh +0 -0
  202. package/scripts/forge/github/auth-status.sh +0 -0
  203. package/scripts/forge/github/issue-comment.sh +0 -0
  204. package/scripts/forge/github/issue-list.sh +2 -2
  205. package/scripts/forge/github/issue-view.sh +0 -0
  206. package/scripts/forge/github/on-it-timestamps.sh +0 -0
  207. package/scripts/forge/github/pr-diff.sh +0 -0
  208. package/scripts/forge/github/pr-exists.sh +3 -1
  209. package/scripts/forge/github/pr-list.sh +2 -2
  210. package/scripts/forge/github/pr-merge.sh +0 -0
  211. package/scripts/forge/github/pr-search.sh +0 -0
  212. package/scripts/forge/github/pr-view.sh +0 -0
  213. package/scripts/forge/github/recently-closed.sh +0 -0
  214. package/scripts/forge/github/recently-merged.sh +0 -0
  215. package/scripts/forge/github/repo-archive.sh +0 -0
  216. package/scripts/forge/github/team-activity.sh +0 -0
  217. package/scripts/forge/github/user-identity.sh +0 -0
  218. package/scripts/forge/gitlab/auth-status.sh +0 -0
  219. package/scripts/forge/gitlab/issue-comment.sh +0 -0
  220. package/scripts/forge/gitlab/issue-list.sh +0 -0
  221. package/scripts/forge/gitlab/issue-view.sh +0 -0
  222. package/scripts/forge/gitlab/pr-diff.sh +0 -0
  223. package/scripts/forge/gitlab/pr-exists.sh +3 -1
  224. package/scripts/forge/gitlab/pr-list.sh +0 -0
  225. package/scripts/forge/gitlab/pr-merge.sh +0 -0
  226. package/scripts/forge/gitlab/pr-search.sh +0 -0
  227. package/scripts/forge/gitlab/pr-view.sh +0 -0
  228. package/scripts/forge/gitlab/recently-closed.sh +0 -0
  229. package/scripts/forge/gitlab/recently-merged.sh +0 -0
  230. package/scripts/forge/gitlab/repo-archive.sh +0 -0
  231. package/scripts/forge/gitlab/user-identity.sh +0 -0
  232. package/scripts/forge/linear/auth-status.sh +15 -0
  233. package/scripts/forge/linear/issue-comment.sh +38 -0
  234. package/scripts/forge/linear/issue-list.sh +35 -0
  235. package/scripts/forge/linear/issue-view.sh +37 -0
  236. package/scripts/forge/linear/recently-closed.sh +45 -0
  237. package/scripts/forge/linear/user-identity.sh +15 -0
  238. package/scripts/postinstall.mjs +51 -0
  239. package/skeleton/.claude/skills/afx/SKILL.md +144 -0
  240. package/skeleton/.claude/skills/codev/SKILL.md +3 -3
  241. package/skeleton/.claude/skills/consult/SKILL.md +1 -1
  242. package/skeleton/.claude/skills/porch/SKILL.md +2 -2
  243. package/skeleton/.claude/skills/update-arch-docs/SKILL.md +107 -0
  244. package/skeleton/DEPENDENCIES.md +1 -1
  245. package/skeleton/builders.md +4 -4
  246. package/skeleton/protocol-schema.json +2 -2
  247. package/skeleton/protocols/air/builder-prompt.md +6 -6
  248. package/skeleton/protocols/air/prompts/pr.md +17 -5
  249. package/skeleton/protocols/air/protocol.md +1 -2
  250. package/skeleton/protocols/aspir/builder-prompt.md +28 -6
  251. package/skeleton/protocols/aspir/prompts/review.md +11 -0
  252. package/skeleton/protocols/aspir/protocol.json +9 -1
  253. package/skeleton/protocols/aspir/protocol.md +1 -1
  254. package/skeleton/protocols/aspir/templates/plan.md +0 -20
  255. package/skeleton/protocols/aspir/templates/spec.md +0 -28
  256. package/skeleton/protocols/bugfix/builder-prompt.md +6 -6
  257. package/skeleton/protocols/bugfix/prompts/pr.md +16 -4
  258. package/skeleton/protocols/experiment/builder-prompt.md +15 -0
  259. package/skeleton/protocols/experiment/protocol.md +1 -6
  260. package/skeleton/protocols/maintain/builder-prompt.md +12 -11
  261. package/skeleton/protocols/maintain/prompts/maintain.md +88 -0
  262. package/skeleton/protocols/maintain/prompts/review.md +84 -0
  263. package/skeleton/protocols/maintain/protocol.json +14 -68
  264. package/skeleton/protocols/maintain/protocol.md +163 -394
  265. package/skeleton/protocols/maintain/templates/maintenance-run.md +18 -49
  266. package/skeleton/protocols/protocol-schema.json +1 -1
  267. package/skeleton/protocols/research/builder-prompt.md +85 -0
  268. package/skeleton/protocols/research/protocol.json +118 -0
  269. package/skeleton/protocols/research/protocol.md +169 -0
  270. package/skeleton/protocols/spike/builder-prompt.md +1 -1
  271. package/skeleton/protocols/spike/protocol.md +4 -4
  272. package/skeleton/protocols/spir/builder-prompt.md +28 -5
  273. package/skeleton/protocols/spir/prompts/review.md +11 -0
  274. package/skeleton/protocols/spir/protocol.json +9 -1
  275. package/skeleton/protocols/spir/protocol.md +6 -5
  276. package/skeleton/protocols/spir/templates/plan.md +0 -20
  277. package/skeleton/protocols/spir/templates/spec.md +0 -28
  278. package/skeleton/resources/commands/agent-farm.md +84 -84
  279. package/skeleton/resources/commands/codev.md +1 -1
  280. package/skeleton/resources/commands/consult.md +16 -7
  281. package/skeleton/resources/commands/overview.md +14 -15
  282. package/skeleton/resources/risk-triage.md +2 -2
  283. package/skeleton/resources/workflow-reference.md +14 -14
  284. package/skeleton/roles/architect.md +39 -39
  285. package/skeleton/roles/builder.md +22 -14
  286. package/skeleton/templates/AGENTS.md +5 -3
  287. package/skeleton/templates/CLAUDE.md +5 -3
  288. package/skeleton/templates/arch.md +100 -30
  289. package/skeleton/templates/cheatsheet.md +12 -12
  290. package/skeleton/templates/lessons-learned.md +45 -7
  291. package/templates/3d-viewer.html +16 -18
  292. package/templates/tower.html +1 -1
  293. package/dashboard/dist/assets/index-CTrlY11L.js +0 -194
  294. package/dashboard/dist/assets/index-CTrlY11L.js.map +0 -1
  295. package/skeleton/.claude/skills/af/SKILL.md +0 -146
  296. package/skeleton/porch/prompts/understand.md +0 -61
  297. package/skeleton/porch/prompts/verify.md +0 -58
  298. package/skeleton/protocols/maintain/prompts/audit.md +0 -111
  299. package/skeleton/protocols/maintain/prompts/clean.md +0 -91
  300. package/skeleton/protocols/maintain/prompts/sync.md +0 -113
  301. package/skeleton/protocols/maintain/prompts/verify.md +0 -110
  302. package/skeleton/protocols/tick/builder-prompt.md +0 -65
  303. package/skeleton/protocols/tick/consult-types/impl-review.md +0 -72
  304. package/skeleton/protocols/tick/consult-types/plan-review.md +0 -59
  305. package/skeleton/protocols/tick/consult-types/pr-review.md +0 -72
  306. package/skeleton/protocols/tick/consult-types/spec-review.md +0 -55
  307. package/skeleton/protocols/tick/protocol.json +0 -151
  308. package/skeleton/protocols/tick/protocol.md +0 -277
  309. package/skeleton/protocols/tick/templates/plan.md +0 -67
  310. package/skeleton/protocols/tick/templates/review.md +0 -89
  311. package/skeleton/protocols/tick/templates/spec.md +0 -61
@@ -14,14 +14,19 @@ import chalk from 'chalk';
14
14
  import { query as claudeQuery } from '@anthropic-ai/claude-agent-sdk';
15
15
  import { Codex } from '@openai/codex-sdk';
16
16
  import { readCodevFile, findWorkspaceRoot } from '../../lib/skeleton.js';
17
+ import { getResolver } from '../porch/artifacts.js';
17
18
  import { MetricsDB } from './metrics.js';
18
19
  import { extractUsage, extractReviewText } from './usage-extractor.js';
19
20
  import { executeForgeCommandSync } from '../../lib/forge.js';
20
21
  const MODEL_CONFIGS = {
21
22
  gemini: { cli: 'gemini', args: ['--model', 'gemini-3-pro-preview'], envVar: 'GEMINI_SYSTEM_MD' },
23
+ hermes: { cli: 'hermes', args: ['chat', '-q'], envVar: null },
22
24
  };
23
25
  // Models that use an Agent SDK instead of CLI subprocess
24
26
  const SDK_MODELS = ['claude', 'codex'];
27
+ // Prevent E2BIG when passing very large prompts to CLI backends.
28
+ // Large payloads are written to a temp file and referenced in the query.
29
+ const CLI_PROMPT_INLINE_MAX_CHARS = 100_000;
25
30
  // Claude Agent SDK turn limit. Claude explores the codebase with Read/Glob/Grep
26
31
  // tools before producing its verdict, so it needs a generous turn budget.
27
32
  const CLAUDE_MAX_TURNS = 200;
@@ -130,46 +135,28 @@ function loadDotenv(workspaceRoot) {
130
135
  }
131
136
  }
132
137
  /**
133
- * Find a spec file by number. Returns null if not found.
134
- * Errors if multiple matches found.
138
+ * Find spec content by project ID using the artifact resolver.
139
+ * Returns a ContentRef with content and label, or null if not found.
135
140
  */
136
- function findSpec(workspaceRoot, id) {
137
- const specsDir = path.join(workspaceRoot, 'codev', 'specs');
138
- const unpadded = id.replace(/^0+/, '') || '0';
139
- const padded = unpadded.padStart(4, '0');
140
- if (fs.existsSync(specsDir)) {
141
- const files = fs.readdirSync(specsDir).filter(f => f.endsWith('.md'));
142
- const matches = files.filter(f => f.startsWith(`${unpadded}-`) || f.startsWith(`${padded}-`));
143
- if (matches.length > 1) {
144
- const list = matches.map(f => ` - codev/specs/${f}`).join('\n');
145
- throw new Error(`Multiple spec files match '${unpadded}' or '${padded}':\n${list}`);
146
- }
147
- if (matches.length === 1) {
148
- return path.join(specsDir, matches[0]);
149
- }
150
- }
151
- return null;
141
+ function findSpecContent(workspaceRoot, id) {
142
+ const resolver = getResolver(workspaceRoot);
143
+ const content = resolver.getSpecContent(id, '');
144
+ if (!content)
145
+ return null;
146
+ const label = resolver.findSpecBaseName(id, '') ?? id;
147
+ return { content, label };
152
148
  }
153
149
  /**
154
- * Find a plan file by number. Returns null if not found.
155
- * Errors if multiple matches found.
150
+ * Find plan content by project ID using the artifact resolver.
151
+ * Returns a ContentRef with content and label, or null if not found.
156
152
  */
157
- function findPlan(workspaceRoot, id) {
158
- const plansDir = path.join(workspaceRoot, 'codev', 'plans');
159
- const unpadded = id.replace(/^0+/, '') || '0';
160
- const padded = unpadded.padStart(4, '0');
161
- if (fs.existsSync(plansDir)) {
162
- const files = fs.readdirSync(plansDir).filter(f => f.endsWith('.md'));
163
- const matches = files.filter(f => f.startsWith(`${unpadded}-`) || f.startsWith(`${padded}-`));
164
- if (matches.length > 1) {
165
- const list = matches.map(f => ` - codev/plans/${f}`).join('\n');
166
- throw new Error(`Multiple plan files match '${unpadded}' or '${padded}':\n${list}`);
167
- }
168
- if (matches.length === 1) {
169
- return path.join(plansDir, matches[0]);
170
- }
171
- }
172
- return null;
153
+ function findPlanContent(workspaceRoot, id) {
154
+ const resolver = getResolver(workspaceRoot);
155
+ const content = resolver.getPlanContent(id, '');
156
+ if (!content)
157
+ return null;
158
+ const baseName = resolver.findSpecBaseName(id, '') ?? id;
159
+ return { content, label: baseName };
173
160
  }
174
161
  /**
175
162
  * Check if running in a builder worktree
@@ -310,11 +297,11 @@ export async function runCodexConsultation(queryText, role, workspaceRoot, outpu
310
297
  try {
311
298
  const codex = new Codex({
312
299
  config: {
313
- experimental_instructions_file: tempFile,
300
+ model_instructions_file: tempFile,
314
301
  },
315
302
  });
316
303
  const thread = codex.startThread({
317
- model: 'gpt-5.2-codex',
304
+ model: 'gpt-5.4',
318
305
  sandboxMode: 'read-only',
319
306
  modelReasoningEffort: 'medium',
320
307
  workingDirectory: workspaceRoot,
@@ -509,28 +496,72 @@ async function runConsultation(model, query, workspaceRoot, role, outputPath, me
509
496
  let tempFile = null;
510
497
  const env = {};
511
498
  let cmd;
499
+ // When true, the query is written to the child's stdin instead of argv.
500
+ // Used for gemini to avoid V8 heap exhaustion on large prompts (#680).
501
+ let stdinPayload = null;
512
502
  if (model === 'gemini') {
513
503
  // Gemini uses GEMINI_SYSTEM_MD env var for role
514
504
  tempFile = path.join(tmpdir(), `codev-role-${Date.now()}.md`);
515
505
  fs.writeFileSync(tempFile, role);
516
506
  env['GEMINI_SYSTEM_MD'] = tempFile;
507
+ // Bugfix #680: gemini-cli v0.37.x crashes on large PR diffs (>500KB) due to
508
+ // V8 old-space exhaustion in the spawned subprocess. Mitigations:
509
+ // 1. Bump heap via NODE_OPTIONS (survives gemini-cli's internal relaunch).
510
+ // 2. Pipe the prompt via stdin instead of argv — avoids ARG_MAX and keeps
511
+ // V8 from holding the full prompt buffer twice.
512
+ env['NODE_OPTIONS'] = [process.env.NODE_OPTIONS ?? '', '--max-old-space-size=8192']
513
+ .join(' ')
514
+ .trim();
515
+ stdinPayload = query;
517
516
  // Use --output-format json to capture token usage/cost in structured output.
518
517
  // Never use --yolo — it allows Gemini to write files (#370).
519
- cmd = [config.cli, '--output-format', 'json', ...config.args, query];
518
+ // No positional query arg: prompt arrives on stdin (triggers non-interactive mode).
519
+ cmd = [config.cli, '--output-format', 'json', ...config.args];
520
+ }
521
+ else if (model === 'hermes') {
522
+ // Hermes does not have a dedicated system prompt flag for single-shot mode.
523
+ // Include role context at the top of the prompt.
524
+ const hermesPrompt = `${role}\n\n---\n\n${query}`;
525
+ // Large inline CLI args can exceed OS ARG_MAX and fail with E2BIG.
526
+ // For very large prompts, write the full prompt to a temp file and pass
527
+ // an instruction that points Hermes at that file.
528
+ if (hermesPrompt.length > CLI_PROMPT_INLINE_MAX_CHARS) {
529
+ tempFile = path.join(tmpdir(), `codev-consult-prompt-${Date.now()}.md`);
530
+ fs.writeFileSync(tempFile, hermesPrompt);
531
+ const instruction = [
532
+ `Read the full consultation prompt from this file: ${tempFile}`,
533
+ 'You have file access. Read files directly from disk to review code.',
534
+ ].join('\n\n');
535
+ cmd = [config.cli, ...config.args, instruction];
536
+ }
537
+ else {
538
+ cmd = [config.cli, ...config.args, hermesPrompt];
539
+ }
520
540
  }
521
541
  else {
522
542
  throw new Error(`Unknown model: ${model}`);
523
543
  }
524
- // Execute with passthrough stdio
525
- // Use 'ignore' for stdin to prevent blocking when spawned as subprocess
544
+ // Execute with passthrough stdio.
545
+ // Use 'ignore' for stdin when no payload — prevents blocking when spawned as subprocess.
546
+ // Use 'pipe' when we need to stream the prompt in (e.g. gemini, see #680).
526
547
  const fullEnv = { ...process.env, ...env };
527
548
  const startTime = Date.now();
549
+ const stdinMode = stdinPayload !== null ? 'pipe' : 'ignore';
528
550
  return new Promise((resolve, reject) => {
529
551
  const proc = spawn(cmd[0], cmd.slice(1), {
530
552
  cwd: workspaceRoot,
531
553
  env: fullEnv,
532
- stdio: ['ignore', 'pipe', 'inherit'],
554
+ stdio: [stdinMode, 'pipe', 'inherit'],
533
555
  });
556
+ if (stdinPayload !== null && proc.stdin) {
557
+ proc.stdin.on('error', (err) => {
558
+ // EPIPE can happen if the child exits before reading all input — not fatal.
559
+ if (err.code !== 'EPIPE') {
560
+ reject(err);
561
+ }
562
+ });
563
+ proc.stdin.end(stdinPayload, 'utf-8');
564
+ }
534
565
  const chunks = [];
535
566
  if (proc.stdout) {
536
567
  proc.stdout.on('data', (chunk) => {
@@ -660,33 +691,39 @@ function fetchPRDiff(prId) {
660
691
  }
661
692
  }
662
693
  /**
663
- * Build query for PR review.
664
- * Includes full PR diff + file list; model reads surrounding context from disk.
694
+ * Compose the PR review prompt text from already-fetched pieces.
695
+ *
696
+ * Split from the I/O wrapper so it can be tested without mocking the forge
697
+ * layer. Points the model at a temp-file path rather than inlining the diff:
698
+ * large (~800KB+) inlined diffs blow past the gemini-cli JSON path and bloat
699
+ * prompts for all models (#684).
665
700
  */
666
- function buildPRQuery(prId) {
667
- const prData = fetchPRData(prId);
668
- const diff = fetchPRDiff(prId);
669
- const fileList = prData.changedFiles.map(f => `- ${f}`).join('\n');
701
+ function composePRQueryText(params) {
702
+ const { prId, info, changedFiles, comments, diffPath, diffBytes, diffLines } = params;
703
+ const fileList = changedFiles.map(f => `- ${f}`).join('\n');
704
+ const diffSizeKb = (diffBytes / 1024).toFixed(1);
670
705
  return `Review Pull Request #${prId}
671
706
 
672
707
  ## PR Info
673
708
  \`\`\`json
674
- ${prData.info}
709
+ ${info}
675
710
  \`\`\`
676
711
 
677
- ## Changed Files
712
+ ## Changed Files (${changedFiles.length})
678
713
  ${fileList}
679
714
 
680
715
  ## PR Diff
681
- \`\`\`diff
682
- ${diff}
683
- \`\`\`
716
+ The full PR diff is **not inlined** in this prompt to keep the payload small. It has been written to this path on disk:
717
+
718
+ **Diff file**: \`${diffPath}\`
719
+ **Size**: ${diffSizeKb} KB (${diffBytes} bytes, ${diffLines} lines)
684
720
 
685
721
  ## How to Review
686
- Review the PR diff above for the changes. You also have **full filesystem access** — read files from disk for surrounding context beyond what the diff shows.
722
+ 1. **Read the diff file** from \`${diffPath}\` to see the exact changes (use the Read tool or \`cat\`).
723
+ 2. You have **full filesystem access** — read any project files from disk for surrounding context beyond what the diff shows.
687
724
 
688
725
  ## Comments
689
- ${prData.comments}
726
+ ${comments}
690
727
 
691
728
  ---
692
729
 
@@ -708,25 +745,58 @@ CONFIDENCE: [HIGH | MEDIUM | LOW]
708
745
 
709
746
  KEY_ISSUES: [List of critical issues if any, or "None"]`;
710
747
  }
748
+ /**
749
+ * Build query for PR review.
750
+ *
751
+ * Writes the full PR diff to a temp file and points the model at the path
752
+ * instead of inlining it. Inlining large diffs (~800KB+) caused gemini-cli's
753
+ * JSON output path to fail with "Unexpected end of JSON input" in 0.3s
754
+ * (#684); it also bloats prompts for Claude/Codex. This mirrors the pattern
755
+ * used by buildImplQuery.
756
+ *
757
+ * The temp file is left in place for the model to read during consultation.
758
+ * OS /tmp rotation handles cleanup.
759
+ */
760
+ function buildPRQuery(prId) {
761
+ const prData = fetchPRData(prId);
762
+ const diff = fetchPRDiff(prId);
763
+ // Private-per-user dir to avoid world-readable /tmp diffs + symlink/clobber
764
+ // races: mkdtempSync creates a fresh dir owned by us; writeFileSync with
765
+ // flag 'wx' refuses to follow a symlink or overwrite an existing file.
766
+ const diffDir = fs.mkdtempSync(path.join(tmpdir(), 'codev-pr-'));
767
+ const diffPath = path.join(diffDir, `pr-${prId}.diff`);
768
+ fs.writeFileSync(diffPath, diff, { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
769
+ const diffBytes = Buffer.byteLength(diff, 'utf-8');
770
+ const diffLines = diff ? diff.split('\n').length : 0;
771
+ return composePRQueryText({
772
+ prId,
773
+ info: prData.info,
774
+ changedFiles: prData.changedFiles,
775
+ comments: prData.comments,
776
+ diffPath,
777
+ diffBytes,
778
+ diffLines,
779
+ });
780
+ }
711
781
  /**
712
782
  * Build query for spec review
713
783
  */
714
- function buildSpecQuery(specPath, planPath) {
715
- let query = `Review Specification: ${path.basename(specPath)}
784
+ function buildSpecQuery(spec, plan) {
785
+ let query = `Review Specification: ${spec.label}
786
+
787
+ ## Specification
788
+
789
+ ${spec.content}
716
790
 
717
- Please read and review this specification:
718
- - Spec file: ${specPath}
719
791
  `;
720
- if (planPath) {
721
- query += `- Plan file: ${planPath}\n`;
722
- }
723
- query += `
724
- ## How to Review
725
- **Read the files listed above directly from disk.** You have full filesystem access.
726
- Do NOT rely on \`git diff\` or \`git log\` to review content — diffs may be truncated or miss uncommitted work.
727
- Open the spec file, read it in full, and evaluate it directly.
792
+ if (plan) {
793
+ query += `## Plan
728
794
 
729
- Please review:
795
+ ${plan.content}
796
+
797
+ `;
798
+ }
799
+ query += `Please review:
730
800
  1. Clarity and completeness of requirements
731
801
  2. Technical feasibility
732
802
  3. Edge cases and error scenarios
@@ -749,7 +819,7 @@ KEY_ISSUES: [List of critical issues if any, or "None"]`;
749
819
  * Build query for implementation review.
750
820
  * Accepts spec/plan paths and optional diff reference override.
751
821
  */
752
- function buildImplQuery(workspaceRoot, specPath, planPath, planPhase, diffRef) {
822
+ function buildImplQuery(workspaceRoot, spec, plan, planPhase, diffRef) {
753
823
  // Get compact diff summary
754
824
  let diffStat = '';
755
825
  let changedFiles = [];
@@ -766,15 +836,15 @@ function buildImplQuery(workspaceRoot, specPath, planPath, planPhase, diffRef) {
766
836
  if (planPhase) {
767
837
  query += ` — Phase: ${planPhase}`;
768
838
  }
769
- query += `\n\n## Context Files\n`;
770
- if (specPath) {
771
- query += `- Spec: ${specPath}\n`;
839
+ query += '\n\n';
840
+ if (spec) {
841
+ query += `## Specification\n\n${spec.content}\n\n`;
772
842
  }
773
- if (planPath) {
774
- query += `- Plan: ${planPath}\n`;
843
+ if (plan) {
844
+ query += `## Plan\n\n${plan.content}\n\n`;
775
845
  }
776
846
  if (planPhase) {
777
- query += `\n## REVIEW SCOPE — CURRENT PLAN PHASE ONLY\n`;
847
+ query += `## REVIEW SCOPE — CURRENT PLAN PHASE ONLY\n`;
778
848
  query += `You are reviewing **plan phase "${planPhase}" ONLY**.\n`;
779
849
  query += `Read the plan, find the section for "${planPhase}", and scope your review to ONLY the work described in that phase.\n\n`;
780
850
  query += `**DO NOT** request changes for work that belongs to other plan phases.\n`;
@@ -792,7 +862,7 @@ function buildImplQuery(workspaceRoot, specPath, planPath, planPhase, diffRef) {
792
862
  query += `Do NOT rely on git diffs to determine the current state of code — diffs miss uncommitted changes in worktrees.\n`;
793
863
  }
794
864
  else {
795
- query += `\n## Instructions\n\nRead the spec and plan files above, then explore the filesystem to find and review the implementation changes.\n`;
865
+ query += `\n## Instructions\n\nExplore the filesystem to find and review the implementation changes.\n`;
796
866
  }
797
867
  query += `
798
868
  Please review:
@@ -816,22 +886,22 @@ KEY_ISSUES: [List of critical issues if any, or "None"]`;
816
886
  /**
817
887
  * Build query for plan review
818
888
  */
819
- function buildPlanQuery(planPath, specPath) {
820
- let query = `Review Implementation Plan: ${path.basename(planPath)}
889
+ function buildPlanQuery(plan, spec) {
890
+ let query = `Review Implementation Plan: ${plan.label}
891
+
892
+ ## Plan
893
+
894
+ ${plan.content}
821
895
 
822
- Please read and review this implementation plan:
823
- - Plan file: ${planPath}
824
896
  `;
825
- if (specPath) {
826
- query += `- Spec file: ${specPath} (for context)\n`;
827
- }
828
- query += `
829
- ## How to Review
830
- **Read the files listed above directly from disk.** You have full filesystem access.
831
- Do NOT rely on \`git diff\` or \`git log\` to review content — diffs may be truncated or miss uncommitted work.
832
- Open the plan file (and spec if provided), read them in full, and evaluate the plan directly.
897
+ if (spec) {
898
+ query += `## Specification (for context)
833
899
 
834
- Please review:
900
+ ${spec.content}
901
+
902
+ `;
903
+ }
904
+ query += `Please review:
835
905
  1. Alignment with specification requirements
836
906
  2. Implementation approach and architecture
837
907
  3. Task breakdown and ordering
@@ -854,7 +924,7 @@ KEY_ISSUES: [List of critical issues if any, or "None"]`;
854
924
  * Build query for phase-scoped review.
855
925
  * Uses git show HEAD for the phase's atomic commit diff.
856
926
  */
857
- function buildPhaseQuery(workspaceRoot, planPhase, specPath, planPath) {
927
+ function buildPhaseQuery(workspaceRoot, planPhase, spec, plan) {
858
928
  let phaseDiff = '';
859
929
  try {
860
930
  phaseDiff = execSync('git show HEAD', { cwd: workspaceRoot, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
@@ -862,11 +932,11 @@ function buildPhaseQuery(workspaceRoot, planPhase, specPath, planPath) {
862
932
  catch {
863
933
  // If git show fails, reviewer explores filesystem
864
934
  }
865
- let query = `Review Phase Implementation: "${planPhase}"\n\n## Context Files\n`;
866
- if (specPath)
867
- query += `- Spec: ${specPath}\n`;
868
- if (planPath)
869
- query += `- Plan: ${planPath}\n`;
935
+ let query = `Review Phase Implementation: "${planPhase}"\n\n`;
936
+ if (spec)
937
+ query += `## Specification\n\n${spec.content}\n\n`;
938
+ if (plan)
939
+ query += `## Plan\n\n${plan.content}\n\n`;
870
940
  query += `
871
941
  ## REVIEW SCOPE — CURRENT PLAN PHASE ONLY
872
942
  You are reviewing **plan phase "${planPhase}" ONLY**.
@@ -937,36 +1007,36 @@ function resolveBuilderQuery(workspaceRoot, type, options) {
937
1007
  const projectId = projectState.id;
938
1008
  switch (type) {
939
1009
  case 'spec': {
940
- const specPath = findSpec(workspaceRoot, projectId);
941
- if (!specPath)
942
- throw new Error(`Spec ${projectId} not found in codev/specs/`);
943
- const planPath = findPlan(workspaceRoot, projectId);
944
- console.error(`Spec: ${specPath}`);
945
- if (planPath)
946
- console.error(`Plan: ${planPath}`);
947
- return buildSpecQuery(specPath, planPath);
1010
+ const spec = findSpecContent(workspaceRoot, projectId);
1011
+ if (!spec)
1012
+ throw new Error(`Spec ${projectId} not found`);
1013
+ const plan = findPlanContent(workspaceRoot, projectId);
1014
+ console.error(`Spec: ${spec.label}`);
1015
+ if (plan)
1016
+ console.error(`Plan: ${plan.label}`);
1017
+ return buildSpecQuery(spec, plan);
948
1018
  }
949
1019
  case 'plan': {
950
- const planPath = findPlan(workspaceRoot, projectId);
951
- if (!planPath)
952
- throw new Error(`Plan ${projectId} not found in codev/plans/`);
953
- const specPath = findSpec(workspaceRoot, projectId);
954
- console.error(`Plan: ${planPath}`);
955
- if (specPath)
956
- console.error(`Spec: ${specPath}`);
957
- return buildPlanQuery(planPath, specPath);
1020
+ const plan = findPlanContent(workspaceRoot, projectId);
1021
+ if (!plan)
1022
+ throw new Error(`Plan ${projectId} not found`);
1023
+ const spec = findSpecContent(workspaceRoot, projectId);
1024
+ console.error(`Plan: ${plan.label}`);
1025
+ if (spec)
1026
+ console.error(`Spec: ${spec.label}`);
1027
+ return buildPlanQuery(plan, spec);
958
1028
  }
959
1029
  case 'impl': {
960
- const specPath = findSpec(workspaceRoot, projectId);
961
- const planPath = findPlan(workspaceRoot, projectId);
1030
+ const spec = findSpecContent(workspaceRoot, projectId);
1031
+ const plan = findPlanContent(workspaceRoot, projectId);
962
1032
  console.error(`Project: ${projectId}`);
963
- if (specPath)
964
- console.error(`Spec: ${specPath}`);
965
- if (planPath)
966
- console.error(`Plan: ${planPath}`);
1033
+ if (spec)
1034
+ console.error(`Spec: ${spec.label}`);
1035
+ if (plan)
1036
+ console.error(`Plan: ${plan.label}`);
967
1037
  if (options.planPhase)
968
1038
  console.error(`Plan phase: ${options.planPhase}`);
969
- return buildImplQuery(workspaceRoot, specPath, planPath, options.planPhase);
1039
+ return buildImplQuery(workspaceRoot, spec, plan, options.planPhase);
970
1040
  }
971
1041
  case 'pr': {
972
1042
  const prId = findPRForCurrentBranch(workspaceRoot);
@@ -978,14 +1048,14 @@ function resolveBuilderQuery(workspaceRoot, type, options) {
978
1048
  if (!currentPhase) {
979
1049
  throw new Error('No current plan phase detected. Use --plan-phase to specify.');
980
1050
  }
981
- const specPath = findSpec(workspaceRoot, projectId);
982
- const planPath = findPlan(workspaceRoot, projectId);
1051
+ const spec = findSpecContent(workspaceRoot, projectId);
1052
+ const plan = findPlanContent(workspaceRoot, projectId);
983
1053
  console.error(`Phase: ${currentPhase}`);
984
- if (specPath)
985
- console.error(`Spec: ${specPath}`);
986
- if (planPath)
987
- console.error(`Plan: ${planPath}`);
988
- return buildPhaseQuery(workspaceRoot, currentPhase, specPath, planPath);
1054
+ if (spec)
1055
+ console.error(`Spec: ${spec.label}`);
1056
+ if (plan)
1057
+ console.error(`Plan: ${plan.label}`);
1058
+ return buildPhaseQuery(workspaceRoot, currentPhase, spec, plan);
989
1059
  }
990
1060
  case 'integration': {
991
1061
  const prId = findPRForCurrentBranch(workspaceRoot);
@@ -1010,24 +1080,24 @@ function resolveArchitectQuery(workspaceRoot, type, options) {
1010
1080
  const issueId = options.issue;
1011
1081
  switch (type) {
1012
1082
  case 'spec': {
1013
- const specPath = findSpec(workspaceRoot, issueId);
1014
- if (!specPath)
1015
- throw new Error(`Spec ${issueId} not found in codev/specs/`);
1016
- const planPath = findPlan(workspaceRoot, issueId);
1017
- console.error(`Spec: ${specPath}`);
1018
- if (planPath)
1019
- console.error(`Plan: ${planPath}`);
1020
- return buildSpecQuery(specPath, planPath);
1083
+ const spec = findSpecContent(workspaceRoot, issueId);
1084
+ if (!spec)
1085
+ throw new Error(`Spec ${issueId} not found`);
1086
+ const plan = findPlanContent(workspaceRoot, issueId);
1087
+ console.error(`Spec: ${spec.label}`);
1088
+ if (plan)
1089
+ console.error(`Plan: ${plan.label}`);
1090
+ return buildSpecQuery(spec, plan);
1021
1091
  }
1022
1092
  case 'plan': {
1023
- const planPath = findPlan(workspaceRoot, issueId);
1024
- if (!planPath)
1025
- throw new Error(`Plan ${issueId} not found in codev/plans/`);
1026
- const specPath = findSpec(workspaceRoot, issueId);
1027
- console.error(`Plan: ${planPath}`);
1028
- if (specPath)
1029
- console.error(`Spec: ${specPath}`);
1030
- return buildPlanQuery(planPath, specPath);
1093
+ const plan = findPlanContent(workspaceRoot, issueId);
1094
+ if (!plan)
1095
+ throw new Error(`Plan ${issueId} not found`);
1096
+ const spec = findSpecContent(workspaceRoot, issueId);
1097
+ console.error(`Plan: ${plan.label}`);
1098
+ if (spec)
1099
+ console.error(`Spec: ${spec.label}`);
1100
+ return buildPlanQuery(plan, spec);
1031
1101
  }
1032
1102
  case 'impl': {
1033
1103
  const pr = findPRForIssue(workspaceRoot, issueId);
@@ -1039,14 +1109,14 @@ function resolveArchitectQuery(workspaceRoot, type, options) {
1039
1109
  // May already be fetched
1040
1110
  }
1041
1111
  const mergeBase = execSync(`git merge-base main origin/${pr.headRefName}`, { cwd: workspaceRoot, encoding: 'utf-8' }).trim();
1042
- const specPath = findSpec(workspaceRoot, issueId);
1043
- const planPath = findPlan(workspaceRoot, issueId);
1112
+ const spec = findSpecContent(workspaceRoot, issueId);
1113
+ const plan = findPlanContent(workspaceRoot, issueId);
1044
1114
  console.error(`Project: ${issueId} (PR #${pr.number}, branch: ${pr.headRefName})`);
1045
- if (specPath)
1046
- console.error(`Spec: ${specPath}`);
1047
- if (planPath)
1048
- console.error(`Plan: ${planPath}`);
1049
- return buildImplQuery(workspaceRoot, specPath, planPath, options.planPhase, `${mergeBase}..origin/${pr.headRefName}`);
1115
+ if (spec)
1116
+ console.error(`Spec: ${spec.label}`);
1117
+ if (plan)
1118
+ console.error(`Plan: ${plan.label}`);
1119
+ return buildImplQuery(workspaceRoot, spec, plan, options.planPhase, `${mergeBase}..origin/${pr.headRefName}`);
1050
1120
  }
1051
1121
  case 'pr': {
1052
1122
  const pr = findPRForIssue(workspaceRoot, issueId);
@@ -1161,7 +1231,7 @@ export async function consult(options) {
1161
1231
  }
1162
1232
  }
1163
1233
  // Add file access instruction for Gemini
1164
- if (model === 'gemini') {
1234
+ if (model === 'gemini' || model === 'hermes') {
1165
1235
  query += '\n\nYou have file access. Read files directly from disk to review code.';
1166
1236
  }
1167
1237
  // Show the query/prompt being sent
@@ -1199,5 +1269,5 @@ export async function consult(options) {
1199
1269
  await runConsultation(model, query, workspaceRoot, role, outputPath, metricsCtx, isGeneralMode);
1200
1270
  }
1201
1271
  // Exported for testing
1202
- export { getDiffStat as _getDiffStat, buildSpecQuery as _buildSpecQuery, buildPlanQuery as _buildPlanQuery, computePersistentOutputPath as _computePersistentOutputPath, };
1272
+ export { getDiffStat as _getDiffStat, buildSpecQuery as _buildSpecQuery, buildPlanQuery as _buildPlanQuery, buildPRQuery as _buildPRQuery, composePRQueryText as _composePRQueryText, computePersistentOutputPath as _computePersistentOutputPath, };
1203
1273
  //# sourceMappingURL=index.js.map