reasonix 0.34.1 → 0.36.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 (91) hide show
  1. package/dashboard/app.css +16 -14
  2. package/dashboard/dist/app.js +77 -18
  3. package/dashboard/dist/app.js.map +1 -1
  4. package/dist/cli/{chat-TD6GR3QK.js → chat-RGMYAOY2.js} +18 -17
  5. package/dist/cli/{chunk-SA4UGZPG.js → chunk-2MCYGFLK.js} +15 -10
  6. package/dist/cli/chunk-2MCYGFLK.js.map +1 -0
  7. package/dist/cli/{chunk-SW3CCXEV.js → chunk-4Q3GRJIU.js} +2 -2
  8. package/dist/cli/{chunk-5JXXEPDM.js → chunk-BHLHOS5Y.js} +8 -2
  9. package/dist/cli/chunk-BHLHOS5Y.js.map +1 -0
  10. package/dist/cli/{chunk-F3ILWP2L.js → chunk-BJ376EN3.js} +9 -8
  11. package/dist/cli/chunk-BJ376EN3.js.map +1 -0
  12. package/dist/cli/{chunk-2AWTGJ2C.js → chunk-CRPQUBP6.js} +26 -9
  13. package/dist/cli/{chunk-2AWTGJ2C.js.map → chunk-CRPQUBP6.js.map} +1 -1
  14. package/dist/cli/{chunk-EINEIIIW.js → chunk-EN4LAZW5.js} +563 -250
  15. package/dist/cli/chunk-EN4LAZW5.js.map +1 -0
  16. package/dist/cli/{chunk-OERAGRJX.js → chunk-IPCPEZWQ.js} +2 -2
  17. package/dist/cli/{chunk-U3V2ZQ5J.js → chunk-KJQIA4US.js} +6 -2
  18. package/dist/cli/chunk-KJQIA4US.js.map +1 -0
  19. package/dist/cli/{chunk-Q36KBLSU.js → chunk-MLXUGPJE.js} +292 -35
  20. package/dist/cli/chunk-MLXUGPJE.js.map +1 -0
  21. package/dist/cli/{chunk-SX6L4HZZ.js → chunk-QPNZWUZF.js} +53 -6
  22. package/dist/cli/chunk-QPNZWUZF.js.map +1 -0
  23. package/dist/cli/{chunk-LNTORE5K.js → chunk-QRUQ2BFT.js} +159 -40
  24. package/dist/cli/chunk-QRUQ2BFT.js.map +1 -0
  25. package/dist/cli/{chunk-KZHMKOJH.js → chunk-T52GAWPP.js} +25 -3
  26. package/dist/cli/chunk-T52GAWPP.js.map +1 -0
  27. package/dist/cli/{chunk-I6YIAK6C.js → chunk-UNMYFZPZ.js} +2 -2
  28. package/dist/cli/{update-4TJWRUIN.js → chunk-WJ3YX4PZ.js} +51 -12
  29. package/dist/cli/chunk-WJ3YX4PZ.js.map +1 -0
  30. package/dist/cli/{chunk-RXGEGA7K.js → chunk-XQIFIB3U.js} +18 -7
  31. package/dist/cli/{chunk-RXGEGA7K.js.map → chunk-XQIFIB3U.js.map} +1 -1
  32. package/dist/cli/{chunk-2EBODRRO.js → chunk-ZJR4QLXB.js} +5 -1
  33. package/dist/cli/{chunk-2EBODRRO.js.map → chunk-ZJR4QLXB.js.map} +1 -1
  34. package/dist/cli/{chunk-6TMHAK5D.js → chunk-ZU45XW3P.js} +2 -2
  35. package/dist/cli/code-KJB3WDU6.js +435 -0
  36. package/dist/cli/code-KJB3WDU6.js.map +1 -0
  37. package/dist/cli/{commands-MEZPSEHV.js → commands-FE2UDFBC.js} +3 -3
  38. package/dist/cli/{commit-CE4EFTUQ.js → commit-3IAGB22T.js} +5 -4
  39. package/dist/cli/commit-3IAGB22T.js.map +1 -0
  40. package/dist/cli/{doctor-YASM64X6.js → doctor-DKD34EFD.js} +7 -7
  41. package/dist/cli/index.js +35 -33
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/{mcp-LDFK5QJI.js → mcp-2RDEQST6.js} +2 -2
  44. package/dist/cli/{mcp-browse-FYHEITCM.js → mcp-browse-VM5GLRBQ.js} +2 -2
  45. package/dist/cli/{mcp-inspect-T2HBR22P.js → mcp-inspect-CWSVCZUQ.js} +3 -3
  46. package/dist/cli/{prompt-V47QKSAR.js → prompt-YEKXMNNV.js} +3 -3
  47. package/dist/cli/{replay-JEDLU7F2.js → replay-D7RT2DR7.js} +3 -3
  48. package/dist/cli/replay-D7RT2DR7.js.map +1 -0
  49. package/dist/cli/{run-NHD2RSTD.js → run-FK5UBIIM.js} +13 -12
  50. package/dist/cli/run-FK5UBIIM.js.map +1 -0
  51. package/dist/cli/{server-MC4A4WAJ.js → server-W4XJK4GX.js} +17 -17
  52. package/dist/cli/{server-MC4A4WAJ.js.map → server-W4XJK4GX.js.map} +1 -1
  53. package/dist/cli/{sessions-ZHWJEW4L.js → sessions-YZXWMIWW.js} +10 -10
  54. package/dist/cli/{setup-DK43MT47.js → setup-IIAJXHP4.js} +196 -130
  55. package/dist/cli/setup-IIAJXHP4.js.map +1 -0
  56. package/dist/cli/update-GUCWB4UN.js +13 -0
  57. package/dist/cli/update-GUCWB4UN.js.map +1 -0
  58. package/dist/cli/{version-O362UKPM.js → version-DWD6RLIU.js} +12 -12
  59. package/dist/index.d.ts +19 -2
  60. package/dist/index.js +543 -78
  61. package/dist/index.js.map +1 -1
  62. package/package.json +1 -1
  63. package/dist/cli/chunk-5JXXEPDM.js.map +0 -1
  64. package/dist/cli/chunk-EINEIIIW.js.map +0 -1
  65. package/dist/cli/chunk-F3ILWP2L.js.map +0 -1
  66. package/dist/cli/chunk-KZHMKOJH.js.map +0 -1
  67. package/dist/cli/chunk-LNTORE5K.js.map +0 -1
  68. package/dist/cli/chunk-Q36KBLSU.js.map +0 -1
  69. package/dist/cli/chunk-SA4UGZPG.js.map +0 -1
  70. package/dist/cli/chunk-SX6L4HZZ.js.map +0 -1
  71. package/dist/cli/chunk-U3V2ZQ5J.js.map +0 -1
  72. package/dist/cli/code-TGUOQBRJ.js +0 -153
  73. package/dist/cli/code-TGUOQBRJ.js.map +0 -1
  74. package/dist/cli/commit-CE4EFTUQ.js.map +0 -1
  75. package/dist/cli/replay-JEDLU7F2.js.map +0 -1
  76. package/dist/cli/run-NHD2RSTD.js.map +0 -1
  77. package/dist/cli/setup-DK43MT47.js.map +0 -1
  78. package/dist/cli/update-4TJWRUIN.js.map +0 -1
  79. /package/dist/cli/{chat-TD6GR3QK.js.map → chat-RGMYAOY2.js.map} +0 -0
  80. /package/dist/cli/{chunk-SW3CCXEV.js.map → chunk-4Q3GRJIU.js.map} +0 -0
  81. /package/dist/cli/{chunk-OERAGRJX.js.map → chunk-IPCPEZWQ.js.map} +0 -0
  82. /package/dist/cli/{chunk-I6YIAK6C.js.map → chunk-UNMYFZPZ.js.map} +0 -0
  83. /package/dist/cli/{chunk-6TMHAK5D.js.map → chunk-ZU45XW3P.js.map} +0 -0
  84. /package/dist/cli/{commands-MEZPSEHV.js.map → commands-FE2UDFBC.js.map} +0 -0
  85. /package/dist/cli/{doctor-YASM64X6.js.map → doctor-DKD34EFD.js.map} +0 -0
  86. /package/dist/cli/{mcp-LDFK5QJI.js.map → mcp-2RDEQST6.js.map} +0 -0
  87. /package/dist/cli/{mcp-browse-FYHEITCM.js.map → mcp-browse-VM5GLRBQ.js.map} +0 -0
  88. /package/dist/cli/{mcp-inspect-T2HBR22P.js.map → mcp-inspect-CWSVCZUQ.js.map} +0 -0
  89. /package/dist/cli/{prompt-V47QKSAR.js.map → prompt-YEKXMNNV.js.map} +0 -0
  90. /package/dist/cli/{sessions-ZHWJEW4L.js.map → sessions-YZXWMIWW.js.map} +0 -0
  91. /package/dist/cli/{version-O362UKPM.js.map → version-DWD6RLIU.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/replay.ts","../../src/cli/ui/ReplayApp.tsx","../../src/cli/ui/StatsPanel.tsx"],"sourcesContent":["import { render } from \"ink\";\nimport React from \"react\";\nimport type { TranscriptRecord } from \"../../transcript/log.js\";\nimport { groupRecordsByTurn, replayFromFile } from \"../../transcript/replay.js\";\nimport { ReplayApp } from \"../ui/ReplayApp.js\";\n\nexport interface ReplayOptions {\n path: string;\n head?: number;\n tail?: number;\n /** Force stdout pretty-print mode (no Ink TUI). Also auto-enabled when stdout is not a TTY. */\n print?: boolean;\n}\n\nexport async function replayCommand(opts: ReplayOptions): Promise<void> {\n const wantPrint =\n opts.print || !process.stdout.isTTY || opts.head !== undefined || opts.tail !== undefined;\n if (wantPrint) {\n printReplay(opts);\n return;\n }\n\n const { parsed } = replayFromFile(opts.path);\n const pages = groupRecordsByTurn(parsed.records);\n const { waitUntilExit } = render(React.createElement(ReplayApp, { meta: parsed.meta, pages }), {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n await waitUntilExit();\n}\n\n// stdout pretty-print path (original behavior, preserved for piping / CI)\n\nfunction printReplay(opts: ReplayOptions): void {\n const { parsed, stats } = replayFromFile(opts.path);\n\n if (parsed.meta) {\n const m = parsed.meta;\n const bits: string[] = [`source=${m.source}`];\n if (m.model) bits.push(`model=${m.model}`);\n if (m.task) bits.push(`task=${m.task}`);\n if (m.mode) bits.push(`mode=${m.mode}`);\n if (m.repeat !== undefined) bits.push(`repeat=${m.repeat}`);\n bits.push(`started=${m.startedAt}`);\n console.log(`[meta] ${bits.join(\" \")}`);\n console.log(\"\");\n }\n\n const records = sliceRecords(parsed.records, opts);\n for (const rec of records) {\n renderRecord(rec);\n }\n\n console.log(\"\");\n console.log(\"── summary ─────────────────────────────────────────\");\n console.log(`model calls: ${stats.turns}`);\n console.log(`user turns: ${stats.userTurns}`);\n console.log(`tool calls: ${stats.toolCalls}`);\n console.log(`cache hit: ${(stats.cacheHitRatio * 100).toFixed(1)}%`);\n console.log(`cost: $${stats.totalCostUsd.toFixed(6)}`);\n console.log(`claude equivalent: $${stats.claudeEquivalentUsd.toFixed(6)}`);\n console.log(`savings vs claude: ${stats.savingsVsClaudePct.toFixed(1)}%`);\n console.log(`models: ${stats.models.join(\", \") || \"—\"}`);\n console.log(`prefix hashes: ${stats.prefixHashes.length} distinct`);\n if (stats.prefixHashes.length === 1) {\n console.log(` (byte-stable prefix: ${stats.prefixHashes[0]?.slice(0, 16)}…)`);\n } else if (stats.prefixHashes.length > 1) {\n console.log(\" (prefix churned — cache-hostile session)\");\n }\n}\n\nfunction sliceRecords(records: TranscriptRecord[], opts: ReplayOptions): TranscriptRecord[] {\n if (opts.head !== undefined && opts.head > 0) return records.slice(0, opts.head);\n if (opts.tail !== undefined && opts.tail > 0) return records.slice(-opts.tail);\n return records;\n}\n\nfunction renderRecord(rec: TranscriptRecord): void {\n const turn = `[t${rec.turn}]`;\n if (rec.role === \"user\") {\n console.log(`${turn} USER: ${oneLine(rec.content)}`);\n } else if (rec.role === \"assistant_final\") {\n const cost = rec.cost !== undefined ? ` $${rec.cost.toFixed(6)}` : \"\";\n const cache =\n rec.usage &&\n (rec.usage.prompt_cache_hit_tokens !== undefined ||\n rec.usage.prompt_cache_miss_tokens !== undefined)\n ? (() => {\n const hit = rec.usage!.prompt_cache_hit_tokens ?? 0;\n const miss = rec.usage!.prompt_cache_miss_tokens ?? 0;\n const total = hit + miss;\n return total > 0 ? ` cache=${((hit / total) * 100).toFixed(1)}%` : \"\";\n })()\n : \"\";\n console.log(`${turn} AGENT:${cost}${cache} ${oneLine(rec.content)}`);\n } else if (rec.role === \"tool\") {\n const args = rec.args ? ` args=${oneLine(rec.args, 80)}` : \"\";\n console.log(`${turn} TOOL ${rec.tool ?? \"?\"}:${args} → ${oneLine(rec.content, 120)}`);\n } else if (rec.role === \"error\") {\n console.log(`${turn} ERROR: ${rec.error ?? rec.content}`);\n } else if (rec.role === \"done\") {\n // Suppress — visually noisy, not informative in replay.\n } else {\n console.log(`${turn} ${rec.role}: ${oneLine(rec.content)}`);\n }\n}\n\nfunction oneLine(s: string, max = 200): string {\n const collapsed = s.replace(/\\s+/g, \" \").trim();\n return collapsed.length > max ? `${collapsed.slice(0, max)}…` : collapsed;\n}\n","/**\n * Ink TUI for `reasonix replay`. Read-only: no input box, no loop.\n * j/k navigation across turn-pages, cumulative stats sidebar updates\n * as you move through time.\n *\n * The navigation logic (grouping records into pages, computing cumulative\n * stats) lives in src/replay.ts as pure functions; this file is just\n * presentation + key bindings.\n */\n\nimport { Box, Static, Text, useApp, useInput } from \"ink\";\nimport React, { useMemo, useState } from \"react\";\nimport type { TranscriptMeta } from \"../../transcript/log.js\";\nimport { type TurnPage, computeCumulativeStats } from \"../../transcript/replay.js\";\nimport { RecordView } from \"./RecordView.js\";\nimport { StatsPanel } from \"./StatsPanel.js\";\n\nexport interface ReplayAppProps {\n meta: TranscriptMeta | null;\n pages: TurnPage[];\n}\n\nexport function ReplayApp({ meta, pages }: ReplayAppProps) {\n const { exit } = useApp();\n const maxIdx = Math.max(0, pages.length - 1);\n // Start at the last page — more useful than \"start from the beginning\"\n // in practice: users mostly want to see the summary + last turn first.\n const [idx, setIdx] = useState(maxIdx);\n\n useInput((input, key) => {\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n exit();\n return;\n }\n if (input === \"j\" || key.downArrow || input === \" \" || key.return) {\n setIdx((i) => Math.min(maxIdx, i + 1));\n } else if (input === \"k\" || key.upArrow) {\n setIdx((i) => Math.max(0, i - 1));\n } else if (input === \"g\") {\n setIdx(0);\n } else if (input === \"G\") {\n setIdx(maxIdx);\n } else if (input === \"h\" || key.leftArrow) {\n setIdx(0);\n } else if (input === \"l\" || key.rightArrow) {\n setIdx(maxIdx);\n }\n });\n\n const cumStats = useMemo(() => computeCumulativeStats(pages, idx), [pages, idx]);\n\n const summary = {\n turns: cumStats.turns,\n totalCostUsd: cumStats.totalCostUsd,\n totalInputCostUsd: cumStats.totalInputCostUsd,\n totalOutputCostUsd: cumStats.totalOutputCostUsd,\n claudeEquivalentUsd: cumStats.claudeEquivalentUsd,\n savingsVsClaudePct: cumStats.savingsVsClaudePct,\n cacheHitRatio: cumStats.cacheHitRatio,\n // Replay is read-only — no live last-turn prompt tokens to show.\n lastPromptTokens: 0,\n lastTurnCostUsd: 0,\n };\n\n const prefixHash =\n cumStats.prefixHashes.length === 1\n ? cumStats.prefixHashes[0]!.slice(0, 16)\n : cumStats.prefixHashes.length === 0\n ? \"(untracked)\"\n : `(churned ×${cumStats.prefixHashes.length})`;\n\n const currentPage = pages[idx];\n const progressLabel =\n pages.length === 0 ? \"empty transcript\" : `turn ${idx + 1} / ${pages.length}`;\n\n return (\n <Box flexDirection=\"column\">\n <StatsPanel summary={summary} />\n\n <Box flexDirection=\"column\" marginTop={1} paddingX={1}>\n <Box justifyContent=\"space-between\">\n <Text color=\"cyan\" bold>\n {progressLabel}\n </Text>\n {meta ? (\n <Text dimColor>\n {meta.source}\n {meta.task ? ` · ${meta.task}` : \"\"}\n {meta.mode ? ` · ${meta.mode}` : \"\"}\n </Text>\n ) : null}\n </Box>\n\n {currentPage ? (\n <Static items={currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec }))}>\n {({ key, rec }) => <RecordView key={key} rec={rec} />}\n </Static>\n ) : (\n <Text dimColor italic>\n no records\n </Text>\n )}\n </Box>\n\n <Box marginTop={1} paddingX={1} borderStyle=\"single\" borderColor=\"gray\">\n <Text dimColor>\n <Text bold>j</Text>/<Text bold>↓</Text>/<Text bold>space</Text> next · <Text bold>k</Text>\n /<Text bold>↑</Text> prev · <Text bold>g</Text> first · <Text bold>G</Text> last ·{\" \"}\n <Text bold>q</Text> quit\n </Text>\n </Box>\n </Box>\n );\n}\n","import { basename } from \"node:path\";\nimport { Box, Text, useStdout } from \"ink\";\nimport React from \"react\";\nimport stringWidth from \"string-width\";\nimport type { EditMode } from \"../../config.js\";\nimport type { SessionSummary } from \"../../telemetry/stats.js\";\nimport { Bar, ChromeRule } from \"./primitives.js\";\nimport { COLOR, GRADIENT } from \"./theme.js\";\nimport { formatBalance, formatCost } from \"./theme/tokens.js\";\n\nconst COLD_START_TURNS = 3;\n\nexport interface StatsPanelProps {\n summary: SessionSummary;\n planMode?: boolean;\n editMode?: EditMode;\n balance?: { currency: string; total: number } | null;\n updateAvailable?: string | null;\n proArmed?: boolean;\n escalated?: boolean;\n budgetUsd?: number | null;\n rootDir?: string;\n sessionName?: string | null;\n}\n\nexport function StatsPanel({\n summary,\n planMode,\n editMode,\n balance,\n updateAvailable,\n proArmed,\n escalated,\n budgetUsd,\n rootDir,\n sessionName,\n}: StatsPanelProps) {\n const coldStart = summary.turns <= COLD_START_TURNS;\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <ChromeRow\n editMode={editMode}\n planMode={planMode}\n proArmed={proArmed ?? false}\n escalated={escalated ?? false}\n summary={summary}\n coldStart={coldStart}\n rootDir={rootDir}\n sessionName={sessionName ?? null}\n updateAvailable={updateAvailable}\n balance={balance ?? null}\n />\n <ChromeRule />\n {budgetUsd !== null && budgetUsd !== undefined ? (\n <BudgetRow spent={summary.totalCostUsd} cap={budgetUsd} />\n ) : null}\n </Box>\n );\n}\n\nfunction ChromeRow({\n editMode,\n planMode,\n proArmed,\n escalated,\n summary,\n coldStart,\n rootDir,\n sessionName,\n updateAvailable,\n balance,\n}: {\n editMode?: EditMode;\n planMode?: boolean;\n proArmed: boolean;\n escalated: boolean;\n summary: SessionSummary;\n coldStart: boolean;\n rootDir?: string;\n sessionName?: string | null;\n updateAvailable?: string | null;\n balance?: { currency: string; total: number } | null;\n}) {\n const modePill = pickModePill(planMode, editMode);\n const proPill = escalated\n ? { label: \"⇧ pro\", color: COLOR.err }\n : proArmed\n ? { label: \"⇧ pro\", color: COLOR.warn }\n : null;\n const projectName = rootDir ? basename(rootDir) : null;\n const cachePct = Math.round(summary.cacheHitRatio * 100);\n const cacheColor =\n summary.cacheHitRatio >= 0.7 ? COLOR.ok : summary.cacheHitRatio >= 0.4 ? COLOR.warn : COLOR.err;\n const balanceLabel = balance\n ? `[${formatBalance(balance.total, balance.currency, { label: true })}]`\n : \"\";\n const costLabel = `[${formatCost(summary.totalCostUsd, balance?.currency)}]`;\n const cacheLabel = \"[c ▰▰▰▰▰▰ 100%]\";\n const updateLabel = updateAvailable ? `↑ ${updateAvailable}` : \"\";\n\n // Greedy width-aware fit. Layout (every gap = 2 cells, applied as suffix\n // to update/mode/pro and as prefix to balance/cache):\n // [brand][·project][›session]<spacer>[update][mode][pro][cost][balance][cache]\n // Always shown: brand, project (if rootDir), mode (if set), pro (if armed),\n // cost. These carve fixedLeft / fixedRight first.\n // Optional, dropped greedy by priority: balance > cache > session > update.\n // The flexbox spacer can shrink to 0, so no minimum reserve.\n const { stdout } = useStdout();\n const cols = (stdout?.columns ?? 80) - 2; // subtract paddingX={1} on both sides\n const SEP_DOT = stringWidth(\" · \");\n const SEP_ARROW = stringWidth(\" › \");\n const GAP = 2;\n\n const fixedLeft =\n stringWidth(\"◈ reasonix\") + (projectName ? SEP_DOT + stringWidth(projectName) : 0);\n const modeW = modePill ? GAP + stringWidth(`[${modePill.label}]`) : 0;\n const proW = proPill ? GAP + stringWidth(`[${proPill.label}]`) : 0;\n const fixedRight = modeW + proW + stringWidth(costLabel);\n let budget = cols - fixedLeft - fixedRight;\n\n const balW = balance ? GAP + stringWidth(balanceLabel) : 0;\n const cacheW = GAP + stringWidth(cacheLabel);\n const sessionW = sessionName ? SEP_ARROW + stringWidth(sessionName) : 0;\n const updateW = updateLabel ? GAP + stringWidth(updateLabel) : 0;\n\n const showBalance = balW > 0 && budget >= balW;\n if (showBalance) budget -= balW;\n const showCache = budget >= cacheW;\n if (showCache) budget -= cacheW;\n const showSession = sessionW > 0 && budget >= sessionW;\n if (showSession) budget -= sessionW;\n const showUpdate = updateW > 0 && budget >= updateW;\n if (showUpdate) budget -= updateW;\n\n return (\n <Box>\n <Text bold color={GRADIENT[0]}>\n {\"◈ \"}\n </Text>\n <Text color={COLOR.brand} bold>\n reasonix\n </Text>\n {projectName ? (\n <>\n <Text color={COLOR.info} dimColor>\n {\" · \"}\n </Text>\n <Text>{projectName}</Text>\n {showSession && sessionName ? (\n <>\n <Text color={COLOR.info} dimColor>\n {\" › \"}\n </Text>\n <Text color={COLOR.info}>{sessionName}</Text>\n </>\n ) : null}\n </>\n ) : null}\n\n <Box flexGrow={1} />\n\n {showUpdate ? (\n <>\n <Text color={COLOR.warn} bold>\n {updateLabel}\n </Text>\n <Text>{\" \"}</Text>\n </>\n ) : null}\n {modePill ? (\n <>\n <Text color={modePill.color} bold>\n {`[${modePill.label}]`}\n </Text>\n <Text>{\" \"}</Text>\n </>\n ) : null}\n {proPill ? (\n <>\n <Text color={proPill.color} bold>\n {`[${proPill.label}]`}\n </Text>\n <Text>{\" \"}</Text>\n </>\n ) : null}\n <Text\n color={\n summary.turns === 0 || coldStart ? COLOR.info : sessionCostColor(summary.totalCostUsd)\n }\n bold={summary.turns > 0 && !coldStart}\n dimColor={summary.turns === 0 || coldStart}\n >\n {costLabel}\n </Text>\n {showBalance && balance ? (\n <>\n <Text>{\" \"}</Text>\n <Text color={balance.total < 1 ? COLOR.err : balance.total < 5 ? COLOR.warn : COLOR.ok}>\n {balanceLabel}\n </Text>\n </>\n ) : null}\n {showCache ? (\n <>\n <Text>{\" \"}</Text>\n <Text dimColor>{\"[\"}</Text>\n <Text dimColor>{\"c \"}</Text>\n <Bar\n ratio={summary.cacheHitRatio}\n color={coldStart ? COLOR.info : cacheColor}\n cells={6}\n dim={coldStart}\n />\n <Text> </Text>\n <Text color={coldStart ? undefined : cacheColor} dimColor={coldStart}>\n {coldStart && summary.turns === 0 ? \"—\" : `${cachePct}%`}\n </Text>\n <Text dimColor>{\"]\"}</Text>\n </>\n ) : null}\n </Box>\n );\n}\n\nfunction pickModePill(\n planMode: boolean | undefined,\n editMode: EditMode | undefined,\n): { label: string; color: string } | null {\n if (planMode) return { label: \"PLAN\", color: COLOR.err };\n if (editMode === \"yolo\") return { label: \"yolo\", color: COLOR.err };\n if (editMode === \"auto\") return { label: \"auto\", color: COLOR.primary };\n if (editMode === \"review\") return { label: \"review\", color: COLOR.info };\n return null;\n}\n\nfunction BudgetRow({ spent, cap }: { spent: number; cap: number }) {\n const pct = Math.max(0, (spent / cap) * 100);\n const color = pct >= 100 ? \"#f87171\" : pct >= 80 ? \"#fbbf24\" : \"#94a3b8\";\n return (\n <Box>\n <Text dimColor>{\" budget \"}</Text>\n <Text color={color}>\n {`$${spent.toFixed(4)} / $${cap.toFixed(2)}`}\n <Text dimColor>{` (${pct.toFixed(0)}%)`}</Text>\n </Text>\n </Box>\n );\n}\n\nfunction sessionCostColor(cost: number): string | undefined {\n if (cost <= 0) return undefined;\n if (cost >= 5) return COLOR.err;\n if (cost >= 0.5) return COLOR.warn;\n return COLOR.ok;\n}\n"],"mappings":"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","names":["React","Box","Text","React","React","Box","Text","React"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/run.ts"],"sourcesContent":["import type { WriteStream } from \"node:fs\";\nimport { stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n defaultConfigPath,\n isPlausibleKey,\n loadApiKey,\n readConfig,\n saveApiKey,\n} from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { CacheFirstLoop, DeepSeekClient, ImmutablePrefix } from \"../../index.js\";\nimport { McpClient } from \"../../mcp/client.js\";\nimport { preflightStdioSpec } from \"../../mcp/preflight.js\";\nimport { bridgeMcpTools } from \"../../mcp/registry.js\";\nimport { parseMcpSpec } from \"../../mcp/spec.js\";\nimport { SseTransport } from \"../../mcp/sse.js\";\nimport { type McpTransport, StdioTransport } from \"../../mcp/stdio.js\";\nimport { StreamableHttpTransport } from \"../../mcp/streamable-http.js\";\nimport { appendUsage } from \"../../telemetry/usage.js\";\nimport { ToolRegistry } from \"../../tools.js\";\nimport { openTranscriptFile, recordFromLoopEvent, writeRecord } from \"../../transcript/log.js\";\nimport { formatMcpLifecycleEvent } from \"../ui/mcp-lifecycle.js\";\nimport { formatMcpSlowToast } from \"../ui/mcp-toast.js\";\n\nexport interface RunOptions {\n task: string;\n model: string;\n system: string;\n budgetUsd?: number;\n /** JSONL transcript path — lets `reasonix replay` / `diff` audit this run. */\n transcript?: string;\n /** Zero or more MCP server specs. Each: `\"name=cmd args...\"` or `\"cmd args...\"`. */\n mcp?: string[];\n /** Global prefix — only honored when a single anonymous server is given. */\n mcpPrefix?: string;\n}\n\nasync function ensureApiKey(): Promise<string> {\n const existing = loadApiKey();\n if (existing) return existing;\n\n if (!stdin.isTTY) {\n process.stderr.write(\n \"DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\\n\" +\n \"Set the env var, or run `reasonix chat` once interactively to save a key.\\n\",\n );\n process.exit(1);\n }\n\n process.stdout.write(\n \"DeepSeek API key not configured.\\nGet one at https://platform.deepseek.com/api_keys\\n\",\n );\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n while (true) {\n const answer = (await rl.question(\"API key › \")).trim();\n if (!answer) continue;\n if (!isPlausibleKey(answer)) {\n process.stdout.write(\"Invalid format. Keys start with 'sk-' and are 30+ chars.\\n\");\n continue;\n }\n saveApiKey(answer);\n process.stdout.write(`Saved to ${defaultConfigPath()}\\n\\n`);\n return answer;\n }\n } finally {\n rl.close();\n }\n}\n\nexport async function runCommand(opts: RunOptions): Promise<void> {\n loadDotenv();\n const apiKey = await ensureApiKey();\n process.env.DEEPSEEK_API_KEY = apiKey;\n\n // Optional MCP setup — mirrors chat's flow. Must happen before loop\n // construction so the tools make it into the prefix.\n const requestedSpecs = opts.mcp ?? [];\n const clients: McpClient[] = [];\n let tools: ToolRegistry | undefined;\n let successCount = 0;\n const disabledNames = new Set(readConfig().mcpDisabled ?? []);\n if (requestedSpecs.length > 0) {\n tools = new ToolRegistry();\n for (const raw of requestedSpecs) {\n let label = \"anon\";\n let mcp: McpClient | undefined;\n try {\n const spec = parseMcpSpec(raw);\n label = spec.name ?? \"anon\";\n if (spec.name && disabledNames.has(spec.name)) {\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"disabled\", name: label })}\\n`);\n continue;\n }\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"handshake\", name: label })}\\n`);\n const t0 = Date.now();\n const prefix = spec.name\n ? `${spec.name}_`\n : requestedSpecs.length === 1 && opts.mcpPrefix\n ? opts.mcpPrefix\n : \"\";\n if (spec.transport === \"stdio\") preflightStdioSpec(spec);\n const transport: McpTransport =\n spec.transport === \"sse\"\n ? new SseTransport({ url: spec.url })\n : spec.transport === \"streamable-http\"\n ? new StreamableHttpTransport({ url: spec.url })\n : new StdioTransport({ command: spec.command, args: spec.args });\n mcp = new McpClient({ transport });\n await mcp.initialize();\n const bridge = await bridgeMcpTools(mcp, {\n registry: tools,\n namePrefix: prefix,\n serverName: label,\n onSlow: (info) =>\n process.stderr.write(\n `${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}\\n`,\n ),\n });\n process.stderr.write(\n `${formatMcpLifecycleEvent({\n state: \"connected\",\n name: label,\n tools: bridge.registeredNames.length,\n ms: Date.now() - t0,\n })}\\n`,\n );\n clients.push(mcp);\n successCount++;\n } catch (err) {\n // Non-fatal — skip and continue, same as `reasonix chat`. A\n // one-shot `run` invocation with a broken MCP server otherwise\n // fails the whole run over a side-concern tool the task might\n // not even touch.\n await mcp?.close().catch(() => undefined);\n process.stderr.write(\n `${formatMcpLifecycleEvent({ state: \"failed\", name: label, reason: (err as Error).message })}\\n → run \\`reasonix setup\\` to remove broken entries from your saved config.\\n`,\n );\n }\n }\n if (successCount === 0) tools = undefined;\n }\n\n const client = new DeepSeekClient();\n const prefix = new ImmutablePrefix({\n system: opts.system,\n toolSpecs: tools?.specs(),\n });\n const loop = new CacheFirstLoop({\n client,\n prefix,\n tools,\n model: opts.model,\n budgetUsd: opts.budgetUsd,\n });\n const prefixHash = prefix.fingerprint;\n\n let transcriptStream: WriteStream | null = null;\n if (opts.transcript) {\n transcriptStream = openTranscriptFile(opts.transcript, {\n version: 1,\n source: \"reasonix run\",\n model: opts.model,\n startedAt: new Date().toISOString(),\n });\n // Also persist the user turn itself (the loop's event stream starts with\n // assistant output, not the prompt we're about to send).\n writeRecord(transcriptStream, {\n ts: new Date().toISOString(),\n turn: 1,\n role: \"user\",\n content: opts.task,\n });\n }\n\n try {\n for await (const ev of loop.step(opts.task)) {\n if (ev.role === \"assistant_delta\" && ev.content) process.stdout.write(ev.content);\n if (ev.role === \"tool\") process.stdout.write(`\\n[tool ${ev.toolName}] ${ev.content}\\n`);\n if (ev.role === \"error\") process.stderr.write(`\\n[error] ${ev.error}\\n`);\n if (ev.role === \"done\") process.stdout.write(\"\\n\");\n if (ev.role === \"assistant_final\" && ev.stats?.usage) {\n // `reasonix run` is often used in CI / scripting — we want\n // those turns to show up in `reasonix stats` too so the\n // dashboard reflects all DeepSeek spend, not just TUI sessions.\n appendUsage({ session: null, model: ev.stats.model, usage: ev.stats.usage });\n }\n // Persist every non-streaming event — deltas would flood the file and\n // aren't useful for replay (replay renders final content, not keystrokes).\n if (transcriptStream && ev.role !== \"assistant_delta\") {\n writeRecord(transcriptStream, recordFromLoopEvent(ev, { model: opts.model, prefixHash }));\n }\n }\n } finally {\n transcriptStream?.end();\n }\n\n const s = loop.stats.summary();\n process.stdout.write(\n `\\n— turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% ` +\n `cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%\\n`,\n );\n if (opts.transcript) {\n process.stdout.write(`\\ntranscript: ${opts.transcript}\\n`);\n process.stdout.write(` → npx reasonix replay ${opts.transcript}\\n`);\n }\n\n for (const c of clients) await c.close();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,OAAO,cAAc;AAC9B,SAAS,uBAAuB;AAoChC,eAAe,eAAgC;AAC7C,QAAM,WAAW,WAAW;AAC5B,MAAI,SAAU,QAAO;AAErB,MAAI,CAAC,MAAM,OAAO;AAChB,YAAQ,OAAO;AAAA,MACb;AAAA,IAEF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACA,QAAM,KAAK,gBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AAC3D,MAAI;AACF,WAAO,MAAM;AACX,YAAM,UAAU,MAAM,GAAG,SAAS,iBAAY,GAAG,KAAK;AACtD,UAAI,CAAC,OAAQ;AACb,UAAI,CAAC,eAAe,MAAM,GAAG;AAC3B,gBAAQ,OAAO,MAAM,4DAA4D;AACjF;AAAA,MACF;AACA,iBAAW,MAAM;AACjB,cAAQ,OAAO,MAAM,YAAY,kBAAkB,CAAC;AAAA;AAAA,CAAM;AAC1D,aAAO;AAAA,IACT;AAAA,EACF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAsB,WAAW,MAAiC;AAChE,aAAW;AACX,QAAM,SAAS,MAAM,aAAa;AAClC,UAAQ,IAAI,mBAAmB;AAI/B,QAAM,iBAAiB,KAAK,OAAO,CAAC;AACpC,QAAM,UAAuB,CAAC;AAC9B,MAAI;AACJ,MAAI,eAAe;AACnB,QAAM,gBAAgB,IAAI,IAAI,WAAW,EAAE,eAAe,CAAC,CAAC;AAC5D,MAAI,eAAe,SAAS,GAAG;AAC7B,YAAQ,IAAI,aAAa;AACzB,eAAW,OAAO,gBAAgB;AAChC,UAAI,QAAQ;AACZ,UAAI;AACJ,UAAI;AACF,cAAM,OAAO,aAAa,GAAG;AAC7B,gBAAQ,KAAK,QAAQ;AACrB,YAAI,KAAK,QAAQ,cAAc,IAAI,KAAK,IAAI,GAAG;AAC7C,kBAAQ,OAAO,MAAM,GAAG,wBAAwB,EAAE,OAAO,YAAY,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AACvF;AAAA,QACF;AACA,gBAAQ,OAAO,MAAM,GAAG,wBAAwB,EAAE,OAAO,aAAa,MAAM,MAAM,CAAC,CAAC;AAAA,CAAI;AACxF,cAAM,KAAK,KAAK,IAAI;AACpB,cAAMA,UAAS,KAAK,OAChB,GAAG,KAAK,IAAI,MACZ,eAAe,WAAW,KAAK,KAAK,YAClC,KAAK,YACL;AACN,YAAI,KAAK,cAAc,QAAS,oBAAmB,IAAI;AACvD,cAAM,YACJ,KAAK,cAAc,QACf,IAAI,aAAa,EAAE,KAAK,KAAK,IAAI,CAAC,IAClC,KAAK,cAAc,oBACjB,IAAI,wBAAwB,EAAE,KAAK,KAAK,IAAI,CAAC,IAC7C,IAAI,eAAe,EAAE,SAAS,KAAK,SAAS,MAAM,KAAK,KAAK,CAAC;AACrE,cAAM,IAAI,UAAU,EAAE,UAAU,CAAC;AACjC,cAAM,IAAI,WAAW;AACrB,cAAM,SAAS,MAAM,eAAe,KAAK;AAAA,UACvC,UAAU;AAAA,UACV,YAAYA;AAAA,UACZ,YAAY;AAAA,UACZ,QAAQ,CAAC,SACP,QAAQ,OAAO;AAAA,YACb,GAAG,mBAAmB,EAAE,MAAM,KAAK,YAAY,OAAO,KAAK,OAAO,YAAY,KAAK,WAAW,CAAC,CAAC;AAAA;AAAA,UAClG;AAAA,QACJ,CAAC;AACD,gBAAQ,OAAO;AAAA,UACb,GAAG,wBAAwB;AAAA,YACzB,OAAO;AAAA,YACP,MAAM;AAAA,YACN,OAAO,OAAO,gBAAgB;AAAA,YAC9B,IAAI,KAAK,IAAI,IAAI;AAAA,UACnB,CAAC,CAAC;AAAA;AAAA,QACJ;AACA,gBAAQ,KAAK,GAAG;AAChB;AAAA,MACF,SAAS,KAAK;AAKZ,cAAM,KAAK,MAAM,EAAE,MAAM,MAAM,MAAS;AACxC,gBAAQ,OAAO;AAAA,UACb,GAAG,wBAAwB,EAAE,OAAO,UAAU,MAAM,OAAO,QAAS,IAAc,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA,QAC9F;AAAA,MACF;AAAA,IACF;AACA,QAAI,iBAAiB,EAAG,SAAQ;AAAA,EAClC;AAEA,QAAM,SAAS,IAAI,eAAe;AAClC,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,QAAQ,KAAK;AAAA,IACb,WAAW,OAAO,MAAM;AAAA,EAC1B,CAAC;AACD,QAAM,OAAO,IAAI,eAAe;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK;AAAA,EAClB,CAAC;AACD,QAAM,aAAa,OAAO;AAE1B,MAAI,mBAAuC;AAC3C,MAAI,KAAK,YAAY;AACnB,uBAAmB,mBAAmB,KAAK,YAAY;AAAA,MACrD,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO,KAAK;AAAA,MACZ,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,CAAC;AAGD,gBAAY,kBAAkB;AAAA,MAC5B,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC3B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,MAAI;AACF,qBAAiB,MAAM,KAAK,KAAK,KAAK,IAAI,GAAG;AAC3C,UAAI,GAAG,SAAS,qBAAqB,GAAG,QAAS,SAAQ,OAAO,MAAM,GAAG,OAAO;AAChF,UAAI,GAAG,SAAS,OAAQ,SAAQ,OAAO,MAAM;AAAA,QAAW,GAAG,QAAQ,KAAK,GAAG,OAAO;AAAA,CAAI;AACtF,UAAI,GAAG,SAAS,QAAS,SAAQ,OAAO,MAAM;AAAA,UAAa,GAAG,KAAK;AAAA,CAAI;AACvE,UAAI,GAAG,SAAS,OAAQ,SAAQ,OAAO,MAAM,IAAI;AACjD,UAAI,GAAG,SAAS,qBAAqB,GAAG,OAAO,OAAO;AAIpD,oBAAY,EAAE,SAAS,MAAM,OAAO,GAAG,MAAM,OAAO,OAAO,GAAG,MAAM,MAAM,CAAC;AAAA,MAC7E;AAGA,UAAI,oBAAoB,GAAG,SAAS,mBAAmB;AACrD,oBAAY,kBAAkB,oBAAoB,IAAI,EAAE,OAAO,KAAK,OAAO,WAAW,CAAC,CAAC;AAAA,MAC1F;AAAA,IACF;AAAA,EACF,UAAE;AACA,sBAAkB,IAAI;AAAA,EACxB;AAEA,QAAM,IAAI,KAAK,MAAM,QAAQ;AAC7B,UAAQ,OAAO;AAAA,IACb;AAAA,eAAa,EAAE,KAAK,WAAW,EAAE,gBAAgB,KAAK,QAAQ,CAAC,CAAC,WACrD,EAAE,aAAa,QAAQ,CAAC,CAAC,mBAAmB,EAAE,mBAAmB,QAAQ,CAAC,CAAC;AAAA;AAAA,EACxF;AACA,MAAI,KAAK,YAAY;AACnB,YAAQ,OAAO,MAAM;AAAA,cAAiB,KAAK,UAAU;AAAA,CAAI;AACzD,YAAQ,OAAO,MAAM,gCAA2B,KAAK,UAAU;AAAA,CAAI;AAAA,EACrE;AAEA,aAAW,KAAK,QAAS,OAAM,EAAE,MAAM;AACzC;","names":["prefix"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/setup.tsx","../../src/cli/ui/Wizard.tsx"],"sourcesContent":["/**\n * `reasonix setup` — re-mount the first-run wizard on demand so users\n * can reconfigure (add/remove MCP servers, switch preset) without\n * editing JSON by hand.\n *\n * Invoked both explicitly (`reasonix setup`) and implicitly (the no-args\n * entry point when `setupCompleted` is false).\n */\n\nimport { render } from \"ink\";\nimport React from \"react\";\nimport { loadApiKey, readConfig } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { Wizard } from \"../ui/Wizard.js\";\n\nexport interface SetupOptions {\n /**\n * When true, bypass the API-key step even if no key is saved — useful\n * from test harnesses. Normal CLI use always pushes through the key\n * step when missing.\n */\n skipKeyStep?: boolean;\n}\n\nexport async function setupCommand(_opts: SetupOptions = {}): Promise<void> {\n loadDotenv();\n const existingKey = loadApiKey();\n const existing = readConfig();\n\n const { waitUntilExit, unmount } = render(\n <Wizard\n existingApiKey={existingKey}\n initial={{ preset: existing.preset, mcp: existing.mcp }}\n onComplete={() => {\n // Ink handles its own enter-to-exit inside the \"saved\" step; we\n // just wait for the app to exit naturally.\n }}\n onCancel={() => {\n unmount();\n }}\n />,\n { exitOnCtrlC: true, patchConsole: false },\n );\n await waitUntilExit();\n}\n","/**\n * First-run / re-configure wizard.\n *\n * Walks a new user through: language → API key → preset pick → MCP server\n * pick → per-server args → save. Saved output lives in\n * `~/.reasonix/config.json` so the next `reasonix chat` starts with\n * everything already wired.\n */\n\nimport { mkdirSync, statSync } from \"node:fs\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport TextInput from \"ink-text-input\";\n// biome-ignore lint/style/useImportType: JSX (jsx: \"react\") needs React as a value at runtime\nimport React, { useEffect, useState } from \"react\";\nimport {\n type PresetName,\n type ReasonixConfig,\n defaultConfigPath,\n isPlausibleKey,\n readConfig,\n redactKey,\n writeConfig,\n} from \"../../config.js\";\nimport {\n detectSystemLanguage,\n getLanguage,\n getSupportedLanguages,\n notifyLanguageChange,\n onLanguageChange,\n setLanguage,\n t,\n} from \"../../i18n/index.js\";\nimport type { LanguageCode } from \"../../i18n/types.js\";\nimport { type CatalogEntry, MCP_CATALOG } from \"../../mcp/catalog.js\";\nimport { MultiSelect, type SelectItem, SingleSelect } from \"./Select.js\";\nimport { PRESET_DESCRIPTIONS } from \"./presets.js\";\n\nexport interface WizardProps {\n /** Called once the config has been saved. */\n onComplete: (cfg: ReasonixConfig) => void;\n /** Called if the user presses Esc to abort. */\n onCancel?: () => void;\n /** Skip the API-key step if a key already exists (env or config). */\n existingApiKey?: string;\n /** Pre-fill selections when re-running (reconfigure flow). */\n initial?: {\n preset?: PresetName;\n mcp?: string[];\n };\n}\n\ntype Step = \"language\" | \"apiKey\" | \"preset\" | \"mcp\" | \"mcpArgs\" | \"review\" | \"saved\";\n\ninterface WizardData {\n language: LanguageCode;\n apiKey: string;\n preset: PresetName;\n selectedCatalog: string[];\n catalogArgs: Record<string, string>;\n}\n\nconst CATALOG_BY_NAME = new Map(MCP_CATALOG.map((e) => [e.name, e]));\n\nconst LANGUAGE_LABELS: Record<LanguageCode, string> = {\n EN: \"English\",\n \"zh-CN\": \"简体中文\",\n};\n\nexport function Wizard({ onComplete, onCancel, existingApiKey, initial }: WizardProps) {\n const { exit } = useApp();\n const [, setLanguageVersion] = useState(0);\n useEffect(() => onLanguageChange(() => setLanguageVersion((v) => v + 1)), []);\n\n const [step, setStep] = useState<Step>(\"language\");\n const [data, setData] = useState<WizardData>({\n language: getLanguage(),\n apiKey: existingApiKey ?? \"\",\n preset: initial?.preset ?? \"auto\",\n selectedCatalog: deriveInitialCatalog(initial?.mcp ?? []),\n catalogArgs: {},\n });\n const [error, setError] = useState<string | null>(null);\n\n useInput((_input, key) => {\n if (key.escape && step !== \"saved\" && onCancel) onCancel();\n });\n\n if (step === \"language\") {\n return (\n <LanguageStep\n initialValue={data.language}\n onSubmit={(lang) => {\n setLanguage(lang);\n notifyLanguageChange();\n setData((d) => ({ ...d, language: lang }));\n setStep(existingApiKey ? \"preset\" : \"apiKey\");\n }}\n />\n );\n }\n\n if (step === \"apiKey\") {\n return (\n <ApiKeyStep\n onSubmit={(key) => {\n setData((d) => ({ ...d, apiKey: key }));\n setError(null);\n setStep(\"preset\");\n }}\n error={error}\n onError={setError}\n />\n );\n }\n\n if (step === \"preset\") {\n return (\n <StepFrame title={t(\"wizard.presetTitle\")} step={1} total={3}>\n <SingleSelect<PresetName>\n items={presetItems()}\n initialValue={data.preset}\n onSubmit={(preset) => {\n setData((d) => ({ ...d, preset }));\n setStep(\"mcp\");\n }}\n />\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.selectFooter\")}</Text>\n </Box>\n </StepFrame>\n );\n }\n\n if (step === \"mcp\") {\n return (\n <StepFrame title={t(\"wizard.mcpTitle\")} step={2} total={3}>\n <MultiSelect\n items={mcpItems()}\n initialSelected={data.selectedCatalog}\n onSubmit={(selected) => {\n setData((d) => ({ ...d, selectedCatalog: selected }));\n const needsArgs = selected.some((name) => CATALOG_BY_NAME.get(name)?.userArgs);\n setStep(needsArgs ? \"mcpArgs\" : \"review\");\n }}\n footer={t(\"wizard.mcpFooterMulti\")}\n />\n </StepFrame>\n );\n }\n\n if (step === \"mcpArgs\") {\n const pending = data.selectedCatalog.filter((name) => {\n const entry = CATALOG_BY_NAME.get(name);\n return entry?.userArgs && !data.catalogArgs[name];\n });\n if (pending.length === 0) {\n setStep(\"review\");\n return null;\n }\n const currentName = pending[0]!;\n const entry = CATALOG_BY_NAME.get(currentName)!;\n return (\n <McpArgsStep\n entry={entry}\n error={error}\n onSubmit={(value) => {\n setData((d) => ({\n ...d,\n catalogArgs: { ...d.catalogArgs, [currentName]: value },\n }));\n setError(null);\n }}\n onError={setError}\n />\n );\n }\n\n if (step === \"review\") {\n const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));\n return (\n <StepFrame title={t(\"wizard.reviewTitle\")} step={3} total={3}>\n <Box flexDirection=\"column\">\n <SummaryLine\n label={t(\"wizard.reviewLabelLanguage\")}\n value={LANGUAGE_LABELS[data.language]}\n />\n <SummaryLine label={t(\"wizard.reviewLabelApiKey\")} value={redactKey(data.apiKey)} />\n <SummaryLine label={t(\"wizard.reviewLabelPreset\")} value={data.preset} />\n <SummaryLine\n label={t(\"wizard.reviewLabelMcp\")}\n value={\n specs.length === 0\n ? t(\"wizard.reviewMcpNone\")\n : t(\"wizard.reviewMcpServers\", { count: specs.length })\n }\n />\n {specs.map((spec, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed\n <Box key={i} paddingLeft={14}>\n <Text dimColor>· {spec}</Text>\n </Box>\n ))}\n <Box marginTop={1}>\n <Text>{t(\"wizard.reviewSavesTo\", { path: defaultConfigPath() })}</Text>\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.reviewFooter\")}</Text>\n </Box>\n </Box>\n <ReviewConfirm\n onConfirm={() => {\n try {\n const specsNow = data.selectedCatalog.map((name) =>\n buildSpec(name, data.catalogArgs),\n );\n const prev = readConfig();\n const next: ReasonixConfig = {\n ...prev,\n apiKey: data.apiKey,\n preset: data.preset,\n mcp: specsNow,\n setupCompleted: true,\n };\n writeConfig(next);\n setStep(\"saved\");\n onComplete(next);\n } catch (e) {\n setError(t(\"wizard.reviewSaveError\", { message: (e as Error).message }));\n }\n }}\n />\n </StepFrame>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"green\" paddingX={1}>\n <Text bold color=\"green\">\n {t(\"wizard.savedTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text>{t(\"ui.welcome\")}</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.savedFooter\")}</Text>\n </Box>\n <ExitOnEnter onExit={exit} />\n </Box>\n );\n}\n\n// ---------- step components ----------\n\nfunction LanguageStep({\n initialValue,\n onSubmit,\n}: {\n initialValue: LanguageCode;\n onSubmit: (lang: LanguageCode) => void;\n}) {\n const items: SelectItem<LanguageCode>[] = getSupportedLanguages().map((code) => ({\n value: code,\n label: LANGUAGE_LABELS[code],\n hint: code === detectSystemLanguage() ? \"(detected)\" : undefined,\n }));\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.languageTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.languageSubtitle\")}</Text>\n </Box>\n <Box marginTop={1}>\n <SingleSelect<LanguageCode>\n items={items}\n initialValue={initialValue}\n onSubmit={onSubmit}\n footer={t(\"wizard.selectFooter\")}\n />\n </Box>\n </Box>\n );\n}\n\nfunction ApiKeyStep({\n onSubmit,\n error,\n onError,\n}: {\n onSubmit: (key: string) => void;\n error: string | null;\n onError: (e: string | null) => void;\n}) {\n const [value, setValue] = useState(\"\");\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.welcomeTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text>{t(\"wizard.apiKeyPrompt\")}</Text>\n </Box>\n <Text dimColor>{t(\"wizard.apiKeyGetOne\")}</Text>\n <Text dimColor>{t(\"wizard.apiKeySavedLocally\", { path: defaultConfigPath() })}</Text>\n <Box marginTop={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.apiKeyInputLabel\")}\n </Text>\n <TextInput\n value={value}\n onChange={setValue}\n onSubmit={(raw) => {\n const trimmed = raw.trim();\n if (!isPlausibleKey(trimmed)) {\n onError(t(\"wizard.apiKeyInvalid\"));\n setValue(\"\");\n return;\n }\n onSubmit(trimmed);\n }}\n mask=\"•\"\n placeholder=\"sk-...\"\n />\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : value ? (\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.apiKeyPreview\", { redacted: redactKey(value) })}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction McpArgsStep({\n entry,\n error,\n onSubmit,\n onError,\n}: {\n entry: CatalogEntry;\n error: string | null;\n onSubmit: (value: string) => void;\n onError: (e: string | null) => void;\n}) {\n const [value, setValue] = useState(\"\");\n const [pendingCreate, setPendingCreate] = useState<string | null>(null);\n\n useInput((input, key) => {\n if (!pendingCreate) return;\n const ch = input.toLowerCase();\n if (ch === \"y\" || key.return) {\n try {\n mkdirSync(pendingCreate, { recursive: true });\n const created = pendingCreate;\n setPendingCreate(null);\n setValue(\"\");\n onError(null);\n onSubmit(created);\n } catch (e) {\n onError(\n t(\"wizard.mcpArgsDirCreateFailed\", {\n path: pendingCreate,\n message: (e as Error).message,\n }),\n );\n setPendingCreate(null);\n }\n } else if (ch === \"n\" || key.escape) {\n setPendingCreate(null);\n onError(null);\n }\n });\n\n if (pendingCreate) {\n return (\n <StepFrame title={t(\"wizard.mcpArgsTitle\", { name: entry.name })} step={2} total={3}>\n <Box flexDirection=\"column\">\n <Text>{t(\"wizard.mcpArgsDirMissing\", { path: pendingCreate })}</Text>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.mcpArgsDirCreateHint\")}</Text>\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n </Box>\n </StepFrame>\n );\n }\n\n return (\n <StepFrame title={t(\"wizard.mcpArgsTitle\", { name: entry.name })} step={2} total={3}>\n <Box flexDirection=\"column\">\n <Text>{entry.summary}</Text>\n {entry.note ? (\n <Box marginTop={1}>\n <Text dimColor>{entry.note}</Text>\n </Box>\n ) : null}\n <Box marginTop={1}>\n <Text>{t(\"wizard.mcpArgsRequiredParam\")}</Text>\n <Text bold>{entry.userArgs}</Text>\n </Box>\n <Box marginTop={1}>\n <Text bold color=\"cyan\">\n {entry.userArgs}\n {\" › \"}\n </Text>\n <TextInput\n value={value}\n onChange={setValue}\n onSubmit={(raw) => {\n const trimmed = raw.trim();\n if (!trimmed) {\n onError(t(\"wizard.mcpArgsEmpty\", { name: entry.name }));\n return;\n }\n if (entry.name === \"filesystem\") {\n const check = checkFilesystemPath(trimmed);\n if (check.kind === \"missing\") {\n setPendingCreate(trimmed);\n return;\n }\n if (check.kind === \"not-a-dir\") {\n onError(t(\"wizard.mcpArgsNotADir\", { path: trimmed }));\n return;\n }\n }\n onSubmit(trimmed);\n setValue(\"\");\n }}\n placeholder={placeholderFor(entry)}\n />\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n </Box>\n </StepFrame>\n );\n}\n\nfunction checkFilesystemPath(p: string): { kind: \"ok\" | \"missing\" | \"not-a-dir\" } {\n try {\n return { kind: statSync(p).isDirectory() ? \"ok\" : \"not-a-dir\" };\n } catch {\n return { kind: \"missing\" };\n }\n}\n\nfunction ReviewConfirm({ onConfirm }: { onConfirm: () => void }) {\n useInput((_i, key) => {\n if (key.return) onConfirm();\n });\n return null;\n}\n\nfunction ExitOnEnter({ onExit }: { onExit: () => void }) {\n useInput((_i, key) => {\n if (key.return) onExit();\n });\n return null;\n}\n\nfunction StepFrame({\n title,\n step,\n total,\n children,\n}: {\n title: string;\n step: number;\n total: number;\n children: React.ReactNode;\n}) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Box>\n <Text dimColor>{t(\"wizard.stepCounter\", { step, total })}</Text>\n <Text bold color=\"cyan\">\n {title}\n </Text>\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n {children}\n </Box>\n </Box>\n );\n}\n\nfunction SummaryLine({ label, value }: { label: string; value: string }) {\n return (\n <Box>\n <Text>{label.padEnd(12)}</Text>\n <Text bold>{value}</Text>\n </Box>\n );\n}\n\nfunction presetItems(): SelectItem<PresetName>[] {\n return ([\"auto\", \"flash\", \"pro\"] as const).map((name) => ({\n value: name as PresetName,\n label: `${name} — ${PRESET_DESCRIPTIONS[name].headline}`,\n hint: PRESET_DESCRIPTIONS[name].cost,\n }));\n}\n\nfunction mcpItems(): SelectItem<string>[] {\n return MCP_CATALOG.map((entry) => {\n const hintParts: string[] = [entry.summary];\n if (entry.userArgs) hintParts.push(t(\"wizard.mcpUserArgsHint\", { arg: entry.userArgs }));\n if (entry.note) hintParts.push(entry.note);\n return {\n value: entry.name,\n label: entry.name,\n hint: hintParts.join(\" · \"),\n };\n });\n}\n\nfunction placeholderFor(entry: CatalogEntry): string {\n if (entry.name === \"filesystem\") return \"e.g. /tmp/reasonix-sandbox\";\n if (entry.name === \"sqlite\") return \"e.g. ./notes.sqlite\";\n return entry.userArgs ?? \"\";\n}\n\nfunction deriveInitialCatalog(existingSpecs: string[]): string[] {\n const packageToName = new Map(MCP_CATALOG.map((e) => [e.package, e.name]));\n const out: string[] = [];\n for (const spec of existingSpecs) {\n for (const [pkg, name] of packageToName) {\n if (spec.includes(pkg)) {\n out.push(name);\n break;\n }\n }\n }\n return out;\n}\n\n/**\n * Build the `--mcp` spec string for a catalog entry. Same format\n * `mcpCommandFor` produces for `reasonix mcp list`, minus the leading\n * `--mcp \"...\"` wrapper — we store the inner spec directly.\n */\nexport function buildSpec(name: string, argsByName: Record<string, string>): string {\n const entry = CATALOG_BY_NAME.get(name);\n if (!entry) return name;\n const userArg = entry.userArgs ? argsByName[name] : undefined;\n const tail = userArg ? ` ${quoteIfNeeded(userArg)}` : \"\";\n return `${entry.name}=npx -y ${entry.package}${tail}`;\n}\n\nfunction quoteIfNeeded(s: string): string {\n // Escape backslashes BEFORE quotes — otherwise a trailing `\\` in the\n // input would consume the closing quote when a downstream parser\n // un-escapes the output (CodeQL js/incomplete-sanitization).\n return /\\s|\"/.test(s) ? `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"` : s;\n}\n"],"mappings":"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","names":["React","entry","React"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/update.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { VERSION, compareVersions, getLatestVersion, isNpxInstall } from \"../../version.js\";\n\nexport type UpdateAction = \"up-to-date\" | \"newer-local\" | \"npx-hint\" | \"run-npm-install\";\n\nexport interface UpdatePlan {\n action: UpdateAction;\n /** Human-readable summary; the CLI prints this verbatim. */\n message: string;\n command?: string[];\n}\n\nexport interface PlanUpdateInput {\n current: string;\n latest: string;\n /** Overrides `isNpxInstall()` (tests). */\n npx?: boolean;\n}\n\n/** Pure decision — split out so tests don't need to spawn child processes or hit the network. */\nexport function planUpdate(input: PlanUpdateInput): UpdatePlan {\n const diff = compareVersions(input.current, input.latest);\n if (diff > 0) {\n return {\n action: \"newer-local\",\n message: `current (${input.current}) is newer than the published ${input.latest} — nothing to do.`,\n };\n }\n if (diff === 0) {\n return { action: \"up-to-date\", message: `reasonix ${input.current} is up to date.` };\n }\n if (input.npx) {\n return {\n action: \"npx-hint\",\n message: [\n `reasonix ${input.latest} is available.`,\n \"you're running via npx — the next `npx reasonix ...` launch will auto-fetch\",\n \"the latest (npx caches packages for a short window). to force a refresh\",\n \"sooner, clear the cache: `npm cache clean --force`.\",\n ].join(\"\\n\"),\n };\n }\n return {\n action: \"run-npm-install\",\n message: `upgrading reasonix ${input.current} → ${input.latest}`,\n command: [\"npm\", \"install\", \"-g\", \"reasonix@latest\"],\n };\n}\n\nexport interface UpdateCommandOptions {\n /** Skip spawning npm; print the decision only. */\n dryRun?: boolean;\n /** Test seam: override the registry lookup. Returns null = offline. */\n fetchLatest?: () => Promise<string | null>;\n /** Test seam: override the npx detector. */\n isNpx?: () => boolean;\n /** Test seam: override the spawner. Must return exit code. */\n spawnInstall?: (argv: string[]) => Promise<number>;\n /** Test seam: stdout writer. */\n write?: (msg: string) => void;\n /** Test seam: process exit — tests don't want to tear down vitest. */\n exit?: (code: number) => void;\n}\n\nfunction defaultSpawn(argv: string[]): Promise<number> {\n return new Promise((resolve, reject) => {\n // `shell: true` on Windows is what lets `npm` resolve to `npm.cmd`\n // without routing through our `prepareSpawn` helper. The args here\n // are literal strings under our control — no user input flows in,\n // so injection is not a concern. Avoiding `prepareSpawn` keeps\n // this command free of a dep on the shell tools module.\n const child = spawn(argv[0]!, argv.slice(1), {\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => resolve(code ?? 1));\n });\n}\n\nexport async function updateCommand(opts: UpdateCommandOptions = {}): Promise<void> {\n const write = opts.write ?? ((m: string) => process.stdout.write(m));\n const exit = opts.exit ?? ((c: number) => process.exit(c));\n const fetchLatest = opts.fetchLatest ?? (() => getLatestVersion({ force: true }));\n const isNpx = opts.isNpx ?? isNpxInstall;\n const doSpawn = opts.spawnInstall ?? defaultSpawn;\n\n write(`current: reasonix ${VERSION}\\n`);\n const latest = await fetchLatest();\n if (!latest) {\n write(\"could not reach registry.npmjs.org — check your network.\\n\");\n exit(1);\n return;\n }\n write(`latest: reasonix ${latest}\\n`);\n\n const plan = planUpdate({ current: VERSION, latest, npx: isNpx() });\n write(`\\n${plan.message}\\n`);\n\n if (plan.action !== \"run-npm-install\" || !plan.command) return;\n if (opts.dryRun) {\n write(`(dry run) would run: ${plan.command.join(\" \")}\\n`);\n return;\n }\n write(`\\nrunning: ${plan.command.join(\" \")}\\n`);\n const code = await doSpawn(plan.command);\n if (code !== 0) {\n write(`\\nnpm exited with code ${code}. upgrade did not complete.\\n`);\n exit(code);\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,aAAa;AAoBf,SAAS,WAAW,OAAoC;AAC7D,QAAM,OAAO,gBAAgB,MAAM,SAAS,MAAM,MAAM;AACxD,MAAI,OAAO,GAAG;AACZ,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,YAAY,MAAM,OAAO,iCAAiC,MAAM,MAAM;AAAA,IACjF;AAAA,EACF;AACA,MAAI,SAAS,GAAG;AACd,WAAO,EAAE,QAAQ,cAAc,SAAS,YAAY,MAAM,OAAO,kBAAkB;AAAA,EACrF;AACA,MAAI,MAAM,KAAK;AACb,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,YAAY,MAAM,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,sBAAsB,MAAM,OAAO,WAAM,MAAM,MAAM;AAAA,IAC9D,SAAS,CAAC,OAAO,WAAW,MAAM,iBAAiB;AAAA,EACrD;AACF;AAiBA,SAAS,aAAa,MAAiC;AACrD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAMtC,UAAM,QAAQ,MAAM,KAAK,CAAC,GAAI,KAAK,MAAM,CAAC,GAAG;AAAA,MAC3C,OAAO;AAAA,MACP,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,KAAK,SAAS,MAAM;AAC1B,UAAM,KAAK,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EACjD,CAAC;AACH;AAEA,eAAsB,cAAc,OAA6B,CAAC,GAAkB;AAClF,QAAM,QAAQ,KAAK,UAAU,CAAC,MAAc,QAAQ,OAAO,MAAM,CAAC;AAClE,QAAM,OAAO,KAAK,SAAS,CAAC,MAAc,QAAQ,KAAK,CAAC;AACxD,QAAM,cAAc,KAAK,gBAAgB,MAAM,iBAAiB,EAAE,OAAO,KAAK,CAAC;AAC/E,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,UAAU,KAAK,gBAAgB;AAErC,QAAM,qBAAqB,OAAO;AAAA,CAAI;AACtC,QAAM,SAAS,MAAM,YAAY;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,iEAA4D;AAClE,SAAK,CAAC;AACN;AAAA,EACF;AACA,QAAM,qBAAqB,MAAM;AAAA,CAAI;AAErC,QAAM,OAAO,WAAW,EAAE,SAAS,SAAS,QAAQ,KAAK,MAAM,EAAE,CAAC;AAClE,QAAM;AAAA,EAAK,KAAK,OAAO;AAAA,CAAI;AAE3B,MAAI,KAAK,WAAW,qBAAqB,CAAC,KAAK,QAAS;AACxD,MAAI,KAAK,QAAQ;AACf,UAAM,wBAAwB,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,CAAI;AACxD;AAAA,EACF;AACA,QAAM;AAAA,WAAc,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,CAAI;AAC9C,QAAM,OAAO,MAAM,QAAQ,KAAK,OAAO;AACvC,MAAI,SAAS,GAAG;AACd,UAAM;AAAA,uBAA0B,IAAI;AAAA,CAA+B;AACnE,SAAK,IAAI;AAAA,EACX;AACF;","names":[]}