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,513 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ sanitizeName,
4
+ sessionsDir
5
+ } from "./chunk-DFP4YSVM.js";
6
+
7
+ // src/code/plan-store.ts
8
+ import {
9
+ existsSync,
10
+ mkdirSync,
11
+ readFileSync,
12
+ readdirSync,
13
+ renameSync,
14
+ statSync,
15
+ unlinkSync,
16
+ writeFileSync
17
+ } from "fs";
18
+ import { dirname, join } from "path";
19
+ function planStatePath(sessionName) {
20
+ return join(sessionsDir(), `${sanitizeName(sessionName)}.plan.json`);
21
+ }
22
+ function loadPlanState(sessionName) {
23
+ const path = planStatePath(sessionName);
24
+ if (!existsSync(path)) return null;
25
+ try {
26
+ const raw = readFileSync(path, "utf8");
27
+ const parsed = JSON.parse(raw);
28
+ if (!parsed || typeof parsed !== "object") return null;
29
+ if (parsed.version !== 1) return null;
30
+ if (!Array.isArray(parsed.steps)) return null;
31
+ if (!Array.isArray(parsed.completedStepIds)) return null;
32
+ if (typeof parsed.updatedAt !== "string") return null;
33
+ const steps = [];
34
+ for (const s of parsed.steps) {
35
+ if (!s || typeof s !== "object") continue;
36
+ const e = s;
37
+ if (typeof e.id !== "string" || !e.id) continue;
38
+ if (typeof e.title !== "string" || !e.title) continue;
39
+ if (typeof e.action !== "string" || !e.action) continue;
40
+ const step = { id: e.id, title: e.title, action: e.action };
41
+ if (e.risk === "low" || e.risk === "med" || e.risk === "high") step.risk = e.risk;
42
+ steps.push(step);
43
+ }
44
+ if (steps.length === 0) return null;
45
+ const completedStepIds = parsed.completedStepIds.filter(
46
+ (id) => typeof id === "string" && id.length > 0
47
+ );
48
+ const out = {
49
+ version: 1,
50
+ steps,
51
+ completedStepIds,
52
+ updatedAt: parsed.updatedAt
53
+ };
54
+ if (typeof parsed.body === "string" && parsed.body) out.body = parsed.body;
55
+ if (typeof parsed.summary === "string" && parsed.summary) out.summary = parsed.summary;
56
+ return out;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ function savePlanState(sessionName, steps, completedStepIds, extras) {
62
+ const path = planStatePath(sessionName);
63
+ try {
64
+ mkdirSync(dirname(path), { recursive: true });
65
+ const state = {
66
+ version: 1,
67
+ steps,
68
+ completedStepIds: [...completedStepIds],
69
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
70
+ };
71
+ if (extras?.body) state.body = extras.body;
72
+ if (extras?.summary) state.summary = extras.summary;
73
+ writeFileSync(path, `${JSON.stringify(state, null, 2)}
74
+ `, "utf8");
75
+ } catch (err) {
76
+ process.stderr.write(
77
+ `\u25B8 plan-store: failed to save plan for "${sessionName}": ${err.message}
78
+ `
79
+ );
80
+ }
81
+ }
82
+ function clearPlanState(sessionName) {
83
+ const path = planStatePath(sessionName);
84
+ try {
85
+ if (existsSync(path)) unlinkSync(path);
86
+ } catch {
87
+ }
88
+ }
89
+ function archivePlanState(sessionName) {
90
+ const active = planStatePath(sessionName);
91
+ if (!existsSync(active)) return null;
92
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
93
+ const suffix = Math.random().toString(36).slice(2, 6);
94
+ const archive = join(
95
+ sessionsDir(),
96
+ `${sanitizeName(sessionName)}.plan.${stamp}-${suffix}.done.json`
97
+ );
98
+ try {
99
+ renameSync(active, archive);
100
+ return archive;
101
+ } catch (err) {
102
+ process.stderr.write(
103
+ `\u25B8 plan-store: failed to archive plan for "${sessionName}": ${err.message}
104
+ `
105
+ );
106
+ return null;
107
+ }
108
+ }
109
+ function listPlanArchives(sessionName) {
110
+ const dir = sessionsDir();
111
+ if (!existsSync(dir)) return [];
112
+ const prefix = `${sanitizeName(sessionName)}.plan.`;
113
+ const suffix = ".done.json";
114
+ let entries;
115
+ try {
116
+ entries = readdirSync(dir);
117
+ } catch {
118
+ return [];
119
+ }
120
+ const summaries = [];
121
+ for (const name of entries) {
122
+ if (!name.startsWith(prefix) || !name.endsWith(suffix)) continue;
123
+ const full = join(dir, name);
124
+ try {
125
+ const raw = readFileSync(full, "utf8");
126
+ const parsed = JSON.parse(raw);
127
+ if (parsed.version !== 1) continue;
128
+ if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;
129
+ const steps = parsed.steps.filter(
130
+ (s) => !!s && typeof s === "object" && typeof s.id === "string" && typeof s.title === "string" && typeof s.action === "string"
131
+ );
132
+ if (steps.length === 0) continue;
133
+ const completedStepIds = Array.isArray(parsed.completedStepIds) ? parsed.completedStepIds.filter((id) => typeof id === "string" && !!id) : [];
134
+ let completedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : "";
135
+ if (!completedAt || Number.isNaN(Date.parse(completedAt))) {
136
+ try {
137
+ completedAt = statSync(full).mtime.toISOString();
138
+ } catch {
139
+ completedAt = (/* @__PURE__ */ new Date(0)).toISOString();
140
+ }
141
+ }
142
+ const entry = { path: full, completedAt, steps, completedStepIds };
143
+ if (typeof parsed.body === "string" && parsed.body) entry.body = parsed.body;
144
+ if (typeof parsed.summary === "string" && parsed.summary) entry.summary = parsed.summary;
145
+ summaries.push(entry);
146
+ } catch {
147
+ }
148
+ }
149
+ summaries.sort((a, b) => b.completedAt.localeCompare(a.completedAt));
150
+ return summaries;
151
+ }
152
+ function relativeTime(updatedAt, now = Date.now()) {
153
+ const t = Date.parse(updatedAt);
154
+ if (Number.isNaN(t)) return updatedAt;
155
+ const diffMs = Math.max(0, now - t);
156
+ const sec = Math.floor(diffMs / 1e3);
157
+ if (sec < 60) return `${sec}s ago`;
158
+ const min = Math.floor(sec / 60);
159
+ if (min < 60) return `${min}m ago`;
160
+ const hr = Math.floor(min / 60);
161
+ if (hr < 24) return `${hr}h ago`;
162
+ const day = Math.floor(hr / 24);
163
+ if (day < 7) return `${day}d ago`;
164
+ return updatedAt.slice(0, 10);
165
+ }
166
+
167
+ // src/cli/ui/slash/commands.ts
168
+ var SLASH_COMMANDS = [
169
+ { cmd: "help", group: "chat", summary: "show the full command reference", aliases: ["?"] },
170
+ {
171
+ cmd: "new",
172
+ group: "chat",
173
+ summary: "start a fresh conversation (clear context + scrollback)",
174
+ aliases: ["reset", "clear"]
175
+ },
176
+ { cmd: "retry", group: "chat", summary: "truncate & resend your last message (fresh sample)" },
177
+ {
178
+ cmd: "compact",
179
+ group: "chat",
180
+ summary: "fold older turns into a summary message (cache-safe). Auto-fires at 50% ctx; this is the manual trigger."
181
+ },
182
+ {
183
+ cmd: "stop",
184
+ group: "chat",
185
+ summary: "abort the current model turn (typed alternative to Esc)"
186
+ },
187
+ {
188
+ cmd: "preset",
189
+ group: "setup",
190
+ argsHint: "<auto|flash|pro>",
191
+ summary: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock. Bare opens picker.",
192
+ argCompleter: ["auto", "flash", "pro"]
193
+ },
194
+ {
195
+ cmd: "model",
196
+ group: "setup",
197
+ argsHint: "<id>",
198
+ summary: "switch DeepSeek model id. Bare opens picker.",
199
+ argCompleter: "models"
200
+ },
201
+ { cmd: "status", group: "info", summary: "current model, flags, context, session" },
202
+ {
203
+ cmd: "cost",
204
+ group: "info",
205
+ argsHint: "[text]",
206
+ summary: "bare \u2192 last turn's spend (Usage card); with text \u2192 estimate cost of sending it next (worst-case + likely-cache)"
207
+ },
208
+ {
209
+ cmd: "context",
210
+ group: "info",
211
+ summary: "show context-window breakdown (system / tools / log / input)"
212
+ },
213
+ {
214
+ cmd: "stats",
215
+ group: "info",
216
+ summary: "cross-session cost dashboard (today / week / month / all-time \xB7 cache hit \xB7 vs Claude)"
217
+ },
218
+ {
219
+ cmd: "doctor",
220
+ group: "info",
221
+ summary: "health check (api / config / api-reach / index / hooks / project)"
222
+ },
223
+ { cmd: "sessions", group: "session", summary: "list saved sessions (current marked with \u25B8)" },
224
+ { cmd: "mcp", group: "extend", summary: "list MCP servers + tools attached to this session" },
225
+ {
226
+ cmd: "resource",
227
+ group: "extend",
228
+ argsHint: "[uri]",
229
+ summary: "browse + read MCP resources (no arg \u2192 list URIs; <uri> \u2192 fetch contents)",
230
+ argCompleter: "mcp-resources"
231
+ },
232
+ {
233
+ cmd: "prompt",
234
+ group: "extend",
235
+ argsHint: "[name]",
236
+ summary: "browse + fetch MCP prompts (no arg \u2192 list names; <name> \u2192 render prompt)",
237
+ argCompleter: "mcp-prompts"
238
+ },
239
+ {
240
+ cmd: "memory",
241
+ group: "extend",
242
+ argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]",
243
+ summary: "show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)"
244
+ },
245
+ {
246
+ cmd: "skill",
247
+ group: "extend",
248
+ argsHint: "[list|show <name>|new <name>|<name> [args]]",
249
+ summary: "list / run / scaffold user skills (<project>/.reasonix/skills + ~/.reasonix/skills)"
250
+ },
251
+ {
252
+ cmd: "init",
253
+ group: "code",
254
+ argsHint: "[force]",
255
+ summary: "scan the project and synthesize a baseline REASONIX.md (model writes; review with /apply). `force` overwrites an existing file.",
256
+ contextual: "code",
257
+ argCompleter: ["force"]
258
+ },
259
+ {
260
+ cmd: "apply",
261
+ group: "code",
262
+ argsHint: "[N|N,M|N-M]",
263
+ summary: "commit pending edit blocks to disk (no arg \u2192 all; `1`, `1,3`, or `1-4` \u2192 that subset, rest stay pending)",
264
+ contextual: "code"
265
+ },
266
+ {
267
+ cmd: "discard",
268
+ group: "code",
269
+ argsHint: "[N|N,M|N-M]",
270
+ summary: "drop pending edit blocks without writing (no arg \u2192 all; indices \u2192 that subset)",
271
+ contextual: "code"
272
+ },
273
+ {
274
+ cmd: "walk",
275
+ group: "code",
276
+ summary: "step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)",
277
+ contextual: "code"
278
+ },
279
+ {
280
+ cmd: "undo",
281
+ group: "code",
282
+ summary: "roll back the last applied edit batch",
283
+ contextual: "code"
284
+ },
285
+ {
286
+ cmd: "history",
287
+ group: "code",
288
+ summary: "list every edit batch this session (ids for /show, undone markers)",
289
+ contextual: "code"
290
+ },
291
+ {
292
+ cmd: "show",
293
+ group: "code",
294
+ argsHint: "[id]",
295
+ summary: "dump a stored edit diff (omit id for newest non-undone)",
296
+ contextual: "code"
297
+ },
298
+ {
299
+ cmd: "commit",
300
+ group: "code",
301
+ argsHint: '"msg"',
302
+ summary: "git add -A && git commit -m ...",
303
+ contextual: "code"
304
+ },
305
+ {
306
+ cmd: "mode",
307
+ group: "code",
308
+ argsHint: "[review|auto|yolo]",
309
+ summary: "edit-gate: review (queue) \xB7 auto (apply+undo) \xB7 yolo (apply+auto-shell). Shift+Tab cycles.",
310
+ contextual: "code",
311
+ argCompleter: ["review", "auto", "yolo"]
312
+ },
313
+ {
314
+ cmd: "plan",
315
+ group: "code",
316
+ argsHint: "[on|off]",
317
+ summary: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
318
+ contextual: "code",
319
+ argCompleter: ["on", "off"]
320
+ },
321
+ {
322
+ cmd: "checkpoint",
323
+ group: "code",
324
+ argsHint: "[name|list|forget <id>]",
325
+ summary: "snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.",
326
+ contextual: "code",
327
+ argCompleter: ["list", "forget"]
328
+ },
329
+ {
330
+ cmd: "restore",
331
+ group: "code",
332
+ argsHint: "<name|id>",
333
+ summary: "roll back files to a named checkpoint (see /checkpoint list)",
334
+ contextual: "code"
335
+ },
336
+ {
337
+ cmd: "cwd",
338
+ group: "code",
339
+ argsHint: "<path>",
340
+ summary: "switch the workspace root mid-session \u2014 re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker",
341
+ contextual: "code",
342
+ aliases: ["sandbox"]
343
+ },
344
+ {
345
+ cmd: "jobs",
346
+ group: "jobs",
347
+ summary: "list background jobs started by run_background",
348
+ contextual: "code"
349
+ },
350
+ {
351
+ cmd: "kill",
352
+ group: "jobs",
353
+ argsHint: "<id>",
354
+ summary: "stop a background job by id (SIGTERM \u2192 SIGKILL after grace)",
355
+ contextual: "code"
356
+ },
357
+ {
358
+ cmd: "logs",
359
+ group: "jobs",
360
+ argsHint: "<id> [lines]",
361
+ summary: "tail a background job's output (default last 80 lines)",
362
+ contextual: "code"
363
+ },
364
+ {
365
+ cmd: "pro",
366
+ group: "advanced",
367
+ argsHint: "[off]",
368
+ summary: "arm v4-pro for the NEXT turn only (one-shot \xB7 auto-disarms after turn)",
369
+ argCompleter: ["off"]
370
+ },
371
+ {
372
+ cmd: "budget",
373
+ group: "advanced",
374
+ argsHint: "[usd|off]",
375
+ summary: "session USD cap \u2014 warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status",
376
+ argCompleter: ["off", "1", "5", "10", "20", "50"]
377
+ },
378
+ {
379
+ cmd: "language",
380
+ group: "advanced",
381
+ argsHint: "<EN|zh-CN>",
382
+ summary: "switch the runtime language",
383
+ argCompleter: ["EN", "zh-CN"],
384
+ aliases: ["lang"]
385
+ },
386
+ {
387
+ cmd: "search-engine",
388
+ group: "advanced",
389
+ argsHint: "<mojeek|searxng> [<endpoint>]",
390
+ summary: "switch web search backend \u2014 mojeek (default, no deps) or searxng (self-hosted)",
391
+ argCompleter: ["mojeek", "searxng"],
392
+ aliases: ["se"]
393
+ },
394
+ {
395
+ cmd: "hooks",
396
+ group: "advanced",
397
+ argsHint: "[reload]",
398
+ summary: "list active hooks (settings.json under .reasonix/) \xB7 reload re-reads from disk"
399
+ },
400
+ {
401
+ cmd: "permissions",
402
+ group: "advanced",
403
+ argsHint: "[list|add <prefix>|remove <prefix|N>|clear confirm]",
404
+ summary: "show / edit shell allowlist (builtin read-only \xB7 per-project: ~/.reasonix/config.json)",
405
+ argCompleter: ["list", "add", "remove", "clear"]
406
+ },
407
+ {
408
+ cmd: "dashboard",
409
+ group: "advanced",
410
+ argsHint: "[stop]",
411
+ summary: "launch the embedded web dashboard (127.0.0.1, token-gated)",
412
+ argCompleter: ["stop"]
413
+ },
414
+ {
415
+ cmd: "loop",
416
+ group: "advanced",
417
+ argsHint: "<5s..6h> <prompt> \xB7 stop \xB7 (no args = status)",
418
+ summary: "auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop"
419
+ },
420
+ {
421
+ cmd: "plans",
422
+ group: "advanced",
423
+ summary: "list this session's active + archived plans, newest first"
424
+ },
425
+ {
426
+ cmd: "replay",
427
+ group: "advanced",
428
+ summary: "load an archived plan as a read-only Time Travel snapshot (default: newest)",
429
+ argsHint: "[N]"
430
+ },
431
+ {
432
+ cmd: "update",
433
+ group: "advanced",
434
+ summary: "show current vs latest version + the shell command to upgrade"
435
+ },
436
+ { cmd: "exit", group: "advanced", summary: "quit the TUI", aliases: ["quit", "q"] }
437
+ ];
438
+ function suggestSlashCommands(prefix, codeMode = false, counts) {
439
+ const p = prefix.toLowerCase();
440
+ const matches = SLASH_COMMANDS.filter((c) => {
441
+ if (c.contextual === "code" && !codeMode) return false;
442
+ if (p === "" && c.group === "advanced") return false;
443
+ if (c.cmd.startsWith(p)) return true;
444
+ return c.aliases?.some((a) => a.startsWith(p)) ?? false;
445
+ });
446
+ if (!counts) return matches;
447
+ const indexOf = new Map(matches.map((s, i) => [s.cmd, i]));
448
+ return [...matches].sort((a, b) => {
449
+ const diff = (counts[b.cmd] ?? 0) - (counts[a.cmd] ?? 0);
450
+ if (diff !== 0) return diff;
451
+ return (indexOf.get(a.cmd) ?? 0) - (indexOf.get(b.cmd) ?? 0);
452
+ });
453
+ }
454
+ function countAdvancedCommands(codeMode) {
455
+ return SLASH_COMMANDS.filter(
456
+ (c) => c.group === "advanced" && (c.contextual !== "code" || codeMode)
457
+ ).length;
458
+ }
459
+ var ALIAS_TO_CMD = (() => {
460
+ const m = {};
461
+ for (const spec of SLASH_COMMANDS) {
462
+ if (!spec.aliases) continue;
463
+ for (const a of spec.aliases) m[a] = spec.cmd;
464
+ }
465
+ return m;
466
+ })();
467
+ function resolveSlashAlias(name) {
468
+ return ALIAS_TO_CMD[name] ?? name;
469
+ }
470
+ function detectSlashArgContext(input, codeMode = false) {
471
+ const m = /^\/(\S+) ([\s\S]*)$/.exec(input);
472
+ if (!m) return null;
473
+ const cmdName = resolveSlashAlias(m[1].toLowerCase());
474
+ const tail = m[2] ?? "";
475
+ const spec = SLASH_COMMANDS.find(
476
+ (s) => s.cmd === cmdName && (s.contextual !== "code" || codeMode)
477
+ );
478
+ if (!spec) return null;
479
+ const hasInternalSpace = /\s/.test(tail);
480
+ const partialOffset = input.length - tail.length;
481
+ if (hasInternalSpace) {
482
+ return { spec, partial: tail, partialOffset, kind: "hint" };
483
+ }
484
+ return {
485
+ spec,
486
+ partial: tail,
487
+ partialOffset,
488
+ kind: spec.argCompleter ? "picker" : "hint"
489
+ };
490
+ }
491
+ function parseSlash(text) {
492
+ if (!text.startsWith("/")) return null;
493
+ const parts = text.slice(1).trim().split(/\s+/);
494
+ const cmd = parts[0]?.toLowerCase() ?? "";
495
+ if (!cmd) return null;
496
+ return { cmd, args: parts.slice(1) };
497
+ }
498
+
499
+ export {
500
+ loadPlanState,
501
+ savePlanState,
502
+ clearPlanState,
503
+ archivePlanState,
504
+ listPlanArchives,
505
+ relativeTime,
506
+ SLASH_COMMANDS,
507
+ suggestSlashCommands,
508
+ countAdvancedCommands,
509
+ resolveSlashAlias,
510
+ detectSlashArgContext,
511
+ parseSlash
512
+ };
513
+ //# sourceMappingURL=chunk-FXGQ5NHE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/code/plan-store.ts","../../src/cli/ui/slash/commands.ts"],"sourcesContent":["/** Persists structured plan state alongside the JSONL log; markdown body lives in the log (it was a tool result) and replays on resume. */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { sanitizeName, sessionsDir } from \"../memory/session.js\";\nimport type { PlanStep } from \"../tools/plan.js\";\n\nexport interface PlanStateOnDisk {\n /** File format version — bump when shape changes. */\n version: 1;\n steps: PlanStep[];\n completedStepIds: string[];\n /** ISO8601 timestamp of the last write. */\n updatedAt: string;\n body?: string;\n summary?: string;\n}\n\nexport function planStatePath(sessionName: string): string {\n return join(sessionsDir(), `${sanitizeName(sessionName)}.plan.json`);\n}\n\nexport function loadPlanState(sessionName: string): PlanStateOnDisk | null {\n const path = planStatePath(sessionName);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<PlanStateOnDisk>;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (parsed.version !== 1) return null;\n if (!Array.isArray(parsed.steps)) return null;\n if (!Array.isArray(parsed.completedStepIds)) return null;\n if (typeof parsed.updatedAt !== \"string\") return null;\n // Defensive: filter out any malformed step entries so a partially\n // corrupted file still yields a usable subset.\n const steps: PlanStep[] = [];\n for (const s of parsed.steps) {\n if (!s || typeof s !== \"object\") continue;\n const e = s as unknown as Record<string, unknown>;\n if (typeof e.id !== \"string\" || !e.id) continue;\n if (typeof e.title !== \"string\" || !e.title) continue;\n if (typeof e.action !== \"string\" || !e.action) continue;\n const step: PlanStep = { id: e.id, title: e.title, action: e.action };\n if (e.risk === \"low\" || e.risk === \"med\" || e.risk === \"high\") step.risk = e.risk;\n steps.push(step);\n }\n if (steps.length === 0) return null;\n const completedStepIds = parsed.completedStepIds.filter(\n (id): id is string => typeof id === \"string\" && id.length > 0,\n );\n const out: PlanStateOnDisk = {\n version: 1,\n steps,\n completedStepIds,\n updatedAt: parsed.updatedAt,\n };\n if (typeof parsed.body === \"string\" && parsed.body) out.body = parsed.body;\n if (typeof parsed.summary === \"string\" && parsed.summary) out.summary = parsed.summary;\n return out;\n } catch {\n return null;\n }\n}\n\n/** Best-effort: write failure logs to stderr instead of crashing the TUI. */\nexport function savePlanState(\n sessionName: string,\n steps: PlanStep[],\n completedStepIds: Iterable<string>,\n extras?: { body?: string; summary?: string },\n): void {\n const path = planStatePath(sessionName);\n try {\n mkdirSync(dirname(path), { recursive: true });\n const state: PlanStateOnDisk = {\n version: 1,\n steps,\n completedStepIds: [...completedStepIds],\n updatedAt: new Date().toISOString(),\n };\n if (extras?.body) state.body = extras.body;\n if (extras?.summary) state.summary = extras.summary;\n writeFileSync(path, `${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n } catch (err) {\n process.stderr.write(\n `▸ plan-store: failed to save plan for \"${sessionName}\": ${(err as Error).message}\\n`,\n );\n }\n}\n\n/** Remove the persisted plan, if any. Used on cancel / clean reset. */\nexport function clearPlanState(sessionName: string): void {\n const path = planStatePath(sessionName);\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n /* nothing to do — leftover file is harmless, will be overwritten next save */\n }\n}\n\n/** Random suffix avoids same-millisecond collision; `:`/`.` swapped for Windows-safe filenames. */\nexport function archivePlanState(sessionName: string): string | null {\n const active = planStatePath(sessionName);\n if (!existsSync(active)) return null;\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const suffix = Math.random().toString(36).slice(2, 6);\n const archive = join(\n sessionsDir(),\n `${sanitizeName(sessionName)}.plan.${stamp}-${suffix}.done.json`,\n );\n try {\n renameSync(active, archive);\n return archive;\n } catch (err) {\n process.stderr.write(\n `▸ plan-store: failed to archive plan for \"${sessionName}\": ${(err as Error).message}\\n`,\n );\n return null;\n }\n}\n\nexport interface PlanArchiveSummary {\n path: string;\n completedAt: string;\n steps: PlanStep[];\n completedStepIds: string[];\n /** Markdown body, when the archive carried it. */\n body?: string;\n /** One-line human-friendly title, when supplied. */\n summary?: string;\n}\n\nexport function listPlanArchives(sessionName: string): PlanArchiveSummary[] {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n const prefix = `${sanitizeName(sessionName)}.plan.`;\n const suffix = \".done.json\";\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n const summaries: PlanArchiveSummary[] = [];\n for (const name of entries) {\n if (!name.startsWith(prefix) || !name.endsWith(suffix)) continue;\n const full = join(dir, name);\n try {\n const raw = readFileSync(full, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<PlanStateOnDisk>;\n if (parsed.version !== 1) continue;\n if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;\n const steps = parsed.steps.filter(\n (s): s is PlanStep =>\n !!s &&\n typeof s === \"object\" &&\n typeof (s as PlanStep).id === \"string\" &&\n typeof (s as PlanStep).title === \"string\" &&\n typeof (s as PlanStep).action === \"string\",\n );\n if (steps.length === 0) continue;\n const completedStepIds = Array.isArray(parsed.completedStepIds)\n ? parsed.completedStepIds.filter((id): id is string => typeof id === \"string\" && !!id)\n : [];\n // Prefer the file's own updatedAt; fall back to mtime if missing\n // or unparseable so a hand-edited archive still sorts sensibly.\n let completedAt = typeof parsed.updatedAt === \"string\" ? parsed.updatedAt : \"\";\n if (!completedAt || Number.isNaN(Date.parse(completedAt))) {\n try {\n completedAt = statSync(full).mtime.toISOString();\n } catch {\n completedAt = new Date(0).toISOString();\n }\n }\n const entry: PlanArchiveSummary = { path: full, completedAt, steps, completedStepIds };\n if (typeof parsed.body === \"string\" && parsed.body) entry.body = parsed.body;\n if (typeof parsed.summary === \"string\" && parsed.summary) entry.summary = parsed.summary;\n summaries.push(entry);\n } catch {\n // Skip the corrupt archive entirely.\n }\n }\n summaries.sort((a, b) => b.completedAt.localeCompare(a.completedAt));\n return summaries;\n}\n\n/** Falls back to raw ISO string past a week — \"47 days ago\" misleads more than it helps. */\nexport function relativeTime(updatedAt: string, now: number = Date.now()): string {\n const t = Date.parse(updatedAt);\n if (Number.isNaN(t)) return updatedAt;\n const diffMs = Math.max(0, now - t);\n const sec = Math.floor(diffMs / 1000);\n if (sec < 60) return `${sec}s ago`;\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.floor(min / 60);\n if (hr < 24) return `${hr}h ago`;\n const day = Math.floor(hr / 24);\n if (day < 7) return `${day}d ago`;\n return updatedAt.slice(0, 10);\n}\n","import type { SlashArgContext, SlashCommandSpec } from \"./types.js\";\n\nexport const SLASH_COMMANDS: readonly SlashCommandSpec[] = [\n { cmd: \"help\", group: \"chat\", summary: \"show the full command reference\", aliases: [\"?\"] },\n {\n cmd: \"new\",\n group: \"chat\",\n summary: \"start a fresh conversation (clear context + scrollback)\",\n aliases: [\"reset\", \"clear\"],\n },\n { cmd: \"retry\", group: \"chat\", summary: \"truncate & resend your last message (fresh sample)\" },\n {\n cmd: \"compact\",\n group: \"chat\",\n summary:\n \"fold older turns into a summary message (cache-safe). Auto-fires at 50% ctx; this is the manual trigger.\",\n },\n {\n cmd: \"stop\",\n group: \"chat\",\n summary: \"abort the current model turn (typed alternative to Esc)\",\n },\n\n {\n cmd: \"preset\",\n group: \"setup\",\n argsHint: \"<auto|flash|pro>\",\n summary: \"model bundle — auto escalates flash → pro, flash/pro lock. Bare opens picker.\",\n argCompleter: [\"auto\", \"flash\", \"pro\"],\n },\n {\n cmd: \"model\",\n group: \"setup\",\n argsHint: \"<id>\",\n summary: \"switch DeepSeek model id. Bare opens picker.\",\n argCompleter: \"models\",\n },\n\n { cmd: \"status\", group: \"info\", summary: \"current model, flags, context, session\" },\n {\n cmd: \"cost\",\n group: \"info\",\n argsHint: \"[text]\",\n summary:\n \"bare → last turn's spend (Usage card); with text → estimate cost of sending it next (worst-case + likely-cache)\",\n },\n {\n cmd: \"context\",\n group: \"info\",\n summary: \"show context-window breakdown (system / tools / log / input)\",\n },\n {\n cmd: \"stats\",\n group: \"info\",\n summary:\n \"cross-session cost dashboard (today / week / month / all-time · cache hit · vs Claude)\",\n },\n {\n cmd: \"doctor\",\n group: \"info\",\n summary: \"health check (api / config / api-reach / index / hooks / project)\",\n },\n\n { cmd: \"sessions\", group: \"session\", summary: \"list saved sessions (current marked with ▸)\" },\n\n { cmd: \"mcp\", group: \"extend\", summary: \"list MCP servers + tools attached to this session\" },\n {\n cmd: \"resource\",\n group: \"extend\",\n argsHint: \"[uri]\",\n summary: \"browse + read MCP resources (no arg → list URIs; <uri> → fetch contents)\",\n argCompleter: \"mcp-resources\",\n },\n {\n cmd: \"prompt\",\n group: \"extend\",\n argsHint: \"[name]\",\n summary: \"browse + fetch MCP prompts (no arg → list names; <name> → render prompt)\",\n argCompleter: \"mcp-prompts\",\n },\n {\n cmd: \"memory\",\n group: \"extend\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n summary: \"show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)\",\n },\n {\n cmd: \"skill\",\n group: \"extend\",\n argsHint: \"[list|show <name>|new <name>|<name> [args]]\",\n summary: \"list / run / scaffold user skills (<project>/.reasonix/skills + ~/.reasonix/skills)\",\n },\n\n {\n cmd: \"init\",\n group: \"code\",\n argsHint: \"[force]\",\n summary:\n \"scan the project and synthesize a baseline REASONIX.md (model writes; review with /apply). `force` overwrites an existing file.\",\n contextual: \"code\",\n argCompleter: [\"force\"],\n },\n {\n cmd: \"apply\",\n group: \"code\",\n argsHint: \"[N|N,M|N-M]\",\n summary:\n \"commit pending edit blocks to disk (no arg → all; `1`, `1,3`, or `1-4` → that subset, rest stay pending)\",\n contextual: \"code\",\n },\n {\n cmd: \"discard\",\n group: \"code\",\n argsHint: \"[N|N,M|N-M]\",\n summary: \"drop pending edit blocks without writing (no arg → all; indices → that subset)\",\n contextual: \"code\",\n },\n {\n cmd: \"walk\",\n group: \"code\",\n summary:\n \"step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)\",\n contextual: \"code\",\n },\n {\n cmd: \"undo\",\n group: \"code\",\n summary: \"roll back the last applied edit batch\",\n contextual: \"code\",\n },\n {\n cmd: \"history\",\n group: \"code\",\n summary: \"list every edit batch this session (ids for /show, undone markers)\",\n contextual: \"code\",\n },\n {\n cmd: \"show\",\n group: \"code\",\n argsHint: \"[id]\",\n summary: \"dump a stored edit diff (omit id for newest non-undone)\",\n contextual: \"code\",\n },\n {\n cmd: \"commit\",\n group: \"code\",\n argsHint: '\"msg\"',\n summary: \"git add -A && git commit -m ...\",\n contextual: \"code\",\n },\n {\n cmd: \"mode\",\n group: \"code\",\n argsHint: \"[review|auto|yolo]\",\n summary:\n \"edit-gate: review (queue) · auto (apply+undo) · yolo (apply+auto-shell). Shift+Tab cycles.\",\n contextual: \"code\",\n argCompleter: [\"review\", \"auto\", \"yolo\"],\n },\n {\n cmd: \"plan\",\n group: \"code\",\n argsHint: \"[on|off]\",\n summary: \"toggle read-only plan mode (writes bounced until submit_plan + approval)\",\n contextual: \"code\",\n argCompleter: [\"on\", \"off\"],\n },\n {\n cmd: \"checkpoint\",\n group: \"code\",\n argsHint: \"[name|list|forget <id>]\",\n summary:\n \"snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.\",\n contextual: \"code\",\n argCompleter: [\"list\", \"forget\"],\n },\n {\n cmd: \"restore\",\n group: \"code\",\n argsHint: \"<name|id>\",\n summary: \"roll back files to a named checkpoint (see /checkpoint list)\",\n contextual: \"code\",\n },\n {\n cmd: \"cwd\",\n group: \"code\",\n argsHint: \"<path>\",\n summary:\n \"switch the workspace root mid-session — re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker\",\n contextual: \"code\",\n aliases: [\"sandbox\"],\n },\n\n {\n cmd: \"jobs\",\n group: \"jobs\",\n summary: \"list background jobs started by run_background\",\n contextual: \"code\",\n },\n {\n cmd: \"kill\",\n group: \"jobs\",\n argsHint: \"<id>\",\n summary: \"stop a background job by id (SIGTERM → SIGKILL after grace)\",\n contextual: \"code\",\n },\n {\n cmd: \"logs\",\n group: \"jobs\",\n argsHint: \"<id> [lines]\",\n summary: \"tail a background job's output (default last 80 lines)\",\n contextual: \"code\",\n },\n\n {\n cmd: \"pro\",\n group: \"advanced\",\n argsHint: \"[off]\",\n summary: \"arm v4-pro for the NEXT turn only (one-shot · auto-disarms after turn)\",\n argCompleter: [\"off\"],\n },\n {\n cmd: \"budget\",\n group: \"advanced\",\n argsHint: \"[usd|off]\",\n summary:\n \"session USD cap — warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status\",\n argCompleter: [\"off\", \"1\", \"5\", \"10\", \"20\", \"50\"],\n },\n {\n cmd: \"language\",\n group: \"advanced\",\n argsHint: \"<EN|zh-CN>\",\n summary: \"switch the runtime language\",\n argCompleter: [\"EN\", \"zh-CN\"],\n aliases: [\"lang\"],\n },\n {\n cmd: \"search-engine\",\n group: \"advanced\",\n argsHint: \"<mojeek|searxng> [<endpoint>]\",\n summary: \"switch web search backend — mojeek (default, no deps) or searxng (self-hosted)\",\n argCompleter: [\"mojeek\", \"searxng\"],\n aliases: [\"se\"],\n },\n {\n cmd: \"hooks\",\n group: \"advanced\",\n argsHint: \"[reload]\",\n summary: \"list active hooks (settings.json under .reasonix/) · reload re-reads from disk\",\n },\n {\n cmd: \"permissions\",\n group: \"advanced\",\n argsHint: \"[list|add <prefix>|remove <prefix|N>|clear confirm]\",\n summary:\n \"show / edit shell allowlist (builtin read-only · per-project: ~/.reasonix/config.json)\",\n argCompleter: [\"list\", \"add\", \"remove\", \"clear\"],\n },\n {\n cmd: \"dashboard\",\n group: \"advanced\",\n argsHint: \"[stop]\",\n summary: \"launch the embedded web dashboard (127.0.0.1, token-gated)\",\n argCompleter: [\"stop\"],\n },\n {\n cmd: \"loop\",\n group: \"advanced\",\n argsHint: \"<5s..6h> <prompt> · stop · (no args = status)\",\n summary: \"auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop\",\n },\n {\n cmd: \"plans\",\n group: \"advanced\",\n summary: \"list this session's active + archived plans, newest first\",\n },\n {\n cmd: \"replay\",\n group: \"advanced\",\n summary: \"load an archived plan as a read-only Time Travel snapshot (default: newest)\",\n argsHint: \"[N]\",\n },\n {\n cmd: \"update\",\n group: \"advanced\",\n summary: \"show current vs latest version + the shell command to upgrade\",\n },\n { cmd: \"exit\", group: \"advanced\", summary: \"quit the TUI\", aliases: [\"quit\", \"q\"] },\n];\n\nexport function suggestSlashCommands(\n prefix: string,\n codeMode = false,\n counts?: Readonly<Record<string, number>>,\n): SlashCommandSpec[] {\n const p = prefix.toLowerCase();\n const matches = SLASH_COMMANDS.filter((c) => {\n if (c.contextual === \"code\" && !codeMode) return false;\n // Empty prefix = browsing the menu — advanced stays hidden until a letter is typed.\n if (p === \"\" && c.group === \"advanced\") return false;\n if (c.cmd.startsWith(p)) return true;\n return c.aliases?.some((a) => a.startsWith(p)) ?? false;\n });\n if (!counts) return matches;\n const indexOf = new Map(matches.map((s, i) => [s.cmd, i]));\n return [...matches].sort((a, b) => {\n const diff = (counts[b.cmd] ?? 0) - (counts[a.cmd] ?? 0);\n if (diff !== 0) return diff;\n return (indexOf.get(a.cmd) ?? 0) - (indexOf.get(b.cmd) ?? 0);\n });\n}\n\nexport function countAdvancedCommands(codeMode: boolean): number {\n return SLASH_COMMANDS.filter(\n (c) => c.group === \"advanced\" && (c.contextual !== \"code\" || codeMode),\n ).length;\n}\n\n/** alias → canonical cmd map, derived from SLASH_COMMANDS at module init. */\nconst ALIAS_TO_CMD: Readonly<Record<string, string>> = (() => {\n const m: Record<string, string> = {};\n for (const spec of SLASH_COMMANDS) {\n if (!spec.aliases) continue;\n for (const a of spec.aliases) m[a] = spec.cmd;\n }\n return m;\n})();\n\nexport function resolveSlashAlias(name: string): string {\n return ALIAS_TO_CMD[name] ?? name;\n}\n\n/** Picker fires only when arg tail has no internal whitespace; past that it's a usage hint. */\nexport function detectSlashArgContext(input: string, codeMode = false): SlashArgContext | null {\n const m = /^\\/(\\S+) ([\\s\\S]*)$/.exec(input);\n if (!m) return null;\n const cmdName = resolveSlashAlias(m[1]!.toLowerCase());\n const tail = m[2] ?? \"\";\n const spec = SLASH_COMMANDS.find(\n (s) => s.cmd === cmdName && (s.contextual !== \"code\" || codeMode),\n );\n if (!spec) return null;\n const hasInternalSpace = /\\s/.test(tail);\n const partialOffset = input.length - tail.length;\n if (hasInternalSpace) {\n return { spec, partial: tail, partialOffset, kind: \"hint\" };\n }\n return {\n spec,\n partial: tail,\n partialOffset,\n kind: spec.argCompleter ? \"picker\" : \"hint\",\n };\n}\n\nexport function parseSlash(text: string): { cmd: string; args: string[] } | null {\n if (!text.startsWith(\"/\")) return null;\n const parts = text.slice(1).trim().split(/\\s+/);\n const cmd = parts[0]?.toLowerCase() ?? \"\";\n if (!cmd) return null;\n return { cmd, args: parts.slice(1) };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ sanitizeName,
4
+ sessionsDir
5
+ } from "./chunk-DFP4YSVM.js";
6
+
7
+ // src/adapters/event-sink-jsonl.ts
8
+ import { chmodSync, createWriteStream, mkdirSync } from "fs";
9
+ import { dirname, join } from "path";
10
+ function eventLogPath(sessionName) {
11
+ return join(sessionsDir(), `${sanitizeName(sessionName)}.events.jsonl`);
12
+ }
13
+ var JsonlEventSink = class {
14
+ constructor(stream) {
15
+ this.stream = stream;
16
+ }
17
+ stream;
18
+ buffered = 0;
19
+ append(ev) {
20
+ if (ev.type === "model.delta") return;
21
+ this.stream.write(`${JSON.stringify(ev)}
22
+ `);
23
+ this.buffered++;
24
+ }
25
+ flush() {
26
+ return new Promise((resolve) => {
27
+ if (this.buffered === 0) return resolve();
28
+ this.stream.uncork();
29
+ this.buffered = 0;
30
+ resolve();
31
+ });
32
+ }
33
+ close() {
34
+ return new Promise((resolve) => {
35
+ this.stream.end(() => resolve());
36
+ });
37
+ }
38
+ };
39
+ function openEventSink(path) {
40
+ mkdirSync(dirname(path), { recursive: true });
41
+ const stream = createWriteStream(path, { flags: "a" });
42
+ try {
43
+ chmodSync(path, 384);
44
+ } catch {
45
+ }
46
+ return new JsonlEventSink(stream);
47
+ }
48
+
49
+ export {
50
+ eventLogPath,
51
+ openEventSink
52
+ };
53
+ //# sourceMappingURL=chunk-G3XNWSFN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/event-sink-jsonl.ts"],"sourcesContent":["import { type WriteStream, chmodSync, createWriteStream, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { Event } from \"../core/events.js\";\nimport { sanitizeName, sessionsDir } from \"../memory/session.js\";\nimport type { EventSink } from \"../ports/event-sink.js\";\n\nexport function eventLogPath(sessionName: string): string {\n return join(sessionsDir(), `${sanitizeName(sessionName)}.events.jsonl`);\n}\n\nexport class JsonlEventSink implements EventSink {\n private buffered = 0;\n\n constructor(private readonly stream: WriteStream) {}\n\n append(ev: Event): void {\n // Skip model.delta — recoverable from model.final.text, would balloon sidecar.\n if (ev.type === \"model.delta\") return;\n this.stream.write(`${JSON.stringify(ev)}\\n`);\n this.buffered++;\n }\n\n flush(): Promise<void> {\n return new Promise((resolve) => {\n if (this.buffered === 0) return resolve();\n this.stream.uncork();\n this.buffered = 0;\n resolve();\n });\n }\n\n close(): Promise<void> {\n return new Promise((resolve) => {\n this.stream.end(() => resolve());\n });\n }\n}\n\nexport function openEventSink(path: string): JsonlEventSink {\n mkdirSync(dirname(path), { recursive: true });\n const stream = createWriteStream(path, { flags: \"a\" });\n try {\n chmodSync(path, 0o600);\n } catch {\n /* chmod no-op on Windows */\n }\n return new JsonlEventSink(stream);\n}\n"],"mappings":";;;;;;;AAAA,SAA2B,WAAW,mBAAmB,iBAAiB;AAC1E,SAAS,SAAS,YAAY;AAKvB,SAAS,aAAa,aAA6B;AACxD,SAAO,KAAK,YAAY,GAAG,GAAG,aAAa,WAAW,CAAC,eAAe;AACxE;AAEO,IAAM,iBAAN,MAA0C;AAAA,EAG/C,YAA6B,QAAqB;AAArB;AAAA,EAAsB;AAAA,EAAtB;AAAA,EAFrB,WAAW;AAAA,EAInB,OAAO,IAAiB;AAEtB,QAAI,GAAG,SAAS,cAAe;AAC/B,SAAK,OAAO,MAAM,GAAG,KAAK,UAAU,EAAE,CAAC;AAAA,CAAI;AAC3C,SAAK;AAAA,EACP;AAAA,EAEA,QAAuB;AACrB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAI,KAAK,aAAa,EAAG,QAAO,QAAQ;AACxC,WAAK,OAAO,OAAO;AACnB,WAAK,WAAW;AAChB,cAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEA,QAAuB;AACrB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,OAAO,IAAI,MAAM,QAAQ,CAAC;AAAA,IACjC,CAAC;AAAA,EACH;AACF;AAEO,SAAS,cAAc,MAA8B;AAC1D,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,SAAS,kBAAkB,MAAM,EAAE,OAAO,IAAI,CAAC;AACrD,MAAI;AACF,cAAU,MAAM,GAAK;AAAA,EACvB,QAAQ;AAAA,EAER;AACA,SAAO,IAAI,eAAe,MAAM;AAClC;","names":[]}