@cybedefend/vibedefend 1.5.0 → 1.7.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 (264) hide show
  1. package/README.md +71 -2
  2. package/dist/api/gateway-request.js +49 -0
  3. package/dist/api/gateway-request.js.map +1 -0
  4. package/dist/api/projects.js +67 -0
  5. package/dist/api/projects.js.map +1 -1
  6. package/dist/api/repository-link.js +157 -0
  7. package/dist/api/repository-link.js.map +1 -0
  8. package/dist/auth/auth-store.js +107 -25
  9. package/dist/auth/auth-store.js.map +1 -1
  10. package/dist/auth/auth.js +21 -0
  11. package/dist/auth/auth.js.map +1 -1
  12. package/dist/clients/cursor.js +5 -1
  13. package/dist/clients/cursor.js.map +1 -1
  14. package/dist/clients/vscode.js +38 -29
  15. package/dist/clients/vscode.js.map +1 -1
  16. package/dist/commands/mcp/allow.js +219 -0
  17. package/dist/commands/mcp/allow.js.map +1 -0
  18. package/dist/commands/mcp/connect.js +337 -0
  19. package/dist/commands/mcp/connect.js.map +1 -0
  20. package/dist/commands/mcp/index.js +170 -0
  21. package/dist/commands/mcp/index.js.map +1 -0
  22. package/dist/commands/mcp/proxy.js +320 -0
  23. package/dist/commands/mcp/proxy.js.map +1 -0
  24. package/dist/commands/mcp/scan.js +349 -0
  25. package/dist/commands/mcp/scan.js.map +1 -0
  26. package/dist/commands/mcp/sync.js +215 -0
  27. package/dist/commands/mcp/sync.js.map +1 -0
  28. package/dist/config.js +6 -0
  29. package/dist/config.js.map +1 -1
  30. package/dist/diagnostics.js +78 -12
  31. package/dist/diagnostics.js.map +1 -1
  32. package/dist/doctor.js +19 -0
  33. package/dist/doctor.js.map +1 -1
  34. package/dist/guards-evaluator/analysis-types.js +27 -0
  35. package/dist/guards-evaluator/analysis-types.js.map +1 -0
  36. package/dist/guards-evaluator/command-analysis.js +572 -0
  37. package/dist/guards-evaluator/command-analysis.js.map +1 -0
  38. package/dist/guards-evaluator/command-view.js +586 -0
  39. package/dist/guards-evaluator/command-view.js.map +1 -0
  40. package/dist/guards-evaluator/env.js +11 -0
  41. package/dist/guards-evaluator/env.js.map +1 -0
  42. package/dist/guards-evaluator/evaluate.js +91 -41
  43. package/dist/guards-evaluator/evaluate.js.map +1 -1
  44. package/dist/guards-evaluator/index.js +2 -1
  45. package/dist/guards-evaluator/index.js.map +1 -1
  46. package/dist/guards-evaluator/inline-code.js +259 -0
  47. package/dist/guards-evaluator/inline-code.js.map +1 -0
  48. package/dist/guards-evaluator/normalize.js +716 -0
  49. package/dist/guards-evaluator/normalize.js.map +1 -0
  50. package/dist/guards-evaluator/preprocess.js +13 -5
  51. package/dist/guards-evaluator/preprocess.js.map +1 -1
  52. package/dist/guards-evaluator/redact.js +19 -0
  53. package/dist/guards-evaluator/redact.js.map +1 -1
  54. package/dist/guards-evaluator/regex.js +25 -3
  55. package/dist/guards-evaluator/regex.js.map +1 -1
  56. package/dist/guards-evaluator/safe-commands.js +155 -0
  57. package/dist/guards-evaluator/safe-commands.js.map +1 -0
  58. package/dist/guards-evaluator/triage.js +315 -0
  59. package/dist/guards-evaluator/triage.js.map +1 -0
  60. package/dist/hook-runner.js +24679 -5079
  61. package/dist/hooks/install.js +38 -0
  62. package/dist/hooks/install.js.map +1 -1
  63. package/dist/hooks/runtime/config.js +38 -0
  64. package/dist/hooks/runtime/config.js.map +1 -1
  65. package/dist/hooks/runtime/emit.js +94 -61
  66. package/dist/hooks/runtime/emit.js.map +1 -1
  67. package/dist/hooks/runtime/fetch-rules.js +22 -7
  68. package/dist/hooks/runtime/fetch-rules.js.map +1 -1
  69. package/dist/hooks/runtime/gateway-http-error.js +34 -0
  70. package/dist/hooks/runtime/gateway-http-error.js.map +1 -0
  71. package/dist/hooks/runtime/guard-rules-cache.js +139 -17
  72. package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
  73. package/dist/hooks/runtime/guard-violations-buffer.js +321 -48
  74. package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
  75. package/dist/hooks/runtime/judge-cache.js +6 -0
  76. package/dist/hooks/runtime/judge-cache.js.map +1 -1
  77. package/dist/hooks/runtime/judge-triage.js +193 -0
  78. package/dist/hooks/runtime/judge-triage.js.map +1 -0
  79. package/dist/hooks/runtime/judge.js +7 -1
  80. package/dist/hooks/runtime/judge.js.map +1 -1
  81. package/dist/hooks/runtime/pre-compact.js +12 -6
  82. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  83. package/dist/hooks/runtime/resolve.js +90 -8
  84. package/dist/hooks/runtime/resolve.js.map +1 -1
  85. package/dist/hooks/runtime/retry-policy.js +57 -0
  86. package/dist/hooks/runtime/retry-policy.js.map +1 -0
  87. package/dist/hooks/runtime/review-sessions.js +74 -0
  88. package/dist/hooks/runtime/review-sessions.js.map +1 -0
  89. package/dist/hooks/runtime/script-resolve.js +695 -0
  90. package/dist/hooks/runtime/script-resolve.js.map +1 -0
  91. package/dist/hooks/runtime/session-activation.js +5 -1
  92. package/dist/hooks/runtime/session-activation.js.map +1 -1
  93. package/dist/hooks/runtime/session-review.js +163 -45
  94. package/dist/hooks/runtime/session-review.js.map +1 -1
  95. package/dist/hooks/runtime/session-start.js +2 -1
  96. package/dist/hooks/runtime/session-start.js.map +1 -1
  97. package/dist/hooks/runtime/session-writes.js +232 -0
  98. package/dist/hooks/runtime/session-writes.js.map +1 -0
  99. package/dist/hooks/runtime/sniff.js +26 -7
  100. package/dist/hooks/runtime/sniff.js.map +1 -1
  101. package/dist/hooks/runtime/target-resolve.js +336 -0
  102. package/dist/hooks/runtime/target-resolve.js.map +1 -0
  103. package/dist/hooks/runtime/unmapped-tools.js +93 -0
  104. package/dist/hooks/runtime/unmapped-tools.js.map +1 -0
  105. package/dist/hooks/runtime/usage-block.js +4 -0
  106. package/dist/hooks/runtime/usage-block.js.map +1 -1
  107. package/dist/hooks/runtime/user-prompt-submit.js +2 -1
  108. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
  109. package/dist/hooks/runtime/wire-text.js +41 -0
  110. package/dist/hooks/runtime/wire-text.js.map +1 -0
  111. package/dist/hooks/runtime/work-dir.js +84 -0
  112. package/dist/hooks/runtime/work-dir.js.map +1 -0
  113. package/dist/index.js +45 -12
  114. package/dist/index.js.map +1 -1
  115. package/dist/install.js +174 -11
  116. package/dist/install.js.map +1 -1
  117. package/dist/link.js +74 -48
  118. package/dist/link.js.map +1 -1
  119. package/dist/login.js +77 -11
  120. package/dist/login.js.map +1 -1
  121. package/dist/mcp/auth/agent-sign-ins.js +174 -0
  122. package/dist/mcp/auth/agent-sign-ins.js.map +1 -0
  123. package/dist/mcp/auth/callback.js +200 -0
  124. package/dist/mcp/auth/callback.js.map +1 -0
  125. package/dist/mcp/auth/connect.js +392 -0
  126. package/dist/mcp/auth/connect.js.map +1 -0
  127. package/dist/mcp/auth/discovery.js +399 -0
  128. package/dist/mcp/auth/discovery.js.map +1 -0
  129. package/dist/mcp/auth/oauth.js +326 -0
  130. package/dist/mcp/auth/oauth.js.map +1 -0
  131. package/dist/mcp/auth/probe.js +128 -0
  132. package/dist/mcp/auth/probe.js.map +1 -0
  133. package/dist/mcp/auth/secrets.js +446 -0
  134. package/dist/mcp/auth/secrets.js.map +1 -0
  135. package/dist/mcp/auth/store.js +260 -0
  136. package/dist/mcp/auth/store.js.map +1 -0
  137. package/dist/mcp/auth/tokenSource.js +55 -0
  138. package/dist/mcp/auth/tokenSource.js.map +1 -0
  139. package/dist/mcp/config/atomicWrite.js +98 -0
  140. package/dist/mcp/config/atomicWrite.js.map +1 -0
  141. package/dist/mcp/config/clients.js +171 -0
  142. package/dist/mcp/config/clients.js.map +1 -0
  143. package/dist/mcp/config/cybedefend-servers.js +48 -0
  144. package/dist/mcp/config/cybedefend-servers.js.map +1 -0
  145. package/dist/mcp/config/discover.js +114 -0
  146. package/dist/mcp/config/discover.js.map +1 -0
  147. package/dist/mcp/config/hook.js +123 -0
  148. package/dist/mcp/config/hook.js.map +1 -0
  149. package/dist/mcp/config/index.js +11 -0
  150. package/dist/mcp/config/index.js.map +1 -0
  151. package/dist/mcp/config/install.js +132 -0
  152. package/dist/mcp/config/install.js.map +1 -0
  153. package/dist/mcp/config/installed.js +37 -0
  154. package/dist/mcp/config/installed.js.map +1 -0
  155. package/dist/mcp/config/jsonEdit.js +119 -0
  156. package/dist/mcp/config/jsonEdit.js.map +1 -0
  157. package/dist/mcp/config/lock.js +102 -0
  158. package/dist/mcp/config/lock.js.map +1 -0
  159. package/dist/mcp/config/proxyBinary.js +211 -0
  160. package/dist/mcp/config/proxyBinary.js.map +1 -0
  161. package/dist/mcp/config/rewrite.js +388 -0
  162. package/dist/mcp/config/rewrite.js.map +1 -0
  163. package/dist/mcp/config/types.js +34 -0
  164. package/dist/mcp/config/types.js.map +1 -0
  165. package/dist/mcp/proxy/audit.js +83 -0
  166. package/dist/mcp/proxy/audit.js.map +1 -0
  167. package/dist/mcp/proxy/authInterceptor.js +119 -0
  168. package/dist/mcp/proxy/authInterceptor.js.map +1 -0
  169. package/dist/mcp/proxy/expand.js +93 -0
  170. package/dist/mcp/proxy/expand.js.map +1 -0
  171. package/dist/mcp/proxy/inject.js +412 -0
  172. package/dist/mcp/proxy/inject.js.map +1 -0
  173. package/dist/mcp/proxy/intercept.js +63 -0
  174. package/dist/mcp/proxy/intercept.js.map +1 -0
  175. package/dist/mcp/proxy/jsonrpc.js +153 -0
  176. package/dist/mcp/proxy/jsonrpc.js.map +1 -0
  177. package/dist/mcp/proxy/log.js +109 -0
  178. package/dist/mcp/proxy/log.js.map +1 -0
  179. package/dist/mcp/proxy/paths.js +57 -0
  180. package/dist/mcp/proxy/paths.js.map +1 -0
  181. package/dist/mcp/proxy/proxy.js +520 -0
  182. package/dist/mcp/proxy/proxy.js.map +1 -0
  183. package/dist/mcp/proxy/serverCheckTimer.js +75 -0
  184. package/dist/mcp/proxy/serverCheckTimer.js.map +1 -0
  185. package/dist/mcp/proxy/sessionLifecycle.js +29 -0
  186. package/dist/mcp/proxy/sessionLifecycle.js.map +1 -0
  187. package/dist/mcp/proxy/spec.js +203 -0
  188. package/dist/mcp/proxy/spec.js.map +1 -0
  189. package/dist/mcp/proxy/sse.js +77 -0
  190. package/dist/mcp/proxy/sse.js.map +1 -0
  191. package/dist/mcp/proxy/upstream.js +2 -0
  192. package/dist/mcp/proxy/upstream.js.map +1 -0
  193. package/dist/mcp/proxy/upstreamHttp.js +499 -0
  194. package/dist/mcp/proxy/upstreamHttp.js.map +1 -0
  195. package/dist/mcp/proxy/upstreamStdio.js +188 -0
  196. package/dist/mcp/proxy/upstreamStdio.js.map +1 -0
  197. package/dist/mcp/proxy/upstreams.js +143 -0
  198. package/dist/mcp/proxy/upstreams.js.map +1 -0
  199. package/dist/mcp/proxy/verifyInterceptor.js +718 -0
  200. package/dist/mcp/proxy/verifyInterceptor.js.map +1 -0
  201. package/dist/mcp/proxy/windowsCommand.js +99 -0
  202. package/dist/mcp/proxy/windowsCommand.js.map +1 -0
  203. package/dist/mcp/report/buffer.js +351 -0
  204. package/dist/mcp/report/buffer.js.map +1 -0
  205. package/dist/mcp/report/coalesce.js +165 -0
  206. package/dist/mcp/report/coalesce.js.map +1 -0
  207. package/dist/mcp/report/constants.js +51 -0
  208. package/dist/mcp/report/constants.js.map +1 -0
  209. package/dist/mcp/report/project.js +83 -0
  210. package/dist/mcp/report/project.js.map +1 -0
  211. package/dist/mcp/report/reporter.js +443 -0
  212. package/dist/mcp/report/reporter.js.map +1 -0
  213. package/dist/mcp/report/types.js +3 -0
  214. package/dist/mcp/report/types.js.map +1 -0
  215. package/dist/mcp/report/validate.js +179 -0
  216. package/dist/mcp/report/validate.js.map +1 -0
  217. package/dist/mcp/scan/args.js +228 -0
  218. package/dist/mcp/scan/args.js.map +1 -0
  219. package/dist/mcp/scan/results.js +253 -0
  220. package/dist/mcp/scan/results.js.map +1 -0
  221. package/dist/mcp/scan/types.js +40 -0
  222. package/dist/mcp/scan/types.js.map +1 -0
  223. package/dist/mcp/verify/api.js +514 -0
  224. package/dist/mcp/verify/api.js.map +1 -0
  225. package/dist/mcp/verify/backend.js +143 -0
  226. package/dist/mcp/verify/backend.js.map +1 -0
  227. package/dist/mcp/verify/cache.js +200 -0
  228. package/dist/mcp/verify/cache.js.map +1 -0
  229. package/dist/mcp/verify/contract-errors.js +198 -0
  230. package/dist/mcp/verify/contract-errors.js.map +1 -0
  231. package/dist/mcp/verify/digest.js +91 -0
  232. package/dist/mcp/verify/digest.js.map +1 -0
  233. package/dist/mcp/verify/factory.js +56 -0
  234. package/dist/mcp/verify/factory.js.map +1 -0
  235. package/dist/mcp/verify/identity.js +286 -0
  236. package/dist/mcp/verify/identity.js.map +1 -0
  237. package/dist/mcp/verify/policy-store.js +63 -0
  238. package/dist/mcp/verify/policy-store.js.map +1 -0
  239. package/dist/mcp/verify/policy.js +160 -0
  240. package/dist/mcp/verify/policy.js.map +1 -0
  241. package/dist/mcp/verify/pool.js +27 -0
  242. package/dist/mcp/verify/pool.js.map +1 -0
  243. package/dist/mcp/verify/token-refresh.js +44 -0
  244. package/dist/mcp/verify/token-refresh.js.map +1 -0
  245. package/dist/mcp/verify/token.js +67 -0
  246. package/dist/mcp/verify/token.js.map +1 -0
  247. package/dist/mcp/verify/verifier.js +1166 -0
  248. package/dist/mcp/verify/verifier.js.map +1 -0
  249. package/dist/org-gate.js +1 -1
  250. package/dist/org-gate.js.map +1 -1
  251. package/dist/org.js +146 -0
  252. package/dist/org.js.map +1 -0
  253. package/dist/project-create.js +129 -0
  254. package/dist/project-create.js.map +1 -0
  255. package/dist/prompts.js +21 -0
  256. package/dist/prompts.js.map +1 -1
  257. package/dist/re2.wasm +0 -0
  258. package/dist/status.js +61 -0
  259. package/dist/status.js.map +1 -1
  260. package/dist/uninstall.js +49 -4
  261. package/dist/uninstall.js.map +1 -1
  262. package/dist/utils.js +62 -6
  263. package/dist/utils.js.map +1 -1
  264. package/package.json +10 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cybedefend-servers.js","sourceRoot":"","sources":["../../../src/mcp/config/cybedefend-servers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,iBAAiB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAc,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAEtD,CAAC;IACT,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;YACjC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,IAAI,CAAC,IAAI,CAAE,MAA+B,CAAC,MAAM,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,MAAyB,OAAO,CAAC,GAAG;IAClF,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Reading every MCP server a user has configured, across clients.
3
+ *
4
+ * Nothing here writes. Discovery is used by `sync` (what changed?), by `scan`
5
+ * (what is out there?) and by `install` (what should be wrapped?), and all
6
+ * three want the same answer, so it is computed once and in one shape.
7
+ *
8
+ * It never throws. A client whose config is a JSON fragment mid-edit, or a
9
+ * file the user cannot read, is reported as a problem on that one file — the
10
+ * measured worst outcome of the previous generation was a single malformed
11
+ * server blinding the verification of the whole park.
12
+ */
13
+ import { existsSync, readFileSync } from 'node:fs';
14
+ import { MCP_CLIENT_ADAPTERS, mcpAdapterFor } from './clients.js';
15
+ import { readJsonDocument } from './jsonEdit.js';
16
+ import { wrappedUpstreamName, } from './types.js';
17
+ export function discoverMcpConfigs(options = {}) {
18
+ const env = options.env ?? process.env;
19
+ const platform = options.platform ?? process.platform;
20
+ const readFileFn = options.readFileFn ?? ((path) => readFileSync(path, 'utf8'));
21
+ const existsFn = options.existsFn ?? existsSync;
22
+ const adapters = options.clients && options.clients.length > 0
23
+ ? options.clients.map((id) => mcpAdapterFor(id))
24
+ : MCP_CLIENT_ADAPTERS;
25
+ const entries = [];
26
+ const problems = [];
27
+ const files = [];
28
+ for (const adapter of adapters) {
29
+ for (const location of adapter.locations(env, platform)) {
30
+ if (!existsFn(location.file))
31
+ continue;
32
+ let text;
33
+ try {
34
+ text = readFileFn(location.file);
35
+ }
36
+ catch {
37
+ problems.push({ location, problem: 'unreadable' });
38
+ continue;
39
+ }
40
+ const parsed = readJsonDocument(text);
41
+ if (!parsed.ok) {
42
+ problems.push({ location, problem: parsed.problem });
43
+ continue;
44
+ }
45
+ files.push(location);
46
+ entries.push(...entriesIn(adapter, location, parsed.document));
47
+ }
48
+ }
49
+ return { entries, problems, files };
50
+ }
51
+ /** Every server in one document, from every map the client uses. */
52
+ export function entriesIn(adapter, location, document) {
53
+ const entries = [];
54
+ for (const map of adapter.serverMaps(document, location)) {
55
+ const servers = readPath(document.value, map.keys);
56
+ if (typeof servers !== 'object' || servers === null || Array.isArray(servers))
57
+ continue;
58
+ // A `projects.<path>.mcpServers` section lives in the user's own file, so
59
+ // it is discovered AND rewritten. `projectPath` is what tells the writer
60
+ // where to put the wrapped entry back.
61
+ const projectPath = map.keys[0] === 'projects' ? map.keys[1] : undefined;
62
+ const entryLocation = projectPath
63
+ ? { ...location, scope: 'project-in-user-file', projectPath }
64
+ : location;
65
+ for (const [name, raw] of Object.entries(servers)) {
66
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
67
+ continue;
68
+ const typed = raw;
69
+ const upstreamName = wrappedUpstreamName(typed);
70
+ const parsed = adapter.parse(name, typed);
71
+ entries.push({
72
+ location: entryLocation,
73
+ name,
74
+ raw: typed,
75
+ kind: parsed.kind,
76
+ wrapped: upstreamName !== null,
77
+ wrappedName: upstreamName ?? undefined,
78
+ spec: parsed.spec,
79
+ });
80
+ }
81
+ }
82
+ return entries;
83
+ }
84
+ /** `readPath(doc, ['projects', '/a/b', 'mcpServers'])`. */
85
+ export function readPath(value, keys) {
86
+ let current = value;
87
+ for (const key of keys) {
88
+ if (typeof current !== 'object' || current === null || Array.isArray(current))
89
+ return undefined;
90
+ if (!Object.hasOwn(current, key))
91
+ return undefined;
92
+ current = Object.getOwnPropertyDescriptor(current, key)?.value;
93
+ }
94
+ return current;
95
+ }
96
+ /**
97
+ * Every entry the proxy could actually be put in front of.
98
+ *
99
+ * Excluded: entries already wrapped (nothing to do) and entries whose
100
+ * transport we could not read — wrapping one of those would replace a working
101
+ * server with a proxy that cannot reach it.
102
+ *
103
+ * ⚠️ NOT excluded any more: `projects.<path>.mcpServers`. Section 0 was
104
+ * amended on 2026-09-09 to "everything that lives in the user's home, never a
105
+ * versioned project file". Those sections live in the user's own
106
+ * `~/.claude.json`, and they are where `claude mcp add` writes by default —
107
+ * filtering them out meant the most common way of adding a server produced
108
+ * one that was never protected. A project's `.mcp.json` is still out of
109
+ * bounds, and it is excluded earlier: no adapter ever looks at one.
110
+ */
111
+ export function wrappableEntries(entries) {
112
+ return entries.filter((entry) => !entry.wrapped && entry.kind !== 'unknown');
113
+ }
114
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/mcp/config/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The Claude Code `SessionStart` hook that keeps the park wrapped.
3
+ *
4
+ * A user who runs `claude mcp add` after installing VibeDefend has just added
5
+ * a server the proxy knows nothing about. The plan's answer is not to ask them
6
+ * to remember `vibedefend sync`: the hook runs it at the start of every
7
+ * session, quietly.
8
+ *
9
+ * What a `SessionStart` hook writes on stdout is APPENDED TO THE AGENT'S
10
+ * CONTEXT. That is why `sync --quiet` prints nothing at all when everything
11
+ * is in order: a line of reassurance on every session start is a line the
12
+ * model reads, pays for, and may act on. One sentence, in English, only when
13
+ * there is something to say.
14
+ */
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { clientConfigRoot } from '../proxy/paths.js';
18
+ import { writeFileAtomic } from './atomicWrite.js';
19
+ import { readJsonDocument, renderJson, DEFAULT_FORMAT } from './jsonEdit.js';
20
+ /** The command the hook runs. Matched to recognise our own entry again. */
21
+ export const SYNC_HOOK_COMMAND = 'vibedefend sync --quiet';
22
+ /** Under the home directory, or under `VIBEDEFEND_MCP_DIR` when it stands in for it. */
23
+ export function claudeCodeSettingsPath(env = process.env) {
24
+ return join(clientConfigRoot(env), '.claude', 'settings.json');
25
+ }
26
+ /**
27
+ * Adds the hook, or reports that it is already there.
28
+ *
29
+ * Idempotent by matching on the command rather than on deep equality: a user
30
+ * who added a `matcher` to our entry keeps it, and we do not add a second
31
+ * one beside it.
32
+ */
33
+ export function installSyncHook(deps = {}) {
34
+ const env = deps.env ?? process.env;
35
+ const file = claudeCodeSettingsPath(env);
36
+ const readFileFn = deps.readFileFn ?? ((path) => readFileSync(path, 'utf8'));
37
+ const writeFileFn = deps.writeFileFn ?? defaultWrite;
38
+ const existsFn = deps.existsFn ?? existsSync;
39
+ let value = {};
40
+ let format = DEFAULT_FORMAT;
41
+ if (existsFn(file)) {
42
+ let text;
43
+ try {
44
+ text = readFileFn(file);
45
+ }
46
+ catch {
47
+ return { outcome: 'unreadable', file };
48
+ }
49
+ const parsed = readJsonDocument(text);
50
+ // Claude Code's settings file is the user's, and it may well have comments
51
+ // in it. Refusing is better than rewriting it without them.
52
+ if (!parsed.ok)
53
+ return { outcome: 'unreadable', file };
54
+ value = parsed.document.value;
55
+ format = parsed.document.format;
56
+ }
57
+ const hooks = asObject(value.hooks) ?? {};
58
+ const sessionStart = Array.isArray(hooks.SessionStart) ? [...hooks.SessionStart] : [];
59
+ if (sessionStart.some((entry) => mentionsSyncHook(entry))) {
60
+ return { outcome: 'already-installed', file };
61
+ }
62
+ sessionStart.push({ hooks: [{ type: 'command', command: SYNC_HOOK_COMMAND }] });
63
+ hooks.SessionStart = sessionStart;
64
+ value.hooks = hooks;
65
+ writeFileFn(file, renderJson(value, format));
66
+ return { outcome: 'installed', file };
67
+ }
68
+ /** Removes our entry, leaving every other hook alone. */
69
+ export function removeSyncHook(deps = {}) {
70
+ const env = deps.env ?? process.env;
71
+ const file = claudeCodeSettingsPath(env);
72
+ const readFileFn = deps.readFileFn ?? ((path) => readFileSync(path, 'utf8'));
73
+ const writeFileFn = deps.writeFileFn ?? defaultWrite;
74
+ const existsFn = deps.existsFn ?? existsSync;
75
+ if (!existsFn(file))
76
+ return { outcome: 'already-installed', file };
77
+ let text;
78
+ try {
79
+ text = readFileFn(file);
80
+ }
81
+ catch {
82
+ return { outcome: 'unreadable', file };
83
+ }
84
+ const parsed = readJsonDocument(text);
85
+ if (!parsed.ok)
86
+ return { outcome: 'unreadable', file };
87
+ const value = parsed.document.value;
88
+ const hooks = asObject(value.hooks);
89
+ if (!hooks || !Array.isArray(hooks.SessionStart))
90
+ return { outcome: 'already-installed', file };
91
+ const kept = hooks.SessionStart.filter((entry) => !mentionsSyncHook(entry));
92
+ if (kept.length === hooks.SessionStart.length)
93
+ return { outcome: 'already-installed', file };
94
+ // An empty array left behind is noise in a file the user reads.
95
+ if (kept.length === 0)
96
+ delete hooks.SessionStart;
97
+ else
98
+ hooks.SessionStart = kept;
99
+ if (Object.keys(hooks).length === 0)
100
+ delete value.hooks;
101
+ writeFileFn(file, renderJson(value, parsed.document.format));
102
+ return { outcome: 'installed', file };
103
+ }
104
+ /** Does this `SessionStart` entry run our sync? */
105
+ export function mentionsSyncHook(entry) {
106
+ const group = asObject(entry);
107
+ if (!group || !Array.isArray(group.hooks))
108
+ return false;
109
+ return group.hooks.some((hook) => {
110
+ const typed = asObject(hook);
111
+ const command = typed && typeof typed.command === 'string' ? typed.command : '';
112
+ return /\bvibedefend\b.*\bsync\b/.test(command);
113
+ });
114
+ }
115
+ function asObject(value) {
116
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
117
+ ? value
118
+ : null;
119
+ }
120
+ function defaultWrite(path, content) {
121
+ writeFileAtomic(path, content, { mode: 0o644, directoryMode: 0o755 });
122
+ }
123
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../src/mcp/config/hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The client-config layer: discover what a user has, wrap it behind the
3
+ * proxy, and put it back.
4
+ */
5
+ export * from './clients.js';
6
+ export * from './discover.js';
7
+ export * from './hook.js';
8
+ export * from './jsonEdit.js';
9
+ export * from './rewrite.js';
10
+ export * from './types.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,132 @@
1
+ import { installedMarkerPath, isProxyInstalled, removeInstalledMarker, writeInstalledMarker, } from './installed.js';
2
+ import { checkProxyBinary, proxyBinaryKnowsMcp, } from './proxyBinary.js';
3
+ import { applySyncPlan, planSync, planUninstall } from './rewrite.js';
4
+ /** Why an install may not proceed, or `null` when it may. */
5
+ export function installRefusal(check, deps) {
6
+ if (!check.onPath)
7
+ return { reason: 'not-on-path', path: null };
8
+ if (!proxyBinaryKnowsMcp(check, { execFileFn: deps.execFileFn })) {
9
+ return { reason: 'no-mcp-subcommand', path: check.onPath };
10
+ }
11
+ return null;
12
+ }
13
+ export function installProxy(deps) {
14
+ const check = checkProxyBinary({ env: deps.env, argv1: deps.argv1, platform: deps.platform });
15
+ const refused = installRefusal(check, deps);
16
+ if (refused)
17
+ return { refused, check };
18
+ const plan = planSync({ env: deps.env, clients: deps.clients, platform: deps.platform });
19
+ const result = applySyncPlan(plan, {
20
+ env: deps.env,
21
+ dryRun: deps.dryRun,
22
+ backup: deps.backup,
23
+ now: deps.now,
24
+ });
25
+ if (!deps.dryRun) {
26
+ writeInstalledMarker({ env: deps.env, binary: check.running, now: deps.now });
27
+ }
28
+ return { check, plan, result };
29
+ }
30
+ /**
31
+ * Taking the proxy out needs no PATH check: restoring an original entry is
32
+ * right whatever the PATH holds. The marker goes with the last client — a
33
+ * partial `--client` uninstall leaves the others wrapped, and their proxies
34
+ * still need their permission to sync.
35
+ */
36
+ export function uninstallProxy(deps) {
37
+ const plan = planUninstall({ env: deps.env, clients: deps.clients, platform: deps.platform });
38
+ const result = applySyncPlan(plan, {
39
+ env: deps.env,
40
+ dryRun: deps.dryRun,
41
+ backup: deps.backup,
42
+ now: deps.now,
43
+ });
44
+ if (!deps.dryRun && !(deps.clients && deps.clients.length > 0) && isProxyInstalled(deps.env)) {
45
+ removeInstalledMarker(deps.env);
46
+ }
47
+ return { plan, result };
48
+ }
49
+ const INSTALL_HINT = 'npm install -g @cybedefend/vibedefend';
50
+ /** The sentence a human gets. `command` is what they typed. */
51
+ export function installRefusalMessage(refusal, command = 'vibedefend sync') {
52
+ if (refusal.reason === 'not-on-path') {
53
+ return (`${command}: no \`vibedefend\` on the PATH. The proxy entries it writes ` +
54
+ '(`vibedefend mcp --name <server>`) would point at a command your MCP clients cannot find. ' +
55
+ `Install it with \`${INSTALL_HINT}\`, then run \`vibedefend sync\` again.`);
56
+ }
57
+ return (`${command}: the \`vibedefend\` on your PATH (${refusal.path}) does not know the \`mcp\` subcommand, ` +
58
+ 'so the proxy entries it writes would not start. ' +
59
+ `Upgrade it with \`${INSTALL_HINT}@latest\`, then run \`vibedefend sync\` again.`);
60
+ }
61
+ /** The one sentence the `SessionStart` hook may add to the agent's context. */
62
+ export function installRefusalSentence(refusal) {
63
+ const cause = refusal.reason === 'not-on-path'
64
+ ? 'there is no `vibedefend` on the PATH'
65
+ : `the \`vibedefend\` on the PATH (${refusal.path}) does not know \`mcp\``;
66
+ return `VibeDefend left the MCP servers unwrapped: ${cause}; run \`${INSTALL_HINT}@latest\` and then \`vibedefend sync\`.`;
67
+ }
68
+ /**
69
+ * The three facts a wrapped entry depends on, and that nothing in a client
70
+ * surfaces when one of them is false: every wrapped server just fails to
71
+ * start. The typical way to get there is nvm — each Node version has its own
72
+ * global install, and switching versions changes which `vibedefend` the PATH
73
+ * resolves to, possibly a release without `mcp`. This diagnoses; it never
74
+ * repairs.
75
+ */
76
+ export function diagnoseProxyInstall(deps) {
77
+ const check = checkProxyBinary({ env: deps.env, argv1: deps.argv1, platform: deps.platform });
78
+ const knowsMcp = check.onPath ? proxyBinaryKnowsMcp(check, { execFileFn: deps.execFileFn }) : null;
79
+ return {
80
+ installed: isProxyInstalled(deps.env),
81
+ marker: installedMarkerPath(deps.env),
82
+ running: check.running,
83
+ onPath: check.onPath,
84
+ matches: check.matches,
85
+ knowsMcp,
86
+ };
87
+ }
88
+ /** Three lines, one per fact, in the order a reader needs them. */
89
+ export function describeProxyInstall(diagnosis) {
90
+ const running = diagnosis.running ?? 'unknown';
91
+ const lines = [];
92
+ lines.push(diagnosis.installed
93
+ ? { level: 'ok', text: `Proxy install: installed (marker at ${diagnosis.marker})` }
94
+ : {
95
+ level: 'warn',
96
+ text: 'Proxy install: not installed — wrapped entries are left alone until you run `vibedefend sync`',
97
+ });
98
+ if (!diagnosis.onPath) {
99
+ lines.push({
100
+ level: 'err',
101
+ text: `Running binary: ${running} — no \`vibedefend\` on the PATH, so a wrapped entry has nothing to start`,
102
+ });
103
+ }
104
+ else if (diagnosis.matches) {
105
+ lines.push({
106
+ level: 'ok',
107
+ text: `Running binary: ${running} — is the \`vibedefend\` on the PATH (${diagnosis.onPath})`,
108
+ });
109
+ }
110
+ else {
111
+ lines.push({
112
+ level: 'warn',
113
+ text: `Running binary: ${running} — NOT the \`vibedefend\` on the PATH (${diagnosis.onPath}); wrapped entries start that one`,
114
+ });
115
+ }
116
+ if (diagnosis.knowsMcp === null) {
117
+ lines.push({ level: 'warn', text: 'PATH vibedefend knows `mcp`: unknown (nothing on the PATH)' });
118
+ }
119
+ else if (diagnosis.knowsMcp) {
120
+ lines.push({ level: 'ok', text: 'PATH vibedefend knows `mcp`: yes' });
121
+ }
122
+ else {
123
+ lines.push({
124
+ level: 'err',
125
+ text: 'PATH vibedefend knows `mcp`: no — every wrapped entry starts a release without the proxy. ' +
126
+ 'Switched Node versions with nvm? Each version has its own global install; run ' +
127
+ '`npm install -g @cybedefend/vibedefend@latest` under the version in use, then `vibedefend sync`.',
128
+ });
129
+ }
130
+ return lines;
131
+ }
132
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/mcp/config/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The `installed` marker.
3
+ *
4
+ * Written by an explicit install (`vibedefend install`, `vibedefend sync`),
5
+ * removed by `vibedefend unwrap`, and the first thing the proxy's
6
+ * background sync checks: with no marker, the proxy never writes into a
7
+ * client's config. A user who has not asked for the park to be wrapped does
8
+ * not get it wrapped because one proxy happened to start.
9
+ */
10
+ import { existsSync, unlinkSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { mcpHomeDir } from '../proxy/paths.js';
13
+ import { writeFileAtomic } from './atomicWrite.js';
14
+ export function installedMarkerPath(env = process.env) {
15
+ return join(mcpHomeDir(env), 'installed');
16
+ }
17
+ export function isProxyInstalled(env = process.env) {
18
+ return existsSync(installedMarkerPath(env));
19
+ }
20
+ export function writeInstalledMarker(deps) {
21
+ const now = deps.now ?? (() => new Date());
22
+ const content = `${JSON.stringify({ installedAt: now().toISOString(), binary: deps.binary }, null, 2)}\n`;
23
+ writeFileAtomic(installedMarkerPath(deps.env ?? process.env), content, {
24
+ mode: 0o600,
25
+ directoryMode: 0o700,
26
+ });
27
+ }
28
+ export function removeInstalledMarker(env = process.env) {
29
+ try {
30
+ unlinkSync(installedMarkerPath(env));
31
+ }
32
+ catch (error) {
33
+ if (error.code !== 'ENOENT')
34
+ throw error;
35
+ }
36
+ }
37
+ //# sourceMappingURL=installed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installed.js","sourceRoot":"","sources":["../../../src/mcp/config/installed.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,OAAO,UAAU,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AASD,MAAM,UAAU,oBAAoB,CAAC,IAAyB;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAC1G,eAAe,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE;QACrE,IAAI,EAAE,KAAK;QACX,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACxE,IAAI,CAAC;QACH,UAAU,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACtE,CAAC;AACH,CAAC"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Editing a client's JSON config without rewriting the parts we did not come
3
+ * for.
4
+ *
5
+ * The Phase 5 acceptance criterion is that `install` followed by `uninstall`
6
+ * leaves the file byte for byte as it was. That rules out
7
+ * `JSON.stringify(value, null, 2)`: it would reindent a four-space file,
8
+ * strip a trailing newline, turn CRLF into LF, and rewrite every line of a
9
+ * document the user has been editing by hand for a year. A diff like that is
10
+ * unreviewable, and it is the kind of thing that makes people uninstall a
11
+ * tool.
12
+ *
13
+ * So the shape is measured on the way in and reproduced on the way out. Key
14
+ * ORDER comes for free: `JSON.parse` preserves insertion order for
15
+ * non-numeric keys, and so does `JSON.stringify`, so an untouched object
16
+ * round-trips unchanged.
17
+ *
18
+ * What is NOT preserved, and cannot be without a full CST: comments (JSONC,
19
+ * which VS Code accepts) and trailing commas. `readJsonDocument` reports
20
+ * whether it saw either, and the callers refuse to write such a file rather
21
+ * than silently dropping a comment the user wrote.
22
+ */
23
+ export const DEFAULT_FORMAT = {
24
+ indent: ' ',
25
+ newline: '\n',
26
+ trailingNewline: true,
27
+ };
28
+ /** Measures the shape of an existing document. */
29
+ export function detectFormat(text) {
30
+ return {
31
+ indent: detectIndent(text),
32
+ newline: text.includes('\r\n') ? '\r\n' : '\n',
33
+ trailingNewline: /\n$/.test(text),
34
+ };
35
+ }
36
+ function detectIndent(text) {
37
+ // The first line that is more indented than its predecessor tells us the
38
+ // unit. A file with no nesting keeps the default, which it will never use.
39
+ for (const line of text.split(/\r?\n/)) {
40
+ const match = /^([ \t]+)\S/.exec(line);
41
+ if (match)
42
+ return match[1];
43
+ }
44
+ return DEFAULT_FORMAT.indent;
45
+ }
46
+ /** Renders a value in the measured shape. */
47
+ export function renderJson(value, format) {
48
+ const body = JSON.stringify(value, null, format.indent);
49
+ const withNewlines = format.newline === '\r\n' ? body.replace(/\n/g, '\r\n') : body;
50
+ return format.trailingNewline ? `${withNewlines}${format.newline}` : withNewlines;
51
+ }
52
+ /**
53
+ * Parses a config document, refusing the shapes we cannot write back.
54
+ *
55
+ * JSONC is refused rather than accepted-and-flattened. VS Code's `mcp.json`
56
+ * legitimately contains comments, and a user who wrote
57
+ * `// disabled for now, see JIRA-4021` above a server would find it gone
58
+ * after an install that never said it touched it.
59
+ */
60
+ export function readJsonDocument(text) {
61
+ if (hasCommentsOrTrailingCommas(text))
62
+ return { ok: false, problem: 'jsonc' };
63
+ let parsed;
64
+ try {
65
+ parsed = JSON.parse(text);
66
+ }
67
+ catch {
68
+ return { ok: false, problem: 'unparseable' };
69
+ }
70
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
71
+ return { ok: false, problem: 'not-an-object' };
72
+ }
73
+ return {
74
+ ok: true,
75
+ document: {
76
+ value: parsed,
77
+ format: detectFormat(text),
78
+ original: text,
79
+ },
80
+ };
81
+ }
82
+ /**
83
+ * Whether the text contains a comment or a trailing comma OUTSIDE a string.
84
+ *
85
+ * A scanner rather than a regular expression: `"url": "https://x/#anchor"`
86
+ * and `{"note": "a, b,}"}` are perfectly ordinary values, and a regex that
87
+ * did not track string state would refuse to touch half the real configs out
88
+ * there.
89
+ */
90
+ export function hasCommentsOrTrailingCommas(text) {
91
+ let inString = false;
92
+ let escaped = false;
93
+ let lastMeaningful = '';
94
+ for (let index = 0; index < text.length; index += 1) {
95
+ const char = text[index];
96
+ if (inString) {
97
+ if (escaped)
98
+ escaped = false;
99
+ else if (char === '\\')
100
+ escaped = true;
101
+ else if (char === '"')
102
+ inString = false;
103
+ continue;
104
+ }
105
+ if (char === '"') {
106
+ inString = true;
107
+ lastMeaningful = '"';
108
+ continue;
109
+ }
110
+ if (char === '/' && (text[index + 1] === '/' || text[index + 1] === '*'))
111
+ return true;
112
+ if ((char === '}' || char === ']') && lastMeaningful === ',')
113
+ return true;
114
+ if (!/\s/.test(char))
115
+ lastMeaningful = char;
116
+ }
117
+ return false;
118
+ }
119
+ //# sourceMappingURL=jsonEdit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonEdit.js","sourceRoot":"","sources":["../../../src/mcp/config/jsonEdit.ts"],"names":[],"mappings":"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"}