opencode-swarm 7.139.2 → 7.139.3

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 (91) hide show
  1. package/.opencode/skills/phase-wrap/SKILL.md +2 -2
  2. package/dist/background/stage-b-gates.d.ts +5 -1
  3. package/dist/background/task-envelope.d.ts +4 -4
  4. package/dist/cli/{config-doctor-hrb61yqe.js → config-doctor-75qky762.js} +3 -3
  5. package/dist/cli/{core-g2dfh9fq.js → core-nn8ffy5h.js} +3 -3
  6. package/dist/cli/{curation-policy-4tp0mbcb.js → curation-policy-f46tf3dw.js} +5 -5
  7. package/dist/cli/{curator-drift-vfh07mqy.js → curator-drift-r52hjskk.js} +2 -2
  8. package/dist/cli/{curator-llm-factory-hvw13hmr.js → curator-llm-factory-1r9vz0g2.js} +28 -28
  9. package/dist/cli/{curator-vpcs1p1v.js → curator-m6mk2v1n.js} +28 -28
  10. package/dist/cli/{dispatch-b1fge4xk.js → dispatch-nbv0hezh.js} +2 -2
  11. package/dist/cli/{evidence-summary-service-kzxr71yd.js → evidence-summary-service-sxcq5nrt.js} +6 -6
  12. package/dist/cli/{gate-evidence-z2b1ymnw.js → gate-evidence-1982qswy.js} +3 -3
  13. package/dist/cli/{guardrail-explain-k62h3c92.js → guardrail-explain-y2wg00dp.js} +29 -29
  14. package/dist/cli/{guardrail-log-2ftew0p3.js → guardrail-log-320grggj.js} +5 -5
  15. package/dist/cli/{hive-promoter-mpf0cfkk.js → hive-promoter-v4w7cjvk.js} +28 -28
  16. package/dist/cli/{index-3eddtgxt.js → index-05mms0va.js} +1 -1
  17. package/dist/cli/{index-y64jv2pe.js → index-0mzz46we.js} +2 -2
  18. package/dist/cli/{index-ffpmqx30.js → index-1jbj7vzv.js} +4 -4
  19. package/dist/cli/{index-y47dwge1.js → index-1yd38ajy.js} +2 -2
  20. package/dist/cli/{index-gvhvz176.js → index-2eet124q.js} +1 -1
  21. package/dist/cli/{index-0wp67gqj.js → index-4fr94329.js} +11 -6
  22. package/dist/cli/{index-y111zefa.js → index-4nt52yr9.js} +80 -31
  23. package/dist/cli/{index-3jcyn8g6.js → index-4wjk296r.js} +1 -1
  24. package/dist/cli/{index-se8xc78k.js → index-5hyq2mw6.js} +30 -30
  25. package/dist/cli/{index-5zyk8qgv.js → index-5syvwd8g.js} +1 -1
  26. package/dist/cli/{index-ktagrqxw.js → index-6axm8q3s.js} +3 -3
  27. package/dist/cli/{index-668dpw5e.js → index-6mvbjqbt.js} +1 -1
  28. package/dist/cli/{index-wsdnttf4.js → index-74hpm6sh.js} +1 -1
  29. package/dist/cli/{index-wrt6m5ze.js → index-ajwmn8gp.js} +289 -73
  30. package/dist/cli/{index-6w1re1qn.js → index-ba5e5e3n.js} +2 -2
  31. package/dist/cli/{index-8gftfvpp.js → index-bcwx1335.js} +1 -1
  32. package/dist/cli/{index-a12906mj.js → index-gen82t10.js} +3 -3
  33. package/dist/cli/{index-s608d3hw.js → index-hckq2r5m.js} +1 -1
  34. package/dist/cli/{index-e9tx99ds.js → index-hwnpd4tw.js} +3 -3
  35. package/dist/cli/{index-3radty3f.js → index-jqn332jx.js} +2 -2
  36. package/dist/cli/{index-x9q4bbhd.js → index-k87hs2ff.js} +1 -1
  37. package/dist/cli/{index-8p9pasnp.js → index-kyz7259r.js} +2 -2
  38. package/dist/cli/{index-prmb5qfp.js → index-mv0czd9c.js} +2 -2
  39. package/dist/cli/{index-6c5f309q.js → index-mzck763x.js} +2 -2
  40. package/dist/cli/{index-a7nh382r.js → index-tkg6rapy.js} +1344 -682
  41. package/dist/cli/{index-yr6x707c.js → index-v7g8dnyh.js} +3 -3
  42. package/dist/cli/{index-w71cq27a.js → index-xeyr62x0.js} +5 -5
  43. package/dist/cli/{index-vm4xw9z3.js → index-xsxrhakg.js} +1 -1
  44. package/dist/cli/{index-5w7ekpy3.js → index-xzzcfwwm.js} +1 -1
  45. package/dist/cli/{index-q11t5erh.js → index-yawwwhmc.js} +2 -2
  46. package/dist/cli/{index-q7ezzgwq.js → index-yh0q21nj.js} +1 -1
  47. package/dist/cli/{index-cyw81ngh.js → index-z6qe8wft.js} +6 -6
  48. package/dist/cli/index.js +28 -28
  49. package/dist/cli/{knowledge-escalator-n9pmx4qr.js → knowledge-escalator-kkjsjtza.js} +6 -6
  50. package/dist/cli/{knowledge-events-raevm18w.js → knowledge-events-jbw9j94n.js} +4 -4
  51. package/dist/cli/{knowledge-link-3tq8w6gs.js → knowledge-link-f6akgbdn.js} +3 -3
  52. package/dist/cli/{knowledge-store-bcfqz8fr.js → knowledge-store-m7vnz62z.js} +4 -4
  53. package/dist/cli/{knowledge-validator-jxqj96cx.js → knowledge-validator-rqj7kech.js} +7 -7
  54. package/dist/cli/{pending-delegations-tmyf18p4.js → pending-delegations-14544jpr.js} +3 -3
  55. package/dist/cli/{pr-subscriptions-hhx6yk24.js → pr-subscriptions-qdwfm1a8.js} +3 -3
  56. package/dist/cli/{runner-w30gazew.js → runner-mzb3e2s6.js} +4 -4
  57. package/dist/cli/{scan-cursor-ah5swtyh.js → scan-cursor-8e6ryhjq.js} +5 -5
  58. package/dist/cli/{schema-f7jgrtxj.js → schema-g7rs20c6.js} +2 -2
  59. package/dist/cli/{scope-persistence-3f0d4fwv.js → scope-persistence-wy65zyha.js} +2 -2
  60. package/dist/cli/{skill-generator-hp7as46c.js → skill-generator-2rv6ftre.js} +8 -8
  61. package/dist/cli/{task-envelope-qn0qtnh0.js → task-envelope-jp2fdsvn.js} +11 -8
  62. package/dist/cli/{workspace-snapshot-h5rzw37b.js → workspace-snapshot-w3tg4ne5.js} +2 -2
  63. package/dist/cli/{worktree-collision-ownership-vexb0b6r.js → worktree-collision-ownership-24s0t62z.js} +4 -4
  64. package/dist/commands/approve-plan-critic.d.ts +2 -2
  65. package/dist/commands/registry.d.ts +1 -1
  66. package/dist/config/agent-model.d.ts +35 -0
  67. package/dist/db/qa-gate-profile.d.ts +11 -3
  68. package/dist/evidence/manager.d.ts +1 -1
  69. package/dist/evidence/phase-participation.d.ts +44 -0
  70. package/dist/hooks/compaction-customizer.d.ts +30 -2
  71. package/dist/hooks/context-budget.d.ts +1 -1
  72. package/dist/hooks/delegation-gate.d.ts +3 -3
  73. package/dist/hooks/guardrails/index.d.ts +5 -1
  74. package/dist/hooks/guardrails/messages-transform.d.ts +2 -0
  75. package/dist/hooks/guardrails/pre-check-result.d.ts +20 -0
  76. package/dist/index.js +420 -415
  77. package/dist/sast/semgrep.d.ts +15 -32
  78. package/dist/services/compaction-service.d.ts +3 -1
  79. package/dist/session/snapshot-writer.d.ts +1 -0
  80. package/dist/state.d.ts +70 -19
  81. package/dist/tools/context-status.d.ts +4 -1
  82. package/dist/tools/lint.d.ts +48 -22
  83. package/dist/tools/pre-check-batch.d.ts +37 -13
  84. package/dist/tools/quality-budget.d.ts +3 -2
  85. package/dist/tools/sast-baseline.d.ts +1 -0
  86. package/dist/tools/sast-scan.d.ts +4 -1
  87. package/dist/tools/secretscan.d.ts +2 -2
  88. package/dist/tools/set-qa-gates.d.ts +5 -4
  89. package/dist/utils/bun-compat.d.ts +20 -0
  90. package/dist/utils/external-tool-runner.d.ts +10 -0
  91. package/package.json +1 -1
@@ -5,7 +5,7 @@ import {
5
5
  getProfileForFile,
6
6
  isCommandAvailable,
7
7
  tool
8
- } from "./index-vm4xw9z3.js";
8
+ } from "./index-xsxrhakg.js";
9
9
  import {
10
10
  captureWorkingTreeFingerprint,
11
11
  createModelEvaluationExecutor,
@@ -13,16 +13,16 @@ import {
13
13
  resolveExecutableFromPath,
14
14
  runEvaluation,
15
15
  runExternalTool
16
- } from "./index-wrt6m5ze.js";
16
+ } from "./index-ajwmn8gp.js";
17
17
  import {
18
18
  stableCanonicalStringify
19
- } from "./index-y64jv2pe.js";
19
+ } from "./index-0mzz46we.js";
20
20
  import {
21
21
  detectStraySwarmDirs,
22
22
  readDoctorArtifact,
23
23
  removeStraySwarmDir,
24
24
  runConfigDoctor
25
- } from "./index-y47dwge1.js";
25
+ } from "./index-1yd38ajy.js";
26
26
  import {
27
27
  MAX_TRANSIENT_RETRIES,
28
28
  archiveEvidence,
@@ -61,7 +61,7 @@ import {
61
61
  savePlan,
62
62
  transientBackoff,
63
63
  validateProjectRoot
64
- } from "./index-0wp67gqj.js";
64
+ } from "./index-4fr94329.js";
65
65
  import {
66
66
  EvaluationTaskV1Schema,
67
67
  GateAuditManifestV1Schema,
@@ -78,7 +78,7 @@ import {
78
78
  saveGateAuditResult,
79
79
  savePromotionDecision,
80
80
  saveTaskSetSnapshot
81
- } from "./index-s608d3hw.js";
81
+ } from "./index-hckq2r5m.js";
82
82
  import {
83
83
  canonicalHash,
84
84
  canonicalJson,
@@ -116,15 +116,15 @@ import {
116
116
  retireSkill,
117
117
  selectCandidateEntries,
118
118
  syncBundledProjectSkillsIfMissingAsync
119
- } from "./index-ktagrqxw.js";
119
+ } from "./index-6axm8q3s.js";
120
120
  import {
121
121
  readRecentEscalations
122
- } from "./index-w71cq27a.js";
122
+ } from "./index-xeyr62x0.js";
123
123
  import {
124
124
  alreadyCuratedThisGeneration,
125
125
  claimNextScanBatch,
126
126
  resolveCohortId
127
- } from "./index-6w1re1qn.js";
127
+ } from "./index-ba5e5e3n.js";
128
128
  import {
129
129
  CURATOR_INIT_PROMPT,
130
130
  CURATOR_PHASE_PROMPT,
@@ -166,7 +166,7 @@ import {
166
166
  resolveRemoteRefsContainingHead,
167
167
  resolveRemoteRefsContainingHeadAsync,
168
168
  switchPrFeedbackTrackingCandidateAsync
169
- } from "./index-wsdnttf4.js";
169
+ } from "./index-74hpm6sh.js";
170
170
  import {
171
171
  appendUnactionable,
172
172
  quarantineEntry,
@@ -177,11 +177,11 @@ import {
177
177
  validateActionableFields,
178
178
  validateLesson,
179
179
  validateSkillPath
180
- } from "./index-ffpmqx30.js";
180
+ } from "./index-1jbj7vzv.js";
181
181
  import {
182
182
  authorizeCuration,
183
183
  buildConfigFingerprintInput
184
- } from "./index-3radty3f.js";
184
+ } from "./index-jqn332jx.js";
185
185
  import {
186
186
  cohortConfigFingerprint
187
187
  } from "./index-rtry5xyf.js";
@@ -214,7 +214,7 @@ import {
214
214
  transactFile,
215
215
  transactKnowledge,
216
216
  wordBigrams
217
- } from "./index-cyw81ngh.js";
217
+ } from "./index-z6qe8wft.js";
218
218
  import {
219
219
  MAX_EVENT_LOG_ENTRIES,
220
220
  _internals,
@@ -224,7 +224,7 @@ import {
224
224
  recordHiveKnowledgeEvent,
225
225
  recordKnowledgeEvent,
226
226
  resolveKnowledgeEventsPath
227
- } from "./index-e9tx99ds.js";
227
+ } from "./index-hwnpd4tw.js";
228
228
  import {
229
229
  resolveHiveDataDir,
230
230
  resolveHiveEventsPath,
@@ -240,16 +240,16 @@ import {
240
240
  resolveLinkDir,
241
241
  sanitizeLinkId,
242
242
  writeLinkPointer
243
- } from "./index-668dpw5e.js";
243
+ } from "./index-6mvbjqbt.js";
244
244
  import {
245
245
  atomicWriteFile
246
- } from "./index-gvhvz176.js";
246
+ } from "./index-2eet124q.js";
247
247
  import {
248
248
  exports_loader,
249
249
  init_loader,
250
250
  loadPluginConfig,
251
251
  loadPluginConfigWithMeta
252
- } from "./index-q11t5erh.js";
252
+ } from "./index-yawwwhmc.js";
253
253
  import {
254
254
  RetrospectiveEvidenceSchema
255
255
  } from "./index-gprxjmm7.js";
@@ -266,7 +266,7 @@ import {
266
266
  package_default,
267
267
  resolveAutoReviewConfig,
268
268
  stripKnownSwarmPrefix
269
- } from "./index-8p9pasnp.js";
269
+ } from "./index-kyz7259r.js";
270
270
  import {
271
271
  KNOWLEDGE_SCHEMA_VERSION,
272
272
  OUTCOME_BLOCK_THRESHOLD,
@@ -282,13 +282,13 @@ import {
282
282
  listActive,
283
283
  subscribe,
284
284
  unsubscribe
285
- } from "./index-5zyk8qgv.js";
285
+ } from "./index-5syvwd8g.js";
286
286
  import {
287
287
  MAX_PENDING_SCOPE_BINDINGS,
288
288
  clearAllScopes,
289
289
  clearScopeBindingFromDisk,
290
290
  clearScopeBindings
291
- } from "./index-3eddtgxt.js";
291
+ } from "./index-05mms0va.js";
292
292
  import {
293
293
  appendLedgerEvent,
294
294
  computePlanHash,
@@ -311,6 +311,7 @@ import {
311
311
  } from "./index-4rhhvd1a.js";
312
312
  import {
313
313
  AGENT_TOOL_MAP,
314
+ ALL_AGENT_NAMES,
314
315
  ALL_SUBAGENT_NAMES,
315
316
  ORCHESTRATOR_NAME,
316
317
  SWARM_WORKTREE_DIR_NAME,
@@ -319,16 +320,16 @@ import {
319
320
  init_agent_names,
320
321
  init_constants,
321
322
  init_tool_metadata
322
- } from "./index-3jcyn8g6.js";
323
+ } from "./index-4wjk296r.js";
323
324
  import {
324
325
  findByBatchId,
325
326
  readDelegations
326
- } from "./index-prmb5qfp.js";
327
+ } from "./index-mv0czd9c.js";
327
328
  import {
328
329
  readSwarmFileAsync,
329
330
  safeHook,
330
331
  validateSwarmPath
331
- } from "./index-x9q4bbhd.js";
332
+ } from "./index-k87hs2ff.js";
332
333
  import {
333
334
  invalidateCachedArtifact,
334
335
  resetSwarmArtifactCache
@@ -353,7 +354,7 @@ import {
353
354
  bunSpawn,
354
355
  bunWrite,
355
356
  mergeEnvForChild
356
- } from "./index-y111zefa.js";
357
+ } from "./index-4nt52yr9.js";
357
358
  import {
358
359
  criticalWarn,
359
360
  error,
@@ -367,6 +368,19 @@ import {
367
368
  __toESM
368
369
  } from "./index-a76rekgs.js";
369
370
 
371
+ // src/config/agent-model.ts
372
+ init_agent_names();
373
+ init_constants();
374
+ var CANONICAL_AGENT_NAMES = new Set(ALL_AGENT_NAMES);
375
+ var KNOWN_EMBEDDED_VARIANTS = new Set([
376
+ "low",
377
+ "medium",
378
+ "high",
379
+ "max",
380
+ "xhigh",
381
+ "thinking"
382
+ ]);
383
+
370
384
  // src/agents/index.ts
371
385
  init_constants();
372
386
  init_schema();
@@ -4900,71 +4914,6 @@ import * as path20 from "path";
4900
4914
  init_agent_names();
4901
4915
  init_schema();
4902
4916
 
4903
- // src/turbo/epic/cochange-conflict.ts
4904
- function pathMatches(scopePath, cochangePath) {
4905
- if (scopePath === cochangePath)
4906
- return true;
4907
- return scopePath.endsWith(`/${cochangePath}`);
4908
- }
4909
- function epicPairConflict(scopeA, scopeB, cochangePairs, threshold) {
4910
- const normA = scopeA.map(normalizePath);
4911
- const normB = scopeB.map(normalizePath);
4912
- const pathPairs = [];
4913
- for (const a of normA) {
4914
- for (const b of normB) {
4915
- if (pathsConflict(a, b)) {
4916
- pathPairs.push([a, b]);
4917
- }
4918
- }
4919
- }
4920
- const pathConflict = pathPairs.length > 0;
4921
- const activePairs = cochangePairs.filter((e) => e.coChangeCount >= threshold.minCoChanges && e.npmi >= threshold.npmi);
4922
- const cochangeMatches = [];
4923
- for (const entry of activePairs) {
4924
- const fa = entry.fileA;
4925
- const fb = entry.fileB;
4926
- const aTouchesFa = normA.some((p) => pathMatches(p, fa));
4927
- const aTouchesFb = normA.some((p) => pathMatches(p, fb));
4928
- const bTouchesFa = normB.some((p) => pathMatches(p, fa));
4929
- const bTouchesFb = normB.some((p) => pathMatches(p, fb));
4930
- const aExclusivelyFa = aTouchesFa && !aTouchesFb;
4931
- const aExclusivelyFb = aTouchesFb && !aTouchesFa;
4932
- const bExclusivelyFa = bTouchesFa && !bTouchesFb;
4933
- const bExclusivelyFb = bTouchesFb && !bTouchesFa;
4934
- const crossCouple = aExclusivelyFa && bExclusivelyFb || aExclusivelyFb && bExclusivelyFa;
4935
- if (crossCouple) {
4936
- cochangeMatches.push({
4937
- a: fa,
4938
- b: fb,
4939
- npmi: entry.npmi,
4940
- coChangeCount: entry.coChangeCount
4941
- });
4942
- }
4943
- }
4944
- const cochangeConflict = cochangeMatches.length > 0;
4945
- let reason;
4946
- if (pathConflict && cochangeConflict)
4947
- reason = "both";
4948
- else if (pathConflict)
4949
- reason = "path";
4950
- else if (cochangeConflict)
4951
- reason = "cochange";
4952
- else
4953
- reason = "none";
4954
- return {
4955
- conflict: pathConflict || cochangeConflict,
4956
- reason,
4957
- evidence: { pathPairs, cochangePairs: cochangeMatches }
4958
- };
4959
- }
4960
-
4961
- // src/state.ts
4962
- init_constants();
4963
- import { randomUUID as randomUUID2 } from "crypto";
4964
- import * as fs2 from "fs/promises";
4965
- import * as path7 from "path";
4966
- init_schema();
4967
-
4968
4917
  // src/db/qa-gate-profile.ts
4969
4918
  import { createHash as createHash3 } from "crypto";
4970
4919
 
@@ -5067,7 +5016,8 @@ var _internals5 = {
5067
5016
  setGates,
5068
5017
  getEffectiveGates,
5069
5018
  computeProfileHash,
5070
- hasAnyProfileWithEnabledGate
5019
+ hasAnyProfileWithEnabledGate,
5020
+ afterSetGatesRead: () => {}
5071
5021
  };
5072
5022
  var DEFAULT_QA_GATES = {
5073
5023
  reviewer: true,
@@ -5082,6 +5032,7 @@ var DEFAULT_QA_GATES = {
5082
5032
  drift_check: true,
5083
5033
  final_council: false
5084
5034
  };
5035
+ var qaGateProfileSavepointCounter = 0;
5085
5036
  function rowToProfile(row) {
5086
5037
  let parsed = {};
5087
5038
  try {
@@ -5100,7 +5051,7 @@ function rowToProfile(row) {
5100
5051
  const knownKeys = new Set(Object.keys(DEFAULT_QA_GATES));
5101
5052
  const filteredParsed = {};
5102
5053
  for (const key of Object.keys(parsed)) {
5103
- if (knownKeys.has(key)) {
5054
+ if (knownKeys.has(key) && typeof parsed[key] === "boolean") {
5104
5055
  filteredParsed[key] = parsed[key];
5105
5056
  }
5106
5057
  }
@@ -5136,12 +5087,18 @@ function hasAnyProfileWithEnabledGate(directory, gate) {
5136
5087
  }
5137
5088
  return false;
5138
5089
  }
5139
- function getOrCreateProfile(directory, planId, projectType) {
5090
+ function getOrCreateProfile(directory, planId, projectType, initialGates = {}) {
5140
5091
  const existing = _internals5.getProfile(directory, planId);
5141
5092
  if (existing)
5142
5093
  return existing;
5143
5094
  const db = getProjectDb(directory);
5144
- const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
5095
+ const seededGates = { ...DEFAULT_QA_GATES };
5096
+ for (const key of Object.keys(DEFAULT_QA_GATES)) {
5097
+ if (typeof initialGates[key] === "boolean") {
5098
+ seededGates[key] = initialGates[key];
5099
+ }
5100
+ }
5101
+ const gatesJson = JSON.stringify(seededGates);
5145
5102
  const insert = db.transaction(() => {
5146
5103
  db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
5147
5104
  });
@@ -5160,35 +5117,51 @@ function getOrCreateProfile(directory, planId, projectType) {
5160
5117
  return after;
5161
5118
  }
5162
5119
  function setGates(directory, planId, gates) {
5163
- const current = _internals5.getProfile(directory, planId);
5164
- if (!current) {
5165
- throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
5166
- }
5167
- if (current.locked_at !== null) {
5168
- throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
5169
- }
5170
- const merged = { ...current.gates };
5171
- for (const key of Object.keys(gates)) {
5172
- const incoming = gates[key];
5173
- if (incoming === undefined)
5174
- continue;
5175
- if (incoming === false && current.gates[key] === true) {
5176
- throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
5120
+ const db = getProjectDb(directory);
5121
+ const savepointName = `qa_gate_profile_set_${qaGateProfileSavepointCounter++}`;
5122
+ const startedTransaction = !db.inTransaction;
5123
+ db.run(startedTransaction ? "BEGIN IMMEDIATE" : `SAVEPOINT ${savepointName}`);
5124
+ try {
5125
+ const currentRow = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
5126
+ if (!currentRow) {
5127
+ throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
5128
+ }
5129
+ const current = rowToProfile(currentRow);
5130
+ if (current.locked_at !== null) {
5131
+ throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
5132
+ }
5133
+ _internals5.afterSetGatesRead(current);
5134
+ const merged = { ...current.gates };
5135
+ for (const key of Object.keys(gates)) {
5136
+ const incoming = gates[key];
5137
+ if (incoming === undefined)
5138
+ continue;
5139
+ if (incoming === false && current.gates[key] === true) {
5140
+ throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
5141
+ }
5142
+ if (incoming === true) {
5143
+ merged[key] = true;
5144
+ }
5177
5145
  }
5178
- if (incoming === true) {
5179
- merged[key] = true;
5146
+ db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
5147
+ JSON.stringify(merged),
5148
+ planId
5149
+ ]);
5150
+ const updatedRow = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
5151
+ if (!updatedRow) {
5152
+ throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
5180
5153
  }
5154
+ db.run(startedTransaction ? "COMMIT" : `RELEASE ${savepointName}`);
5155
+ return rowToProfile(updatedRow);
5156
+ } catch (err) {
5157
+ try {
5158
+ db.run(startedTransaction ? "ROLLBACK" : `ROLLBACK TO ${savepointName}`);
5159
+ if (!startedTransaction) {
5160
+ db.run(`RELEASE ${savepointName}`);
5161
+ }
5162
+ } catch {}
5163
+ throw err;
5181
5164
  }
5182
- const db = getProjectDb(directory);
5183
- db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
5184
- JSON.stringify(merged),
5185
- planId
5186
- ]);
5187
- const updated = _internals5.getProfile(directory, planId);
5188
- if (!updated) {
5189
- throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
5190
- }
5191
- return updated;
5192
5165
  }
5193
5166
  function computeProfileHash(profile) {
5194
5167
  const payload = JSON.stringify({
@@ -5207,6 +5180,71 @@ function getEffectiveGates(profile, sessionOverrides) {
5207
5180
  return merged;
5208
5181
  }
5209
5182
 
5183
+ // src/turbo/epic/cochange-conflict.ts
5184
+ function pathMatches(scopePath, cochangePath) {
5185
+ if (scopePath === cochangePath)
5186
+ return true;
5187
+ return scopePath.endsWith(`/${cochangePath}`);
5188
+ }
5189
+ function epicPairConflict(scopeA, scopeB, cochangePairs, threshold) {
5190
+ const normA = scopeA.map(normalizePath);
5191
+ const normB = scopeB.map(normalizePath);
5192
+ const pathPairs = [];
5193
+ for (const a of normA) {
5194
+ for (const b of normB) {
5195
+ if (pathsConflict(a, b)) {
5196
+ pathPairs.push([a, b]);
5197
+ }
5198
+ }
5199
+ }
5200
+ const pathConflict = pathPairs.length > 0;
5201
+ const activePairs = cochangePairs.filter((e) => e.coChangeCount >= threshold.minCoChanges && e.npmi >= threshold.npmi);
5202
+ const cochangeMatches = [];
5203
+ for (const entry of activePairs) {
5204
+ const fa = entry.fileA;
5205
+ const fb = entry.fileB;
5206
+ const aTouchesFa = normA.some((p) => pathMatches(p, fa));
5207
+ const aTouchesFb = normA.some((p) => pathMatches(p, fb));
5208
+ const bTouchesFa = normB.some((p) => pathMatches(p, fa));
5209
+ const bTouchesFb = normB.some((p) => pathMatches(p, fb));
5210
+ const aExclusivelyFa = aTouchesFa && !aTouchesFb;
5211
+ const aExclusivelyFb = aTouchesFb && !aTouchesFa;
5212
+ const bExclusivelyFa = bTouchesFa && !bTouchesFb;
5213
+ const bExclusivelyFb = bTouchesFb && !bTouchesFa;
5214
+ const crossCouple = aExclusivelyFa && bExclusivelyFb || aExclusivelyFb && bExclusivelyFa;
5215
+ if (crossCouple) {
5216
+ cochangeMatches.push({
5217
+ a: fa,
5218
+ b: fb,
5219
+ npmi: entry.npmi,
5220
+ coChangeCount: entry.coChangeCount
5221
+ });
5222
+ }
5223
+ }
5224
+ const cochangeConflict = cochangeMatches.length > 0;
5225
+ let reason;
5226
+ if (pathConflict && cochangeConflict)
5227
+ reason = "both";
5228
+ else if (pathConflict)
5229
+ reason = "path";
5230
+ else if (cochangeConflict)
5231
+ reason = "cochange";
5232
+ else
5233
+ reason = "none";
5234
+ return {
5235
+ conflict: pathConflict || cochangeConflict,
5236
+ reason,
5237
+ evidence: { pathPairs, cochangePairs: cochangeMatches }
5238
+ };
5239
+ }
5240
+
5241
+ // src/state.ts
5242
+ init_constants();
5243
+ import { randomUUID as randomUUID2 } from "crypto";
5244
+ import * as fs2 from "fs/promises";
5245
+ import * as path7 from "path";
5246
+ init_schema();
5247
+
5210
5248
  // src/hooks/realtime-learning-nudge.ts
5211
5249
  var realtimeLearningNudgeBySession = new Map;
5212
5250
  function resetRealtimeLearningNudgeState() {
@@ -5686,8 +5724,7 @@ var swarmState = {
5686
5724
  knowledgeAckDedup: new Set,
5687
5725
  gateDenialCounts: new Map,
5688
5726
  generatedAgentNames: [],
5689
- lastBudgetPct: 0,
5690
- lastBudgetTokens: 0,
5727
+ lastBudgetBySession: new Map,
5691
5728
  liveContextWindows: new Map,
5692
5729
  agentSessions: defaultRunContext.agentSessions,
5693
5730
  pendingRehydrations: new Set,
@@ -5700,8 +5737,7 @@ function resetSwarmState() {
5700
5737
  swarmState.activeAgent.clear();
5701
5738
  swarmState.delegationChains.clear();
5702
5739
  swarmState.pendingEvents = 0;
5703
- swarmState.lastBudgetPct = 0;
5704
- swarmState.lastBudgetTokens = 0;
5740
+ swarmState.lastBudgetBySession.clear();
5705
5741
  swarmState.liveContextWindows.clear();
5706
5742
  swarmState.agentSessions.clear();
5707
5743
  _lastIdleSweepAtMs = 0;
@@ -6530,10 +6566,19 @@ function hasActiveEpicMode(sessionID) {
6530
6566
  }
6531
6567
  return false;
6532
6568
  }
6569
+ function getDisplayBudget() {
6570
+ let best = null;
6571
+ for (const entry of swarmState.lastBudgetBySession.values()) {
6572
+ if (entry.pct > 0 && (best === null || entry.pct > best.pct)) {
6573
+ best = entry;
6574
+ }
6575
+ }
6576
+ return best;
6577
+ }
6533
6578
  async function rehydratePrSubscriptions(sessionID, directory) {
6534
6579
  const map = new Map;
6535
6580
  try {
6536
- const { listActive: listActive2 } = await import("./pr-subscriptions-hhx6yk24.js");
6581
+ const { listActive: listActive2 } = await import("./pr-subscriptions-qdwfm1a8.js");
6537
6582
  const records = await listActive2(directory);
6538
6583
  for (const record of records) {
6539
6584
  if (record.sessionID !== sessionID)
@@ -18989,7 +19034,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
18989
19034
  };
18990
19035
  }
18991
19036
  async function sweepActiveNearDuplicates(directory, options = {}) {
18992
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-yr6x707c.js");
19037
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
18993
19038
  const { config } = loadPluginConfigWithMeta2(directory);
18994
19039
  const sweepConfig = config.learning?.dedup_sweep;
18995
19040
  if (sweepConfig?.enabled === false)
@@ -20584,7 +20629,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
20584
20629
  }
20585
20630
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
20586
20631
  try {
20587
- const skillModule = await import("./skill-generator-hp7as46c.js");
20632
+ const skillModule = await import("./skill-generator-2rv6ftre.js");
20588
20633
  for (const cand of skillCandidates) {
20589
20634
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
20590
20635
  continue;
@@ -21044,7 +21089,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
21044
21089
  } catch {}
21045
21090
  }
21046
21091
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
21047
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-n9pmx4qr.js");
21092
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-kkjsjtza.js");
21048
21093
  for (const { id } of contradictedEntries) {
21049
21094
  try {
21050
21095
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -21863,7 +21908,7 @@ async function runCuratorPostMortem(directory, options = {}) {
21863
21908
  warnings.push("Failed to collect knowledge summary.");
21864
21909
  }
21865
21910
  try {
21866
- const { getScanStatus } = await import("./scan-cursor-ah5swtyh.js");
21911
+ const { getScanStatus } = await import("./scan-cursor-8e6ryhjq.js");
21867
21912
  const scanStatus = await getScanStatus(directory);
21868
21913
  if (scanStatus.remaining_estimate > 0) {
21869
21914
  warnings.push(`Fair scan: reviewed ${knowledgeSummary.length} entries this sweep (generation ${scanStatus.generation}); ${scanStatus.remaining_estimate} more eligible entries will be visited in future curation sweeps.`);
@@ -22035,9 +22080,9 @@ var _internals24 = {
22035
22080
  verifyPostMortemKnowledgeActions,
22036
22081
  repairPostMortemActions,
22037
22082
  loadDefaultKnowledgeConfig: async (directory) => {
22038
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-f7jgrtxj.js");
22083
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
22039
22084
  try {
22040
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-yr6x707c.js");
22085
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
22041
22086
  const loaded = loadPluginConfigWithMeta2(directory);
22042
22087
  return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
22043
22088
  } catch {
@@ -22045,11 +22090,11 @@ var _internals24 = {
22045
22090
  }
22046
22091
  },
22047
22092
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
22048
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-vpcs1p1v.js");
22093
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-m6mk2v1n.js");
22049
22094
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
22050
22095
  },
22051
22096
  checkHivePromotions: async (entries, knowledgeConfig, directory) => {
22052
- const { checkHivePromotions } = await import("./hive-promoter-mpf0cfkk.js");
22097
+ const { checkHivePromotions } = await import("./hive-promoter-v4w7cjvk.js");
22053
22098
  return checkHivePromotions(entries, knowledgeConfig, directory);
22054
22099
  },
22055
22100
  applyProposalTriage: async (directory, triage) => {
@@ -22058,7 +22103,7 @@ var _internals24 = {
22058
22103
  activateProposal,
22059
22104
  listSkills: listSkills2,
22060
22105
  sanitizeSlug
22061
- } = await import("./skill-generator-hp7as46c.js");
22106
+ } = await import("./skill-generator-2rv6ftre.js");
22062
22107
  const result = {
22063
22108
  approved: [],
22064
22109
  rejected: [],
@@ -22100,7 +22145,7 @@ var _internals24 = {
22100
22145
  return result;
22101
22146
  },
22102
22147
  readPriorDriftReports: async (directory) => {
22103
- const { readPriorDriftReports } = await import("./curator-drift-vfh07mqy.js");
22148
+ const { readPriorDriftReports } = await import("./curator-drift-r52hjskk.js");
22104
22149
  return readPriorDriftReports(directory);
22105
22150
  },
22106
22151
  readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
@@ -24686,9 +24731,9 @@ function hashContent(content) {
24686
24731
  return createHash9("sha1").update(content).digest("hex");
24687
24732
  }
24688
24733
  async function loadConfigForPolicyCurator(directory) {
24689
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-f7jgrtxj.js");
24734
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
24690
24735
  try {
24691
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-yr6x707c.js");
24736
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
24692
24737
  const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
24693
24738
  return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
24694
24739
  } catch {
@@ -28697,7 +28742,7 @@ async function runFinalizeStage(ctx) {
28697
28742
  }
28698
28743
  }
28699
28744
  try {
28700
- const { CuratorConfigSchema: CCS } = await import("./schema-f7jgrtxj.js");
28745
+ const { CuratorConfigSchema: CCS } = await import("./schema-g7rs20c6.js");
28701
28746
  const { config: pmLoadedConfig } = _internals37.loadPluginConfigWithMeta(ctx.directory);
28702
28747
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
28703
28748
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -30970,9 +31015,9 @@ var _internals42 = {
30970
31015
  loadPluginConfigWithMeta,
30971
31016
  loadCuratorDeps: async () => {
30972
31017
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
30973
- import("./schema-f7jgrtxj.js"),
30974
- import("./curator-vpcs1p1v.js"),
30975
- import("./curator-llm-factory-hvw13hmr.js")
31018
+ import("./schema-g7rs20c6.js"),
31019
+ import("./curator-m6mk2v1n.js"),
31020
+ import("./curator-llm-factory-1r9vz0g2.js")
30976
31021
  ]);
30977
31022
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
30978
31023
  }
@@ -31877,7 +31922,7 @@ async function computeCurationDiagnostics(directory, entries) {
31877
31922
  let scanCompleted = false;
31878
31923
  let scanRemaining = 0;
31879
31924
  try {
31880
- const { getScanStatus } = await import("./scan-cursor-ah5swtyh.js");
31925
+ const { getScanStatus } = await import("./scan-cursor-8e6ryhjq.js");
31881
31926
  const status = await getScanStatus(directory);
31882
31927
  scanGeneration = status.generation;
31883
31928
  scanCompleted = status.completed;
@@ -31885,19 +31930,19 @@ async function computeCurationDiagnostics(directory, entries) {
31885
31930
  } catch {}
31886
31931
  let configMatch = null;
31887
31932
  try {
31888
- const { isLinked: isLinked2 } = await import("./knowledge-link-3tq8w6gs.js");
31933
+ const { isLinked: isLinked2 } = await import("./knowledge-link-f6akgbdn.js");
31889
31934
  if (isLinked2(directory)) {
31890
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-f7jgrtxj.js");
31935
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
31891
31936
  let config;
31892
31937
  try {
31893
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-yr6x707c.js");
31938
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
31894
31939
  const loaded = loadPluginConfigWithMeta2(directory);
31895
31940
  config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
31896
31941
  } catch {
31897
31942
  config = KnowledgeConfigSchema2.parse({});
31898
31943
  }
31899
31944
  const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
31900
- const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-4tp0mbcb.js");
31945
+ const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-f46tf3dw.js");
31901
31946
  const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
31902
31947
  const cohortFp = await readCohortConfigFingerprint(directory);
31903
31948
  configMatch = cohortFp === null ? null : currentFp === cohortFp;
@@ -33108,7 +33153,7 @@ async function handleDoctorCommand(directory, args) {
33108
33153
  const result = runConfigDoctor(config, directory);
33109
33154
  let output;
33110
33155
  if (enableAutoFix && result.hasAutoFixableIssues) {
33111
- const { runConfigDoctorWithFixes } = await import("./config-doctor-hrb61yqe.js");
33156
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-75qky762.js");
33112
33157
  const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
33113
33158
  applyLossy: true
33114
33159
  });
@@ -33945,7 +33990,7 @@ async function handleEvidenceCommand(directory, args) {
33945
33990
  return formatTaskEvidenceMarkdown(evidenceData);
33946
33991
  }
33947
33992
  async function handleEvidenceSummaryCommand(directory) {
33948
- const { buildEvidenceSummary } = await import("./evidence-summary-service-kzxr71yd.js");
33993
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-sxcq5nrt.js");
33949
33994
  const artifact = await buildEvidenceSummary(directory);
33950
33995
  if (!artifact) {
33951
33996
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -35621,7 +35666,8 @@ function validateInput(input) {
35621
35666
  }
35622
35667
  return { valid: true };
35623
35668
  }
35624
- async function qualityBudget(input, directory) {
35669
+ async function qualityBudget(input, directory, abortSignal) {
35670
+ abortSignal?.throwIfAborted();
35625
35671
  const validation = validateInput(input);
35626
35672
  if (!validation.valid) {
35627
35673
  throw new Error(`Invalid input: ${validation.error}`);
@@ -35661,10 +35707,12 @@ async function qualityBudget(input, directory) {
35661
35707
  }
35662
35708
  };
35663
35709
  }
35664
- const metrics = await computeQualityMetrics(changedFiles, thresholds, directory);
35710
+ const metrics = await _internals49.computeQualityMetrics(changedFiles, thresholds, directory);
35711
+ abortSignal?.throwIfAborted();
35665
35712
  const errorsCount = metrics.violations.filter((v) => v.severity === "error").length;
35666
35713
  const warningsCount = metrics.violations.filter((v) => v.severity === "warning").length;
35667
35714
  const verdict = errorsCount > 0 ? "fail" : "pass";
35715
+ abortSignal?.throwIfAborted();
35668
35716
  await saveEvidence(directory, "quality_budget", {
35669
35717
  task_id: "quality_budget",
35670
35718
  type: "quality_budget",
@@ -35691,7 +35739,8 @@ async function qualityBudget(input, directory) {
35691
35739
  files: v.files
35692
35740
  })),
35693
35741
  files_analyzed: metrics.files_analyzed
35694
- });
35742
+ }, abortSignal);
35743
+ abortSignal?.throwIfAborted();
35695
35744
  return {
35696
35745
  verdict,
35697
35746
  metrics,
@@ -35718,13 +35767,14 @@ var quality_budget = createSwarmTool({
35718
35767
  exclude_globs: exports_external.array(exports_external.string()).optional()
35719
35768
  }).optional().describe("Quality budget thresholds")
35720
35769
  },
35721
- async execute(args, directory) {
35722
- const result = await _internals49.qualityBudget(args, directory);
35770
+ async execute(args, directory, ctx) {
35771
+ const result = await _internals49.qualityBudget(args, directory, ctx?.abort);
35723
35772
  return JSON.stringify(result);
35724
35773
  }
35725
35774
  });
35726
35775
  var _internals49 = {
35727
- qualityBudget
35776
+ qualityBudget,
35777
+ computeQualityMetrics
35728
35778
  };
35729
35779
 
35730
35780
  // src/tools/sast-scan.ts
@@ -36583,15 +36633,75 @@ function executeRulesSync(filePath, content, language) {
36583
36633
  }
36584
36634
 
36585
36635
  // src/sast/semgrep.ts
36586
- init_logger();
36587
- import * as child_process6 from "child_process";
36588
36636
  import * as fs24 from "fs";
36589
36637
  import * as path61 from "path";
36590
36638
  var semgrepAvailableCache = null;
36639
+ var semgrepAvailabilityProbe = null;
36640
+ var semgrepAvailabilityGeneration = 0;
36591
36641
  var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
36592
36642
  var DEFAULT_TIMEOUT_MS2 = 30000;
36643
+ var SEMGREP_AVAILABILITY_TIMEOUT_MS = 5000;
36644
+ var SEMGREP_AVAILABILITY_MAX_STDOUT_BYTES = 16 * 1024;
36645
+ var SEMGREP_AVAILABILITY_MAX_STDERR_BYTES = 16 * 1024;
36593
36646
  var MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
36594
- var KILL_GRACE_MS = 2000;
36647
+ function resolveSemgrepBinary() {
36648
+ return _internals50.resolveExecutableFromPath(process.platform === "win32" ? ["semgrep.exe"] : ["semgrep"]);
36649
+ }
36650
+ function getAvailabilityProbeCwd() {
36651
+ try {
36652
+ return fs24.realpathSync(process.cwd());
36653
+ } catch {
36654
+ return path61.resolve(process.cwd());
36655
+ }
36656
+ }
36657
+ async function runSemgrepAvailabilityProbe(executable, cwd, generation) {
36658
+ try {
36659
+ const run = await _internals50.runExternalTool({
36660
+ executable,
36661
+ args: ["--version"],
36662
+ cwd,
36663
+ timeoutMs: SEMGREP_AVAILABILITY_TIMEOUT_MS,
36664
+ maxStdoutBytes: SEMGREP_AVAILABILITY_MAX_STDOUT_BYTES,
36665
+ maxStderrBytes: SEMGREP_AVAILABILITY_MAX_STDERR_BYTES
36666
+ });
36667
+ const available = run.status === "completed" && run.exitCode === 0 && !run.stdoutTruncated && !run.stderrTruncated;
36668
+ if (available && generation === semgrepAvailabilityGeneration) {
36669
+ semgrepAvailableCache = true;
36670
+ }
36671
+ return available;
36672
+ } catch {
36673
+ return false;
36674
+ }
36675
+ }
36676
+ async function waitForAvailabilityProbe(probe, abortSignal) {
36677
+ if (!abortSignal)
36678
+ return probe;
36679
+ if (abortSignal.aborted)
36680
+ return false;
36681
+ return new Promise((resolve18) => {
36682
+ let settled = false;
36683
+ const finish = (available) => {
36684
+ if (settled)
36685
+ return;
36686
+ settled = true;
36687
+ abortSignal.removeEventListener("abort", onAbort);
36688
+ resolve18(available);
36689
+ };
36690
+ const onAbort = () => finish(false);
36691
+ abortSignal.addEventListener("abort", onAbort, { once: true });
36692
+ probe.then(finish, () => finish(false));
36693
+ });
36694
+ }
36695
+ function describeExternalToolFailure(status) {
36696
+ switch (status) {
36697
+ case "timeout":
36698
+ return "Semgrep process timed out";
36699
+ case "cancelled":
36700
+ return "Semgrep execution cancelled";
36701
+ case "spawn-error":
36702
+ return "Semgrep process failed to start or terminate safely";
36703
+ }
36704
+ }
36595
36705
  var _internals50 = {
36596
36706
  isSemgrepAvailable,
36597
36707
  checkSemgrepAvailable,
@@ -36599,60 +36709,117 @@ var _internals50 = {
36599
36709
  runSemgrep,
36600
36710
  getRulesDirectory,
36601
36711
  hasBundledRules,
36602
- executeWithTimeout
36712
+ resolveExecutableFromPath,
36713
+ resolveSemgrepBinary,
36714
+ runExternalTool,
36715
+ getAvailabilityProbeCwd
36603
36716
  };
36604
36717
  function isSemgrepAvailable() {
36605
36718
  if (semgrepAvailableCache !== null) {
36606
36719
  return semgrepAvailableCache;
36607
36720
  }
36608
- try {
36609
- child_process6.execFileSync("semgrep", ["--version"], {
36610
- encoding: "utf-8",
36611
- stdio: "pipe"
36612
- });
36613
- semgrepAvailableCache = true;
36614
- return true;
36615
- } catch {
36721
+ return _internals50.resolveSemgrepBinary() !== null;
36722
+ }
36723
+ async function checkSemgrepAvailable(cwd, abortSignal) {
36724
+ if (abortSignal?.aborted)
36725
+ return false;
36726
+ if (semgrepAvailableCache !== null) {
36727
+ return semgrepAvailableCache;
36728
+ }
36729
+ const executable = _internals50.resolveSemgrepBinary();
36730
+ if (!executable) {
36616
36731
  semgrepAvailableCache = false;
36617
36732
  return false;
36618
36733
  }
36619
- }
36620
- async function checkSemgrepAvailable() {
36621
- return _internals50.isSemgrepAvailable();
36734
+ if (!semgrepAvailabilityProbe) {
36735
+ const generation = semgrepAvailabilityGeneration;
36736
+ const probe = runSemgrepAvailabilityProbe(executable, cwd ?? _internals50.getAvailabilityProbeCwd(), generation);
36737
+ semgrepAvailabilityProbe = probe;
36738
+ probe.then(() => {
36739
+ if (semgrepAvailabilityProbe === probe) {
36740
+ semgrepAvailabilityProbe = null;
36741
+ }
36742
+ }, () => {
36743
+ if (semgrepAvailabilityProbe === probe) {
36744
+ semgrepAvailabilityProbe = null;
36745
+ }
36746
+ });
36747
+ }
36748
+ return waitForAvailabilityProbe(semgrepAvailabilityProbe, abortSignal);
36622
36749
  }
36623
36750
  function resetSemgrepCache() {
36751
+ semgrepAvailabilityGeneration++;
36624
36752
  semgrepAvailableCache = null;
36753
+ semgrepAvailabilityProbe = null;
36625
36754
  }
36626
36755
  function parseSemgrepResults(semgrepOutput) {
36627
36756
  const findings = [];
36628
36757
  try {
36629
36758
  const parsed = JSON.parse(semgrepOutput);
36630
- const results = parsed.results || parsed;
36631
- if (!Array.isArray(results)) {
36632
- return [];
36759
+ let results;
36760
+ if (Array.isArray(parsed)) {
36761
+ results = parsed;
36762
+ } else if (parsed && typeof parsed === "object") {
36763
+ const output = parsed;
36764
+ if (output.errors !== undefined && !Array.isArray(output.errors)) {
36765
+ throw new Error("invalid Semgrep errors shape");
36766
+ }
36767
+ if (Array.isArray(output.errors) && output.errors.length > 0) {
36768
+ throw new SemgrepScanError(`Semgrep reported ${output.errors.length} scan error${output.errors.length === 1 ? "" : "s"}`);
36769
+ }
36770
+ results = output.results;
36633
36771
  }
36772
+ if (!Array.isArray(results))
36773
+ throw new Error("invalid Semgrep result shape");
36634
36774
  for (const result of results) {
36635
- if (!result || typeof result !== "object")
36636
- continue;
36637
- const severity = mapSemgrepSeverity(result.extra?.severity || result.severity);
36775
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
36776
+ throw new Error("invalid Semgrep result entry");
36777
+ }
36778
+ const entry = result;
36779
+ const extra = entry.extra && typeof entry.extra === "object" && !Array.isArray(entry.extra) ? entry.extra : null;
36780
+ const start = entry.start && typeof entry.start === "object" && !Array.isArray(entry.start) ? entry.start : null;
36781
+ const ruleId = entry.check_id ?? entry.rule_id;
36782
+ const file = entry.path ?? start?.filename ?? entry.file;
36783
+ const line = start?.line ?? entry.line;
36784
+ const column = start?.col ?? entry.column;
36785
+ const severityValue = extra?.severity ?? entry.severity;
36786
+ const message = extra?.message ?? entry.message;
36787
+ const knownSeverities = new Set([
36788
+ "error",
36789
+ "critical",
36790
+ "warning",
36791
+ "high",
36792
+ "medium",
36793
+ "info",
36794
+ "low"
36795
+ ]);
36796
+ if (typeof ruleId !== "string" || ruleId.length === 0 || typeof file !== "string" || file.length === 0 || file.includes("\x00") || typeof line !== "number" || !Number.isInteger(line) || line < 1 || column !== undefined && (typeof column !== "number" || !Number.isInteger(column) || column < 1) || typeof severityValue !== "string" || !knownSeverities.has(severityValue.toLowerCase()) || message !== undefined && typeof message !== "string" || extra?.fix !== undefined && typeof extra.fix !== "string" || extra?.lines !== undefined && typeof extra.lines !== "string" || entry.lines !== undefined && typeof entry.lines !== "string") {
36797
+ throw new Error("invalid Semgrep result entry");
36798
+ }
36799
+ const severity = mapSemgrepSeverity(severityValue);
36638
36800
  findings.push({
36639
- rule_id: result.check_id || result.rule_id || "unknown",
36801
+ rule_id: ruleId,
36640
36802
  severity,
36641
- message: result.extra?.message || result.message || "Security issue detected",
36803
+ message: message ?? "Security issue detected",
36642
36804
  location: {
36643
- file: result.path || result.start?.filename || result.file || "unknown",
36644
- line: result.start?.line || result.line || 1,
36645
- column: result.start?.col || result.column
36805
+ file,
36806
+ line,
36807
+ column
36646
36808
  },
36647
- remediation: result.extra?.fix,
36648
- excerpt: result.extra?.lines || result.lines || ""
36809
+ remediation: extra?.fix,
36810
+ excerpt: extra?.lines ?? entry.lines ?? ""
36649
36811
  });
36650
36812
  }
36651
- } catch {
36652
- return [];
36813
+ } catch (error2) {
36814
+ if (error2 instanceof SemgrepScanError)
36815
+ throw error2;
36816
+ throw new Error("Semgrep returned invalid JSON output", { cause: error2 });
36653
36817
  }
36654
36818
  return findings;
36655
36819
  }
36820
+
36821
+ class SemgrepScanError extends Error {
36822
+ }
36656
36823
  function mapSemgrepSeverity(severity) {
36657
36824
  const severityLower = (severity || "").toLowerCase();
36658
36825
  switch (severityLower) {
@@ -36669,136 +36836,6 @@ function mapSemgrepSeverity(severity) {
36669
36836
  return "medium";
36670
36837
  }
36671
36838
  }
36672
- async function executeWithTimeout(command, args, options) {
36673
- const maxOutputBytes = options.maxOutputBytes ?? MAX_OUTPUT_BYTES;
36674
- return new Promise((resolve18) => {
36675
- const child = child_process6.spawn(command, args, {
36676
- shell: false,
36677
- cwd: options.cwd,
36678
- stdio: ["ignore", "pipe", "pipe"]
36679
- });
36680
- options.onSpawn?.(child.pid);
36681
- let stdout = "";
36682
- let stderr = "";
36683
- let stdoutBytes = 0;
36684
- let stderrBytes = 0;
36685
- let stdoutTruncated = false;
36686
- let stderrTruncated = false;
36687
- let settled = false;
36688
- let timeout;
36689
- let escalation;
36690
- const settle = (result) => {
36691
- if (settled)
36692
- return;
36693
- settled = true;
36694
- if (timeout)
36695
- clearTimeout(timeout);
36696
- if (escalation)
36697
- clearTimeout(escalation);
36698
- const truncated = stdoutTruncated || stderrTruncated;
36699
- if (child.exitCode === null && child.signalCode === null) {
36700
- try {
36701
- child.kill("SIGTERM");
36702
- } catch (e) {
36703
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36704
- log("[semgrep] child.kill failed:", e.message);
36705
- }
36706
- }
36707
- escalation = setTimeout(() => {
36708
- try {
36709
- child.kill("SIGKILL");
36710
- } catch (e) {
36711
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36712
- log("[semgrep] child.kill failed:", e.message);
36713
- }
36714
- }
36715
- }, KILL_GRACE_MS);
36716
- if (typeof escalation.unref === "function") {
36717
- escalation.unref();
36718
- }
36719
- }
36720
- resolve18({ ...result, truncated });
36721
- };
36722
- timeout = setTimeout(() => {
36723
- settle({
36724
- stdout,
36725
- stderr: "Process timed out",
36726
- exitCode: 124
36727
- });
36728
- }, options.timeoutMs);
36729
- if (typeof timeout.unref === "function") {
36730
- timeout.unref();
36731
- }
36732
- child.stdout?.on("data", (data) => {
36733
- if (stdoutTruncated)
36734
- return;
36735
- const chunk = data.toString();
36736
- const chunkBytes = Buffer.byteLength(chunk, "utf8");
36737
- if (stdoutBytes + chunkBytes > maxOutputBytes) {
36738
- const remaining = Math.max(0, maxOutputBytes - stdoutBytes);
36739
- stdout += Buffer.from(chunk, "utf8").subarray(0, remaining).toString("utf8");
36740
- stdoutBytes = maxOutputBytes;
36741
- stdoutTruncated = true;
36742
- settle({
36743
- stdout,
36744
- stderr,
36745
- exitCode: -1
36746
- });
36747
- try {
36748
- child.kill("SIGTERM");
36749
- } catch (e) {
36750
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36751
- log("[semgrep] child.kill failed:", e.message);
36752
- }
36753
- }
36754
- } else {
36755
- stdout += chunk;
36756
- stdoutBytes += chunkBytes;
36757
- }
36758
- });
36759
- child.stderr?.on("data", (data) => {
36760
- if (stderrTruncated)
36761
- return;
36762
- const chunk = data.toString();
36763
- const chunkBytes = Buffer.byteLength(chunk, "utf8");
36764
- if (stderrBytes + chunkBytes > maxOutputBytes) {
36765
- const remaining = Math.max(0, maxOutputBytes - stderrBytes);
36766
- stderr += Buffer.from(chunk, "utf8").subarray(0, remaining).toString("utf8");
36767
- stderrBytes = maxOutputBytes;
36768
- stderrTruncated = true;
36769
- settle({
36770
- stdout,
36771
- stderr,
36772
- exitCode: -1
36773
- });
36774
- try {
36775
- child.kill("SIGTERM");
36776
- } catch (e) {
36777
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36778
- log("[semgrep] child.kill failed:", e.message);
36779
- }
36780
- }
36781
- } else {
36782
- stderr += chunk;
36783
- stderrBytes += chunkBytes;
36784
- }
36785
- });
36786
- child.on("close", (code) => {
36787
- settle({
36788
- stdout,
36789
- stderr,
36790
- exitCode: code ?? 0
36791
- });
36792
- });
36793
- child.on("error", (err) => {
36794
- settle({
36795
- stdout,
36796
- stderr: err.message,
36797
- exitCode: 1
36798
- });
36799
- });
36800
- });
36801
- }
36802
36839
  async function runSemgrep(options) {
36803
36840
  const files = options.files || [];
36804
36841
  const rulesDir = options.rulesDir || DEFAULT_RULES_DIR;
@@ -36810,7 +36847,24 @@ async function runSemgrep(options) {
36810
36847
  engine: "tier_a"
36811
36848
  };
36812
36849
  }
36813
- if (!_internals50.isSemgrepAvailable()) {
36850
+ if (!await _internals50.checkSemgrepAvailable(options.cwd, options.abortSignal)) {
36851
+ if (options.abortSignal?.aborted) {
36852
+ return {
36853
+ available: true,
36854
+ findings: [],
36855
+ error: "Semgrep execution cancelled",
36856
+ engine: "tier_a"
36857
+ };
36858
+ }
36859
+ return {
36860
+ available: false,
36861
+ findings: [],
36862
+ error: "Semgrep is not installed or not available on PATH",
36863
+ engine: "tier_a"
36864
+ };
36865
+ }
36866
+ const executable = _internals50.resolveSemgrepBinary();
36867
+ if (!executable) {
36814
36868
  return {
36815
36869
  available: false,
36816
36870
  findings: [],
@@ -36828,11 +36882,16 @@ async function runSemgrep(options) {
36828
36882
  }
36829
36883
  args.push(...files);
36830
36884
  try {
36831
- const result = await executeWithTimeout("semgrep", args, {
36885
+ const result = await _internals50.runExternalTool({
36886
+ executable,
36887
+ args,
36888
+ cwd: options.cwd ?? _internals50.getAvailabilityProbeCwd(),
36832
36889
  timeoutMs,
36833
- cwd: options.cwd
36890
+ maxStdoutBytes: MAX_OUTPUT_BYTES,
36891
+ maxStderrBytes: MAX_OUTPUT_BYTES,
36892
+ abortSignal: options.abortSignal
36834
36893
  });
36835
- if (result.truncated) {
36894
+ if (result.stdoutTruncated || result.stderrTruncated) {
36836
36895
  return {
36837
36896
  available: true,
36838
36897
  findings: [],
@@ -36840,6 +36899,14 @@ async function runSemgrep(options) {
36840
36899
  engine: "tier_a"
36841
36900
  };
36842
36901
  }
36902
+ if (result.status !== "completed") {
36903
+ return {
36904
+ available: true,
36905
+ findings: [],
36906
+ error: describeExternalToolFailure(result.status),
36907
+ engine: "tier_a"
36908
+ };
36909
+ }
36843
36910
  if (result.exitCode !== 0) {
36844
36911
  if (result.exitCode === 1 && result.stdout) {
36845
36912
  const findings2 = parseSemgrepResults(result.stdout);
@@ -36852,7 +36919,7 @@ async function runSemgrep(options) {
36852
36919
  return {
36853
36920
  available: true,
36854
36921
  findings: [],
36855
- error: result.stderr || `Semgrep exited with code ${result.exitCode}`,
36922
+ error: `Semgrep exited with code ${result.exitCode}`,
36856
36923
  engine: "tier_a"
36857
36924
  };
36858
36925
  }
@@ -36863,11 +36930,11 @@ async function runSemgrep(options) {
36863
36930
  engine: "tier_a+tier_b"
36864
36931
  };
36865
36932
  } catch (error2) {
36866
- const errorMessage = error2 instanceof Error ? error2.message : "Unknown error running Semgrep";
36933
+ const safeError = error2 instanceof SemgrepScanError || error2 instanceof Error && error2.message === "Semgrep returned invalid JSON output" ? error2.message : "Semgrep execution failed unexpectedly";
36867
36934
  return {
36868
36935
  available: true,
36869
36936
  findings: [],
36870
- error: errorMessage,
36937
+ error: safeError,
36871
36938
  engine: "tier_a"
36872
36939
  };
36873
36940
  }
@@ -36981,8 +37048,30 @@ function assignOccurrenceIndices(findings, directory) {
36981
37048
  };
36982
37049
  });
36983
37050
  }
36984
- async function acquireLock2(lockPath) {
37051
+ async function waitForLockRetry(delayMs, abortSignal) {
37052
+ if (abortSignal?.aborted)
37053
+ return false;
37054
+ return await new Promise((resolve19) => {
37055
+ let settled = false;
37056
+ const finish = (value) => {
37057
+ if (settled)
37058
+ return;
37059
+ settled = true;
37060
+ clearTimeout(timeout);
37061
+ abortSignal?.removeEventListener("abort", onAbort);
37062
+ resolve19(value);
37063
+ };
37064
+ const onAbort = () => finish(false);
37065
+ const timeout = setTimeout(() => finish(true), delayMs);
37066
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
37067
+ if (abortSignal?.aborted)
37068
+ onAbort();
37069
+ });
37070
+ }
37071
+ async function acquireLock2(lockPath, abortSignal) {
36985
37072
  for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
37073
+ if (abortSignal?.aborted)
37074
+ return null;
36986
37075
  try {
36987
37076
  const fd = fs25.openSync(lockPath, "wx");
36988
37077
  fs25.closeSync(fd);
@@ -36994,7 +37083,9 @@ async function acquireLock2(lockPath) {
36994
37083
  };
36995
37084
  } catch {
36996
37085
  if (attempt < LOCK_RETRY_DELAYS_MS.length) {
36997
- await new Promise((resolve19) => setTimeout(resolve19, LOCK_RETRY_DELAYS_MS[attempt]));
37086
+ if (!await waitForLockRetry(LOCK_RETRY_DELAYS_MS[attempt], abortSignal)) {
37087
+ return null;
37088
+ }
36998
37089
  }
36999
37090
  }
37000
37091
  }
@@ -37029,9 +37120,19 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37029
37120
  message: e instanceof Error ? e.message : "Path validation failed"
37030
37121
  };
37031
37122
  }
37123
+ if (opts?.abortSignal?.aborted) {
37124
+ return { status: "error", message: "SAST baseline capture cancelled" };
37125
+ }
37032
37126
  fs25.mkdirSync(path62.dirname(baselinePath), { recursive: true });
37033
37127
  fs25.mkdirSync(path62.dirname(tempPath), { recursive: true });
37034
- const releaseLock = await acquireLock2(lockPath);
37128
+ const releaseLock = await acquireLock2(lockPath, opts?.abortSignal);
37129
+ if (!releaseLock) {
37130
+ return { status: "error", message: "SAST baseline capture cancelled" };
37131
+ }
37132
+ if (opts?.abortSignal?.aborted) {
37133
+ releaseLock();
37134
+ return { status: "error", message: "SAST baseline capture cancelled" };
37135
+ }
37035
37136
  try {
37036
37137
  let existing = null;
37037
37138
  try {
@@ -37095,7 +37196,14 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37095
37196
  message: `Baseline would exceed size cap (${json2.length} bytes > ${MAX_BASELINE_BYTES})`
37096
37197
  };
37097
37198
  }
37199
+ if (opts?.abortSignal?.aborted) {
37200
+ return { status: "error", message: "SAST baseline capture cancelled" };
37201
+ }
37098
37202
  fs25.writeFileSync(tempPath, json2, "utf-8");
37203
+ if (opts?.abortSignal?.aborted) {
37204
+ fs25.rmSync(tempPath, { force: true });
37205
+ return { status: "error", message: "SAST baseline capture cancelled" };
37206
+ }
37099
37207
  fs25.renameSync(tempPath, baselinePath);
37100
37208
  invalidateCachedArtifact(baselinePath);
37101
37209
  return {
@@ -37128,7 +37236,14 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37128
37236
  message: `Baseline would exceed size cap (${json.length} bytes > ${MAX_BASELINE_BYTES})`
37129
37237
  };
37130
37238
  }
37239
+ if (opts?.abortSignal?.aborted) {
37240
+ return { status: "error", message: "SAST baseline capture cancelled" };
37241
+ }
37131
37242
  fs25.writeFileSync(tempPath, json, "utf-8");
37243
+ if (opts?.abortSignal?.aborted) {
37244
+ fs25.rmSync(tempPath, { force: true });
37245
+ return { status: "error", message: "SAST baseline capture cancelled" };
37246
+ }
37132
37247
  fs25.renameSync(tempPath, baselinePath);
37133
37248
  invalidateCachedArtifact(baselinePath);
37134
37249
  return {
@@ -37250,6 +37365,36 @@ function countBySeverity(findings) {
37250
37365
  }
37251
37366
  return counts;
37252
37367
  }
37368
+ function safeSemgrepDiagnostic(message) {
37369
+ const diagnostic = message.trim();
37370
+ if (diagnostic === "Semgrep process timed out" || diagnostic === "Semgrep execution cancelled" || diagnostic === "Semgrep is not installed or not available on PATH" || diagnostic === "Semgrep returned invalid JSON output" || diagnostic === "Semgrep process failed to start or terminate safely" || diagnostic === "Semgrep execution failed unexpectedly") {
37371
+ return diagnostic;
37372
+ }
37373
+ if (/^Semgrep exited with code -?\d+$/.test(diagnostic)) {
37374
+ return diagnostic;
37375
+ }
37376
+ if (/^Semgrep reported \d+ scan errors?$/.test(diagnostic)) {
37377
+ return diagnostic;
37378
+ }
37379
+ if (diagnostic.startsWith("Semgrep output exceeded")) {
37380
+ return "Semgrep output exceeded its bounded limit; results incomplete";
37381
+ }
37382
+ return "Semgrep execution failed";
37383
+ }
37384
+ function cancelledScanResult(findings, filesScanned) {
37385
+ const finalFindings = findings.slice(0, MAX_FINDINGS);
37386
+ return {
37387
+ verdict: "fail",
37388
+ error: "SAST scan cancelled",
37389
+ findings: finalFindings,
37390
+ summary: {
37391
+ engine: "tier_a",
37392
+ files_scanned: filesScanned,
37393
+ findings_count: finalFindings.length,
37394
+ findings_by_severity: countBySeverity(finalFindings)
37395
+ }
37396
+ };
37397
+ }
37253
37398
  function scanFileWithTierA(filePath, language) {
37254
37399
  try {
37255
37400
  const content = fs26.readFileSync(filePath, "utf-8");
@@ -37275,7 +37420,8 @@ async function sastScan(input, directory, config) {
37275
37420
  severity_threshold = "medium",
37276
37421
  capture_baseline = false,
37277
37422
  phase,
37278
- offline_only = false
37423
+ offline_only = false,
37424
+ abort_signal
37279
37425
  } = input;
37280
37426
  if (config?.gates?.sast_scan?.enabled === false) {
37281
37427
  return {
@@ -37298,10 +37444,17 @@ async function sastScan(input, directory, config) {
37298
37444
  let filesScanned = 0;
37299
37445
  let _filesSkipped = 0;
37300
37446
  const scannedFilePaths = [];
37301
- const semgrepAvailable = !offline_only && _internals52.isSemgrepAvailable();
37447
+ if (abort_signal?.aborted) {
37448
+ return cancelledScanResult(allFindings, filesScanned);
37449
+ }
37450
+ const semgrepAvailable = !offline_only && await _internals52.checkSemgrepAvailable(directory, abort_signal);
37302
37451
  const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
37303
37452
  const filesByLanguage = new Map;
37453
+ let semgrepError = abort_signal?.aborted ? "SAST scan cancelled" : undefined;
37304
37454
  for (const filePath of changed_files) {
37455
+ if (abort_signal?.aborted) {
37456
+ return cancelledScanResult(allFindings, filesScanned);
37457
+ }
37305
37458
  if (typeof filePath !== "string" || !filePath) {
37306
37459
  _filesSkipped++;
37307
37460
  continue;
@@ -37356,6 +37509,10 @@ async function sastScan(input, directory, config) {
37356
37509
  if (semgrepAvailable && filesByLanguage.size > 0) {
37357
37510
  try {
37358
37511
  for (const [bucketKey, bucketFiles] of filesByLanguage.entries()) {
37512
+ if (abort_signal?.aborted) {
37513
+ semgrepError = "SAST scan cancelled";
37514
+ break;
37515
+ }
37359
37516
  if (bucketFiles.length === 0)
37360
37517
  continue;
37361
37518
  let semgrepResult;
@@ -37364,13 +37521,21 @@ async function sastScan(input, directory, config) {
37364
37521
  semgrepResult = await _internals52.runSemgrep({
37365
37522
  files: bucketFiles,
37366
37523
  lang,
37367
- useAutoConfig: true
37524
+ useAutoConfig: true,
37525
+ cwd: directory,
37526
+ abortSignal: abort_signal
37368
37527
  });
37369
37528
  } else {
37370
37529
  semgrepResult = await _internals52.runSemgrep({
37371
- files: bucketFiles
37530
+ files: bucketFiles,
37531
+ cwd: directory,
37532
+ abortSignal: abort_signal
37372
37533
  });
37373
37534
  }
37535
+ if (semgrepResult.error) {
37536
+ semgrepError = safeSemgrepDiagnostic(semgrepResult.error);
37537
+ break;
37538
+ }
37374
37539
  if (semgrepResult.findings.length > 0) {
37375
37540
  const semgrepFindings = semgrepResult.findings.map((f) => ({
37376
37541
  rule_id: f.rule_id,
@@ -37393,9 +37558,42 @@ async function sastScan(input, directory, config) {
37393
37558
  }
37394
37559
  }
37395
37560
  }
37396
- } catch (error2) {
37397
- warn(`SAST Scan: Semgrep failed, falling back to Tier A: ${error2}`);
37561
+ } catch {
37562
+ semgrepError = "Semgrep execution failed";
37563
+ }
37564
+ }
37565
+ if (abort_signal?.aborted) {
37566
+ return cancelledScanResult(allFindings, filesScanned);
37567
+ }
37568
+ if (semgrepError) {
37569
+ const finalFindings2 = allFindings.slice(0, MAX_FINDINGS);
37570
+ const summary2 = {
37571
+ engine: "tier_a",
37572
+ files_scanned: filesScanned,
37573
+ findings_count: finalFindings2.length,
37574
+ findings_by_severity: countBySeverity(finalFindings2)
37575
+ };
37576
+ warn(`SAST Scan: Semgrep execution failed closed: ${semgrepError}`);
37577
+ if (abort_signal?.aborted) {
37578
+ return cancelledScanResult(allFindings, filesScanned);
37398
37579
  }
37580
+ await saveEvidence(directory, "sast_scan", {
37581
+ task_id: "sast_scan",
37582
+ type: "sast",
37583
+ timestamp: new Date().toISOString(),
37584
+ agent: "sast_scan",
37585
+ verdict: "fail",
37586
+ summary: `Semgrep execution failed: ${semgrepError}`,
37587
+ ...summary2,
37588
+ findings: finalFindings2,
37589
+ baseline_used: false
37590
+ }, abort_signal);
37591
+ return {
37592
+ verdict: "fail",
37593
+ error: `Semgrep execution failed: ${semgrepError}`,
37594
+ findings: finalFindings2,
37595
+ summary: summary2
37596
+ };
37399
37597
  }
37400
37598
  if (capture_baseline) {
37401
37599
  if (phase === undefined || !Number.isInteger(phase) || phase < 1) {
@@ -37426,7 +37624,10 @@ async function sastScan(input, directory, config) {
37426
37624
  };
37427
37625
  }
37428
37626
  const captureFindings = allFindings.slice(0, MAX_BASELINE_FINDINGS);
37429
- const captureResult = await captureOrMergeBaseline(directory, phase, captureFindings, engine, scannedFilePaths);
37627
+ if (abort_signal?.aborted) {
37628
+ return cancelledScanResult(allFindings, filesScanned);
37629
+ }
37630
+ const captureResult = await captureOrMergeBaseline(directory, phase, captureFindings, engine, scannedFilePaths, { abortSignal: abort_signal });
37430
37631
  const captureStatus = captureResult.status === "written" ? "baseline_captured" : captureResult.status === "merged" ? "baseline_merged" : undefined;
37431
37632
  if (captureResult.status === "error") {
37432
37633
  warn(`SAST Baseline: capture failed \u2014 ${captureResult.message}`);
@@ -37438,6 +37639,9 @@ async function sastScan(input, directory, config) {
37438
37639
  findings_count: finalFindings2.length,
37439
37640
  findings_by_severity: countBySeverity(finalFindings2)
37440
37641
  };
37642
+ if (abort_signal?.aborted) {
37643
+ return cancelledScanResult(allFindings, filesScanned);
37644
+ }
37441
37645
  await saveEvidence(directory, "sast_scan", {
37442
37646
  task_id: "sast_scan",
37443
37647
  type: "sast",
@@ -37448,7 +37652,7 @@ async function sastScan(input, directory, config) {
37448
37652
  ...summary2,
37449
37653
  findings: finalFindings2,
37450
37654
  baseline_used: false
37451
- });
37655
+ }, abort_signal);
37452
37656
  return {
37453
37657
  verdict: "pass",
37454
37658
  findings: finalFindings2,
@@ -37516,6 +37720,9 @@ async function sastScan(input, directory, config) {
37516
37720
  findings_count: finalFindings.length,
37517
37721
  findings_by_severity: findingsBySeverity
37518
37722
  };
37723
+ if (abort_signal?.aborted) {
37724
+ return cancelledScanResult(allFindings, filesScanned);
37725
+ }
37519
37726
  await saveEvidence(directory, "sast_scan", {
37520
37727
  task_id: "sast_scan",
37521
37728
  type: "sast",
@@ -37530,7 +37737,7 @@ async function sastScan(input, directory, config) {
37530
37737
  pre_existing_findings: preExistingFindings,
37531
37738
  baseline_used: true
37532
37739
  }
37533
- });
37740
+ }, abort_signal);
37534
37741
  const result = {
37535
37742
  verdict,
37536
37743
  findings: finalFindings,
@@ -37554,7 +37761,7 @@ var sast_scan = createSwarmTool({
37554
37761
  capture_baseline: exports_external.boolean().optional().describe("When true, capture/merge a phase-scoped baseline of pre-existing findings. Requires phase. Subsequent scans with phase only fail on NEW findings."),
37555
37762
  phase: exports_external.number().int().min(1).optional().describe("Current phase number (positive integer >= 1). Required with capture_baseline. Enables baseline diff when provided on non-capture scans.")
37556
37763
  },
37557
- execute: async (args, directory) => {
37764
+ execute: async (args, directory, ctx) => {
37558
37765
  let safeArgs;
37559
37766
  try {
37560
37767
  if (args && typeof args === "object") {
@@ -37607,7 +37814,8 @@ var sast_scan = createSwarmTool({
37607
37814
  changed_files: safeArgs.changed_files ?? [],
37608
37815
  severity_threshold: safeArgs.severity_threshold ?? "medium",
37609
37816
  capture_baseline: safeArgs.capture_baseline,
37610
- phase: safeArgs.phase
37817
+ phase: safeArgs.phase,
37818
+ abort_signal: ctx?.abort
37611
37819
  };
37612
37820
  const result = await _internals52.sastScan(input, directory);
37613
37821
  return JSON.stringify(result, null, 2);
@@ -37616,6 +37824,7 @@ var sast_scan = createSwarmTool({
37616
37824
  var _internals52 = {
37617
37825
  sastScan,
37618
37826
  sast_scan,
37827
+ checkSemgrepAvailable: (directory, abortSignal) => checkSemgrepAvailable(directory, abortSignal),
37619
37828
  isSemgrepAvailable: () => isSemgrepAvailable(),
37620
37829
  runSemgrep: (options) => runSemgrep(options)
37621
37830
  };
@@ -43167,6 +43376,22 @@ import * as fs30 from "fs";
43167
43376
  import * as path76 from "path";
43168
43377
  var MAX_OUTPUT_BYTES2 = 512000;
43169
43378
  var MAX_COMMAND_LENGTH = 500;
43379
+ var SUPPORTED_LINTERS = ["biome", "eslint"];
43380
+ var LINTER_DETECT_TIMEOUT_MS = 2000;
43381
+ var LINTER_RUN_TIMEOUT_MS = 30000;
43382
+ var OUTPUT_TRUNCATION_SUFFIX = `
43383
+ ... (output truncated)`;
43384
+ var NODE_MODULES = "node_modules";
43385
+ var BIN_DIRECTORY = ".bin";
43386
+ var MAX_PACKAGE_MANIFEST_BYTES = 256 * 1024;
43387
+ var MAX_PATH_SHIM_BYTES = 64 * 1024;
43388
+ var UNSAFE_SHELL_WRAPPER_EXTENSIONS = new Set([".bat", ".cmd", ".ps1"]);
43389
+ var WINDOWS_BATCH_WRAPPER_EXTENSIONS = [".cmd", ".bat"];
43390
+ var WINDOWS_CMD_UNSAFE_TOKEN = /["%!^&|<>\r\n]/;
43391
+ var LINTER_PACKAGES = {
43392
+ biome: "@biomejs/biome",
43393
+ eslint: "eslint"
43394
+ };
43170
43395
  function validateArgs(args) {
43171
43396
  if (typeof args !== "object" || args === null)
43172
43397
  return false;
@@ -43175,112 +43400,533 @@ function validateArgs(args) {
43175
43400
  return false;
43176
43401
  return true;
43177
43402
  }
43178
- function getLinterCommand(linter, mode, projectDir) {
43179
- const isWindows = process.platform === "win32";
43180
- const binDir = path76.join(projectDir, "node_modules", ".bin");
43181
- const biomeBin = isWindows ? path76.join(binDir, "biome.EXE") : path76.join(binDir, "biome");
43182
- const eslintBin = isWindows ? path76.join(binDir, "eslint.cmd") : path76.join(binDir, "eslint");
43183
- switch (linter) {
43184
- case "biome":
43185
- if (mode === "fix") {
43186
- return isWindows ? [biomeBin, "check", "--write", "."] : [biomeBin, "check", "--write", "."];
43403
+ function normalizeCaseForPlatform(value, platform) {
43404
+ return platform === "win32" ? value.toLowerCase() : value;
43405
+ }
43406
+ function normalizeRelativePathForPlatform(value, platform) {
43407
+ return normalizeCaseForPlatform(value.replaceAll("\\", "/").replace(/\/+/g, "/"), platform);
43408
+ }
43409
+ function isRegularFile(candidate) {
43410
+ try {
43411
+ return _internals64.statSync(candidate).isFile();
43412
+ } catch {
43413
+ return false;
43414
+ }
43415
+ }
43416
+ function readTextFileBounded(filePath, maxBytes) {
43417
+ let fd;
43418
+ try {
43419
+ fd = _internals64.openSync(filePath, "r");
43420
+ const stats = _internals64.fstatSync(fd);
43421
+ if (!stats.isFile() || !Number.isSafeInteger(stats.size) || stats.size < 0 || stats.size > maxBytes) {
43422
+ return null;
43423
+ }
43424
+ const bytes = Buffer.alloc(stats.size);
43425
+ let offset = 0;
43426
+ while (offset < bytes.byteLength) {
43427
+ const bytesRead = _internals64.readSync(fd, bytes, offset, bytes.byteLength - offset, null);
43428
+ if (bytesRead === 0)
43429
+ break;
43430
+ offset += bytesRead;
43431
+ }
43432
+ const overflowProbe = Buffer.alloc(1);
43433
+ if (_internals64.readSync(fd, overflowProbe, 0, 1, null) > 0)
43434
+ return null;
43435
+ return bytes.subarray(0, offset).toString("utf8");
43436
+ } catch {
43437
+ return null;
43438
+ } finally {
43439
+ if (fd !== undefined) {
43440
+ try {
43441
+ _internals64.closeSync(fd);
43442
+ } catch {}
43443
+ }
43444
+ }
43445
+ }
43446
+ function toAbsoluteDirectory(directory) {
43447
+ return path76.isAbsolute(directory) ? directory : path76.resolve(directory);
43448
+ }
43449
+ function getAncestorDirectories(directory) {
43450
+ const resolved = path76.resolve(directory);
43451
+ const roots = [];
43452
+ let current = resolved;
43453
+ for (;; ) {
43454
+ roots.push(current);
43455
+ const parent = path76.dirname(current);
43456
+ if (parent === current)
43457
+ break;
43458
+ current = parent;
43459
+ }
43460
+ return roots;
43461
+ }
43462
+ function getTrustedShimNames(linter, platform = _internals64.platform()) {
43463
+ const isWindows = platform === "win32";
43464
+ if (linter === "biome") {
43465
+ return isWindows ? ["biome.exe", "biome.cmd", "biome.ps1"] : ["biome"];
43466
+ }
43467
+ return isWindows ? ["eslint.exe", "eslint.cmd", "eslint.ps1"] : ["eslint"];
43468
+ }
43469
+ function getExpectedPackageBinRelativePath(linter) {
43470
+ return linter === "biome" ? "bin/biome" : "bin/eslint.js";
43471
+ }
43472
+ function getPackageRootFromSearchRoot(searchRoot, linter) {
43473
+ return path76.join(searchRoot, NODE_MODULES, ...LINTER_PACKAGES[linter].split("/"));
43474
+ }
43475
+ function getPackageManifestPath(searchRoot, linter) {
43476
+ return path76.join(getPackageRootFromSearchRoot(searchRoot, linter), "package.json");
43477
+ }
43478
+ function getPackageDisplayPath(searchRoot, linter) {
43479
+ return path76.join(searchRoot, NODE_MODULES, BIN_DIRECTORY, linter === "biome" ? _internals64.platform() === "win32" ? "biome.exe" : "biome" : _internals64.platform() === "win32" ? "eslint" : "eslint");
43480
+ }
43481
+ function getBiomeNativePackage(platform, arch) {
43482
+ const archMap = platform === "darwin" ? { arm64: "@biomejs/cli-darwin-arm64", x64: "@biomejs/cli-darwin-x64" } : platform === "linux" ? {
43483
+ arm64: "@biomejs/cli-linux-arm64",
43484
+ arm: "@biomejs/cli-linux-arm",
43485
+ x64: "@biomejs/cli-linux-x64"
43486
+ } : platform === "win32" ? {
43487
+ arm64: "@biomejs/cli-win32-arm64",
43488
+ x64: "@biomejs/cli-win32-x64"
43489
+ } : null;
43490
+ if (!archMap)
43491
+ return null;
43492
+ return archMap[arch] ?? null;
43493
+ }
43494
+ function getBiomeNativeExecutable(searchRoot, platform = _internals64.platform(), arch = _internals64.arch()) {
43495
+ const nativePackage = getBiomeNativePackage(platform, arch);
43496
+ if (!nativePackage)
43497
+ return null;
43498
+ const executableName = platform === "win32" ? "biome.exe" : "biome";
43499
+ const candidate = path76.join(searchRoot, NODE_MODULES, ...nativePackage.split("/"), executableName);
43500
+ return isRegularFile(candidate) ? candidate : null;
43501
+ }
43502
+ function resolvePackageBinEntry(manifestPath, linter) {
43503
+ try {
43504
+ const contents = readTextFileBounded(manifestPath, MAX_PACKAGE_MANIFEST_BYTES);
43505
+ if (contents === null)
43506
+ return null;
43507
+ const manifest = JSON.parse(contents);
43508
+ if (typeof manifest.bin === "string") {
43509
+ return manifest.bin;
43510
+ }
43511
+ if (manifest.bin && typeof manifest.bin === "object") {
43512
+ const key = linter === "biome" ? "biome" : "eslint";
43513
+ const entry = manifest.bin[key];
43514
+ return typeof entry === "string" ? entry : null;
43515
+ }
43516
+ } catch {}
43517
+ return null;
43518
+ }
43519
+ function resolveCanonicalPackageCommand(searchRoot, linter, source) {
43520
+ const manifestPath = getPackageManifestPath(searchRoot, linter);
43521
+ if (!isRegularFile(manifestPath))
43522
+ return null;
43523
+ const relativeBinPath = resolvePackageBinEntry(manifestPath, linter);
43524
+ if (!relativeBinPath)
43525
+ return null;
43526
+ let canonicalPackageRoot;
43527
+ let canonicalCandidate;
43528
+ try {
43529
+ const packageRoot = path76.dirname(manifestPath);
43530
+ canonicalPackageRoot = _internals64.realpathSync(packageRoot);
43531
+ canonicalCandidate = _internals64.realpathSync(path76.resolve(packageRoot, relativeBinPath));
43532
+ } catch {
43533
+ return null;
43534
+ }
43535
+ const relativeToPackage = path76.relative(canonicalPackageRoot, canonicalCandidate);
43536
+ if (relativeToPackage === "" || relativeToPackage.startsWith("..") || path76.isAbsolute(relativeToPackage)) {
43537
+ return null;
43538
+ }
43539
+ if (!isRegularFile(canonicalCandidate))
43540
+ return null;
43541
+ return {
43542
+ linter,
43543
+ executable: _internals64.execPath(),
43544
+ argsPrefix: [canonicalCandidate],
43545
+ displayPrefix: [getPackageDisplayPath(searchRoot, linter)],
43546
+ source
43547
+ };
43548
+ }
43549
+ function hasTrustedLocalShim(searchRoot, linter) {
43550
+ const binRoot = path76.join(searchRoot, NODE_MODULES, BIN_DIRECTORY);
43551
+ for (const name of getTrustedShimNames(linter)) {
43552
+ if (isRegularFile(path76.join(binRoot, name))) {
43553
+ return true;
43554
+ }
43555
+ }
43556
+ return false;
43557
+ }
43558
+ function resolveLocalLinterCommand(linter, directory) {
43559
+ const roots = getAncestorDirectories(directory);
43560
+ for (const root of roots) {
43561
+ if (linter === "biome") {
43562
+ const nativeExecutable = getBiomeNativeExecutable(root);
43563
+ if (nativeExecutable) {
43564
+ return {
43565
+ linter,
43566
+ executable: nativeExecutable,
43567
+ argsPrefix: [],
43568
+ displayPrefix: [nativeExecutable],
43569
+ source: "local-native"
43570
+ };
43187
43571
  }
43188
- return isWindows ? [biomeBin, "check", "."] : [biomeBin, "check", "."];
43189
- case "eslint":
43190
- if (mode === "fix") {
43191
- return isWindows ? [eslintBin, ".", "--fix"] : [eslintBin, ".", "--fix"];
43572
+ }
43573
+ if (hasTrustedLocalShim(root, linter)) {
43574
+ const resolvedFromShim = resolveCanonicalPackageCommand(root, linter, "local-shim");
43575
+ if (resolvedFromShim)
43576
+ return resolvedFromShim;
43577
+ }
43578
+ const resolvedPackage = resolveCanonicalPackageCommand(root, linter, "safe-package-bin");
43579
+ if (resolvedPackage)
43580
+ return resolvedPackage;
43581
+ }
43582
+ return null;
43583
+ }
43584
+ function findPathShimCandidate(linter, envPath = _internals64.pathEnv(), platform = _internals64.platform()) {
43585
+ const entries = envPath.split(path76.delimiter).filter(Boolean);
43586
+ const shimNames = getTrustedShimNames(linter, platform).filter((name) => name.endsWith(".cmd") || name.endsWith(".ps1") || platform !== "win32");
43587
+ for (const entry of entries) {
43588
+ for (const shimName of shimNames) {
43589
+ const candidate = path76.join(entry, shimName);
43590
+ if (isRegularFile(candidate)) {
43591
+ return candidate;
43192
43592
  }
43193
- return isWindows ? [eslintBin, "."] : [eslintBin, "."];
43593
+ }
43194
43594
  }
43595
+ return null;
43195
43596
  }
43196
- function getAdditionalLinterCommand(linter, mode, cwd) {
43197
- const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
43198
- const gradlew = fs30.existsSync(path76.join(cwd, gradlewName)) ? path76.join(cwd, gradlewName) : null;
43199
- switch (linter) {
43200
- case "ruff":
43201
- return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
43202
- case "clippy":
43203
- return mode === "fix" ? ["cargo", "clippy", "--fix", "--allow-dirty"] : ["cargo", "clippy"];
43204
- case "golangci-lint":
43205
- return mode === "fix" ? ["golangci-lint", "run", "--fix"] : ["golangci-lint", "run"];
43206
- case "checkstyle":
43207
- if (gradlew) {
43208
- return [gradlew, "checkstyleMain"];
43597
+ function getExpectedShimSuffixes(linter) {
43598
+ const packageParts = LINTER_PACKAGES[linter].split("/");
43599
+ const packageSuffix = path76.join("..", ...packageParts, ...getExpectedPackageBinRelativePath(linter).split("/"));
43600
+ const nestedNodeModulesSuffix = path76.join("..", NODE_MODULES, ...packageParts, ...getExpectedPackageBinRelativePath(linter).split("/"));
43601
+ const packageOnlySuffix = path76.join(...packageParts, ...getExpectedPackageBinRelativePath(linter).split("/"));
43602
+ const nodeModulesPackageOnlySuffix = path76.join(NODE_MODULES, ...packageParts, ...getExpectedPackageBinRelativePath(linter).split("/"));
43603
+ return [
43604
+ normalizeRelativePathForPlatform(packageSuffix, "win32"),
43605
+ normalizeRelativePathForPlatform(nestedNodeModulesSuffix, "win32"),
43606
+ normalizeRelativePathForPlatform(packageOnlySuffix, "win32"),
43607
+ normalizeRelativePathForPlatform(nodeModulesPackageOnlySuffix, "win32"),
43608
+ normalizeRelativePathForPlatform(packageSuffix, "linux"),
43609
+ normalizeRelativePathForPlatform(nestedNodeModulesSuffix, "linux"),
43610
+ normalizeRelativePathForPlatform(packageOnlySuffix, "linux"),
43611
+ normalizeRelativePathForPlatform(nodeModulesPackageOnlySuffix, "linux")
43612
+ ];
43613
+ }
43614
+ function resolvePathShimTarget(shimPath, linter, platform = _internals64.platform()) {
43615
+ const contents = readTextFileBounded(shimPath, MAX_PATH_SHIM_BYTES);
43616
+ if (contents === null)
43617
+ return null;
43618
+ const ext = path76.extname(shimPath).toLowerCase();
43619
+ let relativeTarget = null;
43620
+ if (ext === ".cmd") {
43621
+ const regex = /"%(?:dp0%|~dp0)(?<suffix>(?:\\[^"%]+)+)"/gi;
43622
+ for (const match of contents.matchAll(regex)) {
43623
+ const suffix = match.groups?.suffix;
43624
+ if (!suffix)
43625
+ continue;
43626
+ const normalized = normalizeRelativePathForPlatform(suffix, "win32");
43627
+ if (getExpectedShimSuffixes(linter).some((expected) => normalized.endsWith(expected))) {
43628
+ relativeTarget = suffix;
43629
+ break;
43209
43630
  }
43210
- if (isCommandAvailable("gradle")) {
43211
- return ["gradle", "checkstyleMain"];
43631
+ }
43632
+ } else if (ext === ".ps1") {
43633
+ const regex = /"\$basedir(?:_win)?\/(?<suffix>\.\.\/[^"\r\n]+)"/gi;
43634
+ for (const match of contents.matchAll(regex)) {
43635
+ const suffix = match.groups?.suffix;
43636
+ if (!suffix)
43637
+ continue;
43638
+ const normalized = normalizeRelativePathForPlatform(suffix, platform);
43639
+ if (getExpectedShimSuffixes(linter).some((expected) => normalized.endsWith(expected))) {
43640
+ relativeTarget = suffix;
43641
+ break;
43212
43642
  }
43213
- return ["mvn", "checkstyle:check"];
43214
- case "ktlint":
43215
- return mode === "fix" ? ["ktlint", "--format"] : ["ktlint"];
43216
- case "dotnet-format":
43217
- return mode === "fix" ? ["dotnet", "format"] : ["dotnet", "format", "--verify-no-changes"];
43218
- case "cppcheck":
43219
- return ["cppcheck", "--enable=all", "."];
43220
- case "swiftlint":
43221
- return mode === "fix" ? ["swiftlint", "--fix"] : ["swiftlint"];
43222
- case "dart-analyze":
43223
- return mode === "fix" ? ["dart", "fix"] : ["dart", "analyze"];
43224
- case "rubocop": {
43225
- const useBundle = isCommandAvailable("bundle");
43226
- const base = useBundle ? ["bundle", "exec", "rubocop"] : ["rubocop"];
43227
- return mode === "fix" ? [...base, "-A"] : base;
43228
- }
43229
- case "phpstan":
43230
- return [phpVendorBin(cwd, "phpstan"), "analyse"];
43231
- case "pint":
43232
- return mode === "fix" ? [phpVendorBin(cwd, "pint")] : [phpVendorBin(cwd, "pint"), "--test"];
43233
- case "php-cs-fixer":
43234
- return mode === "fix" ? [phpVendorBin(cwd, "php-cs-fixer"), "fix"] : [phpVendorBin(cwd, "php-cs-fixer"), "fix", "--dry-run", "--diff"];
43235
- case "phpcs":
43236
- return [phpVendorBin(cwd, "phpcs")];
43643
+ }
43644
+ } else {
43645
+ const regex = /"\$basedir(?:_win)?\/(?<suffix>\.\.\/[^"\r\n]+)"/gi;
43646
+ for (const match of contents.matchAll(regex)) {
43647
+ const suffix = match.groups?.suffix;
43648
+ if (!suffix)
43649
+ continue;
43650
+ const normalized = normalizeRelativePathForPlatform(suffix, platform);
43651
+ if (getExpectedShimSuffixes(linter).some((expected) => normalized.endsWith(expected))) {
43652
+ relativeTarget = suffix;
43653
+ break;
43654
+ }
43655
+ }
43656
+ }
43657
+ if (!relativeTarget)
43658
+ return null;
43659
+ let canonicalShimDir;
43660
+ try {
43661
+ canonicalShimDir = _internals64.realpathSync(path76.dirname(shimPath));
43662
+ } catch {
43663
+ return null;
43664
+ }
43665
+ const resolvedTarget = path76.resolve(canonicalShimDir, relativeTarget.replaceAll("\\", path76.sep).replace(/^[\\/]+/, ""));
43666
+ if (!isRegularFile(resolvedTarget))
43667
+ return null;
43668
+ let canonicalTarget;
43669
+ try {
43670
+ canonicalTarget = _internals64.realpathSync(resolvedTarget);
43671
+ } catch {
43672
+ return null;
43673
+ }
43674
+ const targetPackageMarker = path76.join(NODE_MODULES, ...LINTER_PACKAGES[linter].split("/"));
43675
+ if (!normalizeCaseForPlatform(canonicalTarget, platform).includes(normalizeCaseForPlatform(targetPackageMarker, platform))) {
43676
+ return null;
43677
+ }
43678
+ let canonicalShimParent;
43679
+ try {
43680
+ canonicalShimParent = _internals64.realpathSync(path76.dirname(canonicalShimDir));
43681
+ } catch {
43682
+ return null;
43683
+ }
43684
+ const allowedRoots = [canonicalShimDir, canonicalShimParent];
43685
+ const isContained2 = allowedRoots.some((root) => {
43686
+ const relative10 = path76.relative(root, canonicalTarget);
43687
+ return relative10 === "" || !relative10.startsWith("..") && !path76.isAbsolute(relative10);
43688
+ });
43689
+ if (!isContained2)
43690
+ return null;
43691
+ return canonicalTarget;
43692
+ }
43693
+ function resolvePathLinterCommand(linter, directory) {
43694
+ const platform = _internals64.platform();
43695
+ const envPath = _internals64.pathEnv();
43696
+ const nativeNames = platform === "win32" ? linter === "biome" ? ["biome.exe"] : ["eslint.exe"] : linter === "biome" ? ["biome"] : ["eslint"];
43697
+ const nativeExecutable = resolveExecutableFromPath(nativeNames, envPath, platform);
43698
+ if (nativeExecutable) {
43699
+ return {
43700
+ linter,
43701
+ executable: nativeExecutable,
43702
+ argsPrefix: [],
43703
+ displayPrefix: [nativeExecutable],
43704
+ source: "path-native"
43705
+ };
43706
+ }
43707
+ const shimCandidate = findPathShimCandidate(linter, envPath, platform);
43708
+ if (!shimCandidate)
43709
+ return null;
43710
+ const shimTarget = resolvePathShimTarget(shimCandidate, linter, platform);
43711
+ if (!shimTarget)
43712
+ return null;
43713
+ return {
43714
+ linter,
43715
+ executable: _internals64.execPath(),
43716
+ argsPrefix: [shimTarget],
43717
+ displayPrefix: [path76.join(directory, NODE_MODULES, BIN_DIRECTORY, linter)],
43718
+ source: "path-shim"
43719
+ };
43720
+ }
43721
+ function getLintModeArgs(linter, mode) {
43722
+ if (linter === "biome") {
43723
+ return mode === "fix" ? ["check", "--write", "."] : ["check", "."];
43724
+ }
43725
+ return mode === "fix" ? [".", "--fix"] : ["."];
43726
+ }
43727
+ function combineOutput(result) {
43728
+ let output = result.stdout;
43729
+ if (result.stderr) {
43730
+ output += `${output ? `
43731
+ ` : ""}${result.stderr}`;
43732
+ }
43733
+ const shouldTruncate = result.stdoutTruncated || result.stderrTruncated || output.length > MAX_OUTPUT_BYTES2;
43734
+ if (!shouldTruncate)
43735
+ return output;
43736
+ const trimmed = output.slice(0, MAX_OUTPUT_BYTES2);
43737
+ return `${trimmed}${OUTPUT_TRUNCATION_SUFFIX}`;
43738
+ }
43739
+ function buildExecutionError(mode, command, linter, result) {
43740
+ const reason = result.message ?? (result.status === "timeout" ? "command timed out" : result.status === "cancelled" ? "command was cancelled" : "unknown error");
43741
+ return {
43742
+ success: false,
43743
+ mode,
43744
+ linter,
43745
+ command,
43746
+ exitCode: result.exitCode ?? undefined,
43747
+ output: combineOutput(result),
43748
+ error: `Execution failed: ${reason}`
43749
+ };
43750
+ }
43751
+ async function probeResolvedCommand(command, cwd, abortSignal) {
43752
+ const result = await _internals64.runExternalTool({
43753
+ executable: command.executable,
43754
+ args: [...command.argsPrefix, "--version"],
43755
+ cwd,
43756
+ timeoutMs: LINTER_DETECT_TIMEOUT_MS,
43757
+ maxStdoutBytes: 4096,
43758
+ maxStderrBytes: 4096,
43759
+ abortSignal
43760
+ });
43761
+ return result.status === "completed" && result.exitCode === 0;
43762
+ }
43763
+ async function resolveLinterCommand(linter, directory) {
43764
+ const cwd = toAbsoluteDirectory(directory);
43765
+ return resolveLocalLinterCommand(linter, cwd) ?? resolvePathLinterCommand(linter, cwd);
43766
+ }
43767
+ async function detectResolvedLinter(directory, abortSignal) {
43768
+ if (!directory)
43769
+ return null;
43770
+ if (!_internals64.existsSync(directory))
43771
+ return null;
43772
+ const cwd = toAbsoluteDirectory(directory);
43773
+ for (const linter of SUPPORTED_LINTERS) {
43774
+ const resolved = await resolveLinterCommand(linter, cwd);
43775
+ if (!resolved)
43776
+ continue;
43777
+ if (await probeResolvedCommand(resolved, cwd, abortSignal)) {
43778
+ return resolved;
43779
+ }
43237
43780
  }
43781
+ return null;
43238
43782
  }
43239
43783
  function detectRuff(cwd) {
43240
- if (fs30.existsSync(path76.join(cwd, "ruff.toml")))
43241
- return isCommandAvailable("ruff");
43784
+ if (_internals64.existsSync(path76.join(cwd, "ruff.toml")))
43785
+ return hasAdditionalCommand("ruff");
43242
43786
  try {
43243
43787
  const pyproject = path76.join(cwd, "pyproject.toml");
43244
- if (fs30.existsSync(pyproject)) {
43245
- const content = fs30.readFileSync(pyproject, "utf-8");
43788
+ if (_internals64.existsSync(pyproject)) {
43789
+ const content = _internals64.readFileSync(pyproject, "utf-8");
43246
43790
  if (content.includes("[tool.ruff]"))
43247
- return isCommandAvailable("ruff");
43791
+ return hasAdditionalCommand("ruff");
43248
43792
  }
43249
43793
  } catch {}
43250
43794
  return false;
43251
43795
  }
43252
43796
  function detectClippy(cwd) {
43253
- return fs30.existsSync(path76.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
43797
+ return _internals64.existsSync(path76.join(cwd, "Cargo.toml")) && hasAdditionalCommand("cargo");
43254
43798
  }
43255
43799
  function detectGolangciLint(cwd) {
43256
- return fs30.existsSync(path76.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
43800
+ return _internals64.existsSync(path76.join(cwd, "go.mod")) && hasAdditionalCommand("golangci-lint");
43257
43801
  }
43258
43802
  function readTextFileSafe(filePath) {
43259
43803
  try {
43260
- return fs30.readFileSync(filePath, "utf-8");
43804
+ return _internals64.readFileSync(filePath, "utf-8");
43261
43805
  } catch {
43262
43806
  return "";
43263
43807
  }
43264
43808
  }
43265
- function phpVendorBin(_cwd, name) {
43266
- const fileName = process.platform === "win32" ? `${name}.bat` : name;
43267
- return path76.join("vendor", "bin", fileName);
43809
+ function resolveAdditionalExecutable(name) {
43810
+ const platform = _internals64.platform();
43811
+ if (platform !== "win32") {
43812
+ return _internals64.isCommandAvailable(name) ? name : null;
43813
+ }
43814
+ return resolveExecutableFromPath([`${name}.exe`], _internals64.pathEnv(), platform);
43815
+ }
43816
+ function resolveWindowsCommandInterpreter() {
43817
+ const candidate = _internals64.comSpec();
43818
+ if (!candidate || !path76.isAbsolute(candidate) || path76.basename(candidate).toLowerCase() !== "cmd.exe" || !isRegularFile(candidate)) {
43819
+ return null;
43820
+ }
43821
+ try {
43822
+ const canonical = _internals64.realpathSync(candidate);
43823
+ return path76.basename(canonical).toLowerCase() === "cmd.exe" ? canonical : null;
43824
+ } catch {
43825
+ return null;
43826
+ }
43827
+ }
43828
+ function buildWindowsBatchCommand(wrapperPath, args) {
43829
+ const interpreter = resolveWindowsCommandInterpreter();
43830
+ if (!interpreter)
43831
+ return null;
43832
+ let canonicalWrapper;
43833
+ try {
43834
+ canonicalWrapper = _internals64.realpathSync(wrapperPath);
43835
+ } catch {
43836
+ return null;
43837
+ }
43838
+ if (!isRegularFile(canonicalWrapper) || !WINDOWS_BATCH_WRAPPER_EXTENSIONS.includes(path76.extname(canonicalWrapper).toLowerCase())) {
43839
+ return null;
43840
+ }
43841
+ const tokens = [canonicalWrapper, ...args];
43842
+ if (tokens.some((token) => WINDOWS_CMD_UNSAFE_TOKEN.test(token)))
43843
+ return null;
43844
+ const command = `call ${tokens.map((token) => `"${token}"`).join(" ")}`;
43845
+ return [interpreter, "/d", "/s", "/v:off", "/c", command];
43846
+ }
43847
+ function resolveWindowsBatchCommandFromPath(name, args) {
43848
+ if (_internals64.platform() !== "win32")
43849
+ return null;
43850
+ const wrapper = resolveExecutableFromPath(WINDOWS_BATCH_WRAPPER_EXTENSIONS.map((extension) => `${name}${extension}`), _internals64.pathEnv(), "win32");
43851
+ return wrapper ? buildWindowsBatchCommand(wrapper, args) : null;
43852
+ }
43853
+ function resolveContainedWindowsBatchCommand(cwd, fileName, args) {
43854
+ if (_internals64.platform() !== "win32")
43855
+ return null;
43856
+ const candidate = path76.join(cwd, fileName);
43857
+ if (!isRegularFile(candidate))
43858
+ return null;
43859
+ let canonicalCwd;
43860
+ let canonicalCandidate;
43861
+ try {
43862
+ canonicalCwd = _internals64.realpathSync(cwd);
43863
+ canonicalCandidate = _internals64.realpathSync(candidate);
43864
+ } catch {
43865
+ return null;
43866
+ }
43867
+ const relative10 = path76.relative(canonicalCwd, canonicalCandidate);
43868
+ if (relative10 === "" || relative10.startsWith("..") || path76.isAbsolute(relative10)) {
43869
+ return null;
43870
+ }
43871
+ return buildWindowsBatchCommand(canonicalCandidate, args);
43872
+ }
43873
+ function buildAdditionalCommand(name, args) {
43874
+ const executable = resolveAdditionalExecutable(name);
43875
+ if (executable)
43876
+ return [executable, ...args];
43877
+ return resolveWindowsBatchCommandFromPath(name, args);
43878
+ }
43879
+ function hasAdditionalCommand(name) {
43880
+ return buildAdditionalCommand(name, []) !== null;
43881
+ }
43882
+ function resolvePhpVendorCommand(cwd, name) {
43883
+ const relativeProxy = path76.join("vendor", "bin", name);
43884
+ if (!_internals64.existsSync(path76.join(cwd, relativeProxy)))
43885
+ return null;
43886
+ if (_internals64.platform() !== "win32")
43887
+ return [relativeProxy];
43888
+ const phpExecutable = resolveAdditionalExecutable("php");
43889
+ return phpExecutable ? [phpExecutable, relativeProxy] : null;
43890
+ }
43891
+ function hasCheckstyleGradleSignal(cwd) {
43892
+ const gradleFiles = ["build.gradle", "build.gradle.kts"].map((file) => path76.join(cwd, file)).filter((file) => _internals64.existsSync(file));
43893
+ return _internals64.existsSync(path76.join(cwd, "checkstyle.xml")) || _internals64.existsSync(path76.join(cwd, "config", "checkstyle", "checkstyle.xml")) || gradleFiles.some((file) => /\bcheckstyle\b/i.test(readTextFileSafe(file)));
43894
+ }
43895
+ function resolveCheckstyleCommand(cwd) {
43896
+ const hasMavenProject = _internals64.existsSync(path76.join(cwd, "pom.xml"));
43897
+ const hasGradleSignal = hasCheckstyleGradleSignal(cwd);
43898
+ if (hasGradleSignal) {
43899
+ if (_internals64.platform() === "win32") {
43900
+ const gradlew = resolveContainedWindowsBatchCommand(cwd, "gradlew.bat", [
43901
+ "checkstyleMain"
43902
+ ]);
43903
+ if (gradlew)
43904
+ return gradlew;
43905
+ } else {
43906
+ const gradlew = path76.join(cwd, "gradlew");
43907
+ if (_internals64.existsSync(gradlew))
43908
+ return [gradlew, "checkstyleMain"];
43909
+ }
43910
+ const gradle = buildAdditionalCommand("gradle", ["checkstyleMain"]);
43911
+ if (gradle) {
43912
+ return gradle;
43913
+ }
43914
+ }
43915
+ const maven = buildAdditionalCommand("mvn", ["checkstyle:check"]);
43916
+ if (hasMavenProject && maven) {
43917
+ return maven;
43918
+ }
43919
+ return null;
43268
43920
  }
43269
- function phpVendorBinExists(cwd, name) {
43270
- const local = path76.join(cwd, "vendor", "bin", name);
43271
- const localWin = path76.join(cwd, "vendor", "bin", `${name}.bat`);
43272
- return fs30.existsSync(local) || fs30.existsSync(localWin);
43921
+ function isUnsafeShellWrapperExecutable(executable, platform = _internals64.platform()) {
43922
+ return platform === "win32" && UNSAFE_SHELL_WRAPPER_EXTENSIONS.has(path76.extname(executable).toLowerCase());
43273
43923
  }
43274
43924
  function detectCheckstyle(cwd) {
43275
- const hasMaven = fs30.existsSync(path76.join(cwd, "pom.xml"));
43276
- const gradleFiles = ["build.gradle", "build.gradle.kts"].map((file) => path76.join(cwd, file)).filter((file) => fs30.existsSync(file));
43277
- const hasGradleCheckstyleSignal = fs30.existsSync(path76.join(cwd, "checkstyle.xml")) || fs30.existsSync(path76.join(cwd, "config", "checkstyle", "checkstyle.xml")) || gradleFiles.some((file) => /\bcheckstyle\b/i.test(readTextFileSafe(file)));
43278
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradleCheckstyleSignal && (fs30.existsSync(path76.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
43279
- return (hasMaven || hasGradleCheckstyleSignal) && hasBinary;
43925
+ return resolveCheckstyleCommand(cwd) !== null;
43280
43926
  }
43281
43927
  function hasRootKotlinFile(cwd) {
43282
43928
  try {
43283
- return fs30.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
43929
+ return _internals64.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
43284
43930
  } catch {
43285
43931
  return false;
43286
43932
  }
@@ -43290,59 +43936,59 @@ function buildGradleHasKotlinSignal(cwd) {
43290
43936
  return /\b(kotlin|org\.jetbrains\.kotlin|ktlint)\b/i.test(content);
43291
43937
  }
43292
43938
  function detectKtlint(cwd) {
43293
- const hasKotlin = fs30.existsSync(path76.join(cwd, "build.gradle.kts")) || fs30.existsSync(path76.join(cwd, "build.gradle")) && buildGradleHasKotlinSignal(cwd) || hasRootKotlinFile(cwd);
43294
- return hasKotlin && isCommandAvailable("ktlint");
43939
+ const hasKotlin = _internals64.existsSync(path76.join(cwd, "build.gradle.kts")) || _internals64.existsSync(path76.join(cwd, "build.gradle")) && buildGradleHasKotlinSignal(cwd) || hasRootKotlinFile(cwd);
43940
+ return hasKotlin && hasAdditionalCommand("ktlint");
43295
43941
  }
43296
43942
  function detectPhpLinter(cwd) {
43297
- if ((fs30.existsSync(path76.join(cwd, "phpstan.neon")) || fs30.existsSync(path76.join(cwd, "phpstan.neon.dist"))) && phpVendorBinExists(cwd, "phpstan")) {
43943
+ if ((_internals64.existsSync(path76.join(cwd, "phpstan.neon")) || _internals64.existsSync(path76.join(cwd, "phpstan.neon.dist"))) && resolvePhpVendorCommand(cwd, "phpstan") !== null) {
43298
43944
  return "phpstan";
43299
43945
  }
43300
- if (fs30.existsSync(path76.join(cwd, "pint.json")) && phpVendorBinExists(cwd, "pint")) {
43946
+ if (_internals64.existsSync(path76.join(cwd, "pint.json")) && resolvePhpVendorCommand(cwd, "pint") !== null) {
43301
43947
  return "pint";
43302
43948
  }
43303
- if (fs30.existsSync(path76.join(cwd, ".php-cs-fixer.php")) && phpVendorBinExists(cwd, "php-cs-fixer")) {
43949
+ if (_internals64.existsSync(path76.join(cwd, ".php-cs-fixer.php")) && resolvePhpVendorCommand(cwd, "php-cs-fixer") !== null) {
43304
43950
  return "php-cs-fixer";
43305
43951
  }
43306
- if (fs30.existsSync(path76.join(cwd, "phpcs.xml")) && phpVendorBinExists(cwd, "phpcs")) {
43952
+ if (_internals64.existsSync(path76.join(cwd, "phpcs.xml")) && resolvePhpVendorCommand(cwd, "phpcs") !== null) {
43307
43953
  return "phpcs";
43308
43954
  }
43309
43955
  return null;
43310
43956
  }
43311
43957
  function detectDotnetFormat(cwd) {
43312
43958
  try {
43313
- const files = fs30.readdirSync(cwd);
43959
+ const files = _internals64.readdirSync(cwd);
43314
43960
  const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
43315
- return hasCsproj && isCommandAvailable("dotnet");
43961
+ return hasCsproj && hasAdditionalCommand("dotnet");
43316
43962
  } catch {
43317
43963
  return false;
43318
43964
  }
43319
43965
  }
43320
43966
  function detectCppcheck(cwd) {
43321
- if (fs30.existsSync(path76.join(cwd, "CMakeLists.txt"))) {
43322
- return isCommandAvailable("cppcheck");
43967
+ if (_internals64.existsSync(path76.join(cwd, "CMakeLists.txt"))) {
43968
+ return hasAdditionalCommand("cppcheck");
43323
43969
  }
43324
43970
  try {
43325
43971
  const dirsToCheck = [cwd, path76.join(cwd, "src")];
43326
43972
  const hasCpp = dirsToCheck.some((dir) => {
43327
43973
  try {
43328
- return fs30.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
43974
+ return _internals64.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
43329
43975
  } catch {
43330
43976
  return false;
43331
43977
  }
43332
43978
  });
43333
- return hasCpp && isCommandAvailable("cppcheck");
43979
+ return hasCpp && hasAdditionalCommand("cppcheck");
43334
43980
  } catch {
43335
43981
  return false;
43336
43982
  }
43337
43983
  }
43338
43984
  function detectSwiftlint(cwd) {
43339
- return fs30.existsSync(path76.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
43985
+ return _internals64.existsSync(path76.join(cwd, "Package.swift")) && hasAdditionalCommand("swiftlint");
43340
43986
  }
43341
43987
  function detectDartAnalyze(cwd) {
43342
- return fs30.existsSync(path76.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
43988
+ return _internals64.existsSync(path76.join(cwd, "pubspec.yaml")) && hasAdditionalCommand("dart");
43343
43989
  }
43344
43990
  function detectRubocop(cwd) {
43345
- return (fs30.existsSync(path76.join(cwd, "Gemfile")) || fs30.existsSync(path76.join(cwd, "gems.rb")) || fs30.existsSync(path76.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
43991
+ return (_internals64.existsSync(path76.join(cwd, "Gemfile")) || _internals64.existsSync(path76.join(cwd, "gems.rb")) || _internals64.existsSync(path76.join(cwd, ".rubocop.yml"))) && (hasAdditionalCommand("rubocop") || hasAdditionalCommand("bundle"));
43346
43992
  }
43347
43993
  function detectAdditionalLinter(cwd) {
43348
43994
  if (detectRuff(cwd))
@@ -43370,208 +44016,179 @@ function detectAdditionalLinter(cwd) {
43370
44016
  return "rubocop";
43371
44017
  return null;
43372
44018
  }
43373
- function findBinInAncestors(startDir, binName) {
43374
- let dir = startDir;
43375
- while (true) {
43376
- const candidate = path76.join(dir, "node_modules", ".bin", binName);
43377
- if (fs30.existsSync(candidate))
43378
- return candidate;
43379
- const parent = path76.dirname(dir);
43380
- if (parent === dir)
43381
- break;
43382
- dir = parent;
43383
- }
43384
- return null;
43385
- }
43386
- function findBinInEnvPath(binName) {
43387
- const searchPath = process.env.PATH ?? "";
43388
- for (const dir of searchPath.split(path76.delimiter)) {
43389
- if (!dir)
43390
- continue;
43391
- const candidate = path76.join(dir, binName);
43392
- if (fs30.existsSync(candidate))
43393
- return candidate;
43394
- }
43395
- return null;
43396
- }
43397
44019
  async function detectAvailableLinter(directory) {
43398
- if (!directory)
43399
- return null;
43400
- if (!fs30.existsSync(directory))
43401
- return null;
43402
- const projectDir = directory;
43403
- const isWindows = process.platform === "win32";
43404
- const biomeBin = isWindows ? path76.join(projectDir, "node_modules", ".bin", "biome.EXE") : path76.join(projectDir, "node_modules", ".bin", "biome");
43405
- const eslintBin = isWindows ? path76.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path76.join(projectDir, "node_modules", ".bin", "eslint");
43406
- const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
43407
- if (localResult)
43408
- return localResult;
43409
- const biomeAncestor = findBinInAncestors(path76.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
43410
- const eslintAncestor = findBinInAncestors(path76.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
43411
- if (biomeAncestor || eslintAncestor) {
43412
- return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
43413
- }
43414
- const pathBiome = findBinInEnvPath(isWindows ? "biome.EXE" : "biome");
43415
- const pathEslint = findBinInEnvPath(isWindows ? "eslint.cmd" : "eslint");
43416
- if (pathBiome || pathEslint) {
43417
- return _detectAvailableLinter(projectDir, pathBiome ?? biomeBin, pathEslint ?? eslintBin);
43418
- }
43419
- return null;
44020
+ const resolved = await detectResolvedLinter(directory);
44021
+ return resolved?.linter ?? null;
43420
44022
  }
43421
- async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
43422
- const DETECT_TIMEOUT = 2000;
43423
- try {
43424
- const biomeProc = bunSpawn([biomeBin, "--version"], {
43425
- stdout: "pipe",
43426
- stderr: "pipe"
43427
- });
43428
- const biomeExit = biomeProc.exited;
43429
- const timeout = new Promise((resolve23) => setTimeout(() => resolve23("timeout"), DETECT_TIMEOUT));
43430
- const result = await Promise.race([biomeExit, timeout]);
43431
- if (result === "timeout") {
43432
- biomeProc.kill();
43433
- } else if (biomeProc.exitCode === 0 && fs30.existsSync(biomeBin)) {
43434
- return "biome";
43435
- }
43436
- } catch {}
43437
- try {
43438
- const eslintProc = bunSpawn([eslintBin, "--version"], {
43439
- stdout: "pipe",
43440
- stderr: "pipe"
43441
- });
43442
- const eslintExit = eslintProc.exited;
43443
- const timeout = new Promise((resolve23) => setTimeout(() => resolve23("timeout"), DETECT_TIMEOUT));
43444
- const result = await Promise.race([eslintExit, timeout]);
43445
- if (result === "timeout") {
43446
- eslintProc.kill();
43447
- } else if (eslintProc.exitCode === 0 && fs30.existsSync(eslintBin)) {
43448
- return "eslint";
43449
- }
43450
- } catch {}
43451
- return null;
43452
- }
43453
- async function runLint(linter, mode, directory) {
43454
- const command = getLinterCommand(linter, mode, directory);
43455
- const commandStr = command.join(" ");
44023
+ async function runResolvedLint(command, mode, directory, abortSignal) {
44024
+ const lintArgs = getLintModeArgs(command.linter, mode);
44025
+ const displayCommand = [...command.displayPrefix, ...lintArgs];
44026
+ const commandStr = displayCommand.join(" ");
43456
44027
  if (commandStr.length > MAX_COMMAND_LENGTH) {
43457
44028
  return {
43458
44029
  success: false,
43459
44030
  mode,
43460
- linter,
43461
- command,
44031
+ linter: command.linter,
44032
+ command: displayCommand,
43462
44033
  error: "Command exceeds maximum allowed length"
43463
44034
  };
43464
44035
  }
43465
- try {
43466
- const proc = bunSpawn(command, {
43467
- stdout: "pipe",
43468
- stderr: "pipe",
43469
- cwd: directory
43470
- });
43471
- const [stdout, stderr] = await Promise.all([
43472
- proc.stdout.text(),
43473
- proc.stderr.text()
43474
- ]);
43475
- const exitCode = await proc.exited;
43476
- let output = stdout;
43477
- if (stderr) {
43478
- output += (output ? `
43479
- ` : "") + stderr;
44036
+ const runResult = await _internals64.runExternalTool({
44037
+ executable: command.executable,
44038
+ args: [...command.argsPrefix, ...lintArgs],
44039
+ cwd: toAbsoluteDirectory(directory),
44040
+ timeoutMs: LINTER_RUN_TIMEOUT_MS,
44041
+ maxStdoutBytes: MAX_OUTPUT_BYTES2,
44042
+ maxStderrBytes: MAX_OUTPUT_BYTES2,
44043
+ abortSignal
44044
+ });
44045
+ if (runResult.status !== "completed") {
44046
+ return buildExecutionError(mode, displayCommand, command.linter, runResult);
44047
+ }
44048
+ const exitCode = runResult.exitCode ?? 0;
44049
+ const result = {
44050
+ success: true,
44051
+ mode,
44052
+ linter: command.linter,
44053
+ command: displayCommand,
44054
+ exitCode,
44055
+ output: combineOutput(runResult)
44056
+ };
44057
+ if (exitCode === 0) {
44058
+ result.message = `${command.linter} ${mode} completed successfully with no issues`;
44059
+ } else if (mode === "fix") {
44060
+ result.message = `${command.linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
44061
+ } else {
44062
+ result.message = `${command.linter} check found issues (exit code ${exitCode}).`;
44063
+ }
44064
+ return result;
44065
+ }
44066
+ function getAdditionalLinterCommand(linter, mode, cwd) {
44067
+ switch (linter) {
44068
+ case "ruff":
44069
+ return buildAdditionalCommand("ruff", mode === "fix" ? ["check", "--fix", "."] : ["check", "."]);
44070
+ case "clippy":
44071
+ return buildAdditionalCommand("cargo", mode === "fix" ? ["clippy", "--fix", "--allow-dirty"] : ["clippy"]);
44072
+ case "golangci-lint":
44073
+ return buildAdditionalCommand("golangci-lint", mode === "fix" ? ["run", "--fix"] : ["run"]);
44074
+ case "checkstyle":
44075
+ return resolveCheckstyleCommand(cwd);
44076
+ case "ktlint":
44077
+ return buildAdditionalCommand("ktlint", mode === "fix" ? ["--format"] : []);
44078
+ case "dotnet-format":
44079
+ return buildAdditionalCommand("dotnet", mode === "fix" ? ["format"] : ["format", "--verify-no-changes"]);
44080
+ case "cppcheck":
44081
+ return buildAdditionalCommand("cppcheck", ["--enable=all", "."]);
44082
+ case "swiftlint":
44083
+ return buildAdditionalCommand("swiftlint", mode === "fix" ? ["--fix"] : []);
44084
+ case "dart-analyze":
44085
+ return buildAdditionalCommand("dart", mode === "fix" ? ["fix"] : ["analyze"]);
44086
+ case "rubocop": {
44087
+ const bundle = buildAdditionalCommand("bundle", [
44088
+ "exec",
44089
+ "rubocop",
44090
+ ...mode === "fix" ? ["-A"] : []
44091
+ ]);
44092
+ if (bundle) {
44093
+ return bundle;
44094
+ }
44095
+ return buildAdditionalCommand("rubocop", mode === "fix" ? ["-A"] : []);
43480
44096
  }
43481
- if (output.length > MAX_OUTPUT_BYTES2) {
43482
- output = `${output.slice(0, MAX_OUTPUT_BYTES2)}
43483
- ... (output truncated)`;
44097
+ case "phpstan": {
44098
+ const base = resolvePhpVendorCommand(cwd, "phpstan");
44099
+ return base ? [...base, "analyse"] : null;
43484
44100
  }
43485
- const result = {
43486
- success: true,
43487
- mode,
43488
- linter,
43489
- command,
43490
- exitCode,
43491
- output
43492
- };
43493
- if (exitCode === 0) {
43494
- result.message = `${linter} ${mode} completed successfully with no issues`;
43495
- } else if (mode === "fix") {
43496
- result.message = `${linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
43497
- } else {
43498
- result.message = `${linter} check found issues (exit code ${exitCode}).`;
44101
+ case "pint": {
44102
+ const base = resolvePhpVendorCommand(cwd, "pint");
44103
+ return base ? mode === "fix" ? base : [...base, "--test"] : null;
43499
44104
  }
43500
- return result;
43501
- } catch (error2) {
44105
+ case "php-cs-fixer": {
44106
+ const base = resolvePhpVendorCommand(cwd, "php-cs-fixer");
44107
+ return base ? mode === "fix" ? [...base, "fix"] : [...base, "fix", "--dry-run", "--diff"] : null;
44108
+ }
44109
+ case "phpcs":
44110
+ return resolvePhpVendorCommand(cwd, "phpcs");
44111
+ }
44112
+ }
44113
+ async function runLint(linter, mode, directory, abortSignal) {
44114
+ const resolved = await _internals64.resolveLinterCommand(linter, directory);
44115
+ if (!resolved) {
43502
44116
  return {
43503
44117
  success: false,
43504
44118
  mode,
43505
44119
  linter,
43506
- command,
43507
- error: error2 instanceof Error ? `Execution failed: ${error2.message}` : "Execution failed: unknown error"
44120
+ error: `No safely resolved ${linter} executable found`
43508
44121
  };
43509
44122
  }
44123
+ return runResolvedLint(resolved, mode, directory, abortSignal);
43510
44124
  }
43511
- async function runAdditionalLint(linter, mode, cwd) {
44125
+ async function runAdditionalLint(linter, mode, cwd, abortSignal) {
43512
44126
  const command = getAdditionalLinterCommand(linter, mode, cwd);
43513
- const commandStr = command.join(" ");
43514
- if (commandStr.length > MAX_COMMAND_LENGTH) {
44127
+ if (!command) {
43515
44128
  return {
43516
44129
  success: false,
43517
44130
  mode,
43518
44131
  linter,
43519
- command,
43520
- error: "Command exceeds maximum allowed length"
44132
+ error: `No safely executable ${linter} command found`
43521
44133
  };
43522
44134
  }
43523
- try {
43524
- const proc = bunSpawn(command, {
43525
- stdout: "pipe",
43526
- stderr: "pipe",
43527
- cwd
43528
- });
43529
- const [stdout, stderr] = await Promise.all([
43530
- proc.stdout.text(),
43531
- proc.stderr.text()
43532
- ]);
43533
- const exitCode = await proc.exited;
43534
- let output = stdout;
43535
- if (stderr) {
43536
- output += (output ? `
43537
- ` : "") + stderr;
43538
- }
43539
- if (output.length > MAX_OUTPUT_BYTES2) {
43540
- output = `${output.slice(0, MAX_OUTPUT_BYTES2)}
43541
- ... (output truncated)`;
43542
- }
43543
- const result = {
43544
- success: true,
44135
+ const commandStr = command.join(" ");
44136
+ if (commandStr.length > MAX_COMMAND_LENGTH) {
44137
+ return {
44138
+ success: false,
43545
44139
  mode,
43546
44140
  linter,
43547
44141
  command,
43548
- exitCode,
43549
- output
44142
+ error: "Command exceeds maximum allowed length"
43550
44143
  };
43551
- if (exitCode === 0) {
43552
- result.message = `${linter} ${mode} completed successfully with no issues`;
43553
- } else if (mode === "fix") {
43554
- result.message = `${linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
43555
- } else {
43556
- result.message = `${linter} check found issues (exit code ${exitCode}).`;
43557
- }
43558
- return result;
43559
- } catch (error2) {
44144
+ }
44145
+ const [executable, ...args] = command;
44146
+ if (isUnsafeShellWrapperExecutable(executable)) {
43560
44147
  return {
43561
44148
  success: false,
43562
44149
  mode,
43563
44150
  linter,
43564
44151
  command,
43565
- error: error2 instanceof Error ? `Execution failed: ${error2.message}` : "Execution failed: unknown error"
44152
+ error: `No safely executable ${linter} command found`
43566
44153
  };
43567
44154
  }
44155
+ const runResult = await _internals64.runExternalTool({
44156
+ executable,
44157
+ args,
44158
+ cwd: toAbsoluteDirectory(cwd),
44159
+ timeoutMs: LINTER_RUN_TIMEOUT_MS,
44160
+ maxStdoutBytes: MAX_OUTPUT_BYTES2,
44161
+ maxStderrBytes: MAX_OUTPUT_BYTES2,
44162
+ abortSignal,
44163
+ windowsVerbatimArguments: _internals64.platform() === "win32" && path76.basename(executable).toLowerCase() === "cmd.exe"
44164
+ });
44165
+ if (runResult.status !== "completed") {
44166
+ return buildExecutionError(mode, command, linter, runResult);
44167
+ }
44168
+ const exitCode = runResult.exitCode ?? 0;
44169
+ const result = {
44170
+ success: true,
44171
+ mode,
44172
+ linter,
44173
+ command,
44174
+ exitCode,
44175
+ output: combineOutput(runResult)
44176
+ };
44177
+ if (exitCode === 0) {
44178
+ result.message = `${linter} ${mode} completed successfully with no issues`;
44179
+ } else if (mode === "fix") {
44180
+ result.message = `${linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
44181
+ } else {
44182
+ result.message = `${linter} check found issues (exit code ${exitCode}).`;
44183
+ }
44184
+ return result;
43568
44185
  }
43569
44186
  var lint = createSwarmTool({
43570
44187
  description: "Run project linter in check or fix mode. Supports biome, eslint (JS/TS), ruff (Python), clippy (Rust), golangci-lint (Go), checkstyle (Java), ktlint (Kotlin), dotnet-format (C#), cppcheck (C/C++), swiftlint (Swift), dart analyze (Dart), and rubocop (Ruby). Returns JSON with success status, exit code, and output for architect pre-reviewer gate. Use check mode for CI/linting and fix mode to automatically apply fixes.",
43571
44188
  args: {
43572
44189
  mode: exports_external.enum(["fix", "check"]).describe('Linting mode: "check" for read-only lint check, "fix" to automatically apply fixes')
43573
44190
  },
43574
- async execute(args, directory) {
44191
+ async execute(args, directory, ctx) {
43575
44192
  if (!validateArgs(args)) {
43576
44193
  const errorResult2 = {
43577
44194
  success: false,
@@ -43590,15 +44207,15 @@ var lint = createSwarmTool({
43590
44207
  }
43591
44208
  const { mode } = args;
43592
44209
  const cwd = directory;
43593
- const linter = await _internals64.detectAvailableLinter(directory);
43594
- if (linter) {
43595
- const result = await _internals64.runLint(linter, mode, directory);
44210
+ const resolvedLinter = await _internals64.detectResolvedLinter(directory, ctx?.abort);
44211
+ if (resolvedLinter) {
44212
+ const result = await _internals64.runResolvedLint(resolvedLinter, mode, directory, ctx?.abort);
43596
44213
  return JSON.stringify(result, null, 2);
43597
44214
  }
43598
44215
  const additionalLinter = _internals64.detectAdditionalLinter(cwd);
43599
44216
  if (additionalLinter) {
43600
44217
  warn(`[lint] Using ${additionalLinter} linter for this project`);
43601
- const result = await _internals64.runAdditionalLint(additionalLinter, mode, cwd);
44218
+ const result = await _internals64.runAdditionalLint(additionalLinter, mode, cwd, ctx?.abort);
43602
44219
  return JSON.stringify(result, null, 2);
43603
44220
  }
43604
44221
  const errorResult = {
@@ -43613,10 +44230,31 @@ For Rust: rustup component add clippy`
43613
44230
  }
43614
44231
  });
43615
44232
  var _internals64 = {
44233
+ MAX_PACKAGE_MANIFEST_BYTES,
44234
+ MAX_PATH_SHIM_BYTES,
44235
+ arch: () => process.arch,
44236
+ closeSync: fs30.closeSync,
44237
+ comSpec: () => process.env.ComSpec,
44238
+ detectAdditionalLinter,
43616
44239
  detectAvailableLinter,
44240
+ detectResolvedLinter,
44241
+ execPath: () => process.execPath,
44242
+ existsSync: fs30.existsSync,
44243
+ fstatSync: fs30.fstatSync,
44244
+ isCommandAvailable,
44245
+ openSync: fs30.openSync,
44246
+ pathEnv: () => process.env.PATH ?? "",
44247
+ platform: () => process.platform,
44248
+ readFileSync: fs30.readFileSync,
44249
+ readSync: fs30.readSync,
44250
+ readdirSync: fs30.readdirSync,
44251
+ realpathSync: fs30.realpathSync,
44252
+ resolveLinterCommand,
44253
+ runAdditionalLint,
44254
+ runExternalTool,
43617
44255
  runLint,
43618
- detectAdditionalLinter,
43619
- runAdditionalLint
44256
+ runResolvedLint,
44257
+ statSync: fs30.statSync
43620
44258
  };
43621
44259
 
43622
44260
  // src/tools/secretscan.ts
@@ -43980,8 +44618,19 @@ function createRedactedContext(line, findings) {
43980
44618
  return `${result.slice(0, edgeLength)}${truncationMarker}${result.slice(-remaining)}`;
43981
44619
  }
43982
44620
  var O_NOFOLLOW = process.platform !== "win32" ? fs31.constants.O_NOFOLLOW : undefined;
43983
- function readBoundedFile(filePath) {
43984
- let fd;
44621
+ function getStableFileIdentity(stat6) {
44622
+ const { dev, ino } = stat6;
44623
+ if (dev < 0n || ino < 0n || dev === 0n && ino === 0n) {
44624
+ return null;
44625
+ }
44626
+ return `${dev}:${ino}`;
44627
+ }
44628
+ function readBoundedFile(filePath, expectedPathStat) {
44629
+ const expectedIdentity = getStableFileIdentity(expectedPathStat);
44630
+ if (expectedIdentity === null) {
44631
+ return { status: "incomplete", reason: "read_error" };
44632
+ }
44633
+ let fd = null;
43985
44634
  try {
43986
44635
  const flags = fs31.constants.O_RDONLY | (O_NOFOLLOW !== undefined ? O_NOFOLLOW : 0);
43987
44636
  fd = _internals65.openFile(filePath, flags);
@@ -43989,13 +44638,17 @@ function readBoundedFile(filePath) {
43989
44638
  return { status: "incomplete", reason: "read_error" };
43990
44639
  }
43991
44640
  let outcome;
44641
+ let closeFailed = false;
43992
44642
  try {
43993
44643
  const stat6 = _internals65.fstatFile(fd);
44644
+ const descriptorIdentity = getStableFileIdentity(stat6);
43994
44645
  const initialSize = stat6.size;
43995
44646
  if (!stat6.isFile()) {
43996
44647
  outcome = { status: "incomplete", reason: "non_file" };
44648
+ } else if (descriptorIdentity === null || descriptorIdentity !== expectedIdentity) {
44649
+ outcome = { status: "incomplete", reason: "read_error" };
43997
44650
  } else {
43998
- const bufferLength = Math.min(initialSize + 1, MAX_FILE_SIZE_BYTES3 + 1);
44651
+ const bufferLength = initialSize >= BigInt(MAX_FILE_SIZE_BYTES3) ? MAX_FILE_SIZE_BYTES3 + 1 : Number(initialSize) + 1;
43999
44652
  const buffer = Buffer.allocUnsafe(bufferLength);
44000
44653
  let bytesRead = 0;
44001
44654
  while (bytesRead < buffer.length) {
@@ -44005,10 +44658,15 @@ function readBoundedFile(filePath) {
44005
44658
  bytesRead += count;
44006
44659
  }
44007
44660
  const postStat = _internals65.fstatFile(fd);
44008
- if (postStat.size !== initialSize) {
44661
+ const postDescriptorIdentity = getStableFileIdentity(postStat);
44662
+ const postPathStat = _internals65.lstatFile(filePath);
44663
+ const postPathIdentity = getStableFileIdentity(postPathStat);
44664
+ if (!postStat.isFile() || postDescriptorIdentity === null || postDescriptorIdentity !== descriptorIdentity || postPathStat.isSymbolicLink() || !postPathStat.isFile() || postPathIdentity === null || postPathIdentity !== descriptorIdentity) {
44665
+ outcome = { status: "incomplete", reason: "read_error" };
44666
+ } else if (postStat.size !== initialSize) {
44009
44667
  outcome = {
44010
44668
  status: "incomplete",
44011
- reason: postStat.size > MAX_FILE_SIZE_BYTES3 ? "oversized" : "read_error"
44669
+ reason: postStat.size > BigInt(MAX_FILE_SIZE_BYTES3) ? "oversized" : "read_error"
44012
44670
  };
44013
44671
  } else {
44014
44672
  outcome = {
@@ -44020,25 +44678,30 @@ function readBoundedFile(filePath) {
44020
44678
  }
44021
44679
  } catch {
44022
44680
  outcome = { status: "incomplete", reason: "read_error" };
44681
+ } finally {
44682
+ if (fd !== null) {
44683
+ try {
44684
+ _internals65.closeFile(fd);
44685
+ } catch {
44686
+ closeFailed = true;
44687
+ }
44688
+ }
44023
44689
  }
44024
- try {
44025
- _internals65.closeFile(fd);
44026
- } catch {
44690
+ if (closeFailed)
44027
44691
  return { status: "incomplete", reason: "read_error" };
44028
- }
44029
44692
  return outcome;
44030
44693
  }
44031
44694
  function scanFileForSecrets(filePath, mode) {
44032
44695
  const findings = [];
44033
44696
  try {
44034
- const lstat3 = fs31.lstatSync(filePath);
44697
+ const lstat3 = _internals65.lstatFile(filePath);
44035
44698
  if (lstat3.isSymbolicLink()) {
44036
44699
  return mode === "explicit" ? { status: "incomplete", reason: "symlink" } : { status: "skipped", reason: "symlink" };
44037
44700
  }
44038
44701
  if (!lstat3.isFile()) {
44039
44702
  return { status: "incomplete", reason: "non_file" };
44040
44703
  }
44041
- const readOutcome = readBoundedFile(filePath);
44704
+ const readOutcome = readBoundedFile(filePath, lstat3);
44042
44705
  if (readOutcome.status === "incomplete")
44043
44706
  return readOutcome;
44044
44707
  const { buffer, truncated } = readOutcome;
@@ -44086,7 +44749,7 @@ function assertNever(value) {
44086
44749
  throw new Error(`Unhandled secretscan outcome: ${String(value)}`);
44087
44750
  }
44088
44751
  async function yieldToEventLoop() {
44089
- await new Promise((resolve24) => setImmediate(resolve24));
44752
+ await new Promise((resolve25) => setImmediate(resolve25));
44090
44753
  }
44091
44754
  function serializeSecretscanResult(result) {
44092
44755
  let findings = result.findings;
@@ -44125,14 +44788,14 @@ function isPathWithinScope(realPath, scanDir) {
44125
44788
  }
44126
44789
  var DISCOVERY_YIELD_INTERVAL = 100;
44127
44790
  function toSafeRelativePath(rootDir, targetPath) {
44128
- const relative10 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
44129
- if (!relative10 || relative10 === ".")
44791
+ const relative11 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
44792
+ if (!relative11 || relative11 === ".")
44130
44793
  return ".";
44131
- if (relative10.startsWith("..") || path77.isAbsolute(relative10)) {
44132
- const basename15 = path77.basename(targetPath).replace(/\\/g, "/");
44133
- return basename15 || ".";
44794
+ if (relative11.startsWith("..") || path77.isAbsolute(relative11)) {
44795
+ const basename16 = path77.basename(targetPath).replace(/\\/g, "/");
44796
+ return basename16 || ".";
44134
44797
  }
44135
- return relative10;
44798
+ return relative11;
44136
44799
  }
44137
44800
  function recordIncompletePath(target, rootDir, targetPath, reason) {
44138
44801
  if (target.length >= MAX_INCOMPLETE_PATHS) {
@@ -44714,10 +45377,10 @@ var _internals65 = {
44714
45377
  now: Date.now,
44715
45378
  yieldToEventLoop,
44716
45379
  openFile: fs31.openSync,
44717
- fstatFile: fs31.fstatSync,
45380
+ fstatFile: (fd) => fs31.fstatSync(fd, { bigint: true }),
44718
45381
  readFileChunk: fs31.readSync,
44719
45382
  closeFile: fs31.closeSync,
44720
- lstatFile: fs31.lstatSync,
45383
+ lstatFile: (path78) => fs31.lstatSync(path78, { bigint: true }),
44721
45384
  closeDirectory: (directory) => directory.closeSync()
44722
45385
  };
44723
45386
 
@@ -45721,7 +46384,7 @@ var DISPATCH_FRAMEWORK_MAP = {
45721
46384
  };
45722
46385
  async function detectTestFrameworkViaDispatch(cwd) {
45723
46386
  try {
45724
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46387
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45725
46388
  const backend = await pickBackend(cwd);
45726
46389
  if (!backend?.selectTestFramework)
45727
46390
  return "none";
@@ -45737,7 +46400,7 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
45737
46400
  if (framework === "none")
45738
46401
  return null;
45739
46402
  try {
45740
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46403
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45741
46404
  const backend = await pickBackend(baseDir);
45742
46405
  if (backend?.buildTestCommand) {
45743
46406
  const cmd = backend.buildTestCommand(framework, files, baseDir, {
@@ -45757,7 +46420,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
45757
46420
  if (framework === "none")
45758
46421
  return null;
45759
46422
  try {
45760
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46423
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45761
46424
  const backend = await pickBackend(baseDir);
45762
46425
  if (!backend?.parseTestOutput)
45763
46426
  return null;
@@ -45967,21 +46630,21 @@ function hasCompoundTestExtension(filename) {
45967
46630
  const lower = filename.toLowerCase();
45968
46631
  return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
45969
46632
  }
45970
- function isLanguageSpecificTestFile(basename16) {
45971
- const lower = basename16.toLowerCase();
46633
+ function isLanguageSpecificTestFile(basename17) {
46634
+ const lower = basename17.toLowerCase();
45972
46635
  if (lower.endsWith("_test.go"))
45973
46636
  return true;
45974
46637
  if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
45975
46638
  return true;
45976
46639
  if (lower.endsWith("_spec.rb"))
45977
46640
  return true;
45978
- if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.java") || basename16.endsWith("Tests.java") || lower.endsWith("it.java")))
46641
+ if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename17) || basename17.endsWith("Test.java") || basename17.endsWith("Tests.java") || lower.endsWith("it.java")))
45979
46642
  return true;
45980
46643
  if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
45981
46644
  return true;
45982
- if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename16) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
46645
+ if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename17) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
45983
46646
  return true;
45984
- if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.php") || basename16.endsWith("Tests.php")))
46647
+ if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename17) || basename17.endsWith("Test.php") || basename17.endsWith("Tests.php")))
45985
46648
  return true;
45986
46649
  if (lower.endsWith(".tests.ps1"))
45987
46650
  return true;
@@ -45989,23 +46652,23 @@ function isLanguageSpecificTestFile(basename16) {
45989
46652
  }
45990
46653
  function isConventionTestFilePath(filePath) {
45991
46654
  const normalizedPath = filePath.replace(/\\/g, "/");
45992
- const basename16 = path81.basename(filePath);
45993
- return hasCompoundTestExtension(basename16) || basename16.includes(".spec.") || basename16.includes(".test.") || isLanguageSpecificTestFile(basename16) || isTestDirectoryPath(normalizedPath);
46655
+ const basename17 = path81.basename(filePath);
46656
+ return hasCompoundTestExtension(basename17) || basename17.includes(".spec.") || basename17.includes(".test.") || isLanguageSpecificTestFile(basename17) || isTestDirectoryPath(normalizedPath);
45994
46657
  }
45995
46658
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45996
46659
  const testFiles = [];
45997
46660
  for (const file of sourceFiles) {
45998
46661
  const absoluteFile = resolveWorkspacePath(file, workingDir);
45999
46662
  const relativeFile = path81.relative(workingDir, absoluteFile);
46000
- const basename16 = path81.basename(absoluteFile);
46663
+ const basename17 = path81.basename(absoluteFile);
46001
46664
  const dirname30 = path81.dirname(absoluteFile);
46002
46665
  const preferRelativeOutput = !path81.isAbsolute(file);
46003
46666
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
46004
46667
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
46005
46668
  continue;
46006
46669
  }
46007
- const nameWithoutExt = basename16.replace(/\.[^.]+$/, "");
46008
- const ext = path81.extname(basename16);
46670
+ const nameWithoutExt = basename17.replace(/\.[^.]+$/, "");
46671
+ const ext = path81.extname(basename17);
46009
46672
  const genericTestNames = [
46010
46673
  `${nameWithoutExt}.spec${ext}`,
46011
46674
  `${nameWithoutExt}.test${ext}`
@@ -46016,7 +46679,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
46016
46679
  ...languageSpecificTestNames
46017
46680
  ].map((candidateName) => path81.join(dirname30, candidateName));
46018
46681
  const testDirectoryNames = [
46019
- basename16,
46682
+ basename17,
46020
46683
  ...genericTestNames,
46021
46684
  ...languageSpecificTestNames
46022
46685
  ];
@@ -46281,13 +46944,13 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
46281
46944
  'Dir.glob("test/**/*_test.rb").sort.each { |f| require_relative f }'
46282
46945
  ];
46283
46946
  case "pest": {
46284
- const args = [phpVendorBin2("pest")];
46947
+ const args = [phpVendorBin("pest")];
46285
46948
  if (scope !== "all" && files.length > 0)
46286
46949
  args.push(...files);
46287
46950
  return args;
46288
46951
  }
46289
46952
  case "phpunit": {
46290
- const args = [phpVendorBin2("phpunit")];
46953
+ const args = [phpVendorBin("phpunit")];
46291
46954
  if (scope !== "all" && files.length > 0)
46292
46955
  args.push(...files);
46293
46956
  return args;
@@ -46302,7 +46965,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
46302
46965
  return null;
46303
46966
  }
46304
46967
  }
46305
- function phpVendorBin2(name) {
46968
+ function phpVendorBin(name) {
46306
46969
  return path81.join("vendor", "bin", process.platform === "win32" ? `${name}.bat` : name);
46307
46970
  }
46308
46971
  function mapFrameworkStatusToResult(status) {
@@ -46741,9 +47404,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
46741
47404
  stderr: "pipe",
46742
47405
  cwd
46743
47406
  });
46744
- const timeoutPromise = new Promise((resolve26) => setTimeout(() => {
47407
+ const timeoutPromise = new Promise((resolve27) => setTimeout(() => {
46745
47408
  proc.kill();
46746
- resolve26(-1);
47409
+ resolve27(-1);
46747
47410
  }, timeout_ms));
46748
47411
  const [exitCode, stdoutResult, stderrResult] = await Promise.all([
46749
47412
  Promise.race([proc.exited, timeoutPromise]),
@@ -46901,11 +47564,11 @@ function normalizeHistoryTestFile(testFile, workingDir) {
46901
47564
  const normalized = testFile.replace(/\\/g, "/");
46902
47565
  if (!path81.isAbsolute(testFile))
46903
47566
  return normalized;
46904
- const relative11 = path81.relative(workingDir, testFile);
46905
- if (relative11.startsWith("..") || path81.isAbsolute(relative11)) {
47567
+ const relative12 = path81.relative(workingDir, testFile);
47568
+ if (relative12.startsWith("..") || path81.isAbsolute(relative12)) {
46906
47569
  return normalized;
46907
47570
  }
46908
- return relative11.replace(/\\/g, "/");
47571
+ return relative12.replace(/\\/g, "/");
46909
47572
  }
46910
47573
  function combineAggregateResult(current, next) {
46911
47574
  if (current === "fail" || next === "fail")
@@ -48329,13 +48992,13 @@ class CircuitBreaker {
48329
48992
  if (this.config.callTimeoutMs <= 0) {
48330
48993
  return fn();
48331
48994
  }
48332
- return new Promise((resolve27, reject) => {
48995
+ return new Promise((resolve28, reject) => {
48333
48996
  const timeout = setTimeout(() => {
48334
48997
  reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
48335
48998
  }, this.config.callTimeoutMs);
48336
48999
  fn().then((result) => {
48337
49000
  clearTimeout(timeout);
48338
- resolve27(result);
49001
+ resolve28(result);
48339
49002
  }).catch((error2) => {
48340
49003
  clearTimeout(timeout);
48341
49004
  reject(error2);
@@ -48634,7 +49297,7 @@ class AutomationQueue {
48634
49297
 
48635
49298
  // src/background/worker.ts
48636
49299
  function sleep(ms) {
48637
- return new Promise((resolve27) => setTimeout(resolve27, ms));
49300
+ return new Promise((resolve28) => setTimeout(resolve28, ms));
48638
49301
  }
48639
49302
 
48640
49303
  class WorkerManager {
@@ -50584,7 +51247,7 @@ function resolveReviewFallbackModels(agentName, registry) {
50584
51247
  init_logger();
50585
51248
 
50586
51249
  // src/hooks/review-receipt-scope.ts
50587
- import * as child_process7 from "child_process";
51250
+ import * as child_process6 from "child_process";
50588
51251
  import { createHash as createHash14 } from "crypto";
50589
51252
  import * as fs42 from "fs";
50590
51253
  import * as path89 from "path";
@@ -50596,7 +51259,7 @@ var HEAD_OUTPUT_BYTES = 256;
50596
51259
  var HEAD_SHA_PATTERN = /^[0-9a-f]{40,64}$/i;
50597
51260
  var REVIEWER_TASK_SCOPE_DESCRIPTION = "reviewer-task-files-v1";
50598
51261
  var _internals74 = {
50599
- spawn: child_process7.spawn,
51262
+ spawn: child_process6.spawn,
50600
51263
  realpath: fs42.promises.realpath,
50601
51264
  lstatBigInt: (path90) => fs42.promises.lstat(path90, { bigint: true }),
50602
51265
  open: fs42.promises.open,
@@ -50611,8 +51274,8 @@ function hasControlCharacter2(value) {
50611
51274
  return false;
50612
51275
  }
50613
51276
  function isContained2(root, candidate) {
50614
- const relative14 = path89.relative(root, candidate);
50615
- return relative14.length > 0 && relative14 !== ".." && !relative14.startsWith(`..${path89.sep}`) && !path89.isAbsolute(relative14);
51277
+ const relative15 = path89.relative(root, candidate);
51278
+ return relative15.length > 0 && relative15 !== ".." && !relative15.startsWith(`..${path89.sep}`) && !path89.isAbsolute(relative15);
50616
51279
  }
50617
51280
  function canonicalPath(relativePath2) {
50618
51281
  return relativePath2.split(path89.sep).join("/");
@@ -50668,7 +51331,7 @@ async function resolveHeadSha(directory) {
50668
51331
  timeout: HEAD_TIMEOUT_MS,
50669
51332
  windowsHide: true
50670
51333
  });
50671
- return await new Promise((resolve30) => {
51334
+ return await new Promise((resolve31) => {
50672
51335
  let settled = false;
50673
51336
  let stdoutBytes = 0;
50674
51337
  const stdoutChunks = [];
@@ -50678,7 +51341,7 @@ async function resolveHeadSha(directory) {
50678
51341
  settled = true;
50679
51342
  if (timeoutHandle !== undefined)
50680
51343
  clearTimeout(timeoutHandle);
50681
- resolve30(value);
51344
+ resolve31(value);
50682
51345
  };
50683
51346
  const killAndFail = () => {
50684
51347
  try {
@@ -51398,8 +52061,8 @@ function normalizedRelativePath(file) {
51398
52061
  return normalized.replace(/^\.\//, "");
51399
52062
  }
51400
52063
  function isWithinRoot2(root, candidate) {
51401
- const relative16 = path91.relative(root, candidate);
51402
- return relative16.length > 0 && relative16 !== ".." && !relative16.startsWith(`..${path91.sep}`) && !path91.isAbsolute(relative16);
52064
+ const relative17 = path91.relative(root, candidate);
52065
+ return relative17.length > 0 && relative17 !== ".." && !relative17.startsWith(`..${path91.sep}`) && !path91.isAbsolute(relative17);
51403
52066
  }
51404
52067
  function rangesOverlap(start, end, ranges) {
51405
52068
  return ranges.some((range) => start <= range.end && end >= range.start);
@@ -51963,8 +52626,8 @@ function formatSelector(selector) {
51963
52626
  }
51964
52627
  }
51965
52628
  function isPathInside(parent, candidate) {
51966
- const relative17 = path92.relative(parent, candidate);
51967
- return relative17.length > 0 && relative17 !== ".." && !relative17.startsWith(`..${path92.sep}`) && !path92.isAbsolute(relative17);
52629
+ const relative18 = path92.relative(parent, candidate);
52630
+ return relative18.length > 0 && relative18 !== ".." && !relative18.startsWith(`..${path92.sep}`) && !path92.isAbsolute(relative18);
51968
52631
  }
51969
52632
  function sameFileSnapshot4(left, right) {
51970
52633
  return (left.dev !== 0n || left.ino !== 0n) && (right.dev !== 0n || right.ino !== 0n) && left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
@@ -53059,14 +53722,14 @@ import * as path96 from "path";
53059
53722
  // src/services/skill-optimizer/store.ts
53060
53723
  import { createHash as createHash15, randomUUID as randomUUID10 } from "crypto";
53061
53724
  import {
53062
- closeSync as closeSync12,
53725
+ closeSync as closeSync13,
53063
53726
  existsSync as existsSync53,
53064
53727
  fsyncSync as fsyncSync3,
53065
53728
  mkdirSync as mkdirSync27,
53066
- openSync as openSync12,
53729
+ openSync as openSync13,
53067
53730
  readFileSync as readFileSync39,
53068
53731
  renameSync as renameSync18,
53069
- statSync as statSync15,
53732
+ statSync as statSync16,
53070
53733
  unlinkSync as unlinkSync20,
53071
53734
  writeFileSync as writeFileSync17,
53072
53735
  writeFileSync as writeFileSyncAsync
@@ -53132,12 +53795,12 @@ function ensureCandidateDir(directory, skillSlug, candidateId2) {
53132
53795
  return dir;
53133
53796
  }
53134
53797
  function writeFileFsyncedThenRename(tempPath, targetPath, data) {
53135
- const fd = openSync12(tempPath, "w");
53798
+ const fd = openSync13(tempPath, "w");
53136
53799
  try {
53137
53800
  writeFileSync17(fd, data, "utf8");
53138
53801
  fsyncSync3(fd);
53139
53802
  } finally {
53140
- closeSync12(fd);
53803
+ closeSync13(fd);
53141
53804
  }
53142
53805
  try {
53143
53806
  renameSync18(tempPath, targetPath);
@@ -53156,7 +53819,7 @@ var _internals79 = {
53156
53819
  renameSync: renameSync18,
53157
53820
  unlinkSync: unlinkSync20,
53158
53821
  existsSync: existsSync53,
53159
- statSync: statSync15
53822
+ statSync: statSync16
53160
53823
  };
53161
53824
  async function appendEvent(directory, eventInput) {
53162
53825
  if (!isValidSkillSlug(eventInput.skillSlug)) {
@@ -53974,17 +54637,17 @@ async function deterministicSeed(input) {
53974
54637
  }
53975
54638
 
53976
54639
  // src/services/skill-optimizer/smoke.ts
53977
- import { existsSync as existsSync55, lstatSync as lstatSync10, readFileSync as readFileSync41, realpathSync as realpathSync9 } from "fs";
54640
+ import { existsSync as existsSync55, lstatSync as lstatSync10, readFileSync as readFileSync41, realpathSync as realpathSync11 } from "fs";
53978
54641
  import * as path98 from "path";
53979
54642
 
53980
54643
  // src/hooks/spawn-helper.ts
53981
- import * as child_process8 from "child_process";
54644
+ import * as child_process7 from "child_process";
53982
54645
  var WIN32_CMD_BINARIES = new Set(["npm", "npx", "pnpm", "yarn"]);
53983
54646
  var _internals82 = {
53984
- spawn: child_process8.spawn
54647
+ spawn: child_process7.spawn
53985
54648
  };
53986
54649
  function spawnAsync(command, cwd, timeoutMs) {
53987
- return new Promise((resolve33) => {
54650
+ return new Promise((resolve34) => {
53988
54651
  try {
53989
54652
  const [rawCmd, ...args] = command;
53990
54653
  const cmd = process.platform === "win32" && WIN32_CMD_BINARIES.has(rawCmd) && !rawCmd.includes(".") ? `${rawCmd}.cmd` : rawCmd;
@@ -54031,24 +54694,24 @@ function spawnAsync(command, cwd, timeoutMs) {
54031
54694
  try {
54032
54695
  proc.kill();
54033
54696
  } catch {}
54034
- resolve33(null);
54697
+ resolve34(null);
54035
54698
  }, timeoutMs);
54036
54699
  proc.on("close", (code) => {
54037
54700
  if (done)
54038
54701
  return;
54039
54702
  done = true;
54040
54703
  clearTimeout(timer);
54041
- resolve33({ exitCode: code ?? 1, stdout, stderr });
54704
+ resolve34({ exitCode: code ?? 1, stdout, stderr });
54042
54705
  });
54043
54706
  proc.on("error", () => {
54044
54707
  if (done)
54045
54708
  return;
54046
54709
  done = true;
54047
54710
  clearTimeout(timer);
54048
- resolve33(null);
54711
+ resolve34(null);
54049
54712
  });
54050
54713
  } catch {
54051
- resolve33(null);
54714
+ resolve34(null);
54052
54715
  }
54053
54716
  });
54054
54717
  }
@@ -54067,7 +54730,7 @@ var _internals83 = {
54067
54730
  const rel = path98.relative(path98.resolve(root), path98.resolve(target));
54068
54731
  return rel.startsWith("..") || path98.isAbsolute(rel);
54069
54732
  },
54070
- realpath: (p) => realpathSync9(p),
54733
+ realpath: (p) => realpathSync11(p),
54071
54734
  evaluateSkillChange,
54072
54735
  isRejectedSkillContent,
54073
54736
  spawnAsync
@@ -54431,7 +55094,7 @@ async function runValidationWithTransientRetry(input, candidateContent, baseline
54431
55094
  if (!input.dispatcher) {
54432
55095
  throw new Error("no evaluation dispatcher available (cannot run validation)");
54433
55096
  }
54434
- const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-w30gazew.js");
55097
+ const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-mzb3e2s6.js");
54435
55098
  const executor = createModelEvaluationExecutor2(input.dispatcher, input.sessionId);
54436
55099
  const result = await _internals84.evaluateCandidateV1({
54437
55100
  projectRoot: input.directory,
@@ -55304,11 +55967,18 @@ function makeInitialState() {
55304
55967
  };
55305
55968
  }
55306
55969
  var sessionStates = new Map;
55970
+ var MAX_TRACKED_COMPACTION_SESSIONS = 500;
55307
55971
  function getSessionState(sessionId) {
55308
55972
  let state = sessionStates.get(sessionId);
55309
- if (!state) {
55310
- state = makeInitialState();
55311
- sessionStates.set(sessionId, state);
55973
+ if (state)
55974
+ return state;
55975
+ state = makeInitialState();
55976
+ sessionStates.set(sessionId, state);
55977
+ if (sessionStates.size > MAX_TRACKED_COMPACTION_SESSIONS) {
55978
+ const oldest = sessionStates.keys().next().value;
55979
+ if (oldest !== undefined && oldest !== sessionId) {
55980
+ sessionStates.delete(oldest);
55981
+ }
55312
55982
  }
55313
55983
  return state;
55314
55984
  }
@@ -55393,8 +56063,8 @@ async function getStatusData(directory, agents, sessionId) {
55393
56063
  agentCount,
55394
56064
  isLegacy: false,
55395
56065
  turboMode: hasActiveTurboMode(),
55396
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55397
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56066
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56067
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55398
56068
  compactionCount: metrics.compactionCount,
55399
56069
  lastSnapshotAt: metrics.lastSnapshotAt
55400
56070
  };
@@ -55410,8 +56080,8 @@ async function getStatusData(directory, agents, sessionId) {
55410
56080
  agentCount: Object.keys(agents).length,
55411
56081
  isLegacy: true,
55412
56082
  turboMode: hasActiveTurboMode(),
55413
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55414
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56083
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56084
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55415
56085
  compactionCount: metrics.compactionCount,
55416
56086
  lastSnapshotAt: metrics.lastSnapshotAt
55417
56087
  };
@@ -55430,8 +56100,8 @@ async function getStatusData(directory, agents, sessionId) {
55430
56100
  agentCount,
55431
56101
  isLegacy: true,
55432
56102
  turboMode: hasActiveTurboMode(),
55433
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55434
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56103
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56104
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55435
56105
  compactionCount: metrics.compactionCount,
55436
56106
  lastSnapshotAt: metrics.lastSnapshotAt
55437
56107
  };
@@ -56255,7 +56925,7 @@ function buildDetailedHelp(commandName, entry) {
56255
56925
  async function handleHelpCommand(ctx) {
56256
56926
  const targetCommand = ctx.args.join(" ");
56257
56927
  if (!targetCommand) {
56258
- const { buildHelpText } = await import("./index-se8xc78k.js");
56928
+ const { buildHelpText } = await import("./index-5hyq2mw6.js");
56259
56929
  return buildHelpText();
56260
56930
  }
56261
56931
  const tokens = targetCommand.split(/\s+/);
@@ -56264,7 +56934,7 @@ async function handleHelpCommand(ctx) {
56264
56934
  return _internals14.buildDetailedHelp(resolved.key, resolved.entry);
56265
56935
  }
56266
56936
  const similar = _internals14.findSimilarCommands(targetCommand);
56267
- const { buildHelpText: fullHelp } = await import("./index-se8xc78k.js");
56937
+ const { buildHelpText: fullHelp } = await import("./index-5hyq2mw6.js");
56268
56938
  if (similar.length > 0) {
56269
56939
  return `Command '/swarm ${targetCommand}' not found.
56270
56940
 
@@ -56441,7 +57111,7 @@ var COMMAND_REGISTRY = {
56441
57111
  },
56442
57112
  "guardrail explain": {
56443
57113
  handler: async (ctx) => {
56444
- const { handleGuardrailExplain } = await import("./guardrail-explain-k62h3c92.js");
57114
+ const { handleGuardrailExplain } = await import("./guardrail-explain-y2wg00dp.js");
56445
57115
  return handleGuardrailExplain(ctx.directory, ctx.args);
56446
57116
  },
56447
57117
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -56451,7 +57121,7 @@ var COMMAND_REGISTRY = {
56451
57121
  },
56452
57122
  "guardrail-explain": {
56453
57123
  handler: async (ctx) => {
56454
- const { handleGuardrailExplain } = await import("./guardrail-explain-k62h3c92.js");
57124
+ const { handleGuardrailExplain } = await import("./guardrail-explain-y2wg00dp.js");
56455
57125
  return handleGuardrailExplain(ctx.directory, ctx.args);
56456
57126
  },
56457
57127
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -56461,7 +57131,7 @@ var COMMAND_REGISTRY = {
56461
57131
  },
56462
57132
  "guardrail-log": {
56463
57133
  handler: async (ctx) => {
56464
- const { handleGuardrailLog } = await import("./guardrail-log-2ftew0p3.js");
57134
+ const { handleGuardrailLog } = await import("./guardrail-log-320grggj.js");
56465
57135
  return handleGuardrailLog(ctx.directory, ctx.args);
56466
57136
  },
56467
57137
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -56915,7 +57585,7 @@ Subcommands:
56915
57585
  handler: (ctx) => handleApprovePlanCriticCommand(ctx.directory, ctx.args, ctx.sessionID),
56916
57586
  description: "Record a MANUAL plan-critic approval to unblock the critic_pre_plan execution gate [reason...]",
56917
57587
  args: "[reason...]",
56918
- details: 'Human-only escape hatch for the ratchet-tighter critic_pre_plan execution gate (issue #2012). When the critic already returned APPROVED but the mechanical snapshot recorder failed to persist it (verdict-format mismatch, dispatch-signal miss, or a plan.json read race), the gate permanently blocks ALL coder delegations because critic_pre_plan defaults to true and cannot be disabled. Running this records a manual plan_critic_gate approval snapshot so the gate unblocks, with a distinct method: "manual_override" audit marker. The agent itself cannot run this command; it must call the approve_plan_critic tool (or ask you to run this command). Both paths funnel into the same forceRecordPlanCriticApproval hook, which requires an active architect session. An audit event is appended to .swarm/events.jsonl. Prefer re-running MODE: CRITIC-GATE first; use this only as an escape hatch when a legitimate APPROVED was lost.',
57588
+ details: 'Human-only escape hatch for the ratchet-tighter critic_pre_plan execution gate (issue #2012). When the critic already returned APPROVED but the mechanical snapshot recorder failed to persist it (verdict-format mismatch, dispatch-signal miss, or a plan.json read race), an enabled critic_pre_plan gate blocks coder delegation. Running this records a manual plan_critic_gate approval snapshot so the gate unblocks, with a distinct method: "manual_override" audit marker. The agent itself cannot run this command; it must call the approve_plan_critic tool (or ask you to run this command). Both paths funnel into the same forceRecordPlanCriticApproval hook, which requires an active architect session. An audit event is appended to .swarm/events.jsonl. Prefer re-running MODE: CRITIC-GATE first; use this only as an escape hatch when a legitimate APPROVED was lost.',
56919
57589
  category: "utility",
56920
57590
  toolPolicy: "restricted"
56921
57591
  },
@@ -59900,14 +60570,6 @@ function getSwarmAgents(swarmId) {
59900
60570
  const id = swarmId ?? "default";
59901
60571
  return _swarmAgentsMap.get(id);
59902
60572
  }
59903
- var KNOWN_VARIANT_VALUES = new Set([
59904
- "low",
59905
- "medium",
59906
- "high",
59907
- "max",
59908
- "xhigh",
59909
- "thinking"
59910
- ]);
59911
60573
 
59912
60574
  // src/hooks/curator-llm-factory.ts
59913
60575
  init_schema();