reasonix 0.32.0 → 0.33.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 (118) hide show
  1. package/dist/cli/chat-EIFLHBZ6.js +39 -0
  2. package/dist/cli/chunk-2AWTGJ2C.js +110 -0
  3. package/dist/cli/chunk-2AWTGJ2C.js.map +1 -0
  4. package/dist/cli/chunk-3Q3C4W66.js +30 -0
  5. package/dist/cli/chunk-3Q3C4W66.js.map +1 -0
  6. package/dist/cli/chunk-4DCHFFEY.js +149 -0
  7. package/dist/cli/chunk-4DCHFFEY.js.map +1 -0
  8. package/dist/cli/chunk-5X7LZJDE.js +36 -0
  9. package/dist/cli/chunk-5X7LZJDE.js.map +1 -0
  10. package/dist/cli/chunk-6TMHAK5D.js +576 -0
  11. package/dist/cli/chunk-6TMHAK5D.js.map +1 -0
  12. package/dist/cli/chunk-APPB3ZPQ.js +43 -0
  13. package/dist/cli/chunk-APPB3ZPQ.js.map +1 -0
  14. package/dist/cli/chunk-BQNUJJN7.js +42 -0
  15. package/dist/cli/chunk-BQNUJJN7.js.map +1 -0
  16. package/dist/cli/chunk-CPOV2O73.js +39 -0
  17. package/dist/cli/chunk-CPOV2O73.js.map +1 -0
  18. package/dist/cli/chunk-D5DKXIP5.js +368 -0
  19. package/dist/cli/chunk-D5DKXIP5.js.map +1 -0
  20. package/dist/cli/chunk-DFP4YSVM.js +247 -0
  21. package/dist/cli/chunk-DFP4YSVM.js.map +1 -0
  22. package/dist/cli/chunk-DULSP7JH.js +410 -0
  23. package/dist/cli/chunk-DULSP7JH.js.map +1 -0
  24. package/dist/cli/chunk-FM57FNPJ.js +46 -0
  25. package/dist/cli/chunk-FM57FNPJ.js.map +1 -0
  26. package/dist/cli/chunk-FWGEHRB7.js +54 -0
  27. package/dist/cli/chunk-FWGEHRB7.js.map +1 -0
  28. package/dist/cli/chunk-FXGQ5NHE.js +513 -0
  29. package/dist/cli/chunk-FXGQ5NHE.js.map +1 -0
  30. package/dist/cli/chunk-G3XNWSFN.js +53 -0
  31. package/dist/cli/chunk-G3XNWSFN.js.map +1 -0
  32. package/dist/cli/chunk-I6YIAK6C.js +757 -0
  33. package/dist/cli/chunk-I6YIAK6C.js.map +1 -0
  34. package/dist/cli/chunk-J5VLP23S.js +94 -0
  35. package/dist/cli/chunk-J5VLP23S.js.map +1 -0
  36. package/dist/cli/chunk-KMWKGPFZ.js +303 -0
  37. package/dist/cli/chunk-KMWKGPFZ.js.map +1 -0
  38. package/dist/cli/chunk-LVQX5KGF.js +14934 -0
  39. package/dist/cli/chunk-LVQX5KGF.js.map +1 -0
  40. package/dist/cli/chunk-MHDNZXJJ.js +48 -0
  41. package/dist/cli/chunk-MHDNZXJJ.js.map +1 -0
  42. package/dist/cli/chunk-ORM6PK57.js +140 -0
  43. package/dist/cli/chunk-ORM6PK57.js.map +1 -0
  44. package/dist/cli/chunk-Q5GRLZJF.js +99 -0
  45. package/dist/cli/chunk-Q5GRLZJF.js.map +1 -0
  46. package/dist/cli/chunk-Q6YFXW7H.js +4986 -0
  47. package/dist/cli/chunk-Q6YFXW7H.js.map +1 -0
  48. package/dist/cli/chunk-QGE6AF76.js +1467 -0
  49. package/dist/cli/chunk-QGE6AF76.js.map +1 -0
  50. package/dist/cli/chunk-RFX7TYVV.js +28 -0
  51. package/dist/cli/chunk-RFX7TYVV.js.map +1 -0
  52. package/dist/cli/chunk-RZILUXUC.js +940 -0
  53. package/dist/cli/chunk-RZILUXUC.js.map +1 -0
  54. package/dist/cli/chunk-SDE5U32Z.js +535 -0
  55. package/dist/cli/chunk-SDE5U32Z.js.map +1 -0
  56. package/dist/cli/chunk-SOZE7V7V.js +340 -0
  57. package/dist/cli/chunk-SOZE7V7V.js.map +1 -0
  58. package/dist/cli/chunk-U3V2ZQ5J.js +479 -0
  59. package/dist/cli/chunk-U3V2ZQ5J.js.map +1 -0
  60. package/dist/cli/chunk-W4LDFAZ6.js +1544 -0
  61. package/dist/cli/chunk-W4LDFAZ6.js.map +1 -0
  62. package/dist/cli/chunk-WBDE4IRI.js +208 -0
  63. package/dist/cli/chunk-WBDE4IRI.js.map +1 -0
  64. package/dist/cli/chunk-XHQIK7B6.js +189 -0
  65. package/dist/cli/chunk-XHQIK7B6.js.map +1 -0
  66. package/dist/cli/chunk-XJLZ4HKU.js +307 -0
  67. package/dist/cli/chunk-XJLZ4HKU.js.map +1 -0
  68. package/dist/cli/chunk-ZPTSJGX5.js +88 -0
  69. package/dist/cli/chunk-ZPTSJGX5.js.map +1 -0
  70. package/dist/cli/chunk-ZTLZO42A.js +231 -0
  71. package/dist/cli/chunk-ZTLZO42A.js.map +1 -0
  72. package/dist/cli/code-F4KJOE3K.js +151 -0
  73. package/dist/cli/code-F4KJOE3K.js.map +1 -0
  74. package/dist/cli/commands-JWT2MWVH.js +352 -0
  75. package/dist/cli/commands-JWT2MWVH.js.map +1 -0
  76. package/dist/cli/commit-RPZBOZS2.js +288 -0
  77. package/dist/cli/commit-RPZBOZS2.js.map +1 -0
  78. package/dist/cli/diff-NTEHCSDW.js +145 -0
  79. package/dist/cli/diff-NTEHCSDW.js.map +1 -0
  80. package/dist/cli/doctor-3TGB2NZN.js +19 -0
  81. package/dist/cli/doctor-3TGB2NZN.js.map +1 -0
  82. package/dist/cli/events-P27CX7LN.js +338 -0
  83. package/dist/cli/events-P27CX7LN.js.map +1 -0
  84. package/dist/cli/index.js +80 -33693
  85. package/dist/cli/index.js.map +1 -1
  86. package/dist/cli/mcp-ARTNQ24O.js +266 -0
  87. package/dist/cli/mcp-ARTNQ24O.js.map +1 -0
  88. package/dist/cli/mcp-browse-HLO2ENDL.js +163 -0
  89. package/dist/cli/mcp-browse-HLO2ENDL.js.map +1 -0
  90. package/dist/cli/mcp-inspect-T2HBR22P.js +103 -0
  91. package/dist/cli/mcp-inspect-T2HBR22P.js.map +1 -0
  92. package/dist/cli/{prompt-XHICFAYN.js → prompt-V47QKSAR.js} +3 -2
  93. package/dist/cli/prompt-V47QKSAR.js.map +1 -0
  94. package/dist/cli/prune-sessions-ERL6B4G5.js +42 -0
  95. package/dist/cli/prune-sessions-ERL6B4G5.js.map +1 -0
  96. package/dist/cli/replay-TMJASRC4.js +273 -0
  97. package/dist/cli/replay-TMJASRC4.js.map +1 -0
  98. package/dist/cli/run-JMEOTQCG.js +215 -0
  99. package/dist/cli/run-JMEOTQCG.js.map +1 -0
  100. package/dist/cli/server-SYC3OVOP.js +2967 -0
  101. package/dist/cli/server-SYC3OVOP.js.map +1 -0
  102. package/dist/cli/sessions-MOJAALJI.js +102 -0
  103. package/dist/cli/sessions-MOJAALJI.js.map +1 -0
  104. package/dist/cli/setup-CCJZAWTY.js +404 -0
  105. package/dist/cli/setup-CCJZAWTY.js.map +1 -0
  106. package/dist/cli/stats-5RJCATCE.js +12 -0
  107. package/dist/cli/stats-5RJCATCE.js.map +1 -0
  108. package/dist/cli/update-4TJWRUIN.js +90 -0
  109. package/dist/cli/update-4TJWRUIN.js.map +1 -0
  110. package/dist/cli/version-3MYFE4G6.js +29 -0
  111. package/dist/cli/version-3MYFE4G6.js.map +1 -0
  112. package/dist/index.d.ts +13 -2
  113. package/dist/index.js +493 -89
  114. package/dist/index.js.map +1 -1
  115. package/package.json +1 -1
  116. package/dist/cli/chunk-VWFJNLIK.js +0 -1031
  117. package/dist/cli/chunk-VWFJNLIK.js.map +0 -1
  118. /package/dist/cli/{prompt-XHICFAYN.js.map → chat-EIFLHBZ6.js.map} +0 -0
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ listSessions,
4
+ pruneStaleSessions
5
+ } from "./chunk-DFP4YSVM.js";
6
+
7
+ // src/cli/commands/prune-sessions.ts
8
+ function pruneSessionsCommand(opts) {
9
+ const days = opts.days ?? 90;
10
+ if (!Number.isFinite(days) || days < 1) {
11
+ console.error(`--days must be a positive integer (got ${days}).`);
12
+ process.exit(1);
13
+ }
14
+ if (opts.dryRun) {
15
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
16
+ const stale = listSessions().filter((s) => s.mtime.getTime() < cutoff);
17
+ if (stale.length === 0) {
18
+ console.log(`no sessions idle \u2265${days} days. Nothing would be pruned.`);
19
+ return;
20
+ }
21
+ console.log(`would prune ${stale.length} session(s) idle \u2265${days} days:`);
22
+ for (const s of stale) {
23
+ console.log(` ${s.name}`);
24
+ }
25
+ console.log("");
26
+ console.log("re-run without --dry-run to actually delete.");
27
+ return;
28
+ }
29
+ const removed = pruneStaleSessions(days);
30
+ if (removed.length === 0) {
31
+ console.log(`no sessions idle \u2265${days} days. Nothing pruned.`);
32
+ return;
33
+ }
34
+ console.log(`pruned ${removed.length} session(s) idle \u2265${days} days:`);
35
+ for (const name of removed) {
36
+ console.log(` ${name}`);
37
+ }
38
+ }
39
+ export {
40
+ pruneSessionsCommand
41
+ };
42
+ //# sourceMappingURL=prune-sessions-ERL6B4G5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/prune-sessions.ts"],"sourcesContent":["import { listSessions, pruneStaleSessions } from \"../../memory/session.js\";\n\nexport interface PruneSessionsOptions {\n days?: number;\n dryRun?: boolean;\n}\n\nexport function pruneSessionsCommand(opts: PruneSessionsOptions): void {\n const days = opts.days ?? 90;\n if (!Number.isFinite(days) || days < 1) {\n console.error(`--days must be a positive integer (got ${days}).`);\n process.exit(1);\n }\n if (opts.dryRun) {\n const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n const stale = listSessions().filter((s) => s.mtime.getTime() < cutoff);\n if (stale.length === 0) {\n console.log(`no sessions idle ≥${days} days. Nothing would be pruned.`);\n return;\n }\n console.log(`would prune ${stale.length} session(s) idle ≥${days} days:`);\n for (const s of stale) {\n console.log(` ${s.name}`);\n }\n console.log(\"\");\n console.log(\"re-run without --dry-run to actually delete.\");\n return;\n }\n const removed = pruneStaleSessions(days);\n if (removed.length === 0) {\n console.log(`no sessions idle ≥${days} days. Nothing pruned.`);\n return;\n }\n console.log(`pruned ${removed.length} session(s) idle ≥${days} days:`);\n for (const name of removed) {\n console.log(` ${name}`);\n }\n}\n"],"mappings":";;;;;;;AAOO,SAAS,qBAAqB,MAAkC;AACrE,QAAM,OAAO,KAAK,QAAQ;AAC1B,MAAI,CAAC,OAAO,SAAS,IAAI,KAAK,OAAO,GAAG;AACtC,YAAQ,MAAM,0CAA0C,IAAI,IAAI;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAK,QAAQ;AACf,UAAM,SAAS,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,KAAK;AAClD,UAAM,QAAQ,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,QAAQ,IAAI,MAAM;AACrE,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAI,0BAAqB,IAAI,iCAAiC;AACtE;AAAA,IACF;AACA,YAAQ,IAAI,eAAe,MAAM,MAAM,0BAAqB,IAAI,QAAQ;AACxE,eAAW,KAAK,OAAO;AACrB,cAAQ,IAAI,KAAK,EAAE,IAAI,EAAE;AAAA,IAC3B;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,8CAA8C;AAC1D;AAAA,EACF;AACA,QAAM,UAAU,mBAAmB,IAAI;AACvC,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,0BAAqB,IAAI,wBAAwB;AAC7D;AAAA,EACF;AACA,UAAQ,IAAI,UAAU,QAAQ,MAAM,0BAAqB,IAAI,QAAQ;AACrE,aAAW,QAAQ,SAAS;AAC1B,YAAQ,IAAI,KAAK,IAAI,EAAE;AAAA,EACzB;AACF;","names":[]}
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ RecordView
4
+ } from "./chunk-APPB3ZPQ.js";
5
+ import {
6
+ Bar,
7
+ ChromeRule,
8
+ formatBalance,
9
+ formatCost
10
+ } from "./chunk-Q5GRLZJF.js";
11
+ import {
12
+ computeCumulativeStats,
13
+ groupRecordsByTurn,
14
+ replayFromFile
15
+ } from "./chunk-XHQIK7B6.js";
16
+ import {
17
+ COLOR,
18
+ GRADIENT
19
+ } from "./chunk-ZPTSJGX5.js";
20
+ import "./chunk-KMWKGPFZ.js";
21
+ import "./chunk-ORM6PK57.js";
22
+
23
+ // src/cli/commands/replay.ts
24
+ import { render } from "ink";
25
+ import React3 from "react";
26
+
27
+ // src/cli/ui/ReplayApp.tsx
28
+ import { Box as Box2, Static, Text as Text2, useApp, useInput } from "ink";
29
+ import React2, { useMemo, useState } from "react";
30
+
31
+ // src/cli/ui/StatsPanel.tsx
32
+ import { basename } from "path";
33
+ import { Box, Text, useStdout } from "ink";
34
+ import React from "react";
35
+ import stringWidth from "string-width";
36
+ var COLD_START_TURNS = 3;
37
+ function StatsPanel({
38
+ summary,
39
+ planMode,
40
+ editMode,
41
+ balance,
42
+ updateAvailable,
43
+ proArmed,
44
+ escalated,
45
+ budgetUsd,
46
+ rootDir,
47
+ sessionName
48
+ }) {
49
+ const coldStart = summary.turns <= COLD_START_TURNS;
50
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React.createElement(
51
+ ChromeRow,
52
+ {
53
+ editMode,
54
+ planMode,
55
+ proArmed: proArmed ?? false,
56
+ escalated: escalated ?? false,
57
+ summary,
58
+ coldStart,
59
+ rootDir,
60
+ sessionName: sessionName ?? null,
61
+ updateAvailable,
62
+ balance: balance ?? null
63
+ }
64
+ ), /* @__PURE__ */ React.createElement(ChromeRule, null), budgetUsd !== null && budgetUsd !== void 0 ? /* @__PURE__ */ React.createElement(BudgetRow, { spent: summary.totalCostUsd, cap: budgetUsd }) : null);
65
+ }
66
+ function ChromeRow({
67
+ editMode,
68
+ planMode,
69
+ proArmed,
70
+ escalated,
71
+ summary,
72
+ coldStart,
73
+ rootDir,
74
+ sessionName,
75
+ updateAvailable,
76
+ balance
77
+ }) {
78
+ const modePill = pickModePill(planMode, editMode);
79
+ const proPill = escalated ? { label: "\u21E7 pro", color: COLOR.err } : proArmed ? { label: "\u21E7 pro", color: COLOR.warn } : null;
80
+ const projectName = rootDir ? basename(rootDir) : null;
81
+ const cachePct = Math.round(summary.cacheHitRatio * 100);
82
+ const cacheColor = summary.cacheHitRatio >= 0.7 ? COLOR.ok : summary.cacheHitRatio >= 0.4 ? COLOR.warn : COLOR.err;
83
+ const balanceLabel = balance ? `[${formatBalance(balance.total, balance.currency, { label: true })}]` : "";
84
+ const costLabel = `[${formatCost(summary.totalCostUsd, balance?.currency)}]`;
85
+ const cacheLabel = "[c \u25B0\u25B0\u25B0\u25B0\u25B0\u25B0 100%]";
86
+ const updateLabel = updateAvailable ? `\u2191 ${updateAvailable}` : "";
87
+ const { stdout } = useStdout();
88
+ const cols = (stdout?.columns ?? 80) - 2;
89
+ const SEP_DOT = stringWidth(" \xB7 ");
90
+ const SEP_ARROW = stringWidth(" \u203A ");
91
+ const GAP = 2;
92
+ const fixedLeft = stringWidth("\u25C8 reasonix") + (projectName ? SEP_DOT + stringWidth(projectName) : 0);
93
+ const modeW = modePill ? GAP + stringWidth(`[${modePill.label}]`) : 0;
94
+ const proW = proPill ? GAP + stringWidth(`[${proPill.label}]`) : 0;
95
+ const fixedRight = modeW + proW + stringWidth(costLabel);
96
+ let budget = cols - fixedLeft - fixedRight;
97
+ const balW = balance ? GAP + stringWidth(balanceLabel) : 0;
98
+ const cacheW = GAP + stringWidth(cacheLabel);
99
+ const sessionW = sessionName ? SEP_ARROW + stringWidth(sessionName) : 0;
100
+ const updateW = updateLabel ? GAP + stringWidth(updateLabel) : 0;
101
+ const showBalance = balW > 0 && budget >= balW;
102
+ if (showBalance) budget -= balW;
103
+ const showCache = budget >= cacheW;
104
+ if (showCache) budget -= cacheW;
105
+ const showSession = sessionW > 0 && budget >= sessionW;
106
+ if (showSession) budget -= sessionW;
107
+ const showUpdate = updateW > 0 && budget >= updateW;
108
+ if (showUpdate) budget -= updateW;
109
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true, color: GRADIENT[0] }, "\u25C8 "), /* @__PURE__ */ React.createElement(Text, { color: COLOR.brand, bold: true }, "reasonix"), projectName ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: COLOR.info, dimColor: true }, " \xB7 "), /* @__PURE__ */ React.createElement(Text, null, projectName), showSession && sessionName ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: COLOR.info, dimColor: true }, " \u203A "), /* @__PURE__ */ React.createElement(Text, { color: COLOR.info }, sessionName)) : null) : null, /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }), showUpdate ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: COLOR.warn, bold: true }, updateLabel), /* @__PURE__ */ React.createElement(Text, null, " ")) : null, modePill ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: modePill.color, bold: true }, `[${modePill.label}]`), /* @__PURE__ */ React.createElement(Text, null, " ")) : null, proPill ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: proPill.color, bold: true }, `[${proPill.label}]`), /* @__PURE__ */ React.createElement(Text, null, " ")) : null, /* @__PURE__ */ React.createElement(
110
+ Text,
111
+ {
112
+ color: summary.turns === 0 || coldStart ? COLOR.info : sessionCostColor(summary.totalCostUsd),
113
+ bold: summary.turns > 0 && !coldStart,
114
+ dimColor: summary.turns === 0 || coldStart
115
+ },
116
+ costLabel
117
+ ), showBalance && balance ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, null, " "), /* @__PURE__ */ React.createElement(Text, { color: balance.total < 1 ? COLOR.err : balance.total < 5 ? COLOR.warn : COLOR.ok }, balanceLabel)) : null, showCache ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, null, " "), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "["), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "c "), /* @__PURE__ */ React.createElement(
118
+ Bar,
119
+ {
120
+ ratio: summary.cacheHitRatio,
121
+ color: coldStart ? COLOR.info : cacheColor,
122
+ cells: 6,
123
+ dim: coldStart
124
+ }
125
+ ), /* @__PURE__ */ React.createElement(Text, null, " "), /* @__PURE__ */ React.createElement(Text, { color: coldStart ? void 0 : cacheColor, dimColor: coldStart }, coldStart && summary.turns === 0 ? "\u2014" : `${cachePct}%`), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "]")) : null);
126
+ }
127
+ function pickModePill(planMode, editMode) {
128
+ if (planMode) return { label: "PLAN", color: COLOR.err };
129
+ if (editMode === "yolo") return { label: "yolo", color: COLOR.err };
130
+ if (editMode === "auto") return { label: "auto", color: COLOR.primary };
131
+ if (editMode === "review") return { label: "review", color: COLOR.info };
132
+ return null;
133
+ }
134
+ function BudgetRow({ spent, cap }) {
135
+ const pct = Math.max(0, spent / cap * 100);
136
+ const color = pct >= 100 ? "#f87171" : pct >= 80 ? "#fbbf24" : "#94a3b8";
137
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " budget "), /* @__PURE__ */ React.createElement(Text, { color }, `$${spent.toFixed(4)} / $${cap.toFixed(2)}`, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, ` (${pct.toFixed(0)}%)`)));
138
+ }
139
+ function sessionCostColor(cost) {
140
+ if (cost <= 0) return void 0;
141
+ if (cost >= 5) return COLOR.err;
142
+ if (cost >= 0.5) return COLOR.warn;
143
+ return COLOR.ok;
144
+ }
145
+
146
+ // src/cli/ui/ReplayApp.tsx
147
+ function ReplayApp({ meta, pages }) {
148
+ const { exit } = useApp();
149
+ const maxIdx = Math.max(0, pages.length - 1);
150
+ const [idx, setIdx] = useState(maxIdx);
151
+ useInput((input, key) => {
152
+ if (input === "q" || key.ctrl && input === "c") {
153
+ exit();
154
+ return;
155
+ }
156
+ if (input === "j" || key.downArrow || input === " " || key.return) {
157
+ setIdx((i) => Math.min(maxIdx, i + 1));
158
+ } else if (input === "k" || key.upArrow) {
159
+ setIdx((i) => Math.max(0, i - 1));
160
+ } else if (input === "g") {
161
+ setIdx(0);
162
+ } else if (input === "G") {
163
+ setIdx(maxIdx);
164
+ } else if (input === "h" || key.leftArrow) {
165
+ setIdx(0);
166
+ } else if (input === "l" || key.rightArrow) {
167
+ setIdx(maxIdx);
168
+ }
169
+ });
170
+ const cumStats = useMemo(() => computeCumulativeStats(pages, idx), [pages, idx]);
171
+ const summary = {
172
+ turns: cumStats.turns,
173
+ totalCostUsd: cumStats.totalCostUsd,
174
+ totalInputCostUsd: cumStats.totalInputCostUsd,
175
+ totalOutputCostUsd: cumStats.totalOutputCostUsd,
176
+ claudeEquivalentUsd: cumStats.claudeEquivalentUsd,
177
+ savingsVsClaudePct: cumStats.savingsVsClaudePct,
178
+ cacheHitRatio: cumStats.cacheHitRatio,
179
+ // Replay is read-only — no live last-turn prompt tokens to show.
180
+ lastPromptTokens: 0,
181
+ lastTurnCostUsd: 0
182
+ };
183
+ const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
184
+ const currentPage = pages[idx];
185
+ const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
186
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(StatsPanel, { summary }), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React2.createElement(Box2, { justifyContent: "space-between" }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React2.createElement(Static, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React2.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "j"), "/", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "k"), "/", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React2.createElement(Text2, { bold: true }, "q"), " quit")));
187
+ }
188
+
189
+ // src/cli/commands/replay.ts
190
+ async function replayCommand(opts) {
191
+ const wantPrint = opts.print || !process.stdout.isTTY || opts.head !== void 0 || opts.tail !== void 0;
192
+ if (wantPrint) {
193
+ printReplay(opts);
194
+ return;
195
+ }
196
+ const { parsed } = replayFromFile(opts.path);
197
+ const pages = groupRecordsByTurn(parsed.records);
198
+ const { waitUntilExit } = render(React3.createElement(ReplayApp, { meta: parsed.meta, pages }), {
199
+ exitOnCtrlC: true,
200
+ patchConsole: false
201
+ });
202
+ await waitUntilExit();
203
+ }
204
+ function printReplay(opts) {
205
+ const { parsed, stats } = replayFromFile(opts.path);
206
+ if (parsed.meta) {
207
+ const m = parsed.meta;
208
+ const bits = [`source=${m.source}`];
209
+ if (m.model) bits.push(`model=${m.model}`);
210
+ if (m.task) bits.push(`task=${m.task}`);
211
+ if (m.mode) bits.push(`mode=${m.mode}`);
212
+ if (m.repeat !== void 0) bits.push(`repeat=${m.repeat}`);
213
+ bits.push(`started=${m.startedAt}`);
214
+ console.log(`[meta] ${bits.join(" ")}`);
215
+ console.log("");
216
+ }
217
+ const records = sliceRecords(parsed.records, opts);
218
+ for (const rec of records) {
219
+ renderRecord(rec);
220
+ }
221
+ console.log("");
222
+ console.log("\u2500\u2500 summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
223
+ console.log(`model calls: ${stats.turns}`);
224
+ console.log(`user turns: ${stats.userTurns}`);
225
+ console.log(`tool calls: ${stats.toolCalls}`);
226
+ console.log(`cache hit: ${(stats.cacheHitRatio * 100).toFixed(1)}%`);
227
+ console.log(`cost: $${stats.totalCostUsd.toFixed(6)}`);
228
+ console.log(`claude equivalent: $${stats.claudeEquivalentUsd.toFixed(6)}`);
229
+ console.log(`savings vs claude: ${stats.savingsVsClaudePct.toFixed(1)}%`);
230
+ console.log(`models: ${stats.models.join(", ") || "\u2014"}`);
231
+ console.log(`prefix hashes: ${stats.prefixHashes.length} distinct`);
232
+ if (stats.prefixHashes.length === 1) {
233
+ console.log(` (byte-stable prefix: ${stats.prefixHashes[0]?.slice(0, 16)}\u2026)`);
234
+ } else if (stats.prefixHashes.length > 1) {
235
+ console.log(" (prefix churned \u2014 cache-hostile session)");
236
+ }
237
+ }
238
+ function sliceRecords(records, opts) {
239
+ if (opts.head !== void 0 && opts.head > 0) return records.slice(0, opts.head);
240
+ if (opts.tail !== void 0 && opts.tail > 0) return records.slice(-opts.tail);
241
+ return records;
242
+ }
243
+ function renderRecord(rec) {
244
+ const turn = `[t${rec.turn}]`;
245
+ if (rec.role === "user") {
246
+ console.log(`${turn} USER: ${oneLine(rec.content)}`);
247
+ } else if (rec.role === "assistant_final") {
248
+ const cost = rec.cost !== void 0 ? ` $${rec.cost.toFixed(6)}` : "";
249
+ const cache = rec.usage && (rec.usage.prompt_cache_hit_tokens !== void 0 || rec.usage.prompt_cache_miss_tokens !== void 0) ? (() => {
250
+ const hit = rec.usage.prompt_cache_hit_tokens ?? 0;
251
+ const miss = rec.usage.prompt_cache_miss_tokens ?? 0;
252
+ const total = hit + miss;
253
+ return total > 0 ? ` cache=${(hit / total * 100).toFixed(1)}%` : "";
254
+ })() : "";
255
+ console.log(`${turn} AGENT:${cost}${cache} ${oneLine(rec.content)}`);
256
+ } else if (rec.role === "tool") {
257
+ const args = rec.args ? ` args=${oneLine(rec.args, 80)}` : "";
258
+ console.log(`${turn} TOOL ${rec.tool ?? "?"}:${args} \u2192 ${oneLine(rec.content, 120)}`);
259
+ } else if (rec.role === "error") {
260
+ console.log(`${turn} ERROR: ${rec.error ?? rec.content}`);
261
+ } else if (rec.role === "done") {
262
+ } else {
263
+ console.log(`${turn} ${rec.role}: ${oneLine(rec.content)}`);
264
+ }
265
+ }
266
+ function oneLine(s, max = 200) {
267
+ const collapsed = s.replace(/\s+/g, " ").trim();
268
+ return collapsed.length > max ? `${collapsed.slice(0, max)}\u2026` : collapsed;
269
+ }
270
+ export {
271
+ replayCommand
272
+ };
273
+ //# sourceMappingURL=replay-TMJASRC4.js.map
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ formatMcpLifecycleEvent,
4
+ formatMcpSlowToast
5
+ } from "./chunk-BQNUJJN7.js";
6
+ import {
7
+ preflightStdioSpec
8
+ } from "./chunk-RFX7TYVV.js";
9
+ import {
10
+ CacheFirstLoop,
11
+ ImmutablePrefix,
12
+ ToolRegistry,
13
+ bridgeMcpTools
14
+ } from "./chunk-Q6YFXW7H.js";
15
+ import {
16
+ McpClient,
17
+ SseTransport,
18
+ StdioTransport,
19
+ StreamableHttpTransport,
20
+ parseMcpSpec
21
+ } from "./chunk-I6YIAK6C.js";
22
+ import "./chunk-XJLZ4HKU.js";
23
+ import {
24
+ openTranscriptFile,
25
+ recordFromLoopEvent,
26
+ writeRecord
27
+ } from "./chunk-XHQIK7B6.js";
28
+ import "./chunk-6TMHAK5D.js";
29
+ import {
30
+ DeepSeekClient
31
+ } from "./chunk-KMWKGPFZ.js";
32
+ import {
33
+ loadDotenv
34
+ } from "./chunk-3Q3C4W66.js";
35
+ import "./chunk-W4LDFAZ6.js";
36
+ import "./chunk-U3V2ZQ5J.js";
37
+ import "./chunk-WBDE4IRI.js";
38
+ import "./chunk-2AWTGJ2C.js";
39
+ import "./chunk-5X7LZJDE.js";
40
+ import "./chunk-DFP4YSVM.js";
41
+ import "./chunk-QGE6AF76.js";
42
+ import {
43
+ defaultConfigPath,
44
+ isPlausibleKey,
45
+ loadApiKey,
46
+ readConfig,
47
+ saveApiKey
48
+ } from "./chunk-DULSP7JH.js";
49
+ import {
50
+ appendUsage
51
+ } from "./chunk-ZTLZO42A.js";
52
+ import "./chunk-ORM6PK57.js";
53
+
54
+ // src/cli/commands/run.ts
55
+ import { stdin, stdout } from "process";
56
+ import { createInterface } from "readline/promises";
57
+ async function ensureApiKey() {
58
+ const existing = loadApiKey();
59
+ if (existing) return existing;
60
+ if (!stdin.isTTY) {
61
+ process.stderr.write(
62
+ "DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\nSet the env var, or run `reasonix chat` once interactively to save a key.\n"
63
+ );
64
+ process.exit(1);
65
+ }
66
+ process.stdout.write(
67
+ "DeepSeek API key not configured.\nGet one at https://platform.deepseek.com/api_keys\n"
68
+ );
69
+ const rl = createInterface({ input: stdin, output: stdout });
70
+ try {
71
+ while (true) {
72
+ const answer = (await rl.question("API key \u203A ")).trim();
73
+ if (!answer) continue;
74
+ if (!isPlausibleKey(answer)) {
75
+ process.stdout.write("Invalid format. Keys start with 'sk-' and are 30+ chars.\n");
76
+ continue;
77
+ }
78
+ saveApiKey(answer);
79
+ process.stdout.write(`Saved to ${defaultConfigPath()}
80
+
81
+ `);
82
+ return answer;
83
+ }
84
+ } finally {
85
+ rl.close();
86
+ }
87
+ }
88
+ async function runCommand(opts) {
89
+ loadDotenv();
90
+ const apiKey = await ensureApiKey();
91
+ process.env.DEEPSEEK_API_KEY = apiKey;
92
+ const requestedSpecs = opts.mcp ?? [];
93
+ const clients = [];
94
+ let tools;
95
+ let successCount = 0;
96
+ const disabledNames = new Set(readConfig().mcpDisabled ?? []);
97
+ if (requestedSpecs.length > 0) {
98
+ tools = new ToolRegistry();
99
+ for (const raw of requestedSpecs) {
100
+ let label = "anon";
101
+ let mcp;
102
+ try {
103
+ const spec = parseMcpSpec(raw);
104
+ label = spec.name ?? "anon";
105
+ if (spec.name && disabledNames.has(spec.name)) {
106
+ process.stderr.write(`${formatMcpLifecycleEvent({ state: "disabled", name: label })}
107
+ `);
108
+ continue;
109
+ }
110
+ process.stderr.write(`${formatMcpLifecycleEvent({ state: "handshake", name: label })}
111
+ `);
112
+ const t0 = Date.now();
113
+ const prefix2 = spec.name ? `${spec.name}_` : requestedSpecs.length === 1 && opts.mcpPrefix ? opts.mcpPrefix : "";
114
+ if (spec.transport === "stdio") preflightStdioSpec(spec);
115
+ const transport = spec.transport === "sse" ? new SseTransport({ url: spec.url }) : spec.transport === "streamable-http" ? new StreamableHttpTransport({ url: spec.url }) : new StdioTransport({ command: spec.command, args: spec.args });
116
+ mcp = new McpClient({ transport });
117
+ await mcp.initialize();
118
+ const bridge = await bridgeMcpTools(mcp, {
119
+ registry: tools,
120
+ namePrefix: prefix2,
121
+ serverName: label,
122
+ onSlow: (info) => process.stderr.write(
123
+ `${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}
124
+ `
125
+ )
126
+ });
127
+ process.stderr.write(
128
+ `${formatMcpLifecycleEvent({
129
+ state: "connected",
130
+ name: label,
131
+ tools: bridge.registeredNames.length,
132
+ ms: Date.now() - t0
133
+ })}
134
+ `
135
+ );
136
+ clients.push(mcp);
137
+ successCount++;
138
+ } catch (err) {
139
+ await mcp?.close().catch(() => void 0);
140
+ process.stderr.write(
141
+ `${formatMcpLifecycleEvent({ state: "failed", name: label, reason: err.message })}
142
+ \u2192 run \`reasonix setup\` to remove broken entries from your saved config.
143
+ `
144
+ );
145
+ }
146
+ }
147
+ if (successCount === 0) tools = void 0;
148
+ }
149
+ const client = new DeepSeekClient();
150
+ const prefix = new ImmutablePrefix({
151
+ system: opts.system,
152
+ toolSpecs: tools?.specs()
153
+ });
154
+ const loop = new CacheFirstLoop({
155
+ client,
156
+ prefix,
157
+ tools,
158
+ model: opts.model,
159
+ budgetUsd: opts.budgetUsd
160
+ });
161
+ const prefixHash = prefix.fingerprint;
162
+ let transcriptStream = null;
163
+ if (opts.transcript) {
164
+ transcriptStream = openTranscriptFile(opts.transcript, {
165
+ version: 1,
166
+ source: "reasonix run",
167
+ model: opts.model,
168
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
169
+ });
170
+ writeRecord(transcriptStream, {
171
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
172
+ turn: 1,
173
+ role: "user",
174
+ content: opts.task
175
+ });
176
+ }
177
+ try {
178
+ for await (const ev of loop.step(opts.task)) {
179
+ if (ev.role === "assistant_delta" && ev.content) process.stdout.write(ev.content);
180
+ if (ev.role === "tool") process.stdout.write(`
181
+ [tool ${ev.toolName}] ${ev.content}
182
+ `);
183
+ if (ev.role === "error") process.stderr.write(`
184
+ [error] ${ev.error}
185
+ `);
186
+ if (ev.role === "done") process.stdout.write("\n");
187
+ if (ev.role === "assistant_final" && ev.stats?.usage) {
188
+ appendUsage({ session: null, model: ev.stats.model, usage: ev.stats.usage });
189
+ }
190
+ if (transcriptStream && ev.role !== "assistant_delta") {
191
+ writeRecord(transcriptStream, recordFromLoopEvent(ev, { model: opts.model, prefixHash }));
192
+ }
193
+ }
194
+ } finally {
195
+ transcriptStream?.end();
196
+ }
197
+ const s = loop.stats.summary();
198
+ process.stdout.write(
199
+ `
200
+ \u2014 turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%
201
+ `
202
+ );
203
+ if (opts.transcript) {
204
+ process.stdout.write(`
205
+ transcript: ${opts.transcript}
206
+ `);
207
+ process.stdout.write(` \u2192 npx reasonix replay ${opts.transcript}
208
+ `);
209
+ }
210
+ for (const c of clients) await c.close();
211
+ }
212
+ export {
213
+ runCommand
214
+ };
215
+ //# sourceMappingURL=run-JMEOTQCG.js.map
@@ -0,0 +1 @@
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":"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","names":["prefix"]}