reasonix 0.46.1 → 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 (139) hide show
  1. package/README.md +62 -13
  2. package/README.zh-CN.md +52 -10
  3. package/dashboard/dist/app.js +217 -60
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/{acp-LKJU5DZX.js → acp-GEOAKSTU.js} +26 -54
  6. package/dist/cli/acp-GEOAKSTU.js.map +1 -0
  7. package/dist/cli/chat-YTPATMMG.js +51 -0
  8. package/dist/cli/{chunk-R2ASNSEO.js → chunk-2XY77LW7.js} +8 -8
  9. package/dist/cli/{chunk-SE7C5ZSI.js → chunk-4MFCAZ2W.js} +3 -3
  10. package/dist/cli/{chunk-DGA5QYFM.js → chunk-6CRPCJAU.js} +55 -13
  11. package/dist/cli/chunk-6CRPCJAU.js.map +1 -0
  12. package/dist/cli/{chunk-TDSBASOF.js → chunk-6QC5RQLE.js} +2 -2
  13. package/dist/cli/chunk-BQ6HC66J.js +530 -0
  14. package/dist/cli/chunk-BQ6HC66J.js.map +1 -0
  15. package/dist/cli/{chunk-7SGGXNB2.js → chunk-CCJAP7G3.js} +2 -2
  16. package/dist/cli/{chunk-3AAG2CUT.js → chunk-CNG32VAB.js} +2 -2
  17. package/dist/cli/{chunk-WRONKNIH.js → chunk-DN4B5S6Y.js} +2 -2
  18. package/dist/cli/{chunk-NCBP5D6E.js → chunk-DQ6K5ZQ7.js} +2 -2
  19. package/dist/cli/{chunk-MIIZJD5O.js → chunk-DWPAKZTY.js} +14 -3
  20. package/dist/cli/chunk-DWPAKZTY.js.map +1 -0
  21. package/dist/cli/{chunk-IYQ325V7.js → chunk-E5WCLUIU.js} +2 -2
  22. package/dist/cli/{chunk-YRLC2EDF.js → chunk-EQATK2L2.js} +2 -2
  23. package/dist/cli/{chunk-TEUDEGX2.js → chunk-FY4S7TJZ.js} +19 -5
  24. package/dist/cli/chunk-FY4S7TJZ.js.map +1 -0
  25. package/dist/cli/{chunk-C72TNHDE.js → chunk-GH7DC2Y5.js} +2 -2
  26. package/dist/cli/{chunk-WQ6ZRDQM.js → chunk-HIYTRCSW.js} +16 -12
  27. package/dist/cli/chunk-HIYTRCSW.js.map +1 -0
  28. package/dist/cli/{chunk-EAOL43HB.js → chunk-HUILPCYX.js} +3 -3
  29. package/dist/cli/{chunk-ZOQHVQON.js → chunk-JBH5RM7X.js} +473 -87
  30. package/dist/cli/chunk-JBH5RM7X.js.map +1 -0
  31. package/dist/cli/{chunk-XPAUNFOL.js → chunk-KVZZ5U75.js} +3 -2
  32. package/dist/cli/chunk-KVZZ5U75.js.map +1 -0
  33. package/dist/cli/{chunk-2AASOSD5.js → chunk-KYQVQ5X4.js} +85 -10
  34. package/dist/cli/chunk-KYQVQ5X4.js.map +1 -0
  35. package/dist/cli/{chunk-2425HK6U.js → chunk-LGEKVMMV.js} +7 -2
  36. package/dist/cli/{chunk-2425HK6U.js.map → chunk-LGEKVMMV.js.map} +1 -1
  37. package/dist/cli/{chunk-6VANO7KB.js → chunk-NRQ5UP5T.js} +165 -24
  38. package/dist/cli/chunk-NRQ5UP5T.js.map +1 -0
  39. package/dist/cli/{chunk-M4E5JK6S.js → chunk-QCFLPSPH.js} +2 -2
  40. package/dist/cli/{chunk-E7TAHQ4A.js → chunk-RRXUIPWG.js} +19 -18
  41. package/dist/cli/chunk-RRXUIPWG.js.map +1 -0
  42. package/dist/cli/{chunk-JLQDNLZF.js → chunk-T5A7EY6B.js} +26 -14
  43. package/dist/cli/chunk-T5A7EY6B.js.map +1 -0
  44. package/dist/cli/{chunk-7LOJS3LV.js → chunk-TDHXB2ER.js} +2 -2
  45. package/dist/cli/{chunk-CXVWUPA3.js → chunk-TKVXTQ3T.js} +26 -26
  46. package/dist/cli/chunk-TKVXTQ3T.js.map +1 -0
  47. package/dist/cli/{chunk-JVFEJAJX.js → chunk-TRSAHHCL.js} +107 -11
  48. package/dist/cli/chunk-TRSAHHCL.js.map +1 -0
  49. package/dist/cli/{chunk-K3AIFMI6.js → chunk-TRWHTFG7.js} +2 -2
  50. package/dist/cli/{chunk-7YW6TPXK.js → chunk-XD6P7AFH.js} +28 -31
  51. package/dist/cli/chunk-XD6P7AFH.js.map +1 -0
  52. package/dist/cli/{chunk-SPXN5JIT.js → chunk-XMHP7BEE.js} +1787 -1081
  53. package/dist/cli/chunk-XMHP7BEE.js.map +1 -0
  54. package/dist/cli/{chunk-JVQT5IYP.js → chunk-YFP3MYMY.js} +19 -9
  55. package/dist/cli/chunk-YFP3MYMY.js.map +1 -0
  56. package/dist/cli/{chunk-HNXDZGC6.js → chunk-ZXSCAODE.js} +9 -9
  57. package/dist/cli/{chunk-HNXDZGC6.js.map → chunk-ZXSCAODE.js.map} +1 -1
  58. package/dist/cli/{code-2JIHL5M2.js → code-Q4NRVEDG.js} +42 -35
  59. package/dist/cli/code-Q4NRVEDG.js.map +1 -0
  60. package/dist/cli/{commands-OPT5AJNH.js → commands-4CDI4GFM.js} +4 -4
  61. package/dist/cli/{commit-KA37H6GM.js → commit-GW7LDQP5.js} +3 -3
  62. package/dist/cli/{desktop-5ONTRU3C.js → desktop-EG6P5SF2.js} +321 -36
  63. package/dist/cli/desktop-EG6P5SF2.js.map +1 -0
  64. package/dist/cli/{diff-SOIA7AKH.js → diff-VI2YX4FN.js} +8 -8
  65. package/dist/cli/{doctor-RCUP4XRV.js → doctor-CQTTZP27.js} +9 -9
  66. package/dist/cli/{events-6KHITNX4.js → events-VRYXOSKI.js} +3 -3
  67. package/dist/cli/index.js +94 -45
  68. package/dist/cli/index.js.map +1 -1
  69. package/dist/cli/{mcp-JP5OWD6R.js → mcp-J2UCD4RZ.js} +2 -2
  70. package/dist/cli/{mcp-browse-ONCJJPJN.js → mcp-browse-GSX34JEK.js} +2 -2
  71. package/dist/cli/{mcp-inspect-TPLHW5JA.js → mcp-inspect-RRFYF4ZV.js} +4 -4
  72. package/dist/cli/{prompt-RJDNCQAP.js → prompt-5TQPIVHV.js} +4 -4
  73. package/dist/cli/{prune-sessions-MKEATRVL.js → prune-sessions-SEWX7GP6.js} +2 -2
  74. package/dist/cli/{replay-4NILJG4U.js → replay-MJCEMODU.js} +9 -9
  75. package/dist/cli/{run-WFGXB4SB.js → run-P4D5VDYE.js} +17 -17
  76. package/dist/cli/{server-5VFQP3PV.js → server-C25JNNZV.js} +82 -34
  77. package/dist/cli/server-C25JNNZV.js.map +1 -0
  78. package/dist/cli/{sessions-5XDJDALO.js → sessions-QIONZJQ6.js} +15 -15
  79. package/dist/cli/{setup-F6XSWLRA.js → setup-NLQ6G5G4.js} +7 -7
  80. package/dist/cli/setup-NLQ6G5G4.js.map +1 -0
  81. package/dist/cli/{stats-ALHBZICE.js → stats-DFZEXHP4.js} +6 -6
  82. package/dist/cli/{version-JVRAHBMM.js → version-GR3X3MPI.js} +15 -15
  83. package/dist/index.d.ts +69 -56
  84. package/dist/index.js +791 -303
  85. package/dist/index.js.map +1 -1
  86. package/package.json +3 -1
  87. package/dist/cli/acp-LKJU5DZX.js.map +0 -1
  88. package/dist/cli/chat-W7LAWEN6.js +0 -51
  89. package/dist/cli/chunk-2AASOSD5.js.map +0 -1
  90. package/dist/cli/chunk-6VANO7KB.js.map +0 -1
  91. package/dist/cli/chunk-7YW6TPXK.js.map +0 -1
  92. package/dist/cli/chunk-CXVWUPA3.js.map +0 -1
  93. package/dist/cli/chunk-DGA5QYFM.js.map +0 -1
  94. package/dist/cli/chunk-DHRVZJ2D.js +0 -642
  95. package/dist/cli/chunk-DHRVZJ2D.js.map +0 -1
  96. package/dist/cli/chunk-E7TAHQ4A.js.map +0 -1
  97. package/dist/cli/chunk-JLQDNLZF.js.map +0 -1
  98. package/dist/cli/chunk-JVFEJAJX.js.map +0 -1
  99. package/dist/cli/chunk-JVQT5IYP.js.map +0 -1
  100. package/dist/cli/chunk-MIIZJD5O.js.map +0 -1
  101. package/dist/cli/chunk-SPXN5JIT.js.map +0 -1
  102. package/dist/cli/chunk-TEUDEGX2.js.map +0 -1
  103. package/dist/cli/chunk-WQ6ZRDQM.js.map +0 -1
  104. package/dist/cli/chunk-XPAUNFOL.js.map +0 -1
  105. package/dist/cli/chunk-ZOQHVQON.js.map +0 -1
  106. package/dist/cli/code-2JIHL5M2.js.map +0 -1
  107. package/dist/cli/desktop-5ONTRU3C.js.map +0 -1
  108. package/dist/cli/server-5VFQP3PV.js.map +0 -1
  109. package/dist/cli/setup-F6XSWLRA.js.map +0 -1
  110. /package/dist/cli/{chat-W7LAWEN6.js.map → chat-YTPATMMG.js.map} +0 -0
  111. /package/dist/cli/{chunk-R2ASNSEO.js.map → chunk-2XY77LW7.js.map} +0 -0
  112. /package/dist/cli/{chunk-SE7C5ZSI.js.map → chunk-4MFCAZ2W.js.map} +0 -0
  113. /package/dist/cli/{chunk-TDSBASOF.js.map → chunk-6QC5RQLE.js.map} +0 -0
  114. /package/dist/cli/{chunk-7SGGXNB2.js.map → chunk-CCJAP7G3.js.map} +0 -0
  115. /package/dist/cli/{chunk-3AAG2CUT.js.map → chunk-CNG32VAB.js.map} +0 -0
  116. /package/dist/cli/{chunk-WRONKNIH.js.map → chunk-DN4B5S6Y.js.map} +0 -0
  117. /package/dist/cli/{chunk-NCBP5D6E.js.map → chunk-DQ6K5ZQ7.js.map} +0 -0
  118. /package/dist/cli/{chunk-IYQ325V7.js.map → chunk-E5WCLUIU.js.map} +0 -0
  119. /package/dist/cli/{chunk-YRLC2EDF.js.map → chunk-EQATK2L2.js.map} +0 -0
  120. /package/dist/cli/{chunk-C72TNHDE.js.map → chunk-GH7DC2Y5.js.map} +0 -0
  121. /package/dist/cli/{chunk-EAOL43HB.js.map → chunk-HUILPCYX.js.map} +0 -0
  122. /package/dist/cli/{chunk-M4E5JK6S.js.map → chunk-QCFLPSPH.js.map} +0 -0
  123. /package/dist/cli/{chunk-7LOJS3LV.js.map → chunk-TDHXB2ER.js.map} +0 -0
  124. /package/dist/cli/{chunk-K3AIFMI6.js.map → chunk-TRWHTFG7.js.map} +0 -0
  125. /package/dist/cli/{commands-OPT5AJNH.js.map → commands-4CDI4GFM.js.map} +0 -0
  126. /package/dist/cli/{commit-KA37H6GM.js.map → commit-GW7LDQP5.js.map} +0 -0
  127. /package/dist/cli/{diff-SOIA7AKH.js.map → diff-VI2YX4FN.js.map} +0 -0
  128. /package/dist/cli/{doctor-RCUP4XRV.js.map → doctor-CQTTZP27.js.map} +0 -0
  129. /package/dist/cli/{events-6KHITNX4.js.map → events-VRYXOSKI.js.map} +0 -0
  130. /package/dist/cli/{mcp-JP5OWD6R.js.map → mcp-J2UCD4RZ.js.map} +0 -0
  131. /package/dist/cli/{mcp-browse-ONCJJPJN.js.map → mcp-browse-GSX34JEK.js.map} +0 -0
  132. /package/dist/cli/{mcp-inspect-TPLHW5JA.js.map → mcp-inspect-RRFYF4ZV.js.map} +0 -0
  133. /package/dist/cli/{prompt-RJDNCQAP.js.map → prompt-5TQPIVHV.js.map} +0 -0
  134. /package/dist/cli/{prune-sessions-MKEATRVL.js.map → prune-sessions-SEWX7GP6.js.map} +0 -0
  135. /package/dist/cli/{replay-4NILJG4U.js.map → replay-MJCEMODU.js.map} +0 -0
  136. /package/dist/cli/{run-WFGXB4SB.js.map → run-P4D5VDYE.js.map} +0 -0
  137. /package/dist/cli/{sessions-5XDJDALO.js.map → sessions-QIONZJQ6.js.map} +0 -0
  138. /package/dist/cli/{stats-ALHBZICE.js.map → stats-DFZEXHP4.js.map} +0 -0
  139. /package/dist/cli/{version-JVRAHBMM.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-JLQDNLZF.js";
6
+ } from "./chunk-T5A7EY6B.js";
7
7
 
8
8
  // src/index/semantic/tool.ts
9
9
  async function registerSemanticSearchTool(registry, opts) {
@@ -43,6 +43,7 @@ async function registerSemanticSearchTool(registry, opts) {
43
43
  apiKey: opts.apiKey,
44
44
  model: opts.model,
45
45
  extraBody: opts.extraBody,
46
+ batchSize: opts.batchSize,
46
47
  signal: ctx?.signal
47
48
  });
48
49
  if (hits === null) {
@@ -92,4 +93,4 @@ export {
92
93
  registerSemanticSearchTool,
93
94
  bootstrapSemanticSearchInCodeMode
94
95
  };
95
- //# sourceMappingURL=chunk-XPAUNFOL.js.map
96
+ //# sourceMappingURL=chunk-KVZZ5U75.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index/semantic/tool.ts"],"sourcesContent":["import type { ToolRegistry } from \"../../tools.js\";\nimport { indexCompatible, indexExists, querySemantic } from \"./builder.js\";\nimport type { SearchHit } from \"./store.js\";\n\ntype SemanticToolOptions = {\n provider?: \"ollama\" | \"openai-compat\";\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n extraBody?: Record<string, unknown>;\n timeoutMs?: number;\n batchSize?: number;\n root: string;\n defaultTopK?: number;\n defaultMinScore?: number;\n};\n\nexport async function registerSemanticSearchTool(\n registry: ToolRegistry,\n opts: SemanticToolOptions,\n): Promise<boolean> {\n if (!(await indexCompatible(opts.root, { provider: opts.provider, model: opts.model })))\n return false;\n const defaultTopK = opts.defaultTopK ?? 8;\n const defaultMinScore = opts.defaultMinScore ?? 0.3;\n\n registry.register({\n name: \"semantic_search\",\n description:\n \"FIRST CHOICE for descriptive queries. Use this BEFORE search_content (grep) when the user describes WHAT code does ('where do we handle X', 'which file owns Y', 'how does Z work', 'find the logic that …'). Returns ranked snippets ordered by semantic relevance — finds the right file even when your description shares no words with the code. Falls back to search_content / search_files only for: exact identifiers, regex patterns, or counting occurrences of a known token. If your first instinct is grep on a paraphrased question, you are wrong — try semantic_search first.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description:\n \"Natural-language description, phrased as a question or noun phrase: 'where do we validate the session cookie?' / 'retry backoff logic' / 'code that prevents user changes from immediately landing on disk'. Do NOT pass exact identifiers — those are search_content's job.\",\n },\n topK: {\n type: \"integer\",\n description: `Number of snippets to return (1..16). Default ${defaultTopK}.`,\n },\n minScore: {\n type: \"number\",\n description: `Drop snippets with cosine score below this (0..1). Default ${defaultMinScore}. Raise for stricter matches; lower if the index is small.`,\n },\n },\n required: [\"query\"],\n },\n fn: async (args: { query: string; topK?: number; minScore?: number }, ctx) => {\n const hits = await querySemantic(opts.root, args.query, {\n topK: args.topK ?? defaultTopK,\n minScore: args.minScore ?? defaultMinScore,\n provider: opts.provider,\n baseUrl: opts.baseUrl,\n apiKey: opts.apiKey,\n model: opts.model,\n extraBody: opts.extraBody,\n batchSize: opts.batchSize,\n signal: ctx?.signal,\n });\n if (hits === null) {\n return \"No semantic index found for this project. Run `reasonix index` to build one.\";\n }\n if (hits.length === 0) {\n return `query: ${args.query}\\n\\nno matches above the score threshold (${args.minScore ?? defaultMinScore}).`;\n }\n return formatHits(args.query, hits);\n },\n });\n return true;\n}\n\nexport function formatHits(query: string, hits: readonly SearchHit[]): string {\n const lines: string[] = [`query: ${query}`, `\\nresults (${hits.length}):`];\n hits.forEach((h, i) => {\n const { entry, score } = h;\n lines.push(\n `\\n${i + 1}. ${entry.path}:${entry.startLine}-${entry.endLine} (score ${score.toFixed(3)})`,\n );\n // Cap each snippet so a 60-line chunk doesn't dominate the\n // model's context. The full chunk is still discoverable via\n // read_file once the model picks the most relevant hit.\n const preview = entry.text.split(\"\\n\").slice(0, 8).join(\"\\n\");\n lines.push(indentBlock(preview, \" \"));\n if (entry.text.split(\"\\n\").length > 8) {\n lines.push(\n ` …(${entry.text.split(\"\\n\").length - 8} more lines — read_file ${entry.path}:${entry.startLine} for the full chunk)`,\n );\n }\n });\n return lines.join(\"\\n\");\n}\n\nfunction indentBlock(text: string, prefix: string): string {\n return text\n .split(\"\\n\")\n .map((l) => prefix + l)\n .join(\"\\n\");\n}\n\n/** Silent: register if index exists, else skip — no Ollama probe, no setup prompt. */\nexport async function bootstrapSemanticSearchInCodeMode(\n registry: ToolRegistry,\n rootDir: string,\n opts: Omit<SemanticToolOptions, \"root\" | \"defaultTopK\" | \"defaultMinScore\"> = {},\n): Promise<{ enabled: boolean }> {\n if (await indexCompatible(rootDir, { provider: opts.provider, model: opts.model })) {\n await registerSemanticSearchTool(registry, { ...opts, root: rootDir });\n return { enabled: true };\n }\n return { enabled: false };\n}\n"],"mappings":";;;;;;;;AAiBA,eAAsB,2BACpB,UACA,MACkB;AAClB,MAAI,CAAE,MAAM,gBAAgB,KAAK,MAAM,EAAE,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,CAAC;AACnF,WAAO;AACT,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,WAAS,SAAS;AAAA,IAChB,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,cAAc;AAAA,IACd,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,OAAO;AAAA,UACL,MAAM;AAAA,UACN,aACE;AAAA,QACJ;AAAA,QACA,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa,iDAAiD,WAAW;AAAA,QAC3E;AAAA,QACA,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa,8DAA8D,eAAe;AAAA,QAC5F;AAAA,MACF;AAAA,MACA,UAAU,CAAC,OAAO;AAAA,IACpB;AAAA,IACA,IAAI,OAAO,MAA2D,QAAQ;AAC5E,YAAM,OAAO,MAAM,cAAc,KAAK,MAAM,KAAK,OAAO;AAAA,QACtD,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU,KAAK,YAAY;AAAA,QAC3B,UAAU,KAAK;AAAA,QACf,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,UAAI,SAAS,MAAM;AACjB,eAAO;AAAA,MACT;AACA,UAAI,KAAK,WAAW,GAAG;AACrB,eAAO,UAAU,KAAK,KAAK;AAAA;AAAA,wCAA6C,KAAK,YAAY,eAAe;AAAA,MAC1G;AACA,aAAO,WAAW,KAAK,OAAO,IAAI;AAAA,IACpC;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,WAAW,OAAe,MAAoC;AAC5E,QAAM,QAAkB,CAAC,UAAU,KAAK,IAAI;AAAA,WAAc,KAAK,MAAM,IAAI;AACzE,OAAK,QAAQ,CAAC,GAAG,MAAM;AACrB,UAAM,EAAE,OAAO,MAAM,IAAI;AACzB,UAAM;AAAA,MACJ;AAAA,EAAK,IAAI,CAAC,KAAK,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI,MAAM,OAAO,YAAY,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC3F;AAIA,UAAM,UAAU,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAC5D,UAAM,KAAK,YAAY,SAAS,KAAK,CAAC;AACtC,QAAI,MAAM,KAAK,MAAM,IAAI,EAAE,SAAS,GAAG;AACrC,YAAM;AAAA,QACJ,aAAQ,MAAM,KAAK,MAAM,IAAI,EAAE,SAAS,CAAC,gCAA2B,MAAM,IAAI,IAAI,MAAM,SAAS;AAAA,MACnG;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,MAAc,QAAwB;AACzD,SAAO,KACJ,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,SAAS,CAAC,EACrB,KAAK,IAAI;AACd;AAGA,eAAsB,kCACpB,UACA,SACA,OAA8E,CAAC,GAChD;AAC/B,MAAI,MAAM,gBAAgB,SAAS,EAAE,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,CAAC,GAAG;AAClF,UAAM,2BAA2B,UAAU,EAAE,GAAG,MAAM,MAAM,QAAQ,CAAC;AACrE,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACA,SAAO,EAAE,SAAS,MAAM;AAC1B;","names":[]}
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
3
3
  import {
4
4
  sanitizeName,
5
5
  sessionsDir
6
- } from "./chunk-E7TAHQ4A.js";
6
+ } from "./chunk-RRXUIPWG.js";
7
7
 
8
8
  // src/code/checkpoints.ts
9
9
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -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-2AASOSD5.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"]}
@@ -44,11 +44,16 @@ function canonicalPresetName(name) {
44
44
  if (name === "auto" || name === "flash" || name === "pro") return name;
45
45
  return "auto";
46
46
  }
47
+ function presetNameForSettings(settings) {
48
+ if (settings.model === "deepseek-v4-pro") return "pro";
49
+ return settings.autoEscalate ? "auto" : "flash";
50
+ }
47
51
 
48
52
  export {
49
53
  PRESETS,
50
54
  PRESET_DESCRIPTIONS,
51
55
  resolvePreset,
52
- canonicalPresetName
56
+ canonicalPresetName,
57
+ presetNameForSettings
53
58
  };
54
- //# sourceMappingURL=chunk-2425HK6U.js.map
59
+ //# sourceMappingURL=chunk-LGEKVMMV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/ui/presets.ts"],"sourcesContent":["import type { PresetName } from \"../../config.js\";\n\nexport interface PresetSettings {\n model: string;\n reasoningEffort: \"high\" | \"max\";\n autoEscalate: boolean;\n}\n\n/** Old names `fast`/`smart`/`max` aliased via `resolvePreset` so legacy configs still load. */\nexport const PRESETS: Record<\"auto\" | \"flash\" | \"pro\", PresetSettings> = {\n auto: {\n model: \"deepseek-v4-flash\",\n reasoningEffort: \"max\",\n autoEscalate: true,\n },\n flash: {\n model: \"deepseek-v4-flash\",\n reasoningEffort: \"max\",\n autoEscalate: false,\n },\n pro: {\n model: \"deepseek-v4-pro\",\n reasoningEffort: \"max\",\n autoEscalate: false,\n },\n};\n\nexport const PRESET_DESCRIPTIONS: Record<\n \"auto\" | \"flash\" | \"pro\",\n { headline: string; cost: string }\n> = {\n auto: {\n headline: \"flash → pro on hard turns\",\n cost: \"default · ~96% turns stay on flash · pro kicks in only when needed\",\n },\n flash: {\n headline: \"v4-flash always\",\n cost: \"cheapest · predictable · /pro still works for a one-turn bump\",\n },\n pro: {\n headline: \"v4-pro always\",\n cost: \"~3× flash (5/31 discount) / ~12× full price · for hard multi-turn work\",\n },\n};\n\n/** Legacy aliases: fast→flash+high, smart→auto, max→pro. Unknown names fall through to auto. */\nexport function resolvePreset(name: PresetName | undefined): PresetSettings {\n if (name === \"auto\" || name === \"flash\" || name === \"pro\") return PRESETS[name];\n if (name === \"fast\") return { ...PRESETS.flash, reasoningEffort: \"high\" };\n if (name === \"smart\") return PRESETS.auto;\n if (name === \"max\") return PRESETS.pro;\n return PRESETS.auto;\n}\n\n/** Canonical name for storage / display — unknown values become auto. */\nexport function canonicalPresetName(name: PresetName | undefined): \"auto\" | \"flash\" | \"pro\" {\n if (name === \"auto\" || name === \"flash\" || name === \"pro\") return name;\n return \"auto\";\n}\n"],"mappings":";;;;AASO,IAAM,UAA4D;AAAA,EACvE,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AAAA,EACA,KAAK;AAAA,IACH,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AACF;AAEO,IAAM,sBAGT;AAAA,EACF,MAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AAAA,EACA,KAAK;AAAA,IACH,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACF;AAGO,SAAS,cAAc,MAA8C;AAC1E,MAAI,SAAS,UAAU,SAAS,WAAW,SAAS,MAAO,QAAO,QAAQ,IAAI;AAC9E,MAAI,SAAS,OAAQ,QAAO,EAAE,GAAG,QAAQ,OAAO,iBAAiB,OAAO;AACxE,MAAI,SAAS,QAAS,QAAO,QAAQ;AACrC,MAAI,SAAS,MAAO,QAAO,QAAQ;AACnC,SAAO,QAAQ;AACjB;AAGO,SAAS,oBAAoB,MAAwD;AAC1F,MAAI,SAAS,UAAU,SAAS,WAAW,SAAS,MAAO,QAAO;AAClE,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/cli/ui/presets.ts"],"sourcesContent":["import type { PresetName } from \"../../config.js\";\n\nexport interface PresetSettings {\n model: string;\n reasoningEffort: \"high\" | \"max\";\n autoEscalate: boolean;\n}\n\n/** Old names `fast`/`smart`/`max` aliased via `resolvePreset` so legacy configs still load. */\nexport const PRESETS: Record<\"auto\" | \"flash\" | \"pro\", PresetSettings> = {\n auto: {\n model: \"deepseek-v4-flash\",\n reasoningEffort: \"max\",\n autoEscalate: true,\n },\n flash: {\n model: \"deepseek-v4-flash\",\n reasoningEffort: \"max\",\n autoEscalate: false,\n },\n pro: {\n model: \"deepseek-v4-pro\",\n reasoningEffort: \"max\",\n autoEscalate: false,\n },\n};\n\nexport const PRESET_DESCRIPTIONS: Record<\n \"auto\" | \"flash\" | \"pro\",\n { headline: string; cost: string }\n> = {\n auto: {\n headline: \"flash → pro on hard turns\",\n cost: \"default · ~96% turns stay on flash · pro kicks in only when needed\",\n },\n flash: {\n headline: \"v4-flash always\",\n cost: \"cheapest · predictable · /pro still works for a one-turn bump\",\n },\n pro: {\n headline: \"v4-pro always\",\n cost: \"~3× flash (5/31 discount) / ~12× full price · for hard multi-turn work\",\n },\n};\n\n/** Legacy aliases: fast→flash+high, smart→auto, max→pro. Unknown names fall through to auto. */\nexport function resolvePreset(name: PresetName | undefined): PresetSettings {\n if (name === \"auto\" || name === \"flash\" || name === \"pro\") return PRESETS[name];\n if (name === \"fast\") return { ...PRESETS.flash, reasoningEffort: \"high\" };\n if (name === \"smart\") return PRESETS.auto;\n if (name === \"max\") return PRESETS.pro;\n return PRESETS.auto;\n}\n\n/** Canonical name for storage / display — unknown values become auto. */\nexport function canonicalPresetName(name: PresetName | undefined): \"auto\" | \"flash\" | \"pro\" {\n if (name === \"auto\" || name === \"flash\" || name === \"pro\") return name;\n return \"auto\";\n}\n\nexport function presetNameForSettings(settings: PresetSettings): \"auto\" | \"flash\" | \"pro\" {\n if (settings.model === \"deepseek-v4-pro\") return \"pro\";\n return settings.autoEscalate ? \"auto\" : \"flash\";\n}\n"],"mappings":";;;;AASO,IAAM,UAA4D;AAAA,EACvE,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AAAA,EACA,KAAK;AAAA,IACH,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AACF;AAEO,IAAM,sBAGT;AAAA,EACF,MAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AAAA,EACA,KAAK;AAAA,IACH,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACF;AAGO,SAAS,cAAc,MAA8C;AAC1E,MAAI,SAAS,UAAU,SAAS,WAAW,SAAS,MAAO,QAAO,QAAQ,IAAI;AAC9E,MAAI,SAAS,OAAQ,QAAO,EAAE,GAAG,QAAQ,OAAO,iBAAiB,OAAO;AACxE,MAAI,SAAS,QAAS,QAAO,QAAQ;AACrC,MAAI,SAAS,MAAO,QAAO,QAAQ;AACnC,SAAO,QAAQ;AACjB;AAGO,SAAS,oBAAoB,MAAwD;AAC1F,MAAI,SAAS,UAAU,SAAS,WAAW,SAAS,MAAO,QAAO;AAClE,SAAO;AACT;AAEO,SAAS,sBAAsB,UAAoD;AACxF,MAAI,SAAS,UAAU,kBAAmB,QAAO;AACjD,SAAO,SAAS,eAAe,SAAS;AAC1C;","names":[]}