reasonix 0.31.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 (122) hide show
  1. package/README.md +3 -7
  2. package/README.zh-CN.md +2 -6
  3. package/dashboard/dist/app.js +348 -80
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/chat-EIFLHBZ6.js +39 -0
  6. package/dist/cli/chunk-2AWTGJ2C.js +110 -0
  7. package/dist/cli/chunk-2AWTGJ2C.js.map +1 -0
  8. package/dist/cli/chunk-3Q3C4W66.js +30 -0
  9. package/dist/cli/chunk-3Q3C4W66.js.map +1 -0
  10. package/dist/cli/chunk-4DCHFFEY.js +149 -0
  11. package/dist/cli/chunk-4DCHFFEY.js.map +1 -0
  12. package/dist/cli/chunk-5X7LZJDE.js +36 -0
  13. package/dist/cli/chunk-5X7LZJDE.js.map +1 -0
  14. package/dist/cli/chunk-6TMHAK5D.js +576 -0
  15. package/dist/cli/chunk-6TMHAK5D.js.map +1 -0
  16. package/dist/cli/chunk-APPB3ZPQ.js +43 -0
  17. package/dist/cli/chunk-APPB3ZPQ.js.map +1 -0
  18. package/dist/cli/chunk-BQNUJJN7.js +42 -0
  19. package/dist/cli/chunk-BQNUJJN7.js.map +1 -0
  20. package/dist/cli/chunk-CPOV2O73.js +39 -0
  21. package/dist/cli/chunk-CPOV2O73.js.map +1 -0
  22. package/dist/cli/chunk-D5DKXIP5.js +368 -0
  23. package/dist/cli/chunk-D5DKXIP5.js.map +1 -0
  24. package/dist/cli/chunk-DFP4YSVM.js +247 -0
  25. package/dist/cli/chunk-DFP4YSVM.js.map +1 -0
  26. package/dist/cli/chunk-DULSP7JH.js +410 -0
  27. package/dist/cli/chunk-DULSP7JH.js.map +1 -0
  28. package/dist/cli/chunk-FM57FNPJ.js +46 -0
  29. package/dist/cli/chunk-FM57FNPJ.js.map +1 -0
  30. package/dist/cli/chunk-FWGEHRB7.js +54 -0
  31. package/dist/cli/chunk-FWGEHRB7.js.map +1 -0
  32. package/dist/cli/chunk-FXGQ5NHE.js +513 -0
  33. package/dist/cli/chunk-FXGQ5NHE.js.map +1 -0
  34. package/dist/cli/chunk-G3XNWSFN.js +53 -0
  35. package/dist/cli/chunk-G3XNWSFN.js.map +1 -0
  36. package/dist/cli/chunk-I6YIAK6C.js +757 -0
  37. package/dist/cli/chunk-I6YIAK6C.js.map +1 -0
  38. package/dist/cli/chunk-J5VLP23S.js +94 -0
  39. package/dist/cli/chunk-J5VLP23S.js.map +1 -0
  40. package/dist/cli/chunk-KMWKGPFZ.js +303 -0
  41. package/dist/cli/chunk-KMWKGPFZ.js.map +1 -0
  42. package/dist/cli/chunk-LVQX5KGF.js +14934 -0
  43. package/dist/cli/chunk-LVQX5KGF.js.map +1 -0
  44. package/dist/cli/chunk-MHDNZXJJ.js +48 -0
  45. package/dist/cli/chunk-MHDNZXJJ.js.map +1 -0
  46. package/dist/cli/chunk-ORM6PK57.js +140 -0
  47. package/dist/cli/chunk-ORM6PK57.js.map +1 -0
  48. package/dist/cli/chunk-Q5GRLZJF.js +99 -0
  49. package/dist/cli/chunk-Q5GRLZJF.js.map +1 -0
  50. package/dist/cli/chunk-Q6YFXW7H.js +4986 -0
  51. package/dist/cli/chunk-Q6YFXW7H.js.map +1 -0
  52. package/dist/cli/chunk-QGE6AF76.js +1467 -0
  53. package/dist/cli/chunk-QGE6AF76.js.map +1 -0
  54. package/dist/cli/chunk-RFX7TYVV.js +28 -0
  55. package/dist/cli/chunk-RFX7TYVV.js.map +1 -0
  56. package/dist/cli/chunk-RZILUXUC.js +940 -0
  57. package/dist/cli/chunk-RZILUXUC.js.map +1 -0
  58. package/dist/cli/chunk-SDE5U32Z.js +535 -0
  59. package/dist/cli/chunk-SDE5U32Z.js.map +1 -0
  60. package/dist/cli/chunk-SOZE7V7V.js +340 -0
  61. package/dist/cli/chunk-SOZE7V7V.js.map +1 -0
  62. package/dist/cli/chunk-U3V2ZQ5J.js +479 -0
  63. package/dist/cli/chunk-U3V2ZQ5J.js.map +1 -0
  64. package/dist/cli/chunk-W4LDFAZ6.js +1544 -0
  65. package/dist/cli/chunk-W4LDFAZ6.js.map +1 -0
  66. package/dist/cli/chunk-WBDE4IRI.js +208 -0
  67. package/dist/cli/chunk-WBDE4IRI.js.map +1 -0
  68. package/dist/cli/chunk-XHQIK7B6.js +189 -0
  69. package/dist/cli/chunk-XHQIK7B6.js.map +1 -0
  70. package/dist/cli/chunk-XJLZ4HKU.js +307 -0
  71. package/dist/cli/chunk-XJLZ4HKU.js.map +1 -0
  72. package/dist/cli/chunk-ZPTSJGX5.js +88 -0
  73. package/dist/cli/chunk-ZPTSJGX5.js.map +1 -0
  74. package/dist/cli/chunk-ZTLZO42A.js +231 -0
  75. package/dist/cli/chunk-ZTLZO42A.js.map +1 -0
  76. package/dist/cli/code-F4KJOE3K.js +151 -0
  77. package/dist/cli/code-F4KJOE3K.js.map +1 -0
  78. package/dist/cli/commands-JWT2MWVH.js +352 -0
  79. package/dist/cli/commands-JWT2MWVH.js.map +1 -0
  80. package/dist/cli/commit-RPZBOZS2.js +288 -0
  81. package/dist/cli/commit-RPZBOZS2.js.map +1 -0
  82. package/dist/cli/diff-NTEHCSDW.js +145 -0
  83. package/dist/cli/diff-NTEHCSDW.js.map +1 -0
  84. package/dist/cli/doctor-3TGB2NZN.js +19 -0
  85. package/dist/cli/doctor-3TGB2NZN.js.map +1 -0
  86. package/dist/cli/events-P27CX7LN.js +338 -0
  87. package/dist/cli/events-P27CX7LN.js.map +1 -0
  88. package/dist/cli/index.js +83 -34028
  89. package/dist/cli/index.js.map +1 -1
  90. package/dist/cli/mcp-ARTNQ24O.js +266 -0
  91. package/dist/cli/mcp-ARTNQ24O.js.map +1 -0
  92. package/dist/cli/mcp-browse-HLO2ENDL.js +163 -0
  93. package/dist/cli/mcp-browse-HLO2ENDL.js.map +1 -0
  94. package/dist/cli/mcp-inspect-T2HBR22P.js +103 -0
  95. package/dist/cli/mcp-inspect-T2HBR22P.js.map +1 -0
  96. package/dist/cli/{prompt-XHICFAYN.js → prompt-V47QKSAR.js} +3 -2
  97. package/dist/cli/prompt-V47QKSAR.js.map +1 -0
  98. package/dist/cli/prune-sessions-ERL6B4G5.js +42 -0
  99. package/dist/cli/prune-sessions-ERL6B4G5.js.map +1 -0
  100. package/dist/cli/replay-TMJASRC4.js +273 -0
  101. package/dist/cli/replay-TMJASRC4.js.map +1 -0
  102. package/dist/cli/run-JMEOTQCG.js +215 -0
  103. package/dist/cli/run-JMEOTQCG.js.map +1 -0
  104. package/dist/cli/server-SYC3OVOP.js +2967 -0
  105. package/dist/cli/server-SYC3OVOP.js.map +1 -0
  106. package/dist/cli/sessions-MOJAALJI.js +102 -0
  107. package/dist/cli/sessions-MOJAALJI.js.map +1 -0
  108. package/dist/cli/setup-CCJZAWTY.js +404 -0
  109. package/dist/cli/setup-CCJZAWTY.js.map +1 -0
  110. package/dist/cli/stats-5RJCATCE.js +12 -0
  111. package/dist/cli/stats-5RJCATCE.js.map +1 -0
  112. package/dist/cli/update-4TJWRUIN.js +90 -0
  113. package/dist/cli/update-4TJWRUIN.js.map +1 -0
  114. package/dist/cli/version-3MYFE4G6.js +29 -0
  115. package/dist/cli/version-3MYFE4G6.js.map +1 -0
  116. package/dist/index.d.ts +49 -96
  117. package/dist/index.js +567 -759
  118. package/dist/index.js.map +1 -1
  119. package/package.json +1 -1
  120. package/dist/cli/chunk-VWFJNLIK.js +0 -1031
  121. package/dist/cli/chunk-VWFJNLIK.js.map +0 -1
  122. /package/dist/cli/{prompt-XHICFAYN.js.map → chat-EIFLHBZ6.js.map} +0 -0
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DeepSeekClient
4
+ } from "./chunk-KMWKGPFZ.js";
5
+ import {
6
+ loadDotenv
7
+ } from "./chunk-3Q3C4W66.js";
8
+ import {
9
+ loadApiKey
10
+ } from "./chunk-DULSP7JH.js";
11
+
12
+ // src/cli/commands/commit.ts
13
+ import { spawn, spawnSync } from "child_process";
14
+ import { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from "fs";
15
+ import { tmpdir } from "os";
16
+ import { join } from "path";
17
+ import { stdin, stdout } from "process";
18
+ import { createInterface } from "readline/promises";
19
+ var DEFAULT_MODEL = "deepseek-v4-flash";
20
+ var DIFF_BYTE_CAP = 80 * 1024;
21
+ var LOG_COUNT = 10;
22
+ var SYSTEM_PROMPT = `You draft git commit messages.
23
+
24
+ Output ONLY the commit message \u2014 no preamble, no \`\`\` fences, no "Here's a commit message:" lead-in. The first line of your output IS the commit subject.
25
+
26
+ Match the project's existing style:
27
+ - Look at the recent commits provided. Mirror their voice, conventional-commit prefix usage (or absence), tense, length, body structure.
28
+ - If recent commits use a "type(scope): summary" prefix, use it. If they don't, don't invent one.
29
+ - Subject line: one line, \u226472 chars, imperative mood, no trailing period.
30
+ - Body (optional): explain WHY when the diff isn't self-evident. Wrap at ~72 chars. Skip the body for trivial changes \u2014 repeating the subject in the body is noise.
31
+
32
+ The diff is the source of truth for what changed; describe THAT, not your guesses about the broader project. If the diff includes a deletion you can't explain from the surrounding context, name it but don't speculate about why.
33
+
34
+ No emojis unless the recent commits use them.
35
+ No co-author trailers, no "Generated with X" footers.`;
36
+ function runGit(args, opts = {}) {
37
+ const result = spawnSync("git", args, {
38
+ encoding: "utf8",
39
+ stdio: ["pipe", "pipe", "pipe"],
40
+ input: opts.input,
41
+ maxBuffer: 32 * 1024 * 1024
42
+ });
43
+ return {
44
+ stdout: result.stdout ?? "",
45
+ stderr: result.stderr ?? "",
46
+ status: result.status
47
+ };
48
+ }
49
+ function dieIfNotGitRepo() {
50
+ const r = runGit(["rev-parse", "--is-inside-work-tree"]);
51
+ if (r.status !== 0) {
52
+ process.stderr.write("reasonix commit: not inside a git repository.\n");
53
+ process.exit(1);
54
+ }
55
+ }
56
+ function readDiff() {
57
+ const staged = runGit(["diff", "--staged", "--no-color"]);
58
+ if (staged.status !== 0) {
59
+ process.stderr.write(`reasonix commit: git diff --staged failed: ${staged.stderr.trim()}
60
+ `);
61
+ process.exit(1);
62
+ }
63
+ if (staged.stdout.trim().length > 0) {
64
+ return capDiff(staged.stdout, "staged");
65
+ }
66
+ const wt = runGit(["diff", "--no-color"]);
67
+ if (wt.stdout.trim().length === 0) {
68
+ return null;
69
+ }
70
+ return capDiff(wt.stdout, "working-tree");
71
+ }
72
+ function capDiff(raw, source) {
73
+ if (raw.length <= DIFF_BYTE_CAP) {
74
+ return { diff: raw, source, truncated: false };
75
+ }
76
+ const head = raw.slice(0, Math.floor(DIFF_BYTE_CAP * 0.7));
77
+ const tail = raw.slice(-Math.floor(DIFF_BYTE_CAP * 0.3));
78
+ return {
79
+ diff: `${head}
80
+
81
+ [\u2026 ${raw.length - DIFF_BYTE_CAP} bytes of diff truncated \u2026]
82
+
83
+ ${tail}`,
84
+ source,
85
+ truncated: true
86
+ };
87
+ }
88
+ function readRecentCommits() {
89
+ const r = runGit(["log", `-${LOG_COUNT}`, "--no-merges", "--format=%s%n%b%n---END---"]);
90
+ if (r.status !== 0) {
91
+ return "";
92
+ }
93
+ return r.stdout.trim();
94
+ }
95
+ async function draftMessage(client, model, diff, recentCommits) {
96
+ const userParts = [];
97
+ if (recentCommits) {
98
+ userParts.push(`Recent commits (style reference):
99
+
100
+ ${recentCommits}`);
101
+ }
102
+ if (diff.source === "working-tree") {
103
+ userParts.push(
104
+ "(NOTE: diff is from the working tree, not the staging area \u2014 nothing is staged yet. The user will stage selectively after seeing the draft.)"
105
+ );
106
+ }
107
+ userParts.push(`Diff to summarize:
108
+
109
+ ${diff.diff}`);
110
+ const resp = await client.chat({
111
+ model,
112
+ messages: [
113
+ { role: "system", content: SYSTEM_PROMPT },
114
+ { role: "user", content: userParts.join("\n\n") }
115
+ ],
116
+ temperature: 0.2
117
+ });
118
+ return stripCodeFences(resp.content.trim());
119
+ }
120
+ function stripCodeFences(s) {
121
+ const trimmed = s.trim();
122
+ const fenceOpen = /^```[a-zA-Z]*\n/;
123
+ const fenceClose = /\n?```$/;
124
+ if (fenceOpen.test(trimmed) && fenceClose.test(trimmed)) {
125
+ return trimmed.replace(fenceOpen, "").replace(fenceClose, "").trim();
126
+ }
127
+ return trimmed;
128
+ }
129
+ function printDraft(message) {
130
+ const sep = "\u2500".repeat(60);
131
+ process.stdout.write(`
132
+ ${sep}
133
+ ${message}
134
+ ${sep}
135
+
136
+ `);
137
+ }
138
+ async function promptChoice() {
139
+ const rl = createInterface({ input: stdin, output: stdout });
140
+ try {
141
+ const answer = await rl.question("[a]ccept / [r]egenerate / [e]dit / [c]ancel: ");
142
+ const k = answer.trim().toLowerCase();
143
+ if (k === "" || k === "a" || k === "y" || k === "yes") return "accept";
144
+ if (k === "r" || k === "regen" || k === "regenerate") return "regen";
145
+ if (k === "e" || k === "edit") return "edit";
146
+ return "cancel";
147
+ } finally {
148
+ rl.close();
149
+ }
150
+ }
151
+ function editInExternal(initial) {
152
+ const editor = process.env.GIT_EDITOR ?? process.env.VISUAL ?? process.env.EDITOR;
153
+ if (!editor) {
154
+ process.stderr.write(
155
+ "reasonix commit: no $EDITOR / $VISUAL / $GIT_EDITOR set \u2014 can't open editor. Pick [a]ccept and `git commit --amend` afterwards.\n"
156
+ );
157
+ return null;
158
+ }
159
+ const dir = mkdtempSync(join(tmpdir(), "reasonix-commit-"));
160
+ const path = join(dir, "COMMIT_EDITMSG");
161
+ writeFileSync(path, initial, "utf8");
162
+ const result = spawnSync(`${editor} "${path}"`, {
163
+ stdio: "inherit",
164
+ shell: true
165
+ });
166
+ if (result.status !== 0) {
167
+ try {
168
+ unlinkSync(path);
169
+ } catch {
170
+ }
171
+ process.stderr.write(
172
+ `reasonix commit: editor exited ${result.status} \u2014 keeping prior draft.
173
+ `
174
+ );
175
+ return null;
176
+ }
177
+ let edited;
178
+ try {
179
+ edited = readFileSync(path, "utf8");
180
+ } catch {
181
+ return null;
182
+ } finally {
183
+ try {
184
+ unlinkSync(path);
185
+ } catch {
186
+ }
187
+ }
188
+ const cleaned = edited.split(/\r?\n/).filter((line) => !/^\s*#/.test(line)).join("\n").trim();
189
+ return cleaned || null;
190
+ }
191
+ function commitWithMessage(message) {
192
+ const child = spawn("git", ["commit", "-F", "-"], {
193
+ stdio: ["pipe", "inherit", "inherit"]
194
+ });
195
+ child.stdin.write(message);
196
+ child.stdin.end();
197
+ child.on("close", (code) => {
198
+ if (code !== 0) {
199
+ process.stderr.write(`reasonix commit: git commit exited ${code}.
200
+ `);
201
+ process.exit(code ?? 1);
202
+ }
203
+ });
204
+ }
205
+ async function commitCommand(opts = {}) {
206
+ loadDotenv();
207
+ dieIfNotGitRepo();
208
+ const apiKey = loadApiKey() ?? process.env.DEEPSEEK_API_KEY;
209
+ if (!apiKey) {
210
+ process.stderr.write(
211
+ "reasonix commit: DEEPSEEK_API_KEY not set. Run `reasonix setup` to save one, or export it.\n"
212
+ );
213
+ process.exit(1);
214
+ }
215
+ const diff = readDiff();
216
+ if (!diff) {
217
+ process.stderr.write(
218
+ "reasonix commit: no staged changes and working tree is clean \u2014 nothing to commit.\n"
219
+ );
220
+ process.exit(1);
221
+ }
222
+ if (diff.source === "working-tree") {
223
+ process.stderr.write(
224
+ "reasonix commit: nothing staged \u2014 drafting from working-tree diff. Stage your changes and re-run, or use the draft as a starting point.\n"
225
+ );
226
+ }
227
+ if (diff.truncated) {
228
+ process.stderr.write(
229
+ "reasonix commit: diff exceeded 80KB; head + tail sent to the model. Large diffs often produce vague drafts \u2014 consider committing in smaller chunks.\n"
230
+ );
231
+ }
232
+ const client = new DeepSeekClient({ apiKey });
233
+ const model = opts.model ?? DEFAULT_MODEL;
234
+ const recentCommits = readRecentCommits();
235
+ let message = "";
236
+ let firstPass = true;
237
+ while (true) {
238
+ if (firstPass) {
239
+ process.stdout.write("Drafting commit message\u2026\n");
240
+ } else {
241
+ process.stdout.write("Regenerating\u2026\n");
242
+ }
243
+ firstPass = false;
244
+ try {
245
+ message = await draftMessage(client, model, diff, recentCommits);
246
+ } catch (err) {
247
+ process.stderr.write(`reasonix commit: model call failed \u2014 ${err.message}
248
+ `);
249
+ process.exit(1);
250
+ }
251
+ if (!message) {
252
+ process.stderr.write("reasonix commit: model returned an empty draft. Try again.\n");
253
+ process.exit(1);
254
+ }
255
+ printDraft(message);
256
+ if (opts.yes) break;
257
+ if (diff.source === "working-tree") {
258
+ process.stdout.write(
259
+ "(no staged changes \u2014 draft printed above for you to copy. Stage with `git add` and re-run to commit.)\n"
260
+ );
261
+ return;
262
+ }
263
+ const choice = await promptChoice();
264
+ if (choice === "accept") break;
265
+ if (choice === "cancel") {
266
+ process.stderr.write("commit cancelled.\n");
267
+ return;
268
+ }
269
+ if (choice === "edit") {
270
+ const edited = editInExternal(message);
271
+ if (edited) {
272
+ message = edited;
273
+ printDraft(message);
274
+ const next = await promptChoice();
275
+ if (next === "accept") break;
276
+ if (next === "cancel") {
277
+ process.stderr.write("commit cancelled.\n");
278
+ return;
279
+ }
280
+ }
281
+ }
282
+ }
283
+ commitWithMessage(message);
284
+ }
285
+ export {
286
+ commitCommand
287
+ };
288
+ //# sourceMappingURL=commit-RPZBOZS2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/commit.ts"],"sourcesContent":["/** Drafts via diff + recent log (style mimicry); commit uses `-F -` so multi-line bodies survive shell quoting. */\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { DeepSeekClient } from \"../../client.js\";\nimport { loadApiKey } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\n\nexport interface CommitOptions {\n /** Override the default model (deepseek-v4-flash). */\n model?: string;\n /** Skip the confirmation step — useful in scripts where the diff has been pre-reviewed. */\n yes?: boolean;\n}\n\nconst DEFAULT_MODEL = \"deepseek-v4-flash\";\nconst DIFF_BYTE_CAP = 80 * 1024;\nconst LOG_COUNT = 10;\n\nconst SYSTEM_PROMPT = `You draft git commit messages.\n\nOutput ONLY the commit message — no preamble, no \\`\\`\\` fences, no \"Here's a commit message:\" lead-in. The first line of your output IS the commit subject.\n\nMatch the project's existing style:\n- Look at the recent commits provided. Mirror their voice, conventional-commit prefix usage (or absence), tense, length, body structure.\n- If recent commits use a \"type(scope): summary\" prefix, use it. If they don't, don't invent one.\n- Subject line: one line, ≤72 chars, imperative mood, no trailing period.\n- Body (optional): explain WHY when the diff isn't self-evident. Wrap at ~72 chars. Skip the body for trivial changes — repeating the subject in the body is noise.\n\nThe diff is the source of truth for what changed; describe THAT, not your guesses about the broader project. If the diff includes a deletion you can't explain from the surrounding context, name it but don't speculate about why.\n\nNo emojis unless the recent commits use them.\nNo co-author trailers, no \"Generated with X\" footers.`;\n\nfunction runGit(\n args: string[],\n opts: { input?: string } = {},\n): { stdout: string; stderr: string; status: number | null } {\n const result = spawnSync(\"git\", args, {\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n input: opts.input,\n maxBuffer: 32 * 1024 * 1024,\n });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n status: result.status,\n };\n}\n\nfunction dieIfNotGitRepo(): void {\n const r = runGit([\"rev-parse\", \"--is-inside-work-tree\"]);\n if (r.status !== 0) {\n process.stderr.write(\"reasonix commit: not inside a git repository.\\n\");\n process.exit(1);\n }\n}\n\ninterface DiffResult {\n diff: string;\n source: \"staged\" | \"working-tree\";\n truncated: boolean;\n}\n\nfunction readDiff(): DiffResult | null {\n const staged = runGit([\"diff\", \"--staged\", \"--no-color\"]);\n if (staged.status !== 0) {\n process.stderr.write(`reasonix commit: git diff --staged failed: ${staged.stderr.trim()}\\n`);\n process.exit(1);\n }\n if (staged.stdout.trim().length > 0) {\n return capDiff(staged.stdout, \"staged\");\n }\n const wt = runGit([\"diff\", \"--no-color\"]);\n if (wt.stdout.trim().length === 0) {\n return null;\n }\n return capDiff(wt.stdout, \"working-tree\");\n}\n\nfunction capDiff(raw: string, source: \"staged\" | \"working-tree\"): DiffResult {\n if (raw.length <= DIFF_BYTE_CAP) {\n return { diff: raw, source, truncated: false };\n }\n const head = raw.slice(0, Math.floor(DIFF_BYTE_CAP * 0.7));\n const tail = raw.slice(-Math.floor(DIFF_BYTE_CAP * 0.3));\n return {\n diff: `${head}\\n\\n[… ${raw.length - DIFF_BYTE_CAP} bytes of diff truncated …]\\n\\n${tail}`,\n source,\n truncated: true,\n };\n}\n\nfunction readRecentCommits(): string {\n const r = runGit([\"log\", `-${LOG_COUNT}`, \"--no-merges\", \"--format=%s%n%b%n---END---\"]);\n if (r.status !== 0) {\n // Repo may not have any commits yet (initial commit case). Don't\n // fail — let the model work from the diff alone.\n return \"\";\n }\n return r.stdout.trim();\n}\n\nasync function draftMessage(\n client: DeepSeekClient,\n model: string,\n diff: DiffResult,\n recentCommits: string,\n): Promise<string> {\n const userParts: string[] = [];\n if (recentCommits) {\n userParts.push(`Recent commits (style reference):\\n\\n${recentCommits}`);\n }\n if (diff.source === \"working-tree\") {\n userParts.push(\n \"(NOTE: diff is from the working tree, not the staging area — nothing is staged yet. The user will stage selectively after seeing the draft.)\",\n );\n }\n userParts.push(`Diff to summarize:\\n\\n${diff.diff}`);\n\n const resp = await client.chat({\n model,\n messages: [\n { role: \"system\", content: SYSTEM_PROMPT },\n { role: \"user\", content: userParts.join(\"\\n\\n\") },\n ],\n temperature: 0.2,\n });\n return stripCodeFences(resp.content.trim());\n}\n\nfunction stripCodeFences(s: string): string {\n // Some models still wrap output in ``` despite the system prompt\n // telling them not to. Strip a single leading + trailing fence pair\n // if present. Only operates on a wrapping pair — internal fences\n // (a code block inside the body) stay.\n const trimmed = s.trim();\n const fenceOpen = /^```[a-zA-Z]*\\n/;\n const fenceClose = /\\n?```$/;\n if (fenceOpen.test(trimmed) && fenceClose.test(trimmed)) {\n return trimmed.replace(fenceOpen, \"\").replace(fenceClose, \"\").trim();\n }\n return trimmed;\n}\n\nfunction printDraft(message: string): void {\n const sep = \"─\".repeat(60);\n process.stdout.write(`\\n${sep}\\n${message}\\n${sep}\\n\\n`);\n}\n\nasync function promptChoice(): Promise<\"accept\" | \"regen\" | \"edit\" | \"cancel\"> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(\"[a]ccept / [r]egenerate / [e]dit / [c]ancel: \");\n const k = answer.trim().toLowerCase();\n if (k === \"\" || k === \"a\" || k === \"y\" || k === \"yes\") return \"accept\";\n if (k === \"r\" || k === \"regen\" || k === \"regenerate\") return \"regen\";\n if (k === \"e\" || k === \"edit\") return \"edit\";\n return \"cancel\";\n } finally {\n rl.close();\n }\n}\n\nfunction editInExternal(initial: string): string | null {\n const editor = process.env.GIT_EDITOR ?? process.env.VISUAL ?? process.env.EDITOR;\n if (!editor) {\n process.stderr.write(\n \"reasonix commit: no $EDITOR / $VISUAL / $GIT_EDITOR set — can't open editor. Pick [a]ccept and `git commit --amend` afterwards.\\n\",\n );\n return null;\n }\n const dir = mkdtempSync(join(tmpdir(), \"reasonix-commit-\"));\n const path = join(dir, \"COMMIT_EDITMSG\");\n writeFileSync(path, initial, \"utf8\");\n // spawnSync with shell:true is required so $EDITOR strings like\n // `code --wait` work — they're shell command lines, not argv tuples.\n // The trust boundary is the user's own env var; matches how git\n // itself launches editors.\n const result = spawnSync(`${editor} \"${path}\"`, {\n stdio: \"inherit\",\n shell: true,\n });\n if (result.status !== 0) {\n try {\n unlinkSync(path);\n } catch {\n /* ignore */\n }\n process.stderr.write(\n `reasonix commit: editor exited ${result.status} — keeping prior draft.\\n`,\n );\n return null;\n }\n let edited: string;\n try {\n edited = readFileSync(path, \"utf8\");\n } catch {\n return null;\n } finally {\n try {\n unlinkSync(path);\n } catch {\n /* ignore */\n }\n }\n // Strip git's standard `# …` comment lines, even though we didn't\n // emit any — a user habituated to `git commit` may add `#`-prefixed\n // notes by reflex.\n const cleaned = edited\n .split(/\\r?\\n/)\n .filter((line) => !/^\\s*#/.test(line))\n .join(\"\\n\")\n .trim();\n return cleaned || null;\n}\n\nfunction commitWithMessage(message: string): void {\n // -F - reads the message from stdin, sidestepping shell quoting and\n // letting multi-line bodies through cleanly. Inherit stdio so the\n // user sees git's own confirmation / pre-commit hook output.\n const child = spawn(\"git\", [\"commit\", \"-F\", \"-\"], {\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n });\n child.stdin.write(message);\n child.stdin.end();\n child.on(\"close\", (code) => {\n if (code !== 0) {\n process.stderr.write(`reasonix commit: git commit exited ${code}.\\n`);\n process.exit(code ?? 1);\n }\n });\n}\n\nexport async function commitCommand(opts: CommitOptions = {}): Promise<void> {\n loadDotenv();\n dieIfNotGitRepo();\n\n const apiKey = loadApiKey() ?? process.env.DEEPSEEK_API_KEY;\n if (!apiKey) {\n process.stderr.write(\n \"reasonix commit: DEEPSEEK_API_KEY not set. Run `reasonix setup` to save one, or export it.\\n\",\n );\n process.exit(1);\n }\n\n const diff = readDiff();\n if (!diff) {\n process.stderr.write(\n \"reasonix commit: no staged changes and working tree is clean — nothing to commit.\\n\",\n );\n process.exit(1);\n }\n if (diff.source === \"working-tree\") {\n process.stderr.write(\n \"reasonix commit: nothing staged — drafting from working-tree diff. Stage your changes and re-run, or use the draft as a starting point.\\n\",\n );\n }\n if (diff.truncated) {\n process.stderr.write(\n \"reasonix commit: diff exceeded 80KB; head + tail sent to the model. Large diffs often produce vague drafts — consider committing in smaller chunks.\\n\",\n );\n }\n\n const client = new DeepSeekClient({ apiKey });\n const model = opts.model ?? DEFAULT_MODEL;\n const recentCommits = readRecentCommits();\n\n let message = \"\";\n let firstPass = true;\n while (true) {\n if (firstPass) {\n process.stdout.write(\"Drafting commit message…\\n\");\n } else {\n process.stdout.write(\"Regenerating…\\n\");\n }\n firstPass = false;\n try {\n message = await draftMessage(client, model, diff, recentCommits);\n } catch (err) {\n process.stderr.write(`reasonix commit: model call failed — ${(err as Error).message}\\n`);\n process.exit(1);\n }\n if (!message) {\n process.stderr.write(\"reasonix commit: model returned an empty draft. Try again.\\n\");\n process.exit(1);\n }\n printDraft(message);\n\n if (opts.yes) break;\n if (diff.source === \"working-tree\") {\n // Refuse to commit a working-tree-derived draft — the staging\n // area is empty so `git commit` would fail anyway. Print the\n // draft so the user can copy it; exit 0 because we did our job.\n process.stdout.write(\n \"(no staged changes — draft printed above for you to copy. Stage with `git add` and re-run to commit.)\\n\",\n );\n return;\n }\n\n const choice = await promptChoice();\n if (choice === \"accept\") break;\n if (choice === \"cancel\") {\n process.stderr.write(\"commit cancelled.\\n\");\n return;\n }\n if (choice === \"edit\") {\n const edited = editInExternal(message);\n if (edited) {\n message = edited;\n printDraft(message);\n // Re-prompt: the user may want to edit again, accept, etc.\n const next = await promptChoice();\n if (next === \"accept\") break;\n if (next === \"cancel\") {\n process.stderr.write(\"commit cancelled.\\n\");\n return;\n }\n // next is \"regen\" or another \"edit\" — fall through to the\n // loop top to re-draft (regen) or land back at this branch.\n }\n // editor returned no edit — loop top will regen by default.\n }\n // Anything else (regen, or unsuccessful edit) → loop top redraws.\n }\n\n commitWithMessage(message);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ RecordView
4
+ } from "./chunk-APPB3ZPQ.js";
5
+ import {
6
+ diffTranscripts,
7
+ findNextDivergence,
8
+ findPrevDivergence,
9
+ renderMarkdown,
10
+ renderSummaryTable
11
+ } from "./chunk-XJLZ4HKU.js";
12
+ import {
13
+ readTranscript
14
+ } from "./chunk-XHQIK7B6.js";
15
+ import "./chunk-KMWKGPFZ.js";
16
+ import "./chunk-ORM6PK57.js";
17
+
18
+ // src/cli/commands/diff.ts
19
+ import { writeFileSync } from "fs";
20
+ import { basename } from "path";
21
+ import { render } from "ink";
22
+ import React2 from "react";
23
+
24
+ // src/cli/ui/DiffApp.tsx
25
+ import { Box, Static, Text, useApp, useInput } from "ink";
26
+ import React, { useState } from "react";
27
+ function DiffApp({ report }) {
28
+ const { exit } = useApp();
29
+ const maxIdx = Math.max(0, report.pairs.length - 1);
30
+ const initialIdx = report.firstDivergenceTurn ? report.pairs.findIndex((p) => p.turn === report.firstDivergenceTurn) : 0;
31
+ const [idx, setIdx] = useState(Math.max(0, initialIdx));
32
+ useInput((input, key) => {
33
+ if (input === "q" || key.ctrl && input === "c") {
34
+ exit();
35
+ return;
36
+ }
37
+ if (input === "j" || key.downArrow || input === " " || key.return) {
38
+ setIdx((i) => Math.min(maxIdx, i + 1));
39
+ } else if (input === "k" || key.upArrow) {
40
+ setIdx((i) => Math.max(0, i - 1));
41
+ } else if (input === "g") {
42
+ setIdx(0);
43
+ } else if (input === "G") {
44
+ setIdx(maxIdx);
45
+ } else if (input === "n") {
46
+ const next = findNextDivergence(report.pairs, idx);
47
+ if (next !== -1) setIdx(next);
48
+ } else if (input === "N" || input === "p") {
49
+ const prev = findPrevDivergence(report.pairs, idx);
50
+ if (prev !== -1) setIdx(prev);
51
+ }
52
+ });
53
+ const pair = report.pairs[idx];
54
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(DiffHeader, { report }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React.createElement(Text, null, pair ? /* @__PURE__ */ React.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React.createElement(Box, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React.createElement(Text, null, pair.divergenceNote)) : null, /* @__PURE__ */ React.createElement(Box, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { bold: true }, "j"), "/", /* @__PURE__ */ React.createElement(Text, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "k"), "/", /* @__PURE__ */ React.createElement(Text, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "N"), "/", /* @__PURE__ */ React.createElement(Text, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "g"), "/", /* @__PURE__ */ React.createElement(Text, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "q"), " ", "quit")));
55
+ }
56
+ function DiffHeader({ report }) {
57
+ const a = report.a;
58
+ const b = report.b;
59
+ const cacheDelta = b.stats.cacheHitRatio - a.stats.cacheHitRatio;
60
+ const costDelta = a.stats.totalCostUsd > 0 ? (b.stats.totalCostUsd - a.stats.totalCostUsd) / a.stats.totalCostUsd * 100 : 0;
61
+ const aStable = a.stats.prefixHashes.length <= 1;
62
+ const bStable = b.stats.prefixHashes.length <= 1;
63
+ let prefixLine = null;
64
+ if (aStable !== bStable) {
65
+ const stableLabel = aStable ? report.a.label : report.b.label;
66
+ const churnLabel = aStable ? report.b.label : report.a.label;
67
+ const churnCount = aStable ? b.stats.prefixHashes.length : a.stats.prefixHashes.length;
68
+ prefixLine = `${stableLabel} stayed byte-stable; ${churnLabel} churned ${churnCount} distinct prefixes.`;
69
+ } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
70
+ prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
71
+ }
72
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React.createElement(Text, { color: "blue" }, a.label), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " vs B="), /* @__PURE__ */ React.createElement(Text, { color: "magenta" }, b.label)), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "cache "), /* @__PURE__ */ React.createElement(Text, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React.createElement(Text, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React.createElement(Text, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "cost "), /* @__PURE__ */ React.createElement(Text, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React.createElement(Text, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React.createElement(Text, { color: costDelta <= 0 ? "green" : "red", bold: true }, " ", costDelta >= 0 ? "+" : "", costDelta.toFixed(1), "%")), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "model calls "), /* @__PURE__ */ React.createElement(Text, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true, italic: true }, prefixLine)) : null);
73
+ }
74
+ function Pane({
75
+ label,
76
+ headerColor,
77
+ records
78
+ }) {
79
+ return /* @__PURE__ */ React.createElement(
80
+ Box,
81
+ {
82
+ flexDirection: "column",
83
+ flexGrow: 1,
84
+ paddingX: 1,
85
+ borderStyle: "single",
86
+ borderColor: headerColor
87
+ },
88
+ /* @__PURE__ */ React.createElement(Text, { color: headerColor, bold: true }, label),
89
+ records.length === 0 ? /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React.createElement(Static, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React.createElement(RecordView, { key, rec, compact: true }))
90
+ );
91
+ }
92
+ function KindBadge({ kind }) {
93
+ if (kind === "match") {
94
+ return /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u2713 match");
95
+ }
96
+ if (kind === "diverge") {
97
+ return /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "\u2605 diverge");
98
+ }
99
+ if (kind === "only_in_a") {
100
+ return /* @__PURE__ */ React.createElement(Text, { color: "blue" }, "\u2190 only in A");
101
+ }
102
+ return /* @__PURE__ */ React.createElement(Text, { color: "magenta" }, "\u2192 only in B");
103
+ }
104
+ function paneRecords(pair, side) {
105
+ if (!pair) return [];
106
+ const tools = side === "a" ? pair.aTools : pair.bTools;
107
+ const assistant = side === "a" ? pair.aAssistant : pair.bAssistant;
108
+ const out = [...tools];
109
+ if (assistant) out.push(assistant);
110
+ return out;
111
+ }
112
+
113
+ // src/cli/commands/diff.ts
114
+ async function diffCommand(opts) {
115
+ const aParsed = readTranscript(opts.a);
116
+ const bParsed = readTranscript(opts.b);
117
+ const report = diffTranscripts(
118
+ { label: opts.labelA ?? basename(opts.a), parsed: aParsed },
119
+ { label: opts.labelB ?? basename(opts.b), parsed: bParsed }
120
+ );
121
+ const wantMarkdown = !!opts.mdPath;
122
+ const wantPrint = opts.print || !process.stdout.isTTY;
123
+ const wantTui = opts.tui || !wantPrint && !wantMarkdown;
124
+ if (wantMarkdown) {
125
+ console.log(renderSummaryTable(report));
126
+ const md = renderMarkdown(report);
127
+ writeFileSync(opts.mdPath, md, "utf8");
128
+ console.log(`
129
+ markdown report written to ${opts.mdPath}`);
130
+ return;
131
+ }
132
+ if (wantTui) {
133
+ const { waitUntilExit } = render(React2.createElement(DiffApp, { report }), {
134
+ exitOnCtrlC: true,
135
+ patchConsole: false
136
+ });
137
+ await waitUntilExit();
138
+ return;
139
+ }
140
+ console.log(renderSummaryTable(report));
141
+ }
142
+ export {
143
+ diffCommand
144
+ };
145
+ //# sourceMappingURL=diff-NTEHCSDW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/diff.ts","../../src/cli/ui/DiffApp.tsx"],"sourcesContent":["import { writeFileSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { render } from \"ink\";\nimport React from \"react\";\nimport { diffTranscripts, renderMarkdown, renderSummaryTable } from \"../../transcript/diff.js\";\nimport { readTranscript } from \"../../transcript/log.js\";\nimport { DiffApp } from \"../ui/DiffApp.js\";\n\nexport interface DiffOptions {\n a: string;\n b: string;\n mdPath?: string;\n labelA?: string;\n labelB?: string;\n /** Force stdout summary table (no Ink TUI). Auto when stdout isn't a TTY. */\n print?: boolean;\n /** Force the TUI even when stdout isn't a TTY (rare). */\n tui?: boolean;\n}\n\nexport async function diffCommand(opts: DiffOptions): Promise<void> {\n const aParsed = readTranscript(opts.a);\n const bParsed = readTranscript(opts.b);\n\n const report = diffTranscripts(\n { label: opts.labelA ?? basename(opts.a), parsed: aParsed },\n { label: opts.labelB ?? basename(opts.b), parsed: bParsed },\n );\n\n const wantMarkdown = !!opts.mdPath;\n const wantPrint = opts.print || !process.stdout.isTTY;\n const wantTui = opts.tui || (!wantPrint && !wantMarkdown);\n\n if (wantMarkdown) {\n // Markdown export implies the user wants an artifact, not a TUI.\n // Still echo the stdout summary to confirm the action.\n console.log(renderSummaryTable(report));\n const md = renderMarkdown(report);\n writeFileSync(opts.mdPath!, md, \"utf8\");\n console.log(`\\nmarkdown report written to ${opts.mdPath}`);\n return;\n }\n\n if (wantTui) {\n const { waitUntilExit } = render(React.createElement(DiffApp, { report }), {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n await waitUntilExit();\n return;\n }\n\n // stdout fallback (piped, --print, or non-TTY)\n console.log(renderSummaryTable(report));\n}\n","/**\n * Ink TUI for `reasonix diff`. Split-pane: A on the left, B on the right,\n * shared cursor. Header shows aggregate deltas; footer shows the current\n * pair's divergence note (if any) + key cheat sheet.\n *\n * j/k moves the cursor by one turn; n/N jumps to the next/prev divergent\n * turn — which is the whole point of a diff tool. Quit with q.\n *\n * Pure navigation lives in src/diff.ts (findNextDivergence / findPrevDivergence).\n */\n\nimport { Box, Static, Text, useApp, useInput } from \"ink\";\nimport React, { useState } from \"react\";\nimport {\n type DiffReport,\n type TurnPair,\n findNextDivergence,\n findPrevDivergence,\n} from \"../../transcript/diff.js\";\nimport { RecordView } from \"./RecordView.js\";\n\nexport interface DiffAppProps {\n report: DiffReport;\n}\n\nexport function DiffApp({ report }: DiffAppProps) {\n const { exit } = useApp();\n const maxIdx = Math.max(0, report.pairs.length - 1);\n // Start at the first divergence when one exists — that's the user's most\n // likely destination. Falls back to idx 0 for fully-matching diffs.\n const initialIdx = report.firstDivergenceTurn\n ? report.pairs.findIndex((p) => p.turn === report.firstDivergenceTurn)\n : 0;\n const [idx, setIdx] = useState(Math.max(0, initialIdx));\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 === \"n\") {\n const next = findNextDivergence(report.pairs, idx);\n if (next !== -1) setIdx(next);\n } else if (input === \"N\" || input === \"p\") {\n const prev = findPrevDivergence(report.pairs, idx);\n if (prev !== -1) setIdx(prev);\n }\n });\n\n const pair = report.pairs[idx];\n\n return (\n <Box flexDirection=\"column\">\n <DiffHeader report={report} />\n\n <Box marginTop={1} paddingX={1} justifyContent=\"space-between\">\n <Text color=\"cyan\" bold>\n turn {pair?.turn ?? \"?\"} ({idx + 1} / {report.pairs.length})\n </Text>\n <Text>{pair ? <KindBadge kind={pair.kind} /> : null}</Text>\n </Box>\n\n <Box flexDirection=\"row\" marginTop={1}>\n <Pane label={report.a.label} headerColor=\"blue\" records={paneRecords(pair, \"a\")} />\n <Pane label={report.b.label} headerColor=\"magenta\" records={paneRecords(pair, \"b\")} />\n </Box>\n\n {pair?.divergenceNote ? (\n <Box marginTop={1} paddingX={1}>\n <Text color=\"yellow\">★ </Text>\n <Text>{pair.divergenceNote}</Text>\n </Box>\n ) : null}\n\n <Box marginTop={1} paddingX={1} borderStyle=\"single\" borderColor=\"gray\">\n <Text dimColor>\n <Text bold>j</Text>/<Text bold>↓</Text> next · <Text bold>k</Text>/<Text bold>↑</Text>{\" \"}\n prev · <Text bold>n</Text> next-diverge · <Text bold>N</Text>/<Text bold>p</Text>{\" \"}\n prev-diverge · <Text bold>g</Text>/<Text bold>G</Text> first/last · <Text bold>q</Text>{\" \"}\n quit\n </Text>\n </Box>\n </Box>\n );\n}\n\n// ----------------------------------------------------------------------------\n\nfunction DiffHeader({ report }: { report: DiffReport }) {\n const a = report.a;\n const b = report.b;\n\n const cacheDelta = b.stats.cacheHitRatio - a.stats.cacheHitRatio;\n const costDelta =\n a.stats.totalCostUsd > 0\n ? ((b.stats.totalCostUsd - a.stats.totalCostUsd) / a.stats.totalCostUsd) * 100\n : 0;\n\n // Prefix stability one-liner (same logic as the stdout summary).\n const aStable = a.stats.prefixHashes.length <= 1;\n const bStable = b.stats.prefixHashes.length <= 1;\n let prefixLine: string | null = null;\n if (aStable !== bStable) {\n const stableLabel = aStable ? report.a.label : report.b.label;\n const churnLabel = aStable ? report.b.label : report.a.label;\n const churnCount = aStable ? b.stats.prefixHashes.length : a.stats.prefixHashes.length;\n prefixLine = `${stableLabel} stayed byte-stable; ${churnLabel} churned ${churnCount} distinct prefixes.`;\n } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {\n prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}… — cache delta attributable to log stability, not prompt change.`;\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Box justifyContent=\"space-between\">\n <Text>\n <Text color=\"cyan\" bold>\n reasonix diff\n </Text>\n <Text dimColor> · A=</Text>\n <Text color=\"blue\">{a.label}</Text>\n <Text dimColor> vs B=</Text>\n <Text color=\"magenta\">{b.label}</Text>\n </Text>\n <Text dimColor>{report.pairs.length} turns aligned</Text>\n </Box>\n\n <Box marginTop={1} gap={3}>\n <Text>\n <Text dimColor>cache </Text>\n <Text>{(a.stats.cacheHitRatio * 100).toFixed(1)}%</Text>\n <Text dimColor> → </Text>\n <Text>{(b.stats.cacheHitRatio * 100).toFixed(1)}%</Text>\n <Text color={cacheDelta >= 0 ? \"green\" : \"red\"} bold>\n {\" \"}\n {cacheDelta >= 0 ? \"+\" : \"\"}\n {(cacheDelta * 100).toFixed(1)}pp\n </Text>\n </Text>\n <Text>\n <Text dimColor>cost </Text>\n <Text>${a.stats.totalCostUsd.toFixed(6)}</Text>\n <Text dimColor> → </Text>\n <Text>${b.stats.totalCostUsd.toFixed(6)}</Text>\n <Text color={costDelta <= 0 ? \"green\" : \"red\"} bold>\n {\" \"}\n {costDelta >= 0 ? \"+\" : \"\"}\n {costDelta.toFixed(1)}%\n </Text>\n </Text>\n <Text>\n <Text dimColor>model calls </Text>\n <Text>\n {a.stats.turns} → {b.stats.turns}\n </Text>\n </Text>\n </Box>\n\n {prefixLine ? (\n <Box marginTop={1}>\n <Text dimColor italic>\n {prefixLine}\n </Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Pane({\n label,\n headerColor,\n records,\n}: {\n label: string;\n headerColor: \"blue\" | \"magenta\";\n records: TurnPair[\"aTools\"];\n}) {\n return (\n <Box\n flexDirection=\"column\"\n flexGrow={1}\n paddingX={1}\n borderStyle=\"single\"\n borderColor={headerColor}\n >\n <Text color={headerColor} bold>\n {label}\n </Text>\n {records.length === 0 ? (\n <Box marginTop={1}>\n <Text dimColor italic>\n (no records on this side for this turn)\n </Text>\n </Box>\n ) : (\n <Static items={records.map((rec, i) => ({ key: `${label}-${i}`, rec }))}>\n {({ key, rec }) => <RecordView key={key} rec={rec} compact />}\n </Static>\n )}\n </Box>\n );\n}\n\nfunction KindBadge({ kind }: { kind: TurnPair[\"kind\"] }) {\n if (kind === \"match\") {\n return <Text color=\"green\">✓ match</Text>;\n }\n if (kind === \"diverge\") {\n return <Text color=\"yellow\">★ diverge</Text>;\n }\n if (kind === \"only_in_a\") {\n return <Text color=\"blue\">← only in A</Text>;\n }\n return <Text color=\"magenta\">→ only in B</Text>;\n}\n\n// ----------------------------------------------------------------------------\n\nfunction paneRecords(pair: TurnPair | undefined, side: \"a\" | \"b\"): TurnPair[\"aTools\"] {\n if (!pair) return [];\n const tools = side === \"a\" ? pair.aTools : pair.bTools;\n const assistant = side === \"a\" ? pair.aAssistant : pair.bAssistant;\n const out: TurnPair[\"aTools\"] = [...tools];\n if (assistant) out.push(assistant);\n return out;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,OAAOA,YAAW;;;ACQlB,SAAS,KAAK,QAAQ,MAAM,QAAQ,gBAAgB;AACpD,OAAO,SAAS,gBAAgB;AAazB,SAAS,QAAQ,EAAE,OAAO,GAAiB;AAChD,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,SAAS,KAAK,IAAI,GAAG,OAAO,MAAM,SAAS,CAAC;AAGlD,QAAM,aAAa,OAAO,sBACtB,OAAO,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,OAAO,mBAAmB,IACnE;AACJ,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,KAAK,IAAI,GAAG,UAAU,CAAC;AAEtD,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,UAAU,OAAQ,IAAI,QAAQ,UAAU,KAAM;AAChD,WAAK;AACL;AAAA,IACF;AACA,QAAI,UAAU,OAAO,IAAI,aAAa,UAAU,OAAO,IAAI,QAAQ;AACjE,aAAO,CAAC,MAAM,KAAK,IAAI,QAAQ,IAAI,CAAC,CAAC;AAAA,IACvC,WAAW,UAAU,OAAO,IAAI,SAAS;AACvC,aAAO,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,IAClC,WAAW,UAAU,KAAK;AACxB,aAAO,CAAC;AAAA,IACV,WAAW,UAAU,KAAK;AACxB,aAAO,MAAM;AAAA,IACf,WAAW,UAAU,KAAK;AACxB,YAAM,OAAO,mBAAmB,OAAO,OAAO,GAAG;AACjD,UAAI,SAAS,GAAI,QAAO,IAAI;AAAA,IAC9B,WAAW,UAAU,OAAO,UAAU,KAAK;AACzC,YAAM,OAAO,mBAAmB,OAAO,OAAO,GAAG;AACjD,UAAI,SAAS,GAAI,QAAO,IAAI;AAAA,IAC9B;AAAA,EACF,CAAC;AAED,QAAM,OAAO,OAAO,MAAM,GAAG;AAE7B,SACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,cAAW,QAAgB,GAE5B,oCAAC,OAAI,WAAW,GAAG,UAAU,GAAG,gBAAe,mBAC7C,oCAAC,QAAK,OAAM,QAAO,MAAI,QAAC,SAChB,MAAM,QAAQ,KAAI,MAAG,MAAM,GAAE,OAAI,OAAO,MAAM,QAAO,GAC7D,GACA,oCAAC,YAAM,OAAO,oCAAC,aAAU,MAAM,KAAK,MAAM,IAAK,IAAK,CACtD,GAEA,oCAAC,OAAI,eAAc,OAAM,WAAW,KAClC,oCAAC,QAAK,OAAO,OAAO,EAAE,OAAO,aAAY,QAAO,SAAS,YAAY,MAAM,GAAG,GAAG,GACjF,oCAAC,QAAK,OAAO,OAAO,EAAE,OAAO,aAAY,WAAU,SAAS,YAAY,MAAM,GAAG,GAAG,CACtF,GAEC,MAAM,iBACL,oCAAC,OAAI,WAAW,GAAG,UAAU,KAC3B,oCAAC,QAAK,OAAM,YAAS,SAAE,GACvB,oCAAC,YAAM,KAAK,cAAe,CAC7B,IACE,MAEJ,oCAAC,OAAI,WAAW,GAAG,UAAU,GAAG,aAAY,UAAS,aAAY,UAC/D,oCAAC,QAAK,UAAQ,QACZ,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,KAAC,oCAAC,QAAK,MAAI,QAAC,QAAC,GAAO,eAAQ,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,KAAC,oCAAC,QAAK,MAAI,QAAC,QAAC,GAAQ,KAAI,cACpF,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,uBAAgB,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,KAAC,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAQ,KAAI,sBACvE,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,KAAC,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAO,qBAAc,oCAAC,QAAK,MAAI,QAAC,GAAC,GAAQ,KAAI,MAE9F,CACF,CACF;AAEJ;AAIA,SAAS,WAAW,EAAE,OAAO,GAA2B;AACtD,QAAM,IAAI,OAAO;AACjB,QAAM,IAAI,OAAO;AAEjB,QAAM,aAAa,EAAE,MAAM,gBAAgB,EAAE,MAAM;AACnD,QAAM,YACJ,EAAE,MAAM,eAAe,KACjB,EAAE,MAAM,eAAe,EAAE,MAAM,gBAAgB,EAAE,MAAM,eAAgB,MACzE;AAGN,QAAM,UAAU,EAAE,MAAM,aAAa,UAAU;AAC/C,QAAM,UAAU,EAAE,MAAM,aAAa,UAAU;AAC/C,MAAI,aAA4B;AAChC,MAAI,YAAY,SAAS;AACvB,UAAM,cAAc,UAAU,OAAO,EAAE,QAAQ,OAAO,EAAE;AACxD,UAAM,aAAa,UAAU,OAAO,EAAE,QAAQ,OAAO,EAAE;AACvD,UAAM,aAAa,UAAU,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,aAAa;AAChF,iBAAa,GAAG,WAAW,wBAAwB,UAAU,YAAY,UAAU;AAAA,EACrF,WAAW,EAAE,MAAM,aAAa,CAAC,KAAK,EAAE,MAAM,aAAa,CAAC,MAAM,EAAE,MAAM,aAAa,CAAC,GAAG;AACzF,iBAAa,sBAAsB,EAAE,MAAM,aAAa,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,EACzE;AAEA,SACE,oCAAC,OAAI,eAAc,UAAS,aAAY,SAAQ,aAAY,QAAO,UAAU,KAC3E,oCAAC,OAAI,gBAAe,mBAClB,oCAAC,YACC,oCAAC,QAAK,OAAM,QAAO,MAAI,QAAC,eAExB,GACA,oCAAC,QAAK,UAAQ,QAAC,UAAK,GACpB,oCAAC,QAAK,OAAM,UAAQ,EAAE,KAAM,GAC5B,oCAAC,QAAK,UAAQ,QAAC,QAAM,GACrB,oCAAC,QAAK,OAAM,aAAW,EAAE,KAAM,CACjC,GACA,oCAAC,QAAK,UAAQ,QAAE,OAAO,MAAM,QAAO,gBAAc,CACpD,GAEA,oCAAC,OAAI,WAAW,GAAG,KAAK,KACtB,oCAAC,YACC,oCAAC,QAAK,UAAQ,QAAC,QAAM,GACrB,oCAAC,aAAO,EAAE,MAAM,gBAAgB,KAAK,QAAQ,CAAC,GAAE,GAAC,GACjD,oCAAC,QAAK,UAAQ,QAAC,UAAG,GAClB,oCAAC,aAAO,EAAE,MAAM,gBAAgB,KAAK,QAAQ,CAAC,GAAE,GAAC,GACjD,oCAAC,QAAK,OAAO,cAAc,IAAI,UAAU,OAAO,MAAI,QACjD,MACA,cAAc,IAAI,MAAM,KACvB,aAAa,KAAK,QAAQ,CAAC,GAAE,IACjC,CACF,GACA,oCAAC,YACC,oCAAC,QAAK,UAAQ,QAAC,OAAK,GACpB,oCAAC,YAAK,KAAE,EAAE,MAAM,aAAa,QAAQ,CAAC,CAAE,GACxC,oCAAC,QAAK,UAAQ,QAAC,UAAG,GAClB,oCAAC,YAAK,KAAE,EAAE,MAAM,aAAa,QAAQ,CAAC,CAAE,GACxC,oCAAC,QAAK,OAAO,aAAa,IAAI,UAAU,OAAO,MAAI,QAChD,MACA,aAAa,IAAI,MAAM,IACvB,UAAU,QAAQ,CAAC,GAAE,GACxB,CACF,GACA,oCAAC,YACC,oCAAC,QAAK,UAAQ,QAAC,cAAY,GAC3B,oCAAC,YACE,EAAE,MAAM,OAAM,YAAI,EAAE,MAAM,KAC7B,CACF,CACF,GAEC,aACC,oCAAC,OAAI,WAAW,KACd,oCAAC,QAAK,UAAQ,MAAC,QAAM,QAClB,UACH,CACF,IACE,IACN;AAEJ;AAEA,SAAS,KAAK;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAY;AAAA,MACZ,aAAa;AAAA;AAAA,IAEb,oCAAC,QAAK,OAAO,aAAa,MAAI,QAC3B,KACH;AAAA,IACC,QAAQ,WAAW,IAClB,oCAAC,OAAI,WAAW,KACd,oCAAC,QAAK,UAAQ,MAAC,QAAM,QAAC,yCAEtB,CACF,IAEA,oCAAC,UAAO,OAAO,QAAQ,IAAI,CAAC,KAAK,OAAO,EAAE,KAAK,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,EAAE,KACnE,CAAC,EAAE,KAAK,IAAI,MAAM,oCAAC,cAAW,KAAU,KAAU,SAAO,MAAC,CAC7D;AAAA,EAEJ;AAEJ;AAEA,SAAS,UAAU,EAAE,KAAK,GAA+B;AACvD,MAAI,SAAS,SAAS;AACpB,WAAO,oCAAC,QAAK,OAAM,WAAQ,cAAO;AAAA,EACpC;AACA,MAAI,SAAS,WAAW;AACtB,WAAO,oCAAC,QAAK,OAAM,YAAS,gBAAS;AAAA,EACvC;AACA,MAAI,SAAS,aAAa;AACxB,WAAO,oCAAC,QAAK,OAAM,UAAO,kBAAW;AAAA,EACvC;AACA,SAAO,oCAAC,QAAK,OAAM,aAAU,kBAAW;AAC1C;AAIA,SAAS,YAAY,MAA4B,MAAqC;AACpF,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,QAAM,QAAQ,SAAS,MAAM,KAAK,SAAS,KAAK;AAChD,QAAM,YAAY,SAAS,MAAM,KAAK,aAAa,KAAK;AACxD,QAAM,MAA0B,CAAC,GAAG,KAAK;AACzC,MAAI,UAAW,KAAI,KAAK,SAAS;AACjC,SAAO;AACT;;;ADrNA,eAAsB,YAAY,MAAkC;AAClE,QAAM,UAAU,eAAe,KAAK,CAAC;AACrC,QAAM,UAAU,eAAe,KAAK,CAAC;AAErC,QAAM,SAAS;AAAA,IACb,EAAE,OAAO,KAAK,UAAU,SAAS,KAAK,CAAC,GAAG,QAAQ,QAAQ;AAAA,IAC1D,EAAE,OAAO,KAAK,UAAU,SAAS,KAAK,CAAC,GAAG,QAAQ,QAAQ;AAAA,EAC5D;AAEA,QAAM,eAAe,CAAC,CAAC,KAAK;AAC5B,QAAM,YAAY,KAAK,SAAS,CAAC,QAAQ,OAAO;AAChD,QAAM,UAAU,KAAK,OAAQ,CAAC,aAAa,CAAC;AAE5C,MAAI,cAAc;AAGhB,YAAQ,IAAI,mBAAmB,MAAM,CAAC;AACtC,UAAM,KAAK,eAAe,MAAM;AAChC,kBAAc,KAAK,QAAS,IAAI,MAAM;AACtC,YAAQ,IAAI;AAAA,6BAAgC,KAAK,MAAM,EAAE;AACzD;AAAA,EACF;AAEA,MAAI,SAAS;AACX,UAAM,EAAE,cAAc,IAAI,OAAOC,OAAM,cAAc,SAAS,EAAE,OAAO,CAAC,GAAG;AAAA,MACzE,aAAa;AAAA,MACb,cAAc;AAAA,IAChB,CAAC;AACD,UAAM,cAAc;AACpB;AAAA,EACF;AAGA,UAAQ,IAAI,mBAAmB,MAAM,CAAC;AACxC;","names":["React","React"]}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ doctorCommand,
4
+ runDoctorChecks
5
+ } from "./chunk-D5DKXIP5.js";
6
+ import "./chunk-KMWKGPFZ.js";
7
+ import "./chunk-3Q3C4W66.js";
8
+ import "./chunk-RZILUXUC.js";
9
+ import "./chunk-WBDE4IRI.js";
10
+ import "./chunk-2AWTGJ2C.js";
11
+ import "./chunk-5X7LZJDE.js";
12
+ import "./chunk-DFP4YSVM.js";
13
+ import "./chunk-QGE6AF76.js";
14
+ import "./chunk-DULSP7JH.js";
15
+ export {
16
+ doctorCommand,
17
+ runDoctorChecks
18
+ };
19
+ //# sourceMappingURL=doctor-3TGB2NZN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}