reasonix 0.43.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +49 -11
  2. package/README.zh-CN.md +35 -7
  3. package/dashboard/app.css +225 -4
  4. package/dashboard/dist/app.js +6441 -6080
  5. package/dashboard/dist/app.js.map +1 -1
  6. package/data/deepseek-tokenizer.json.gz +0 -0
  7. package/dist/cli/{acp-DAGPCVFZ.js → acp-TYZ2CTDL.js} +28 -30
  8. package/dist/cli/acp-TYZ2CTDL.js.map +1 -0
  9. package/dist/cli/chat-TH7VNNCJ.js +51 -0
  10. package/dist/cli/chunk-2425HK6U.js +0 -0
  11. package/dist/cli/chunk-25T6CVUP.js +0 -0
  12. package/dist/cli/chunk-2UQP6H6T.js +0 -0
  13. package/dist/cli/{chunk-3Z6IBU3D.js → chunk-2V6EAEUW.js} +95 -31
  14. package/dist/cli/chunk-2V6EAEUW.js.map +1 -0
  15. package/dist/cli/{chunk-XCGGEJTI.js → chunk-4CTDEJUF.js} +2 -2
  16. package/dist/cli/chunk-4QUNBQQ2.js +0 -0
  17. package/dist/cli/{chunk-74EX7SUH.js → chunk-5QCB62C4.js} +33 -7
  18. package/dist/cli/{chunk-74EX7SUH.js.map → chunk-5QCB62C4.js.map} +1 -1
  19. package/dist/cli/chunk-6OWJV3YW.js +390 -0
  20. package/dist/cli/chunk-6OWJV3YW.js.map +1 -0
  21. package/dist/cli/chunk-6PBZN4VI.js +0 -0
  22. package/dist/cli/{chunk-7O5ALB4C.js → chunk-7CIGMZT3.js} +2 -2
  23. package/dist/cli/{chunk-H6PS7IUE.js → chunk-7UCMM425.js} +7 -3
  24. package/dist/cli/chunk-7UCMM425.js.map +1 -0
  25. package/dist/cli/{chunk-TJX6BFZZ.js → chunk-AB2RED3C.js} +3 -3
  26. package/dist/cli/{chunk-XPDVG52A.js → chunk-AVFXO2EZ.js} +361 -13
  27. package/dist/cli/chunk-AVFXO2EZ.js.map +1 -0
  28. package/dist/cli/{chunk-FHOGSSCH.js → chunk-C53JQES5.js} +3 -3
  29. package/dist/cli/{chunk-RE4RAVFF.js → chunk-CGDR2ELH.js} +92 -30
  30. package/dist/cli/chunk-CGDR2ELH.js.map +1 -0
  31. package/dist/cli/{chunk-OSZC7C6F.js → chunk-CWZKQ5FE.js} +7 -4
  32. package/dist/cli/chunk-CWZKQ5FE.js.map +1 -0
  33. package/dist/cli/{devtools-YECO25QO.js → chunk-FEZK652I.js} +10 -85
  34. package/dist/cli/chunk-FEZK652I.js.map +1 -0
  35. package/dist/cli/{chunk-45U62RI3.js → chunk-HNXDZGC6.js} +104 -2
  36. package/dist/cli/chunk-HNXDZGC6.js.map +1 -0
  37. package/dist/cli/chunk-J5XJHLWM.js +0 -0
  38. package/dist/cli/chunk-JMBMLOBP.js +0 -0
  39. package/dist/cli/{chunk-5JJRUIPA.js → chunk-JNAQYELD.js} +16 -8
  40. package/dist/cli/{chunk-5JJRUIPA.js.map → chunk-JNAQYELD.js.map} +1 -1
  41. package/dist/cli/{chunk-YFGF5NKA.js → chunk-KGBG6M2X.js} +19 -15
  42. package/dist/cli/chunk-KGBG6M2X.js.map +1 -0
  43. package/dist/cli/{chunk-3BXRZFWS.js → chunk-KLQTAZIY.js} +12 -4
  44. package/dist/cli/chunk-KLQTAZIY.js.map +1 -0
  45. package/dist/cli/{chunk-VK5HG73G.js → chunk-KM465GST.js} +9 -9
  46. package/dist/cli/{chunk-DOYHN4KB.js → chunk-LIR2HBQH.js} +2 -2
  47. package/dist/cli/{chunk-YYQAUTTN.js → chunk-MJ6W5UN3.js} +2 -2
  48. package/dist/cli/{chunk-6PZ3CXBP.js → chunk-MRHHQJAQ.js} +5 -4
  49. package/dist/cli/chunk-MRHHQJAQ.js.map +1 -0
  50. package/dist/cli/{chunk-PQXPXJBJ.js → chunk-NVURFF27.js} +16 -5
  51. package/dist/cli/chunk-NVURFF27.js.map +1 -0
  52. package/dist/cli/{chunk-2R4QCDOZ.js → chunk-OPFUUYHL.js} +540 -287
  53. package/dist/cli/chunk-OPFUUYHL.js.map +1 -0
  54. package/dist/cli/chunk-PLHAZOLZ.js +0 -0
  55. package/dist/cli/{chunk-HFEAY5DT.js → chunk-R3CTO2HM.js} +2 -2
  56. package/dist/cli/{chunk-O52OLQL3.js → chunk-RDRC3XDT.js} +136 -38
  57. package/dist/cli/chunk-RDRC3XDT.js.map +1 -0
  58. package/dist/cli/chunk-S4XVGLRW.js +0 -0
  59. package/dist/cli/chunk-SZ5XES2N.js +0 -0
  60. package/dist/cli/{chunk-2K65GZBT.js → chunk-TEUDEGX2.js} +64 -19
  61. package/dist/cli/chunk-TEUDEGX2.js.map +1 -0
  62. package/dist/cli/{chunk-2Z35JOA4.js → chunk-TKVXTQ3T.js} +4 -4
  63. package/dist/cli/{chunk-2Z35JOA4.js.map → chunk-TKVXTQ3T.js.map} +1 -1
  64. package/dist/cli/chunk-TUK7OWJA.js +0 -0
  65. package/dist/cli/{chunk-32TIKD5U.js → chunk-TXJMRPIL.js} +3 -3
  66. package/dist/cli/{chunk-2KDUS647.js → chunk-V26WPN3J.js} +7 -4
  67. package/dist/cli/chunk-V26WPN3J.js.map +1 -0
  68. package/dist/cli/{chunk-F3PXYSNN.js → chunk-WK3UFQY3.js} +2 -2
  69. package/dist/cli/{chunk-6G3CUUFG.js → chunk-X53B3JIX.js} +3 -3
  70. package/dist/cli/{chunk-6G3CUUFG.js.map → chunk-X53B3JIX.js.map} +1 -1
  71. package/dist/cli/chunk-XJXDHAES.js +0 -0
  72. package/dist/cli/{chunk-6AK4EY3D.js → chunk-XSU4QVFW.js} +1 -81
  73. package/dist/cli/chunk-XSU4QVFW.js.map +1 -0
  74. package/dist/cli/chunk-XXC2BYTV.js +0 -0
  75. package/dist/cli/{chunk-P7EKE5ZQ.js → chunk-Z4S7EYXG.js} +4482 -1310
  76. package/dist/cli/chunk-Z4S7EYXG.js.map +1 -0
  77. package/dist/cli/chunk-ZZM6QJ4W.js +0 -0
  78. package/dist/cli/{chunk-YQ6NTIIE.js → chunk-ZZYBBX5N.js} +13 -5
  79. package/dist/cli/chunk-ZZYBBX5N.js.map +1 -0
  80. package/dist/cli/{code-SMKEW6CD.js → code-PSVJ3KEN.js} +48 -36
  81. package/dist/cli/code-PSVJ3KEN.js.map +1 -0
  82. package/dist/cli/{commands-FVVB5FZF.js → commands-OCU42XG4.js} +4 -4
  83. package/dist/cli/{commit-HE4VSPZ7.js → commit-XCQIQCYG.js} +3 -3
  84. package/dist/cli/{desktop-Q7NDXCON.js → desktop-KWGR4BNE.js} +210 -69
  85. package/dist/cli/desktop-KWGR4BNE.js.map +1 -0
  86. package/dist/cli/devtools-HW3WDT3Q.js +91 -0
  87. package/dist/cli/devtools-HW3WDT3Q.js.map +1 -0
  88. package/dist/cli/{diff-435UTPC5.js → diff-NHANTNC3.js} +9 -9
  89. package/dist/cli/{doctor-OT7KH75K.js → doctor-CC5CLOGG.js} +10 -10
  90. package/dist/cli/events-XEFAD5VX.js +0 -0
  91. package/dist/cli/index.js +132 -94
  92. package/dist/cli/index.js.map +1 -1
  93. package/dist/cli/{mcp-WUL2WO75.js → mcp-MPVGBBJF.js} +2 -2
  94. package/dist/cli/{mcp-browse-RR7R4XET.js → mcp-browse-4XOTC3FJ.js} +3 -3
  95. package/dist/cli/{mcp-inspect-REGLYBWT.js → mcp-inspect-CEMGKKAH.js} +14 -9
  96. package/dist/cli/mcp-inspect-CEMGKKAH.js.map +1 -0
  97. package/dist/cli/{prompt-UW6EFLVR.js → prompt-2D7ID24X.js} +4 -4
  98. package/dist/cli/prune-sessions-3RWUBYRS.js +0 -0
  99. package/dist/cli/{replay-YOURXV4C.js → replay-SR44E6RS.js} +10 -10
  100. package/dist/cli/{run-Q6BUXV66.js → run-MDGL27WL.js} +35 -36
  101. package/dist/cli/run-MDGL27WL.js.map +1 -0
  102. package/dist/cli/{server-XGDBRWMB.js → server-27ARQXIZ.js} +67 -24
  103. package/dist/cli/server-27ARQXIZ.js.map +1 -0
  104. package/dist/cli/{sessions-FH7QVYSY.js → sessions-CKQXCYGP.js} +18 -18
  105. package/dist/cli/sessions-CKQXCYGP.js.map +1 -0
  106. package/dist/cli/{setup-VDS6SVEP.js → setup-TPAGSVXO.js} +6 -6
  107. package/dist/cli/{stats-MQVI2XQH.js → stats-DPUBZNVX.js} +6 -4
  108. package/dist/cli/update-6ITLPRDV.js +0 -0
  109. package/dist/cli/{version-DAHGZY5N.js → version-2X3BHVVK.js} +15 -15
  110. package/dist/index.d.ts +181 -53
  111. package/dist/index.js +1322 -533
  112. package/dist/index.js.map +1 -1
  113. package/package.json +21 -8
  114. package/dist/cli/.-3G6VX5S7.js +0 -327
  115. package/dist/cli/.-6YRPB2C7.js +0 -329
  116. package/dist/cli/.-EYSVINK3.js +0 -317
  117. package/dist/cli/acp-DAGPCVFZ.js.map +0 -1
  118. package/dist/cli/chat-7ES4IBNH.js +0 -50
  119. package/dist/cli/chunk-2K65GZBT.js.map +0 -1
  120. package/dist/cli/chunk-2KDUS647.js.map +0 -1
  121. package/dist/cli/chunk-2R4QCDOZ.js.map +0 -1
  122. package/dist/cli/chunk-3BXRZFWS.js.map +0 -1
  123. package/dist/cli/chunk-3Z6IBU3D.js.map +0 -1
  124. package/dist/cli/chunk-45U62RI3.js.map +0 -1
  125. package/dist/cli/chunk-6AK4EY3D.js.map +0 -1
  126. package/dist/cli/chunk-6PZ3CXBP.js.map +0 -1
  127. package/dist/cli/chunk-H6PS7IUE.js.map +0 -1
  128. package/dist/cli/chunk-O52OLQL3.js.map +0 -1
  129. package/dist/cli/chunk-OSZC7C6F.js.map +0 -1
  130. package/dist/cli/chunk-P7EKE5ZQ.js.map +0 -1
  131. package/dist/cli/chunk-PQXPXJBJ.js.map +0 -1
  132. package/dist/cli/chunk-PV55UMTO.js +0 -200
  133. package/dist/cli/chunk-PV55UMTO.js.map +0 -1
  134. package/dist/cli/chunk-RE4RAVFF.js.map +0 -1
  135. package/dist/cli/chunk-XPDVG52A.js.map +0 -1
  136. package/dist/cli/chunk-YFGF5NKA.js.map +0 -1
  137. package/dist/cli/chunk-YQ6NTIIE.js.map +0 -1
  138. package/dist/cli/code-SMKEW6CD.js.map +0 -1
  139. package/dist/cli/desktop-Q7NDXCON.js.map +0 -1
  140. package/dist/cli/devtools-YECO25QO.js.map +0 -1
  141. package/dist/cli/doctor-OT7KH75K.js.map +0 -1
  142. package/dist/cli/mcp-inspect-REGLYBWT.js.map +0 -1
  143. package/dist/cli/prompt-UW6EFLVR.js.map +0 -1
  144. package/dist/cli/run-Q6BUXV66.js.map +0 -1
  145. package/dist/cli/server-XGDBRWMB.js.map +0 -1
  146. package/dist/cli/sessions-FH7QVYSY.js.map +0 -1
  147. package/dist/cli/stats-MQVI2XQH.js.map +0 -1
  148. /package/dist/cli/{.-3G6VX5S7.js.map → chat-TH7VNNCJ.js.map} +0 -0
  149. /package/dist/cli/{chunk-XCGGEJTI.js.map → chunk-4CTDEJUF.js.map} +0 -0
  150. /package/dist/cli/{chunk-7O5ALB4C.js.map → chunk-7CIGMZT3.js.map} +0 -0
  151. /package/dist/cli/{chunk-TJX6BFZZ.js.map → chunk-AB2RED3C.js.map} +0 -0
  152. /package/dist/cli/{chunk-FHOGSSCH.js.map → chunk-C53JQES5.js.map} +0 -0
  153. /package/dist/cli/{chunk-VK5HG73G.js.map → chunk-KM465GST.js.map} +0 -0
  154. /package/dist/cli/{chunk-DOYHN4KB.js.map → chunk-LIR2HBQH.js.map} +0 -0
  155. /package/dist/cli/{chunk-YYQAUTTN.js.map → chunk-MJ6W5UN3.js.map} +0 -0
  156. /package/dist/cli/{chunk-HFEAY5DT.js.map → chunk-R3CTO2HM.js.map} +0 -0
  157. /package/dist/cli/{chunk-32TIKD5U.js.map → chunk-TXJMRPIL.js.map} +0 -0
  158. /package/dist/cli/{chunk-F3PXYSNN.js.map → chunk-WK3UFQY3.js.map} +0 -0
  159. /package/dist/cli/{commands-FVVB5FZF.js.map → commands-OCU42XG4.js.map} +0 -0
  160. /package/dist/cli/{commit-HE4VSPZ7.js.map → commit-XCQIQCYG.js.map} +0 -0
  161. /package/dist/cli/{diff-435UTPC5.js.map → diff-NHANTNC3.js.map} +0 -0
  162. /package/dist/cli/{.-6YRPB2C7.js.map → doctor-CC5CLOGG.js.map} +0 -0
  163. /package/dist/cli/{mcp-WUL2WO75.js.map → mcp-MPVGBBJF.js.map} +0 -0
  164. /package/dist/cli/{mcp-browse-RR7R4XET.js.map → mcp-browse-4XOTC3FJ.js.map} +0 -0
  165. /package/dist/cli/{.-EYSVINK3.js.map → prompt-2D7ID24X.js.map} +0 -0
  166. /package/dist/cli/{replay-YOURXV4C.js.map → replay-SR44E6RS.js.map} +0 -0
  167. /package/dist/cli/{setup-VDS6SVEP.js.map → setup-TPAGSVXO.js.map} +0 -0
  168. /package/dist/cli/{chat-7ES4IBNH.js.map → stats-DPUBZNVX.js.map} +0 -0
  169. /package/dist/cli/{version-DAHGZY5N.js.map → version-2X3BHVVK.js.map} +0 -0
@@ -1 +0,0 @@
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 } from \"../tools/plan.js\";\n\nexport interface PlanStateOnDisk {\n /** File format version — bump when shape changes. */\n version: 1;\n steps: PlanStep[];\n completedStepIds: string[];\n /** ISO8601 timestamp of the last write. */\n updatedAt: string;\n body?: string;\n summary?: string;\n}\n\nexport function planStatePath(sessionName: string): string {\n return join(sessionsDir(), `${sanitizeName(sessionName)}.plan.json`);\n}\n\nexport function loadPlanState(sessionName: string): PlanStateOnDisk | null {\n const path = planStatePath(sessionName);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<PlanStateOnDisk>;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (parsed.version !== 1) return null;\n if (!Array.isArray(parsed.steps)) return null;\n if (!Array.isArray(parsed.completedStepIds)) return null;\n if (typeof parsed.updatedAt !== \"string\") return null;\n // Defensive: filter out any malformed step entries so a partially\n // corrupted file still yields a usable subset.\n const steps: PlanStep[] = [];\n for (const s of parsed.steps) {\n if (!s || typeof s !== \"object\") continue;\n const e = s as unknown as Record<string, unknown>;\n if (typeof e.id !== \"string\" || !e.id) continue;\n if (typeof e.title !== \"string\" || !e.title) continue;\n if (typeof e.action !== \"string\" || !e.action) continue;\n const step: PlanStep = { id: e.id, title: e.title, action: e.action };\n if (e.risk === \"low\" || e.risk === \"med\" || e.risk === \"high\") step.risk = e.risk;\n steps.push(step);\n }\n if (steps.length === 0) return null;\n const completedStepIds = parsed.completedStepIds.filter(\n (id): id is string => typeof id === \"string\" && id.length > 0,\n );\n const out: PlanStateOnDisk = {\n version: 1,\n steps,\n completedStepIds,\n updatedAt: parsed.updatedAt,\n };\n if (typeof parsed.body === \"string\" && parsed.body) out.body = parsed.body;\n if (typeof parsed.summary === \"string\" && parsed.summary) out.summary = parsed.summary;\n return out;\n } catch {\n return null;\n }\n}\n\n/** Best-effort: write failure logs to stderr instead of crashing the TUI. */\nexport function savePlanState(\n sessionName: string,\n steps: PlanStep[],\n completedStepIds: Iterable<string>,\n extras?: { body?: string; summary?: string },\n): void {\n const path = planStatePath(sessionName);\n try {\n mkdirSync(dirname(path), { recursive: true });\n const state: PlanStateOnDisk = {\n version: 1,\n steps,\n completedStepIds: [...completedStepIds],\n updatedAt: new Date().toISOString(),\n };\n if (extras?.body) state.body = extras.body;\n if (extras?.summary) state.summary = extras.summary;\n writeFileSync(path, `${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n } catch (err) {\n process.stderr.write(\n `▸ plan-store: failed to save plan for \"${sessionName}\": ${(err as Error).message}\\n`,\n );\n }\n}\n\n/** Remove the persisted plan, if any. Used on cancel / clean reset. */\nexport function clearPlanState(sessionName: string): void {\n const path = planStatePath(sessionName);\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n /* nothing to do — leftover file is harmless, will be overwritten next save */\n }\n}\n\n/** Random suffix avoids same-millisecond collision; `:`/`.` swapped for Windows-safe filenames. */\nexport function archivePlanState(sessionName: string): string | null {\n const active = planStatePath(sessionName);\n if (!existsSync(active)) return null;\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const suffix = Math.random().toString(36).slice(2, 6);\n const archive = join(\n sessionsDir(),\n `${sanitizeName(sessionName)}.plan.${stamp}-${suffix}.done.json`,\n );\n try {\n renameSync(active, archive);\n return archive;\n } catch (err) {\n process.stderr.write(\n `▸ plan-store: failed to archive plan for \"${sessionName}\": ${(err as Error).message}\\n`,\n );\n return null;\n }\n}\n\nexport interface PlanArchiveSummary {\n path: string;\n completedAt: string;\n steps: PlanStep[];\n completedStepIds: string[];\n /** Markdown body, when the archive carried it. */\n body?: string;\n /** One-line human-friendly title, when supplied. */\n summary?: string;\n}\n\nexport function listPlanArchives(sessionName: string): PlanArchiveSummary[] {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n const prefix = `${sanitizeName(sessionName)}.plan.`;\n const suffix = \".done.json\";\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n const summaries: PlanArchiveSummary[] = [];\n for (const name of entries) {\n if (!name.startsWith(prefix) || !name.endsWith(suffix)) continue;\n const full = join(dir, name);\n try {\n const raw = readFileSync(full, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<PlanStateOnDisk>;\n if (parsed.version !== 1) continue;\n if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) continue;\n const steps = parsed.steps.filter(\n (s): s is PlanStep =>\n !!s &&\n typeof s === \"object\" &&\n typeof (s as PlanStep).id === \"string\" &&\n typeof (s as PlanStep).title === \"string\" &&\n typeof (s as PlanStep).action === \"string\",\n );\n if (steps.length === 0) continue;\n const completedStepIds = Array.isArray(parsed.completedStepIds)\n ? parsed.completedStepIds.filter((id): id is string => typeof id === \"string\" && !!id)\n : [];\n // Prefer the file's own updatedAt; fall back to mtime if missing\n // or unparseable so a hand-edited archive still sorts sensibly.\n let completedAt = typeof parsed.updatedAt === \"string\" ? parsed.updatedAt : \"\";\n if (!completedAt || Number.isNaN(Date.parse(completedAt))) {\n try {\n completedAt = statSync(full).mtime.toISOString();\n } catch {\n completedAt = new Date(0).toISOString();\n }\n }\n const entry: PlanArchiveSummary = { path: full, completedAt, steps, completedStepIds };\n if (typeof parsed.body === \"string\" && parsed.body) entry.body = parsed.body;\n if (typeof parsed.summary === \"string\" && parsed.summary) entry.summary = parsed.summary;\n summaries.push(entry);\n } catch {\n // Skip the corrupt archive entirely.\n }\n }\n summaries.sort((a, b) => b.completedAt.localeCompare(a.completedAt));\n return summaries;\n}\n\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) 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 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\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: \"mcp\", group: \"extend\", summary: \"list MCP servers + tools attached to this session\" },\n {\n cmd: \"resource\",\n group: \"extend\",\n argsHint: \"[uri]\",\n summary: \"browse + read MCP resources (no arg → list URIs; <uri> → fetch contents)\",\n argCompleter: \"mcp-resources\",\n },\n {\n cmd: \"prompt\",\n group: \"extend\",\n argsHint: \"[name]\",\n summary: \"browse + fetch MCP prompts (no arg → list names; <name> → render prompt)\",\n argCompleter: \"mcp-prompts\",\n },\n {\n cmd: \"memory\",\n group: \"extend\",\n argsHint: \"[list|show <name>|forget <name>|clear <scope> confirm]\",\n summary: \"show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)\",\n },\n {\n cmd: \"skill\",\n group: \"extend\",\n argsHint: \"[list|show <name>|new <name>|<name> [args]]\",\n summary: \"list / run / scaffold user skills (<project>/.reasonix/skills + ~/.reasonix/skills)\",\n argCompleter: \"skills\",\n },\n\n {\n cmd: \"init\",\n group: \"code\",\n argsHint: \"[force]\",\n summary:\n \"scan the project and synthesize a baseline REASONIX.md (model writes; review with /apply). `force` overwrites an existing file.\",\n contextual: \"code\",\n argCompleter: [\"force\"],\n },\n {\n cmd: \"apply\",\n group: \"code\",\n argsHint: \"[N|N,M|N-M]\",\n summary:\n \"commit pending edit blocks to disk (no arg → all; `1`, `1,3`, or `1-4` → that subset, rest stay pending)\",\n contextual: \"code\",\n },\n {\n cmd: \"discard\",\n group: \"code\",\n argsHint: \"[N|N,M|N-M]\",\n summary: \"drop pending edit blocks without writing (no arg → all; indices → that subset)\",\n contextual: \"code\",\n },\n {\n cmd: \"walk\",\n group: \"code\",\n summary:\n \"step through pending edits one block at a time (git-add-p style: y/n per block, a apply rest, A flip AUTO)\",\n contextual: \"code\",\n },\n {\n cmd: \"undo\",\n group: \"code\",\n summary: \"roll back the last applied edit batch\",\n contextual: \"code\",\n },\n {\n cmd: \"history\",\n group: \"code\",\n summary: \"list every edit batch this session (ids for /show, undone markers)\",\n contextual: \"code\",\n },\n {\n cmd: \"show\",\n group: \"code\",\n argsHint: \"[id]\",\n summary: \"dump a stored edit diff (omit id for newest non-undone)\",\n contextual: \"code\",\n },\n {\n cmd: \"commit\",\n group: \"code\",\n argsHint: '\"msg\"',\n summary: \"git add -A && git commit -m ...\",\n contextual: \"code\",\n },\n {\n cmd: \"mode\",\n group: \"code\",\n argsHint: \"[review|auto|yolo]\",\n summary:\n \"edit-gate: review (queue) · auto (apply+undo) · yolo (apply+auto-shell). Shift+Tab cycles.\",\n contextual: \"code\",\n argCompleter: [\"review\", \"auto\", \"yolo\"],\n },\n {\n cmd: \"plan\",\n group: \"code\",\n argsHint: \"[on|off]\",\n summary: \"toggle read-only plan mode (writes bounced until submit_plan + approval)\",\n contextual: \"code\",\n argCompleter: [\"on\", \"off\"],\n },\n {\n cmd: \"checkpoint\",\n group: \"code\",\n argsHint: \"[name|list|forget <id>]\",\n summary:\n \"snapshot every file the session has touched (Cursor-style internal store, not git). /checkpoint alone lists.\",\n contextual: \"code\",\n argCompleter: [\"list\", \"forget\"],\n },\n {\n cmd: \"restore\",\n group: \"code\",\n argsHint: \"<name|id>\",\n summary: \"roll back files to a named checkpoint (see /checkpoint list)\",\n contextual: \"code\",\n },\n {\n cmd: \"cwd\",\n group: \"code\",\n argsHint: \"<path>\",\n summary:\n \"switch the workspace root mid-session — re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker\",\n contextual: \"code\",\n aliases: [\"sandbox\"],\n },\n\n {\n cmd: \"jobs\",\n group: \"jobs\",\n summary: \"list background jobs started by run_background\",\n contextual: \"code\",\n },\n {\n cmd: \"kill\",\n group: \"jobs\",\n argsHint: \"<id>\",\n summary: \"stop a background job by id (SIGTERM → SIGKILL after grace)\",\n contextual: \"code\",\n },\n {\n cmd: \"logs\",\n group: \"jobs\",\n argsHint: \"<id> [lines]\",\n summary: \"tail a background job's output (default last 80 lines)\",\n contextual: \"code\",\n },\n\n {\n cmd: \"pro\",\n group: \"advanced\",\n argsHint: \"[off]\",\n summary: \"arm v4-pro for the NEXT turn only (one-shot · auto-disarms after turn)\",\n argCompleter: [\"off\"],\n },\n {\n cmd: \"budget\",\n group: \"advanced\",\n argsHint: \"[usd|off]\",\n summary:\n \"session USD cap — warns at 80%, refuses next turn at 100%. Off by default. /budget alone shows status\",\n argCompleter: [\"off\", \"1\", \"5\", \"10\", \"20\", \"50\"],\n },\n {\n cmd: \"search-engine\",\n group: \"advanced\",\n argsHint: \"<mojeek|searxng> [<endpoint>]\",\n summary: \"switch web search backend — mojeek (default, no deps) or searxng (self-hosted)\",\n argCompleter: [\"mojeek\", \"searxng\"],\n aliases: [\"se\"],\n },\n {\n cmd: \"hooks\",\n group: \"advanced\",\n argsHint: \"[reload]\",\n summary: \"list active hooks (settings.json under .reasonix/) · reload re-reads from disk\",\n },\n {\n cmd: \"permissions\",\n group: \"advanced\",\n argsHint: \"[list|add <prefix>|remove <prefix|N>|clear confirm]\",\n summary:\n \"show / edit shell allowlist (builtin read-only · per-project: ~/.reasonix/config.json)\",\n argCompleter: [\"list\", \"add\", \"remove\", \"clear\"],\n },\n {\n cmd: \"dashboard\",\n group: \"advanced\",\n argsHint: \"[stop]\",\n summary: \"launch the embedded web dashboard (127.0.0.1, token-gated)\",\n argCompleter: [\"stop\"],\n },\n {\n cmd: \"loop\",\n group: \"advanced\",\n argsHint: \"<5s..6h> <prompt> · stop · (no args = status)\",\n summary: \"auto-resubmit <prompt> every <interval> until you type something / Esc / /loop stop\",\n },\n {\n cmd: \"plans\",\n group: \"advanced\",\n summary: \"list this session's active + archived plans, newest first\",\n },\n {\n cmd: \"replay\",\n group: \"advanced\",\n summary: \"load an archived plan as a read-only Time Travel snapshot (default: newest)\",\n argsHint: \"[N]\",\n },\n {\n cmd: \"update\",\n group: \"advanced\",\n summary: \"show current vs latest version + the shell command to upgrade\",\n },\n { cmd: \"exit\", group: \"advanced\", summary: \"quit the TUI\", aliases: [\"quit\", \"q\"] },\n];\n\nexport function suggestSlashCommands(\n prefix: string,\n codeMode = false,\n counts?: Readonly<Record<string, number>>,\n): SlashCommandSpec[] {\n const p = prefix.toLowerCase();\n const matches = SLASH_COMMANDS.filter((c) => {\n // 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 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":";;;;;;;;AAEA,SAAS,YAAY,WAAW,cAA2B,QAAQ,qBAAqB;AACxF,SAAS,eAAe;AACxB,SAAS,SAAS,MAAM,UAAU,SAAS,WAAW;AA+BtD,SAAS,aAAa,SAAyB;AAC7C,SAAO,QAAQ,OAAO,EACnB,QAAQ,YAAY,GAAG,EACvB,QAAQ,OAAO,EAAE;AACtB;AAEA,SAAS,UAAU,SAAyB;AAC1C,SAAO,KAAK,QAAQ,GAAG,aAAa,YAAY,aAAa,OAAO,GAAG,aAAa;AACtF;AAEA,SAAS,UAAU,SAAyB;AAC1C,SAAO,KAAK,UAAU,OAAO,GAAG,YAAY;AAC9C;AAEA,SAAS,aAAa,SAAiB,IAAoB;AACzD,SAAO,KAAK,UAAU,OAAO,GAAG,GAAG,EAAE,OAAO;AAC9C;AAGO,SAAS,gBAAgB,SAAmC;AACjE,QAAM,OAAO,UAAU,OAAO;AAC9B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AAIpC,WAAO,OAAO;AAAA,MACZ,CAAC,MACC,OAAO,MAAM,YACb,MAAM,QACN,OAAO,EAAE,OAAO,YAChB,OAAO,EAAE,SAAS,YAClB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,WAAW,YACpB,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,UAAU;AAAA,IACvB;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,WAAW,SAAiB,OAA+B;AAClE,QAAM,OAAO,UAAU,OAAO;AAC9B,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,MAAM;AAC5D;AAGO,SAAS,eAAe,SAAiB,IAA+B;AAC7E,QAAM,OAAO,aAAa,SAAS,EAAE;AACrC,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,KAAK,GAAG;AACvE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUO,SAAS,iBAAiB,MAA+C;AAC9E,QAAM,UAAU,QAAQ,KAAK,OAAO;AACpC,QAAM,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAClF,QAAM,QAA0B,CAAC;AACjC,MAAI,QAAQ;AACZ,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,KAAK,OAAO;AAC1B,QAAI,KAAK,IAAI,CAAC,EAAG;AACjB,SAAK,IAAI,CAAC;AACV,UAAM,MAAM,QAAQ,SAAS,CAAC;AAI9B,QAAI,QAAQ,WAAW,CAAC,IAAI,WAAW,GAAG,OAAO,GAAG,GAAG,EAAE,EAAG;AAC5D,UAAM,MAAM,SAAS,SAAS,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AACtD,QAAI,WAAW,GAAG,GAAG;AACnB,UAAI;AACF,cAAM,UAAU,aAAa,KAAK,MAAM;AACxC,cAAM,KAAK,EAAE,MAAM,KAAK,QAAQ,CAAC;AACjC,iBAAS,QAAQ;AAAA,MACnB,QAAQ;AAGN,cAAM,KAAK,EAAE,MAAM,KAAK,SAAS,KAAK,CAAC;AAAA,MACzC;AAAA,IACF,OAAO;AACL,YAAM,KAAK,EAAE,MAAM,KAAK,SAAS,KAAK,CAAC;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,aAAyB;AAAA,IAC7B;AAAA,IACA,MAAM,KAAK;AAAA,IACX,SAAS;AAAA,IACT,WAAW,KAAK,IAAI;AAAA,IACpB,QAAQ,KAAK,UAAU;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AACA,QAAM,SAAS,aAAa,SAAS,EAAE;AACvC,YAAU,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,gBAAc,QAAQ,KAAK,UAAU,UAAU,GAAG,MAAM;AAExD,QAAM,OAAuB;AAAA,IAC3B;AAAA,IACA,MAAM,KAAK;AAAA,IACX,WAAW,WAAW;AAAA,IACtB,QAAQ,WAAW;AAAA,IACnB,WAAW,MAAM;AAAA,IACjB;AAAA,EACF;AACA,QAAM,QAAQ,gBAAgB,OAAO;AACrC,QAAM,KAAK,IAAI;AACf,aAAW,SAAS,KAAK;AACzB,SAAO;AACT;AAGO,SAAS,eAAe,SAAiB,UAAyC;AACvF,QAAM,QAAQ,gBAAgB,OAAO;AAErC,QAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAChD,MAAI,KAAM,QAAO;AACjB,QAAM,SAAS,CAAC,GAAG,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACnE,SAAO,UAAU;AACnB;AAYO,SAAS,kBAAkB,SAAiB,IAA2B;AAC5E,QAAM,KAAK,eAAe,SAAS,EAAE;AACrC,QAAM,UAAU,QAAQ,OAAO;AAC/B,QAAM,SAAwB,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AACvE,MAAI,CAAC,IAAI;AACP,WAAO,QAAQ,KAAK,EAAE,MAAM,gBAAgB,QAAQ,cAAc,EAAE,GAAG,CAAC;AACxE,WAAO;AAAA,EACT;AACA,aAAW,KAAK,GAAG,OAAO;AACxB,UAAM,MAAM,QAAQ,SAAS,EAAE,IAAI;AACnC,QAAI,QAAQ,WAAW,CAAC,IAAI,WAAW,GAAG,OAAO,GAAG,GAAG,EAAE,GAAG;AAC1D,aAAO,QAAQ,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,uBAAuB,CAAC;AACpE;AAAA,IACF;AACA,QAAI;AACF,UAAI,EAAE,YAAY,MAAM;AACtB,YAAI,WAAW,GAAG,GAAG;AACnB,iBAAO,GAAG;AACV,iBAAO,QAAQ,KAAK,EAAE,IAAI;AAAA,QAC5B;AAAA,MACF,OAAO;AACL,kBAAU,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3C,sBAAc,KAAK,EAAE,SAAS,MAAM;AACpC,eAAO,SAAS,KAAK,EAAE,IAAI;AAAA,MAC7B;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,QAAQ,KAAK,EAAE,MAAM,EAAE,MAAM,QAAS,IAAc,QAAQ,CAAC;AAAA,IACtE;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAiB,IAAqB;AACrE,QAAM,SAAS,aAAa,SAAS,EAAE;AACvC,MAAI,UAAU;AACd,MAAI,WAAW,MAAM,GAAG;AACtB,QAAI;AACF,aAAO,MAAM;AACb,gBAAU;AAAA,IACZ,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,QAAQ,gBAAgB,OAAO;AACrC,QAAM,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAC5C,MAAI,KAAK,WAAW,MAAM,QAAQ;AAChC,eAAW,SAAS,IAAI;AACxB,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAGO,SAAS,OAAO,IAAoB;AACzC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAAO,KAAK,IAAI,GAAG,MAAM,EAAE;AACjC,QAAM,IAAI,KAAK,MAAM,OAAO,GAAI;AAChC,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,SAAO,GAAG,CAAC;AACb;;;ACzPA;AAAA,EACE,cAAAA;AAAA,EACA,aAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,eAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAevB,SAAS,cAAc,aAA6B;AACzD,SAAOC,MAAK,YAAY,GAAG,GAAG,aAAa,WAAW,CAAC,YAAY;AACrE;AAEO,SAAS,cAAc,aAA6C;AACzE,QAAM,OAAO,cAAc,WAAW;AACtC,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,MAAM;AACrC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAI,OAAO,YAAY,EAAG,QAAO;AACjC,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,EAAG,QAAO;AACzC,QAAI,CAAC,MAAM,QAAQ,OAAO,gBAAgB,EAAG,QAAO;AACpD,QAAI,OAAO,OAAO,cAAc,SAAU,QAAO;AAGjD,UAAM,QAAoB,CAAC;AAC3B,eAAW,KAAK,OAAO,OAAO;AAC5B,UAAI,CAAC,KAAK,OAAO,MAAM,SAAU;AACjC,YAAM,IAAI;AACV,UAAI,OAAO,EAAE,OAAO,YAAY,CAAC,EAAE,GAAI;AACvC,UAAI,OAAO,EAAE,UAAU,YAAY,CAAC,EAAE,MAAO;AAC7C,UAAI,OAAO,EAAE,WAAW,YAAY,CAAC,EAAE,OAAQ;AAC/C,YAAM,OAAiB,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO;AACpE,UAAI,EAAE,SAAS,SAAS,EAAE,SAAS,SAAS,EAAE,SAAS,OAAQ,MAAK,OAAO,EAAE;AAC7E,YAAM,KAAK,IAAI;AAAA,IACjB;AACA,QAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,UAAM,mBAAmB,OAAO,iBAAiB;AAAA,MAC/C,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS;AAAA,IAC9D;AACA,UAAM,MAAuB;AAAA,MAC3B,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,WAAW,OAAO;AAAA,IACpB;AACA,QAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAM,KAAI,OAAO,OAAO;AACtE,QAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAS,KAAI,UAAU,OAAO;AAC/E,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,cACd,aACA,OACA,kBACA,QACM;AACN,QAAM,OAAO,cAAc,WAAW;AACtC,MAAI;AACF,IAAAC,WAAUC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,UAAM,QAAyB;AAAA,MAC7B,SAAS;AAAA,MACT;AAAA,MACA,kBAAkB,CAAC,GAAG,gBAAgB;AAAA,MACtC,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AACA,QAAI,QAAQ,KAAM,OAAM,OAAO,OAAO;AACtC,QAAI,QAAQ,QAAS,OAAM,UAAU,OAAO;AAC5C,IAAAC,eAAc,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EACnE,SAAS,KAAK;AACZ,YAAQ,OAAO;AAAA,MACb,+CAA0C,WAAW,MAAO,IAAc,OAAO;AAAA;AAAA,IACnF;AAAA,EACF;AACF;AAGO,SAAS,eAAe,aAA2B;AACxD,QAAM,OAAO,cAAc,WAAW;AACtC,MAAI;AACF,QAAIJ,YAAW,IAAI,EAAG,YAAW,IAAI;AAAA,EACvC,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,iBAAiB,aAAoC;AACnE,QAAM,SAAS,cAAc,WAAW;AACxC,MAAI,CAACA,YAAW,MAAM,EAAG,QAAO;AAChC,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AAC3D,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AACpD,QAAM,UAAUD;AAAA,IACd,YAAY;AAAA,IACZ,GAAG,aAAa,WAAW,CAAC,SAAS,KAAK,IAAI,MAAM;AAAA,EACtD;AACA,MAAI;AACF,eAAW,QAAQ,OAAO;AAC1B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO;AAAA,MACb,kDAA6C,WAAW,MAAO,IAAc,OAAO;AAAA;AAAA,IACtF;AACA,WAAO;AAAA,EACT;AACF;AAaO,SAAS,iBAAiB,aAA2C;AAC1E,QAAM,MAAM,YAAY;AACxB,MAAI,CAACC,YAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,QAAM,SAAS,GAAG,aAAa,WAAW,CAAC;AAC3C,QAAM,SAAS;AACf,MAAI;AACJ,MAAI;AACF,cAAUK,aAAY,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,YAAkC,CAAC;AACzC,aAAW,QAAQ,SAAS;AAC1B,QAAI,CAAC,KAAK,WAAW,MAAM,KAAK,CAAC,KAAK,SAAS,MAAM,EAAG;AACxD,UAAM,OAAON,MAAK,KAAK,IAAI;AAC3B,QAAI;AACF,YAAM,MAAME,cAAa,MAAM,MAAM;AACrC,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,OAAO,YAAY,EAAG;AAC1B,UAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,OAAO,MAAM,WAAW,EAAG;AAC/D,YAAM,QAAQ,OAAO,MAAM;AAAA,QACzB,CAAC,MACC,CAAC,CAAC,KACF,OAAO,MAAM,YACb,OAAQ,EAAe,OAAO,YAC9B,OAAQ,EAAe,UAAU,YACjC,OAAQ,EAAe,WAAW;AAAA,MACtC;AACA,UAAI,MAAM,WAAW,EAAG;AACxB,YAAM,mBAAmB,MAAM,QAAQ,OAAO,gBAAgB,IAC1D,OAAO,iBAAiB,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,CAAC,CAAC,EAAE,IACnF,CAAC;AAGL,UAAI,cAAc,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAC5E,UAAI,CAAC,eAAe,OAAO,MAAM,KAAK,MAAM,WAAW,CAAC,GAAG;AACzD,YAAI;AACF,wBAAc,SAAS,IAAI,EAAE,MAAM,YAAY;AAAA,QACjD,QAAQ;AACN,yBAAc,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,QACxC;AAAA,MACF;AACA,YAAM,QAA4B,EAAE,MAAM,MAAM,aAAa,OAAO,iBAAiB;AACrF,UAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAM,OAAM,OAAO,OAAO;AACxE,UAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAS,OAAM,UAAU,OAAO;AACjF,gBAAU,KAAK,KAAK;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,YAAU,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,cAAc,EAAE,WAAW,CAAC;AACnE,SAAO;AACT;AAOO,SAAS,sBAAgD;AAC9D,QAAM,MAAM,YAAY;AACxB,MAAI,CAACD,YAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,MAAI;AACJ,MAAI;AACF,cAAUK,aAAY,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAAgC,CAAC;AACvC,QAAM,SAAS;AACf,QAAM,aAAa;AACnB,aAAW,QAAQ,SAAS;AAC1B,QAAI,CAAC,KAAK,SAAS,MAAM,EAAG;AAC5B,UAAM,UAAU,KAAK,QAAQ,UAAU;AACvC,QAAI,UAAU,EAAG;AACjB,UAAM,cAAc,KAAK,MAAM,GAAG,OAAO;AACzC,QAAI,CAAC,YAAa;AAClB,UAAM,OAAON,MAAK,KAAK,IAAI;AAC3B,QAAI;AACF,YAAM,MAAME,cAAa,MAAM,MAAM;AACrC,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,OAAO,YAAY,EAAG;AAC1B,UAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,OAAO,MAAM,WAAW,EAAG;AAC/D,YAAM,QAAQ,OAAO,MAAM;AAAA,QACzB,CAAC,MACC,CAAC,CAAC,KACF,OAAO,MAAM,YACb,OAAQ,EAAe,OAAO,YAC9B,OAAQ,EAAe,UAAU,YACjC,OAAQ,EAAe,WAAW;AAAA,MACtC;AACA,UAAI,MAAM,WAAW,EAAG;AACxB,YAAM,mBAAmB,MAAM,QAAQ,OAAO,gBAAgB,IAC1D,OAAO,iBAAiB,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,CAAC,CAAC,EAAE,IACnF,CAAC;AACL,UAAI,cAAc,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAC5E,UAAI,CAAC,eAAe,OAAO,MAAM,KAAK,MAAM,WAAW,CAAC,GAAG;AACzD,YAAI;AACF,wBAAc,SAAS,IAAI,EAAE,MAAM,YAAY;AAAA,QACjD,QAAQ;AACN,yBAAc,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,QACxC;AAAA,MACF;AACA,YAAM,QAAgC;AAAA,QACpC;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,UAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAM,OAAM,OAAO,OAAO;AACxE,UAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAS,OAAM,UAAU,OAAO;AACjF,UAAI,KAAK,KAAK;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,MAAI,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,cAAc,EAAE,WAAW,CAAC;AAC7D,SAAO;AACT;AAGO,SAAS,aAAa,WAAmB,MAAc,KAAK,IAAI,GAAW;AAChF,QAAM,IAAI,KAAK,MAAM,SAAS;AAC9B,MAAI,OAAO,MAAM,CAAC,EAAG,QAAO;AAC5B,QAAM,SAAS,KAAK,IAAI,GAAG,MAAM,CAAC;AAClC,QAAM,MAAM,KAAK,MAAM,SAAS,GAAI;AACpC,MAAI,MAAM,GAAI,QAAO,GAAG,GAAG;AAC3B,QAAM,MAAM,KAAK,MAAM,MAAM,EAAE;AAC/B,MAAI,MAAM,GAAI,QAAO,GAAG,GAAG;AAC3B,QAAM,KAAK,KAAK,MAAM,MAAM,EAAE;AAC9B,MAAI,KAAK,GAAI,QAAO,GAAG,EAAE;AACzB,QAAM,MAAM,KAAK,MAAM,KAAK,EAAE;AAC9B,MAAI,MAAM,EAAG,QAAO,GAAG,GAAG;AAC1B,SAAO,UAAU,MAAM,GAAG,EAAE;AAC9B;;;AClRO,IAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAaA,IAAM,mBAAmB,IAAI;AAAA,EAC3B,kBAAkB,IAAI,CAAC,OAAO,UAAU,CAAC,OAAO,KAAK,CAAC;AACxD;AAEO,SAAS,0BACd,UACK;AACL,SAAO,SACJ,IAAI,CAAC,SAAS,WAAW,EAAE,SAAS,MAAM,EAAE,EAC5C,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,YACJ,iBAAiB,IAAI,EAAE,QAAQ,KAAK,IAAK,iBAAiB,IAAI,EAAE,QAAQ,KAAK;AAC/E,QAAI,cAAc,EAAG,QAAO;AAC5B,WAAO,EAAE,QAAQ,EAAE;AAAA,EACrB,CAAC,EACA,IAAI,CAAC,UAAU,MAAM,OAAO;AACjC;AAEO,IAAM,iBAA8C;AAAA,EACzD,EAAE,KAAK,QAAQ,OAAO,QAAQ,SAAS,mCAAmC,SAAS,CAAC,GAAG,EAAE;AAAA,EACzF;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS,CAAC,SAAS,OAAO;AAAA,EAC5B;AAAA,EACA,EAAE,KAAK,SAAS,OAAO,QAAQ,SAAS,qDAAqD;AAAA,EAC7F;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,EACJ;AAAA,EAEA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc,CAAC,QAAQ,SAAS,KAAK;AAAA,EACvC;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc,CAAC,MAAM,OAAO;AAAA,IAC5B,SAAS,CAAC,MAAM;AAAA,EAClB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,EAAE,KAAK,UAAU,OAAO,QAAQ,SAAS,yCAAyC;AAAA,EAClF;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EAEA,EAAE,KAAK,YAAY,OAAO,WAAW,SAAS,mDAA8C;AAAA,EAE5F,EAAE,KAAK,OAAO,OAAO,UAAU,SAAS,oDAAoD;AAAA,EAC5F;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EAEA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,YAAY;AAAA,IACZ,cAAc,CAAC,OAAO;AAAA,EACxB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SACE;AAAA,IACF,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,YAAY;AAAA,IACZ,cAAc,CAAC,UAAU,QAAQ,MAAM;AAAA,EACzC;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,cAAc,CAAC,MAAM,KAAK;AAAA,EAC5B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,YAAY;AAAA,IACZ,cAAc,CAAC,QAAQ,QAAQ;AAAA,EACjC;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,YAAY;AAAA,IACZ,SAAS,CAAC,SAAS;AAAA,EACrB;AAAA,EAEA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EAEA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc,CAAC,KAAK;AAAA,EACtB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,cAAc,CAAC,OAAO,KAAK,KAAK,MAAM,MAAM,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc,CAAC,UAAU,SAAS;AAAA,IAClC,SAAS,CAAC,IAAI;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SACE;AAAA,IACF,cAAc,CAAC,QAAQ,OAAO,UAAU,OAAO;AAAA,EACjD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc,CAAC,MAAM;AAAA,EACvB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA,EAAE,KAAK,QAAQ,OAAO,YAAY,SAAS,gBAAgB,SAAS,CAAC,QAAQ,GAAG,EAAE;AACpF;AAEO,SAAS,qBACd,QACA,WAAW,OACX,QACoB;AACpB,QAAM,IAAI,OAAO,YAAY;AAC7B,QAAM,UAAU,eAAe,OAAO,CAAC,MAAM;AAG3C,QAAI,MAAM,GAAI,QAAO,EAAE,UAAU;AACjC,QAAI,EAAE,eAAe,UAAU,CAAC,SAAU,QAAO;AACjD,QAAI,EAAE,IAAI,WAAW,CAAC,EAAG,QAAO;AAChC,WAAO,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK;AAAA,EACpD,CAAC;AACD,MAAI,MAAM,GAAI,QAAO,0BAA0B,OAAO;AACtD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AACzD,SAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM;AACjC,UAAM,QAAQ,OAAO,EAAE,GAAG,KAAK,MAAM,OAAO,EAAE,GAAG,KAAK;AACtD,QAAI,SAAS,EAAG,QAAO;AACvB,YAAQ,QAAQ,IAAI,EAAE,GAAG,KAAK,MAAM,QAAQ,IAAI,EAAE,GAAG,KAAK;AAAA,EAC5D,CAAC;AACH;AAEO,SAAS,sBAAsB,UAA2B;AAC/D,SAAO,eAAe;AAAA,IACpB,CAAC,MAAM,EAAE,UAAU,eAAe,EAAE,eAAe,UAAU;AAAA,EAC/D,EAAE;AACJ;AAGA,IAAM,gBAAkD,MAAM;AAC5D,QAAM,IAA4B,CAAC;AACnC,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,KAAK,QAAS;AACnB,eAAW,KAAK,KAAK,QAAS,GAAE,CAAC,IAAI,KAAK;AAAA,EAC5C;AACA,SAAO;AACT,GAAG;AAEI,SAAS,kBAAkB,MAAsB;AACtD,SAAO,aAAa,IAAI,KAAK;AAC/B;AAGO,SAAS,sBAAsB,OAAe,WAAW,OAA+B;AAC7F,QAAM,IAAI,sBAAsB,KAAK,KAAK;AAC1C,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,UAAU,kBAAkB,EAAE,CAAC,EAAG,YAAY,CAAC;AACrD,QAAM,OAAO,EAAE,CAAC,KAAK;AACrB,QAAM,OAAO,eAAe;AAAA,IAC1B,CAAC,MAAM,EAAE,QAAQ,YAAY,EAAE,eAAe,UAAU;AAAA,EAC1D;AACA,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,mBAAmB,KAAK,KAAK,IAAI;AACvC,QAAM,gBAAgB,MAAM,SAAS,KAAK;AAC1C,MAAI,kBAAkB;AACpB,WAAO,EAAE,MAAM,SAAS,MAAM,eAAe,MAAM,OAAO;AAAA,EAC5D;AACA,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,MAAM,KAAK,eAAe,WAAW;AAAA,EACvC;AACF;AAEO,SAAS,WAAW,MAAsD;AAC/E,MAAI,CAAC,KAAK,WAAW,GAAG,EAAG,QAAO;AAClC,QAAM,QAAQ,KAAK,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK;AAC9C,QAAM,MAAM,MAAM,CAAC,GAAG,YAAY,KAAK;AACvC,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,EAAE,KAAK,MAAM,MAAM,MAAM,CAAC,EAAE;AACrC;","names":["existsSync","mkdirSync","readFileSync","readdirSync","writeFileSync","dirname","join","join","existsSync","readFileSync","mkdirSync","dirname","writeFileSync","readdirSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/mcp/summary.ts","../../src/cli/commands/mcp-runtime.ts"],"sourcesContent":["import type { InspectionReport } from \"./inspect.js\";\nimport type { BridgeEnv, McpClientHost } from \"./registry.js\";\nimport type { GetPromptResult, ReadResourceResult } from \"./types.js\";\n\nexport interface McpServerSummary {\n label: string;\n spec: string;\n toolCount: number;\n report: InspectionReport;\n host: McpClientHost;\n bridgeEnv: BridgeEnv;\n readResource(uri: string): Promise<ReadResourceResult>;\n getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;\n}\n\nexport function buildMcpServerSummary(opts: {\n label: string;\n spec: string;\n toolCount: number;\n report: InspectionReport;\n host: McpClientHost;\n bridgeEnv: BridgeEnv;\n}): McpServerSummary {\n return {\n label: opts.label,\n spec: opts.spec,\n toolCount: opts.toolCount,\n report: opts.report,\n host: opts.host,\n bridgeEnv: opts.bridgeEnv,\n readResource(uri) {\n return opts.host.client.readResource(uri);\n },\n getPrompt(name, args) {\n return args !== undefined\n ? opts.host.client.getPrompt(name, args)\n : opts.host.client.getPrompt(name);\n },\n };\n}\n","import { mcpEnvFor, readConfig } from \"../../config.js\";\nimport { t } from \"../../i18n/index.js\";\nimport type { CacheFirstLoop } from \"../../loop.js\";\nimport { McpClient } from \"../../mcp/client.js\";\nimport { type InspectionReport, inspectMcpServer } from \"../../mcp/inspect.js\";\nimport { preflightStdioSpec } from \"../../mcp/preflight.js\";\nimport { type McpClientHost, bridgeMcpTools } from \"../../mcp/registry.js\";\nimport { parseMcpSpec } from \"../../mcp/spec.js\";\nimport { buildMcpServerSummary } from \"../../mcp/summary.js\";\nimport { buildTransportFromSpec } from \"../../mcp/transport-from-spec.js\";\nimport type { ToolRegistry } from \"../../tools.js\";\nimport type { ToolSpec } from \"../../types.js\";\nimport { formatMcpLifecycleEvent } from \"../ui/mcp-lifecycle.js\";\nimport { formatMcpSlowToast } from \"../ui/mcp-toast.js\";\nimport type { McpServerSummary } from \"../ui/slash.js\";\n\nexport interface ProgressInfo {\n toolName: string;\n progress: number;\n total?: number;\n message?: string;\n}\n\ninterface SpecRecord {\n spec: string;\n client: McpClient;\n summary: McpServerSummary;\n /** Names of bridged tools — used for hot-unbridge. */\n registeredNames: string[];\n /** ToolSpec snapshots captured AFTER bridge — handed to loop.prefix.addTool on hot-add. */\n registeredSpecs: ToolSpec[];\n}\n\nexport interface RuntimeContext {\n getTools: () => ToolRegistry | undefined;\n getMcpPrefix: () => string | undefined;\n getRequestedCount: () => number;\n progressSink: { current: ((info: ProgressInfo) => void) | null };\n}\n\nexport type McpLifecycleNotice =\n | { kind: \"handshake\"; name: string }\n | {\n kind: \"connected\";\n name: string;\n tools: number;\n resources: number;\n prompts: number;\n ms: number;\n }\n | { kind: \"disabled\"; name: string }\n | { kind: \"failed\"; name: string; reason: string }\n | { kind: \"slow\"; serverName: string; p95Ms: number; sampleSize: number };\n\nexport type McpLifecycleSink = (notice: McpLifecycleNotice) => void;\n\nexport const stderrLifecycleSink: McpLifecycleSink = (n) => {\n if (n.kind === \"slow\") {\n process.stderr.write(\n `${formatMcpSlowToast({ name: n.serverName, p95Ms: n.p95Ms, sampleSize: n.sampleSize })}\\n`,\n );\n return;\n }\n if (n.kind === \"failed\") {\n process.stderr.write(\n `${formatMcpLifecycleEvent({ state: \"failed\", name: n.name, reason: n.reason })}\\n → ${t(\"mcpLifecycle.failedSetupHint\")}\\n`,\n );\n return;\n }\n if (n.kind === \"connected\") {\n process.stderr.write(\n `${formatMcpLifecycleEvent({\n state: \"connected\",\n name: n.name,\n tools: n.tools,\n resources: n.resources,\n prompts: n.prompts,\n ms: n.ms,\n })}\\n`,\n );\n return;\n }\n process.stderr.write(`${formatMcpLifecycleEvent({ state: n.kind, name: n.name })}\\n`);\n};\n\nexport interface McpRuntime {\n size(): number;\n specs(): string[];\n summaries(): McpServerSummary[];\n addSpec(\n raw: string,\n loop?: CacheFirstLoop,\n ): Promise<{ ok: true; summary: McpServerSummary } | { ok: false; reason: string }>;\n removeSpec(raw: string, loop?: CacheFirstLoop): Promise<boolean>;\n reloadFromConfig(loop?: CacheFirstLoop): Promise<{\n added: string[];\n removed: string[];\n failed: Array<{ spec: string; reason: string }>;\n summaries: McpServerSummary[];\n }>;\n closeAll(): Promise<void>;\n /** Replace the sink that lifecycle events flow through — App.tsx swaps this in on mount so toasts land in the alt-screen UI instead of corrupting it via stderr. */\n setLifecycleSink(sink: McpLifecycleSink): void;\n}\n\nexport function createMcpRuntime(ctx: RuntimeContext): McpRuntime {\n const records = new Map<string, SpecRecord>();\n const insertionOrder: string[] = [];\n let sink: McpLifecycleSink = stderrLifecycleSink;\n\n async function addSpec(\n raw: string,\n loop?: CacheFirstLoop,\n ): Promise<{ ok: true; summary: McpServerSummary } | { ok: false; reason: string }> {\n if (records.has(raw)) {\n return { ok: true, summary: records.get(raw)!.summary };\n }\n const tools = ctx.getTools();\n if (!tools) return { ok: false, reason: \"no tool registry available\" };\n const disabledNames = new Set(readConfig().mcpDisabled ?? []);\n let label = \"anon\";\n let mcp: McpClient | undefined;\n // Per-server readiness gate — tool dispatches via the bridge await\n // this before calling into `live.callTool`. Resolved on `connected`,\n // rejected on `failed`, so a tool invoked mid-handshake waits\n // (capped by `bridgeMcpTools`'s `readyTimeoutMs`) instead of\n // surfacing a transport error.\n let resolveReady!: () => void;\n let rejectReady!: (err: Error) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n // Avoid unhandledRejection if no consumer awaits `ready` yet.\n ready.catch(() => undefined);\n try {\n const spec = parseMcpSpec(raw);\n label = spec.name ?? \"anon\";\n if (spec.name && disabledNames.has(spec.name)) {\n sink({ kind: \"disabled\", name: label });\n rejectReady(new Error(`MCP server \"${label}\" is disabled`));\n return { ok: false, reason: \"disabled by user\" };\n }\n sink({ kind: \"handshake\", name: label });\n const t0 = Date.now();\n const namePrefix = spec.name\n ? `${spec.name}_`\n : ctx.getRequestedCount() === 1 && ctx.getMcpPrefix()\n ? (ctx.getMcpPrefix() as string)\n : \"\";\n if (spec.transport === \"stdio\") preflightStdioSpec(spec);\n const transport = buildTransportFromSpec(spec, { env: mcpEnvFor(spec.name, readConfig()) });\n mcp = new McpClient({ transport });\n await mcp.initialize();\n const host: McpClientHost = { client: mcp };\n const bridge = await bridgeMcpTools(mcp, {\n registry: tools,\n namePrefix,\n serverName: label,\n host,\n ready,\n onProgress: (info) => ctx.progressSink.current?.(info),\n onSlow: (info) =>\n sink({\n kind: \"slow\",\n serverName: info.serverName,\n p95Ms: info.p95Ms,\n sampleSize: info.sampleSize,\n }),\n });\n let report: InspectionReport;\n try {\n report = await inspectMcpServer(mcp);\n } catch {\n report = {\n protocolVersion: mcp.protocolVersion,\n serverInfo: mcp.serverInfo,\n capabilities: mcp.serverCapabilities ?? {},\n tools: { supported: true, items: [] },\n resources: { supported: false, reason: \"inspect failed\" },\n prompts: { supported: false, reason: \"inspect failed\" },\n elapsedMs: 0,\n };\n }\n const ms = Date.now() - t0;\n const resourceCount = report.resources.supported ? report.resources.items.length : 0;\n const promptCount = report.prompts.supported ? report.prompts.items.length : 0;\n sink({\n kind: \"connected\",\n name: label,\n tools: bridge.registeredNames.length,\n resources: resourceCount,\n prompts: promptCount,\n ms,\n });\n resolveReady();\n const summary = buildMcpServerSummary({\n label,\n spec: raw,\n toolCount: bridge.registeredNames.length,\n report,\n host,\n bridgeEnv: bridge.env,\n });\n // Snapshot tool specs AFTER bridge so hot-add can replay them into loop.prefix.\n const allSpecs = tools.specs();\n const registeredSpecs = allSpecs.filter((s) =>\n bridge.registeredNames.includes(s.function.name),\n );\n records.set(raw, {\n spec: raw,\n client: mcp,\n summary,\n registeredNames: bridge.registeredNames,\n registeredSpecs,\n });\n insertionOrder.push(raw);\n // Hot-add: shift the prefix so the live loop sees the new tools\n // on the very next turn. Each addTool is one cache-miss turn.\n if (loop) for (const s of registeredSpecs) loop.prefix.addTool(s);\n return { ok: true, summary };\n } catch (err) {\n await mcp?.close().catch(() => undefined);\n const reason = (err as Error).message;\n sink({ kind: \"failed\", name: label, reason });\n rejectReady(new Error(`MCP server \"${label}\" failed to start: ${reason}`));\n return { ok: false, reason };\n }\n }\n\n async function removeSpec(raw: string, loop?: CacheFirstLoop): Promise<boolean> {\n const record = records.get(raw);\n if (!record) return false;\n await record.client.close().catch(() => undefined);\n const tools = ctx.getTools();\n for (const name of record.registeredNames) {\n tools?.unregister(name);\n loop?.prefix.removeTool(name);\n }\n records.delete(raw);\n const idx = insertionOrder.indexOf(raw);\n if (idx >= 0) insertionOrder.splice(idx, 1);\n return true;\n }\n\n async function reloadFromConfig(loop?: CacheFirstLoop): Promise<{\n added: string[];\n removed: string[];\n failed: Array<{ spec: string; reason: string }>;\n summaries: McpServerSummary[];\n }> {\n const desired = readConfig().mcp ?? [];\n const desiredSet = new Set(desired);\n const currentSet = new Set(records.keys());\n const added: string[] = [];\n const removed: string[] = [];\n const failed: Array<{ spec: string; reason: string }> = [];\n\n for (const spec of [...currentSet]) {\n if (!desiredSet.has(spec)) {\n await removeSpec(spec, loop);\n removed.push(spec);\n }\n }\n for (const spec of desired) {\n if (currentSet.has(spec)) continue;\n const result = await addSpec(spec, loop);\n if (result.ok) added.push(spec);\n else failed.push({ spec, reason: result.reason });\n }\n return { added, removed, failed, summaries: summaries() };\n }\n\n function specs(): string[] {\n return [...insertionOrder];\n }\n function summaries(): McpServerSummary[] {\n return insertionOrder\n .map((s) => records.get(s)?.summary)\n .filter((s): s is McpServerSummary => Boolean(s));\n }\n async function closeAll(): Promise<void> {\n for (const r of records.values()) await r.client.close().catch(() => undefined);\n records.clear();\n insertionOrder.length = 0;\n }\n function setLifecycleSink(s: McpLifecycleSink): void {\n sink = s;\n }\n return {\n size: () => records.size,\n specs,\n summaries,\n addSpec,\n removeSpec,\n reloadFromConfig,\n closeAll,\n setLifecycleSink,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/event-redaction.ts","../../src/core/eventize.ts","../../src/core/pause-policy.ts","../../src/tools/skills.ts"],"sourcesContent":["const SECRET_KEY_RE =\n /(secret|token|password|passphrase|api[-_]?key|authorization|cookie|credential|passwd|pwd)/i;\n\nexport function redactEventValue<T>(value: T): T {\n return redactUnknown(value, null) as T;\n}\n\nfunction redactUnknown(value: unknown, key: string | null): unknown {\n if (Array.isArray(value)) return value.map((item) => redactUnknown(item, null));\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [childKey, childValue] of Object.entries(value)) {\n out[childKey] = redactUnknown(childValue, childKey);\n }\n return out;\n }\n if (typeof value === \"string\") {\n if ((key && SECRET_KEY_RE.test(key)) || /^Bearer\\s+/i.test(value)) return \"[redacted]\";\n }\n return value;\n}\n","import type { LoopEvent } from \"../loop.js\";\nimport type { ChatMessage, RawUsage, ToolCall } from \"../types.js\";\nimport { redactEventValue } from \"./event-redaction.js\";\nimport type {\n Event,\n ErrorEvent as KernelErrorEvent,\n ModelDeltaEvent,\n ModelFinalEvent,\n ModelTurnStartedEvent,\n SessionCompactedEvent,\n SessionOpenedEvent,\n SlashInvokedEvent,\n StatusEvent,\n ToolCallEvent,\n ToolConfirmAllowEvent,\n ToolConfirmAlwaysAllowEvent,\n ToolConfirmDenyEvent,\n ToolDispatchedEvent,\n ToolIntentEvent,\n ToolPreparingEvent,\n ToolResultEvent,\n UserMessageEvent,\n} from \"./events.js\";\n\nexport interface EventizeContext {\n model: string;\n prefixHash: string;\n reasoningEffort: \"high\" | \"max\";\n}\n\nexport class Eventizer {\n private nextId = 0;\n private lastTurn = -1;\n private nextToolSeq = 0;\n /** Tool calls announced via tool_call_delta but not yet dispatched. FIFO upgraded by tool_start. */\n private preparingCallIds: string[] = [];\n /** Tool calls dispatched but not yet finished. FIFO popped by tool result. */\n private inflightCallIds: string[] = [];\n /** Per-turn dedupe so each toolCallIndex emits exactly one tool.preparing. */\n private announcedToolIdx = new Set<string>();\n\n consume(ev: LoopEvent, ctx: EventizeContext): Event[] {\n const out: Event[] = [];\n if (ev.turn !== this.lastTurn) {\n this.lastTurn = ev.turn;\n this.announcedToolIdx.clear();\n out.push(this.turnStartedEvent(ev.turn, ctx));\n }\n switch (ev.role) {\n case \"assistant_delta\":\n if (ev.content) out.push(this.deltaEvent(ev.turn, \"content\", ev.content));\n if (ev.reasoningDelta) out.push(this.deltaEvent(ev.turn, \"reasoning\", ev.reasoningDelta));\n break;\n case \"tool_call_delta\": {\n const idx = ev.toolCallIndex;\n const name = ev.toolName;\n if (idx === undefined || !name) break;\n const key = `${ev.turn}:${idx}`;\n if (this.announcedToolIdx.has(key)) break;\n this.announcedToolIdx.add(key);\n const callId = `tc-${++this.nextToolSeq}`;\n this.preparingCallIds.push(callId);\n out.push(this.toolPreparingEvent(ev.turn, callId, name));\n break;\n }\n case \"assistant_final\":\n out.push(this.finalEvent(ev));\n break;\n case \"tool_start\": {\n const callId = this.preparingCallIds.shift() ?? `tc-${++this.nextToolSeq}`;\n this.inflightCallIds.push(callId);\n out.push(this.toolIntentEvent(ev.turn, callId, ev.toolName ?? \"\", ev.toolArgs ?? \"\"));\n out.push(this.toolDispatchedEvent(ev.turn, callId));\n break;\n }\n case \"tool\": {\n const callId = this.inflightCallIds.shift() ?? `tc-orphan-${++this.nextToolSeq}`;\n const ok = !looksLikeToolError(ev.content, ev.toolName);\n out.push(this.toolResultEvent(ev.turn, callId, ok, ev.content, 0));\n break;\n }\n case \"warning\":\n out.push(this.classifyWarning(ev));\n break;\n case \"error\":\n out.push(this.errorEvent(ev.turn, ev.error ?? ev.content, false));\n break;\n case \"status\":\n out.push(this.statusEvent(ev.turn, ev.content));\n break;\n // `done` / `branch_*` intentionally drop — no kernel-level event.\n default:\n break;\n }\n return out;\n }\n\n emitUserMessage(turn: number, text: string): UserMessageEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"user.message\",\n text,\n };\n }\n\n emitSlashInvoked(turn: number, name: string, args: string): SlashInvokedEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"slash.invoked\",\n name,\n args,\n };\n }\n\n emitSessionOpened(turn: number, name: string, resumedFromTurn: number): SessionOpenedEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"session.opened\",\n name,\n resumedFromTurn,\n };\n }\n\n emitSessionCompacted(\n turn: number,\n before: number,\n after: number,\n reason: \"user\" | \"auto-context-pressure\",\n replacementMessages: ReadonlyArray<ChatMessage>,\n ): SessionCompactedEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"session.compacted\",\n beforeMessages: before,\n afterMessages: after,\n reason,\n replacementMessages,\n };\n }\n\n emitToolCall(turn: number, name: string, args: Record<string, unknown>): ToolCallEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.call\",\n name,\n args: redactEventValue(args),\n };\n }\n\n emitToolConfirmAllow(\n turn: number,\n kind: \"run_command\" | \"run_background\",\n payload: { command: string },\n ): ToolConfirmAllowEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.confirm.allow\",\n kind,\n payload: redactEventValue(payload),\n };\n }\n\n emitToolConfirmDeny(\n turn: number,\n kind: \"run_command\" | \"run_background\",\n payload: { command: string },\n denyContext?: string,\n ): ToolConfirmDenyEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.confirm.deny\",\n kind,\n payload: redactEventValue(payload),\n denyContext,\n };\n }\n\n emitToolConfirmAlwaysAllow(\n turn: number,\n kind: \"run_command\" | \"run_background\",\n payload: { command: string },\n prefix: string,\n ): ToolConfirmAlwaysAllowEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.confirm.always_allow\",\n kind,\n payload: redactEventValue(payload),\n prefix,\n };\n }\n\n private turnStartedEvent(turn: number, ctx: EventizeContext): ModelTurnStartedEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"model.turn.started\",\n model: ctx.model,\n reasoningEffort: ctx.reasoningEffort,\n prefixHash: ctx.prefixHash,\n };\n }\n\n private deltaEvent(\n turn: number,\n channel: \"content\" | \"reasoning\" | \"tool_args\",\n text: string,\n ): ModelDeltaEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"model.delta\",\n channel,\n text,\n };\n }\n\n private finalEvent(ev: LoopEvent): ModelFinalEvent {\n const usage: RawUsage = ev.stats\n ? {\n prompt_tokens: ev.stats.usage.promptTokens,\n completion_tokens: ev.stats.usage.completionTokens,\n total_tokens: ev.stats.usage.totalTokens,\n prompt_cache_hit_tokens: ev.stats.usage.promptCacheHitTokens,\n prompt_cache_miss_tokens: ev.stats.usage.promptCacheMissTokens,\n }\n : {};\n const costUsd = ev.stats?.cost ?? 0;\n const out: ModelFinalEvent = {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn: ev.turn,\n type: \"model.final\",\n content: ev.content,\n // toolCalls land later via tool_start → tool.intent — not in this event.\n toolCalls: [] as ReadonlyArray<ToolCall>,\n usage,\n costUsd,\n };\n if (ev.forcedSummary) out.forcedSummary = true;\n return out;\n }\n\n private toolPreparingEvent(turn: number, callId: string, name: string): ToolPreparingEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.preparing\",\n callId,\n name,\n };\n }\n\n private toolIntentEvent(\n turn: number,\n callId: string,\n name: string,\n args: string,\n ): ToolIntentEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.intent\",\n callId,\n name,\n args,\n };\n }\n\n private toolDispatchedEvent(turn: number, callId: string): ToolDispatchedEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.dispatched\",\n callId,\n };\n }\n\n private toolResultEvent(\n turn: number,\n callId: string,\n ok: boolean,\n output: string,\n durationMs: number,\n ): ToolResultEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"tool.result\",\n callId,\n ok,\n output,\n durationMs,\n };\n }\n\n private statusEvent(turn: number, text: string): StatusEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"status\",\n text,\n };\n }\n\n private errorEvent(turn: number, message: string, recoverable: boolean): KernelErrorEvent {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn,\n type: \"error\",\n message,\n recoverable,\n };\n }\n\n /** Pattern-match warning text since LoopEvent doesn't carry a typed kind. */\n private classifyWarning(ev: LoopEvent): Event {\n const c = ev.content;\n if (/\\bauto-escalating to\\b|\\barmed\\b.*pro|NEEDS_PRO/.test(c)) {\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn: ev.turn,\n type: \"policy.escalated\",\n fromModel: \"\",\n toModel: \"\",\n reason: c.includes(\"armed\") ? \"user-request\" : \"self-report\",\n };\n }\n if (/budget\\b.*\\$|\\$\\d.*\\/\\s*\\$\\d/.test(c)) {\n const blocked = /blocked|exceeded|refus/i.test(c);\n return {\n id: ++this.nextId,\n ts: new Date().toISOString(),\n turn: ev.turn,\n type: blocked ? \"policy.budget.blocked\" : \"policy.budget.warning\",\n spentUsd: 0,\n capUsd: 0,\n };\n }\n return this.errorEvent(ev.turn, c, true);\n }\n}\n\nfunction looksLikeToolError(content: string, _toolName: string | undefined): boolean {\n if (!content) return false;\n if (content.startsWith(\"ERROR:\")) return true;\n if (content.startsWith(\"[hook block]\")) return true;\n if (/^\\{\"error\"\\s*:/.test(content)) return true;\n if (/\\bConfirmationError:|\\bNeedsConfirmationError\\b/.test(content)) return true;\n return false;\n}\n","/** Shared editMode → auto-resolve rules so CLI TUI + Tauri desktop don't drift. */\n\nimport type { EditMode } from \"../config.js\";\nimport type { PauseRequest } from \"./pause-gate.js\";\n\n/** Mirrors shell.ts's allowAll bypass: only review still pauses on checkpoints. */\nexport function shouldAutoResolveCheckpoint(editMode: EditMode): boolean {\n return editMode === \"auto\" || editMode === \"yolo\";\n}\n\n/** null = surface to user; non-null = resolve gate immediately with this verdict. */\nexport function autoResolveVerdict(req: PauseRequest, editMode: EditMode): unknown | null {\n if (req.kind === \"plan_checkpoint\" && shouldAutoResolveCheckpoint(editMode)) {\n return { type: \"continue\" };\n }\n // yolo mirrors shell.ts's allowAll bypass — outside-sandbox reads/writes pass\n // through too. Stays \"run_once\" rather than \"always_allow\" so the YOLO session\n // doesn't pollute the on-disk allowlist with every transient path it touched.\n if (req.kind === \"path_access\" && editMode === \"yolo\") {\n return { type: \"run_once\" };\n }\n return null;\n}\n","/** runAs: inline appends the body to the parent log; subagent spawns an isolated child loop and only returns the final answer. */\n\nimport { type Skill, SkillStore } from \"../skills.js\";\nimport type { ToolRegistry } from \"../tools.js\";\n\n/** Returns serialized tool-result string — dispatch path is pure pass-through. */\nexport type SubagentRunner = (skill: Skill, task: string, signal?: AbortSignal) => Promise<string>;\n\nexport interface SkillToolsOptions {\n /** Override `$HOME` — tests set this to a tmpdir. */\n homeDir?: string;\n projectRoot?: string;\n /** When omitted, subagent skills error rather than silently falling back to inline (loses isolation). */\n subagentRunner?: SubagentRunner;\n /** Hide built-in skills (test-only knob; production callers leave off). */\n disableBuiltins?: boolean;\n}\n\nexport function registerSkillTools(\n registry: ToolRegistry,\n opts: SkillToolsOptions = {},\n): ToolRegistry {\n const store = new SkillStore({\n homeDir: opts.homeDir,\n projectRoot: opts.projectRoot,\n disableBuiltins: opts.disableBuiltins,\n });\n const subagentRunner = opts.subagentRunner;\n\n registry.register({\n name: \"run_skill\",\n description:\n \"Invoke a playbook from the Skills index pinned in the system prompt. Each entry is a self-contained instruction block. Pass `name` as the BARE skill identifier (e.g. 'explore'), NOT the `[🧬 subagent]` tag that appears after it in the index. Entries tagged `[🧬 subagent]` spawn an isolated subagent — only the final distilled answer comes back, the model's tool calls + reasoning during the run never enter your context. Plain skills are inlined: the body becomes a tool result you read and follow. For subagent skills, supply 'arguments' describing the concrete task — they'll be the only context the subagent has.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description:\n \"Skill identifier as it appears in the pinned Skills index (e.g. 'explore', 'review', 'security-review'). Case-sensitive.\",\n },\n arguments: {\n type: \"string\",\n description:\n \"Free-form arguments the skill should act on. For inline skills: appended to the body as an 'Arguments:' line; the skill's own instructions decide how to consume them. For `[🧬 subagent]` skills: REQUIRED — becomes the entire task description the subagent receives, since it has no other context.\",\n },\n },\n required: [\"name\"],\n },\n fn: async (args: { name?: unknown; arguments?: unknown }, ctx) => {\n const raw = typeof args.name === \"string\" ? args.name.trim() : \"\";\n if (!raw) {\n return JSON.stringify({ error: \"run_skill requires a 'name' argument\" });\n }\n // Defensive: The Skills index writes entries like\n // `explore [🧬 subagent]`, and models sometimes copy the\n // decoration verbatim into the `name` argument instead of just\n // the identifier. Rather than reject those calls:\n // 1. Drop any `[...]` bracketed tag (possibly containing\n // emoji + \"subagent\" label).\n // 2. Find the first whitespace-delimited token whose first\n // char is alphanumeric — that's the skill identifier,\n // whether the tag came before or after the name.\n const stripped = raw.replace(/\\[[^\\]]*\\]/g, \" \").trim();\n const tokens = stripped.split(/\\s+/).filter(Boolean);\n const name = tokens.find((t) => /^[a-zA-Z0-9]/.test(t)) ?? \"\";\n if (!name) {\n return JSON.stringify({\n error: \"run_skill requires a 'name' argument\",\n hint: `'${raw}' is just a marker/tag, not a skill name`,\n });\n }\n const skill = store.read(name);\n if (!skill) {\n const available = store\n .list()\n .map((s) => s.name)\n .join(\", \");\n return JSON.stringify({\n error: `unknown skill: ${JSON.stringify(name)}`,\n available: available || \"(none — user has not defined any skills)\",\n });\n }\n const rawArgs = typeof args.arguments === \"string\" ? args.arguments.trim() : \"\";\n\n if (skill.runAs === \"subagent\") {\n if (!subagentRunner) {\n return JSON.stringify({\n error: `run_skill: skill ${JSON.stringify(name)} is marked runAs=subagent but no subagent runner is configured for this session. Skill authors who need isolation should run inside reasonix code (or a library setup that passes subagentRunner to registerSkillTools).`,\n });\n }\n if (!rawArgs) {\n return JSON.stringify({\n error: `run_skill: skill ${JSON.stringify(name)} is a subagent and requires 'arguments' — the subagent has no other context, so describe the concrete task in the arguments field.`,\n });\n }\n return subagentRunner(skill, rawArgs, ctx?.signal);\n }\n\n const header = [\n `# Skill: ${skill.name}`,\n skill.description ? `> ${skill.description}` : \"\",\n `(scope: ${skill.scope} · ${skill.path})`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n const argsBlock = rawArgs ? `\\n\\nArguments: ${rawArgs}` : \"\";\n const inner = `${header}\\n\\n${skill.body}${argsBlock}`;\n // Sentinel-wrapped so ContextManager.fold preserves the body verbatim instead of paraphrasing it.\n return `<skill-pin name=${JSON.stringify(skill.name)}>\\n${inner}\\n</skill-pin>`;\n },\n });\n\n return registry;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/mcp/types.ts","../../src/mcp/client.ts","../../src/mcp/shell-split.ts","../../src/mcp/spec.ts","../../src/mcp/inspect.ts","../../src/mcp/stdio.ts","../../src/mcp/sse.ts","../../src/mcp/streamable-http.ts"],"sourcesContent":["/** MCP types (spec 2024-11-05). Stdio wire format is NDJSON — one JSON-RPC message per line, no Content-Length framing. */\n\nexport type JsonRpcId = string | number;\n\nexport interface JsonRpcRequest<P = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcNotification<P = unknown> {\n jsonrpc: \"2.0\";\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcSuccess<R = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result: R;\n}\n\nexport interface JsonRpcError {\n jsonrpc: \"2.0\";\n id: JsonRpcId | null;\n error: {\n /** JSON-RPC standard codes: -32700 parse, -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal. MCP also defines its own range. */\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport type JsonRpcResponse<R = unknown> = JsonRpcSuccess<R> | JsonRpcError;\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;\n\nexport interface McpClientInfo {\n name: string;\n version: string;\n}\n\nexport interface McpClientCapabilities {\n /** Empty object advertises support without any optional sub-features. */\n tools?: Record<string, never>;\n /** Advertised when the client can consume `resources/list` + `resources/read`. */\n resources?: Record<string, never>;\n /** Advertised when the client can consume `prompts/list` + `prompts/get`. */\n prompts?: Record<string, never>;\n // sampling would go here — deferred.\n}\n\nexport interface InitializeParams {\n protocolVersion: string;\n capabilities: McpClientCapabilities;\n clientInfo: McpClientInfo;\n}\n\nexport interface InitializeResult {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: {\n tools?: { listChanged?: boolean };\n resources?: unknown;\n prompts?: unknown;\n };\n instructions?: string;\n}\n\nexport interface McpToolSchema {\n /** JSON Schema — compatible with Reasonix's tools.ts JSONSchema shape. */\n type?: string;\n properties?: Record<string, unknown>;\n required?: string[];\n [extra: string]: unknown;\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n /** MCP calls this `inputSchema`. Reasonix's `parameters` field is the same concept. */\n inputSchema: McpToolSchema;\n}\n\nexport interface ListToolsResult {\n tools: McpTool[];\n nextCursor?: string;\n}\n\nexport interface CallToolParams {\n name: string;\n arguments?: Record<string, unknown>;\n _meta?: { progressToken?: string | number };\n}\n\nexport interface ProgressNotificationParams {\n progressToken: string | number;\n progress: number;\n total?: number;\n message?: string;\n}\n\n/** Values a `ProgressHandler` receives — `progressToken` is already matched away. */\nexport interface McpProgressInfo {\n progress: number;\n total?: number;\n message?: string;\n}\n\nexport type McpProgressHandler = (info: McpProgressInfo) => void;\n\nexport interface McpContentBlockText {\n type: \"text\";\n text: string;\n}\n\nexport interface McpContentBlockImage {\n type: \"image\";\n data: string;\n mimeType: string;\n}\n\n/** MCP result content is an array of typed blocks. Reasonix consumes only text for now — image blocks get stringified with a placeholder. */\nexport type McpContentBlock = McpContentBlockText | McpContentBlockImage;\n\nexport interface CallToolResult {\n content: McpContentBlock[];\n /** True = tool raised an error; the content describes it. */\n isError?: boolean;\n}\n\nexport interface McpResource {\n uri: string;\n name: string;\n description?: string;\n /** Hint for the content type (e.g. \"text/markdown\"). Purely informational. */\n mimeType?: string;\n}\n\nexport interface ListResourcesParams {\n /** Pagination cursor from a previous listResources response. */\n cursor?: string;\n}\n\nexport interface ListResourcesResult {\n resources: McpResource[];\n nextCursor?: string;\n}\n\nexport interface ReadResourceParams {\n uri: string;\n}\n\n/** Server populates exactly one of `text` (UTF-8) or `blob` (base64) per entry. */\nexport interface McpResourceContentsText {\n uri: string;\n mimeType?: string;\n text: string;\n}\n\nexport interface McpResourceContentsBlob {\n uri: string;\n mimeType?: string;\n blob: string;\n}\n\nexport type McpResourceContents = McpResourceContentsText | McpResourceContentsBlob;\n\nexport interface ReadResourceResult {\n contents: McpResourceContents[];\n}\n\nexport interface McpPromptArgument {\n name: string;\n description?: string;\n required?: boolean;\n}\n\nexport interface McpPrompt {\n name: string;\n description?: string;\n arguments?: McpPromptArgument[];\n}\n\nexport interface ListPromptsParams {\n cursor?: string;\n}\n\nexport interface ListPromptsResult {\n prompts: McpPrompt[];\n nextCursor?: string;\n}\n\nexport interface GetPromptParams {\n name: string;\n arguments?: Record<string, string>;\n}\n\nexport interface McpPromptMessage {\n role: \"user\" | \"assistant\";\n content: McpContentBlock | McpPromptResourceBlock;\n}\n\nexport interface McpPromptResourceBlock {\n type: \"resource\";\n resource: McpResourceContents;\n}\n\nexport interface GetPromptResult {\n description?: string;\n messages: McpPromptMessage[];\n}\n\n/** Current MCP protocol version Reasonix is coded against. */\nexport const MCP_PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Type guard — success vs error response. */\nexport function isJsonRpcError(msg: JsonRpcResponse): msg is JsonRpcError {\n return \"error\" in msg;\n}\n","import { VERSION } from \"../version.js\";\nimport type { McpTransport } from \"./stdio.js\";\nimport {\n type CallToolParams,\n type CallToolResult,\n type GetPromptParams,\n type GetPromptResult,\n type InitializeParams,\n type InitializeResult,\n type JsonRpcId,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcResponse,\n type ListPromptsParams,\n type ListPromptsResult,\n type ListResourcesParams,\n type ListResourcesResult,\n type ListToolsResult,\n MCP_PROTOCOL_VERSION,\n type McpClientInfo,\n type McpProgressHandler,\n type ProgressNotificationParams,\n type ReadResourceParams,\n type ReadResourceResult,\n isJsonRpcError,\n} from \"./types.js\";\n\nexport interface McpClientOptions {\n transport: McpTransport;\n clientInfo?: McpClientInfo;\n /** Per-request timeout. Default 60s. */\n requestTimeoutMs?: number;\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n timeout: NodeJS.Timeout;\n}\n\nexport class McpClient {\n private readonly transport: McpTransport;\n private readonly clientInfo: McpClientInfo;\n private readonly requestTimeoutMs: number;\n private readonly pending = new Map<JsonRpcId, PendingRequest>();\n private nextId = 1;\n private readerStarted = false;\n private initialized = false;\n private _serverCapabilities: InitializeResult[\"capabilities\"] = {};\n private _serverInfo: InitializeResult[\"serverInfo\"] = { name: \"\", version: \"\" };\n private _protocolVersion = \"\";\n private _instructions: string | undefined;\n // Progress-token → handler for notifications/progress routing. Tokens\n // are minted per call when the caller supplies an onProgress\n // callback; cleared when the final response lands (or the pending\n // request rejects). No leaks — the `try/finally` in callTool\n // guarantees cleanup even on timeout.\n private readonly progressHandlers = new Map<string | number, McpProgressHandler>();\n private nextProgressToken = 1;\n\n constructor(opts: McpClientOptions) {\n this.transport = opts.transport;\n this.clientInfo = opts.clientInfo ?? { name: \"reasonix\", version: VERSION };\n this.requestTimeoutMs = opts.requestTimeoutMs ?? 60_000;\n }\n\n /** Server's advertised capabilities, available after initialize(). */\n get serverCapabilities(): InitializeResult[\"capabilities\"] {\n return this._serverCapabilities;\n }\n\n /** Server's self-reported name + version, available after initialize(). */\n get serverInfo(): InitializeResult[\"serverInfo\"] {\n return this._serverInfo;\n }\n\n /** Protocol version the server agreed to during the handshake. */\n get protocolVersion(): string {\n return this._protocolVersion;\n }\n\n /** Optional free-form instructions the server provides at handshake. */\n get serverInstructions(): string | undefined {\n return this._instructions;\n }\n\n /** Compliant servers reject other methods until this completes. */\n async initialize(): Promise<InitializeResult> {\n if (this.initialized) throw new Error(\"MCP client already initialized\");\n this.startReaderIfNeeded();\n const result = await this.request<InitializeResult>(\"initialize\", {\n protocolVersion: MCP_PROTOCOL_VERSION,\n // Advertise every method the client can consume so servers know\n // they can send listChanged notifications etc. Sub-feature flags\n // (e.g. `resources.subscribe`) are omitted — we don't implement\n // those yet and the empty object means \"method-level support, no\n // sub-features.\"\n capabilities: { tools: {}, resources: {}, prompts: {} },\n clientInfo: this.clientInfo,\n } satisfies InitializeParams);\n this._serverCapabilities = result.capabilities ?? {};\n this._serverInfo = result.serverInfo ?? { name: \"\", version: \"\" };\n this._protocolVersion = result.protocolVersion ?? \"\";\n this._instructions = result.instructions;\n // Per spec: client sends notifications/initialized after receiving the\n // initialize response. Only then is the connection live for other\n // methods.\n await this.transport.send({\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n });\n this.initialized = true;\n return result;\n }\n\n /** List tools the server exposes. */\n async listTools(): Promise<ListToolsResult> {\n this.assertInitialized();\n return this.request<ListToolsResult>(\"tools/list\", {});\n }\n\n /** Abort sends `notifications/cancelled` and rejects immediately; late server responses are dropped. */\n async callTool(\n name: string,\n args?: Record<string, unknown>,\n opts: { onProgress?: McpProgressHandler; signal?: AbortSignal } = {},\n ): Promise<CallToolResult> {\n this.assertInitialized();\n const params: CallToolParams = { name, arguments: args ?? {} };\n let token: number | undefined;\n if (opts.onProgress) {\n token = this.nextProgressToken++;\n this.progressHandlers.set(token, opts.onProgress);\n params._meta = { progressToken: token };\n }\n try {\n return await this.request<CallToolResult>(\"tools/call\", params, opts.signal);\n } finally {\n if (token !== undefined) this.progressHandlers.delete(token);\n }\n }\n\n /** Throws on method-not-found; callers should gate on `serverCapabilities.resources` first. */\n async listResources(cursor?: string): Promise<ListResourcesResult> {\n this.assertInitialized();\n return this.request<ListResourcesResult>(\"resources/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListResourcesParams);\n }\n\n /** Read the contents of a resource by URI. */\n async readResource(uri: string): Promise<ReadResourceResult> {\n this.assertInitialized();\n return this.request<ReadResourceResult>(\"resources/read\", {\n uri,\n } satisfies ReadResourceParams);\n }\n\n /** List prompt templates the server exposes. */\n async listPrompts(cursor?: string): Promise<ListPromptsResult> {\n this.assertInitialized();\n return this.request<ListPromptsResult>(\"prompts/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListPromptsParams);\n }\n\n async getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult> {\n this.assertInitialized();\n return this.request<GetPromptResult>(\"prompts/get\", {\n name,\n ...(args ? { arguments: args } : {}),\n } satisfies GetPromptParams);\n }\n\n /** Close the transport and reject any outstanding requests. */\n async close(): Promise<void> {\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(new Error(\"MCP client closed\"));\n }\n this.pending.clear();\n await this.transport.close();\n }\n\n private assertInitialized(): void {\n if (!this.initialized) throw new Error(\"MCP client not initialized — call initialize() first\");\n }\n\n private async request<R>(method: string, params: unknown, signal?: AbortSignal): Promise<R> {\n const id = this.nextId++;\n const frame: JsonRpcRequest = { jsonrpc: \"2.0\", id, method, params };\n let abortHandler: (() => void) | null = null;\n const promise = new Promise<R>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n reject(\n new Error(`MCP request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`),\n );\n }, this.requestTimeoutMs);\n this.pending.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout,\n });\n // Wire up cancellation: when signal fires, send an MCP cancellation\n // notification to the server (so it can stop whatever it was doing)\n // and reject the caller immediately — no need to wait for the\n // subprocess to finish its in-flight work. Late responses from the\n // server are dropped by `dispatch` because the id is gone from\n // `pending`.\n if (signal) {\n if (signal.aborted) {\n this.pending.delete(id);\n clearTimeout(timeout);\n reject(new Error(`MCP request ${method} (id=${id}) aborted before send`));\n return;\n }\n abortHandler = () => {\n this.pending.delete(id);\n clearTimeout(timeout);\n void this.transport\n .send({\n jsonrpc: \"2.0\",\n method: \"notifications/cancelled\",\n params: { requestId: id, reason: \"aborted by user\" },\n })\n .catch(() => {\n // Transport may already be closing — swallow; we still\n // reject the caller below so they unblock.\n });\n reject(new Error(`MCP request ${method} (id=${id}) aborted by user`));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n promise.catch(() => undefined);\n // Swallow rejection on the race-leg derivative too — if `send` wins the race,\n // a late-rejecting `promise.then(...)` would otherwise be orphaned (#742).\n const promiseSettled = promise.then(\n () => undefined,\n () => undefined,\n );\n try {\n await Promise.race([this.transport.send(frame), promiseSettled]);\n } catch (err) {\n const pending = this.pending.get(id);\n if (pending) clearTimeout(pending.timeout);\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n throw err;\n }\n try {\n return await promise;\n } finally {\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n\n private startReaderIfNeeded(): void {\n if (this.readerStarted) return;\n this.readerStarted = true;\n // Fire-and-forget: the reader runs for the lifetime of the client.\n void this.readLoop();\n }\n\n private async readLoop(): Promise<void> {\n try {\n for await (const msg of this.transport.messages()) {\n this.dispatch(msg);\n }\n } catch (err) {\n // Surface as rejections on all pending requests so nobody hangs.\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(err as Error);\n }\n this.pending.clear();\n }\n }\n\n private dispatch(msg: JsonRpcMessage): void {\n // Notifications (no `id`): route by method. Progress notifications\n // go to the per-call handler if one was registered; everything\n // else is dropped silently (we don't yet handle tools/list_changed\n // or resources/list_changed).\n if (!(\"id\" in msg) || msg.id === null || msg.id === undefined) {\n if (\"method\" in msg && msg.method === \"notifications/progress\") {\n const p = msg.params as ProgressNotificationParams | undefined;\n if (!p || p.progressToken === undefined) return;\n const handler = this.progressHandlers.get(p.progressToken);\n if (!handler) return; // late notification after the call resolved\n handler({ progress: p.progress, total: p.total, message: p.message });\n }\n return;\n }\n if (!(\"result\" in msg) && !(\"error\" in msg)) return; // it's a request from server\n const pending = this.pending.get(msg.id);\n if (!pending) return; // late response after timeout; drop\n this.pending.delete(msg.id);\n clearTimeout(pending.timeout);\n const resp = msg as JsonRpcResponse;\n if (isJsonRpcError(resp)) {\n pending.reject(new Error(`MCP ${resp.error.code}: ${resp.error.message}`));\n } else {\n pending.resolve(resp.result);\n }\n }\n}\n","/** Quote-aware argv split for `--mcp`; throws on unterminated quotes. NOT a full shell parser. */\nexport function shellSplit(input: string): string[] {\n const tokens: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n let i = 0;\n const s = input;\n\n while (i < s.length) {\n const ch = s[i]!;\n\n if (quote) {\n if (ch === quote) {\n quote = null;\n i++;\n continue;\n }\n // backslash escapes inside double quotes only\n if (ch === \"\\\\\" && quote === '\"' && i + 1 < s.length) {\n cur += s[i + 1];\n i += 2;\n continue;\n }\n cur += ch;\n i++;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\") {\n quote = ch as '\"' | \"'\";\n i++;\n continue;\n }\n\n // Backslash escape ONLY applies inside double quotes (handled above).\n // Outside quotes, backslashes pass through literally — otherwise\n // Windows paths like `C:\\path\\to\\exe` get mangled. POSIX users who\n // want to escape a space outside quotes can use single quotes instead.\n\n if (ch === \" \" || ch === \"\\t\") {\n if (cur.length > 0) {\n tokens.push(cur);\n cur = \"\";\n }\n i++;\n continue;\n }\n\n cur += ch;\n i++;\n }\n\n if (quote) {\n throw new Error(\n `shellSplit: unterminated ${quote === '\"' ? \"double\" : \"single\"} quote in input`,\n );\n }\n if (cur.length > 0) tokens.push(cur);\n return tokens;\n}\n","/** Plain http:// stays HTTP+SSE for back-compat; Streamable HTTP is opt-in via the `streamable+` URL prefix. */\n\nimport { shellSplit } from \"./shell-split.js\";\n\nexport interface StdioMcpSpec {\n transport: \"stdio\";\n /** Namespace prefix applied to each registered tool, or null if anonymous. */\n name: string | null;\n /** Argv[0]. */\n command: string;\n /** Remaining argv. */\n args: string[];\n}\n\nexport interface SseMcpSpec {\n transport: \"sse\";\n name: string | null;\n /** Fully qualified SSE endpoint URL. */\n url: string;\n}\n\nexport interface StreamableHttpMcpSpec {\n transport: \"streamable-http\";\n name: string | null;\n /** Fully qualified Streamable HTTP endpoint URL (no `streamable+` prefix). */\n url: string;\n}\n\nexport type McpSpec = StdioMcpSpec | SseMcpSpec | StreamableHttpMcpSpec;\n\nconst NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;\nconst HTTP_URL = /^https?:\\/\\//i;\nconst STREAMABLE_PREFIX = /^streamable\\+(https?:\\/\\/.+)$/i;\n\nexport function parseMcpSpec(input: string): McpSpec {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new Error(\"empty MCP spec\");\n }\n\n const nameMatch = NAME_PREFIX.exec(trimmed);\n const name = nameMatch ? nameMatch[1]! : null;\n const body = (nameMatch ? nameMatch[2]! : trimmed).trim();\n\n if (!body) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n\n const streamMatch = STREAMABLE_PREFIX.exec(body);\n if (streamMatch) {\n return { transport: \"streamable-http\", name, url: streamMatch[1]! };\n }\n\n if (HTTP_URL.test(body)) {\n return { transport: \"sse\", name, url: body };\n }\n\n const argv = shellSplit(body);\n if (argv.length === 0) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n const [command, ...args] = argv;\n return { transport: \"stdio\", name, command: command!, args };\n}\n","/** Unsupported list methods surface as `{supported:false}` instead of throwing — minimal servers still get a clean report. */\n\nimport type { McpClient } from \"./client.js\";\nimport type { McpPrompt, McpResource, McpTool } from \"./types.js\";\n\nexport interface InspectionReport {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: Record<string, unknown>;\n instructions?: string;\n tools: SectionResult<McpTool>;\n resources: SectionResult<McpResource>;\n prompts: SectionResult<McpPrompt>;\n /** Wall-clock for the three list calls combined; surfaced as the server's \"p95-ish\" latency in the browser. */\n elapsedMs: number;\n}\n\nexport type SectionResult<T> =\n | { supported: true; items: T[] }\n | { supported: false; reason: string };\n\n/** Caller owns initialize() / close() — keeps this pure so tests can feed a FakeMcpTransport. */\nexport async function inspectMcpServer(client: McpClient): Promise<InspectionReport> {\n const t0 = Date.now();\n // Always try all three listings — some servers omit capability flags but still serve the methods.\n const tools = await trySection<McpTool>(() => client.listTools().then((r) => r.tools));\n const resources = await trySection<McpResource>(() =>\n client.listResources().then((r) => r.resources),\n );\n const prompts = await trySection<McpPrompt>(() => client.listPrompts().then((r) => r.prompts));\n\n return {\n protocolVersion: client.protocolVersion || \"(unknown)\",\n serverInfo: client.serverInfo,\n capabilities: client.serverCapabilities ?? {},\n instructions: client.serverInstructions,\n tools,\n resources,\n prompts,\n elapsedMs: Date.now() - t0,\n };\n}\n\nasync function trySection<T>(load: () => Promise<T[]>): Promise<SectionResult<T>> {\n try {\n const items = await load();\n return { supported: true, items };\n } catch (err) {\n const msg = (err as Error).message ?? String(err);\n // -32601 is JSON-RPC \"method not found\" — the canonical response\n // from a server that doesn't implement this family. Treat it as\n // \"not supported\" rather than a hard error, so the CLI can render\n // a clean summary instead of aborting on the first missing method.\n if (/-32601/.test(msg) || /method not found/i.test(msg)) {\n return { supported: false, reason: \"method not found (-32601)\" };\n }\n return { supported: false, reason: msg };\n }\n}\n","/** MCP stdio = newline-delimited JSON-RPC; transport iface lets tests fake it without spawning. */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface McpTransport {\n /** Send one JSON-RPC message. Resolves when the bytes are accepted. */\n send(message: JsonRpcMessage): Promise<void>;\n /** Async iterator over incoming messages. Ends when the connection closes. */\n messages(): AsyncIterableIterator<JsonRpcMessage>;\n /** Close the underlying resource (kill child process, close streams). */\n close(): Promise<void>;\n}\n\nexport interface StdioTransportOptions {\n /** Argv to spawn. First element is the command. */\n command: string;\n args?: string[];\n /** Env overlay — merged over process.env unless replaceEnv=true. */\n env?: Record<string, string>;\n /** When true, only the env above is visible to the child. Default false. */\n replaceEnv?: boolean;\n /** CWD for the child. Default: process.cwd(). */\n cwd?: string;\n /** Default true on win32 to resolve `.cmd`/`.bat` wrappers (npx.cmd etc.). */\n shell?: boolean;\n}\n\nexport class StdioTransport implements McpTransport {\n private readonly child: ChildProcess;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private closed = false;\n private stdoutBuffer = \"\";\n\n constructor(opts: StdioTransportOptions) {\n const env = opts.replaceEnv ? { ...(opts.env ?? {}) } : { ...process.env, ...(opts.env ?? {}) };\n // Windows wraps binaries as .cmd/.bat shims (npx.cmd, pnpm.cmd, …).\n // child_process.spawn without shell:true can't resolve them, which\n // breaks `--mcp \"npx -y some-server\"` — the most common MCP setup.\n // Default shell:true on win32 and leave POSIX alone.\n const shell = opts.shell ?? process.platform === \"win32\";\n\n if (shell) {\n // Node's shell:true + args[] triggers DEP0190 because it concatenates\n // with spaces and doesn't quote args — unsafe if an arg contains\n // shell metacharacters. We build a single command line ourselves,\n // quoting ONLY the args (command stays bare so the shell's PATH /\n // PATHEXT lookup finds `npx` → `npx.cmd` on Windows).\n const line = [\n opts.command,\n ...(opts.args ?? []).map((a) => quoteArg(a, process.platform === \"win32\")),\n ].join(\" \");\n this.child = spawn(line, [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n shell: true,\n });\n } else {\n this.child = spawn(opts.command, opts.args ?? [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n });\n }\n this.child.stdout!.setEncoding(\"utf8\");\n this.child.stdout!.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.on(\"close\", () => this.onClose());\n this.child.on(\"error\", (err) => {\n // Surface spawn errors as a synthetic JsonRpcError so callers don't\n // hang on a stream that never emits anything.\n this.push({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message: `transport error: ${err.message}` },\n });\n });\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP transport is closed\");\n return new Promise((resolve, reject) => {\n const line = `${JSON.stringify(message)}\\n`;\n this.child.stdin!.write(line, \"utf8\", (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return; // closed while we were waiting\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n // Signal any pending waiters.\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.child.stdin!.end();\n } catch {\n /* already ended */\n }\n if (this.child.exitCode === null && !this.child.killed) {\n // child.kill(\"SIGTERM\") throws EINVAL on Windows; plain kill()\n // can also throw on failed spawns. Swallow both.\n try {\n this.child.kill(process.platform === \"win32\" ? undefined : \"SIGTERM\");\n } catch {\n /* already exited or unsignallable */\n }\n }\n }\n\n /** Parse incoming stdout chunks into NDJSON messages. */\n private onStdout(chunk: string): void {\n this.stdoutBuffer += chunk;\n let newlineIdx: number;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic loop shape\n while ((newlineIdx = this.stdoutBuffer.indexOf(\"\\n\")) !== -1) {\n const line = this.stdoutBuffer.slice(0, newlineIdx).trim();\n this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);\n if (!line) continue;\n try {\n const msg = JSON.parse(line) as JsonRpcMessage;\n this.push(msg);\n } catch {\n // Malformed lines are dropped — some servers emit startup banners\n // before the JSON-RPC loop begins. We surface the noise to stderr\n // via the inherited stderr stream, not our event queue.\n if (process.env.REASONIX_DEBUG_MCP === \"1\") {\n process.stderr.write(`[mcp-stdio] dropped malformed line: ${line}\\n`);\n }\n }\n }\n }\n\n private onClose(): void {\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n\n private push(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n\nfunction quoteArg(s: string, windows: boolean): string {\n if (!windows) {\n // POSIX: single-quote, escape single quotes.\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n // cmd.exe: double-quote, escape internal quotes by doubling.\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n}\n","/** MCP HTTP+SSE transport (spec 2024-11-05) — POST endpoint URL arrives as the first `event: endpoint` SSE frame. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface SseTransportOptions {\n /** SSE endpoint URL, e.g. `https://mcp.example.com/sse`. */\n url: string;\n /** Extra headers sent on both the SSE GET and the JSON-RPC POSTs (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nexport class SseTransport implements McpTransport {\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n private closed = false;\n private postUrl: string | null = null;\n private readonly endpointReady: Promise<string>;\n private resolveEndpoint!: (url: string) => void;\n private rejectEndpoint!: (err: Error) => void;\n\n constructor(opts: SseTransportOptions) {\n this.url = opts.url;\n this.headers = opts.headers ?? {};\n this.endpointReady = new Promise<string>((resolve, reject) => {\n this.resolveEndpoint = resolve;\n this.rejectEndpoint = reject;\n });\n // Swallow unhandled-rejection noise if nobody ever calls send().\n this.endpointReady.catch(() => undefined);\n void this.runStream();\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP SSE transport is closed\");\n const postUrl = await this.endpointReady;\n const res = await fetch(postUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...this.headers },\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n // Drain body so the socket returns to the pool even if the server\n // elected to write one. We explicitly don't parse it — responses\n // arrive on the SSE channel.\n await res.arrayBuffer().catch(() => undefined);\n if (!res.ok) {\n throw new Error(`MCP SSE POST ${postUrl} failed: ${res.status} ${res.statusText}`);\n }\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n // Reject any still-pending send() that was waiting for the endpoint.\n this.rejectEndpoint(new Error(\"MCP SSE transport closed before endpoint was ready\"));\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n }\n\n private async runStream(): Promise<void> {\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"GET\",\n headers: { accept: \"text/event-stream\", ...this.headers },\n signal: this.controller.signal,\n });\n } catch (err) {\n this.failHandshake(`SSE connect to ${this.url} failed: ${(err as Error).message}`);\n return;\n }\n if (!res.ok || !res.body) {\n // Drain body to free the socket before giving up.\n await res.body?.cancel().catch(() => undefined);\n this.failHandshake(`SSE handshake ${this.url} → ${res.status} ${res.statusText}`);\n return;\n }\n\n const parser = createParser({\n onEvent: (ev) => this.handleEvent(ev.event ?? \"message\", ev.data),\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of res.body as AsyncIterable<Uint8Array>) {\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushError(`SSE stream error: ${(err as Error).message}`);\n }\n } finally {\n this.markClosed();\n }\n }\n\n private handleEvent(type: string, data: string): void {\n if (type === \"endpoint\") {\n if (this.postUrl) return; // ignore repeat announcements\n try {\n this.postUrl = new URL(data, this.url).toString();\n this.resolveEndpoint(this.postUrl);\n } catch (err) {\n this.failHandshake(`SSE endpoint event had bad URL \"${data}\": ${(err as Error).message}`);\n }\n return;\n }\n if (type === \"message\") {\n try {\n const parsed = JSON.parse(data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n // Malformed JSON-RPC on an SSE frame — drop it, same as stdio.\n }\n return;\n }\n // Unknown event types (server pings, custom extensions) — ignore.\n }\n\n private failHandshake(reason: string): void {\n this.rejectEndpoint(new Error(reason));\n this.pushError(reason);\n this.markClosed();\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n\n private pushError(message: string): void {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message },\n });\n }\n\n private markClosed(): void {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n}\n","/** MCP Streamable HTTP transport (2025-03-26) — POST-only; no long-lived GET stream, no Last-Event-ID resume. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface StreamableHttpTransportOptions {\n /** Streamable HTTP endpoint URL, e.g. `https://mcp.example.com/mcp`. */\n url: string;\n /** Extra headers sent on every request (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nconst SESSION_HEADER = \"mcp-session-id\";\n\nexport class StreamableHttpTransport implements McpTransport {\n private readonly url: string;\n private readonly extraHeaders: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n /** Session id minted by server on (typically) the initialize response. */\n private sessionId: string | null = null;\n private closed = false;\n /** Background SSE read-loops kicked off by send(); awaited on close(). */\n private readonly streams = new Set<Promise<void>>();\n\n constructor(opts: StreamableHttpTransportOptions) {\n this.url = opts.url;\n this.extraHeaders = opts.headers ?? {};\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP Streamable HTTP transport is closed\");\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n // Both accepted — server picks. application/json first signals a\n // mild preference for the simpler shape when the response is a\n // single message.\n accept: \"application/json, text/event-stream\",\n ...this.extraHeaders,\n };\n if (this.sessionId !== null) headers[\"mcp-session-id\"] = this.sessionId;\n\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n } catch (err) {\n throw new Error(`MCP Streamable HTTP POST ${this.url} failed: ${(err as Error).message}`);\n }\n\n // Capture session id the first time the server hands one out.\n const serverSessionId = res.headers.get(SESSION_HEADER);\n if (serverSessionId && this.sessionId === null) {\n this.sessionId = serverSessionId;\n }\n\n if (res.status === 404 && this.sessionId !== null) {\n // Session expired / unknown to the server. Surface as an error so\n // McpClient can recreate; drain the body so the socket goes back\n // to the pool.\n await res.body?.cancel().catch(() => undefined);\n throw new Error(\n `MCP Streamable HTTP session expired (server returned 404 with Mcp-Session-Id \"${this.sessionId}\"). Reinitialize the client.`,\n );\n }\n\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(\n `MCP Streamable HTTP POST ${this.url} → ${res.status} ${res.statusText}${body ? `: ${body}` : \"\"}`,\n );\n }\n\n // 202 Accepted: request was a notification or pure ack — no body.\n if (res.status === 202) {\n await res.body?.cancel().catch(() => undefined);\n return;\n }\n\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n if (ct.includes(\"application/json\")) {\n let parsed: unknown;\n try {\n parsed = await res.json();\n } catch (err) {\n throw new Error(`MCP Streamable HTTP body wasn't valid JSON: ${(err as Error).message}`);\n }\n if (Array.isArray(parsed)) {\n for (const item of parsed) this.pushMessage(item as JsonRpcMessage);\n } else {\n this.pushMessage(parsed as JsonRpcMessage);\n }\n return;\n }\n\n if (ct.includes(\"text/event-stream\")) {\n // Stream may carry multiple events (progress notifications +\n // the eventual response). Read it concurrently with subsequent\n // sends — return as soon as the stream is wired so callers can\n // pipeline more requests.\n if (!res.body) {\n throw new Error(\"MCP Streamable HTTP SSE response had no body\");\n }\n const stream = this.consumeStream(res.body as AsyncIterable<Uint8Array>);\n this.streams.add(stream);\n stream.finally(() => this.streams.delete(stream));\n return;\n }\n\n // Unknown content type — drain and treat as a no-op rather than\n // hanging. Servers that want to extend the protocol should not\n // wedge older clients with an unexpected MIME.\n await res.body?.cancel().catch(() => undefined);\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n // Wait for any in-flight SSE streams to wind down so a subsequent\n // process.exit() doesn't trip on a hanging socket. Cap at \"done\";\n // controller.abort() above unblocks them.\n await Promise.allSettled(Array.from(this.streams));\n }\n\n /** Visible for tests — confirm session header round-trip. */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n private async consumeStream(body: AsyncIterable<Uint8Array>): Promise<void> {\n const parser = createParser({\n onEvent: (ev) => {\n // Per spec, server-side events use the `message` event type\n // (default if `event:` line is missing). Other event types\n // (server pings, custom extensions) we silently ignore.\n const type = ev.event ?? \"message\";\n if (type !== \"message\") return;\n try {\n const parsed = JSON.parse(ev.data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n /* malformed JSON — drop, mirror SSE behavior */\n }\n },\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of body) {\n if (this.closed) break;\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: {\n code: -32000,\n message: `Streamable HTTP stream error: ${(err as Error).message}`,\n },\n });\n }\n }\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/ui/keystroke-context.tsx","../../src/cli/ui/stdin-reader.ts","../../src/cli/ui/Select.tsx"],"sourcesContent":["/**\n * KeystrokeContext — React surface in front of the raw stdin reader.\n *\n * Replaces Ink's `useInput` chain. Reasonix's components no longer\n * import `useInput` from \"ink\"; they call `useKeystroke(handler,\n * isActive)` from this module. The provider mounted once at App\n * level owns a `StdinReader`, subscribes a single fan-out function\n * to it, and dispatches each parsed `KeyEvent` to every active\n * consumer.\n *\n * Why a Context instead of a singleton import: the provider can be\n * disabled in tests / replay mode without touching the components,\n * and the lifecycle (start/stop on mount/unmount) is tied to the\n * React tree rather than a global side effect.\n *\n * Why not just keep Ink's useInput: Ink's parse-keypress uses a\n * 100 ms intra-CSI timeout that's too short for Windows ConPTY,\n * leaking arrow-key bytes / paste markers into the buffer. Our\n * reader uses 250 ms and recognises the ESC-stripped variants too\n * — see `stdin-reader.ts`.\n */\n\nimport { useInput } from \"ink\";\n// biome-ignore lint/style/useImportType: tsconfig jsx=react needs React as a runtime value\nimport React, { createContext, useContext, useEffect, useRef } from \"react\";\nimport { type KeyEvent, type StdinReader, getStdinReader } from \"./stdin-reader.js\";\n\ninterface KeystrokeBus {\n /** Subscribe — returns an unsubscribe function. */\n subscribe(handler: KeystrokeHandler): () => void;\n}\n\nexport type KeystrokeHandler = (ev: KeyEvent) => void;\n\nconst KeystrokeContext = createContext<KeystrokeBus | null>(null);\n\nexport interface KeystrokeProviderProps {\n children: React.ReactNode;\n /**\n * Optional reader override. Tests inject a synthetic reader so\n * they can `feed()` chunks instead of touching real stdin. Production\n * callers leave this unset and get the singleton.\n */\n reader?: StdinReader;\n}\n\nexport function KeystrokeProvider({\n children,\n reader: providedReader,\n}: KeystrokeProviderProps): React.ReactElement {\n const handlersRef = useRef<Set<KeystrokeHandler>>(new Set());\n // Ref so the bus value's identity is stable across re-renders —\n // consumers don't accidentally re-subscribe every render.\n const busRef = useRef<KeystrokeBus | null>(null);\n if (busRef.current === null) {\n busRef.current = {\n subscribe(handler) {\n handlersRef.current.add(handler);\n return () => {\n handlersRef.current.delete(handler);\n };\n },\n };\n }\n\n useEffect(() => {\n const reader = providedReader ?? getStdinReader();\n reader.start();\n const unsubscribe = reader.subscribe((ev) => {\n // Snapshot the handler set so handlers added/removed during\n // dispatch don't perturb iteration. Cheap — typical N=1-3.\n for (const fn of [...handlersRef.current]) fn(ev);\n });\n return () => {\n unsubscribe();\n // Don't `stop()` the singleton on every unmount — multiple\n // mounts (test reruns, hot-reload) must not tear down stdin.\n // The singleton's own start() is idempotent; stop() is the\n // process-exit handler's job.\n };\n }, [providedReader]);\n\n return <KeystrokeContext.Provider value={busRef.current}>{children}</KeystrokeContext.Provider>;\n}\n\n/** Subscribe to keystroke events; falls back to Ink's useInput when no KeystrokeProvider is mounted. */\nexport function useKeystroke(handler: KeystrokeHandler, isActive = true): void {\n const bus = useContext(KeystrokeContext);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n if (!bus || !isActive) return undefined;\n return bus.subscribe((ev) => handlerRef.current(ev));\n }, [bus, isActive]);\n\n useInput(\n (input, key) => {\n if (bus) return;\n handlerRef.current({\n input,\n upArrow: key.upArrow,\n downArrow: key.downArrow,\n leftArrow: key.leftArrow,\n rightArrow: key.rightArrow,\n return: key.return,\n escape: key.escape,\n backspace: key.backspace,\n delete: key.delete,\n tab: key.tab,\n shift: key.shift,\n ctrl: key.ctrl,\n meta: key.meta,\n pageUp: key.pageUp,\n pageDown: key.pageDown,\n });\n },\n { isActive: !bus && isActive },\n );\n}\n\n/**\n * Lower-level hook for components that need a stable subscription\n * across the lifetime of the consumer (typically StdinReader-aware\n * unit tests).\n */\nexport function useKeystrokeBus(): KeystrokeBus | null {\n return useContext(KeystrokeContext);\n}\n\n/** Test helper — assemble a KeyEvent with sensible defaults. */\nexport function makeKeyEvent(overrides: Partial<KeyEvent> = {}): KeyEvent {\n return { input: \"\", ...overrides };\n}\n","/** Sole stdin owner; 250 ms ESC-ambiguity timer (ConPTY splits sequences past parse-keypress's 100 ms). */\n\nimport { stdin } from \"node:process\";\n\nexport interface KeyEvent {\n /** Empty for control keys (arrows / Enter / Esc); holds the letter for Ctrl+/Alt+. */\n input: string;\n upArrow?: boolean;\n downArrow?: boolean;\n leftArrow?: boolean;\n rightArrow?: boolean;\n pageUp?: boolean;\n pageDown?: boolean;\n home?: boolean;\n end?: boolean;\n delete?: boolean;\n backspace?: boolean;\n tab?: boolean;\n return?: boolean;\n escape?: boolean;\n shift?: boolean;\n ctrl?: boolean;\n meta?: boolean;\n /** Bracketed-paste content; consumers MUST NOT re-interpret as keystrokes (e.g. `\\n` ≠ submit). */\n paste?: boolean;\n /** xterm SGR mode 1006 wheel-up. */\n mouseScrollUp?: boolean;\n /** Mouse wheel down — symmetric to `mouseScrollUp`. */\n mouseScrollDown?: boolean;\n /** Left-button press; row/col are 1-based. */\n mouseClick?: boolean;\n /** Left-button motion (button held during drag). Mode 1002 only. */\n mouseDrag?: boolean;\n /** Any-button release. Mode 1002 only. */\n mouseRelease?: boolean;\n mouseRow?: number;\n mouseCol?: number;\n}\n\ntype Subscriber = (ev: KeyEvent) => void;\n\n/** ESC ambiguity timeout. Long enough for ConPTY-split sequences. */\nconst ESC_TIMEOUT_MS = 250;\n\n/** Bracketed-paste markers (DECSET 2004). */\nconst PASTE_START = \"\\x1b[200~\";\nconst PASTE_END = \"\\x1b[201~\";\n/** ESC-stripped variants — ConPTY occasionally eats the leading ESC. */\nconst PASTE_START_BARE = \"[200~\";\nconst PASTE_END_BARE = \"[201~\";\n\nconst CSI_TAIL_MAP: ReadonlyArray<{ tail: string; ev: KeyEvent }> = [\n { tail: \"A\", ev: { input: \"\", upArrow: true } },\n { tail: \"B\", ev: { input: \"\", downArrow: true } },\n { tail: \"C\", ev: { input: \"\", rightArrow: true } },\n { tail: \"D\", ev: { input: \"\", leftArrow: true } },\n { tail: \"H\", ev: { input: \"\", home: true } },\n { tail: \"F\", ev: { input: \"\", end: true } },\n { tail: \"1~\", ev: { input: \"\", home: true } },\n { tail: \"4~\", ev: { input: \"\", end: true } },\n { tail: \"5~\", ev: { input: \"\", pageUp: true } },\n { tail: \"6~\", ev: { input: \"\", pageDown: true } },\n { tail: \"3~\", ev: { input: \"\", delete: true } },\n { tail: \"Z\", ev: { input: \"\", shift: true, tab: true } },\n // Some Windows hosts (PowerShell 7.x conhost path) emit the\n // modifier-encoded back-tab `\\x1b[1;2Z` instead of bare `\\x1b[Z`.\n // Issue #373 — without this entry Shift+Tab is silently dropped.\n { tail: \"1;2Z\", ev: { input: \"\", shift: true, tab: true } },\n // modifyOtherKeys (xterm CSI > 4 ; 2 m) sequences for Enter / Tab\n // with modifiers. Only fired when App.tsx has enabled the mode at\n // startup; otherwise Shift+Enter stays indistinguishable from Enter.\n // Modifier encoding: 2=shift, 3=alt, 4=alt+shift, 5=ctrl,\n // 6=ctrl+shift, 7=ctrl+alt, 8=ctrl+alt+shift. Keycodes: 9=Tab, 13=Enter.\n { tail: \"27;2;9~\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"27;2;13~\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"27;5;13~\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"27;6;13~\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n // Kitty keyboard protocol — same idea, different envelope:\n // `\\x1b[<keycode>;<mod>u`. Some terminals (kitty, recent Windows\n // Terminal previews) prefer this shape. Harmless to map here too.\n { tail: \"9;2u\", ev: { input: \"\", tab: true, shift: true } },\n { tail: \"13;2u\", ev: { input: \"\", return: true, shift: true } },\n { tail: \"13;5u\", ev: { input: \"\", return: true, ctrl: true } },\n { tail: \"13;6u\", ev: { input: \"\", return: true, ctrl: true, shift: true } },\n];\n\n/** SS3 sequences (`\\x1bO<letter>`) — some terminals send these for arrows. */\nconst SS3_MAP: Record<string, KeyEvent> = {\n A: { input: \"\", upArrow: true },\n B: { input: \"\", downArrow: true },\n C: { input: \"\", rightArrow: true },\n D: { input: \"\", leftArrow: true },\n H: { input: \"\", home: true },\n F: { input: \"\", end: true },\n};\n\n/** ESC-stripped CSI lookahead — ConPTY occasionally drops the leading ESC. */\nfunction tryEscapelessCsi(chunk: string, i: number): { advance: number; ev: KeyEvent } | null {\n if (chunk[i] !== \"[\") return null;\n // Paste start as a special case (handled by caller).\n // Try each known tail.\n for (const entry of CSI_TAIL_MAP) {\n const candidate = `[${entry.tail}`;\n if (chunk.slice(i, i + candidate.length) === candidate) {\n return { advance: candidate.length, ev: entry.ev };\n }\n }\n return null;\n}\n\n/** `[<btn;col;row[Mm]` — SGR mouse report body (without leading ESC). */\nconst SGR_MOUSE_ESCAPELESS_RE = /^\\[<\\d+;\\d+;\\d+[Mm]/;\n\nfunction decodeSgrMouseBody(body: string): KeyEvent | null {\n const m = /^<(\\d+);(\\d+);(\\d+)([Mm])$/.exec(body);\n if (!m) return null;\n const btn = Number.parseInt(m[1]!, 10);\n const col = Number.parseInt(m[2]!, 10);\n const row = Number.parseInt(m[3]!, 10);\n if (!Number.isFinite(btn) || !Number.isFinite(col) || !Number.isFinite(row)) return null;\n const tail = m[4]!;\n if (tail === \"m\") return { input: \"\", mouseRelease: true, mouseRow: row, mouseCol: col };\n if (btn === 64) return { input: \"\", mouseScrollUp: true, mouseRow: row, mouseCol: col };\n if (btn === 65) return { input: \"\", mouseScrollDown: true, mouseRow: row, mouseCol: col };\n if (btn === 0) return { input: \"\", mouseClick: true, mouseRow: row, mouseCol: col };\n if (btn === 32) return { input: \"\", mouseDrag: true, mouseRow: row, mouseCol: col };\n return null;\n}\n\n/** ConPTY can strip the ESC off SGR mouse reports — match the bare shape and drop, issue #867. */\nfunction tryEscapelessSgrMouse(\n chunk: string,\n i: number,\n): { advance: number; ev: KeyEvent | null } | null {\n if (chunk[i] !== \"[\") return null;\n const m = SGR_MOUSE_ESCAPELESS_RE.exec(chunk.slice(i));\n if (!m) return null;\n const body = m[0].slice(1);\n return { advance: m[0].length, ev: decodeSgrMouseBody(body) };\n}\n\nfunction isCsiFinal(ch: string): boolean {\n const code = ch.charCodeAt(0);\n return code >= 0x40 && code <= 0x7e;\n}\n\n/** Unknown sequence → null → caller drops bytes silently (don't insert as text). */\nfunction lookupCsi(tail: string): KeyEvent | null {\n for (const entry of CSI_TAIL_MAP) {\n if (entry.tail === tail) return entry.ev;\n }\n return null;\n}\n\n/** modifyOtherKeys / Kitty: reconstruct the keystroke from `<codepoint>` + `<mod>`. */\nfunction decodeModifiedKey(cp: number, mod: number): KeyEvent | null {\n if (mod < 1 || mod > 8) return null;\n const bits = mod - 1;\n const shift = (bits & 1) !== 0;\n const alt = (bits & 2) !== 0;\n const ctrl = (bits & 4) !== 0;\n if (cp >= 0x20 && cp <= 0x7e && !ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp) };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x20 && cp <= 0x7e && alt && !ctrl) {\n const ev: KeyEvent = { input: String.fromCharCode(cp), meta: true };\n if (shift) ev.shift = true;\n return ev;\n }\n if (cp >= 0x41 && cp <= 0x7a && ctrl && !alt) {\n const ev: KeyEvent = { input: String.fromCharCode(cp).toLowerCase(), ctrl: true };\n if (shift) ev.shift = true;\n return ev;\n }\n return null;\n}\n\n/** Generic modifyOtherKeys / Kitty envelope — picks up the keys lookupCsi misses (`@`, `_`, `[`, `\\`, `]`, `^` under `>4;2m`). */\nfunction tryDecodeGenericCsi(seq: string): KeyEvent | null {\n let m = /^27;(\\d+);(\\d+)~$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[2]!, 10), Number.parseInt(m[1]!, 10));\n m = /^(\\d+);(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), Number.parseInt(m[2]!, 10));\n m = /^(\\d+)u$/.exec(seq);\n if (m) return decodeModifiedKey(Number.parseInt(m[1]!, 10), 1);\n return null;\n}\n\n// Bidi controls + zero-width invisibles that browsers smuggle into the clipboard (e.g. a B-site tab title with RLE/PDF wrappers). They render as 0 cells but still occupy buffer offsets, so cursor + line-split math drifts. ZWJ / ZWNJ / variation selectors / combining marks are NOT in the class — emoji sequences and accented letters keep their semantics. Issue #849.\nconst PASTE_INVISIBLE_RE = /[\\u200B\\u200E\\u200F\\u202A-\\u202E\\u2060\\u2066-\\u2069\\u00AD\\uFEFF]/g;\n\nexport function sanitizePasteText(s: string): string {\n return s.replace(PASTE_INVISIBLE_RE, \"\");\n}\n\n/** Heuristic paste-burst detector — wraps raw multi-line chunks when the terminal didn't (#522). */\nexport function looksLikeUnbracketedPaste(chunk: string): boolean {\n if (chunk.length < 2) return false;\n if (chunk.includes(PASTE_START) || chunk.includes(PASTE_START_BARE)) return false;\n if (chunk.includes(PASTE_END) || chunk.includes(PASTE_END_BARE)) return false;\n // ESC anywhere = real keypress / control sequence, not a paste burst.\n if (chunk.includes(\"\\x1b\")) return false;\n // \\r\\n is one terminal-converted Enter, not two breaks — fold first.\n const norm = chunk.replace(/\\r\\n/g, \"\\n\");\n if (norm === \"\\r\" || norm === \"\\n\") return false;\n let breaks = 0;\n let firstBreakIdx = -1;\n for (let i = 0; i < norm.length; i++) {\n const c = norm[i];\n if (c === \"\\r\" || c === \"\\n\") {\n if (firstBreakIdx < 0) firstBreakIdx = i;\n breaks++;\n }\n }\n if (breaks >= 2) return true;\n // 1 break with non-empty text on BOTH sides — paste burst. (\"abc\\r\"\n // alone stays as type-then-Enter so a fast typist still submits.)\n if (breaks === 1) return firstBreakIdx > 0 && firstBreakIdx < norm.length - 1;\n return false;\n}\n\nexport class StdinReader {\n private subscribers = new Set<Subscriber>();\n private state: \"idle\" | \"esc\" | \"csi\" | \"ss3\" | \"paste\" = \"idle\";\n /** Buffer for partial sequences across chunks. */\n private csiBuf = \"\";\n /** Buffer for paste content. */\n private pasteBuf = \"\";\n private escTimer: NodeJS.Timeout | null = null;\n // Deferred-dispatch handle paired with `escTimer`. The timer\n // queues an Immediate that runs in the event loop's CHECK phase —\n // i.e. AFTER the POLL phase where stdin 'data' events fire — so\n // a multi-byte sequence whose chunks queued up while the loop was\n // blocked (heavy render, etc.) gets a chance to be processed\n // BEFORE we emit a bogus standalone-Esc. Fixes the \"I didn't press\n // Esc but it aborted the turn\" class of bug: previously the timer's\n // setTimeout callback ran in the timers phase ahead of poll, so a\n // split sequence like `\\x1b` + `[A` would dispatch escape+upArrow\n // even though the user only pressed Up.\n private escImmediate: NodeJS.Immediate | null = null;\n private started = false;\n /** The actual `data` listener — kept as a field so `stop()` can detach it. */\n private listener: ((chunk: Buffer | string) => void) | null = null;\n\n start(): void {\n if (this.started) return;\n // bun leaves `isTTY` undefined in a real terminal, so probe setRawMode directly.\n try {\n stdin.setRawMode(true);\n } catch {\n return;\n }\n stdin.setEncoding(\"utf8\");\n stdin.resume();\n this.listener = (chunk) =>\n this.handleChunk(typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\"));\n stdin.on(\"data\", this.listener);\n this.started = true;\n }\n\n stop(): void {\n if (!this.started) return;\n if (this.listener) {\n stdin.off(\"data\", this.listener);\n this.listener = null;\n }\n try {\n stdin.setRawMode(false);\n } catch {\n // setRawMode may throw if stdin is already closed; ignore.\n }\n stdin.pause();\n this.cancelEscTimer();\n this.state = \"idle\";\n this.csiBuf = \"\";\n this.pasteBuf = \"\";\n this.started = false;\n }\n\n subscribe(fn: Subscriber): () => void {\n this.subscribers.add(fn);\n return () => {\n this.subscribers.delete(fn);\n };\n }\n\n /** Test seam — drives the parser without a real TTY. */\n feed(chunk: string): void {\n this.handleChunk(chunk);\n }\n\n private dispatch(ev: KeyEvent): void {\n for (const sub of this.subscribers) sub(ev);\n }\n\n private cancelEscTimer(): void {\n if (this.escTimer) {\n clearTimeout(this.escTimer);\n this.escTimer = null;\n }\n if (this.escImmediate) {\n clearImmediate(this.escImmediate);\n this.escImmediate = null;\n }\n }\n\n private scheduleEscTimer(): void {\n this.cancelEscTimer();\n this.escTimer = setTimeout(() => {\n this.escTimer = null;\n // Defer the actual dispatch to the CHECK phase so any pending\n // stdin 'data' events that queued up during a long render still\n // get a chance to consume the rest of a split sequence. The\n // chunk handler cancels this Immediate at its start, so a\n // sequence completing first wins; only a truly-orphaned `\\x1b`\n // reaches the dispatch below.\n this.escImmediate = setImmediate(() => {\n this.escImmediate = null;\n if (this.state === \"esc\") {\n this.state = \"idle\";\n this.dispatch({ input: \"\", escape: true });\n }\n });\n }, ESC_TIMEOUT_MS);\n }\n\n private handleChunk(rawChunk: string): void {\n this.cancelEscTimer();\n // Paste rescue when DECSET 2004 markers don't arrive (multiplexers\n // strip them, some Windows pipes too) — otherwise each \\r in a\n // multi-line paste fires Enter and the loop submits N prompts (#522).\n const chunk =\n this.state === \"idle\" && looksLikeUnbracketedPaste(rawChunk)\n ? PASTE_START + rawChunk + PASTE_END\n : rawChunk;\n let i = 0;\n while (i < chunk.length) {\n // ── paste accumulator ──\n if (this.state === \"paste\") {\n // Look for end marker (with or without ESC).\n const endA = chunk.indexOf(PASTE_END, i);\n const endB = chunk.indexOf(PASTE_END_BARE, i);\n let endIdx = -1;\n let endLen = 0;\n if (endA !== -1 && (endB === -1 || endA <= endB)) {\n endIdx = endA;\n endLen = PASTE_END.length;\n } else if (endB !== -1) {\n endIdx = endB;\n endLen = PASTE_END_BARE.length;\n }\n if (endIdx === -1) {\n this.pasteBuf += chunk.slice(i);\n i = chunk.length;\n break;\n }\n this.pasteBuf += chunk.slice(i, endIdx);\n this.dispatch({ input: sanitizePasteText(this.pasteBuf), paste: true });\n this.pasteBuf = \"\";\n this.state = \"idle\";\n i = endIdx + endLen;\n continue;\n }\n\n // ── CSI accumulator ──\n if (this.state === \"csi\") {\n const ch = chunk[i]!;\n this.csiBuf += ch;\n if (isCsiFinal(ch)) {\n this.dispatchCsi(this.csiBuf);\n this.csiBuf = \"\";\n // Only reset state if `dispatchCsi` didn't already mutate it\n // (it transitions to `paste` for the `200~` start marker —\n // resetting here would clobber that and the paste content\n // would be parsed as keystrokes).\n if (this.state === \"csi\") this.state = \"idle\";\n }\n i++;\n continue;\n }\n\n // ── SS3 single-byte tail ──\n if (this.state === \"ss3\") {\n const ev = SS3_MAP[chunk[i]!];\n if (ev) this.dispatch(ev);\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── ESC pending ──\n if (this.state === \"esc\") {\n const ch = chunk[i]!;\n if (ch === \"[\") {\n this.state = \"csi\";\n this.csiBuf = \"\";\n i++;\n continue;\n }\n if (ch === \"O\") {\n this.state = \"ss3\";\n i++;\n continue;\n }\n // Alt+Enter: ESC + CR (or ESC + LF). Universal newline shortcut on terminals\n // that don't support modifyOtherKeys (Shift+Enter falls through to plain Enter there).\n if (ch === \"\\r\" || ch === \"\\n\") {\n this.dispatch({ input: \"\", return: true, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n // ESC + any other char = Alt+key (rare; we still dispatch).\n this.dispatch({ input: ch, meta: true });\n this.state = \"idle\";\n i++;\n continue;\n }\n\n // ── idle ──\n const ch = chunk[i]!;\n\n if (ch === \"\\x1b\") {\n this.state = \"esc\";\n i++;\n continue;\n }\n\n // ESC-stripped paste-start (ConPTY): bare `[200~` at idle.\n if (chunk.slice(i, i + PASTE_START_BARE.length) === PASTE_START_BARE) {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n i += PASTE_START_BARE.length;\n continue;\n }\n // ESC-stripped CSI tails — recover before treating `[` as text.\n const escapeless = tryEscapelessCsi(chunk, i);\n if (escapeless) {\n this.dispatch(escapeless.ev);\n i += escapeless.advance;\n continue;\n }\n const mouseEscapeless = tryEscapelessSgrMouse(chunk, i);\n if (mouseEscapeless) {\n if (mouseEscapeless.ev) this.dispatch(mouseEscapeless.ev);\n i += mouseEscapeless.advance;\n continue;\n }\n\n // Single-byte control keys.\n // \\r (CR, 0x0D) is Enter on every terminal in raw mode.\n // \\n (LF, 0x0A) is what Ctrl+J emits — keep it distinct so the\n // multiline reducer can map it to \"insert newline\" instead of\n // \"submit\". Pastes containing \\n still arrive via either the\n // bracketed-paste accumulator or a multi-byte printable chunk\n // that includes the newline; neither hits this single-byte\n // branch, so this split is safe.\n if (ch === \"\\r\") {\n this.dispatch({ input: \"\", return: true });\n i++;\n continue;\n }\n if (ch === \"\\n\") {\n this.dispatch({ input: \"j\", ctrl: true });\n i++;\n continue;\n }\n if (ch === \"\\t\") {\n this.dispatch({ input: \"\", tab: true });\n i++;\n continue;\n }\n if (ch === \"\\x7f\" || ch === \"\\b\") {\n this.dispatch({ input: \"\", backspace: true });\n i++;\n continue;\n }\n if (ch === \"\\x03\") {\n // Ctrl+C — terminate the process. Raw mode disables the\n // default SIGINT, so we have to handle it ourselves.\n this.dispatch({ input: \"c\", ctrl: true });\n i++;\n continue;\n }\n\n const code = ch.charCodeAt(0);\n // Other Ctrl+letter (0x01-0x1A → A-Z, except already-handled).\n if (code >= 1 && code <= 26) {\n const letter = String.fromCharCode(0x60 + code); // a..z\n this.dispatch({ input: letter, ctrl: true });\n i++;\n continue;\n }\n\n // Regular printable input. Coalesce a run of printable chars\n // into one event so a multi-byte UTF-8 paste-burst arrives as\n // one `input` rather than N adjacent events.\n let end = i + 1;\n while (end < chunk.length) {\n const c = chunk[end]!;\n if (c === \"\\x1b\" || c === \"\\r\" || c === \"\\n\" || c === \"\\t\") break;\n if (c === \"\\x7f\" || c === \"\\b\" || c === \"\\x03\") break;\n const cc = c.charCodeAt(0);\n if (cc >= 1 && cc <= 26) break;\n // Don't swallow into a printable run if a CSI / paste prefix\n // starts at this position.\n if (c === \"[\" && (tryEscapelessCsi(chunk, end) || tryEscapelessSgrMouse(chunk, end))) break;\n if (chunk.slice(end, end + PASTE_START_BARE.length) === PASTE_START_BARE) break;\n end++;\n }\n this.dispatch({ input: chunk.slice(i, end) });\n i = end;\n }\n\n // After processing, if we're still in `esc` state, schedule the\n // ambiguity timer. The next chunk may carry the rest of the CSI;\n // if not, the timer fires and dispatches a standalone Esc.\n if (this.state === \"esc\") {\n this.scheduleEscTimer();\n }\n }\n\n private dispatchCsi(seq: string): void {\n // seq is the bytes after `\\x1b[`, e.g. \"A\", \"5~\", \"200~\", \"Z\".\n if (seq === \"200~\") {\n this.state = \"paste\";\n this.pasteBuf = \"\";\n return;\n }\n if (seq === \"201~\") {\n // Stray paste-end — we shouldn't reach here outside paste mode,\n // but if we do, drop it silently.\n return;\n }\n // SGR mouse report — surface wheel/click/drag/release, drop the rest. Always consumes the bytes even when the button isn't one we map (issue #867).\n if (seq.length > 1 && seq.charCodeAt(0) === 60 /* '<' */) {\n const ev = decodeSgrMouseBody(seq);\n if (ev) this.dispatch(ev);\n return;\n }\n const ev = lookupCsi(seq);\n if (ev) {\n this.dispatch(ev);\n return;\n }\n const generic = tryDecodeGenericCsi(seq);\n if (generic) {\n this.dispatch(generic);\n return;\n }\n // Unknown CSI → drop. Do NOT insert raw bytes as text.\n }\n}\n\n/** Singleton — one reader per process. */\nlet singleton: StdinReader | null = null;\n\nexport function getStdinReader(): StdinReader {\n if (!singleton) singleton = new StdinReader();\n return singleton;\n}\n","/** Arrow-key list components for Ink — single-select and multi-select. */\n\nimport { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\nimport { useKeystroke } from \"./keystroke-context.js\";\nimport type { KeyEvent } from \"./stdin-reader.js\";\nimport { type UiColor, useColor } from \"./theme.js\";\n\nexport interface SelectItem<V extends string = string> {\n value: V;\n label: string;\n /** Optional descriptive text rendered dimmed. */\n hint?: string;\n /** Disabled rows render dimmed and are skipped on nav. */\n disabled?: boolean;\n}\n\nexport interface SingleSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialValue?: V;\n onSubmit: (value: V) => void;\n onCancel?: () => void;\n /** Fired when Tab is pressed on the currently highlighted item. */\n onTab?: (value: V) => void;\n /** Optional dim footer beneath the list. */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function SingleSelect<V extends string>({\n items,\n initialValue,\n onSubmit,\n onTab,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: SingleSelectProps<V>) {\n const color = useColor();\n const initialIndex = Math.max(\n 0,\n items.findIndex((i) => i.value === initialValue && !i.disabled),\n );\n const [index, setIndex] = useState(initialIndex === -1 ? 0 : initialIndex);\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.return) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onSubmit(chosen.value);\n } else if (ev.tab) {\n const chosen = items[index];\n if (chosen && !chosen.disabled) onTab?.(chosen.value);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={i === index ? \"▸\" : \" \"}\n color={color}\n inlineHint={inlineHints}\n />\n ))}\n {footer ? (\n <Box marginTop={1}>\n <Text dimColor>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nexport interface MultiSelectProps<V extends string> {\n items: SelectItem<V>[];\n initialSelected?: V[];\n onSubmit: (values: V[]) => void;\n onCancel?: () => void;\n /** Footer hint under the list — e.g. \"[Space] toggle · [Enter] confirm\". */\n footer?: string;\n /** Render item hints on the same row as the label instead of a second row. */\n inlineHints?: boolean;\n /** Ignore matching keystrokes so an enclosing component can own them. */\n ignoreKey?: (ev: KeyEvent) => boolean;\n}\n\nexport function MultiSelect<V extends string>({\n items,\n initialSelected = [],\n onSubmit,\n onCancel,\n footer,\n inlineHints = false,\n ignoreKey,\n}: MultiSelectProps<V>) {\n const color = useColor();\n const [index, setIndex] = useState(() => {\n const first = items.findIndex((i) => !i.disabled);\n return first === -1 ? 0 : first;\n });\n const [selected, setSelected] = useState<Set<V>>(new Set(initialSelected));\n\n useKeystroke((ev) => {\n if (ev.paste || ignoreKey?.(ev)) return;\n if (ev.upArrow) {\n setIndex((i) => findNextEnabled(items, i, -1));\n } else if (ev.downArrow) {\n setIndex((i) => findNextEnabled(items, i, +1));\n } else if (ev.input === \" \") {\n const item = items[index];\n if (!item || item.disabled) return;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(item.value)) next.delete(item.value);\n else next.add(item.value);\n return next;\n });\n } else if (ev.return) {\n const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);\n onSubmit(ordered);\n } else if (ev.escape && onCancel) {\n onCancel();\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n {items.map((item, i) => {\n const checked = selected.has(item.value);\n const marker = checked ? \"[x]\" : \"[ ]\";\n return (\n <SelectRow\n key={item.value}\n item={item}\n active={i === index}\n marker={`${i === index ? \"▸\" : \" \"} ${marker}`}\n color={color}\n inlineHint={inlineHints}\n />\n );\n })}\n {footer ? (\n <Box marginTop={1}>\n <Text dimColor>{footer}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction SelectRow<V extends string>({\n item,\n active,\n marker,\n color,\n inlineHint = false,\n}: {\n item: SelectItem<V>;\n active: boolean;\n marker: string;\n color: UiColor;\n inlineHint?: boolean;\n}) {\n const rowColor = item.disabled ? color.info : active ? color.primary : undefined;\n const labelText = `${marker} ${item.label}`;\n if (inlineHint) {\n return (\n <Box flexDirection=\"row\" flexWrap=\"nowrap\" minHeight={1}>\n <Text color={rowColor} bold={active} dimColor={item.disabled} wrap=\"truncate\">\n {labelText}\n </Text>\n {item.hint ? <Text dimColor wrap=\"truncate\">{` ${item.hint}`}</Text> : null}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text color={rowColor} bold={active} dimColor={item.disabled}>\n {labelText}\n </Text>\n </Box>\n {item.hint ? (\n <Box paddingLeft={marker.length + 1}>\n <Text dimColor>{item.hint}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction findNextEnabled<V extends string>(\n items: SelectItem<V>[],\n from: number,\n step: -1 | 1,\n): number {\n if (items.length === 0) return 0;\n let i = from;\n for (let tries = 0; tries < items.length; tries++) {\n i = (i + step + items.length) % items.length;\n if (!items[i]?.disabled) return i;\n }\n return from;\n}\n"],"mappings":"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","names":["ch","ev","React","import_react","React"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/ui/mcp-lifecycle.ts","../../src/cli/ui/mcp-toast.ts"],"sourcesContent":["/** Formats one-liner MCP lifecycle events per `docs/design/agent-tui-terminal.html` §37. */\n\nimport { t } from \"../../i18n/index.js\";\n\nexport type McpLifecycleEvent =\n | { state: \"handshake\"; name: string }\n | {\n state: \"connected\";\n name: string;\n tools: number;\n resources?: number;\n prompts?: number;\n ms: number;\n }\n | { state: \"failed\"; name: string; reason: string }\n | { state: \"disabled\"; name: string }\n | { state: \"reconnect\"; name: string };\n\nconst STATE: Record<McpLifecycleEvent[\"state\"], { glyph: string; label: () => string }> = {\n handshake: { glyph: \"↻\", label: () => t(\"mcpLifecycle.handshake\") },\n connected: { glyph: \"✓\", label: () => t(\"mcpLifecycle.connected\") },\n failed: { glyph: \"✖\", label: () => t(\"mcpLifecycle.failed\") },\n disabled: { glyph: \"○\", label: () => t(\"mcpLifecycle.disabled\") },\n reconnect: { glyph: \"↻\", label: () => t(\"mcpLifecycle.reconnect\") },\n};\n\nconst NAME_COL = 22;\nconst STATE_COL = 15;\n\nexport function formatMcpLifecycleEvent(ev: McpLifecycleEvent): string {\n const { glyph, label } = STATE[ev.state];\n const namePart = `MCP · ${ev.name}`;\n const namePad = \" \".repeat(Math.max(1, NAME_COL - namePart.length));\n const stateField = `${glyph} ${label()}`.padEnd(STATE_COL);\n return `⌘ ${namePart}${namePad}${stateField}${describeDetail(ev)}`;\n}\n\nfunction describeDetail(ev: McpLifecycleEvent): string {\n if (ev.state === \"handshake\") return t(\"mcpLifecycle.initDetail\");\n if (ev.state === \"failed\") return ev.reason;\n if (ev.state === \"disabled\") return t(\"mcpLifecycle.disabledDetail\", { name: ev.name });\n if (ev.state === \"reconnect\") return t(\"mcpLifecycle.reconnectDetail\");\n const parts: string[] = [`${ev.tools} tools`];\n if (ev.resources && ev.resources > 0) parts.push(`${ev.resources} resources`);\n if (ev.prompts && ev.prompts > 0) parts.push(`${ev.prompts} prompts`);\n parts.push(`${ev.ms}ms`);\n return parts.join(\" · \");\n}\n","/** One-line warn toast emitted when an MCP server's p95 crosses the slow threshold (design §32). */\n\nimport { t } from \"../../i18n/index.js\";\n\nexport interface McpSlowToast {\n name: string;\n p95Ms: number;\n sampleSize: number;\n}\n\nexport function formatMcpSlowToast(tst: McpSlowToast): string {\n const seconds = (tst.p95Ms / 1000).toFixed(1);\n return t(\"mcpHealth.slowToast\", { name: tst.name, seconds, sampleSize: tst.sampleSize });\n}\n"],"mappings":";;;;;;;AAkBA,IAAM,QAAoF;AAAA,EACxF,WAAW,EAAE,OAAO,UAAK,OAAO,MAAM,EAAE,wBAAwB,EAAE;AAAA,EAClE,WAAW,EAAE,OAAO,UAAK,OAAO,MAAM,EAAE,wBAAwB,EAAE;AAAA,EAClE,QAAQ,EAAE,OAAO,UAAK,OAAO,MAAM,EAAE,qBAAqB,EAAE;AAAA,EAC5D,UAAU,EAAE,OAAO,UAAK,OAAO,MAAM,EAAE,uBAAuB,EAAE;AAAA,EAChE,WAAW,EAAE,OAAO,UAAK,OAAO,MAAM,EAAE,wBAAwB,EAAE;AACpE;AAEA,IAAM,WAAW;AACjB,IAAM,YAAY;AAEX,SAAS,wBAAwB,IAA+B;AACrE,QAAM,EAAE,OAAO,MAAM,IAAI,MAAM,GAAG,KAAK;AACvC,QAAM,WAAW,YAAS,GAAG,IAAI;AACjC,QAAM,UAAU,IAAI,OAAO,KAAK,IAAI,GAAG,WAAW,SAAS,MAAM,CAAC;AAClE,QAAM,aAAa,GAAG,KAAK,IAAI,MAAM,CAAC,GAAG,OAAO,SAAS;AACzD,SAAO,UAAK,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,eAAe,EAAE,CAAC;AAClE;AAEA,SAAS,eAAe,IAA+B;AACrD,MAAI,GAAG,UAAU,YAAa,QAAO,EAAE,yBAAyB;AAChE,MAAI,GAAG,UAAU,SAAU,QAAO,GAAG;AACrC,MAAI,GAAG,UAAU,WAAY,QAAO,EAAE,+BAA+B,EAAE,MAAM,GAAG,KAAK,CAAC;AACtF,MAAI,GAAG,UAAU,YAAa,QAAO,EAAE,8BAA8B;AACrE,QAAM,QAAkB,CAAC,GAAG,GAAG,KAAK,QAAQ;AAC5C,MAAI,GAAG,aAAa,GAAG,YAAY,EAAG,OAAM,KAAK,GAAG,GAAG,SAAS,YAAY;AAC5E,MAAI,GAAG,WAAW,GAAG,UAAU,EAAG,OAAM,KAAK,GAAG,GAAG,OAAO,UAAU;AACpE,QAAM,KAAK,GAAG,GAAG,EAAE,IAAI;AACvB,SAAO,MAAM,KAAK,QAAK;AACzB;;;ACrCO,SAAS,mBAAmB,KAA2B;AAC5D,QAAM,WAAW,IAAI,QAAQ,KAAM,QAAQ,CAAC;AAC5C,SAAO,EAAE,uBAAuB,EAAE,MAAM,IAAI,MAAM,SAAS,YAAY,IAAI,WAAW,CAAC;AACzF;","names":[]}