reasonix 0.47.0 → 0.47.1

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 (108) hide show
  1. package/dist/cli/{acp-QK3DMC53.js → acp-GEOAKSTU.js} +21 -49
  2. package/dist/cli/acp-GEOAKSTU.js.map +1 -0
  3. package/dist/cli/{chat-VV5UWY4V.js → chat-YTPATMMG.js} +23 -23
  4. package/dist/cli/{chunk-FDKOUJKZ.js → chunk-2XY77LW7.js} +7 -7
  5. package/dist/cli/{chunk-QVDWH2A2.js → chunk-4MFCAZ2W.js} +3 -3
  6. package/dist/cli/{chunk-24A7FHGJ.js → chunk-6CRPCJAU.js} +14 -1
  7. package/dist/cli/chunk-6CRPCJAU.js.map +1 -0
  8. package/dist/cli/{chunk-VKYSZKH2.js → chunk-6QC5RQLE.js} +2 -2
  9. package/dist/cli/chunk-BQ6HC66J.js +530 -0
  10. package/dist/cli/chunk-BQ6HC66J.js.map +1 -0
  11. package/dist/cli/{chunk-OJVITDGB.js → chunk-CCJAP7G3.js} +2 -2
  12. package/dist/cli/{chunk-R6GQKKBW.js → chunk-CNG32VAB.js} +2 -2
  13. package/dist/cli/{chunk-QVUFWDD2.js → chunk-DN4B5S6Y.js} +2 -2
  14. package/dist/cli/{chunk-LBLR4CUZ.js → chunk-DQ6K5ZQ7.js} +2 -2
  15. package/dist/cli/{chunk-VNQGCA3Q.js → chunk-DWPAKZTY.js} +14 -3
  16. package/dist/cli/chunk-DWPAKZTY.js.map +1 -0
  17. package/dist/cli/{chunk-BWYVFFKR.js → chunk-GH7DC2Y5.js} +2 -2
  18. package/dist/cli/{chunk-BYYVYJDX.js → chunk-HUILPCYX.js} +3 -3
  19. package/dist/cli/{chunk-ICAFSZHS.js → chunk-JBH5RM7X.js} +174 -65
  20. package/dist/cli/chunk-JBH5RM7X.js.map +1 -0
  21. package/dist/cli/{chunk-K6GUKSXH.js → chunk-KVZZ5U75.js} +2 -2
  22. package/dist/cli/{chunk-WF7TPVZM.js → chunk-KYQVQ5X4.js} +84 -9
  23. package/dist/cli/chunk-KYQVQ5X4.js.map +1 -0
  24. package/dist/cli/{chunk-KDRUEXII.js → chunk-NRQ5UP5T.js} +20 -6
  25. package/dist/cli/chunk-NRQ5UP5T.js.map +1 -0
  26. package/dist/cli/{chunk-VJMBISEI.js → chunk-QCFLPSPH.js} +2 -2
  27. package/dist/cli/{chunk-YDPLF7XR.js → chunk-T5A7EY6B.js} +2 -2
  28. package/dist/cli/{chunk-VMUUFWFF.js → chunk-TDHXB2ER.js} +2 -2
  29. package/dist/cli/{chunk-GDKB2PPK.js → chunk-TRSAHHCL.js} +107 -11
  30. package/dist/cli/chunk-TRSAHHCL.js.map +1 -0
  31. package/dist/cli/{chunk-6J6BSUCR.js → chunk-TRWHTFG7.js} +2 -2
  32. package/dist/cli/{chunk-VC2CQA5D.js → chunk-XD6P7AFH.js} +26 -29
  33. package/dist/cli/chunk-XD6P7AFH.js.map +1 -0
  34. package/dist/cli/{chunk-ICSYGIPN.js → chunk-XMHP7BEE.js} +421 -80
  35. package/dist/cli/chunk-XMHP7BEE.js.map +1 -0
  36. package/dist/cli/{chunk-COWPEX54.js → chunk-YFP3MYMY.js} +5 -5
  37. package/dist/cli/{chunk-CI2PF5QX.js → chunk-ZXSCAODE.js} +8 -8
  38. package/dist/cli/{chunk-CI2PF5QX.js.map → chunk-ZXSCAODE.js.map} +1 -1
  39. package/dist/cli/{code-C24TUAE5.js → code-Q4NRVEDG.js} +29 -27
  40. package/dist/cli/code-Q4NRVEDG.js.map +1 -0
  41. package/dist/cli/{commands-RR3GIYOK.js → commands-4CDI4GFM.js} +4 -4
  42. package/dist/cli/{commit-FSHPIINM.js → commit-GW7LDQP5.js} +3 -3
  43. package/dist/cli/{desktop-7NCHPEFB.js → desktop-EG6P5SF2.js} +80 -22
  44. package/dist/cli/desktop-EG6P5SF2.js.map +1 -0
  45. package/dist/cli/{diff-RAAHHLHV.js → diff-VI2YX4FN.js} +8 -8
  46. package/dist/cli/{doctor-PKVQIXRT.js → doctor-CQTTZP27.js} +8 -8
  47. package/dist/cli/index.js +45 -37
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/{mcp-CRJ26PP4.js → mcp-J2UCD4RZ.js} +2 -2
  50. package/dist/cli/{mcp-browse-QPAOWZOP.js → mcp-browse-GSX34JEK.js} +2 -2
  51. package/dist/cli/{mcp-inspect-CVCLABRS.js → mcp-inspect-RRFYF4ZV.js} +2 -2
  52. package/dist/cli/{prompt-SKYXERSI.js → prompt-5TQPIVHV.js} +3 -3
  53. package/dist/cli/{replay-KPDW2ZMJ.js → replay-MJCEMODU.js} +8 -8
  54. package/dist/cli/{run-WIKDIXTG.js → run-P4D5VDYE.js} +13 -13
  55. package/dist/cli/{server-P6V2G3P6.js → server-C25JNNZV.js} +11 -11
  56. package/dist/cli/{sessions-2NULRMSA.js → sessions-QIONZJQ6.js} +12 -12
  57. package/dist/cli/{setup-Y5WDBQFL.js → setup-NLQ6G5G4.js} +6 -6
  58. package/dist/cli/setup-NLQ6G5G4.js.map +1 -0
  59. package/dist/cli/{stats-T7BL2YOR.js → stats-DFZEXHP4.js} +6 -6
  60. package/dist/cli/{version-3KWDNWLN.js → version-GR3X3MPI.js} +12 -12
  61. package/dist/index.d.ts +40 -48
  62. package/dist/index.js +286 -237
  63. package/dist/index.js.map +1 -1
  64. package/package.json +3 -1
  65. package/dist/cli/acp-QK3DMC53.js.map +0 -1
  66. package/dist/cli/chunk-24A7FHGJ.js.map +0 -1
  67. package/dist/cli/chunk-GDKB2PPK.js.map +0 -1
  68. package/dist/cli/chunk-ICAFSZHS.js.map +0 -1
  69. package/dist/cli/chunk-ICSYGIPN.js.map +0 -1
  70. package/dist/cli/chunk-KDRUEXII.js.map +0 -1
  71. package/dist/cli/chunk-UDVFBEXC.js +0 -642
  72. package/dist/cli/chunk-UDVFBEXC.js.map +0 -1
  73. package/dist/cli/chunk-VC2CQA5D.js.map +0 -1
  74. package/dist/cli/chunk-VNQGCA3Q.js.map +0 -1
  75. package/dist/cli/chunk-WF7TPVZM.js.map +0 -1
  76. package/dist/cli/code-C24TUAE5.js.map +0 -1
  77. package/dist/cli/desktop-7NCHPEFB.js.map +0 -1
  78. package/dist/cli/setup-Y5WDBQFL.js.map +0 -1
  79. /package/dist/cli/{chat-VV5UWY4V.js.map → chat-YTPATMMG.js.map} +0 -0
  80. /package/dist/cli/{chunk-FDKOUJKZ.js.map → chunk-2XY77LW7.js.map} +0 -0
  81. /package/dist/cli/{chunk-QVDWH2A2.js.map → chunk-4MFCAZ2W.js.map} +0 -0
  82. /package/dist/cli/{chunk-VKYSZKH2.js.map → chunk-6QC5RQLE.js.map} +0 -0
  83. /package/dist/cli/{chunk-OJVITDGB.js.map → chunk-CCJAP7G3.js.map} +0 -0
  84. /package/dist/cli/{chunk-R6GQKKBW.js.map → chunk-CNG32VAB.js.map} +0 -0
  85. /package/dist/cli/{chunk-QVUFWDD2.js.map → chunk-DN4B5S6Y.js.map} +0 -0
  86. /package/dist/cli/{chunk-LBLR4CUZ.js.map → chunk-DQ6K5ZQ7.js.map} +0 -0
  87. /package/dist/cli/{chunk-BWYVFFKR.js.map → chunk-GH7DC2Y5.js.map} +0 -0
  88. /package/dist/cli/{chunk-BYYVYJDX.js.map → chunk-HUILPCYX.js.map} +0 -0
  89. /package/dist/cli/{chunk-K6GUKSXH.js.map → chunk-KVZZ5U75.js.map} +0 -0
  90. /package/dist/cli/{chunk-VJMBISEI.js.map → chunk-QCFLPSPH.js.map} +0 -0
  91. /package/dist/cli/{chunk-YDPLF7XR.js.map → chunk-T5A7EY6B.js.map} +0 -0
  92. /package/dist/cli/{chunk-VMUUFWFF.js.map → chunk-TDHXB2ER.js.map} +0 -0
  93. /package/dist/cli/{chunk-6J6BSUCR.js.map → chunk-TRWHTFG7.js.map} +0 -0
  94. /package/dist/cli/{chunk-COWPEX54.js.map → chunk-YFP3MYMY.js.map} +0 -0
  95. /package/dist/cli/{commands-RR3GIYOK.js.map → commands-4CDI4GFM.js.map} +0 -0
  96. /package/dist/cli/{commit-FSHPIINM.js.map → commit-GW7LDQP5.js.map} +0 -0
  97. /package/dist/cli/{diff-RAAHHLHV.js.map → diff-VI2YX4FN.js.map} +0 -0
  98. /package/dist/cli/{doctor-PKVQIXRT.js.map → doctor-CQTTZP27.js.map} +0 -0
  99. /package/dist/cli/{mcp-CRJ26PP4.js.map → mcp-J2UCD4RZ.js.map} +0 -0
  100. /package/dist/cli/{mcp-browse-QPAOWZOP.js.map → mcp-browse-GSX34JEK.js.map} +0 -0
  101. /package/dist/cli/{mcp-inspect-CVCLABRS.js.map → mcp-inspect-RRFYF4ZV.js.map} +0 -0
  102. /package/dist/cli/{prompt-SKYXERSI.js.map → prompt-5TQPIVHV.js.map} +0 -0
  103. /package/dist/cli/{replay-KPDW2ZMJ.js.map → replay-MJCEMODU.js.map} +0 -0
  104. /package/dist/cli/{run-WIKDIXTG.js.map → run-P4D5VDYE.js.map} +0 -0
  105. /package/dist/cli/{server-P6V2G3P6.js.map → server-C25JNNZV.js.map} +0 -0
  106. /package/dist/cli/{sessions-2NULRMSA.js.map → sessions-QIONZJQ6.js.map} +0 -0
  107. /package/dist/cli/{stats-T7BL2YOR.js.map → stats-DFZEXHP4.js.map} +0 -0
  108. /package/dist/cli/{version-3KWDNWLN.js.map → version-GR3X3MPI.js.map} +0 -0
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
3
3
  import {
4
4
  indexCompatible,
5
5
  querySemantic
6
- } from "./chunk-YDPLF7XR.js";
6
+ } from "./chunk-T5A7EY6B.js";
7
7
 
8
8
  // src/index/semantic/tool.ts
9
9
  async function registerSemanticSearchTool(registry, opts) {
@@ -93,4 +93,4 @@ export {
93
93
  registerSemanticSearchTool,
94
94
  bootstrapSemanticSearchInCodeMode
95
95
  };
96
- //# sourceMappingURL=chunk-K6GUKSXH.js.map
96
+ //# sourceMappingURL=chunk-KVZZ5U75.js.map
@@ -195,7 +195,7 @@ function loadPlanState(sessionName) {
195
195
  const raw = readFileSync2(path, "utf8");
196
196
  const parsed = JSON.parse(raw);
197
197
  if (!parsed || typeof parsed !== "object") return null;
198
- if (parsed.version !== 1) return null;
198
+ if (parsed.version !== 1 && parsed.version !== 2) return null;
199
199
  if (!Array.isArray(parsed.steps)) return null;
200
200
  if (!Array.isArray(parsed.completedStepIds)) return null;
201
201
  if (typeof parsed.updatedAt !== "string") return null;
@@ -208,18 +208,27 @@ function loadPlanState(sessionName) {
208
208
  if (typeof e.action !== "string" || !e.action) continue;
209
209
  const step = { id: e.id, title: e.title, action: e.action };
210
210
  if (e.risk === "low" || e.risk === "med" || e.risk === "high") step.risk = e.risk;
211
+ const targets = stringList(e.targets);
212
+ if (targets) step.targets = targets;
213
+ if (typeof e.acceptance === "string" && e.acceptance.trim()) {
214
+ step.acceptance = e.acceptance.trim();
215
+ }
216
+ const verification = stringList(e.verification);
217
+ if (verification) step.verification = verification;
211
218
  steps.push(step);
212
219
  }
213
220
  if (steps.length === 0) return null;
214
221
  const completedStepIds = parsed.completedStepIds.filter(
215
222
  (id) => typeof id === "string" && id.length > 0
216
223
  );
224
+ const stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);
217
225
  const out = {
218
- version: 1,
226
+ version: parsed.version,
219
227
  steps,
220
228
  completedStepIds,
221
229
  updatedAt: parsed.updatedAt
222
230
  };
231
+ if (stepCompletions) out.stepCompletions = stepCompletions;
223
232
  if (typeof parsed.body === "string" && parsed.body) out.body = parsed.body;
224
233
  if (typeof parsed.summary === "string" && parsed.summary) out.summary = parsed.summary;
225
234
  return out;
@@ -232,11 +241,13 @@ function savePlanState(sessionName, steps, completedStepIds, extras) {
232
241
  try {
233
242
  mkdirSync2(dirname2(path), { recursive: true });
234
243
  const state = {
235
- version: 1,
244
+ version: 2,
236
245
  steps,
237
246
  completedStepIds: [...completedStepIds],
238
247
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
239
248
  };
249
+ const stepCompletions = normalizeStepCompletionsForWrite(extras?.stepCompletions);
250
+ if (stepCompletions) state.stepCompletions = stepCompletions;
240
251
  if (extras?.body) state.body = extras.body;
241
252
  if (extras?.summary) state.summary = extras.summary;
242
253
  writeFileSync2(path, `${JSON.stringify(state, null, 2)}
@@ -293,7 +304,7 @@ function listPlanArchives(sessionName) {
293
304
  try {
294
305
  const raw = readFileSync2(full, "utf8");
295
306
  const parsed = JSON.parse(raw);
296
- if (parsed.version !== 1) continue;
307
+ if (parsed.version !== 1 && parsed.version !== 2) continue;
297
308
  if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;
298
309
  const steps = parsed.steps.filter(
299
310
  (s) => !!s && typeof s === "object" && typeof s.id === "string" && typeof s.title === "string" && typeof s.action === "string"
@@ -309,6 +320,8 @@ function listPlanArchives(sessionName) {
309
320
  }
310
321
  }
311
322
  const entry = { path: full, completedAt, steps, completedStepIds };
323
+ const stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);
324
+ if (stepCompletions) entry.stepCompletions = stepCompletions;
312
325
  if (typeof parsed.body === "string" && parsed.body) entry.body = parsed.body;
313
326
  if (typeof parsed.summary === "string" && parsed.summary) entry.summary = parsed.summary;
314
327
  summaries.push(entry);
@@ -340,7 +353,7 @@ function listAllPlanArchives() {
340
353
  try {
341
354
  const raw = readFileSync2(full, "utf8");
342
355
  const parsed = JSON.parse(raw);
343
- if (parsed.version !== 1) continue;
356
+ if (parsed.version !== 1 && parsed.version !== 2) continue;
344
357
  if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;
345
358
  const steps = parsed.steps.filter(
346
359
  (s) => !!s && typeof s === "object" && typeof s.id === "string" && typeof s.title === "string" && typeof s.action === "string"
@@ -362,6 +375,8 @@ function listAllPlanArchives() {
362
375
  steps,
363
376
  completedStepIds
364
377
  };
378
+ const stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);
379
+ if (stepCompletions) entry.stepCompletions = stepCompletions;
365
380
  if (typeof parsed.body === "string" && parsed.body) entry.body = parsed.body;
366
381
  if (typeof parsed.summary === "string" && parsed.summary) entry.summary = parsed.summary;
367
382
  out.push(entry);
@@ -371,6 +386,65 @@ function listAllPlanArchives() {
371
386
  out.sort((a, b) => b.completedAt.localeCompare(a.completedAt));
372
387
  return out;
373
388
  }
389
+ function stringList(raw) {
390
+ if (!Array.isArray(raw)) return void 0;
391
+ const out = raw.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
392
+ return out.length > 0 ? out : void 0;
393
+ }
394
+ function normalizeStepCompletionsForWrite(raw) {
395
+ if (!raw) return void 0;
396
+ const entries = raw instanceof Map ? [...raw.entries()] : Object.entries(raw);
397
+ const out = {};
398
+ for (const [key, value] of entries) {
399
+ const completion = sanitizeStepCompletion(value, key);
400
+ if (completion) out[completion.stepId] = completion;
401
+ }
402
+ return Object.keys(out).length > 0 ? out : void 0;
403
+ }
404
+ function sanitizeStepCompletions(raw) {
405
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
406
+ const out = {};
407
+ for (const [key, value] of Object.entries(raw)) {
408
+ const completion = sanitizeStepCompletion(value, key);
409
+ if (completion) out[completion.stepId] = completion;
410
+ }
411
+ return Object.keys(out).length > 0 ? out : void 0;
412
+ }
413
+ function sanitizeStepCompletion(raw, fallbackStepId) {
414
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
415
+ const entry = raw;
416
+ const stepId = typeof entry.stepId === "string" && entry.stepId.trim() ? entry.stepId.trim() : fallbackStepId?.trim();
417
+ const result = typeof entry.result === "string" ? entry.result.trim() : "";
418
+ if (!stepId || !result) return void 0;
419
+ const completion = { kind: "step_completed", stepId, result };
420
+ if (typeof entry.title === "string" && entry.title.trim()) completion.title = entry.title.trim();
421
+ if (typeof entry.notes === "string" && entry.notes.trim()) completion.notes = entry.notes.trim();
422
+ const evidence = sanitizeEvidenceList(entry.evidence);
423
+ if (evidence) completion.evidence = evidence;
424
+ return completion;
425
+ }
426
+ function sanitizeEvidenceList(raw) {
427
+ if (!Array.isArray(raw)) return void 0;
428
+ const out = [];
429
+ for (const item of raw) {
430
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
431
+ const entry = item;
432
+ const kind = entry.kind;
433
+ if (kind !== "verification" && kind !== "diff" && kind !== "checkpoint" && kind !== "manual") {
434
+ continue;
435
+ }
436
+ const summary = typeof entry.summary === "string" ? entry.summary.trim() : "";
437
+ if (!summary) continue;
438
+ const evidence = { kind, summary };
439
+ if (typeof entry.command === "string" && entry.command.trim()) {
440
+ evidence.command = entry.command.trim();
441
+ }
442
+ const paths = stringList(entry.paths);
443
+ if (paths) evidence.paths = paths;
444
+ out.push(evidence);
445
+ }
446
+ return out.length > 0 ? out : void 0;
447
+ }
374
448
  function relativeTime(updatedAt, now = Date.now()) {
375
449
  const t = Date.parse(updatedAt);
376
450
  if (Number.isNaN(t)) return updatedAt;
@@ -614,10 +688,10 @@ var SLASH_COMMANDS = [
614
688
  {
615
689
  cmd: "plan",
616
690
  group: "code",
617
- argsHint: "[on|off]",
618
- summary: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
691
+ argsHint: "[on|off|strict]",
692
+ summary: "toggle read-only plan mode / strict lifecycle rails",
619
693
  contextual: "code",
620
- argCompleter: ["on", "off"]
694
+ argCompleter: ["on", "off", "strict"]
621
695
  },
622
696
  {
623
697
  cmd: "checkpoint",
@@ -785,6 +859,7 @@ function detectSlashArgContext(input, codeMode = false) {
785
859
  }
786
860
  function parseSlash(text) {
787
861
  if (!text.startsWith("/")) return null;
862
+ if (text.startsWith("//")) return null;
788
863
  const parts = text.slice(1).trim().split(/\s+/);
789
864
  const cmd = parts[0]?.toLowerCase() ?? "";
790
865
  if (!cmd) return null;
@@ -815,4 +890,4 @@ export {
815
890
  detectSlashArgContext,
816
891
  parseSlash
817
892
  };
818
- //# sourceMappingURL=chunk-WF7TPVZM.js.map
893
+ //# sourceMappingURL=chunk-KYQVQ5X4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/code/checkpoints.ts","../../src/code/plan-store.ts","../../src/cli/ui/slash/commands.ts"],"sourcesContent":["/** One file per checkpoint (not jsonl) so delete/restore is cheap and a corrupt snapshot only loses itself. */\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\n\n/** One file's state at the time of snapshot. `content === null` → didn't exist. */\nexport interface CheckpointFile {\n path: string;\n content: string | null;\n}\n\nexport interface Checkpoint {\n id: string;\n /** User-given name, or `auto-<reason>` for system-created snapshots. */\n name: string;\n /** Absolute workspace root the snapshot belongs to. */\n rootDir: string;\n createdAt: number;\n source: \"manual\" | \"auto-session-start\" | \"auto-pre-restore\";\n files: CheckpointFile[];\n /** Total bytes of file content captured (sum of `content?.length`). */\n bytes: number;\n}\n\nexport interface CheckpointMeta {\n id: string;\n name: string;\n createdAt: number;\n source: Checkpoint[\"source\"];\n fileCount: number;\n bytes: number;\n}\n\n/** Sanitize a directory path into a safe filesystem name for the store. */\nfunction sanitizeRoot(rootDir: string): string {\n return resolve(rootDir)\n .replace(/[\\\\/:]+/g, \"_\")\n .replace(/^_+/, \"\");\n}\n\nfunction storeRoot(rootDir: string): string {\n return join(homedir(), \".reasonix\", \"sessions\", sanitizeRoot(rootDir), \"checkpoints\");\n}\n\nfunction indexPath(rootDir: string): string {\n return join(storeRoot(rootDir), \"index.json\");\n}\n\nfunction snapshotPath(rootDir: string, id: string): string {\n return join(storeRoot(rootDir), `${id}.json`);\n}\n\n/** Load the index of checkpoint metadata for a workspace. Empty when missing. */\nexport function listCheckpoints(rootDir: string): CheckpointMeta[] {\n const path = indexPath(rootDir);\n if (!existsSync(path)) return [];\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n // Defensive: filter out malformed entries rather than throwing on\n // a single bad row. A stale entry is annoying; a thrown listCheckpoints\n // would break /checkpoint list entirely.\n return parsed.filter(\n (m): m is CheckpointMeta =>\n typeof m === \"object\" &&\n m !== null &&\n typeof m.id === \"string\" &&\n typeof m.name === \"string\" &&\n typeof m.createdAt === \"number\" &&\n typeof m.source === \"string\" &&\n typeof m.fileCount === \"number\" &&\n typeof m.bytes === \"number\",\n );\n } catch {\n return [];\n }\n}\n\nfunction writeIndex(rootDir: string, items: CheckpointMeta[]): void {\n const path = indexPath(rootDir);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(items, null, 2), \"utf8\");\n}\n\n/** Read a single checkpoint by id. Returns null when missing or corrupt. */\nexport function loadCheckpoint(rootDir: string, id: string): Checkpoint | null {\n const path = snapshotPath(rootDir, id);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && Array.isArray(parsed.files)) {\n return parsed as Checkpoint;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CreateCheckpointOptions {\n rootDir: string;\n name: string;\n source?: Checkpoint[\"source\"];\n paths: readonly string[];\n}\n\n/** Missing files recorded as `content: null` so restore knows to delete; ID has random suffix to avoid same-ms collision. */\nexport function createCheckpoint(opts: CreateCheckpointOptions): CheckpointMeta {\n const absRoot = resolve(opts.rootDir);\n const id = `cp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n const files: CheckpointFile[] = [];\n let bytes = 0;\n const seen = new Set<string>();\n for (const p of opts.paths) {\n if (seen.has(p)) continue;\n seen.add(p);\n const abs = resolve(absRoot, p);\n // Path-escape guard. A snapshot of `../../../etc/passwd` is not\n // something we want — refuse silently rather than abort the whole\n // checkpoint.\n if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep}`)) continue;\n const rel = relative(absRoot, abs).split(sep).join(\"/\");\n if (existsSync(abs)) {\n try {\n const content = readFileSync(abs, \"utf8\");\n files.push({ path: rel, content });\n bytes += content.length;\n } catch {\n // Unreadable (binary, perms) — record as null so restore knows\n // to delete on revert. Wrong for binary files but consistent.\n files.push({ path: rel, content: null });\n }\n } else {\n files.push({ path: rel, content: null });\n }\n }\n\n const checkpoint: Checkpoint = {\n id,\n name: opts.name,\n rootDir: absRoot,\n createdAt: Date.now(),\n source: opts.source ?? \"manual\",\n files,\n bytes,\n };\n const cpPath = snapshotPath(absRoot, id);\n mkdirSync(dirname(cpPath), { recursive: true });\n writeFileSync(cpPath, JSON.stringify(checkpoint), \"utf8\");\n\n const meta: CheckpointMeta = {\n id,\n name: opts.name,\n createdAt: checkpoint.createdAt,\n source: checkpoint.source,\n fileCount: files.length,\n bytes,\n };\n const items = listCheckpoints(absRoot);\n items.push(meta);\n writeIndex(absRoot, items);\n return meta;\n}\n\n/** Most-recent name wins on collision. */\nexport function findCheckpoint(rootDir: string, idOrName: string): CheckpointMeta | null {\n const items = listCheckpoints(rootDir);\n // Prefer exact id match, then most-recent name match.\n const byId = items.find((m) => m.id === idOrName);\n if (byId) return byId;\n const byName = [...items].reverse().find((m) => m.name === idOrName);\n return byName ?? null;\n}\n\nexport interface RestoreResult {\n /** Files we wrote back to disk. */\n restored: string[];\n /** Files we removed (snapshot had `content: null`, file existed). */\n removed: string[];\n /** Files we couldn't touch (errors), with the reason. */\n skipped: Array<{ path: string; reason: string }>;\n}\n\n/** Path-escape rechecked against live `rootDir` since snapshot's may differ (project moved). */\nexport function restoreCheckpoint(rootDir: string, id: string): RestoreResult {\n const cp = loadCheckpoint(rootDir, id);\n const absRoot = resolve(rootDir);\n const result: RestoreResult = { restored: [], removed: [], skipped: [] };\n if (!cp) {\n result.skipped.push({ path: \"(checkpoint)\", reason: `not found: ${id}` });\n return result;\n }\n for (const f of cp.files) {\n const abs = resolve(absRoot, f.path);\n if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep}`)) {\n result.skipped.push({ path: f.path, reason: \"path escapes rootDir\" });\n continue;\n }\n try {\n if (f.content === null) {\n if (existsSync(abs)) {\n rmSync(abs);\n result.removed.push(f.path);\n }\n } else {\n mkdirSync(dirname(abs), { recursive: true });\n writeFileSync(abs, f.content, \"utf8\");\n result.restored.push(f.path);\n }\n } catch (err) {\n result.skipped.push({ path: f.path, reason: (err as Error).message });\n }\n }\n return result;\n}\n\nexport function deleteCheckpoint(rootDir: string, id: string): boolean {\n const cpPath = snapshotPath(rootDir, id);\n let removed = false;\n if (existsSync(cpPath)) {\n try {\n rmSync(cpPath);\n removed = true;\n } catch {\n return false;\n }\n }\n const items = listCheckpoints(rootDir);\n const next = items.filter((m) => m.id !== id);\n if (next.length !== items.length) {\n writeIndex(rootDir, next);\n removed = true;\n }\n return removed;\n}\n\n/** Format ms-timestamp diff as human-readable relative age. */\nexport function fmtAgo(ms: number): string {\n const now = Date.now();\n const diff = Math.max(0, now - ms);\n const s = Math.floor(diff / 1000);\n if (s < 60) return `${s}s ago`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ago`;\n const h = Math.floor(m / 60);\n if (h < 24) return `${h}h ago`;\n const d = Math.floor(h / 24);\n return `${d}d ago`;\n}\n","/** 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, StepCompletion, StepEvidence } from \"../tools/plan.js\";\n\nexport interface PlanStateOnDisk {\n /** File format version — bump when shape changes. */\n version: 1 | 2;\n steps: PlanStep[];\n completedStepIds: string[];\n stepCompletions?: Record<string, StepCompletion>;\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 && parsed.version !== 2) 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 const targets = stringList(e.targets);\n if (targets) step.targets = targets;\n if (typeof e.acceptance === \"string\" && e.acceptance.trim()) {\n step.acceptance = e.acceptance.trim();\n }\n const verification = stringList(e.verification);\n if (verification) step.verification = verification;\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 stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);\n const out: PlanStateOnDisk = {\n version: parsed.version,\n steps,\n completedStepIds,\n updatedAt: parsed.updatedAt,\n };\n if (stepCompletions) out.stepCompletions = stepCompletions;\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?: {\n body?: string;\n summary?: string;\n stepCompletions?: ReadonlyMap<string, StepCompletion> | Record<string, StepCompletion>;\n },\n): void {\n const path = planStatePath(sessionName);\n try {\n mkdirSync(dirname(path), { recursive: true });\n const state: PlanStateOnDisk = {\n version: 2,\n steps,\n completedStepIds: [...completedStepIds],\n updatedAt: new Date().toISOString(),\n };\n const stepCompletions = normalizeStepCompletionsForWrite(extras?.stepCompletions);\n if (stepCompletions) state.stepCompletions = stepCompletions;\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 stepCompletions?: Record<string, StepCompletion>;\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 && parsed.version !== 2) 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 const stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);\n if (stepCompletions) entry.stepCompletions = stepCompletions;\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\nexport interface PlanArchiveWithSession extends PlanArchiveSummary {\n sessionName: string;\n}\n\n/** Cross-session enumeration in a single dir scan — used by the dashboard plans panel where the per-session loop was O(N×M) and timed out for users with hundreds of sessions. */\nexport function listAllPlanArchives(): PlanArchiveWithSession[] {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n const out: PlanArchiveWithSession[] = [];\n const suffix = \".done.json\";\n const planMarker = \".plan.\";\n for (const name of entries) {\n if (!name.endsWith(suffix)) continue;\n const planIdx = name.indexOf(planMarker);\n if (planIdx < 0) continue;\n const sessionName = name.slice(0, planIdx);\n if (!sessionName) 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 && parsed.version !== 2) 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 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: PlanArchiveWithSession = {\n sessionName,\n path: full,\n completedAt,\n steps,\n completedStepIds,\n };\n const stepCompletions = sanitizeStepCompletions(parsed.stepCompletions);\n if (stepCompletions) entry.stepCompletions = stepCompletions;\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 out.push(entry);\n } catch {\n // Skip the corrupt archive entirely.\n }\n }\n out.sort((a, b) => b.completedAt.localeCompare(a.completedAt));\n return out;\n}\n\nfunction stringList(raw: unknown): string[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const out = raw\n .map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n .filter((entry) => entry.length > 0);\n return out.length > 0 ? out : undefined;\n}\n\nfunction normalizeStepCompletionsForWrite(\n raw: ReadonlyMap<string, StepCompletion> | Record<string, StepCompletion> | undefined,\n): Record<string, StepCompletion> | undefined {\n if (!raw) return undefined;\n const entries =\n raw instanceof Map\n ? [...raw.entries()]\n : (Object.entries(raw) as Array<[string, StepCompletion]>);\n const out: Record<string, StepCompletion> = {};\n for (const [key, value] of entries) {\n const completion = sanitizeStepCompletion(value, key);\n if (completion) out[completion.stepId] = completion;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeStepCompletions(raw: unknown): Record<string, StepCompletion> | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n const out: Record<string, StepCompletion> = {};\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n const completion = sanitizeStepCompletion(value, key);\n if (completion) out[completion.stepId] = completion;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeStepCompletion(raw: unknown, fallbackStepId?: string): StepCompletion | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n const entry = raw as Record<string, unknown>;\n const stepId =\n typeof entry.stepId === \"string\" && entry.stepId.trim()\n ? entry.stepId.trim()\n : fallbackStepId?.trim();\n const result = typeof entry.result === \"string\" ? entry.result.trim() : \"\";\n if (!stepId || !result) return undefined;\n const completion: StepCompletion = { kind: \"step_completed\", stepId, result };\n if (typeof entry.title === \"string\" && entry.title.trim()) completion.title = entry.title.trim();\n if (typeof entry.notes === \"string\" && entry.notes.trim()) completion.notes = entry.notes.trim();\n const evidence = sanitizeEvidenceList(entry.evidence);\n if (evidence) completion.evidence = evidence;\n return completion;\n}\n\nfunction sanitizeEvidenceList(raw: unknown): StepEvidence[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const out: StepEvidence[] = [];\n for (const item of raw) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) continue;\n const entry = item as Record<string, unknown>;\n const kind = entry.kind;\n if (kind !== \"verification\" && kind !== \"diff\" && kind !== \"checkpoint\" && kind !== \"manual\") {\n continue;\n }\n const summary = typeof entry.summary === \"string\" ? entry.summary.trim() : \"\";\n if (!summary) continue;\n const evidence: StepEvidence = { kind, summary };\n if (typeof entry.command === \"string\" && entry.command.trim()) {\n evidence.command = entry.command.trim();\n }\n const paths = stringList(entry.paths);\n if (paths) evidence.paths = paths;\n out.push(evidence);\n }\n return out.length > 0 ? out : undefined;\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, SlashGroup } from \"./types.js\";\n\nexport const SLASH_GROUP_ORDER = [\n \"setup\",\n \"info\",\n \"chat\",\n \"extend\",\n \"session\",\n \"code\",\n \"jobs\",\n \"advanced\",\n] as const satisfies readonly SlashGroup[];\n\nexport const SLASH_GROUP_LABEL: Record<SlashGroup, string> = {\n setup: \"SETUP\",\n info: \"INFO\",\n chat: \"CHAT\",\n extend: \"EXTEND\",\n session: \"SESSION\",\n code: \"CODE\",\n jobs: \"JOBS\",\n advanced: \"ADVANCED\",\n};\n\nconst SLASH_GROUP_RANK = new Map<SlashGroup, number>(\n SLASH_GROUP_ORDER.map((group, index) => [group, index]),\n);\n\nexport function orderSlashCommandsByGroup<T extends Pick<SlashCommandSpec, \"group\">>(\n commands: readonly T[],\n): T[] {\n return commands\n .map((command, index) => ({ command, index }))\n .sort((a, b) => {\n const groupDiff =\n SLASH_GROUP_RANK.get(a.command.group)! - SLASH_GROUP_RANK.get(b.command.group)!;\n if (groupDiff !== 0) return groupDiff;\n return a.index - b.index;\n })\n .map((entry) => entry.command);\n}\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 cmd: \"btw\",\n group: \"chat\",\n argsHint: \"<question>\",\n summary:\n \"ask a quick side question — answered from a blank slate, never added to the conversation context\",\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: \"language\",\n group: \"setup\",\n argsHint: \"<EN|zh-CN>\",\n summary: \"switch the runtime language\",\n argCompleter: [\"EN\", \"zh-CN\"],\n aliases: [\"lang\"],\n },\n {\n cmd: \"theme\",\n group: \"setup\",\n argsHint: \"[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]\",\n summary: \"show or persist the terminal theme preference. Bare opens picker.\",\n argCompleter: [\n \"auto\",\n \"default\",\n \"dark\",\n \"light\",\n \"tokyo-night\",\n \"github-dark\",\n \"github-light\",\n \"high-contrast\",\n ],\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: \"keys\",\n group: \"info\",\n summary: \"keyboard + mouse + copy/paste reference\",\n },\n {\n cmd: \"copy\",\n group: \"chat\",\n summary: \"vim/tmux-style copy mode — j/k navigate, v select, y yank to clipboard\",\n },\n {\n cmd: \"feedback\",\n group: \"info\",\n summary: \"open a GitHub issue with diagnostic info copied to clipboard\",\n },\n\n { cmd: \"sessions\", group: \"session\", summary: \"list saved sessions (current marked with ▸)\" },\n {\n cmd: \"title\",\n group: \"session\",\n summary: \"ask the model to rename this session from the conversation\",\n aliases: [\"retitle\"],\n },\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:\n \"[list|paths|paths add <path>|paths remove <path|N>|show <name>|new <name>|<name> [args]]\",\n summary: \"list / run / scaffold skills (project + custom + global + builtin)\",\n argCompleter: \"skills\",\n },\n {\n cmd: \"qq\",\n group: \"extend\",\n argsHint: \"<connect|status|disconnect>\",\n summary: \"connect, inspect, or disconnect the QQ channel\",\n argCompleter: [\"connect\", \"status\", \"disconnect\"],\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|strict]\",\n summary: \"toggle read-only plan mode / strict lifecycle rails\",\n contextual: \"code\",\n argCompleter: [\"on\", \"off\", \"strict\"],\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 argCompleter: \"path\",\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: \"search-engine\",\n group: \"advanced\",\n argsHint: \"<mojeek|searxng|metaso> [<endpoint>]\",\n summary:\n \"switch web search backend — mojeek (default, no deps), searxng (self-hosted), or metaso (free quota 100/d)\",\n argCompleter: [\"mojeek\", \"searxng\", \"metaso\"],\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 // Empty prefix = browsing the menu — show the full release command surface except\n // advanced rows, which remain collapsed behind the footer hint.\n if (p === \"\") return c.group !== \"advanced\";\n if (c.contextual === \"code\" && !codeMode) return false;\n if (c.cmd.startsWith(p)) return true;\n return c.aliases?.some((a) => a.startsWith(p)) ?? false;\n });\n if (p === \"\") return orderSlashCommandsByGroup(matches);\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 // \"//\" is a line comment, not a slash command\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":["existsSync","mkdirSync","readFileSync","readdirSync","writeFileSync","dirname","join","join","existsSync","readFileSync","mkdirSync","dirname","writeFileSync","readdirSync"]}
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
3
3
  import {
4
4
  loadLanguage,
5
5
  saveLanguage
6
- } from "./chunk-24A7FHGJ.js";
6
+ } from "./chunk-6CRPCJAU.js";
7
7
 
8
8
  // src/i18n/EN.ts
9
9
  var EN = {
@@ -450,6 +450,7 @@ var EN = {
450
450
  reviewSaveError: "Could not save config: {message}",
451
451
  reviewFooter: "[Enter] save \xB7 [Esc] cancel",
452
452
  savedTitle: "\u25B8 Saved.",
453
+ savedShellHint: "Shell commands the model wants to run ask each time \u2014 pick `allow always` on the prompt to whitelist that exact command for this project. No global allow-all flag by design.",
453
454
  savedFooter: "[Enter] to exit",
454
455
  selectFooter: "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel",
455
456
  stepCounter: "Step {step}/{total} \xB7 ",
@@ -514,6 +515,8 @@ var EN = {
514
515
  title: "Checkpoint \u2014 step done",
515
516
  continue: "Continue \u2014 run the next step",
516
517
  continueHint: "Model resumes with the next step.",
518
+ finish: "Finish \u2014 summarize and close",
519
+ finishHint: "Model records the final step and summarizes the completed plan.",
517
520
  revise: "Revise \u2014 give feedback before the next step",
518
521
  reviseHint: "Stay paused, type guidance; model adjusts the remaining plan.",
519
522
  stop: "Stop \u2014 end the plan here",
@@ -654,6 +657,10 @@ var EN = {
654
657
  helpShellDetail: " the conversation so the model sees it next turn.",
655
658
  helpShellConsent: " No allowlist gate \u2014 user-typed = explicit consent.",
656
659
  helpShellExample: " Example: !git status !ls src/ !npm test",
660
+ helpShellGateTitle: "Model-invoked shell commands (per-call approval):",
661
+ helpShellGate: " \u2191\u2193 + \u23CE each call shows a prompt with `allow once` / `allow always`",
662
+ helpShellGateDetail: " / `deny`. Pick `allow always` to whitelist that exact",
663
+ helpShellGatePolicy: " command prefix for this project. No global allow-all flag.",
657
664
  helpMemoryTitle: "Quick memory:",
658
665
  helpMemoryPin: " #<note> append <note> to <project>/REASONIX.md (committable).",
659
666
  helpMemoryPinEx: " Example: #findByEmail must be case-insensitive",
@@ -965,7 +972,7 @@ var EN = {
965
972
  changesNoteShort: "Changes take effect on next /new or launch. Subcommands: /memory list | show | forget | clear"
966
973
  },
967
974
  mcp: {
968
- noServers: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog.',
975
+ noServers: 'no MCP servers attached. Run `reasonix setup` to pick some, or launch with --mcp "<spec>". `reasonix mcp list` shows the catalog. Note: model-invoked shell commands are gated per-call (allow once / allow always / deny) \u2014 no global allow-all flag.',
969
976
  toolsLabel: " tools {count}",
970
977
  resourcesHint: "`/resource` to browse+read",
971
978
  promptsHint: "`/prompt` to browse+fetch",
@@ -1396,7 +1403,7 @@ var EN = {
1396
1403
  slow: "slow \xB7 {ms}ms",
1397
1404
  verySlow: "very slow \xB7 {ms}ms",
1398
1405
  slowToast: "\u26A0 MCP `{name}` slow \xB7 {seconds}s p95 over the last {sampleSize} calls",
1399
- emptyHint: "\u2139 no MCP servers configured \u2014 try: `reasonix setup` to re-pick, or `reasonix mcp install filesystem`"
1406
+ emptyHint: "\u2139 no MCP servers configured \u2014 try: `reasonix setup` to re-pick, or `reasonix mcp install filesystem` \xB7 shell commands gate per-call (allow once / allow always / deny), no global allow-all"
1400
1407
  },
1401
1408
  denyContextInput: {
1402
1409
  description: "Tell the agent why you denied this. The next attempt will see your reason as additional context."
@@ -1957,6 +1964,7 @@ var zhCN = {
1957
1964
  reviewSaveError: "\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25\uFF1A{message}",
1958
1965
  reviewFooter: "[Enter] \u4FDD\u5B58 \xB7 [Esc] \u53D6\u6D88",
1959
1966
  savedTitle: "\u25B8 \u5DF2\u4FDD\u5B58\u3002",
1967
+ savedShellHint: "\u6A21\u578B\u53D1\u8D77\u7684 shell \u547D\u4EE4\u6BCF\u6B21\u90FD\u4F1A\u5F39\u51FA\u786E\u8BA4 \u2014\u2014 \u5728\u63D0\u793A\u6846\u91CC\u9009 `allow always` \u53EF\u5C06\u8BE5\u547D\u4EE4\u524D\u7F00\u52A0\u5165\u672C\u9879\u76EE\u767D\u540D\u5355\u3002\u8BBE\u8BA1\u4E0A\u6CA1\u6709\u300C\u5168\u5C40\u653E\u884C\u300D\u5F00\u5173\u3002",
1960
1968
  savedFooter: "[Enter] \u9000\u51FA",
1961
1969
  selectFooter: "[\u2191\u2193] \u79FB\u52A8 \xB7 [Enter] \u786E\u8BA4 \xB7 [Esc] \u53D6\u6D88",
1962
1970
  stepCounter: "\u6B65\u9AA4 {step}/{total} \xB7 ",
@@ -2021,6 +2029,8 @@ var zhCN = {
2021
2029
  title: "\u68C0\u67E5\u70B9 \u2014\u2014 \u5F53\u524D\u6B65\u9AA4\u5DF2\u5B8C\u6210",
2022
2030
  continue: "\u7EE7\u7EED \u2014\u2014 \u6267\u884C\u4E0B\u4E00\u6B65",
2023
2031
  continueHint: "\u6A21\u578B\u4ECE\u4E0B\u4E00\u6B65\u7EE7\u7EED\u3002",
2032
+ finish: "\u5B8C\u6210 \u2014\u2014 \u603B\u7ED3\u5E76\u6536\u5C3E",
2033
+ finishHint: "\u6A21\u578B\u8BB0\u5F55\u6700\u540E\u4E00\u6B65\uFF0C\u7136\u540E\u603B\u7ED3\u5DF2\u5B8C\u6210\u7684\u8BA1\u5212\u3002",
2024
2034
  revise: "\u8C03\u6574 \u2014\u2014 \u5728\u4E0B\u4E00\u6B65\u524D\u7ED9\u53CD\u9988",
2025
2035
  reviseHint: "\u5148\u6682\u505C\uFF0C\u8F93\u5165\u6307\u5F15\uFF1B\u6A21\u578B\u4F1A\u8C03\u6574\u5269\u4F59\u8BA1\u5212\u3002",
2026
2036
  stop: "\u505C\u6B62 \u2014\u2014 \u5728\u6B64\u7ED3\u675F\u8BA1\u5212",
@@ -2161,6 +2171,10 @@ var zhCN = {
2161
2171
  helpShellDetail: " \u4EE5\u4FBF\u6A21\u578B\u5728\u4E0B\u4E00\u8F6E\u770B\u5230\u3002\u65E0\u5141\u8BB8\u5217\u8868\u9650\u5236\u3002",
2162
2172
  helpShellConsent: " \u7528\u6237\u8F93\u5165 = \u660E\u786E\u540C\u610F\u3002",
2163
2173
  helpShellExample: " \u793A\u4F8B\uFF1A!git status !ls src/ !npm test",
2174
+ helpShellGateTitle: "\u6A21\u578B\u53D1\u8D77\u7684 shell \u547D\u4EE4\uFF08\u6309\u6B21\u5BA1\u6279\uFF09\uFF1A",
2175
+ helpShellGate: " \u2191\u2193 + \u23CE \u6BCF\u6B21\u90FD\u4F1A\u5F39\u51FA `allow once` / `allow always` /",
2176
+ helpShellGateDetail: " `deny` \u4E09\u9009\u4E00\u3002\u9009 `allow always` \u53EF\u5C06\u8BE5\u547D\u4EE4\u524D\u7F00",
2177
+ helpShellGatePolicy: " \u52A0\u5165\u672C\u9879\u76EE\u767D\u540D\u5355\u3002\u8BBE\u8BA1\u4E0A\u6CA1\u6709\u300C\u5168\u5C40\u653E\u884C\u300D\u5F00\u5173\u3002",
2164
2178
  helpMemoryTitle: "\u5FEB\u901F\u8BB0\u5FC6\uFF1A",
2165
2179
  helpMemoryPin: " #<note> \u5C06 <note> \u8FFD\u52A0\u5230 <project>/REASONIX.md\uFF08\u53EF\u63D0\u4EA4\uFF09\u3002",
2166
2180
  helpMemoryPinEx: " \u793A\u4F8B\uFF1A#findByEmail \u5FC5\u987B\u533A\u5206\u5927\u5C0F\u5199",
@@ -2472,7 +2486,7 @@ var zhCN = {
2472
2486
  changesNoteShort: "\u66F4\u6539\u5728\u4E0B\u6B21 /new \u6216\u542F\u52A8\u65F6\u751F\u6548\u3002\u5B50\u547D\u4EE4\uFF1A/memory list | show | forget | clear"
2473
2487
  },
2474
2488
  mcp: {
2475
- noServers: '\u672A\u9644\u52A0 MCP \u670D\u52A1\u5668\u3002\u8FD0\u884C `reasonix setup` \u9009\u62E9\u4E00\u4E9B\uFF0C\u6216\u4F7F\u7528 --mcp "<spec>" \u542F\u52A8\u3002`reasonix mcp list` \u663E\u793A\u76EE\u5F55\u3002',
2489
+ noServers: '\u672A\u9644\u52A0 MCP \u670D\u52A1\u5668\u3002\u8FD0\u884C `reasonix setup` \u9009\u62E9\u4E00\u4E9B\uFF0C\u6216\u4F7F\u7528 --mcp "<spec>" \u542F\u52A8\u3002`reasonix mcp list` \u663E\u793A\u76EE\u5F55\u3002\u6CE8\uFF1A\u6A21\u578B\u53D1\u8D77\u7684 shell \u547D\u4EE4\u6309\u6B21\u5BA1\u6279\uFF08allow once / allow always / deny\uFF09\uFF0C\u8BBE\u8BA1\u4E0A\u6CA1\u6709\u300C\u5168\u5C40\u653E\u884C\u300D\u5F00\u5173\u3002',
2476
2490
  toolsLabel: " \u5DE5\u5177 {count}",
2477
2491
  resourcesHint: "`/resource` \u6D4F\u89C8+\u8BFB\u53D6",
2478
2492
  promptsHint: "`/prompt` \u6D4F\u89C8+\u83B7\u53D6",
@@ -2903,7 +2917,7 @@ var zhCN = {
2903
2917
  slow: "\u7F13\u6162 \xB7 {ms}ms",
2904
2918
  verySlow: "\u975E\u5E38\u6162 \xB7 {ms}ms",
2905
2919
  slowToast: "\u26A0 MCP `{name}` \u54CD\u5E94\u7F13\u6162 \xB7 P95 {seconds}s \xB7 \u6700\u8FD1 {sampleSize} \u6B21\u8C03\u7528",
2906
- emptyHint: "\u2139 \u672A\u914D\u7F6E MCP \u670D\u52A1\u5668 \u2014\u2014 \u53EF\u5C1D\u8BD5\uFF1A`reasonix setup` \u91CD\u65B0\u9009\u62E9\uFF0C\u6216 `reasonix mcp install filesystem`"
2920
+ emptyHint: "\u2139 \u672A\u914D\u7F6E MCP \u670D\u52A1\u5668 \u2014\u2014 \u53EF\u5C1D\u8BD5\uFF1A`reasonix setup` \u91CD\u65B0\u9009\u62E9\uFF0C\u6216 `reasonix mcp install filesystem` \xB7 shell \u547D\u4EE4\u6309\u6B21\u5BA1\u6279\uFF08allow once / allow always / deny\uFF09\uFF0C\u65E0\u5168\u5C40\u653E\u884C"
2907
2921
  },
2908
2922
  denyContextInput: {
2909
2923
  description: "\u544A\u8BC9\u6A21\u578B\u4F60\u4E3A\u4EC0\u4E48\u62D2\u7EDD\u4E86\u3002\u6A21\u578B\u4E0B\u6B21\u4F1A\u770B\u5230\u4F60\u7684\u7406\u7531\u4F5C\u4E3A\u989D\u5916\u7684\u4E0A\u4E0B\u6587\u3002"
@@ -3108,4 +3122,4 @@ export {
3108
3122
  tObj,
3109
3123
  t
3110
3124
  };
3111
- //# sourceMappingURL=chunk-KDRUEXII.js.map
3125
+ //# sourceMappingURL=chunk-NRQ5UP5T.js.map