@nexrall/code-core 1.4.71 → 1.4.72

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 (125) hide show
  1. package/dist/agent/agentRegistry.d.ts.map +1 -1
  2. package/dist/agent/agentRegistry.js +28 -10
  3. package/dist/agent/agentTypes.d.ts.map +1 -1
  4. package/dist/agent/agentTypes.js +159 -48
  5. package/dist/agent/askOnce.d.ts.map +1 -1
  6. package/dist/agent/askOnce.js +14 -5
  7. package/dist/agent/backgroundAgents.d.ts.map +1 -1
  8. package/dist/agent/backgroundAgents.js +22 -8
  9. package/dist/agent/claimEvidence.d.ts.map +1 -1
  10. package/dist/agent/claimEvidence.js +2 -1
  11. package/dist/agent/compaction.d.ts.map +1 -1
  12. package/dist/agent/compaction.js +31 -10
  13. package/dist/agent/crossProcessLock.d.ts.map +1 -1
  14. package/dist/agent/crossProcessLock.js +14 -6
  15. package/dist/agent/editCompleteness.d.ts.map +1 -1
  16. package/dist/agent/fileLocks.d.ts.map +1 -1
  17. package/dist/agent/fileLocks.js +14 -5
  18. package/dist/agent/flaky.d.ts.map +1 -1
  19. package/dist/agent/hooks.d.ts.map +1 -1
  20. package/dist/agent/hooks.js +284 -75
  21. package/dist/agent/iterationPolicy.d.ts.map +1 -1
  22. package/dist/agent/iterationPolicy.js +26 -23
  23. package/dist/agent/lifecycleHost.d.ts.map +1 -1
  24. package/dist/agent/lifecycleHost.js +35 -11
  25. package/dist/agent/loop.d.ts +22 -4
  26. package/dist/agent/loop.d.ts.map +1 -1
  27. package/dist/agent/loop.js +223 -51
  28. package/dist/agent/memory.d.ts.map +1 -1
  29. package/dist/agent/memory.js +13 -7
  30. package/dist/agent/modelCatalogue.d.ts.map +1 -1
  31. package/dist/agent/modelCatalogue.js +4 -1
  32. package/dist/agent/nestedInstructions.d.ts +122 -0
  33. package/dist/agent/nestedInstructions.d.ts.map +1 -0
  34. package/dist/agent/nestedInstructions.js +696 -0
  35. package/dist/agent/peerRegistry.d.ts.map +1 -1
  36. package/dist/agent/peerRegistry.js +44 -6
  37. package/dist/agent/peerTransport.d.ts.map +1 -1
  38. package/dist/agent/peerTransport.js +18 -6
  39. package/dist/agent/planMode.d.ts.map +1 -1
  40. package/dist/agent/planMode.js +194 -41
  41. package/dist/agent/readDedupe.d.ts.map +1 -1
  42. package/dist/agent/readDedupe.js +3 -1
  43. package/dist/agent/securityLint.d.ts.map +1 -1
  44. package/dist/agent/sharedTasks.d.ts.map +1 -1
  45. package/dist/agent/sharedTasks.js +20 -5
  46. package/dist/agent/skills.d.ts +79 -0
  47. package/dist/agent/skills.d.ts.map +1 -1
  48. package/dist/agent/skills.js +403 -14
  49. package/dist/agent/subAgentBudget.d.ts.map +1 -1
  50. package/dist/agent/subTask.d.ts.map +1 -1
  51. package/dist/agent/subTask.js +87 -37
  52. package/dist/agent/subTaskSupport.d.ts.map +1 -1
  53. package/dist/agent/subTaskSupport.js +43 -14
  54. package/dist/agent/testIntegrity.d.ts.map +1 -1
  55. package/dist/agent/testIntegrity.js +4 -2
  56. package/dist/agent/toolDescriptions.d.ts.map +1 -1
  57. package/dist/agent/toolPrefetch.d.ts.map +1 -1
  58. package/dist/agent/toolPrefetch.js +15 -11
  59. package/dist/agent/trust.d.ts.map +1 -1
  60. package/dist/agent/trust.js +6 -2
  61. package/dist/agent/worktree.d.ts.map +1 -1
  62. package/dist/agent/worktree.js +70 -16
  63. package/dist/agent/worktreeEnforcement.d.ts.map +1 -1
  64. package/dist/agent/worktreeEnforcement.js +9 -4
  65. package/dist/api/client.d.ts.map +1 -1
  66. package/dist/api/client.js +63 -31
  67. package/dist/auth/index.d.ts.map +1 -1
  68. package/dist/auth/index.js +3 -1
  69. package/dist/checkpoint/manager.d.ts.map +1 -1
  70. package/dist/checkpoint/manager.js +22 -7
  71. package/dist/commands/loader.d.ts.map +1 -1
  72. package/dist/commands/loader.js +6 -1
  73. package/dist/index.d.ts +2 -0
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +2 -0
  76. package/dist/mcp/client.d.ts +34 -0
  77. package/dist/mcp/client.d.ts.map +1 -1
  78. package/dist/mcp/client.js +89 -24
  79. package/dist/mcp/httpClient.d.ts +24 -1
  80. package/dist/mcp/httpClient.d.ts.map +1 -1
  81. package/dist/mcp/httpClient.js +80 -14
  82. package/dist/mcp/instructions.d.ts +11 -0
  83. package/dist/mcp/instructions.d.ts.map +1 -0
  84. package/dist/mcp/instructions.js +42 -0
  85. package/dist/mcp/manager.d.ts +24 -0
  86. package/dist/mcp/manager.d.ts.map +1 -1
  87. package/dist/mcp/manager.js +117 -24
  88. package/dist/mcp/server.d.ts.map +1 -1
  89. package/dist/mcp/server.js +48 -14
  90. package/dist/mcp/sseClient.d.ts +9 -1
  91. package/dist/mcp/sseClient.d.ts.map +1 -1
  92. package/dist/mcp/sseClient.js +43 -10
  93. package/dist/mcp/stats.d.ts.map +1 -1
  94. package/dist/permissions/bashClassify.d.ts.map +1 -1
  95. package/dist/permissions/bashClassify.js +25 -3
  96. package/dist/permissions/destructive.d.ts.map +1 -1
  97. package/dist/permissions/destructive.js +7 -2
  98. package/dist/permissions/destructiveTokens.d.ts.map +1 -1
  99. package/dist/permissions/destructiveTokens.js +104 -24
  100. package/dist/permissions/modePolicy.d.ts.map +1 -1
  101. package/dist/permissions/modePolicy.js +40 -17
  102. package/dist/permissions/rules.d.ts.map +1 -1
  103. package/dist/permissions/rules.js +7 -5
  104. package/dist/plugins/data.d.ts.map +1 -1
  105. package/dist/plugins/data.js +6 -2
  106. package/dist/plugins/index.d.ts.map +1 -1
  107. package/dist/plugins/index.js +18 -7
  108. package/dist/plugins/installer.d.ts.map +1 -1
  109. package/dist/plugins/installer.js +17 -10
  110. package/dist/plugins/sources.d.ts.map +1 -1
  111. package/dist/plugins/sources.js +9 -3
  112. package/dist/tools/executor.d.ts.map +1 -1
  113. package/dist/tools/executor.js +454 -168
  114. package/dist/tools/sandbox.d.ts.map +1 -1
  115. package/dist/tools/sandbox.js +5 -1
  116. package/dist/tools/symbols.d.ts.map +1 -1
  117. package/dist/tools/symbols.js +30 -8
  118. package/dist/tools/tsLangService.d.ts.map +1 -1
  119. package/dist/tools/tsLangService.js +12 -4
  120. package/dist/types.d.ts +17 -0
  121. package/dist/types.d.ts.map +1 -1
  122. package/dist/util/frontmatter.d.ts.map +1 -1
  123. package/dist/util/miniYaml.d.ts.map +1 -1
  124. package/dist/util/miniYaml.js +3 -1
  125. package/package.json +1 -1
@@ -76,6 +76,36 @@ class McpManager {
76
76
  for (const client of this._clients.values())
77
77
  client.setElicitationHandler(handler);
78
78
  }
79
+ /**
80
+ * Route server→client notifications for one client: `tools/list_changed` re-fetches
81
+ * the tool table and rebuilds the prefixed map in place (new tools appear on the next
82
+ * turn — no restart), `prompts/list_changed` drops the cached prompt list. A failed
83
+ * refresh keeps the OLD table: a transient error must not empty the toolset.
84
+ */
85
+ _wireNotifications(name, client) {
86
+ client.setNotificationHandler((method) => {
87
+ if (method === 'notifications/tools/list_changed') {
88
+ void client
89
+ .refreshTools()
90
+ .then(() => this._refreshToolMap(name, client.tools))
91
+ .catch(() => {
92
+ /* keep the old table on failure */
93
+ });
94
+ }
95
+ else if (method === 'notifications/prompts/list_changed') {
96
+ this._promptCache.delete(name);
97
+ }
98
+ });
99
+ }
100
+ /** Replace one server's entries in the prefixed tool map with its current list. */
101
+ _refreshToolMap(name, tools) {
102
+ for (const key of [...this._toolMap.keys()]) {
103
+ if (this._toolMap.get(key) === name)
104
+ this._toolMap.delete(key);
105
+ }
106
+ for (const tool of tools)
107
+ this._toolMap.set(`${name}__${tool.name}`, name);
108
+ }
79
109
  /** Pick the transport for a server config: stdio (command), Streamable HTTP, or legacy SSE. */
80
110
  static makeClient(name, cfg) {
81
111
  if (cfg.type === 'sse')
@@ -91,11 +121,10 @@ class McpManager {
91
121
  try {
92
122
  Object.assign(merged.mcpServers, (0, index_1.pluginMcpServers)(workDir));
93
123
  }
94
- catch { /* plugin scan is best-effort */ }
95
- const locations = [
96
- path.join(os.homedir(), '.nexrall', 'mcp.json'),
97
- path.join(workDir, '.nexrall', 'mcp.json'),
98
- ];
124
+ catch {
125
+ /* plugin scan is best-effort */
126
+ }
127
+ const locations = [path.join(os.homedir(), '.nexrall', 'mcp.json'), path.join(workDir, '.nexrall', 'mcp.json')];
99
128
  // A list, so the two scopes UNION rather than override: switching a server off in
100
129
  // the project must add to whatever the user file already silences.
101
130
  const disabled = new Set();
@@ -128,16 +157,14 @@ class McpManager {
128
157
  }
129
158
  const client = McpManager.makeClient(name, serverCfg);
130
159
  client.setElicitationHandler(this._elicitationHandler);
160
+ this._wireNotifications(name, client);
131
161
  try {
132
162
  await client.connect();
133
163
  this._clients.set(name, client);
134
164
  this._errors.delete(name);
135
165
  this._needsAuth.delete(name);
136
166
  // Register tools with prefixed names: serverName__toolName
137
- for (const tool of client.tools) {
138
- const prefixed = `${name}__${tool.name}`;
139
- this._toolMap.set(prefixed, name);
140
- }
167
+ this._refreshToolMap(name, client.tools);
141
168
  }
142
169
  catch (err) {
143
170
  if (err instanceof httpClient_1.McpOAuthRequiredError) {
@@ -204,7 +231,21 @@ class McpManager {
204
231
  // stats of two servers with a same-named tool never merge.
205
232
  const startedAt = Date.now();
206
233
  this.stats.begin();
207
- const timed = (p) => p.then((v) => { this.stats.ok(prefixedName, Date.now() - startedAt); return v; }, (e) => { this.stats.fail(prefixedName, Date.now() - startedAt, e.message || String(e)); throw e; });
234
+ const timed = (p) => p.then((v) => {
235
+ this.stats.ok(prefixedName, Date.now() - startedAt);
236
+ return v;
237
+ }, (e) => {
238
+ this.stats.fail(prefixedName, Date.now() - startedAt, e.message || String(e));
239
+ throw e;
240
+ });
241
+ // A 401 mid-CALL is different from one at connect: the server is already up and
242
+ // some tools work — record it so /mcp flips to needs-auth (with the missing
243
+ // scope) and let the enriched message reach the caller (the model sees WHY).
244
+ const trackAuth = (p) => p.catch((err) => {
245
+ if (err instanceof httpClient_1.McpOAuthRequiredError)
246
+ this._needsAuth.set(serverName, err.wwwAuthenticate);
247
+ throw err;
248
+ });
208
249
  const racers = [timed(client.callTool(toolName, input)), timed(timeoutPromise)];
209
250
  if (abortSignal) {
210
251
  let pollId;
@@ -220,9 +261,9 @@ class McpManager {
220
261
  // Clearing the poll on whichever branch wins avoids leaving a live interval
221
262
  // (and the closure it holds) running for the rest of the process lifetime —
222
263
  // the same leak-prevention pattern client.ts uses for its own abort poll.
223
- return Promise.race(racers).finally(() => clearInterval(pollId));
264
+ return trackAuth(Promise.race(racers).finally(() => clearInterval(pollId)));
224
265
  }
225
- return Promise.race(racers);
266
+ return trackAuth(Promise.race(racers));
226
267
  }
227
268
  /** Resources across connected, enabled servers. One server failing never hides the others. */
228
269
  async listResources(server) {
@@ -241,7 +282,9 @@ class McpManager {
241
282
  if (!client)
242
283
  throw new Error(`No connected MCP server named "${server}". Connected: ${[...this._clients.keys()].join(', ') || '(none)'}`);
243
284
  let timer;
244
- const timeout = new Promise((_, rej) => { timer = setTimeout(() => rej(new Error(`Reading ${uri} timed out after 60s`)), 60000); });
285
+ const timeout = new Promise((_, rej) => {
286
+ timer = setTimeout(() => rej(new Error(`Reading ${uri} timed out after 60s`)), 60000);
287
+ });
245
288
  try {
246
289
  return await Promise.race([client.readResource(uri), timeout]);
247
290
  }
@@ -300,13 +343,18 @@ class McpManager {
300
343
  throw new Error(`MCP prompt "${server}:${name}" takes ${declared.length} argument(s)${declared.length ? ` (${declared.map((a) => a.name).join(', ')})` : ''} — got ${values.length}.`);
301
344
  }
302
345
  const args = {};
303
- declared.forEach((a, i) => { const v = values[i]; if (v !== undefined && v !== '')
304
- args[a.name] = v; });
346
+ declared.forEach((a, i) => {
347
+ const v = values[i];
348
+ if (v !== undefined && v !== '')
349
+ args[a.name] = v;
350
+ });
305
351
  const missing = declared.filter((a) => a.required && args[a.name] === undefined);
306
352
  if (missing.length)
307
353
  throw new Error(`MCP prompt "${server}:${name}" is missing required argument(s): ${missing.map((a) => a.name).join(', ')}.`);
308
354
  let timer;
309
- const timeout = new Promise((_, rej) => { timer = setTimeout(() => rej(new Error(`MCP prompt ${server}:${name} timed out after 60s`)), 60000); });
355
+ const timeout = new Promise((_, rej) => {
356
+ timer = setTimeout(() => rej(new Error(`MCP prompt ${server}:${name} timed out after 60s`)), 60000);
357
+ });
310
358
  try {
311
359
  return await Promise.race([client.getPrompt(name, args), timeout]);
312
360
  }
@@ -334,7 +382,18 @@ class McpManager {
334
382
  getStatus() {
335
383
  const out = [];
336
384
  for (const [name, client] of this._clients) {
337
- out.push({ name, transport: McpManager.transportOf(this._configs.get(name)), tools: client.tools, connected: true, disabled: false });
385
+ out.push({
386
+ name,
387
+ transport: McpManager.transportOf(this._configs.get(name)),
388
+ tools: client.tools,
389
+ connected: true,
390
+ disabled: false,
391
+ // A server can be UP and still have recorded a 401 from a mid-call
392
+ // insufficient_scope challenge — the row says so, the detail says which scope.
393
+ needsAuth: this._needsAuth.has(name),
394
+ missingScope: this.missingScope(name) || undefined,
395
+ protocolVersion: client.protocolVersion,
396
+ });
338
397
  }
339
398
  for (const name of this._configs.keys()) {
340
399
  if (this._clients.has(name))
@@ -344,12 +403,24 @@ class McpManager {
344
403
  // A disabled server that never connected has no error to report — say nothing
345
404
  // rather than repeat "disconnected" as if it were a failure.
346
405
  if (disabled && !error) {
347
- out.push({ name, transport: McpManager.transportOf(this._configs.get(name)), tools: [], connected: false, disabled: true });
406
+ out.push({
407
+ name,
408
+ transport: McpManager.transportOf(this._configs.get(name)),
409
+ tools: [],
410
+ connected: false,
411
+ disabled: true,
412
+ });
348
413
  continue;
349
414
  }
350
415
  out.push({
351
- name, transport: McpManager.transportOf(this._configs.get(name)), tools: [],
352
- connected: false, disabled, error, needsAuth: this._needsAuth.has(name),
416
+ name,
417
+ transport: McpManager.transportOf(this._configs.get(name)),
418
+ tools: [],
419
+ connected: false,
420
+ disabled,
421
+ error,
422
+ needsAuth: this._needsAuth.has(name),
423
+ missingScope: this.missingScope(name) || undefined,
353
424
  });
354
425
  }
355
426
  return out.sort((a, b) => a.name.localeCompare(b.name));
@@ -358,6 +429,23 @@ class McpManager {
358
429
  authChallenge(name) {
359
430
  return this._needsAuth.get(name) ?? '';
360
431
  }
432
+ /** Permissions an auth-required server says are missing ('' when unknown/plain 401). */
433
+ missingScope(name) {
434
+ return (0, httpClient_1.parseOAuthChallenge)(this._needsAuth.get(name) ?? '').scope ?? '';
435
+ }
436
+ /**
437
+ * `initialize.instructions` from every connected server that sent one — static
438
+ * reference notes (workflow rules, repo layout) the conversation should carry.
439
+ * The caller decides where they go; ordering is by server name for stable output.
440
+ */
441
+ serverInstructions() {
442
+ const out = [];
443
+ for (const [name, client] of this._clients) {
444
+ if (client.instructions)
445
+ out.push({ server: name, instructions: client.instructions });
446
+ }
447
+ return out.sort((a, b) => a.server.localeCompare(b.server));
448
+ }
361
449
  /** True when the user has switched this server off for the session. */
362
450
  isDisabled(name) {
363
451
  return this._disabled.has(name);
@@ -386,6 +474,7 @@ class McpManager {
386
474
  this._dropClient(name);
387
475
  const client = McpManager.makeClient(name, cfg);
388
476
  client.setElicitationHandler(this._elicitationHandler);
477
+ this._wireNotifications(name, client);
389
478
  try {
390
479
  await client.connect();
391
480
  }
@@ -399,8 +488,7 @@ class McpManager {
399
488
  this._clients.set(name, client);
400
489
  this._errors.delete(name);
401
490
  this._needsAuth.delete(name);
402
- for (const tool of client.tools)
403
- this._toolMap.set(`${name}__${tool.name}`, name);
491
+ this._refreshToolMap(name, client.tools);
404
492
  return null;
405
493
  }
406
494
  /** Disconnect one server for the session (its config stays; `enable` brings it back). */
@@ -415,9 +503,12 @@ class McpManager {
415
503
  const client = this._clients.get(name);
416
504
  if (client) {
417
505
  try {
506
+ client.setNotificationHandler(null);
418
507
  client.disconnect();
419
508
  }
420
- catch { /* best effort */ }
509
+ catch {
510
+ /* best effort */
511
+ }
421
512
  }
422
513
  this._clients.delete(name);
423
514
  for (const key of [...this._toolMap.keys()]) {
@@ -491,7 +582,9 @@ class McpManager {
491
582
  if ((0, index_1.pluginMcpServers)(workDir)[name])
492
583
  return 'plugin';
493
584
  }
494
- catch { /* plugin scan is best-effort */ }
585
+ catch {
586
+ /* plugin scan is best-effort */
587
+ }
495
588
  return 'none';
496
589
  }
497
590
  /** Remove a server from whichever scope(s) define it. Returns true if anything was removed. */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAaD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,EAAE,CA+BlD;AASD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuHhE"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAaD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,EAAE,CA8ClD;AAcD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyIhE"}
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.readOnlyMcpTools = readOnlyMcpTools;
16
16
  exports.serveMcp = serveMcp;
17
17
  const executor_1 = require("../tools/executor");
18
+ const client_1 = require("./client");
18
19
  // ─── The toolset ──────────────────────────────────────────────────────────────
19
20
  /** Wrap one executor tool. The executor is the single source of behaviour — same caps, same
20
21
  * error messages, same path resolution as the CLI's own turns. */
@@ -31,32 +32,52 @@ const fromExecutor = (name, description, inputSchema) => ({
31
32
  */
32
33
  function readOnlyMcpTools() {
33
34
  return [
34
- fromExecutor('read_file', 'Read a text file. Returns numbered lines so later edits can cite them. Binary files are refused.', { type: 'object', properties: {
35
+ fromExecutor('read_file', 'Read a text file. Returns numbered lines so later edits can cite them. Binary files are refused.', {
36
+ type: 'object',
37
+ properties: {
35
38
  path: { type: 'string', description: 'File path, absolute or relative to the workspace root' },
36
39
  offset: { type: 'number', description: '0-based line to start from (default 0)' },
37
40
  limit: { type: 'number', description: 'How many lines to return' },
38
- }, required: ['path'] }),
39
- fromExecutor('list_directory', 'List the entries of a directory, each marked as file or directory.', { type: 'object', properties: {
41
+ },
42
+ required: ['path'],
43
+ }),
44
+ fromExecutor('list_directory', 'List the entries of a directory, each marked as file or directory.', {
45
+ type: 'object',
46
+ properties: {
40
47
  path: { type: 'string', description: 'Directory path (default: the workspace root)' },
41
- } }),
42
- fromExecutor('glob', 'Find files by glob pattern, e.g. "**/*.ts" or "src/**/*.test.js".', { type: 'object', properties: {
48
+ },
49
+ }),
50
+ fromExecutor('glob', 'Find files by glob pattern, e.g. "**/*.ts" or "src/**/*.test.js".', {
51
+ type: 'object',
52
+ properties: {
43
53
  pattern: { type: 'string', description: 'Glob pattern' },
44
54
  path: { type: 'string', description: 'Directory to search from (default: the workspace root)' },
45
- }, required: ['pattern'] }),
46
- fromExecutor('search_files', 'Search file contents with a regular expression. Returns matching file paths by default; ask for context lines to get the matches themselves.', { type: 'object', properties: {
55
+ },
56
+ required: ['pattern'],
57
+ }),
58
+ fromExecutor('search_files', 'Search file contents with a regular expression. Returns matching file paths by default; ask for context lines to get the matches themselves.', {
59
+ type: 'object',
60
+ properties: {
47
61
  pattern: { type: 'string', description: 'Regular expression' },
48
62
  path: { type: 'string', description: 'Directory to search (default: the workspace root)' },
49
63
  type: { type: 'string', description: '"files" (paths only) or "content" (matching lines)' },
50
64
  include: { type: 'string', description: 'Only files matching this glob, e.g. "*.ts"' },
51
65
  case_insensitive: { type: 'boolean', description: 'Case-insensitive match' },
52
66
  context_lines: { type: 'number', description: 'Lines of context around each match (implies type "content")' },
53
- }, required: ['pattern'] }),
67
+ },
68
+ required: ['pattern'],
69
+ }),
54
70
  ];
55
71
  }
56
72
  // ─── Protocol ─────────────────────────────────────────────────────────────────
57
- /** Versions whose framing we actually implement (they are all the same for our surface). */
58
- const SUPPORTED_PROTOCOLS = ['2024-11-05', '2025-03-26', '2025-06-18'];
59
- const DEFAULT_PROTOCOL = '2024-11-05';
73
+ // Shared with the client half of the module so the two cannot drift. All listed
74
+ // revisions have the same framing for our surface (initialize/tools/resources);
75
+ // when a client asks for a revision we do not implement, the answer is the LATEST
76
+ // we do — that is what the MCP spec requires ("another protocol version it
77
+ // supports, preferably the latest"), and it keeps modern clients on a modern
78
+ // negotiation instead of downgrading them to the 2024 framing by default.
79
+ const SUPPORTED_PROTOCOLS = client_1.MCP_PROTOCOL_VERSIONS;
80
+ const DEFAULT_PROTOCOL = client_1.MCP_PROTOCOL_VERSION;
60
81
  const SERVER_NAME = 'nexrall';
61
82
  /**
62
83
  * Serve MCP on stdio until the client closes the pipe. Resolves (never rejects) at EOF — the
@@ -69,7 +90,9 @@ async function serveMcp(opts) {
69
90
  const tools = opts.tools ?? readOnlyMcpTools();
70
91
  const version = opts.version ?? '0.0.0';
71
92
  const byName = new Map(tools.map((t) => [t.name, t]));
72
- const send = (msg) => { output.write(JSON.stringify(msg) + '\n'); };
93
+ const send = (msg) => {
94
+ output.write(JSON.stringify(msg) + '\n');
95
+ };
73
96
  const reply = (id, result) => send({ jsonrpc: '2.0', id, result });
74
97
  const replyError = (id, code, message) => send({ jsonrpc: '2.0', id, error: { code, message } });
75
98
  async function handle(message) {
@@ -110,7 +133,15 @@ async function serveMcp(opts) {
110
133
  if (!tool) {
111
134
  // A RESULT, not a protocol error: the calling model should read "not offered" as tool
112
135
  // output and pick another route, not see the whole call fail.
113
- reply(id, { content: [{ type: 'text', text: `Tool "${name}" is not offered by this server (read-only). Available: ${[...byName.keys()].join(', ')}.` }], isError: true });
136
+ reply(id, {
137
+ content: [
138
+ {
139
+ type: 'text',
140
+ text: `Tool "${name}" is not offered by this server (read-only). Available: ${[...byName.keys()].join(', ')}.`,
141
+ },
142
+ ],
143
+ isError: true,
144
+ });
114
145
  return;
115
146
  }
116
147
  try {
@@ -122,7 +153,10 @@ async function serveMcp(opts) {
122
153
  }
123
154
  catch (err) {
124
155
  // A throwing tool is the tool's problem, not the session's — answer and keep serving.
125
- reply(id, { content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }], isError: true });
156
+ reply(id, {
157
+ content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }],
158
+ isError: true,
159
+ });
126
160
  }
127
161
  return;
128
162
  }
@@ -1,4 +1,4 @@
1
- import type { McpServerConfig, McpToolDefinition, McpTransport, McpResource, McpPrompt, McpElicitationHandler } from './client';
1
+ import type { McpServerConfig, McpToolDefinition, McpTransport, McpResource, McpPrompt, McpElicitationHandler, McpNotificationHandler } from './client';
2
2
  export declare class McpSseClient implements McpTransport {
3
3
  private readonly config;
4
4
  private _nextId;
@@ -10,11 +10,19 @@ export declare class McpSseClient implements McpTransport {
10
10
  private readonly _pending;
11
11
  private _endpointWaiter;
12
12
  private _elicitationHandler;
13
+ private _notificationHandler;
14
+ private _protocolVersion;
15
+ private _instructions;
13
16
  readonly name: string;
14
17
  constructor(name: string, config: McpServerConfig);
15
18
  get tools(): McpToolDefinition[];
19
+ get protocolVersion(): string | undefined;
20
+ get instructions(): string | undefined;
16
21
  setElicitationHandler(handler: McpElicitationHandler | null): void;
22
+ setNotificationHandler(handler: McpNotificationHandler | null): void;
17
23
  connect(): Promise<void>;
24
+ /** Re-fetch `tools/list` (see McpClient.refreshTools for the failure rule). */
25
+ refreshTools(): Promise<void>;
18
26
  listResources(): Promise<McpResource[]>;
19
27
  readResource(uri: string): Promise<string>;
20
28
  listPrompts(): Promise<McpPrompt[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"sseClient.d.ts","sourceRoot":"","sources":["../../src/mcp/sseClient.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AA6BhI,qBAAa,YAAa,YAAW,YAAY;IAYrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAXjD,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0H;IACnJ,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,mBAAmB,CAAsC;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAmB,MAAM,EAAE,eAAe;IAIlE,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAE/B;IAED,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,GAAG,IAAI;IAE5D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxB,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAOvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAOnC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAc5E,UAAU,IAAI,IAAI;IAalB,4EAA4E;YAC9D,WAAW;IAoCzB,OAAO,CAAC,eAAe;IAYvB,oFAAoF;IACpF,OAAO,CAAC,KAAK;IAWb,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,cAAc;IAqDtB,OAAO,CAAC,IAAI;YAgBE,OAAO;IAIrB,6EAA6E;YAC/D,YAAY;CAwB3B"}
1
+ {"version":3,"file":"sseClient.d.ts","sourceRoot":"","sources":["../../src/mcp/sseClient.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,UAAU,CAAC;AA6BlB,qBAAa,YAAa,YAAW,YAAY;IAoB7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAnBzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGrB;IACJ,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAAuC;IACnE,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAAqB;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAGpB,IAAI,EAAE,MAAM,EACK,MAAM,EAAE,eAAe;IAK1C,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAE/B;IAED,IAAI,eAAe,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,GAAG,IAAI;IAIlE,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,IAAI;IAI9D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,+EAA+E;IACzE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IASvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IASnC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAMtE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAc5E,UAAU,IAAI,IAAI;IAalB,4EAA4E;YAC9D,WAAW;IA4CzB,OAAO,CAAC,eAAe;IAYvB,oFAAoF;IACpF,OAAO,CAAC,KAAK;IAWb,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,cAAc;IA0DtB,OAAO,CAAC,IAAI;YAoBE,OAAO;IAIrB,6EAA6E;YAC/D,YAAY;CAwB3B"}
@@ -21,22 +21,42 @@ class McpSseClient {
21
21
  this._pending = new Map();
22
22
  this._endpointWaiter = null;
23
23
  this._elicitationHandler = null;
24
+ this._notificationHandler = null;
24
25
  this.name = name;
25
26
  }
26
27
  get tools() {
27
28
  return this._tools;
28
29
  }
29
- setElicitationHandler(handler) { this._elicitationHandler = handler; }
30
+ get protocolVersion() {
31
+ return this._protocolVersion;
32
+ }
33
+ get instructions() {
34
+ return this._instructions;
35
+ }
36
+ setElicitationHandler(handler) {
37
+ this._elicitationHandler = handler;
38
+ }
39
+ setNotificationHandler(handler) {
40
+ this._notificationHandler = handler;
41
+ }
30
42
  async connect() {
31
43
  if (!this.config.url)
32
44
  throw new Error(`MCP server "${this.name}" has no url (sse transport requires one).`);
33
45
  await this._openStream();
34
- await this._rpc('initialize', {
35
- protocolVersion: '2024-11-05',
46
+ const init = (await this._rpc('initialize', {
47
+ protocolVersion: client_1.MCP_PROTOCOL_VERSION,
36
48
  capabilities: (0, client_1.elicitationCapability)(this.config.bareElicitationCapability),
37
49
  clientInfo: { name: 'nexrall-code', version: '1.0' },
38
- });
50
+ }));
51
+ this._protocolVersion =
52
+ typeof init?.protocolVersion === 'string' && init.protocolVersion ? init.protocolVersion : undefined;
53
+ this._instructions =
54
+ typeof init?.instructions === 'string' && init.instructions.trim() ? init.instructions : undefined;
39
55
  await this._notify('notifications/initialized');
56
+ await this.refreshTools();
57
+ }
58
+ /** Re-fetch `tools/list` (see McpClient.refreshTools for the failure rule). */
59
+ async refreshTools() {
40
60
  const res = (await this._rpc('tools/list', {}));
41
61
  this._tools = res?.tools ?? [];
42
62
  }
@@ -111,8 +131,10 @@ class McpSseClient {
111
131
  const stream = resp.body;
112
132
  stream.on('data', (chunk) => this._feed(chunk.toString('utf-8')));
113
133
  stream.on('end', () => this._onStreamClosed(new Error('MCP SSE stream ended')));
114
- stream.on('error', (err) => { if (!this._closed)
115
- this._onStreamClosed(err); });
134
+ stream.on('error', (err) => {
135
+ if (!this._closed)
136
+ this._onStreamClosed(err);
137
+ });
116
138
  // Wait for the `endpoint` event so we know where to POST messages.
117
139
  await new Promise((resolve, reject) => {
118
140
  const timer = setTimeout(() => {
@@ -120,8 +142,14 @@ class McpSseClient {
120
142
  reject(new Error('Timed out waiting for SSE endpoint event'));
121
143
  }, ENDPOINT_TIMEOUT_MS);
122
144
  this._endpointWaiter = {
123
- resolve: () => { clearTimeout(timer); resolve(); },
124
- reject: (e) => { clearTimeout(timer); reject(e); },
145
+ resolve: () => {
146
+ clearTimeout(timer);
147
+ resolve();
148
+ },
149
+ reject: (e) => {
150
+ clearTimeout(timer);
151
+ reject(e);
152
+ },
125
153
  };
126
154
  });
127
155
  }
@@ -199,7 +227,9 @@ class McpSseClient {
199
227
  void (async () => {
200
228
  const reply = await (0, client_1.serverRequestReply)(this.name, this._elicitationHandler, method, params);
201
229
  await this._postMessage({ jsonrpc: '2.0', id: reqId, ...reply });
202
- })().catch(() => { });
230
+ })().catch(() => {
231
+ /* server times the request out itself */
232
+ });
203
233
  return;
204
234
  }
205
235
  if (typeof msg.id === 'number' && this._pending.has(msg.id)) {
@@ -212,7 +242,10 @@ class McpSseClient {
212
242
  resolve(msg.result ?? null);
213
243
  return;
214
244
  }
215
- // Server-initiated notifications (no matching id) are ignored.
245
+ // Server-initiated notification (no id): list_changed for tools/prompts/resources.
246
+ if (typeof msg.method === 'string' && msg.id == null) {
247
+ this._notificationHandler?.(msg.method, msg.params);
248
+ }
216
249
  }
217
250
  _rpc(method, params) {
218
251
  const id = this._nextId++;
@@ -1 +1 @@
1
- {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/mcp/stats.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAK;IAErB,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAIb,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAUpD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAWrE,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,6FAA6F;IAC7F,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAYjF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,YAAY;IAS5D,iFAAiF;IACjF,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,MAAM;CAKf;AAED,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,GAAG,UAAO,GAAG,MAAM,CAO5E"}
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/mcp/stats.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAK;IAErB,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAIb,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAUpD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,IAAI;IAWrE,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,6FAA6F;IAC7F,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAYjF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,YAAY;IAS5D,iFAAiF;IACjF,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ1B,OAAO,CAAC,MAAM;CAQf;AAED,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,GAAG,UAAO,GAAG,MAAM,CAS5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"bashClassify.d.ts","sourceRoot":"","sources":["../../src/permissions/bashClassify.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB1D"}
1
+ {"version":3,"file":"bashClassify.d.ts","sourceRoot":"","sources":["../../src/permissions/bashClassify.ts"],"names":[],"mappings":"AA2DA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB1D"}
@@ -32,9 +32,31 @@ exports.bashNeedsRepoLock = bashNeedsRepoLock;
32
32
  * a lockfile, or mutate a shared build/dependency directory.
33
33
  */
34
34
  const MUTATING_BASH_VERBS = new Set([
35
- 'git', 'npm', 'pnpm', 'yarn', 'bun', 'cargo', 'go', 'pip', 'pip3', 'poetry', 'uv',
36
- 'bundle', 'composer', 'gradle', 'mvn', 'make', 'terraform', 'helm', 'docker',
37
- 'vsce', 'tsc', 'pytest', 'dotnet', 'swift', 'gem',
35
+ 'git',
36
+ 'npm',
37
+ 'pnpm',
38
+ 'yarn',
39
+ 'bun',
40
+ 'cargo',
41
+ 'go',
42
+ 'pip',
43
+ 'pip3',
44
+ 'poetry',
45
+ 'uv',
46
+ 'bundle',
47
+ 'composer',
48
+ 'gradle',
49
+ 'mvn',
50
+ 'make',
51
+ 'terraform',
52
+ 'helm',
53
+ 'docker',
54
+ 'vsce',
55
+ 'tsc',
56
+ 'pytest',
57
+ 'dotnet',
58
+ 'swift',
59
+ 'gem',
38
60
  ]);
39
61
  /**
40
62
  * Whether a bash command should take the repo-state lock.
@@ -1 +1 @@
1
- {"version":3,"file":"destructive.d.ts","sourceRoot":"","sources":["../../src/permissions/destructive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAGnF;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,GAAG,IAAI,CAQvG"}
1
+ {"version":3,"file":"destructive.d.ts","sourceRoot":"","sources":["../../src/permissions/destructive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsRD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAGnF;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,GAAG,IAAI,CASvG"}
@@ -60,7 +60,11 @@ const RAW_RULES = [
60
60
  {
61
61
  category: 'database',
62
62
  reason: 'DELETE without a WHERE clause (deletes every row)',
63
- re: new RegExp(CMD_POS + DB_CLIENT + String.raw `\b[\s\S]*?\bdelete\s+from\s+[\w.` + '`' + String.raw `"]+\s*(?:;|"|'|$)(?![\s\S]*\bwhere\b)`, 'i'),
63
+ re: new RegExp(CMD_POS +
64
+ DB_CLIENT +
65
+ String.raw `\b[\s\S]*?\bdelete\s+from\s+[\w.` +
66
+ '`' +
67
+ String.raw `"]+\s*(?:;|"|'|$)(?![\s\S]*\bwhere\b)`, 'i'),
64
68
  },
65
69
  {
66
70
  category: 'database',
@@ -94,7 +98,8 @@ const STRIPPED_RULES = [
94
98
  {
95
99
  category: 'database',
96
100
  reason: 'reverses or resets database migrations (may drop columns/tables)',
97
- re: new RegExp(CMD_POS + String.raw `(?:rails\s+db:(?:drop|reset)|rake\s+db:(?:drop|reset)|(?:npx\s+)?prisma\s+migrate\s+reset|(?:npx\s+)?sequelize(?:-cli)?\s+db:drop|python\s+manage\.py\s+flush|knex\s+migrate:rollback\s+--all|alembic\s+downgrade\s+base)`, 'i'),
101
+ re: new RegExp(CMD_POS +
102
+ String.raw `(?:rails\s+db:(?:drop|reset)|rake\s+db:(?:drop|reset)|(?:npx\s+)?prisma\s+migrate\s+reset|(?:npx\s+)?sequelize(?:-cli)?\s+db:drop|python\s+manage\.py\s+flush|knex\s+migrate:rollback\s+--all|alembic\s+downgrade\s+base)`, 'i'),
98
103
  },
99
104
  // ── Git history / remotes ───────────────────────────────────────────────────
100
105
  {
@@ -1 +1 @@
1
- {"version":3,"file":"destructiveTokens.d.ts","sourceRoot":"","sources":["../../src/permissions/destructiveTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA2BhD;AAgQD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,EAC/C,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,GAC7C,UAAU,GAAG,IAAI,CA8DnB"}
1
+ {"version":3,"file":"destructiveTokens.d.ts","sourceRoot":"","sources":["../../src/permissions/destructiveTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA6DhD;AAsVD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,EAC/C,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,GAC7C,UAAU,GAAG,IAAI,CA+HnB"}