chainlesschain 0.162.39 → 0.162.41

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 (172) hide show
  1. package/README.md +368 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DCjoAX_u.js → AIOps-Ut7EevnG.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-XHoOmsbP.js → ActionButton-Dv6BlfJg.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics--xaFkDnL.js → Analytics-TQVQuJ7u.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CSa3FBn8.js → AppLayout-MSqLm2WK.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-ONWXiAwG.js → Audit-mw81HwVy.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CKOPNdgy.js → Backup-BQcPWDb1.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-PNj4uVqg.js → BaseInput-BYo_pwBH.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-CZCulyXV.js → Chat-zi3YUKx2.js} +5 -5
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjuJpfpV.js → ChatBubbleRenderer-DWSm1XJJ.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-jvy668lD.js → Checkbox-BvC8Erjt.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DhUebOQD.js → Codegen-C32vx0OP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-BiBvHfdT.js → Col-DMBwmqyZ.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CmEdEti-.js → Community-nDWncmKV.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-CtxpF4R5.js → Compact-lIc1HFn8.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CvPTrTAJ.js → Compliance-D14I_gd2.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-BMafGHjy.js → Cowork-BiNI-_ZL.js} +3 -3
  19. package/src/assets/web-panel/assets/{Cron-mdg_4TR1.js → Cron-N13sFzHb.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain--dGxsUvn.js → Crosschain-Dlnl0-v6.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-C9oKaCml.js → DID-CxtYS31I.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-CoGxKMvy.js → Dashboard-G4UnHlTR.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-CDDu3ZZ3.js → Dropdown-BazlxFGY.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Dy7_r9Ag.js → EmailListRenderer-BrpNdihm.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CNg6vImJ.js → FamilyGuardDashboard-HD7jbOOR.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-CT61bf3u.js → Federation-Bz8lzAGI.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-CSLRnXhg.js → FormItemContext-CcyzGS00.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-CZ4NE5N3.js → GenericCardRenderer-DRo9cwmp.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-DBuOma3L.js → Git-B7bn333J.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-BTU_SEef.js → Governance-DZX9CWAM.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-47SAmLC_.js → Inference-B3XhsL6W.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-DCrK5vP4.js → KnowledgeGraph-CxFRTlQe.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-BqiDxdav.js → Logs-xuys6mKH.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CReUjsDt.js → Marketplace-CXyxv4WU.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-agZBV3p8.js → McpTools-BzZLQVI3.js} +6 -6
  36. package/src/assets/web-panel/assets/{Memory-C_YvUtyS.js → Memory-BANtaBa7.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-41fP1Tui.js → MobileBridge-BJIwjmxr.js} +3 -3
  38. package/src/assets/web-panel/assets/{MobileProjects-BkqLvGfL.js → MobileProjects-B857uSAZ.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-JFJCXUnk.js → Mtc-Cn7ceFEz.js} +5 -5
  40. package/src/assets/web-panel/assets/{MtcAudit-BHNpPZC9.js → MtcAudit-B0zE978G.js} +6 -6
  41. package/src/assets/web-panel/assets/{Multisig-DuCRumiz.js → Multisig-CQFT0wXW.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-DK-g0fKY.js → NLProgramming-DSxKdVY-.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-BSMcjsPf.js → Notes-DtlTfam8.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-9ouC118H.js → NotificationSettings-CHQwayAg.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-Brpmzh9n.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-DSV7oRnR.js → Organization-nF_tzZDT.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-DVkkORc3.js → Overflow-CgCSf_PH.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-BXXjkkQD.js → P2P-Bvn46bLY.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-O5hNnLTP.js → PdhVaultBrowser-Bzl9k7Gj.js} +5 -5
  50. package/src/assets/web-panel/assets/{Permissions-D_s0H5Av.js → Permissions-Dmezbuo8.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-CzMDrwUi.js → PersonalDataHub-lCKRxwZr.js} +3 -3
  52. package/src/assets/web-panel/assets/{Pipeline-i9krLVTL.js → Pipeline-DDCGm9PA.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-cMQcj9I8.js → Privacy-Cgu18Kjl.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-Ca_l7avo.js → ProjectInit-CkF1AeRY.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-BkaIhd6b.js → ProjectSettings-D0Q-orz1.js} +2 -2
  56. package/src/assets/web-panel/assets/Projects-KfGELrSY.js +1 -0
  57. package/src/assets/web-panel/assets/{Providers-D0nzYiqz.js → Providers-BACLV0z8.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-Bzzr9d0f.js → QuickAsk-CPsZUqDl.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-C-UFbQnX.js → Recommend-5jX0OI1-.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-BKMIKO5F.js → Reputation-5JKv54z0.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-Bs7htK1T.js → Row-DLiTF5LY.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-v6MdULUh.js → RssFeed-CFdGmCKW.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-DlRWYzvz.js → Search-BjIOnmA7.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-DXWO37xX.js → Security-BujPqQSo.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-C2tWA-O0.js → Services-ChciPnMu.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-Q8pIYY4a.js → Skeleton-Cwswp1Jv.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-D7XBlErj.js → Skills-CtwR4vJV.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-CiyMVPJ1.js → Sla-pRIevich.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CadCeeiR.js → SpeechSettings-BRqB28Ai.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-DzNAUhQq.js → SyncSettings-BYyj58_h.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-DTLpT48U.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-DfgEpUa4.js → Templates-Bbz_h7oW.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-C8ajkuYi.js → Tenant-D-H4E3cu.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-B9rHwQQx.js → Terminal-CLLi0-lV.js} +2 -2
  75. package/src/assets/web-panel/assets/{TimelineRenderer-D1ZVNezX.js → TimelineRenderer-BKI6eG0k.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-CAkED4mx.js → Tokens-rsE_yDjM.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-CJSrm6X0.js → Trigger-8TpwuTGk.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-B-TeorSk.js → Trust-sMtZkHPs.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-Di7Ymofy.js → UkeySign-BAy2bAdG.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-DM1eYNZe.js → VideoEditing-CBeR_DYK.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-DvRWkbmR.js → Wallet-BymDnBcq.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-CeZ3Y622.js → WebAuthn-DQIjmqNz.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-Cq8c4h5j.js → WorkflowEditor-Cj7PB73f.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-7-WfML6Q.js → chat-DYnGj4vi.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-D6FgCmB-.js → colors-qOLKZNvN.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-ClYV25qi.js → compact-item-BpjCLPcW.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-CDhtjdkV.js → createContext-CfakUZVQ.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-DgtRXlrj.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-DZSH5LQd.js → hasIn-C9RW1s7t.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-B4PMzmOx.js → index-8Ia91vNV.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-B78X5S22.js → index-B4kS312z.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-CDX4QU3k.js → index-BE67I0SW.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DPEYvNvq.js → index-BFOSDeeo.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CKgS8E_X.js → index-BIz-pX0k.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Di9pFrHV.js → index-BJoWi1aR.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BHeK8I5A.js → index-B_K0YtG2.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BpzOUiSb.js → index-BdR8XRyF.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DWRoh3_3.js → index-BfyRXPyV.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-C7pQa2is.js → index-Bl5LBZJM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DZ4zuoCP.js → index-BlxRICmz.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B_mMFQ4S.js → index-BxiHBsfU.js} +1 -1
  102. package/src/assets/web-panel/assets/{index---azBCXl.js → index-C2S1hUWG.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BJ7mrOaB.js → index-CEHyZ77C.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CxwfFZ1u.js → index-CJZ2noI2.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DGj1orXm.js → index-COYEuArt.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DL6GFJAd.js → index-CVZTLSL1.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-z-R0KaJS.js → index-CbnJ6FsO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-tU6pZ1TP.js → index-CvWFTG56.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-rCs9VJJp.js → index-D-RzTqlR.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B6VWGnwq.js → index-DA80prWe.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D0YzTJJO.js → index-DAjszh8P.js} +1 -1
  112. package/src/assets/web-panel/assets/index-DIGTMmnW.js +1 -0
  113. package/src/assets/web-panel/assets/{index-DjG82V0v.js → index-DQvVYNoJ.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DLizxxId.js → index-DSWdpR3c.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C7sC56w8.js → index-DadPmrxI.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BlBF_l8m.js → index-DgMJagCq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bj8hZiyL.js → index-DkmLJFE_.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CrTmxbL8.js → index-DzXYG5YJ.js} +1 -1
  119. package/src/assets/web-panel/assets/index-Ef5jERRW.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BUOPjAUM.js → index-JkOMWGMX.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CmU631Je.js → index-T3bIqK_p.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-BqOIoEo6.js → index-UiiqS5k2.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CSjoWPxB.js → index-VYIJmPvJ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B13QnrnE.js → index-ZCtDWP2C.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DgaF1F0W.js → index-f9yoj84i.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Or_McYjX.js → index-lPc7EzUi.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DGJK8D0l.js → index-m9JeDv6B.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CWOkL-8O.js → index-qf0fAus7.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-CSdsIGy3.js → initDefaultProps-DgsgQr1H.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Do-AcZV4.js → motion-TeUH7wzx.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-BmgOoMsi.js → move-DdkIeWQx.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-D4Tm7-s9.js → omit-BH_PH6HT.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-CuWA8-lj.js → pickAttrs-CllCh-Nl.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-BSbEF5op.js → placementArrow-BCjE2AzM.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-GIMJwB_9.js → responsiveObserve-BAVGAvRQ.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DlZxpIBe.js → slide-D4ZW-Inn.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-BZ26LPlh.js → statusUtils-j4pxhmKV.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-Yn_3FZ0l.js → styleChecker-DH2SLtPg.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-O_LOE1AB.js → useFlexGapSupport-CYMMs-_Q.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-VFMyQqtl.js → useFs-BOX2ddKh.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-B_hyrGB-.js → usePersonalDataHub-BwcnN5z_.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-D4LttGy7.js → vnode-Cwalh7Hj.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-KnTK1fjj.js → zoom-B2_q_nbu.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +38 -4
  146. package/src/commands/init.js +115 -2
  147. package/src/commands/mcp.js +57 -0
  148. package/src/commands/memory.js +62 -0
  149. package/src/commands/session.js +106 -12
  150. package/src/index.js +10 -0
  151. package/src/lib/agent-core.js +1 -0
  152. package/src/lib/agent-session-export.js +124 -0
  153. package/src/lib/ide-context.js +62 -0
  154. package/src/lib/init-ai-refine.js +66 -0
  155. package/src/lib/json-schema-output.js +181 -0
  156. package/src/lib/mcp-serve.js +259 -0
  157. package/src/lib/project-instructions.js +364 -0
  158. package/src/lib/project-inventory.js +355 -0
  159. package/src/lib/repl-bang-memorize.js +142 -0
  160. package/src/lib/repl-completer.js +25 -4
  161. package/src/lib/repl-rewind.js +107 -0
  162. package/src/lib/update-notice-refresh.mjs +10 -0
  163. package/src/lib/update-notice.js +154 -0
  164. package/src/repl/agent-repl.js +263 -1
  165. package/src/runtime/agent-core.js +162 -0
  166. package/src/runtime/system-prompt.js +21 -1
  167. package/src/assets/web-panel/assets/OrderTableRenderer-LG2nUO5y.js +0 -1
  168. package/src/assets/web-panel/assets/Projects-Dy9yNmDg.js +0 -1
  169. package/src/assets/web-panel/assets/Tasks-BjdHjZeb.js +0 -1
  170. package/src/assets/web-panel/assets/devWarning-O0FVFeZg.js +0 -1
  171. package/src/assets/web-panel/assets/index--ANIKvhL.js +0 -1
  172. package/src/assets/web-panel/assets/index-DUfp4rnQ.js +0 -1
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Agent-session Markdown export — Claude-Code `/export` parity for the JSONL
3
+ * headless/agent session store (`~/.../sessions/<id>.jsonl`, the `--resume`
4
+ * sessions). `cc session export` keeps serving chat-DB sessions and falls
5
+ * back to this renderer when the id only exists in the JSONL store.
6
+ *
7
+ * Pure function over the event list (see jsonl-session-store.js appendEvent):
8
+ * { type, timestamp, data }
9
+ * types: session_start{title,provider,model} · user_message · assistant_message
10
+ * · system · tool_call{tool,args} · tool_result{tool,result}
11
+ * · compact(stats) · token_usage{...}
12
+ */
13
+
14
+ export const TOOL_BLOCK_CAP = 4000;
15
+
16
+ function asText(content) {
17
+ if (content == null) return "";
18
+ if (typeof content === "string") return content;
19
+ try {
20
+ return JSON.stringify(content, null, 2);
21
+ } catch {
22
+ return String(content);
23
+ }
24
+ }
25
+
26
+ function fence(body, lang = "") {
27
+ const text = asText(body);
28
+ const capped =
29
+ text.length > TOOL_BLOCK_CAP
30
+ ? `${text.slice(0, TOOL_BLOCK_CAP)}\n… [truncated]`
31
+ : text;
32
+ // avoid breaking out of the fence when the payload contains ```
33
+ const guard = capped.includes("```") ? "````" : "```";
34
+ return `${guard}${lang}\n${capped}\n${guard}`;
35
+ }
36
+
37
+ /** Render a JSONL agent session as a readable Markdown transcript. */
38
+ export function renderAgentSessionMarkdown(sessionId, events, opts = {}) {
39
+ const L = [];
40
+ const start = events.find((e) => e?.type === "session_start");
41
+ const firstTs = events.find((e) => Number.isFinite(e?.timestamp))?.timestamp;
42
+
43
+ L.push(`# Agent Session ${sessionId}`);
44
+ L.push("");
45
+ const metaBits = [];
46
+ if (start?.data?.title && start.data.title !== "Untitled")
47
+ metaBits.push(`title: ${start.data.title}`);
48
+ if (start?.data?.provider) metaBits.push(`provider: ${start.data.provider}`);
49
+ if (start?.data?.model) metaBits.push(`model: ${start.data.model}`);
50
+ if (firstTs) metaBits.push(`started: ${new Date(firstTs).toISOString()}`);
51
+ if (opts.exportedAt) metaBits.push(`exported: ${opts.exportedAt}`);
52
+ if (metaBits.length) {
53
+ L.push(`> ${metaBits.join(" · ")}`);
54
+ L.push("");
55
+ }
56
+
57
+ let users = 0;
58
+ let assistants = 0;
59
+ let tokensIn = 0;
60
+ let tokensOut = 0;
61
+
62
+ for (const ev of events) {
63
+ if (!ev || typeof ev !== "object") continue;
64
+ switch (ev.type) {
65
+ case "user_message":
66
+ users += 1;
67
+ L.push("## 👤 User");
68
+ L.push("");
69
+ L.push(asText(ev.data?.content));
70
+ L.push("");
71
+ break;
72
+ case "assistant_message":
73
+ assistants += 1;
74
+ L.push("## 🤖 Assistant");
75
+ L.push("");
76
+ L.push(asText(ev.data?.content));
77
+ L.push("");
78
+ break;
79
+ case "system":
80
+ L.push("## ⚙ System");
81
+ L.push("");
82
+ L.push(asText(ev.data?.content));
83
+ L.push("");
84
+ break;
85
+ case "tool_call":
86
+ L.push(`**🔧 tool_call — \`${ev.data?.tool || "?"}\`**`);
87
+ L.push("");
88
+ L.push(fence(ev.data?.args, "json"));
89
+ L.push("");
90
+ break;
91
+ case "tool_result":
92
+ L.push(`**↩ tool_result — \`${ev.data?.tool || "?"}\`**`);
93
+ L.push("");
94
+ L.push(fence(ev.data?.result));
95
+ L.push("");
96
+ break;
97
+ case "compact": {
98
+ const s = ev.data || {};
99
+ const saved = s.savedTokens ?? s.saved ?? null;
100
+ L.push(
101
+ `> ⊟ context compacted${saved != null ? ` — saved ~${saved} tokens` : ""}`,
102
+ );
103
+ L.push("");
104
+ break;
105
+ }
106
+ case "token_usage": {
107
+ const d = ev.data || {};
108
+ tokensIn += Number(d.inputTokens ?? d.input_tokens ?? 0) || 0;
109
+ tokensOut += Number(d.outputTokens ?? d.output_tokens ?? 0) || 0;
110
+ break;
111
+ }
112
+ default:
113
+ break; // session_start handled above; unknown types skipped
114
+ }
115
+ }
116
+
117
+ L.push("---");
118
+ const totals = [`${users} user / ${assistants} assistant turns`];
119
+ if (tokensIn || tokensOut)
120
+ totals.push(`tokens in/out: ${tokensIn}/${tokensOut}`);
121
+ L.push(`_${totals.join(" · ")}_`);
122
+ L.push("");
123
+ return L.join("\n");
124
+ }
@@ -251,6 +251,68 @@ export async function collectIdeDiagnostics(mcp, filePath, opts = {}) {
251
251
  return relevant.length > 0 ? relevant : null;
252
252
  }
253
253
 
254
+ // ─── IDE-native diff approval (Claude-Code parity) ──────────────────────────
255
+ //
256
+ // When a permission `ask` fires for a file edit and an IDE bridge is up, the
257
+ // confirmation can be the editor's own openDiff review instead of a terminal
258
+ // y/N. The openDiff contract (extension P2): it BLOCKS until the user decides;
259
+ // on Accept the IDE itself writes the (possibly user-edited) right-hand text
260
+ // to the file — so an accepted review REPLACES the tool's own write, it does
261
+ // not precede it. The caller must skip normal execution on "accepted".
262
+
263
+ /** Env kill-switch for diff-approval routing: CC_IDE_DIFF_APPROVAL=0 disables. */
264
+ export function ideDiffApprovalEnabled(env = process.env) {
265
+ const v = String(env?.CC_IDE_DIFF_APPROVAL ?? "").toLowerCase();
266
+ return !(v === "0" || v === "false" || v === "off");
267
+ }
268
+
269
+ /** Does this MCP surface expose the IDE bridge's openDiff tool? */
270
+ export function hasIdeOpenDiff(mcp) {
271
+ return !!(
272
+ mcp?.mcpClient?.callTool &&
273
+ mcp.externalToolExecutors?.mcp__ide__openDiff?.kind === "mcp"
274
+ );
275
+ }
276
+
277
+ /**
278
+ * Run one blocking openDiff review in the connected IDE. Returns
279
+ * { outcome:"accepted", finalText|null } — the IDE wrote the file itself
280
+ * { outcome:"rejected" } — nothing was written
281
+ * null — IDE unavailable / transport
282
+ * error / malformed reply → the
283
+ * caller falls back to its normal
284
+ * confirmation path.
285
+ * Deliberately NO timeout: a review takes as long as the user takes (the MCP
286
+ * HTTP client has no request timeout; the extension holds the response open).
287
+ */
288
+ export async function requestIdeDiffApproval(mcp, req = {}) {
289
+ if (!hasIdeOpenDiff(mcp)) return null;
290
+ if (!req.path || typeof req.modifiedText !== "string") return null;
291
+ const exec = mcp.externalToolExecutors.mcp__ide__openDiff;
292
+ let result;
293
+ try {
294
+ result = await mcp.mcpClient.callTool(exec.serverName, exec.toolName, {
295
+ path: req.path,
296
+ modifiedText: req.modifiedText,
297
+ ...(typeof req.originalText === "string"
298
+ ? { originalText: req.originalText }
299
+ : {}),
300
+ ...(req.title ? { title: req.title } : {}),
301
+ });
302
+ } catch {
303
+ return null;
304
+ }
305
+ const data = parseToolResultJson(result);
306
+ if (data?.outcome === "accepted") {
307
+ return {
308
+ outcome: "accepted",
309
+ finalText: typeof data.finalText === "string" ? data.finalText : null,
310
+ };
311
+ }
312
+ if (data?.outcome === "rejected") return { outcome: "rejected" };
313
+ return null; // anything else is not a verdict — fail safe to fallback
314
+ }
315
+
254
316
  /**
255
317
  * Render pulled diagnostics as a compact feedback string for the tool result.
256
318
  * Returns null when there is nothing to report.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `cc init --ai` — agent-enhanced inventory (claude /init parity, module 99
3
+ * §5.2). Runs AFTER the offline census wrote the starter cc.md: a bounded
4
+ * headless agent reads the repo (README, entry files) and rewrites cc.md so
5
+ * the Conventions section holds real, observed conventions instead of the
6
+ * "(add project rules here)" placeholder.
7
+ *
8
+ * Self-reference guard: the child run executes with CC_PROJECT_MEMORY=0 so
9
+ * the half-baked cc.md being refined is NOT injected into the very agent
10
+ * refining it. Restored in finally.
11
+ *
12
+ * Offline inventory stays the `cc init` default — this is strictly opt-in
13
+ * (needs a reachable LLM).
14
+ */
15
+
16
+ export const AI_REFINE_MAX_TURNS = 12;
17
+ export const AI_REFINE_TOOLS = [
18
+ "read_file",
19
+ "list_dir",
20
+ "search_files",
21
+ "write_file",
22
+ ];
23
+
24
+ export function buildRefinePrompt() {
25
+ return [
26
+ "You are refining this project's memory file `cc.md` (it was just generated from an offline folder census).",
27
+ "Steps:",
28
+ "1. read_file cc.md to see the current draft.",
29
+ "2. Read the README and one or two key entry/config files to learn the project's real conventions (code style, commit format, how to run tests, architecture notes worth remembering).",
30
+ "3. write_file cc.md with the refined version: KEEP the existing sections and data, but replace the placeholder under `## Conventions` with concrete, observed conventions (5-10 short bullets max). Do not invent facts you did not see.",
31
+ "Keep the file concise — it is loaded into every agent run.",
32
+ ].join("\n");
33
+ }
34
+
35
+ /**
36
+ * Run the refine pass.
37
+ * @param {object} opts { cwd, provider?, model?, baseUrl?, apiKey?,
38
+ * maxTurns?, runHeadless? (test seam) }
39
+ * @returns {Promise<{exitCode:number, result:string, isError:boolean}>}
40
+ */
41
+ export async function aiRefineMemoryFile(opts = {}) {
42
+ const run =
43
+ opts.runHeadless ||
44
+ (await import("../runtime/headless-runner.js")).runAgentHeadless;
45
+
46
+ const prev = process.env.CC_PROJECT_MEMORY;
47
+ process.env.CC_PROJECT_MEMORY = "0"; // self-reference guard
48
+ try {
49
+ return await run({
50
+ prompt: buildRefinePrompt(),
51
+ cwd: opts.cwd || process.cwd(),
52
+ provider: opts.provider || undefined,
53
+ model: opts.model || undefined,
54
+ baseUrl: opts.baseUrl || undefined,
55
+ apiKey: opts.apiKey || undefined,
56
+ permissionMode: "acceptEdits", // write_file must work headless
57
+ allowedTools: AI_REFINE_TOOLS,
58
+ maxTurns: opts.maxTurns || AI_REFINE_MAX_TURNS,
59
+ expandFileRefs: false,
60
+ outputFormat: "text",
61
+ });
62
+ } finally {
63
+ if (prev === undefined) delete process.env.CC_PROJECT_MEMORY;
64
+ else process.env.CC_PROJECT_MEMORY = prev;
65
+ }
66
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `cc agent -p --json-schema <file>` — structured output for headless runs.
3
+ *
4
+ * The final answer must be JSON that validates against a (subset) JSON
5
+ * Schema; invalid replies are retried with a corrective prompt (up to
6
+ * MAX_ATTEMPTS total). Implemented entirely AROUND runAgentHeadless using its
7
+ * `deps.writeOut` capture seam — the runner itself is untouched: each attempt
8
+ * runs with output captured, the validated JSON is the only thing printed.
9
+ *
10
+ * Validator subset (enough for tool/script contracts, not full draft-2020):
11
+ * type (object/array/string/number/integer/boolean/null), properties,
12
+ * required, items, enum, const, additionalProperties:false. Zero deps.
13
+ */
14
+
15
+ import fsDefault from "fs";
16
+
17
+ export const MAX_ATTEMPTS = 3;
18
+ export const _deps = { fs: fsDefault };
19
+
20
+ /** Validate `value` against the schema subset. Returns error strings ([] = valid). */
21
+ export function validateAgainstSchema(value, schema, path = "$") {
22
+ const errors = [];
23
+ if (!schema || typeof schema !== "object") return errors;
24
+
25
+ const typeOf = (v) =>
26
+ v === null
27
+ ? "null"
28
+ : Array.isArray(v)
29
+ ? "array"
30
+ : typeof v === "number" && Number.isInteger(v)
31
+ ? "integer"
32
+ : typeof v;
33
+
34
+ if (schema.type) {
35
+ const want = Array.isArray(schema.type) ? schema.type : [schema.type];
36
+ const got = typeOf(value);
37
+ const ok = want.some((t) => t === got || (t === "number" && got === "integer"));
38
+ if (!ok) {
39
+ errors.push(`${path}: expected type ${want.join("|")}, got ${got}`);
40
+ return errors; // type mismatch — deeper checks are noise
41
+ }
42
+ }
43
+ if (schema.enum && !schema.enum.some((e) => JSON.stringify(e) === JSON.stringify(value))) {
44
+ errors.push(`${path}: value not in enum [${schema.enum.map((e) => JSON.stringify(e)).join(", ")}]`);
45
+ }
46
+ if (schema.const !== undefined && JSON.stringify(schema.const) !== JSON.stringify(value)) {
47
+ errors.push(`${path}: must equal const ${JSON.stringify(schema.const)}`);
48
+ }
49
+ if (typeOf(value) === "object" && !Array.isArray(value)) {
50
+ for (const req of schema.required || []) {
51
+ if (!(req in value)) errors.push(`${path}: missing required property "${req}"`);
52
+ }
53
+ const props = schema.properties || {};
54
+ for (const [k, v] of Object.entries(value)) {
55
+ if (props[k]) {
56
+ errors.push(...validateAgainstSchema(v, props[k], `${path}.${k}`));
57
+ } else if (schema.additionalProperties === false) {
58
+ errors.push(`${path}: unexpected property "${k}"`);
59
+ }
60
+ }
61
+ }
62
+ if (Array.isArray(value) && schema.items) {
63
+ value.forEach((item, i) => {
64
+ errors.push(...validateAgainstSchema(item, schema.items, `${path}[${i}]`));
65
+ });
66
+ }
67
+ return errors;
68
+ }
69
+
70
+ /** Pull a JSON payload out of an LLM reply (bare, fenced, or embedded). */
71
+ export function extractJsonPayload(text) {
72
+ const raw = String(text || "").trim();
73
+ const tries = [];
74
+ tries.push(raw);
75
+ const fence = /```(?:json)?\s*([\s\S]*?)```/i.exec(raw);
76
+ if (fence) tries.push(fence[1].trim());
77
+ const firstObj = raw.indexOf("{");
78
+ const lastObj = raw.lastIndexOf("}");
79
+ if (firstObj !== -1 && lastObj > firstObj) tries.push(raw.slice(firstObj, lastObj + 1));
80
+ const firstArr = raw.indexOf("[");
81
+ const lastArr = raw.lastIndexOf("]");
82
+ if (firstArr !== -1 && lastArr > firstArr) tries.push(raw.slice(firstArr, lastArr + 1));
83
+ for (const candidate of tries) {
84
+ if (!candidate) continue;
85
+ try {
86
+ return { ok: true, value: JSON.parse(candidate) };
87
+ } catch {
88
+ /* next candidate */
89
+ }
90
+ }
91
+ return { ok: false, error: "reply contains no parseable JSON" };
92
+ }
93
+
94
+ export function buildSchemaInstruction(schema) {
95
+ return [
96
+ "OUTPUT CONTRACT: your FINAL reply must be ONLY a JSON value (no prose, no markdown fences) that validates against this JSON Schema:",
97
+ JSON.stringify(schema),
98
+ ].join("\n");
99
+ }
100
+
101
+ export function buildRetryPrompt(originalPrompt, raw, errors) {
102
+ return [
103
+ originalPrompt,
104
+ "",
105
+ "Your previous reply failed JSON Schema validation:",
106
+ ...errors.slice(0, 10).map((e) => `- ${e}`),
107
+ "",
108
+ `Previous reply (for reference): ${String(raw).slice(0, 2000)}`,
109
+ "",
110
+ "Reply again with ONLY the corrected JSON.",
111
+ ].join("\n");
112
+ }
113
+
114
+ /**
115
+ * Run a headless turn constrained to a schema, retrying on validation
116
+ * failure. Prints the validated JSON to writeOut; returns the exit code.
117
+ *
118
+ * @param {object} cfg { schemaFile|schema, baseOptions, runHeadless,
119
+ * maxAttempts?, writeOut?, writeErr?, deps? }
120
+ */
121
+ export async function runJsonSchemaConstrained(cfg = {}) {
122
+ const fs = cfg.deps?.fs || _deps.fs;
123
+ const writeOut = cfg.writeOut || ((s) => process.stdout.write(s));
124
+ const writeErr = cfg.writeErr || ((s) => process.stderr.write(s));
125
+ const maxAttempts = cfg.maxAttempts || MAX_ATTEMPTS;
126
+
127
+ const schema =
128
+ cfg.schema || JSON.parse(fs.readFileSync(cfg.schemaFile, "utf-8"));
129
+ const instruction = buildSchemaInstruction(schema);
130
+ const base = cfg.baseOptions || {};
131
+
132
+ let prompt = base.prompt;
133
+ let lastRaw = "";
134
+ let lastErrors = ["no attempts ran"];
135
+
136
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
137
+ let captured = "";
138
+ const outcome = await cfg.runHeadless(
139
+ {
140
+ ...base,
141
+ prompt,
142
+ outputFormat: "text",
143
+ appendSystemPrompt: [base.appendSystemPrompt, instruction]
144
+ .filter(Boolean)
145
+ .join("\n\n"),
146
+ },
147
+ {
148
+ writeOut: (s) => {
149
+ captured += s;
150
+ },
151
+ writeErr,
152
+ },
153
+ );
154
+ const raw = String(outcome?.result ?? captured ?? "").trim() || captured.trim();
155
+ lastRaw = raw;
156
+ const parsed = extractJsonPayload(raw);
157
+ if (parsed.ok) {
158
+ const errors = validateAgainstSchema(parsed.value, schema);
159
+ if (errors.length === 0) {
160
+ writeOut(`${JSON.stringify(parsed.value, null, 2)}\n`);
161
+ return 0;
162
+ }
163
+ lastErrors = errors;
164
+ } else {
165
+ lastErrors = [parsed.error];
166
+ }
167
+ if (attempt < maxAttempts) {
168
+ writeErr(
169
+ `--json-schema: attempt ${attempt} failed validation (${lastErrors.length} error(s)) — retrying…\n`,
170
+ );
171
+ prompt = buildRetryPrompt(base.prompt, raw, lastErrors);
172
+ }
173
+ }
174
+
175
+ writeErr(
176
+ `--json-schema: reply failed validation after ${maxAttempts} attempts:\n${lastErrors
177
+ .map((e) => ` - ${e}`)
178
+ .join("\n")}\nLast reply:\n${lastRaw.slice(0, 1000)}\n`,
179
+ );
180
+ return 1;
181
+ }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * `cc mcp serve` — expose cc's local file tools as an MCP server so OTHER
3
+ * MCP clients (Claude Desktop, another cc, any Streamable-HTTP client) can
4
+ * use this machine's workspace. Claude-Code `claude mcp serve` parity.
5
+ *
6
+ * Protocol: Streamable-HTTP MCP, same shape the IDE-bridge work verified
7
+ * against the real CLI MCPClient — every request is POST JSON-RPC answered
8
+ * with application/json (no persistent SSE GET needed): `initialize`,
9
+ * `notifications/initialized`, `tools/list`, `tools/call`; tool failures are
10
+ * `isError` results, transport failures JSON-RPC errors.
11
+ *
12
+ * Security: tools are CONFINED to the serve root (path resolves inside root
13
+ * or the call fails), Bearer-token auth is on by default (random token,
14
+ * printed once), `--read-only` drops write_file. Zero npm deps (node http).
15
+ */
16
+
17
+ import http from "http";
18
+ import fsDefault from "fs";
19
+ import pathDefault from "path";
20
+ import { randomBytes } from "crypto";
21
+
22
+ export const MAX_READ_BYTES = 200 * 1024;
23
+ export const MAX_LIST_ENTRIES = 500;
24
+ export const MAX_SEARCH_RESULTS = 200;
25
+ export const MAX_SEARCH_ENTRIES = 50_000;
26
+ const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build"]);
27
+
28
+ export const _deps = { fs: fsDefault, path: pathDefault };
29
+
30
+ /** Resolve `rel` inside `root`; throws on escape (.. traversal, abs paths out). */
31
+ export function confine(root, rel, deps = _deps) {
32
+ const abs = deps.path.resolve(root, rel || ".");
33
+ const normRoot = deps.path.resolve(root);
34
+ if (abs !== normRoot && !abs.startsWith(normRoot + deps.path.sep)) {
35
+ throw new Error(`path escapes serve root: ${rel}`);
36
+ }
37
+ return abs;
38
+ }
39
+
40
+ function ok(text) {
41
+ return { content: [{ type: "text", text: String(text) }] };
42
+ }
43
+ function fail(message) {
44
+ return { content: [{ type: "text", text: String(message) }], isError: true };
45
+ }
46
+
47
+ /** Build the tool table (name → {description, inputSchema, handler}). */
48
+ export function buildTools({ root, readOnly = false, deps = _deps }) {
49
+ const fs = deps.fs;
50
+ const tools = {
51
+ read_file: {
52
+ description: `Read a UTF-8 file under the serve root (${MAX_READ_BYTES} byte cap)`,
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: { path: { type: "string" } },
56
+ required: ["path"],
57
+ },
58
+ handler: ({ path: rel }) => {
59
+ const abs = confine(root, rel, deps);
60
+ const buf = fs.readFileSync(abs);
61
+ const truncated = buf.length > MAX_READ_BYTES;
62
+ const text = (truncated ? buf.slice(0, MAX_READ_BYTES) : buf).toString(
63
+ "utf-8",
64
+ );
65
+ return ok(truncated ? `${text}\n… [truncated ${buf.length} bytes]` : text);
66
+ },
67
+ },
68
+ list_dir: {
69
+ description: "List a directory under the serve root (dirs get trailing /)",
70
+ inputSchema: {
71
+ type: "object",
72
+ properties: { path: { type: "string" } },
73
+ },
74
+ handler: ({ path: rel } = {}) => {
75
+ const abs = confine(root, rel || ".", deps);
76
+ const entries = fs
77
+ .readdirSync(abs, { withFileTypes: true })
78
+ .slice(0, MAX_LIST_ENTRIES)
79
+ .map((e) => (e.isDirectory() ? `${e.name}/` : e.name));
80
+ return ok(entries.join("\n"));
81
+ },
82
+ },
83
+ search_files: {
84
+ description:
85
+ "Find files under the serve root whose RELATIVE PATH contains the query (case-insensitive, bounded walk)",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ query: { type: "string" },
90
+ dir: { type: "string" },
91
+ },
92
+ required: ["query"],
93
+ },
94
+ handler: ({ query, dir } = {}) => {
95
+ const base = confine(root, dir || ".", deps);
96
+ const q = String(query).toLowerCase();
97
+ const hits = [];
98
+ let seen = 0;
99
+ const walk = (d) => {
100
+ if (hits.length >= MAX_SEARCH_RESULTS || seen >= MAX_SEARCH_ENTRIES)
101
+ return;
102
+ let list;
103
+ try {
104
+ list = fs.readdirSync(d, { withFileTypes: true });
105
+ } catch {
106
+ return;
107
+ }
108
+ for (const e of list) {
109
+ if (hits.length >= MAX_SEARCH_RESULTS || ++seen >= MAX_SEARCH_ENTRIES)
110
+ return;
111
+ const abs = deps.path.join(d, e.name);
112
+ if (e.isDirectory()) {
113
+ if (!SKIP_DIRS.has(e.name) && !e.name.startsWith("."))
114
+ walk(abs);
115
+ } else {
116
+ const rel = deps.path.relative(root, abs).replace(/\\/g, "/");
117
+ if (rel.toLowerCase().includes(q)) hits.push(rel);
118
+ }
119
+ }
120
+ };
121
+ walk(base);
122
+ return ok(hits.join("\n") || "(no matches)");
123
+ },
124
+ },
125
+ };
126
+ if (!readOnly) {
127
+ tools.write_file = {
128
+ description: "Write a UTF-8 file under the serve root (creates parent dirs)",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ path: { type: "string" },
133
+ content: { type: "string" },
134
+ },
135
+ required: ["path", "content"],
136
+ },
137
+ handler: ({ path: rel, content }) => {
138
+ const abs = confine(root, rel, deps);
139
+ fs.mkdirSync(deps.path.dirname(abs), { recursive: true });
140
+ fs.writeFileSync(abs, String(content), "utf-8");
141
+ return ok(`wrote ${Buffer.byteLength(String(content))} bytes to ${rel}`);
142
+ },
143
+ };
144
+ }
145
+ return tools;
146
+ }
147
+
148
+ function rpcResult(id, result) {
149
+ return JSON.stringify({ jsonrpc: "2.0", id, result });
150
+ }
151
+ function rpcError(id, code, message) {
152
+ return JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
153
+ }
154
+
155
+ /**
156
+ * Start the server. Returns { server, port, token, url, close() }.
157
+ *
158
+ * @param {object} opts { root, port=0, token (null → random, false → no auth),
159
+ * readOnly, deps }
160
+ */
161
+ export function startMcpServe(opts = {}) {
162
+ const deps = { ..._deps, ...(opts.deps || {}) };
163
+ const root = deps.path.resolve(opts.root || process.cwd());
164
+ const readOnly = Boolean(opts.readOnly);
165
+ const token =
166
+ opts.token === false
167
+ ? null
168
+ : opts.token || randomBytes(16).toString("hex");
169
+ const tools = buildTools({ root, readOnly, deps });
170
+
171
+ const server = http.createServer((req, res) => {
172
+ const send = (status, body) => {
173
+ res.writeHead(status, { "Content-Type": "application/json" });
174
+ res.end(body);
175
+ };
176
+ if (req.method !== "POST") {
177
+ return send(405, rpcError(null, -32600, "POST only"));
178
+ }
179
+ if (token) {
180
+ const auth = req.headers.authorization || "";
181
+ if (auth !== `Bearer ${token}`) {
182
+ return send(401, rpcError(null, -32001, "unauthorized"));
183
+ }
184
+ }
185
+ let raw = "";
186
+ req.on("data", (c) => {
187
+ raw += c;
188
+ });
189
+ req.on("end", () => {
190
+ let msg;
191
+ try {
192
+ msg = JSON.parse(raw);
193
+ } catch {
194
+ return send(400, rpcError(null, -32700, "parse error"));
195
+ }
196
+ const { id, method, params } = msg || {};
197
+ try {
198
+ if (method === "initialize") {
199
+ return send(
200
+ 200,
201
+ rpcResult(id, {
202
+ protocolVersion: params?.protocolVersion || "2025-03-26",
203
+ capabilities: { tools: {} },
204
+ serverInfo: { name: "cc-mcp-serve", version: "1.0.0" },
205
+ }),
206
+ );
207
+ }
208
+ if (method === "notifications/initialized") {
209
+ res.writeHead(202);
210
+ return res.end();
211
+ }
212
+ if (method === "tools/list") {
213
+ return send(
214
+ 200,
215
+ rpcResult(id, {
216
+ tools: Object.entries(tools).map(([name, t]) => ({
217
+ name,
218
+ description: t.description,
219
+ inputSchema: t.inputSchema,
220
+ })),
221
+ }),
222
+ );
223
+ }
224
+ if (method === "tools/call") {
225
+ const tool = tools[params?.name];
226
+ if (!tool) {
227
+ return send(200, rpcResult(id, fail(`unknown tool: ${params?.name}`)));
228
+ }
229
+ let result;
230
+ try {
231
+ result = tool.handler(params?.arguments || {});
232
+ } catch (err) {
233
+ result = fail(err.message);
234
+ }
235
+ return send(200, rpcResult(id, result));
236
+ }
237
+ return send(200, rpcError(id, -32601, `method not found: ${method}`));
238
+ } catch (err) {
239
+ return send(500, rpcError(id, -32603, err.message));
240
+ }
241
+ });
242
+ });
243
+
244
+ return new Promise((resolve, reject) => {
245
+ server.on("error", reject);
246
+ server.listen(opts.port || 0, "127.0.0.1", () => {
247
+ const port = server.address().port;
248
+ resolve({
249
+ server,
250
+ port,
251
+ token,
252
+ root,
253
+ readOnly,
254
+ url: `http://127.0.0.1:${port}/mcp`,
255
+ close: () => new Promise((r) => server.close(r)),
256
+ });
257
+ });
258
+ });
259
+ }