luckerr 0.41.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 (156) hide show
  1. package/README.md +267 -0
  2. package/README.zh-CN.md +237 -0
  3. package/dashboard/app.css +3022 -0
  4. package/dashboard/dist/app.js +30137 -0
  5. package/dashboard/dist/app.js.map +1 -0
  6. package/dashboard/dist/vendor-hljs.css +10 -0
  7. package/dashboard/dist/vendor-uplot.css +1 -0
  8. package/dashboard/index.html +19 -0
  9. package/data/deepseek-tokenizer.json.gz +0 -0
  10. package/dist/cli/acp-EOOAI4F5.js +712 -0
  11. package/dist/cli/acp-EOOAI4F5.js.map +1 -0
  12. package/dist/cli/chat-7J6GJXL2.js +51 -0
  13. package/dist/cli/chat-7J6GJXL2.js.map +1 -0
  14. package/dist/cli/chunk-2425HK6U.js +54 -0
  15. package/dist/cli/chunk-2425HK6U.js.map +1 -0
  16. package/dist/cli/chunk-25T6CVUP.js +172 -0
  17. package/dist/cli/chunk-25T6CVUP.js.map +1 -0
  18. package/dist/cli/chunk-2UQP6H6T.js +31 -0
  19. package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
  20. package/dist/cli/chunk-56OAJILV.js +47 -0
  21. package/dist/cli/chunk-56OAJILV.js.map +1 -0
  22. package/dist/cli/chunk-5FTI4KXH.js +150 -0
  23. package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
  24. package/dist/cli/chunk-5TWQD73O.js +2846 -0
  25. package/dist/cli/chunk-5TWQD73O.js.map +1 -0
  26. package/dist/cli/chunk-653BOCMK.js +40 -0
  27. package/dist/cli/chunk-653BOCMK.js.map +1 -0
  28. package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
  29. package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
  30. package/dist/cli/chunk-6DRKA2IL.js +341 -0
  31. package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
  32. package/dist/cli/chunk-6LV63NJV.js +634 -0
  33. package/dist/cli/chunk-6LV63NJV.js.map +1 -0
  34. package/dist/cli/chunk-74EX7SUH.js +25293 -0
  35. package/dist/cli/chunk-74EX7SUH.js.map +1 -0
  36. package/dist/cli/chunk-74U5RKTX.js +60611 -0
  37. package/dist/cli/chunk-74U5RKTX.js.map +1 -0
  38. package/dist/cli/chunk-ANJSUESV.js +143 -0
  39. package/dist/cli/chunk-ANJSUESV.js.map +1 -0
  40. package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
  41. package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
  42. package/dist/cli/chunk-DDIH3ZAA.js +400 -0
  43. package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
  44. package/dist/cli/chunk-ELN3Z3B2.js +621 -0
  45. package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
  46. package/dist/cli/chunk-F6BSQJGV.js +200 -0
  47. package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
  48. package/dist/cli/chunk-FET2UAG5.js +246 -0
  49. package/dist/cli/chunk-FET2UAG5.js.map +1 -0
  50. package/dist/cli/chunk-FFJ342IJ.js +190 -0
  51. package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
  52. package/dist/cli/chunk-GB3247B6.js +130 -0
  53. package/dist/cli/chunk-GB3247B6.js.map +1 -0
  54. package/dist/cli/chunk-HC2J4U3G.js +373 -0
  55. package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
  56. package/dist/cli/chunk-HRUZAIHQ.js +42 -0
  57. package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
  58. package/dist/cli/chunk-J3ZJFUDL.js +308 -0
  59. package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
  60. package/dist/cli/chunk-J5XJHLWM.js +55 -0
  61. package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
  62. package/dist/cli/chunk-JFGLMRZ6.js +160 -0
  63. package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
  64. package/dist/cli/chunk-JMBMLOBP.js +26 -0
  65. package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
  66. package/dist/cli/chunk-JMWHXZEL.js +551 -0
  67. package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
  68. package/dist/cli/chunk-KEQGPJBO.js +209 -0
  69. package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
  70. package/dist/cli/chunk-M4K6U37F.js +232 -0
  71. package/dist/cli/chunk-M4K6U37F.js.map +1 -0
  72. package/dist/cli/chunk-MIJI2WMN.js +95 -0
  73. package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
  74. package/dist/cli/chunk-MPAO3JNR.js +128 -0
  75. package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
  76. package/dist/cli/chunk-PZOFBEDC.js +873 -0
  77. package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
  78. package/dist/cli/chunk-RAILYQLN.js +46 -0
  79. package/dist/cli/chunk-RAILYQLN.js.map +1 -0
  80. package/dist/cli/chunk-RR35VQVT.js +90 -0
  81. package/dist/cli/chunk-RR35VQVT.js.map +1 -0
  82. package/dist/cli/chunk-RRA7VPW4.js +417 -0
  83. package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
  84. package/dist/cli/chunk-RU36QVN3.js +452 -0
  85. package/dist/cli/chunk-RU36QVN3.js.map +1 -0
  86. package/dist/cli/chunk-RUBIINXR.js +1819 -0
  87. package/dist/cli/chunk-RUBIINXR.js.map +1 -0
  88. package/dist/cli/chunk-S4XVGLRW.js +499 -0
  89. package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
  90. package/dist/cli/chunk-TUK7OWJA.js +51 -0
  91. package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
  92. package/dist/cli/chunk-VALDDV76.js +580 -0
  93. package/dist/cli/chunk-VALDDV76.js.map +1 -0
  94. package/dist/cli/chunk-WQOGPYGN.js +11390 -0
  95. package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
  96. package/dist/cli/chunk-WREKDFXT.js +34320 -0
  97. package/dist/cli/chunk-WREKDFXT.js.map +1 -0
  98. package/dist/cli/chunk-Y7XQU2EL.js +270 -0
  99. package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
  100. package/dist/cli/chunk-YBVCZJU4.js +54 -0
  101. package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
  102. package/dist/cli/chunk-YLIHDXUQ.js +749 -0
  103. package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
  104. package/dist/cli/chunk-YV5XXFD7.js +767 -0
  105. package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
  106. package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
  107. package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
  108. package/dist/cli/code-CRKVCMFZ.js +155 -0
  109. package/dist/cli/code-CRKVCMFZ.js.map +1 -0
  110. package/dist/cli/commands-QLMD3T7B.js +356 -0
  111. package/dist/cli/commands-QLMD3T7B.js.map +1 -0
  112. package/dist/cli/commit-53PP32NC.js +293 -0
  113. package/dist/cli/commit-53PP32NC.js.map +1 -0
  114. package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
  115. package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
  116. package/dist/cli/devtools-YECO25QO.js +3719 -0
  117. package/dist/cli/devtools-YECO25QO.js.map +1 -0
  118. package/dist/cli/diff-LYNRCJZE.js +166 -0
  119. package/dist/cli/diff-LYNRCJZE.js.map +1 -0
  120. package/dist/cli/doctor-5IBP4R5J.js +28 -0
  121. package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
  122. package/dist/cli/events-QN6KLN2V.js +340 -0
  123. package/dist/cli/events-QN6KLN2V.js.map +1 -0
  124. package/dist/cli/index.js +3500 -0
  125. package/dist/cli/index.js.map +1 -0
  126. package/dist/cli/mcp-FGKEH7RG.js +277 -0
  127. package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
  128. package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
  129. package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
  130. package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
  131. package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
  132. package/dist/cli/package.json +3 -0
  133. package/dist/cli/prompt-I775PNKT.js +16 -0
  134. package/dist/cli/prompt-I775PNKT.js.map +1 -0
  135. package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
  136. package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
  137. package/dist/cli/replay-RDXLUAOE.js +292 -0
  138. package/dist/cli/replay-RDXLUAOE.js.map +1 -0
  139. package/dist/cli/run-RCAC2RYW.js +223 -0
  140. package/dist/cli/run-RCAC2RYW.js.map +1 -0
  141. package/dist/cli/server-FFU6TLYJ.js +3658 -0
  142. package/dist/cli/server-FFU6TLYJ.js.map +1 -0
  143. package/dist/cli/sessions-QT26MQAE.js +107 -0
  144. package/dist/cli/sessions-QT26MQAE.js.map +1 -0
  145. package/dist/cli/setup-VV4WKXHV.js +767 -0
  146. package/dist/cli/setup-VV4WKXHV.js.map +1 -0
  147. package/dist/cli/stats-JVZPQWAN.js +15 -0
  148. package/dist/cli/stats-JVZPQWAN.js.map +1 -0
  149. package/dist/cli/update-KYI3OVJP.js +15 -0
  150. package/dist/cli/update-KYI3OVJP.js.map +1 -0
  151. package/dist/cli/version-ANYORXTI.js +34 -0
  152. package/dist/cli/version-ANYORXTI.js.map +1 -0
  153. package/dist/index.d.ts +2557 -0
  154. package/dist/index.js +15000 -0
  155. package/dist/index.js.map +1 -0
  156. package/package.json +106 -0
@@ -0,0 +1,749 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ sanitizeName,
5
+ sessionsDir
6
+ } from "./chunk-Y7XQU2EL.js";
7
+
8
+ // src/code/checkpoints.ts
9
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
10
+ import { homedir } from "os";
11
+ import { dirname, join, relative, resolve, sep } from "path";
12
+ function sanitizeRoot(rootDir) {
13
+ return resolve(rootDir).replace(/[\\/:]+/g, "_").replace(/^_+/, "");
14
+ }
15
+ function storeRoot(rootDir) {
16
+ return join(homedir(), ".luckerr", "sessions", sanitizeRoot(rootDir), "checkpoints");
17
+ }
18
+ function indexPath(rootDir) {
19
+ return join(storeRoot(rootDir), "index.json");
20
+ }
21
+ function snapshotPath(rootDir, id) {
22
+ return join(storeRoot(rootDir), `${id}.json`);
23
+ }
24
+ function listCheckpoints(rootDir) {
25
+ const path = indexPath(rootDir);
26
+ if (!existsSync(path)) return [];
27
+ try {
28
+ const raw = readFileSync(path, "utf8");
29
+ const parsed = JSON.parse(raw);
30
+ if (!Array.isArray(parsed)) return [];
31
+ return parsed.filter(
32
+ (m) => typeof m === "object" && m !== null && typeof m.id === "string" && typeof m.name === "string" && typeof m.createdAt === "number" && typeof m.source === "string" && typeof m.fileCount === "number" && typeof m.bytes === "number"
33
+ );
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+ function writeIndex(rootDir, items) {
39
+ const path = indexPath(rootDir);
40
+ mkdirSync(dirname(path), { recursive: true });
41
+ writeFileSync(path, JSON.stringify(items, null, 2), "utf8");
42
+ }
43
+ function loadCheckpoint(rootDir, id) {
44
+ const path = snapshotPath(rootDir, id);
45
+ if (!existsSync(path)) return null;
46
+ try {
47
+ const raw = readFileSync(path, "utf8");
48
+ const parsed = JSON.parse(raw);
49
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
50
+ return parsed;
51
+ }
52
+ return null;
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ function createCheckpoint(opts) {
58
+ const absRoot = resolve(opts.rootDir);
59
+ const id = `cp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
60
+ const files = [];
61
+ let bytes = 0;
62
+ const seen = /* @__PURE__ */ new Set();
63
+ for (const p of opts.paths) {
64
+ if (seen.has(p)) continue;
65
+ seen.add(p);
66
+ const abs = resolve(absRoot, p);
67
+ if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep}`)) continue;
68
+ const rel = relative(absRoot, abs).split(sep).join("/");
69
+ if (existsSync(abs)) {
70
+ try {
71
+ const content = readFileSync(abs, "utf8");
72
+ files.push({ path: rel, content });
73
+ bytes += content.length;
74
+ } catch {
75
+ files.push({ path: rel, content: null });
76
+ }
77
+ } else {
78
+ files.push({ path: rel, content: null });
79
+ }
80
+ }
81
+ const checkpoint = {
82
+ id,
83
+ name: opts.name,
84
+ rootDir: absRoot,
85
+ createdAt: Date.now(),
86
+ source: opts.source ?? "manual",
87
+ files,
88
+ bytes
89
+ };
90
+ const cpPath = snapshotPath(absRoot, id);
91
+ mkdirSync(dirname(cpPath), { recursive: true });
92
+ writeFileSync(cpPath, JSON.stringify(checkpoint), "utf8");
93
+ const meta = {
94
+ id,
95
+ name: opts.name,
96
+ createdAt: checkpoint.createdAt,
97
+ source: checkpoint.source,
98
+ fileCount: files.length,
99
+ bytes
100
+ };
101
+ const items = listCheckpoints(absRoot);
102
+ items.push(meta);
103
+ writeIndex(absRoot, items);
104
+ return meta;
105
+ }
106
+ function findCheckpoint(rootDir, idOrName) {
107
+ const items = listCheckpoints(rootDir);
108
+ const byId = items.find((m) => m.id === idOrName);
109
+ if (byId) return byId;
110
+ const byName = [...items].reverse().find((m) => m.name === idOrName);
111
+ return byName ?? null;
112
+ }
113
+ function restoreCheckpoint(rootDir, id) {
114
+ const cp = loadCheckpoint(rootDir, id);
115
+ const absRoot = resolve(rootDir);
116
+ const result = { restored: [], removed: [], skipped: [] };
117
+ if (!cp) {
118
+ result.skipped.push({ path: "(checkpoint)", reason: `not found: ${id}` });
119
+ return result;
120
+ }
121
+ for (const f of cp.files) {
122
+ const abs = resolve(absRoot, f.path);
123
+ if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep}`)) {
124
+ result.skipped.push({ path: f.path, reason: "path escapes rootDir" });
125
+ continue;
126
+ }
127
+ try {
128
+ if (f.content === null) {
129
+ if (existsSync(abs)) {
130
+ rmSync(abs);
131
+ result.removed.push(f.path);
132
+ }
133
+ } else {
134
+ mkdirSync(dirname(abs), { recursive: true });
135
+ writeFileSync(abs, f.content, "utf8");
136
+ result.restored.push(f.path);
137
+ }
138
+ } catch (err) {
139
+ result.skipped.push({ path: f.path, reason: err.message });
140
+ }
141
+ }
142
+ return result;
143
+ }
144
+ function deleteCheckpoint(rootDir, id) {
145
+ const cpPath = snapshotPath(rootDir, id);
146
+ let removed = false;
147
+ if (existsSync(cpPath)) {
148
+ try {
149
+ rmSync(cpPath);
150
+ removed = true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+ const items = listCheckpoints(rootDir);
156
+ const next = items.filter((m) => m.id !== id);
157
+ if (next.length !== items.length) {
158
+ writeIndex(rootDir, next);
159
+ removed = true;
160
+ }
161
+ return removed;
162
+ }
163
+ function fmtAgo(ms) {
164
+ const now = Date.now();
165
+ const diff = Math.max(0, now - ms);
166
+ const s = Math.floor(diff / 1e3);
167
+ if (s < 60) return `${s}s ago`;
168
+ const m = Math.floor(s / 60);
169
+ if (m < 60) return `${m}m ago`;
170
+ const h = Math.floor(m / 60);
171
+ if (h < 24) return `${h}h ago`;
172
+ const d = Math.floor(h / 24);
173
+ return `${d}d ago`;
174
+ }
175
+
176
+ // src/code/plan-store.ts
177
+ import {
178
+ existsSync as existsSync2,
179
+ mkdirSync as mkdirSync2,
180
+ readFileSync as readFileSync2,
181
+ readdirSync as readdirSync2,
182
+ renameSync,
183
+ statSync,
184
+ unlinkSync,
185
+ writeFileSync as writeFileSync2
186
+ } from "fs";
187
+ import { dirname as dirname2, join as join2 } from "path";
188
+ function planStatePath(sessionName) {
189
+ return join2(sessionsDir(), `${sanitizeName(sessionName)}.plan.json`);
190
+ }
191
+ function loadPlanState(sessionName) {
192
+ const path = planStatePath(sessionName);
193
+ if (!existsSync2(path)) return null;
194
+ try {
195
+ const raw = readFileSync2(path, "utf8");
196
+ const parsed = JSON.parse(raw);
197
+ if (!parsed || typeof parsed !== "object") return null;
198
+ if (parsed.version !== 1) return null;
199
+ if (!Array.isArray(parsed.steps)) return null;
200
+ if (!Array.isArray(parsed.completedStepIds)) return null;
201
+ if (typeof parsed.updatedAt !== "string") return null;
202
+ const steps = [];
203
+ for (const s of parsed.steps) {
204
+ if (!s || typeof s !== "object") continue;
205
+ const e = s;
206
+ if (typeof e.id !== "string" || !e.id) continue;
207
+ if (typeof e.title !== "string" || !e.title) continue;
208
+ if (typeof e.action !== "string" || !e.action) continue;
209
+ const step = { id: e.id, title: e.title, action: e.action };
210
+ if (e.risk === "low" || e.risk === "med" || e.risk === "high") step.risk = e.risk;
211
+ steps.push(step);
212
+ }
213
+ if (steps.length === 0) return null;
214
+ const completedStepIds = parsed.completedStepIds.filter(
215
+ (id) => typeof id === "string" && id.length > 0
216
+ );
217
+ const out = {
218
+ version: 1,
219
+ steps,
220
+ completedStepIds,
221
+ updatedAt: parsed.updatedAt
222
+ };
223
+ if (typeof parsed.body === "string" && parsed.body) out.body = parsed.body;
224
+ if (typeof parsed.summary === "string" && parsed.summary) out.summary = parsed.summary;
225
+ return out;
226
+ } catch {
227
+ return null;
228
+ }
229
+ }
230
+ function savePlanState(sessionName, steps, completedStepIds, extras) {
231
+ const path = planStatePath(sessionName);
232
+ try {
233
+ mkdirSync2(dirname2(path), { recursive: true });
234
+ const state = {
235
+ version: 1,
236
+ steps,
237
+ completedStepIds: [...completedStepIds],
238
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
239
+ };
240
+ if (extras?.body) state.body = extras.body;
241
+ if (extras?.summary) state.summary = extras.summary;
242
+ writeFileSync2(path, `${JSON.stringify(state, null, 2)}
243
+ `, "utf8");
244
+ } catch (err) {
245
+ process.stderr.write(
246
+ `\u25B8 plan-store: failed to save plan for "${sessionName}": ${err.message}
247
+ `
248
+ );
249
+ }
250
+ }
251
+ function clearPlanState(sessionName) {
252
+ const path = planStatePath(sessionName);
253
+ try {
254
+ if (existsSync2(path)) unlinkSync(path);
255
+ } catch {
256
+ }
257
+ }
258
+ function archivePlanState(sessionName) {
259
+ const active = planStatePath(sessionName);
260
+ if (!existsSync2(active)) return null;
261
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
262
+ const suffix = Math.random().toString(36).slice(2, 6);
263
+ const archive = join2(
264
+ sessionsDir(),
265
+ `${sanitizeName(sessionName)}.plan.${stamp}-${suffix}.done.json`
266
+ );
267
+ try {
268
+ renameSync(active, archive);
269
+ return archive;
270
+ } catch (err) {
271
+ process.stderr.write(
272
+ `\u25B8 plan-store: failed to archive plan for "${sessionName}": ${err.message}
273
+ `
274
+ );
275
+ return null;
276
+ }
277
+ }
278
+ function listPlanArchives(sessionName) {
279
+ const dir = sessionsDir();
280
+ if (!existsSync2(dir)) return [];
281
+ const prefix = `${sanitizeName(sessionName)}.plan.`;
282
+ const suffix = ".done.json";
283
+ let entries;
284
+ try {
285
+ entries = readdirSync2(dir);
286
+ } catch {
287
+ return [];
288
+ }
289
+ const summaries = [];
290
+ for (const name of entries) {
291
+ if (!name.startsWith(prefix) || !name.endsWith(suffix)) continue;
292
+ const full = join2(dir, name);
293
+ try {
294
+ const raw = readFileSync2(full, "utf8");
295
+ const parsed = JSON.parse(raw);
296
+ if (parsed.version !== 1) continue;
297
+ if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;
298
+ const steps = parsed.steps.filter(
299
+ (s) => !!s && typeof s === "object" && typeof s.id === "string" && typeof s.title === "string" && typeof s.action === "string"
300
+ );
301
+ if (steps.length === 0) continue;
302
+ const completedStepIds = Array.isArray(parsed.completedStepIds) ? parsed.completedStepIds.filter((id) => typeof id === "string" && !!id) : [];
303
+ let completedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : "";
304
+ if (!completedAt || Number.isNaN(Date.parse(completedAt))) {
305
+ try {
306
+ completedAt = statSync(full).mtime.toISOString();
307
+ } catch {
308
+ completedAt = (/* @__PURE__ */ new Date(0)).toISOString();
309
+ }
310
+ }
311
+ const entry = { path: full, completedAt, steps, completedStepIds };
312
+ if (typeof parsed.body === "string" && parsed.body) entry.body = parsed.body;
313
+ if (typeof parsed.summary === "string" && parsed.summary) entry.summary = parsed.summary;
314
+ summaries.push(entry);
315
+ } catch {
316
+ }
317
+ }
318
+ summaries.sort((a, b) => b.completedAt.localeCompare(a.completedAt));
319
+ return summaries;
320
+ }
321
+ function relativeTime(updatedAt, now = Date.now()) {
322
+ const t = Date.parse(updatedAt);
323
+ if (Number.isNaN(t)) return updatedAt;
324
+ const diffMs = Math.max(0, now - t);
325
+ const sec = Math.floor(diffMs / 1e3);
326
+ if (sec < 60) return `${sec}s ago`;
327
+ const min = Math.floor(sec / 60);
328
+ if (min < 60) return `${min}m ago`;
329
+ const hr = Math.floor(min / 60);
330
+ if (hr < 24) return `${hr}h ago`;
331
+ const day = Math.floor(hr / 24);
332
+ if (day < 7) return `${day}d ago`;
333
+ return updatedAt.slice(0, 10);
334
+ }
335
+
336
+ // src/cli/ui/slash/commands.ts
337
+ var SLASH_GROUP_ORDER = [
338
+ "setup",
339
+ "info",
340
+ "chat",
341
+ "extend",
342
+ "session",
343
+ "code",
344
+ "jobs",
345
+ "advanced"
346
+ ];
347
+ var SLASH_GROUP_RANK = new Map(
348
+ SLASH_GROUP_ORDER.map((group, index) => [group, index])
349
+ );
350
+ function orderSlashCommandsByGroup(commands) {
351
+ return commands.map((command, index) => ({ command, index })).sort((a, b) => {
352
+ const groupDiff = SLASH_GROUP_RANK.get(a.command.group) - SLASH_GROUP_RANK.get(b.command.group);
353
+ if (groupDiff !== 0) return groupDiff;
354
+ return a.index - b.index;
355
+ }).map((entry) => entry.command);
356
+ }
357
+ var SLASH_COMMANDS = [
358
+ { cmd: "help", group: "chat", summary: "show the full command reference", aliases: ["?"] },
359
+ {
360
+ cmd: "new",
361
+ group: "chat",
362
+ summary: "start a fresh conversation (clear context + scrollback)",
363
+ aliases: ["reset", "clear"]
364
+ },
365
+ { cmd: "retry", group: "chat", summary: "truncate & resend your last message (fresh sample)" },
366
+ {
367
+ cmd: "compact",
368
+ group: "chat",
369
+ summary: "fold older turns into a summary message (cache-safe). Auto-fires at 50% ctx; this is the manual trigger."
370
+ },
371
+ {
372
+ cmd: "stop",
373
+ group: "chat",
374
+ summary: "abort the current model turn (typed alternative to Esc)"
375
+ },
376
+ {
377
+ cmd: "btw",
378
+ group: "chat",
379
+ argsHint: "<question>",
380
+ summary: "ask a quick side question \u2014 answered from a blank slate, never added to the conversation context"
381
+ },
382
+ {
383
+ cmd: "preset",
384
+ group: "setup",
385
+ argsHint: "<auto|flash|pro>",
386
+ summary: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock. Bare opens picker.",
387
+ argCompleter: ["auto", "flash", "pro"]
388
+ },
389
+ {
390
+ cmd: "model",
391
+ group: "setup",
392
+ argsHint: "<id>",
393
+ summary: "switch DeepSeek model id. Bare opens picker.",
394
+ argCompleter: "models"
395
+ },
396
+ {
397
+ cmd: "language",
398
+ group: "setup",
399
+ argsHint: "<EN|zh-CN>",
400
+ summary: "switch the runtime language",
401
+ argCompleter: ["EN", "zh-CN"],
402
+ aliases: ["lang"]
403
+ },
404
+ {
405
+ cmd: "theme",
406
+ group: "setup",
407
+ argsHint: "[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]",
408
+ summary: "show or persist the terminal theme preference. Bare opens picker.",
409
+ argCompleter: [
410
+ "auto",
411
+ "default",
412
+ "dark",
413
+ "light",
414
+ "tokyo-night",
415
+ "github-dark",
416
+ "github-light",
417
+ "high-contrast"
418
+ ]
419
+ },
420
+ { cmd: "status", group: "info", summary: "current model, flags, context, session" },
421
+ {
422
+ cmd: "cost",
423
+ group: "info",
424
+ argsHint: "[text]",
425
+ summary: "bare \u2192 last turn's spend (Usage card); with text \u2192 estimate cost of sending it next (worst-case + likely-cache)"
426
+ },
427
+ {
428
+ cmd: "context",
429
+ group: "info",
430
+ summary: "show context-window breakdown (system / tools / log / input)"
431
+ },
432
+ {
433
+ cmd: "stats",
434
+ group: "info",
435
+ summary: "cross-session cost dashboard (today / week / month / all-time \xB7 cache hit \xB7 vs Claude)"
436
+ },
437
+ {
438
+ cmd: "doctor",
439
+ group: "info",
440
+ summary: "health check (api / config / api-reach / index / hooks / project)"
441
+ },
442
+ {
443
+ cmd: "keys",
444
+ group: "info",
445
+ summary: "keyboard + mouse + copy/paste reference"
446
+ },
447
+ {
448
+ cmd: "copy",
449
+ group: "chat",
450
+ summary: "vim/tmux-style copy mode \u2014 j/k navigate, v select, y yank to clipboard"
451
+ },
452
+ {
453
+ cmd: "feedback",
454
+ group: "info",
455
+ summary: "open a GitHub issue with diagnostic info copied to clipboard"
456
+ },
457
+ { cmd: "sessions", group: "session", summary: "list saved sessions (current marked with \u25B8)" },
458
+ { cmd: "mcp", group: "extend", summary: "list MCP servers + tools attached to this session" },
459
+ {
460
+ cmd: "resource",
461
+ group: "extend",
462
+ argsHint: "[uri]",
463
+ summary: "browse + read MCP resources (no arg \u2192 list URIs; <uri> \u2192 fetch contents)",
464
+ argCompleter: "mcp-resources"
465
+ },
466
+ {
467
+ cmd: "prompt",
468
+ group: "extend",
469
+ argsHint: "[name]",
470
+ summary: "browse + fetch MCP prompts (no arg \u2192 list names; <name> \u2192 render prompt)",
471
+ argCompleter: "mcp-prompts"
472
+ },
473
+ {
474
+ cmd: "memory",
475
+ group: "extend",
476
+ argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]",
477
+ summary: "show / manage pinned memory (LUCKERR.md + ~/.luckerr/memory)"
478
+ },
479
+ {
480
+ cmd: "skill",
481
+ group: "extend",
482
+ argsHint: "[list|show <name>|new <name>|<name> [args]]",
483
+ summary: "list / run / scaffold user skills (<project>/.luckerr/skills + ~/.luckerr/skills)"
484
+ },
485
+ {
486
+ cmd: "init",
487
+ group: "code",
488
+ argsHint: "[force]",
489
+ summary: "scan the project and synthesize a baseline LUCKERR.md (model writes; review with /apply). `force` overwrites an existing file.",
490
+ contextual: "code",
491
+ argCompleter: ["force"]
492
+ },
493
+ {
494
+ cmd: "apply",
495
+ group: "code",
496
+ argsHint: "[N|N,M|N-M]",
497
+ summary: "commit pending edit blocks to disk (no arg \u2192 all; `1`, `1,3`, or `1-4` \u2192 that subset, rest stay pending)",
498
+ contextual: "code"
499
+ },
500
+ {
501
+ cmd: "discard",
502
+ group: "code",
503
+ argsHint: "[N|N,M|N-M]",
504
+ summary: "drop pending edit blocks without writing (no arg \u2192 all; indices \u2192 that subset)",
505
+ contextual: "code"
506
+ },
507
+ {
508
+ cmd: "walk",
509
+ group: "code",
510
+ summary: "step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)",
511
+ contextual: "code"
512
+ },
513
+ {
514
+ cmd: "undo",
515
+ group: "code",
516
+ summary: "roll back the last applied edit batch",
517
+ contextual: "code"
518
+ },
519
+ {
520
+ cmd: "history",
521
+ group: "code",
522
+ summary: "list every edit batch this session (ids for /show, undone markers)",
523
+ contextual: "code"
524
+ },
525
+ {
526
+ cmd: "show",
527
+ group: "code",
528
+ argsHint: "[id]",
529
+ summary: "dump a stored edit diff (omit id for newest non-undone)",
530
+ contextual: "code"
531
+ },
532
+ {
533
+ cmd: "commit",
534
+ group: "code",
535
+ argsHint: '"msg"',
536
+ summary: "git add -A && git commit -m ...",
537
+ contextual: "code"
538
+ },
539
+ {
540
+ cmd: "mode",
541
+ group: "code",
542
+ argsHint: "[review|auto|yolo]",
543
+ summary: "edit-gate: review (queue) \xB7 auto (apply+undo) \xB7 yolo (apply+auto-shell). Shift+Tab cycles.",
544
+ contextual: "code",
545
+ argCompleter: ["review", "auto", "yolo"]
546
+ },
547
+ {
548
+ cmd: "plan",
549
+ group: "code",
550
+ argsHint: "[on|off]",
551
+ summary: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
552
+ contextual: "code",
553
+ argCompleter: ["on", "off"]
554
+ },
555
+ {
556
+ cmd: "checkpoint",
557
+ group: "code",
558
+ argsHint: "[name|list|forget <id>]",
559
+ summary: "snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.",
560
+ contextual: "code",
561
+ argCompleter: ["list", "forget"]
562
+ },
563
+ {
564
+ cmd: "restore",
565
+ group: "code",
566
+ argsHint: "<name|id>",
567
+ summary: "roll back files to a named checkpoint (see /checkpoint list)",
568
+ contextual: "code"
569
+ },
570
+ {
571
+ cmd: "cwd",
572
+ group: "code",
573
+ argsHint: "<path>",
574
+ summary: "switch the workspace root mid-session \u2014 re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker",
575
+ contextual: "code",
576
+ aliases: ["sandbox"]
577
+ },
578
+ {
579
+ cmd: "jobs",
580
+ group: "jobs",
581
+ summary: "list background jobs started by run_background",
582
+ contextual: "code"
583
+ },
584
+ {
585
+ cmd: "kill",
586
+ group: "jobs",
587
+ argsHint: "<id>",
588
+ summary: "stop a background job by id (SIGTERM \u2192 SIGKILL after grace)",
589
+ contextual: "code"
590
+ },
591
+ {
592
+ cmd: "logs",
593
+ group: "jobs",
594
+ argsHint: "<id> [lines]",
595
+ summary: "tail a background job's output (default last 80 lines)",
596
+ contextual: "code"
597
+ },
598
+ {
599
+ cmd: "pro",
600
+ group: "advanced",
601
+ argsHint: "[off]",
602
+ summary: "arm v4-pro for the NEXT turn only (one-shot \xB7 auto-disarms after turn)",
603
+ argCompleter: ["off"]
604
+ },
605
+ {
606
+ cmd: "budget",
607
+ group: "advanced",
608
+ argsHint: "[usd|off]",
609
+ summary: "session USD cap \u2014 warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status",
610
+ argCompleter: ["off", "1", "5", "10", "20", "50"]
611
+ },
612
+ {
613
+ cmd: "search-engine",
614
+ group: "advanced",
615
+ argsHint: "<mojeek|searxng> [<endpoint>]",
616
+ summary: "switch web search backend \u2014 mojeek (default, no deps) or searxng (self-hosted)",
617
+ argCompleter: ["mojeek", "searxng"],
618
+ aliases: ["se"]
619
+ },
620
+ {
621
+ cmd: "hooks",
622
+ group: "advanced",
623
+ argsHint: "[reload]",
624
+ summary: "list active hooks (settings.json under .luckerr/) \xB7 reload re-reads from disk"
625
+ },
626
+ {
627
+ cmd: "permissions",
628
+ group: "advanced",
629
+ argsHint: "[list|add <prefix>|remove <prefix|N>|clear confirm]",
630
+ summary: "show / edit shell allowlist (builtin read-only \xB7 per-project: ~/.luckerr/config.json)",
631
+ argCompleter: ["list", "add", "remove", "clear"]
632
+ },
633
+ {
634
+ cmd: "dashboard",
635
+ group: "advanced",
636
+ argsHint: "[stop]",
637
+ summary: "launch the embedded web dashboard (127.0.0.1, token-gated)",
638
+ argCompleter: ["stop"]
639
+ },
640
+ {
641
+ cmd: "loop",
642
+ group: "advanced",
643
+ argsHint: "<5s..6h> <prompt> \xB7 stop \xB7 (no args = status)",
644
+ summary: "auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop"
645
+ },
646
+ {
647
+ cmd: "plans",
648
+ group: "advanced",
649
+ summary: "list this session's active + archived plans, newest first"
650
+ },
651
+ {
652
+ cmd: "replay",
653
+ group: "advanced",
654
+ summary: "load an archived plan as a read-only Time Travel snapshot (default: newest)",
655
+ argsHint: "[N]"
656
+ },
657
+ {
658
+ cmd: "update",
659
+ group: "advanced",
660
+ summary: "show current vs latest version + the shell command to upgrade"
661
+ },
662
+ { cmd: "exit", group: "advanced", summary: "quit the TUI", aliases: ["quit", "q"] }
663
+ ];
664
+ function suggestSlashCommands(prefix, codeMode = false, counts) {
665
+ const p = prefix.toLowerCase();
666
+ const matches = SLASH_COMMANDS.filter((c) => {
667
+ if (p === "") return c.group !== "advanced";
668
+ if (c.contextual === "code" && !codeMode) return false;
669
+ if (c.cmd.startsWith(p)) return true;
670
+ return c.aliases?.some((a) => a.startsWith(p)) ?? false;
671
+ });
672
+ if (p === "") return orderSlashCommandsByGroup(matches);
673
+ if (!counts) return matches;
674
+ const indexOf = new Map(matches.map((s, i) => [s.cmd, i]));
675
+ return [...matches].sort((a, b) => {
676
+ const diff = (counts[b.cmd] ?? 0) - (counts[a.cmd] ?? 0);
677
+ if (diff !== 0) return diff;
678
+ return (indexOf.get(a.cmd) ?? 0) - (indexOf.get(b.cmd) ?? 0);
679
+ });
680
+ }
681
+ function countAdvancedCommands(codeMode) {
682
+ return SLASH_COMMANDS.filter(
683
+ (c) => c.group === "advanced" && (c.contextual !== "code" || codeMode)
684
+ ).length;
685
+ }
686
+ var ALIAS_TO_CMD = (() => {
687
+ const m = {};
688
+ for (const spec of SLASH_COMMANDS) {
689
+ if (!spec.aliases) continue;
690
+ for (const a of spec.aliases) m[a] = spec.cmd;
691
+ }
692
+ return m;
693
+ })();
694
+ function resolveSlashAlias(name) {
695
+ return ALIAS_TO_CMD[name] ?? name;
696
+ }
697
+ function detectSlashArgContext(input, codeMode = false) {
698
+ const m = /^\/(\S+) ([\s\S]*)$/.exec(input);
699
+ if (!m) return null;
700
+ const cmdName = resolveSlashAlias(m[1].toLowerCase());
701
+ const tail = m[2] ?? "";
702
+ const spec = SLASH_COMMANDS.find(
703
+ (s) => s.cmd === cmdName && (s.contextual !== "code" || codeMode)
704
+ );
705
+ if (!spec) return null;
706
+ const hasInternalSpace = /\s/.test(tail);
707
+ const partialOffset = input.length - tail.length;
708
+ if (hasInternalSpace) {
709
+ return { spec, partial: tail, partialOffset, kind: "hint" };
710
+ }
711
+ return {
712
+ spec,
713
+ partial: tail,
714
+ partialOffset,
715
+ kind: spec.argCompleter ? "picker" : "hint"
716
+ };
717
+ }
718
+ function parseSlash(text) {
719
+ if (!text.startsWith("/")) return null;
720
+ const parts = text.slice(1).trim().split(/\s+/);
721
+ const cmd = parts[0]?.toLowerCase() ?? "";
722
+ if (!cmd) return null;
723
+ return { cmd, args: parts.slice(1) };
724
+ }
725
+
726
+ export {
727
+ listCheckpoints,
728
+ loadCheckpoint,
729
+ createCheckpoint,
730
+ findCheckpoint,
731
+ restoreCheckpoint,
732
+ deleteCheckpoint,
733
+ fmtAgo,
734
+ loadPlanState,
735
+ savePlanState,
736
+ clearPlanState,
737
+ archivePlanState,
738
+ listPlanArchives,
739
+ relativeTime,
740
+ SLASH_GROUP_ORDER,
741
+ orderSlashCommandsByGroup,
742
+ SLASH_COMMANDS,
743
+ suggestSlashCommands,
744
+ countAdvancedCommands,
745
+ resolveSlashAlias,
746
+ detectSlashArgContext,
747
+ parseSlash
748
+ };
749
+ //# sourceMappingURL=chunk-YLIHDXUQ.js.map