@straiffi/archon 1.3.9 → 1.3.11

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 (79) hide show
  1. package/dist/client/assets/{TestsDialog-D9OPA6gD.js → TestsDialog-BUCv8I-8.js} +1 -1
  2. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DcEW5QFD.js → architectureDiagram-3BPJPVTR-C3Jb8g9R.js} +1 -1
  3. package/dist/client/assets/{blockDiagram-GPEHLZMM-BHHpGbRU.js → blockDiagram-GPEHLZMM-CZcqQgBX.js} +1 -1
  4. package/dist/client/assets/{c4Diagram-AAUBKEIU-DVT9bEMk.js → c4Diagram-AAUBKEIU-Cpv5hJF9.js} +1 -1
  5. package/dist/client/assets/channel-DmMIqd-b.js +1 -0
  6. package/dist/client/assets/{chunk-2J33WTMH-0mWaR2pM.js → chunk-2J33WTMH-DkbZ544o.js} +1 -1
  7. package/dist/client/assets/{chunk-3OPIFGDE-ie5RMOFe.js → chunk-3OPIFGDE-CTkBmjbl.js} +1 -1
  8. package/dist/client/assets/{chunk-5ZQYHXKU-Don__9Av.js → chunk-5ZQYHXKU-Cvt23bYu.js} +1 -1
  9. package/dist/client/assets/{chunk-727SXJPM-wDyJwSOz.js → chunk-727SXJPM-DX5ql843.js} +1 -1
  10. package/dist/client/assets/{chunk-AQP2D5EJ-zL6GwfQR.js → chunk-AQP2D5EJ-BWR4RAvi.js} +1 -1
  11. package/dist/client/assets/{chunk-CSCIHK7Q-hG7GT6kM.js → chunk-CSCIHK7Q-BrZJ9QMn.js} +1 -1
  12. package/dist/client/assets/{chunk-KSCS5N6A-DHZ1vx53.js → chunk-KSCS5N6A-Xsa2cnhz.js} +1 -1
  13. package/dist/client/assets/{chunk-L5ZTLDWV-BOAP3jHG.js → chunk-L5ZTLDWV-5L-9s_-b.js} +1 -1
  14. package/dist/client/assets/{chunk-LZXEDZCA-VTzUvCIM.js → chunk-LZXEDZCA-XcqcrPu3.js} +2 -2
  15. package/dist/client/assets/{chunk-ND2GUHAM-CD0EckYa.js → chunk-ND2GUHAM-Cehi3-t2.js} +1 -1
  16. package/dist/client/assets/{chunk-NZK2D7GU-DgkHXBUY.js → chunk-NZK2D7GU-s6NmMa8K.js} +1 -1
  17. package/dist/client/assets/{chunk-O5CBEL6O-BD8HlJHx.js → chunk-O5CBEL6O-CkU-v8ar.js} +1 -1
  18. package/dist/client/assets/{chunk-WU5MYG2G-BKs69T9I.js → chunk-WU5MYG2G-DJrhJw82.js} +1 -1
  19. package/dist/client/assets/classDiagram-4FO5ZUOK-CDTuTjGy.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BhEXEoEw.js +1 -0
  21. package/dist/client/assets/{dagre-BM42HDAG-DIQuqVZj.js → dagre-BM42HDAG-B-PypVRB.js} +1 -1
  22. package/dist/client/assets/{diagram-2AECGRRQ-BGa8V9_K.js → diagram-2AECGRRQ-Cm3pGoMu.js} +1 -1
  23. package/dist/client/assets/{diagram-5GNKFQAL-B7f44SFN.js → diagram-5GNKFQAL-DCwV7VX3.js} +1 -1
  24. package/dist/client/assets/{diagram-KO2AKTUF-BwAWQUFX.js → diagram-KO2AKTUF-B5RVwHze.js} +1 -1
  25. package/dist/client/assets/{diagram-LMA3HP47-DSuDRJQp.js → diagram-LMA3HP47-CmLaK5_k.js} +1 -1
  26. package/dist/client/assets/{diagram-OG6HWLK6-DD0GZshJ.js → diagram-OG6HWLK6-Mj0hhakE.js} +1 -1
  27. package/dist/client/assets/{erDiagram-TEJ5UH35-CoKzhckI.js → erDiagram-TEJ5UH35-D30rUvl5.js} +1 -1
  28. package/dist/client/assets/{flowDiagram-I6XJVG4X-vNA14hkg.js → flowDiagram-I6XJVG4X-Dh1RDWX7.js} +1 -1
  29. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CbqJzGXk.js → ganttDiagram-6RSMTGT7-B7f77DA_.js} +1 -1
  30. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zZhPvDvO.js → gitGraphDiagram-PVQCEYII-CKBXqaKU.js} +1 -1
  31. package/dist/client/assets/index-Cs7XmROA.js +260 -0
  32. package/dist/client/assets/index-ZyUvSLAF.css +2 -0
  33. package/dist/client/assets/{infoDiagram-5YYISTIA-B5MQ6uAZ.js → infoDiagram-5YYISTIA-BPpU75Ej.js} +1 -1
  34. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-Y-U0XVw_.js → ishikawaDiagram-YF4QCWOH-C4J2Eb6K.js} +1 -1
  35. package/dist/client/assets/{journeyDiagram-JHISSGLW-0OYZ1b1F.js → journeyDiagram-JHISSGLW-BthEH9FE.js} +1 -1
  36. package/dist/client/assets/{kanban-definition-UN3LZRKU-BETGupMr.js → kanban-definition-UN3LZRKU-DzSlAH8r.js} +1 -1
  37. package/dist/client/assets/{line-C0Uu4OEF.js → line-BTeJbdYR.js} +1 -1
  38. package/dist/client/assets/{mermaid-parser.core-CsK7xRnD.js → mermaid-parser.core-BJonz176.js} +1 -1
  39. package/dist/client/assets/{mermaid.core-DyZulcQu.js → mermaid.core-tt3Uc0ap.js} +3 -3
  40. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BrOgCom9.js → mindmap-definition-RKZ34NQL-iITkeEX1.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-4H26LBE5-CJm4InQ8.js → pieDiagram-4H26LBE5--CgS4G7C.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DLwvvQpW.js → quadrantDiagram-W4KKPZXB-Clq6DEX-.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DWkgqL-a.js → requirementDiagram-4Y6WPE33-r_apj3BK.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CcmrH_Ja.js → sankeyDiagram-5OEKKPKP-2XqJHFyi.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BTGXDMfv.js → sequenceDiagram-3UESZ5HK-k4Qrdp_I.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-AJRCARHV-BKSjTfBG.js → stateDiagram-AJRCARHV-CMr088KL.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-t7kpZS05.js +1 -0
  48. package/dist/client/assets/{timeline-definition-PNZ67QCA-12oqidas.js → timeline-definition-PNZ67QCA-BpkNV3oH.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-CIIHVFJN-MkEkIDNu.js → vennDiagram-CIIHVFJN-Bmte21GM.js} +1 -1
  50. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CyjJsknI.js → wardleyDiagram-YWT4CUSO-CLJX-O9B.js} +1 -1
  51. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CgM87B4K.js → xychartDiagram-2RQKCTM6-Bc_SL4rS.js} +1 -1
  52. package/dist/client/index.html +2 -2
  53. package/dist/server/db.js +1 -0
  54. package/dist/server/db.js.map +1 -1
  55. package/dist/server/index.js +5 -4
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/lib/agent.js +108 -84
  58. package/dist/server/lib/agent.js.map +1 -1
  59. package/dist/server/lib/chatOperations.js +6 -2
  60. package/dist/server/lib/chatOperations.js.map +1 -1
  61. package/dist/server/lib/chats.js +19 -0
  62. package/dist/server/lib/chats.js.map +1 -1
  63. package/dist/server/lib/models.js +49 -0
  64. package/dist/server/lib/models.js.map +1 -1
  65. package/dist/server/lib/ticketFollowUpOperations.js +1 -0
  66. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  67. package/dist/server/lib/ticketMessages.js +6 -6
  68. package/dist/server/lib/ticketMessages.js.map +1 -1
  69. package/dist/server/lib/ticketSettingsOperations.js +2 -2
  70. package/dist/server/lib/ticketSettingsOperations.js.map +1 -1
  71. package/dist/server/workers/chat.js +17 -18
  72. package/dist/server/workers/chat.js.map +1 -1
  73. package/package.json +1 -1
  74. package/dist/client/assets/channel-D7eTyqAO.js +0 -1
  75. package/dist/client/assets/classDiagram-4FO5ZUOK-CMf0_kXS.js +0 -1
  76. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Cdz00cqE.js +0 -1
  77. package/dist/client/assets/index-WAeyJA70.css +0 -2
  78. package/dist/client/assets/index-pzF5Qlv0.js +0 -260
  79. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DRLUAKxv.js +0 -1
@@ -1,10 +1,11 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { randomUUID } from 'crypto';
3
- import { isAbsolute, join, relative, resolve, sep } from 'path';
3
+ import { readFileSync } from 'fs';
4
+ import { extname, isAbsolute, join, relative, resolve, sep } from 'path';
4
5
  import Database from 'better-sqlite3';
5
6
  import db from '../db.js';
6
7
  import { getChatSession, updateChatSessionUsageSnapshot } from './chats.js';
7
- import { normalizeModelId } from './models.js';
8
+ import { normalizeClaudeModelId, normalizeModelId } from './models.js';
8
9
  import { PLAN_MODE_QUESTION_TOOL_NAME } from './planModeQuestions.js';
9
10
  import { getSpecSession, updateSpecSessionResumeId, updateSpecSessionStatus, updateSpecSessionTitle, updateSpecSessionUsageSnapshot } from './specs.js';
10
11
  import { updateSpecReviewRunStatus, updateSpecReviewRunStreaming, updateSpecReviewRunUsage } from './specReviews.js';
@@ -14,6 +15,74 @@ import { getTicket } from './tickets.js';
14
15
  const OPENCODE_PLACEHOLDER_TITLE_PATTERN = /^New session\b/i;
15
16
  const OPENCODE_SESSION_METADATA_LOOKUP_DELAY_MS = 1500;
16
17
  const AGENT_PROGRESS_UPDATE_INTERVAL_MS = 100;
18
+ const resolveAgentModel = (tool, model) => {
19
+ if (tool === 'claude') {
20
+ return normalizeClaudeModelId(model) || null;
21
+ }
22
+ return normalizeModelId(model) || null;
23
+ };
24
+ const CLAUDE_IMAGE_EXTENSIONS = {
25
+ '.png': 'image/png',
26
+ '.jpg': 'image/jpeg',
27
+ '.jpeg': 'image/jpeg',
28
+ '.webp': 'image/webp',
29
+ '.gif': 'image/gif',
30
+ };
31
+ const buildClaudeStdinMessage = (prompt, files, cwd) => {
32
+ if (files.length === 0) {
33
+ return JSON.stringify({ type: 'user', message: { role: 'user', content: prompt } });
34
+ }
35
+ const content = [{ type: 'text', text: prompt }];
36
+ for (const filePath of files) {
37
+ const absolutePath = isAbsolute(filePath) ? filePath : join(cwd, filePath);
38
+ const ext = extname(absolutePath).toLowerCase();
39
+ const mediaType = CLAUDE_IMAGE_EXTENSIONS[ext];
40
+ if (!mediaType) {
41
+ continue;
42
+ }
43
+ const data = readFileSync(absolutePath).toString('base64');
44
+ content.push({
45
+ type: 'image',
46
+ source: { type: 'base64', media_type: mediaType, data },
47
+ });
48
+ }
49
+ return JSON.stringify({ type: 'user', message: { role: 'user', content } });
50
+ };
51
+ const writeClaudeStdin = (child, message) => {
52
+ const stdin = child.stdin;
53
+ if (!stdin) {
54
+ return;
55
+ }
56
+ try {
57
+ stdin.end(message);
58
+ }
59
+ catch {
60
+ // Best-effort: the child may have exited already.
61
+ }
62
+ };
63
+ const buildClaudeArgs = (mode, model, resumeSessionId, sessionId) => {
64
+ const baseArgs = mode === 'plan'
65
+ ? [
66
+ '--permission-mode',
67
+ 'plan',
68
+ '--disallowedTools',
69
+ PLAN_MODE_QUESTION_TOOL_NAME,
70
+ ]
71
+ : ['--dangerously-skip-permissions'];
72
+ return [
73
+ ...baseArgs,
74
+ '--print',
75
+ '--verbose',
76
+ '--output-format',
77
+ 'stream-json',
78
+ '--include-partial-messages',
79
+ '--input-format',
80
+ 'stream-json',
81
+ ...(model ? ['--model', model] : []),
82
+ resumeSessionId ? '--resume' : '--session-id',
83
+ sessionId,
84
+ ];
85
+ };
17
86
  const OPENCODE_SESSION_BY_ID_QUERY = 'SELECT id, title FROM session WHERE id = ? LIMIT 1';
18
87
  const OPENCODE_SESSION_BY_CREATED_AT_QUERY = 'SELECT id, title FROM session WHERE time_created > ? ORDER BY time_created DESC LIMIT 1';
19
88
  const OPENCODE_COMPLETED_ASSISTANT_MESSAGE_USAGE_QUERY = "SELECT data FROM message WHERE session_id = ? AND json_extract(data, '$.role') = 'assistant' AND json_extract(data, '$.time.completed') IS NOT NULL ORDER BY time_created DESC LIMIT 1";
@@ -101,6 +170,8 @@ const extractChatUsageSnapshot = (value) => {
101
170
  ['result', 'usage', 'totalTokens'],
102
171
  ]),
103
172
  usage_snapshot_input_tokens: getNumberAtPaths(value, [
173
+ ['message', 'usage', 'input_tokens'],
174
+ ['usage', 'input_tokens'],
104
175
  ['tokens', 'input'],
105
176
  ['usage', 'tokens', 'input'],
106
177
  ['usage', 'input'],
@@ -111,6 +182,8 @@ const extractChatUsageSnapshot = (value) => {
111
182
  ['result', 'usage', 'inputTokens'],
112
183
  ]),
113
184
  usage_snapshot_output_tokens: getNumberAtPaths(value, [
185
+ ['message', 'usage', 'output_tokens'],
186
+ ['usage', 'output_tokens'],
114
187
  ['tokens', 'output'],
115
188
  ['usage', 'tokens', 'output'],
116
189
  ['usage', 'output'],
@@ -121,6 +194,8 @@ const extractChatUsageSnapshot = (value) => {
121
194
  ['result', 'usage', 'outputTokens'],
122
195
  ]),
123
196
  usage_snapshot_cache_read_tokens: getNumberAtPaths(value, [
197
+ ['message', 'usage', 'cache_read_input_tokens'],
198
+ ['usage', 'cache_read_input_tokens'],
124
199
  ['tokens', 'cache', 'read'],
125
200
  ['usage', 'tokens', 'cacheRead'],
126
201
  ['usage', 'cacheRead'],
@@ -141,6 +216,7 @@ const extractChatUsageSnapshot = (value) => {
141
216
  ['result', 'usage', 'reasoningTokens'],
142
217
  ]),
143
218
  usage_snapshot_cost: getNumberAtPaths(value, [
219
+ ['total_cost_usd'],
144
220
  ['cost'],
145
221
  ['result', 'cost'],
146
222
  ]),
@@ -161,6 +237,12 @@ const mergeUsageFromJsonLine = (current, line) => {
161
237
  continue;
162
238
  }
163
239
  }
240
+ if (next.usage_snapshot_total_tokens === null && next.usage_snapshot_input_tokens !== null && next.usage_snapshot_output_tokens !== null) {
241
+ next = {
242
+ ...next,
243
+ usage_snapshot_total_tokens: next.usage_snapshot_input_tokens + next.usage_snapshot_output_tokens,
244
+ };
245
+ }
164
246
  return next;
165
247
  };
166
248
  const hasUsageSnapshot = (value) => {
@@ -1162,7 +1244,7 @@ export const stopSpecReviewAgent = async (specReviewRunId) => {
1162
1244
  return true;
1163
1245
  };
1164
1246
  export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = null, resumeSessionId = null, sessionField = 'session_id', trackBuildProcess = false, emitSuccessUpdate = true, opencodeAgentMode, }) => {
1165
- const resolvedModel = normalizeModelId(model);
1247
+ const resolvedModel = resolveAgentModel(tool, model);
1166
1248
  return new Promise((resolve, reject) => {
1167
1249
  let sessionId = null;
1168
1250
  let spawnTimestamp = null;
@@ -1211,30 +1293,7 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
1211
1293
  sessionId = resumeSessionId || randomUUID();
1212
1294
  return [
1213
1295
  'claude',
1214
- opencodeAgentMode === 'plan'
1215
- ? [
1216
- '--permission-mode',
1217
- 'plan',
1218
- '--disallowedTools',
1219
- PLAN_MODE_QUESTION_TOOL_NAME,
1220
- '--print',
1221
- '--verbose',
1222
- '--output-format',
1223
- 'stream-json',
1224
- resumeSessionId ? '--resume' : '--session-id',
1225
- sessionId,
1226
- prompt,
1227
- ]
1228
- : [
1229
- '--dangerously-skip-permissions',
1230
- '--print',
1231
- '--verbose',
1232
- '--output-format',
1233
- 'stream-json',
1234
- resumeSessionId ? '--resume' : '--session-id',
1235
- sessionId,
1236
- prompt,
1237
- ]
1296
+ buildClaudeArgs(opencodeAgentMode, resolvedModel, resumeSessionId, sessionId),
1238
1297
  ];
1239
1298
  })()
1240
1299
  : ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), prompt]];
@@ -1247,10 +1306,13 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
1247
1306
  }
1248
1307
  const child = spawn(cmd, args, {
1249
1308
  cwd,
1250
- stdio: ['ignore', 'pipe', 'pipe'],
1309
+ stdio: tool === 'claude' ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
1251
1310
  env: { ...process.env, PWD: cwd },
1252
1311
  detached: process.platform !== 'win32',
1253
1312
  });
1313
+ if (tool === 'claude') {
1314
+ writeClaudeStdin(child, buildClaudeStdinMessage(prompt, [], cwd));
1315
+ }
1254
1316
  if (trackBuildProcess && buildState) {
1255
1317
  buildState.child = child;
1256
1318
  activeBuildProcesses.set(ticketId, buildState);
@@ -1423,7 +1485,7 @@ export const runAgent = ({ ticketId, tool, prompt, cwd, io, model, variant = nul
1423
1485
  });
1424
1486
  };
1425
1487
  export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, opencodeAgentMode = 'build', }) => {
1426
- const resolvedModel = normalizeModelId(model);
1488
+ const resolvedModel = resolveAgentModel(tool, model);
1427
1489
  return new Promise((resolve, reject) => {
1428
1490
  let sessionId = null;
1429
1491
  let spawnTimestamp = null;
@@ -1453,30 +1515,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
1453
1515
  sessionId = resumeSessionId || randomUUID();
1454
1516
  return [
1455
1517
  'claude',
1456
- opencodeAgentMode === 'plan'
1457
- ? [
1458
- '--permission-mode',
1459
- 'plan',
1460
- '--disallowedTools',
1461
- PLAN_MODE_QUESTION_TOOL_NAME,
1462
- '--print',
1463
- '--verbose',
1464
- '--output-format',
1465
- 'stream-json',
1466
- resumeSessionId ? '--resume' : '--session-id',
1467
- sessionId,
1468
- prompt,
1469
- ]
1470
- : [
1471
- '--dangerously-skip-permissions',
1472
- '--print',
1473
- '--verbose',
1474
- '--output-format',
1475
- 'stream-json',
1476
- resumeSessionId ? '--resume' : '--session-id',
1477
- sessionId,
1478
- prompt,
1479
- ]
1518
+ buildClaudeArgs(opencodeAgentMode, resolvedModel, resumeSessionId, sessionId),
1480
1519
  ];
1481
1520
  })()
1482
1521
  : ['opencode', ['run', ...(opencodeAgentMode === 'plan' ? ['--agent', 'plan'] : []), '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
@@ -1502,12 +1541,15 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
1502
1541
  }
1503
1542
  const child = spawn(cmd, args, {
1504
1543
  cwd,
1505
- stdio: ['ignore', 'pipe', 'pipe'],
1544
+ stdio: tool === 'claude' ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
1506
1545
  env: { ...process.env, PWD: cwd },
1507
1546
  detached: process.platform !== 'win32',
1508
1547
  });
1509
1548
  chatState.child = child;
1510
1549
  activeChatProcesses.set(chatSessionId, chatState);
1550
+ if (tool === 'claude') {
1551
+ writeClaudeStdin(child, buildClaudeStdinMessage(prompt, files, cwd));
1552
+ }
1511
1553
  startTime = Date.now();
1512
1554
  if (tool === 'claude') {
1513
1555
  const logLines = [];
@@ -1748,7 +1790,7 @@ export const runChatAgent = ({ chatSessionId, tool, prompt, files = [], cwd, io,
1748
1790
  });
1749
1791
  };
1750
1792
  export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io, model, variant = null, resumeSessionId = null, }) => {
1751
- const resolvedModel = normalizeModelId(model);
1793
+ const resolvedModel = resolveAgentModel(tool, model);
1752
1794
  return new Promise((resolve, reject) => {
1753
1795
  let sessionId = null;
1754
1796
  let spawnTimestamp = null;
@@ -1778,19 +1820,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
1778
1820
  sessionId = resumeSessionId || randomUUID();
1779
1821
  return [
1780
1822
  'claude',
1781
- [
1782
- '--permission-mode',
1783
- 'plan',
1784
- '--disallowedTools',
1785
- PLAN_MODE_QUESTION_TOOL_NAME,
1786
- '--print',
1787
- '--verbose',
1788
- '--output-format',
1789
- 'stream-json',
1790
- resumeSessionId ? '--resume' : '--session-id',
1791
- sessionId,
1792
- prompt,
1793
- ],
1823
+ buildClaudeArgs('plan', resolvedModel, resumeSessionId, sessionId),
1794
1824
  ];
1795
1825
  })()
1796
1826
  : ['opencode', ['run', '--agent', 'plan', '--format', 'json', '--thinking', ...(resumeSessionId ? ['--session', resumeSessionId] : []), ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
@@ -1816,12 +1846,15 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
1816
1846
  }
1817
1847
  const child = spawn(cmd, args, {
1818
1848
  cwd,
1819
- stdio: ['ignore', 'pipe', 'pipe'],
1849
+ stdio: tool === 'claude' ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
1820
1850
  env: { ...process.env, PWD: cwd },
1821
1851
  detached: process.platform !== 'win32',
1822
1852
  });
1823
1853
  specState.child = child;
1824
1854
  activeSpecProcesses.set(specSessionId, specState);
1855
+ if (tool === 'claude') {
1856
+ writeClaudeStdin(child, buildClaudeStdinMessage(prompt, files, cwd));
1857
+ }
1825
1858
  startTime = Date.now();
1826
1859
  if (tool === 'claude') {
1827
1860
  const logLines = [];
@@ -2072,7 +2105,7 @@ export const runSpecAgent = ({ specSessionId, tool, prompt, files = [], cwd, io,
2072
2105
  * the spec_sessions row is never touched by this path.
2073
2106
  */
2074
2107
  export const runSpecReviewAgent = ({ specReviewRunId, tool, prompt, files = [], cwd, io, model, variant = null, }) => {
2075
- const resolvedModel = normalizeModelId(model);
2108
+ const resolvedModel = resolveAgentModel(tool, model);
2076
2109
  return new Promise((resolve, reject) => {
2077
2110
  let settled = false;
2078
2111
  let startTime = null;
@@ -2099,31 +2132,22 @@ export const runSpecReviewAgent = ({ specReviewRunId, tool, prompt, files = [],
2099
2132
  const [cmd, args] = tool === 'claude'
2100
2133
  ? [
2101
2134
  'claude',
2102
- [
2103
- '--permission-mode',
2104
- 'plan',
2105
- '--disallowedTools',
2106
- PLAN_MODE_QUESTION_TOOL_NAME,
2107
- '--print',
2108
- '--verbose',
2109
- '--output-format',
2110
- 'stream-json',
2111
- '--session-id',
2112
- randomUUID(),
2113
- prompt,
2114
- ],
2135
+ buildClaudeArgs('plan', resolvedModel, null, randomUUID()),
2115
2136
  ]
2116
2137
  : ['opencode', ['run', '--agent', 'plan', '--format', 'json', '--thinking', ...(resolvedModel ? ['--model', resolvedModel] : []), ...(variant ? ['--variant', variant] : []), ...files.flatMap(filePath => ['--file', filePath]), ...(files.length > 0 ? ['--'] : []), prompt]];
2117
2138
  updateSpecReviewRunStreaming(specReviewRunId, null);
2118
2139
  emitReviewUpdated();
2119
2140
  const child = spawn(cmd, args, {
2120
2141
  cwd,
2121
- stdio: ['ignore', 'pipe', 'pipe'],
2142
+ stdio: tool === 'claude' ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
2122
2143
  env: { ...process.env, PWD: cwd },
2123
2144
  detached: process.platform !== 'win32',
2124
2145
  });
2125
2146
  reviewState.child = child;
2126
2147
  activeSpecReviewProcesses.set(specReviewRunId, reviewState);
2148
+ if (tool === 'claude') {
2149
+ writeClaudeStdin(child, buildClaudeStdinMessage(prompt, files, cwd));
2150
+ }
2127
2151
  startTime = Date.now();
2128
2152
  if (tool === 'claude') {
2129
2153
  const logLines = [];