reasonix 0.50.0 → 0.51.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 (147) hide show
  1. package/dashboard/dist/app.css +1 -1
  2. package/dashboard/dist/app.js +24 -22
  3. package/dashboard/dist/app.js.map +1 -1
  4. package/dist/cli/{acp-6B25WIFF.js → acp-XEUHGG7X.js} +34 -31
  5. package/dist/cli/acp-XEUHGG7X.js.map +1 -0
  6. package/dist/cli/chat-NJ2Q5KHG.js +50 -0
  7. package/dist/cli/{chunk-OPGWCKKU.js → chunk-2HVTBFCI.js} +3 -3
  8. package/dist/cli/{chunk-AJIZ5KFK.js → chunk-2WUEAI2I.js} +3 -3
  9. package/dist/cli/{chunk-I4Q3QT4W.js → chunk-36BM7INR.js} +2 -2
  10. package/dist/cli/{chunk-3RNFYDDM.js → chunk-3BTK5BHI.js} +11 -7
  11. package/dist/cli/chunk-3BTK5BHI.js.map +1 -0
  12. package/dist/cli/{chunk-GMSAB2TC.js → chunk-3YRTIWFX.js} +2 -2
  13. package/dist/cli/{chunk-NLRC3DWQ.js → chunk-544J4PXD.js} +5 -5
  14. package/dist/cli/{chunk-7WITYWKN.js → chunk-5AIDYVH2.js} +2 -2
  15. package/dist/cli/{chunk-ALCOQP6R.js → chunk-5BBC6YMV.js} +5 -5
  16. package/dist/cli/{chunk-S4XVGLRW.js → chunk-6UNHNVJR.js} +72 -5
  17. package/dist/cli/chunk-6UNHNVJR.js.map +1 -0
  18. package/dist/cli/{chunk-IK6WWRIX.js → chunk-6XWXIVQ3.js} +38 -22
  19. package/dist/cli/chunk-6XWXIVQ3.js.map +1 -0
  20. package/dist/cli/{chunk-AAHB2PFX.js → chunk-7YB26OQO.js} +4 -4
  21. package/dist/cli/chunk-7YB26OQO.js.map +1 -0
  22. package/dist/cli/{chunk-MXWPAPZW.js → chunk-A5PBEIJ7.js} +53 -10
  23. package/dist/cli/chunk-A5PBEIJ7.js.map +1 -0
  24. package/dist/cli/{chunk-FQSQFCBI.js → chunk-BA5R6BAE.js} +2 -2
  25. package/dist/cli/{chunk-XWPZHWC2.js → chunk-BM6BBFAV.js} +2 -2
  26. package/dist/cli/{chunk-CAGKEGNE.js → chunk-BOWSNGQC.js} +52 -140
  27. package/dist/cli/chunk-BOWSNGQC.js.map +1 -0
  28. package/dist/cli/{chunk-EZ57UEZQ.js → chunk-C2MRSJTV.js} +2 -2
  29. package/dist/cli/{chunk-PYIZZAVQ.js → chunk-DVD67FXQ.js} +1716 -4
  30. package/dist/cli/chunk-DVD67FXQ.js.map +1 -0
  31. package/dist/cli/{chunk-ZAXMJANP.js → chunk-EAMXOWUW.js} +3 -3
  32. package/dist/cli/{chunk-TX652NBA.js → chunk-EWVFGYT6.js} +2 -2
  33. package/dist/cli/{chunk-IBRTU5WO.js → chunk-FP7IOWBQ.js} +18 -1182
  34. package/dist/cli/chunk-FP7IOWBQ.js.map +1 -0
  35. package/dist/cli/{chunk-I6FBSTTR.js → chunk-HGK57NBN.js} +9 -353
  36. package/dist/cli/chunk-HGK57NBN.js.map +1 -0
  37. package/dist/cli/chunk-JHWQDJZA.js +80 -0
  38. package/dist/cli/chunk-JHWQDJZA.js.map +1 -0
  39. package/dist/cli/{chunk-X2BQZQEE.js → chunk-K3QJ3GKI.js} +3 -3
  40. package/dist/cli/{chunk-GPUH2BNM.js → chunk-K4YQFULP.js} +612 -254
  41. package/dist/cli/chunk-K4YQFULP.js.map +1 -0
  42. package/dist/cli/chunk-L3VPEESB.js +31 -0
  43. package/dist/cli/chunk-L3VPEESB.js.map +1 -0
  44. package/dist/cli/{chunk-ENFBF6HI.js → chunk-N4SEBLU4.js} +383 -5
  45. package/dist/cli/chunk-N4SEBLU4.js.map +1 -0
  46. package/dist/cli/chunk-NRROJXXT.js +879 -0
  47. package/dist/cli/chunk-NRROJXXT.js.map +1 -0
  48. package/dist/cli/{chunk-3KRRTLC5.js → chunk-R6KIHEF3.js} +1619 -1036
  49. package/dist/cli/chunk-R6KIHEF3.js.map +1 -0
  50. package/dist/cli/{chunk-VVMY4M7J.js → chunk-SBHF5NWD.js} +27 -4
  51. package/dist/cli/chunk-SBHF5NWD.js.map +1 -0
  52. package/dist/cli/{chunk-OWA42BKS.js → chunk-SXSAWOB7.js} +14 -14
  53. package/dist/cli/{chunk-6IUMTRFP.js → chunk-UMZ6KHTS.js} +2 -2
  54. package/dist/cli/{chunk-7X4JJOO7.js → chunk-UO6E7FN3.js} +69 -5
  55. package/dist/cli/{chunk-7X4JJOO7.js.map → chunk-UO6E7FN3.js.map} +1 -1
  56. package/dist/cli/{chunk-3ZZXQ3CZ.js → chunk-UPW544V3.js} +2 -2
  57. package/dist/cli/{chunk-XJZWMU5P.js → chunk-WPOKBW5E.js} +2 -2
  58. package/dist/cli/{chunk-WSBFVOCO.js → chunk-Z3MKG7MQ.js} +2 -2
  59. package/dist/cli/{code-TBK2TASK.js → code-BMXLBC7D.js} +37 -36
  60. package/dist/cli/{code-TBK2TASK.js.map → code-BMXLBC7D.js.map} +1 -1
  61. package/dist/cli/{commands-NXTKSQTN.js → commands-E4RZXMF6.js} +5 -5
  62. package/dist/cli/{commit-IR5SPP7A.js → commit-KSRQ64IL.js} +3 -3
  63. package/dist/cli/{config-XK5WQGTS.js → config-QNDONOTU.js} +4 -2
  64. package/dist/cli/{desktop-5NTQBADL.js → desktop-H3ZHIMDA.js} +83 -37
  65. package/dist/cli/desktop-H3ZHIMDA.js.map +1 -0
  66. package/dist/cli/{diff-JNYX5BSZ.js → diff-I4PYI43W.js} +9 -9
  67. package/dist/cli/{doctor-IKYLUFXX.js → doctor-Y2E4MY2F.js} +12 -12
  68. package/dist/cli/{events-HSC57ONU.js → events-47HOT7ZA.js} +5 -5
  69. package/dist/cli/find-in-code-YLEIK5FK.js +145 -0
  70. package/dist/cli/find-in-code-YLEIK5FK.js.map +1 -0
  71. package/dist/cli/index.js +95 -44
  72. package/dist/cli/index.js.map +1 -1
  73. package/dist/cli/{mcp-BDJJWOCD.js → mcp-76DK63ZB.js} +3 -3
  74. package/dist/cli/{mcp-browse-NJRZDI6V.js → mcp-browse-SDNUGO74.js} +3 -3
  75. package/dist/cli/{mcp-inspect-Y62NWZQL.js → mcp-inspect-BL5DEO5M.js} +3 -3
  76. package/dist/cli/{prompt-UTOIFUQC.js → prompt-JLATI3P7.js} +5 -5
  77. package/dist/cli/{prune-sessions-UCUD4XAP.js → prune-sessions-WHZDFUKD.js} +4 -4
  78. package/dist/cli/{replay-VVIN64MN.js → replay-MHXS7C7Z.js} +10 -10
  79. package/dist/cli/{run-76OBDZFB.js → run-SXNCPRJE.js} +22 -22
  80. package/dist/cli/{server-SZZDKTH2.js → server-GEHOE6CO.js} +61 -35
  81. package/dist/cli/server-GEHOE6CO.js.map +1 -0
  82. package/dist/cli/{sessions-FZTGRCM5.js → sessions-EPBFYISL.js} +18 -18
  83. package/dist/cli/{setup-4UNENGOE.js → setup-IW2XR5XI.js} +8 -7
  84. package/dist/cli/setup-IW2XR5XI.js.map +1 -0
  85. package/dist/cli/{stats-F4NDOD7D.js → stats-4WB4XHBP.js} +6 -6
  86. package/dist/cli/symbols-UQ274IOB.js +167 -0
  87. package/dist/cli/symbols-UQ274IOB.js.map +1 -0
  88. package/dist/cli/version-4SP3DLLH.js +33 -0
  89. package/dist/index.d.ts +25 -6
  90. package/dist/index.js +2700 -578
  91. package/dist/index.js.map +1 -1
  92. package/package.json +6 -3
  93. package/scripts/postinstall.mjs +10 -0
  94. package/dist/cli/acp-6B25WIFF.js.map +0 -1
  95. package/dist/cli/chat-7WASPB4O.js +0 -50
  96. package/dist/cli/chunk-3KRRTLC5.js.map +0 -1
  97. package/dist/cli/chunk-3RNFYDDM.js.map +0 -1
  98. package/dist/cli/chunk-AAHB2PFX.js.map +0 -1
  99. package/dist/cli/chunk-CAGKEGNE.js.map +0 -1
  100. package/dist/cli/chunk-ENFBF6HI.js.map +0 -1
  101. package/dist/cli/chunk-GPUH2BNM.js.map +0 -1
  102. package/dist/cli/chunk-I6FBSTTR.js.map +0 -1
  103. package/dist/cli/chunk-IBRTU5WO.js.map +0 -1
  104. package/dist/cli/chunk-IK6WWRIX.js.map +0 -1
  105. package/dist/cli/chunk-MXWPAPZW.js.map +0 -1
  106. package/dist/cli/chunk-PYIZZAVQ.js.map +0 -1
  107. package/dist/cli/chunk-S4XVGLRW.js.map +0 -1
  108. package/dist/cli/chunk-VVMY4M7J.js.map +0 -1
  109. package/dist/cli/desktop-5NTQBADL.js.map +0 -1
  110. package/dist/cli/server-SZZDKTH2.js.map +0 -1
  111. package/dist/cli/setup-4UNENGOE.js.map +0 -1
  112. package/dist/cli/version-LUVTWHLL.js +0 -33
  113. /package/dist/cli/{chat-7WASPB4O.js.map → chat-NJ2Q5KHG.js.map} +0 -0
  114. /package/dist/cli/{chunk-OPGWCKKU.js.map → chunk-2HVTBFCI.js.map} +0 -0
  115. /package/dist/cli/{chunk-AJIZ5KFK.js.map → chunk-2WUEAI2I.js.map} +0 -0
  116. /package/dist/cli/{chunk-I4Q3QT4W.js.map → chunk-36BM7INR.js.map} +0 -0
  117. /package/dist/cli/{chunk-GMSAB2TC.js.map → chunk-3YRTIWFX.js.map} +0 -0
  118. /package/dist/cli/{chunk-NLRC3DWQ.js.map → chunk-544J4PXD.js.map} +0 -0
  119. /package/dist/cli/{chunk-7WITYWKN.js.map → chunk-5AIDYVH2.js.map} +0 -0
  120. /package/dist/cli/{chunk-ALCOQP6R.js.map → chunk-5BBC6YMV.js.map} +0 -0
  121. /package/dist/cli/{chunk-FQSQFCBI.js.map → chunk-BA5R6BAE.js.map} +0 -0
  122. /package/dist/cli/{chunk-XWPZHWC2.js.map → chunk-BM6BBFAV.js.map} +0 -0
  123. /package/dist/cli/{chunk-EZ57UEZQ.js.map → chunk-C2MRSJTV.js.map} +0 -0
  124. /package/dist/cli/{chunk-ZAXMJANP.js.map → chunk-EAMXOWUW.js.map} +0 -0
  125. /package/dist/cli/{chunk-TX652NBA.js.map → chunk-EWVFGYT6.js.map} +0 -0
  126. /package/dist/cli/{chunk-X2BQZQEE.js.map → chunk-K3QJ3GKI.js.map} +0 -0
  127. /package/dist/cli/{chunk-OWA42BKS.js.map → chunk-SXSAWOB7.js.map} +0 -0
  128. /package/dist/cli/{chunk-6IUMTRFP.js.map → chunk-UMZ6KHTS.js.map} +0 -0
  129. /package/dist/cli/{chunk-3ZZXQ3CZ.js.map → chunk-UPW544V3.js.map} +0 -0
  130. /package/dist/cli/{chunk-XJZWMU5P.js.map → chunk-WPOKBW5E.js.map} +0 -0
  131. /package/dist/cli/{chunk-WSBFVOCO.js.map → chunk-Z3MKG7MQ.js.map} +0 -0
  132. /package/dist/cli/{commands-NXTKSQTN.js.map → commands-E4RZXMF6.js.map} +0 -0
  133. /package/dist/cli/{commit-IR5SPP7A.js.map → commit-KSRQ64IL.js.map} +0 -0
  134. /package/dist/cli/{config-XK5WQGTS.js.map → config-QNDONOTU.js.map} +0 -0
  135. /package/dist/cli/{diff-JNYX5BSZ.js.map → diff-I4PYI43W.js.map} +0 -0
  136. /package/dist/cli/{doctor-IKYLUFXX.js.map → doctor-Y2E4MY2F.js.map} +0 -0
  137. /package/dist/cli/{events-HSC57ONU.js.map → events-47HOT7ZA.js.map} +0 -0
  138. /package/dist/cli/{mcp-BDJJWOCD.js.map → mcp-76DK63ZB.js.map} +0 -0
  139. /package/dist/cli/{mcp-browse-NJRZDI6V.js.map → mcp-browse-SDNUGO74.js.map} +0 -0
  140. /package/dist/cli/{mcp-inspect-Y62NWZQL.js.map → mcp-inspect-BL5DEO5M.js.map} +0 -0
  141. /package/dist/cli/{prompt-UTOIFUQC.js.map → prompt-JLATI3P7.js.map} +0 -0
  142. /package/dist/cli/{prune-sessions-UCUD4XAP.js.map → prune-sessions-WHZDFUKD.js.map} +0 -0
  143. /package/dist/cli/{replay-VVIN64MN.js.map → replay-MHXS7C7Z.js.map} +0 -0
  144. /package/dist/cli/{run-76OBDZFB.js.map → run-SXNCPRJE.js.map} +0 -0
  145. /package/dist/cli/{sessions-FZTGRCM5.js.map → sessions-EPBFYISL.js.map} +0 -0
  146. /package/dist/cli/{stats-F4NDOD7D.js.map → stats-4WB4XHBP.js.map} +0 -0
  147. /package/dist/cli/{version-LUVTWHLL.js.map → version-4SP3DLLH.js.map} +0 -0
@@ -0,0 +1,879 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ bootstrapSemanticSearchInCodeMode
5
+ } from "./chunk-Z3MKG7MQ.js";
6
+ import {
7
+ registerSkillTools
8
+ } from "./chunk-BM6BBFAV.js";
9
+ import {
10
+ preflightStdioSpec
11
+ } from "./chunk-SVD4UPRQ.js";
12
+ import {
13
+ SHARED_SUBAGENT_SINK,
14
+ ToolRegistry,
15
+ formatSubagentResult,
16
+ registerChoiceTool,
17
+ registerFilesystemTools,
18
+ registerMemoryTools,
19
+ registerPlanTool,
20
+ registerTodoTool,
21
+ registerWebTools,
22
+ spawnSubagent
23
+ } from "./chunk-K4YQFULP.js";
24
+ import {
25
+ JobRegistry,
26
+ registerShellTools
27
+ } from "./chunk-UMZ6KHTS.js";
28
+ import {
29
+ SkillStore
30
+ } from "./chunk-N4SEBLU4.js";
31
+ import {
32
+ MCP_CATALOG
33
+ } from "./chunk-PLHAZOLZ.js";
34
+ import {
35
+ grammarForPath
36
+ } from "./chunk-L3VPEESB.js";
37
+ import {
38
+ DeepSeekClient
39
+ } from "./chunk-C2MRSJTV.js";
40
+ import {
41
+ defaultConfigPath,
42
+ loadEditMode,
43
+ loadEndpoint,
44
+ loadFilesystemOutlineThresholdBytes,
45
+ loadJavaSourceEnabled,
46
+ loadProjectShellAllowed,
47
+ loadResolvedSkillPaths,
48
+ loadSubagentModels,
49
+ loadToolRateLimit,
50
+ parseMcpSpec,
51
+ readConfig,
52
+ searchEnabled,
53
+ writeConfig
54
+ } from "./chunk-A5PBEIJ7.js";
55
+
56
+ // src/tools/code-query.ts
57
+ import { readFile } from "fs/promises";
58
+ import { resolve as pathResolve } from "path";
59
+
60
+ // src/core/lazy.ts
61
+ function lazy(load) {
62
+ let pending = null;
63
+ return () => {
64
+ if (!pending) pending = load();
65
+ return pending;
66
+ };
67
+ }
68
+
69
+ // src/tools/code-query.ts
70
+ var loadSymbols = lazy(() => import("./symbols-UQ274IOB.js"));
71
+ var loadFindInCode = lazy(() => import("./find-in-code-YLEIK5FK.js"));
72
+ var UNSUPPORTED = "language not supported (TS/TSX/JS/JSX/Python/Go/Rust/Java); use search_content for grep-style matching";
73
+ function registerCodeQueryTools(registry, opts) {
74
+ const { rootDir } = opts;
75
+ registry.register({
76
+ name: "get_symbols",
77
+ description: "Outline a single TS/TSX/JS/JSX/Python/Go/Rust/Java file via tree-sitter \u2014 returns its top-level + nested symbols (functions, classes, methods, interfaces, types, enums, namespaces) with 1-based line/column. Grammar-aware, ignores names inside comments/strings. Use for 'what's in this file' / 'where is X defined here'; for cross-file scans use search_content. Result: {path, symbols:[{name, kind, line, column, endLine, endColumn, parent?}]} or {path, error}.",
78
+ readOnly: true,
79
+ parallelSafe: true,
80
+ stormExempt: true,
81
+ parameters: {
82
+ type: "object",
83
+ properties: {
84
+ path: {
85
+ type: "string",
86
+ description: "File path (relative to project root or absolute)."
87
+ }
88
+ },
89
+ required: ["path"]
90
+ },
91
+ fn: async (args) => {
92
+ const filePath = resolveProjectPath(rootDir, args.path);
93
+ if (!grammarForPath(filePath)) {
94
+ return JSON.stringify({ path: args.path, error: UNSUPPORTED });
95
+ }
96
+ const source = await readFile(filePath, "utf8");
97
+ const { extractSymbols } = await loadSymbols();
98
+ const symbols = await extractSymbols(filePath, source);
99
+ return JSON.stringify({ path: args.path, symbols });
100
+ }
101
+ });
102
+ registry.register({
103
+ name: "find_in_code",
104
+ description: "Find an identifier `name` in a single TS/TSX/JS/JSX/Python/Go/Rust/Java file, AST-filtered \u2014 skips matches inside comments and strings. Optional `kind` narrows by syntactic role: 'call' (function call site), 'definition' (declaration name), 'reference' (other uses), 'any' (default). Within-file only \u2014 does NOT resolve cross-file references; use search_content + reading for that. Result: {path, matches:[{line, column, kind, snippet}]} or {path, error}.",
105
+ readOnly: true,
106
+ parallelSafe: true,
107
+ stormExempt: true,
108
+ parameters: {
109
+ type: "object",
110
+ properties: {
111
+ name: {
112
+ type: "string",
113
+ description: "Exact identifier text to find."
114
+ },
115
+ path: {
116
+ type: "string",
117
+ description: "File path (relative to project root or absolute)."
118
+ },
119
+ kind: {
120
+ type: "string",
121
+ enum: ["any", "call", "definition", "reference"],
122
+ description: "Filter by syntactic role. Default 'any'."
123
+ }
124
+ },
125
+ required: ["name", "path"]
126
+ },
127
+ fn: async (args) => {
128
+ const filePath = resolveProjectPath(rootDir, args.path);
129
+ if (!grammarForPath(filePath)) {
130
+ return JSON.stringify({ path: args.path, error: UNSUPPORTED });
131
+ }
132
+ const source = await readFile(filePath, "utf8");
133
+ const kind = args.kind ?? "any";
134
+ const findOpts = kind === "any" ? {} : { kind };
135
+ const { findInCode } = await loadFindInCode();
136
+ const matches = await findInCode(filePath, source, args.name, findOpts);
137
+ return JSON.stringify({ path: args.path, matches });
138
+ }
139
+ });
140
+ }
141
+ function resolveProjectPath(rootDir, raw) {
142
+ const stripped = raw.replace(/^[/\\]+/, "");
143
+ return pathResolve(rootDir, stripped.length === 0 ? "." : stripped);
144
+ }
145
+
146
+ // src/java/class-source-finder.ts
147
+ import { execFile } from "child_process";
148
+ import * as fs2 from "fs";
149
+ import * as fsp from "fs/promises";
150
+ import * as os from "os";
151
+ import * as path from "path";
152
+
153
+ // src/java/zip-reader.ts
154
+ import * as fs from "fs";
155
+ import * as zlib from "zlib";
156
+ var EOCD_SIGNATURE = 101010256;
157
+ var CENTRAL_DIR_SIGNATURE = 33639248;
158
+ var LOCAL_FILE_SIGNATURE = 67324752;
159
+ var EOCD_MIN_SIZE = 22;
160
+ var EOCD_MAX_COMMENT = 65535;
161
+ var COMPRESSION_STORED = 0;
162
+ var COMPRESSION_DEFLATED = 8;
163
+ function readU32LE(buf, offset) {
164
+ return buf.readUInt32LE(offset);
165
+ }
166
+ function readU16LE(buf, offset) {
167
+ return buf.readUInt16LE(offset);
168
+ }
169
+ function findEOCD(fd, fileSize) {
170
+ const searchStart = Math.max(0, fileSize - EOCD_MAX_COMMENT - EOCD_MIN_SIZE);
171
+ let chunkOffset = fileSize;
172
+ while (chunkOffset > searchStart) {
173
+ const readSize = Math.min(1024, chunkOffset - searchStart);
174
+ chunkOffset -= readSize;
175
+ const buf = Buffer.alloc(readSize);
176
+ fs.readSync(fd, buf, 0, readSize, chunkOffset);
177
+ for (let i = readSize - 4; i >= 0; i--) {
178
+ if (buf.readUInt32LE(i) === EOCD_SIGNATURE) {
179
+ const eocdOffset = chunkOffset + i;
180
+ const eocdSize = Math.min(EOCD_MIN_SIZE + EOCD_MAX_COMMENT, fileSize - eocdOffset);
181
+ const eocdBuf = Buffer.alloc(eocdSize);
182
+ fs.readSync(fd, eocdBuf, 0, eocdSize, eocdOffset);
183
+ return { offset: eocdOffset, buf: eocdBuf };
184
+ }
185
+ }
186
+ }
187
+ throw new Error("Not a valid ZIP file: EOCD signature not found");
188
+ }
189
+ function parseCentralDirectory(fd, eocdBuf) {
190
+ const centralDirOffset = readU32LE(eocdBuf, 16);
191
+ const totalEntries = readU16LE(eocdBuf, 10);
192
+ const entries = [];
193
+ let offset = centralDirOffset;
194
+ for (let i = 0; i < totalEntries; i++) {
195
+ const headerBuf = Buffer.alloc(46);
196
+ fs.readSync(fd, headerBuf, 0, 46, offset);
197
+ if (readU32LE(headerBuf, 0) !== CENTRAL_DIR_SIGNATURE) {
198
+ throw new Error(`Corrupt central directory at offset ${offset}`);
199
+ }
200
+ const compressionMethod = readU16LE(headerBuf, 10);
201
+ const compressedSize = readU32LE(headerBuf, 20);
202
+ const uncompressedSize = readU32LE(headerBuf, 24);
203
+ const fileNameLen = readU16LE(headerBuf, 28);
204
+ const extraLen = readU16LE(headerBuf, 30);
205
+ const commentLen = readU16LE(headerBuf, 32);
206
+ const localHeaderOffset = readU32LE(headerBuf, 42);
207
+ const nameBuf = Buffer.alloc(fileNameLen);
208
+ fs.readSync(fd, nameBuf, 0, fileNameLen, offset + 46);
209
+ const fileName = nameBuf.toString("utf8");
210
+ entries.push({
211
+ fileName,
212
+ compressionMethod,
213
+ compressedSize,
214
+ uncompressedSize,
215
+ localHeaderOffset
216
+ });
217
+ offset += 46 + fileNameLen + extraLen + commentLen;
218
+ }
219
+ return entries;
220
+ }
221
+ function readJarEntry(jarPath, entryName) {
222
+ const fd = fs.openSync(jarPath, "r");
223
+ try {
224
+ const stat = fs.fstatSync(fd);
225
+ const fileSize = stat.size;
226
+ const eocd = findEOCD(fd, fileSize);
227
+ const entries = parseCentralDirectory(fd, eocd.buf);
228
+ const target = entries.find((e) => e.fileName === entryName);
229
+ if (!target) return null;
230
+ const localHeaderBuf = Buffer.alloc(30);
231
+ fs.readSync(fd, localHeaderBuf, 0, 30, target.localHeaderOffset);
232
+ if (readU32LE(localHeaderBuf, 0) !== LOCAL_FILE_SIGNATURE) {
233
+ throw new Error(`Corrupt local file header at offset ${target.localHeaderOffset}`);
234
+ }
235
+ const localFileNameLen = readU16LE(localHeaderBuf, 26);
236
+ const localExtraLen = readU16LE(localHeaderBuf, 28);
237
+ const dataOffset = target.localHeaderOffset + 30 + localFileNameLen + localExtraLen;
238
+ const compressedBuf = Buffer.alloc(target.compressedSize);
239
+ fs.readSync(fd, compressedBuf, 0, target.compressedSize, dataOffset);
240
+ let data;
241
+ if (target.compressionMethod === COMPRESSION_STORED) {
242
+ data = compressedBuf;
243
+ } else if (target.compressionMethod === COMPRESSION_DEFLATED) {
244
+ data = zlib.inflateRawSync(compressedBuf);
245
+ } else {
246
+ throw new Error(
247
+ `Unsupported compression method ${target.compressionMethod} for entry "${entryName}"`
248
+ );
249
+ }
250
+ return { fileName: target.fileName, data };
251
+ } finally {
252
+ fs.closeSync(fd);
253
+ }
254
+ }
255
+
256
+ // src/java/class-source-finder.ts
257
+ var ClassSourceFinder = class _ClassSourceFinder {
258
+ projectRoot;
259
+ repoPaths;
260
+ javapCommand;
261
+ maxJarScan;
262
+ signal;
263
+ static defaultRepoPaths() {
264
+ const home = os.homedir();
265
+ const candidates = [path.join(home, ".m2", "repository"), path.join(home, ".gradle", "caches")];
266
+ return candidates.filter((p) => fs2.existsSync(p));
267
+ }
268
+ constructor(options) {
269
+ this.projectRoot = path.resolve(options.projectRoot);
270
+ this.repoPaths = options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths.map((p) => path.resolve(p)) : _ClassSourceFinder.defaultRepoPaths();
271
+ this.javapCommand = options.javapCommand ?? "javap";
272
+ this.maxJarScan = options.maxJarScan ?? 2e3;
273
+ this.signal = options.signal;
274
+ }
275
+ async findSource(fullyQualifiedName, options) {
276
+ this.throwIfAborted();
277
+ const projectResult = await this.searchProject(fullyQualifiedName);
278
+ if (projectResult) return projectResult;
279
+ return this.searchRepositories(fullyQualifiedName, options?.jarKeyword);
280
+ }
281
+ async findSourceInJar(fullyQualifiedName, jarPath) {
282
+ this.throwIfAborted();
283
+ const resolvedJarPath = path.resolve(jarPath);
284
+ if (!fs2.existsSync(resolvedJarPath)) {
285
+ return { found: false, method: "not-found" };
286
+ }
287
+ const classEntry = `${fullyQualifiedName.replace(/\./g, "/")}.class`;
288
+ try {
289
+ const content = readJarEntry(resolvedJarPath, classEntry);
290
+ if (!content) return { found: false, method: "not-found" };
291
+ const source = await this.decompileFromJar(resolvedJarPath, content.data, fullyQualifiedName);
292
+ return { found: true, source, method: "jar", sourcePath: resolvedJarPath };
293
+ } catch (err) {
294
+ return { found: false, method: "not-found" };
295
+ }
296
+ }
297
+ async searchProject(fqn) {
298
+ const simpleName = this.simpleClassName(fqn);
299
+ const suffixes = [`${simpleName}.java`, `${simpleName}.java.txt`];
300
+ const queue = [this.projectRoot];
301
+ while (queue.length > 0) {
302
+ this.throwIfAborted();
303
+ const dir = queue.shift();
304
+ let entries;
305
+ try {
306
+ entries = await fsp.readdir(dir, { withFileTypes: true });
307
+ } catch {
308
+ continue;
309
+ }
310
+ for (const entry of entries) {
311
+ const fullPath = path.join(dir, entry.name);
312
+ if (entry.isDirectory()) {
313
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "target" || entry.name === "build" || entry.name === "dist" || entry.name === ".idea" || entry.name === ".vscode" || entry.name === ".gradle") {
314
+ continue;
315
+ }
316
+ queue.push(fullPath);
317
+ } else if (entry.isFile()) {
318
+ if (suffixes.includes(entry.name)) {
319
+ const source = await fsp.readFile(fullPath, "utf-8");
320
+ return { found: true, source, method: "project", sourcePath: fullPath };
321
+ }
322
+ }
323
+ }
324
+ }
325
+ return null;
326
+ }
327
+ async searchRepositories(fqn, jarKeyword) {
328
+ const javaEntry = `${fqn.replace(/\./g, "/")}.java`;
329
+ const classEntry = `${fqn.replace(/\./g, "/")}.class`;
330
+ const sourceJars = [];
331
+ const regularJars = [];
332
+ for (const repoDir of this.repoPaths) {
333
+ await this.walkForJars(repoDir, sourceJars, regularJars, jarKeyword);
334
+ }
335
+ for (const jarPath of sourceJars) {
336
+ this.throwIfAborted();
337
+ try {
338
+ const content = readJarEntry(jarPath, javaEntry);
339
+ if (content) {
340
+ return {
341
+ found: true,
342
+ source: content.data.toString("utf-8"),
343
+ method: "m2-source-jar",
344
+ sourcePath: jarPath
345
+ };
346
+ }
347
+ } catch {
348
+ }
349
+ }
350
+ let scanned = 0;
351
+ for (const jarPath of regularJars) {
352
+ this.throwIfAborted();
353
+ if (scanned >= this.maxJarScan) break;
354
+ scanned++;
355
+ try {
356
+ const content = readJarEntry(jarPath, classEntry);
357
+ if (content) {
358
+ const source = await this.decompileFromJar(jarPath, content.data, fqn);
359
+ return { found: true, source, method: "m2-jar", sourcePath: jarPath };
360
+ }
361
+ } catch {
362
+ }
363
+ }
364
+ return { found: false, method: "not-found" };
365
+ }
366
+ static MAX_WALK_DEPTH = 64;
367
+ async walkForJars(dir, sourceJars, regularJars, keyword, depth = 0) {
368
+ if (depth >= _ClassSourceFinder.MAX_WALK_DEPTH) return;
369
+ let entries;
370
+ try {
371
+ entries = await fsp.readdir(dir, { withFileTypes: true });
372
+ } catch {
373
+ return;
374
+ }
375
+ for (const entry of entries) {
376
+ this.throwIfAborted();
377
+ if (sourceJars.length + regularJars.length >= this.maxJarScan) return;
378
+ const fullPath = path.join(dir, entry.name);
379
+ if (entry.isDirectory()) {
380
+ await this.walkForJars(fullPath, sourceJars, regularJars, keyword, depth + 1);
381
+ } else if (entry.isFile() && entry.name.endsWith(".jar")) {
382
+ if (!keyword || fullPath.toLowerCase().includes(keyword.toLowerCase())) {
383
+ if (entry.name.endsWith("-sources.jar") || entry.name.includes("-sources-")) {
384
+ sourceJars.push(fullPath);
385
+ } else {
386
+ regularJars.push(fullPath);
387
+ }
388
+ }
389
+ }
390
+ }
391
+ }
392
+ async decompileFromJar(jarPath, classBytes, fqn) {
393
+ const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "reasonix-java-src-"));
394
+ try {
395
+ const pkgPath = fqn.replace(/\./g, path.sep);
396
+ const classDir = path.join(tmpDir, path.dirname(pkgPath));
397
+ await fsp.mkdir(classDir, { recursive: true });
398
+ const classFile = path.join(tmpDir, `${pkgPath}.class`);
399
+ await fsp.writeFile(classFile, classBytes);
400
+ const raw = await this.runJavap(fqn, tmpDir);
401
+ return _ClassSourceFinder.compressJavapOutput(raw);
402
+ } finally {
403
+ await fsp.rm(tmpDir, { recursive: true, force: true }).catch(() => {
404
+ });
405
+ }
406
+ }
407
+ // Strip constant pool, debug tables (LineNumberTable / LocalVariableTable /
408
+ // StackMapTable), and "Compiled from …" from javap output — cuts ~60-80% of
409
+ // tokens for AI consumption while keeping method sigs + bytecode.
410
+ static compressJavapOutput(raw) {
411
+ const lines = raw.split("\n");
412
+ const out = [];
413
+ let skipUntilIndent = null;
414
+ for (const line of lines) {
415
+ if (/^\s+#\d+\s*=/.test(line)) continue;
416
+ if (skipUntilIndent !== null) {
417
+ const m = line.match(/^(\s*)/);
418
+ const indent = m?.[1]?.length ?? 0;
419
+ if (indent > skipUntilIndent) continue;
420
+ skipUntilIndent = null;
421
+ if (line.trim() === "") continue;
422
+ }
423
+ const debugMatch = line.match(/^(\s+)(LineNumberTable|LocalVariableTable|StackMapTable):/);
424
+ if (debugMatch) {
425
+ skipUntilIndent = debugMatch[1].length;
426
+ continue;
427
+ }
428
+ if (line.startsWith("Compiled from ")) continue;
429
+ out.push(line);
430
+ }
431
+ return out.join("\n").replace(/\n{3,}/g, "\n\n").trim();
432
+ }
433
+ runJavap(className, classPath) {
434
+ return new Promise((resolve2, reject) => {
435
+ execFile(
436
+ this.javapCommand,
437
+ ["-c", "-p", "-cp", classPath, className],
438
+ {
439
+ maxBuffer: 15 * 1024 * 1024,
440
+ timeout: 3e4,
441
+ signal: this.signal
442
+ },
443
+ (err, stdout, stderr) => {
444
+ if (err) {
445
+ const msg = [stdout, stderr].filter(Boolean).join("\n") || err.message;
446
+ reject(new Error(`javap failed: ${msg}`));
447
+ return;
448
+ }
449
+ resolve2(stdout);
450
+ }
451
+ );
452
+ });
453
+ }
454
+ simpleClassName(fqn) {
455
+ const lastDot = fqn.lastIndexOf(".");
456
+ return lastDot === -1 ? fqn : fqn.slice(lastDot + 1);
457
+ }
458
+ throwIfAborted() {
459
+ if (this.signal?.aborted) {
460
+ throw new DOMException("Aborted", "AbortError");
461
+ }
462
+ }
463
+ };
464
+
465
+ // src/tools/java-source.ts
466
+ function registerJavaSourceTool(registry, opts = {}) {
467
+ registry.register({
468
+ name: "java_source",
469
+ description: [
470
+ "Find and return Java source code by fully-qualified class name.",
471
+ "",
472
+ "Search mode: walk the project tree for a `.java` file, then scan `~/.m2/repository` jars whose filename or path contains `jarKeyword`.",
473
+ "",
474
+ "Returns the source text (or decompiled bytecode) on success, or a clear 'not found' message.",
475
+ "Only call this tool once per class name \u2014 it's I/O heavy."
476
+ ].join("\n"),
477
+ readOnly: true,
478
+ parameters: {
479
+ type: "object",
480
+ properties: {
481
+ className: {
482
+ type: "string",
483
+ description: 'Fully qualified Java class name, e.g. "com.google.common.collect.Lists" or "org.springframework.web.servlet.DispatcherServlet".'
484
+ },
485
+ jarKeyword: {
486
+ type: "string",
487
+ description: 'Only search jars whose filename or path contains this keyword (case-insensitive). Keep it short \u2014 a narrow substring like "spring-core", "guava", or "mycompany-utils" scans faster and matches more precisely than a long fragment.'
488
+ }
489
+ },
490
+ required: ["className", "jarKeyword"]
491
+ },
492
+ parallelSafe: true,
493
+ fn: async (args) => {
494
+ const className = (args?.className ?? "").trim();
495
+ if (!className) {
496
+ throw new Error("java_source: `className` is required");
497
+ }
498
+ if (!/^[a-zA-Z_$][\w$]*(\.[a-zA-Z_$][\w$]*)*$/.test(className)) {
499
+ throw new Error(
500
+ `java_source: "${className}" is not a valid fully qualified Java class name. Expected format: \`com.example.MyClass\``
501
+ );
502
+ }
503
+ const jarKeyword = args.jarKeyword.trim();
504
+ if (!jarKeyword) {
505
+ throw new Error("java_source: `jarKeyword` must not be empty");
506
+ }
507
+ const projectRoot = opts.projectRoot || process.cwd();
508
+ const finder = new ClassSourceFinder({ projectRoot });
509
+ const result = await finder.findSource(className, { jarKeyword });
510
+ if (!result.found) {
511
+ const keywordLine = ` \u2022 Maven .m2 / Gradle cache for jars containing keyword "${jarKeyword}"`;
512
+ const tip = "Try a different keyword, or check if the class is in a different library.";
513
+ return JSON.stringify({
514
+ status: "not-found",
515
+ className,
516
+ message: `No source found for "${className}". Searched:
517
+ \u2022 ${projectRoot}/ for matching .java files
518
+ ${keywordLine}
519
+
520
+ ${tip}`
521
+ });
522
+ }
523
+ return JSON.stringify({
524
+ status: "found",
525
+ className,
526
+ method: result.method,
527
+ sourcePath: result.sourcePath,
528
+ source: result.source
529
+ });
530
+ }
531
+ });
532
+ return registry;
533
+ }
534
+
535
+ // src/tools/scaffold.ts
536
+ var VALID_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
537
+ var VALID_SERVER_NAME = /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/;
538
+ var VALID_TOOL_NAME = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
539
+ function registerScaffoldTools(registry, opts = {}) {
540
+ const configPath = opts.configPath ?? defaultConfigPath();
541
+ registry.register({
542
+ name: "create_skill",
543
+ description: 'Scaffold a SKILL.md the user can later invoke via `/skill <name>`. Frontmatter (description / allowed_tools / run_as / model) is filled from structured args here. Use `run_as: "subagent"` for read-and-synthesize playbooks; default inline appends body to parent log. Refuses to overwrite existing skills.',
544
+ parameters: {
545
+ type: "object",
546
+ properties: {
547
+ name: {
548
+ type: "string",
549
+ description: "Identifier \u2014 letters/digits/`_`/`-`/`.`, 1\u201364 chars. Becomes filename + frontmatter `name`."
550
+ },
551
+ description: {
552
+ type: "string",
553
+ description: 'One-liner for the skills index. Lead with the verb ("Run X and \u2026").'
554
+ },
555
+ body: {
556
+ type: "string",
557
+ description: "Markdown playbook. Reference tools by name."
558
+ },
559
+ scope: {
560
+ type: "string",
561
+ enum: ["project", "global"],
562
+ description: "`project` (default) = workspace .reasonix/skills/; `global` = ~/.reasonix/skills/."
563
+ },
564
+ allowed_tools: {
565
+ type: "array",
566
+ items: { type: "string" },
567
+ description: "Optional tool allowlist for `run_as: subagent`. Omit for full inherited toolset."
568
+ },
569
+ run_as: {
570
+ type: "string",
571
+ enum: ["inline", "subagent"],
572
+ description: "inline (default) appends body to parent log. subagent spawns isolated child; only final answer returns."
573
+ },
574
+ model: {
575
+ type: "string",
576
+ enum: ["deepseek-v4-flash", "deepseek-v4-pro"],
577
+ description: "Subagent model override. Default flash; use pro only when the playbook needs it."
578
+ }
579
+ },
580
+ required: ["name", "description", "body"]
581
+ },
582
+ fn: async (args) => {
583
+ const name = typeof args.name === "string" ? args.name.trim() : "";
584
+ if (!VALID_SKILL_NAME.test(name)) {
585
+ return JSON.stringify({
586
+ error: `invalid skill name: ${JSON.stringify(name)} \u2014 use letters, digits, _, -, .`
587
+ });
588
+ }
589
+ const description = typeof args.description === "string" ? args.description.trim().replace(/\n+/g, " ") : "";
590
+ if (!description) {
591
+ return JSON.stringify({
592
+ error: "create_skill requires a non-empty 'description'"
593
+ });
594
+ }
595
+ const body = typeof args.body === "string" ? args.body : "";
596
+ if (!body.trim()) {
597
+ return JSON.stringify({ error: "create_skill requires a non-empty 'body'" });
598
+ }
599
+ const scope = args.scope === "global" ? "global" : opts.projectRoot ? "project" : "global";
600
+ const runAs = args.run_as === "subagent" ? "subagent" : "inline";
601
+ const allowedTools = parseAllowedTools(args.allowed_tools);
602
+ if (allowedTools && "error" in allowedTools) {
603
+ return JSON.stringify({ error: allowedTools.error });
604
+ }
605
+ const model = typeof args.model === "string" && args.model.startsWith("deepseek-") ? args.model : void 0;
606
+ const content = serializeSkill({
607
+ name,
608
+ description,
609
+ runAs,
610
+ allowedTools: allowedTools ?? void 0,
611
+ model,
612
+ body
613
+ });
614
+ const store = new SkillStore({
615
+ homeDir: opts.homeDir,
616
+ projectRoot: opts.projectRoot,
617
+ customSkillPaths: opts.projectRoot ? loadResolvedSkillPaths(opts.projectRoot, configPath) : []
618
+ });
619
+ const result = store.createWithContent(name, scope, content);
620
+ if ("error" in result) {
621
+ return JSON.stringify({ error: result.error });
622
+ }
623
+ return JSON.stringify({
624
+ success: true,
625
+ path: result.path,
626
+ scope,
627
+ name,
628
+ run_as: runAs
629
+ });
630
+ }
631
+ });
632
+ registry.register({
633
+ name: "add_mcp_server",
634
+ description: 'Register a new MCP server in the user\'s config (`mcp` array). Takes effect next session. Use stdio for local commands, sse/streamable-http for remote. Pass `from_catalog` (e.g. "filesystem", "github") to auto-fill command+args from the bundled catalog. Refuses name collisions.',
635
+ parameters: {
636
+ type: "object",
637
+ properties: {
638
+ name: {
639
+ type: "string",
640
+ description: "Namespace prefix on every tool. Letters/digits/`_`/`-`, must start with letter or `_`."
641
+ },
642
+ transport: {
643
+ type: "string",
644
+ enum: ["stdio", "sse", "streamable-http"],
645
+ description: "stdio = local command via stdin/stdout; sse / streamable-http = remote. Required unless `from_catalog` is set."
646
+ },
647
+ command: {
648
+ type: "string",
649
+ description: "Argv[0] for stdio \u2014 typically `npx` or a binary path."
650
+ },
651
+ args: {
652
+ type: "array",
653
+ items: { type: "string" },
654
+ description: 'Remaining argv for stdio \u2014 e.g. `["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]`.'
655
+ },
656
+ url: {
657
+ type: "string",
658
+ description: "Endpoint URL for sse / streamable-http \u2014 must be http(s)://."
659
+ },
660
+ from_catalog: {
661
+ type: "string",
662
+ description: "Bundled catalog shortcut: filesystem / memory / github / puppeteer / everything. Fills command+args; user supplies user-args via `args`."
663
+ }
664
+ },
665
+ required: ["name"]
666
+ },
667
+ fn: async (args) => {
668
+ const name = typeof args.name === "string" ? args.name.trim() : "";
669
+ if (!VALID_SERVER_NAME.test(name)) {
670
+ return JSON.stringify({
671
+ error: `invalid server name: ${JSON.stringify(name)} \u2014 must match [a-zA-Z_][a-zA-Z0-9_-]*`
672
+ });
673
+ }
674
+ const specStr = buildSpecString({
675
+ name,
676
+ transport: typeof args.transport === "string" ? args.transport : void 0,
677
+ command: typeof args.command === "string" ? args.command : void 0,
678
+ argv: Array.isArray(args.args) ? args.args.filter((a) => typeof a === "string") : void 0,
679
+ url: typeof args.url === "string" ? args.url : void 0,
680
+ fromCatalog: typeof args.from_catalog === "string" ? args.from_catalog : void 0
681
+ });
682
+ if ("error" in specStr) {
683
+ return JSON.stringify({ error: specStr.error });
684
+ }
685
+ let parsed;
686
+ try {
687
+ parsed = parseMcpSpec(specStr.spec);
688
+ } catch (err) {
689
+ return JSON.stringify({ error: err.message });
690
+ }
691
+ if (parsed.transport === "stdio") {
692
+ try {
693
+ preflightStdioSpec(parsed);
694
+ } catch (err) {
695
+ return JSON.stringify({ error: err.message });
696
+ }
697
+ }
698
+ const cfg = readConfig(configPath);
699
+ const existing = cfg.mcp ?? [];
700
+ const collision = existing.find((s) => parseSpecName(s) === name);
701
+ if (collision) {
702
+ return JSON.stringify({
703
+ error: `MCP server ${JSON.stringify(name)} already registered: ${collision}`
704
+ });
705
+ }
706
+ cfg.mcp = [...existing, specStr.spec];
707
+ writeConfig(cfg, configPath);
708
+ return JSON.stringify({
709
+ success: true,
710
+ name,
711
+ transport: parsed.transport,
712
+ spec: specStr.spec,
713
+ config_path: configPath,
714
+ active_on_next_launch: true
715
+ });
716
+ }
717
+ });
718
+ return registry;
719
+ }
720
+ function serializeSkill(args) {
721
+ const lines = ["---", `name: ${args.name}`, `description: ${args.description}`];
722
+ if (args.runAs === "subagent") {
723
+ lines.push("runAs: subagent");
724
+ }
725
+ if (args.allowedTools && args.allowedTools.length > 0) {
726
+ lines.push(`allowed-tools: ${args.allowedTools.join(", ")}`);
727
+ }
728
+ if (args.model) {
729
+ lines.push(`model: ${args.model}`);
730
+ }
731
+ lines.push("---", "");
732
+ return `${lines.join("\n")}
733
+ ${args.body.trim()}
734
+ `;
735
+ }
736
+ function parseAllowedTools(raw) {
737
+ if (raw === void 0 || raw === null) return void 0;
738
+ if (!Array.isArray(raw)) {
739
+ return { error: "'allowed_tools' must be an array of tool-name strings" };
740
+ }
741
+ const out = [];
742
+ for (const v of raw) {
743
+ if (typeof v !== "string") {
744
+ return { error: "'allowed_tools' entries must be strings" };
745
+ }
746
+ const trimmed = v.trim();
747
+ if (!trimmed) continue;
748
+ if (!VALID_TOOL_NAME.test(trimmed)) {
749
+ return { error: `invalid tool name in allowed_tools: ${JSON.stringify(trimmed)}` };
750
+ }
751
+ out.push(trimmed);
752
+ }
753
+ return out.length > 0 ? out : void 0;
754
+ }
755
+ function buildSpecString(input) {
756
+ if (input.fromCatalog) {
757
+ const entry = MCP_CATALOG.find((e) => e.name === input.fromCatalog);
758
+ if (!entry) {
759
+ const known = MCP_CATALOG.map((e) => e.name).join(", ");
760
+ return {
761
+ error: `unknown catalog entry: ${JSON.stringify(input.fromCatalog)} \u2014 known: ${known}`
762
+ };
763
+ }
764
+ const userArgs = input.argv ?? [];
765
+ if (entry.userArgs && userArgs.length === 0) {
766
+ return {
767
+ error: `catalog entry "${entry.name}" needs ${entry.userArgs} \u2014 pass it via the 'args' parameter`
768
+ };
769
+ }
770
+ const tail = userArgs.map(quoteIfNeeded).join(" ");
771
+ const body = `npx -y ${entry.package}${tail ? ` ${tail}` : ""}`;
772
+ return { spec: `${input.name}=${body}` };
773
+ }
774
+ const transport = input.transport;
775
+ if (!transport) {
776
+ return { error: "add_mcp_server requires 'transport' (or 'from_catalog')" };
777
+ }
778
+ if (transport === "stdio") {
779
+ if (!input.command || !input.command.trim()) {
780
+ return { error: "stdio transport requires 'command'" };
781
+ }
782
+ const tail = (input.argv ?? []).map(quoteIfNeeded).join(" ");
783
+ const body = `${quoteIfNeeded(input.command.trim())}${tail ? ` ${tail}` : ""}`;
784
+ return { spec: `${input.name}=${body}` };
785
+ }
786
+ if (transport === "sse" || transport === "streamable-http") {
787
+ if (!input.url || !/^https?:\/\//i.test(input.url)) {
788
+ return { error: `${transport} transport requires an http(s):// 'url'` };
789
+ }
790
+ const prefix = transport === "streamable-http" ? "streamable+" : "";
791
+ return { spec: `${input.name}=${prefix}${input.url.trim()}` };
792
+ }
793
+ return { error: `unknown transport: ${JSON.stringify(transport)}` };
794
+ }
795
+ function parseSpecName(spec) {
796
+ const m = spec.trim().match(/^([a-zA-Z_][a-zA-Z0-9_-]*)=/);
797
+ return m ? m[1] ?? null : null;
798
+ }
799
+ function quoteIfNeeded(s) {
800
+ return /\s|"/.test(s) ? `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : s;
801
+ }
802
+
803
+ // src/code/setup.ts
804
+ function applyPlanMode(tools, editMode) {
805
+ tools.setPlanMode(editMode === "plan");
806
+ }
807
+ async function buildCodeToolset(opts) {
808
+ const tools = new ToolRegistry({ rateLimit: loadToolRateLimit() });
809
+ applyPlanMode(tools, loadEditMode(opts.configPath));
810
+ const jobs = new JobRegistry();
811
+ const outlineThresholdBytes = loadFilesystemOutlineThresholdBytes();
812
+ const registerRooted = (root) => {
813
+ registerFilesystemTools(tools, { rootDir: root, outlineThresholdBytes });
814
+ const cfg = readConfig();
815
+ registerShellTools(tools, {
816
+ rootDir: root,
817
+ extraAllowed: () => loadProjectShellAllowed(root),
818
+ allowAll: () => loadEditMode() === "yolo",
819
+ jobs,
820
+ onJobsChanged: opts.onJobsChanged,
821
+ sensitivePaths: cfg.sensitivePaths
822
+ });
823
+ registerMemoryTools(tools, { projectRoot: root });
824
+ registerCodeQueryTools(tools, { rootDir: root });
825
+ };
826
+ const reBootstrapSemantic = async (root) => {
827
+ const result = await bootstrapSemanticSearchInCodeMode(tools, root);
828
+ if (!result.enabled) tools.unregister("semantic_search");
829
+ return result;
830
+ };
831
+ registerRooted(opts.rootDir);
832
+ registerPlanTool(tools);
833
+ registerChoiceTool(tools);
834
+ registerTodoTool(tools);
835
+ registerScaffoldTools(tools, { projectRoot: opts.rootDir });
836
+ if (searchEnabled()) {
837
+ registerWebTools(tools);
838
+ }
839
+ if (loadJavaSourceEnabled()) {
840
+ registerJavaSourceTool(tools, { projectRoot: opts.rootDir });
841
+ }
842
+ let subagentClient = null;
843
+ registerSkillTools(tools, {
844
+ projectRoot: opts.rootDir,
845
+ customSkillPaths: loadResolvedSkillPaths(opts.rootDir),
846
+ subagentModels: loadSubagentModels(),
847
+ onSkillInstalled: opts.onSkillInstalled,
848
+ subagentRunner: async (skill, task, signal) => {
849
+ if (!subagentClient) {
850
+ const ep = loadEndpoint();
851
+ subagentClient = new DeepSeekClient({ apiKey: ep.apiKey, baseUrl: ep.baseUrl });
852
+ }
853
+ const result = await spawnSubagent({
854
+ client: subagentClient,
855
+ parentRegistry: tools,
856
+ parentSignal: signal,
857
+ system: skill.body,
858
+ task,
859
+ model: skill.model,
860
+ allowedTools: skill.allowedTools,
861
+ skillName: skill.name,
862
+ // Late-bound: the TUI's `useSubagent` writes the live callback into
863
+ // SHARED_SUBAGENT_SINK after mount. Until then `.current` is null
864
+ // and the events are silently dropped — that's fine for non-TUI
865
+ // callers (`reasonix chat --transcript`, library use).
866
+ sink: opts.subagentSink ?? SHARED_SUBAGENT_SINK
867
+ });
868
+ return formatSubagentResult(result);
869
+ }
870
+ });
871
+ const semantic = await reBootstrapSemantic(opts.rootDir);
872
+ return { tools, jobs, registerRooted, reBootstrapSemantic, semantic };
873
+ }
874
+
875
+ export {
876
+ applyPlanMode,
877
+ buildCodeToolset
878
+ };
879
+ //# sourceMappingURL=chunk-NRROJXXT.js.map