opencode-swarm 7.139.1 → 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 (93) hide show
  1. package/.opencode/skills/phase-wrap/SKILL.md +2 -2
  2. package/.opencode/skills/swarm-pr-review/SKILL.md +15 -0
  3. package/dist/background/stage-b-gates.d.ts +5 -1
  4. package/dist/background/task-envelope.d.ts +4 -4
  5. package/dist/cli/{config-doctor-tgnr91q3.js → config-doctor-75qky762.js} +3 -3
  6. package/dist/cli/{core-g2dfh9fq.js → core-nn8ffy5h.js} +3 -3
  7. package/dist/cli/{curation-policy-2qnhgdtt.js → curation-policy-f46tf3dw.js} +5 -5
  8. package/dist/cli/{curator-drift-vfh07mqy.js → curator-drift-r52hjskk.js} +2 -2
  9. package/dist/cli/{curator-llm-factory-1ftzece0.js → curator-llm-factory-1r9vz0g2.js} +28 -28
  10. package/dist/cli/{curator-nbsag633.js → curator-m6mk2v1n.js} +28 -28
  11. package/dist/cli/{dispatch-b1fge4xk.js → dispatch-nbv0hezh.js} +2 -2
  12. package/dist/cli/{evidence-summary-service-kzxr71yd.js → evidence-summary-service-sxcq5nrt.js} +6 -6
  13. package/dist/cli/{gate-evidence-z2b1ymnw.js → gate-evidence-1982qswy.js} +3 -3
  14. package/dist/cli/{guardrail-explain-vb9k9zwm.js → guardrail-explain-y2wg00dp.js} +29 -29
  15. package/dist/cli/{guardrail-log-0djnvfas.js → guardrail-log-320grggj.js} +5 -5
  16. package/dist/cli/{hive-promoter-bpt26yew.js → hive-promoter-v4w7cjvk.js} +28 -28
  17. package/dist/cli/{index-3eddtgxt.js → index-05mms0va.js} +1 -1
  18. package/dist/cli/{index-y1zcsf2a.js → index-0mzz46we.js} +2 -2
  19. package/dist/cli/{index-ywysg26s.js → index-1jbj7vzv.js} +4 -4
  20. package/dist/cli/{index-91s653xx.js → index-1yd38ajy.js} +2 -2
  21. package/dist/cli/{index-gvhvz176.js → index-2eet124q.js} +1 -1
  22. package/dist/cli/{index-0wp67gqj.js → index-4fr94329.js} +11 -6
  23. package/dist/cli/{index-y111zefa.js → index-4nt52yr9.js} +80 -31
  24. package/dist/cli/{index-3jcyn8g6.js → index-4wjk296r.js} +1 -1
  25. package/dist/cli/{index-xka8s8jw.js → index-5hyq2mw6.js} +30 -30
  26. package/dist/cli/{index-5zyk8qgv.js → index-5syvwd8g.js} +1 -1
  27. package/dist/cli/{index-en6qq954.js → index-6axm8q3s.js} +3 -3
  28. package/dist/cli/{index-668dpw5e.js → index-6mvbjqbt.js} +1 -1
  29. package/dist/cli/{index-wsdnttf4.js → index-74hpm6sh.js} +1 -1
  30. package/dist/cli/{index-wrt6m5ze.js → index-ajwmn8gp.js} +289 -73
  31. package/dist/cli/{index-0ymhx85c.js → index-ba5e5e3n.js} +2 -2
  32. package/dist/cli/{index-hn0mdcmj.js → index-bcwx1335.js} +1 -1
  33. package/dist/cli/{index-a12906mj.js → index-gen82t10.js} +3 -3
  34. package/dist/cli/{index-s608d3hw.js → index-hckq2r5m.js} +1 -1
  35. package/dist/cli/{index-aymt7hzw.js → index-hwnpd4tw.js} +3 -3
  36. package/dist/cli/{index-xec724kg.js → index-jqn332jx.js} +2 -2
  37. package/dist/cli/{index-x9q4bbhd.js → index-k87hs2ff.js} +1 -1
  38. package/dist/cli/{index-15526t16.js → index-kyz7259r.js} +2 -2
  39. package/dist/cli/{index-prmb5qfp.js → index-mv0czd9c.js} +2 -2
  40. package/dist/cli/{index-hgrkhxbk.js → index-mzck763x.js} +2 -2
  41. package/dist/cli/{index-xr07r80d.js → index-tkg6rapy.js} +1345 -682
  42. package/dist/cli/{index-ht4q3548.js → index-v7g8dnyh.js} +3 -3
  43. package/dist/cli/{index-vxbppwvf.js → index-xeyr62x0.js} +5 -5
  44. package/dist/cli/{index-vm4xw9z3.js → index-xsxrhakg.js} +1 -1
  45. package/dist/cli/{index-5w7ekpy3.js → index-xzzcfwwm.js} +1 -1
  46. package/dist/cli/{index-dw09fz3w.js → index-yawwwhmc.js} +2 -2
  47. package/dist/cli/{index-q7ezzgwq.js → index-yh0q21nj.js} +1 -1
  48. package/dist/cli/{index-db7wf6ke.js → index-z6qe8wft.js} +6 -6
  49. package/dist/cli/index.js +28 -28
  50. package/dist/cli/{knowledge-escalator-pjg2x75c.js → knowledge-escalator-kkjsjtza.js} +6 -6
  51. package/dist/cli/{knowledge-events-vxheptk8.js → knowledge-events-jbw9j94n.js} +4 -4
  52. package/dist/cli/{knowledge-link-3tq8w6gs.js → knowledge-link-f6akgbdn.js} +3 -3
  53. package/dist/cli/{knowledge-store-36mascwz.js → knowledge-store-m7vnz62z.js} +4 -4
  54. package/dist/cli/{knowledge-validator-anats6rj.js → knowledge-validator-rqj7kech.js} +7 -7
  55. package/dist/cli/{pending-delegations-tmyf18p4.js → pending-delegations-14544jpr.js} +3 -3
  56. package/dist/cli/{pr-subscriptions-hhx6yk24.js → pr-subscriptions-qdwfm1a8.js} +3 -3
  57. package/dist/cli/{runner-w30gazew.js → runner-mzb3e2s6.js} +4 -4
  58. package/dist/cli/{scan-cursor-apbz9eez.js → scan-cursor-8e6ryhjq.js} +5 -5
  59. package/dist/cli/{schema-x9fr2ey8.js → schema-g7rs20c6.js} +2 -2
  60. package/dist/cli/{scope-persistence-3f0d4fwv.js → scope-persistence-wy65zyha.js} +2 -2
  61. package/dist/cli/{skill-generator-20czn4kd.js → skill-generator-2rv6ftre.js} +8 -8
  62. package/dist/cli/{task-envelope-qn0qtnh0.js → task-envelope-jp2fdsvn.js} +11 -8
  63. package/dist/cli/{workspace-snapshot-h5rzw37b.js → workspace-snapshot-w3tg4ne5.js} +2 -2
  64. package/dist/cli/{worktree-collision-ownership-vexb0b6r.js → worktree-collision-ownership-24s0t62z.js} +4 -4
  65. package/dist/commands/approve-plan-critic.d.ts +2 -2
  66. package/dist/commands/registry.d.ts +1 -1
  67. package/dist/config/agent-model.d.ts +35 -0
  68. package/dist/db/qa-gate-profile.d.ts +11 -3
  69. package/dist/evidence/manager.d.ts +1 -1
  70. package/dist/evidence/phase-participation.d.ts +44 -0
  71. package/dist/hooks/compaction-customizer.d.ts +30 -2
  72. package/dist/hooks/context-budget.d.ts +1 -1
  73. package/dist/hooks/delegation-gate.d.ts +3 -3
  74. package/dist/hooks/guardrails/index.d.ts +5 -1
  75. package/dist/hooks/guardrails/messages-transform.d.ts +2 -0
  76. package/dist/hooks/guardrails/pre-check-result.d.ts +20 -0
  77. package/dist/hooks/pr-workflow-gate.d.ts +8 -1
  78. package/dist/index.js +421 -416
  79. package/dist/sast/semgrep.d.ts +15 -32
  80. package/dist/services/compaction-service.d.ts +3 -1
  81. package/dist/session/snapshot-writer.d.ts +1 -0
  82. package/dist/state.d.ts +70 -19
  83. package/dist/tools/context-status.d.ts +4 -1
  84. package/dist/tools/lint.d.ts +48 -22
  85. package/dist/tools/pre-check-batch.d.ts +37 -13
  86. package/dist/tools/quality-budget.d.ts +3 -2
  87. package/dist/tools/sast-baseline.d.ts +1 -0
  88. package/dist/tools/sast-scan.d.ts +4 -1
  89. package/dist/tools/secretscan.d.ts +2 -2
  90. package/dist/tools/set-qa-gates.d.ts +5 -4
  91. package/dist/utils/bun-compat.d.ts +20 -0
  92. package/dist/utils/external-tool-runner.d.ts +10 -0
  93. 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-y1zcsf2a.js";
19
+ } from "./index-0mzz46we.js";
20
20
  import {
21
21
  detectStraySwarmDirs,
22
22
  readDoctorArtifact,
23
23
  removeStraySwarmDir,
24
24
  runConfigDoctor
25
- } from "./index-91s653xx.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-en6qq954.js";
119
+ } from "./index-6axm8q3s.js";
120
120
  import {
121
121
  readRecentEscalations
122
- } from "./index-vxbppwvf.js";
122
+ } from "./index-xeyr62x0.js";
123
123
  import {
124
124
  alreadyCuratedThisGeneration,
125
125
  claimNextScanBatch,
126
126
  resolveCohortId
127
- } from "./index-0ymhx85c.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-ywysg26s.js";
180
+ } from "./index-1jbj7vzv.js";
181
181
  import {
182
182
  authorizeCuration,
183
183
  buildConfigFingerprintInput
184
- } from "./index-xec724kg.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-db7wf6ke.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-aymt7hzw.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-dw09fz3w.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-15526t16.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();
@@ -2199,6 +2213,7 @@ var PrWorkflowGateStateSchema = exports_external.object({
2199
2213
  prReviewBaseDispatch: PrReviewBaseDispatchRecordSchema.optional(),
2200
2214
  prReviewTriggerLedger: exports_external.array(PrReviewInlineTriggerRowSchema).length(PR_REVIEW_REQUIRED_TRIGGER_IDS.length).optional(),
2201
2215
  prReviewTriggerEvalPath: exports_external.string().min(1).optional(),
2216
+ prReviewTriggerEvalRunId: exports_external.string().min(1).optional(),
2202
2217
  prReviewValidationBatches: exports_external.array(PrReviewValidationBatchRecordSchema).max(MAX_WORKFLOW_BATCHES).optional(),
2203
2218
  prReviewBatchCoherence: exports_external.record(exports_external.string().min(1), PrReviewBatchCoherenceRecordSchema).refine((entries) => Object.keys(entries).length <= MAX_WORKFLOW_BATCHES, `prReviewBatchCoherence may not exceed ${MAX_WORKFLOW_BATCHES} entries`).optional(),
2204
2219
  prReviewRetiredReviewerSessionIds: exports_external.array(exports_external.string().min(1)).max(MAX_RETIRED_REVIEWER_SESSION_IDS).optional(),
@@ -4899,71 +4914,6 @@ import * as path20 from "path";
4899
4914
  init_agent_names();
4900
4915
  init_schema();
4901
4916
 
4902
- // src/turbo/epic/cochange-conflict.ts
4903
- function pathMatches(scopePath, cochangePath) {
4904
- if (scopePath === cochangePath)
4905
- return true;
4906
- return scopePath.endsWith(`/${cochangePath}`);
4907
- }
4908
- function epicPairConflict(scopeA, scopeB, cochangePairs, threshold) {
4909
- const normA = scopeA.map(normalizePath);
4910
- const normB = scopeB.map(normalizePath);
4911
- const pathPairs = [];
4912
- for (const a of normA) {
4913
- for (const b of normB) {
4914
- if (pathsConflict(a, b)) {
4915
- pathPairs.push([a, b]);
4916
- }
4917
- }
4918
- }
4919
- const pathConflict = pathPairs.length > 0;
4920
- const activePairs = cochangePairs.filter((e) => e.coChangeCount >= threshold.minCoChanges && e.npmi >= threshold.npmi);
4921
- const cochangeMatches = [];
4922
- for (const entry of activePairs) {
4923
- const fa = entry.fileA;
4924
- const fb = entry.fileB;
4925
- const aTouchesFa = normA.some((p) => pathMatches(p, fa));
4926
- const aTouchesFb = normA.some((p) => pathMatches(p, fb));
4927
- const bTouchesFa = normB.some((p) => pathMatches(p, fa));
4928
- const bTouchesFb = normB.some((p) => pathMatches(p, fb));
4929
- const aExclusivelyFa = aTouchesFa && !aTouchesFb;
4930
- const aExclusivelyFb = aTouchesFb && !aTouchesFa;
4931
- const bExclusivelyFa = bTouchesFa && !bTouchesFb;
4932
- const bExclusivelyFb = bTouchesFb && !bTouchesFa;
4933
- const crossCouple = aExclusivelyFa && bExclusivelyFb || aExclusivelyFb && bExclusivelyFa;
4934
- if (crossCouple) {
4935
- cochangeMatches.push({
4936
- a: fa,
4937
- b: fb,
4938
- npmi: entry.npmi,
4939
- coChangeCount: entry.coChangeCount
4940
- });
4941
- }
4942
- }
4943
- const cochangeConflict = cochangeMatches.length > 0;
4944
- let reason;
4945
- if (pathConflict && cochangeConflict)
4946
- reason = "both";
4947
- else if (pathConflict)
4948
- reason = "path";
4949
- else if (cochangeConflict)
4950
- reason = "cochange";
4951
- else
4952
- reason = "none";
4953
- return {
4954
- conflict: pathConflict || cochangeConflict,
4955
- reason,
4956
- evidence: { pathPairs, cochangePairs: cochangeMatches }
4957
- };
4958
- }
4959
-
4960
- // src/state.ts
4961
- init_constants();
4962
- import { randomUUID as randomUUID2 } from "crypto";
4963
- import * as fs2 from "fs/promises";
4964
- import * as path7 from "path";
4965
- init_schema();
4966
-
4967
4917
  // src/db/qa-gate-profile.ts
4968
4918
  import { createHash as createHash3 } from "crypto";
4969
4919
 
@@ -5066,7 +5016,8 @@ var _internals5 = {
5066
5016
  setGates,
5067
5017
  getEffectiveGates,
5068
5018
  computeProfileHash,
5069
- hasAnyProfileWithEnabledGate
5019
+ hasAnyProfileWithEnabledGate,
5020
+ afterSetGatesRead: () => {}
5070
5021
  };
5071
5022
  var DEFAULT_QA_GATES = {
5072
5023
  reviewer: true,
@@ -5081,6 +5032,7 @@ var DEFAULT_QA_GATES = {
5081
5032
  drift_check: true,
5082
5033
  final_council: false
5083
5034
  };
5035
+ var qaGateProfileSavepointCounter = 0;
5084
5036
  function rowToProfile(row) {
5085
5037
  let parsed = {};
5086
5038
  try {
@@ -5099,7 +5051,7 @@ function rowToProfile(row) {
5099
5051
  const knownKeys = new Set(Object.keys(DEFAULT_QA_GATES));
5100
5052
  const filteredParsed = {};
5101
5053
  for (const key of Object.keys(parsed)) {
5102
- if (knownKeys.has(key)) {
5054
+ if (knownKeys.has(key) && typeof parsed[key] === "boolean") {
5103
5055
  filteredParsed[key] = parsed[key];
5104
5056
  }
5105
5057
  }
@@ -5135,12 +5087,18 @@ function hasAnyProfileWithEnabledGate(directory, gate) {
5135
5087
  }
5136
5088
  return false;
5137
5089
  }
5138
- function getOrCreateProfile(directory, planId, projectType) {
5090
+ function getOrCreateProfile(directory, planId, projectType, initialGates = {}) {
5139
5091
  const existing = _internals5.getProfile(directory, planId);
5140
5092
  if (existing)
5141
5093
  return existing;
5142
5094
  const db = getProjectDb(directory);
5143
- 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);
5144
5102
  const insert = db.transaction(() => {
5145
5103
  db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
5146
5104
  });
@@ -5159,35 +5117,51 @@ function getOrCreateProfile(directory, planId, projectType) {
5159
5117
  return after;
5160
5118
  }
5161
5119
  function setGates(directory, planId, gates) {
5162
- const current = _internals5.getProfile(directory, planId);
5163
- if (!current) {
5164
- throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
5165
- }
5166
- if (current.locked_at !== null) {
5167
- throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
5168
- }
5169
- const merged = { ...current.gates };
5170
- for (const key of Object.keys(gates)) {
5171
- const incoming = gates[key];
5172
- if (incoming === undefined)
5173
- continue;
5174
- if (incoming === false && current.gates[key] === true) {
5175
- 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
+ }
5176
5145
  }
5177
- if (incoming === true) {
5178
- 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}`);
5179
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;
5180
5164
  }
5181
- const db = getProjectDb(directory);
5182
- db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
5183
- JSON.stringify(merged),
5184
- planId
5185
- ]);
5186
- const updated = _internals5.getProfile(directory, planId);
5187
- if (!updated) {
5188
- throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
5189
- }
5190
- return updated;
5191
5165
  }
5192
5166
  function computeProfileHash(profile) {
5193
5167
  const payload = JSON.stringify({
@@ -5206,6 +5180,71 @@ function getEffectiveGates(profile, sessionOverrides) {
5206
5180
  return merged;
5207
5181
  }
5208
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
+
5209
5248
  // src/hooks/realtime-learning-nudge.ts
5210
5249
  var realtimeLearningNudgeBySession = new Map;
5211
5250
  function resetRealtimeLearningNudgeState() {
@@ -5685,8 +5724,7 @@ var swarmState = {
5685
5724
  knowledgeAckDedup: new Set,
5686
5725
  gateDenialCounts: new Map,
5687
5726
  generatedAgentNames: [],
5688
- lastBudgetPct: 0,
5689
- lastBudgetTokens: 0,
5727
+ lastBudgetBySession: new Map,
5690
5728
  liveContextWindows: new Map,
5691
5729
  agentSessions: defaultRunContext.agentSessions,
5692
5730
  pendingRehydrations: new Set,
@@ -5699,8 +5737,7 @@ function resetSwarmState() {
5699
5737
  swarmState.activeAgent.clear();
5700
5738
  swarmState.delegationChains.clear();
5701
5739
  swarmState.pendingEvents = 0;
5702
- swarmState.lastBudgetPct = 0;
5703
- swarmState.lastBudgetTokens = 0;
5740
+ swarmState.lastBudgetBySession.clear();
5704
5741
  swarmState.liveContextWindows.clear();
5705
5742
  swarmState.agentSessions.clear();
5706
5743
  _lastIdleSweepAtMs = 0;
@@ -6529,10 +6566,19 @@ function hasActiveEpicMode(sessionID) {
6529
6566
  }
6530
6567
  return false;
6531
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
+ }
6532
6578
  async function rehydratePrSubscriptions(sessionID, directory) {
6533
6579
  const map = new Map;
6534
6580
  try {
6535
- const { listActive: listActive2 } = await import("./pr-subscriptions-hhx6yk24.js");
6581
+ const { listActive: listActive2 } = await import("./pr-subscriptions-qdwfm1a8.js");
6536
6582
  const records = await listActive2(directory);
6537
6583
  for (const record of records) {
6538
6584
  if (record.sessionID !== sessionID)
@@ -18988,7 +19034,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
18988
19034
  };
18989
19035
  }
18990
19036
  async function sweepActiveNearDuplicates(directory, options = {}) {
18991
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-ht4q3548.js");
19037
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
18992
19038
  const { config } = loadPluginConfigWithMeta2(directory);
18993
19039
  const sweepConfig = config.learning?.dedup_sweep;
18994
19040
  if (sweepConfig?.enabled === false)
@@ -20583,7 +20629,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
20583
20629
  }
20584
20630
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
20585
20631
  try {
20586
- const skillModule = await import("./skill-generator-20czn4kd.js");
20632
+ const skillModule = await import("./skill-generator-2rv6ftre.js");
20587
20633
  for (const cand of skillCandidates) {
20588
20634
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
20589
20635
  continue;
@@ -21043,7 +21089,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
21043
21089
  } catch {}
21044
21090
  }
21045
21091
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
21046
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-pjg2x75c.js");
21092
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-kkjsjtza.js");
21047
21093
  for (const { id } of contradictedEntries) {
21048
21094
  try {
21049
21095
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -21862,7 +21908,7 @@ async function runCuratorPostMortem(directory, options = {}) {
21862
21908
  warnings.push("Failed to collect knowledge summary.");
21863
21909
  }
21864
21910
  try {
21865
- const { getScanStatus } = await import("./scan-cursor-apbz9eez.js");
21911
+ const { getScanStatus } = await import("./scan-cursor-8e6ryhjq.js");
21866
21912
  const scanStatus = await getScanStatus(directory);
21867
21913
  if (scanStatus.remaining_estimate > 0) {
21868
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.`);
@@ -22034,9 +22080,9 @@ var _internals24 = {
22034
22080
  verifyPostMortemKnowledgeActions,
22035
22081
  repairPostMortemActions,
22036
22082
  loadDefaultKnowledgeConfig: async (directory) => {
22037
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-x9fr2ey8.js");
22083
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
22038
22084
  try {
22039
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-ht4q3548.js");
22085
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
22040
22086
  const loaded = loadPluginConfigWithMeta2(directory);
22041
22087
  return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
22042
22088
  } catch {
@@ -22044,11 +22090,11 @@ var _internals24 = {
22044
22090
  }
22045
22091
  },
22046
22092
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
22047
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-nbsag633.js");
22093
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-m6mk2v1n.js");
22048
22094
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
22049
22095
  },
22050
22096
  checkHivePromotions: async (entries, knowledgeConfig, directory) => {
22051
- const { checkHivePromotions } = await import("./hive-promoter-bpt26yew.js");
22097
+ const { checkHivePromotions } = await import("./hive-promoter-v4w7cjvk.js");
22052
22098
  return checkHivePromotions(entries, knowledgeConfig, directory);
22053
22099
  },
22054
22100
  applyProposalTriage: async (directory, triage) => {
@@ -22057,7 +22103,7 @@ var _internals24 = {
22057
22103
  activateProposal,
22058
22104
  listSkills: listSkills2,
22059
22105
  sanitizeSlug
22060
- } = await import("./skill-generator-20czn4kd.js");
22106
+ } = await import("./skill-generator-2rv6ftre.js");
22061
22107
  const result = {
22062
22108
  approved: [],
22063
22109
  rejected: [],
@@ -22099,7 +22145,7 @@ var _internals24 = {
22099
22145
  return result;
22100
22146
  },
22101
22147
  readPriorDriftReports: async (directory) => {
22102
- const { readPriorDriftReports } = await import("./curator-drift-vfh07mqy.js");
22148
+ const { readPriorDriftReports } = await import("./curator-drift-r52hjskk.js");
22103
22149
  return readPriorDriftReports(directory);
22104
22150
  },
22105
22151
  readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
@@ -24685,9 +24731,9 @@ function hashContent(content) {
24685
24731
  return createHash9("sha1").update(content).digest("hex");
24686
24732
  }
24687
24733
  async function loadConfigForPolicyCurator(directory) {
24688
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-x9fr2ey8.js");
24734
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
24689
24735
  try {
24690
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-ht4q3548.js");
24736
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
24691
24737
  const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
24692
24738
  return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
24693
24739
  } catch {
@@ -28696,7 +28742,7 @@ async function runFinalizeStage(ctx) {
28696
28742
  }
28697
28743
  }
28698
28744
  try {
28699
- const { CuratorConfigSchema: CCS } = await import("./schema-x9fr2ey8.js");
28745
+ const { CuratorConfigSchema: CCS } = await import("./schema-g7rs20c6.js");
28700
28746
  const { config: pmLoadedConfig } = _internals37.loadPluginConfigWithMeta(ctx.directory);
28701
28747
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
28702
28748
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -30969,9 +31015,9 @@ var _internals42 = {
30969
31015
  loadPluginConfigWithMeta,
30970
31016
  loadCuratorDeps: async () => {
30971
31017
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
30972
- import("./schema-x9fr2ey8.js"),
30973
- import("./curator-nbsag633.js"),
30974
- import("./curator-llm-factory-1ftzece0.js")
31018
+ import("./schema-g7rs20c6.js"),
31019
+ import("./curator-m6mk2v1n.js"),
31020
+ import("./curator-llm-factory-1r9vz0g2.js")
30975
31021
  ]);
30976
31022
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
30977
31023
  }
@@ -31876,7 +31922,7 @@ async function computeCurationDiagnostics(directory, entries) {
31876
31922
  let scanCompleted = false;
31877
31923
  let scanRemaining = 0;
31878
31924
  try {
31879
- const { getScanStatus } = await import("./scan-cursor-apbz9eez.js");
31925
+ const { getScanStatus } = await import("./scan-cursor-8e6ryhjq.js");
31880
31926
  const status = await getScanStatus(directory);
31881
31927
  scanGeneration = status.generation;
31882
31928
  scanCompleted = status.completed;
@@ -31884,19 +31930,19 @@ async function computeCurationDiagnostics(directory, entries) {
31884
31930
  } catch {}
31885
31931
  let configMatch = null;
31886
31932
  try {
31887
- const { isLinked: isLinked2 } = await import("./knowledge-link-3tq8w6gs.js");
31933
+ const { isLinked: isLinked2 } = await import("./knowledge-link-f6akgbdn.js");
31888
31934
  if (isLinked2(directory)) {
31889
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-x9fr2ey8.js");
31935
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-g7rs20c6.js");
31890
31936
  let config;
31891
31937
  try {
31892
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-ht4q3548.js");
31938
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-v7g8dnyh.js");
31893
31939
  const loaded = loadPluginConfigWithMeta2(directory);
31894
31940
  config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
31895
31941
  } catch {
31896
31942
  config = KnowledgeConfigSchema2.parse({});
31897
31943
  }
31898
31944
  const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
31899
- const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-2qnhgdtt.js");
31945
+ const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-f46tf3dw.js");
31900
31946
  const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
31901
31947
  const cohortFp = await readCohortConfigFingerprint(directory);
31902
31948
  configMatch = cohortFp === null ? null : currentFp === cohortFp;
@@ -33107,7 +33153,7 @@ async function handleDoctorCommand(directory, args) {
33107
33153
  const result = runConfigDoctor(config, directory);
33108
33154
  let output;
33109
33155
  if (enableAutoFix && result.hasAutoFixableIssues) {
33110
- const { runConfigDoctorWithFixes } = await import("./config-doctor-tgnr91q3.js");
33156
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-75qky762.js");
33111
33157
  const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
33112
33158
  applyLossy: true
33113
33159
  });
@@ -33944,7 +33990,7 @@ async function handleEvidenceCommand(directory, args) {
33944
33990
  return formatTaskEvidenceMarkdown(evidenceData);
33945
33991
  }
33946
33992
  async function handleEvidenceSummaryCommand(directory) {
33947
- const { buildEvidenceSummary } = await import("./evidence-summary-service-kzxr71yd.js");
33993
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-sxcq5nrt.js");
33948
33994
  const artifact = await buildEvidenceSummary(directory);
33949
33995
  if (!artifact) {
33950
33996
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -35620,7 +35666,8 @@ function validateInput(input) {
35620
35666
  }
35621
35667
  return { valid: true };
35622
35668
  }
35623
- async function qualityBudget(input, directory) {
35669
+ async function qualityBudget(input, directory, abortSignal) {
35670
+ abortSignal?.throwIfAborted();
35624
35671
  const validation = validateInput(input);
35625
35672
  if (!validation.valid) {
35626
35673
  throw new Error(`Invalid input: ${validation.error}`);
@@ -35660,10 +35707,12 @@ async function qualityBudget(input, directory) {
35660
35707
  }
35661
35708
  };
35662
35709
  }
35663
- const metrics = await computeQualityMetrics(changedFiles, thresholds, directory);
35710
+ const metrics = await _internals49.computeQualityMetrics(changedFiles, thresholds, directory);
35711
+ abortSignal?.throwIfAborted();
35664
35712
  const errorsCount = metrics.violations.filter((v) => v.severity === "error").length;
35665
35713
  const warningsCount = metrics.violations.filter((v) => v.severity === "warning").length;
35666
35714
  const verdict = errorsCount > 0 ? "fail" : "pass";
35715
+ abortSignal?.throwIfAborted();
35667
35716
  await saveEvidence(directory, "quality_budget", {
35668
35717
  task_id: "quality_budget",
35669
35718
  type: "quality_budget",
@@ -35690,7 +35739,8 @@ async function qualityBudget(input, directory) {
35690
35739
  files: v.files
35691
35740
  })),
35692
35741
  files_analyzed: metrics.files_analyzed
35693
- });
35742
+ }, abortSignal);
35743
+ abortSignal?.throwIfAborted();
35694
35744
  return {
35695
35745
  verdict,
35696
35746
  metrics,
@@ -35717,13 +35767,14 @@ var quality_budget = createSwarmTool({
35717
35767
  exclude_globs: exports_external.array(exports_external.string()).optional()
35718
35768
  }).optional().describe("Quality budget thresholds")
35719
35769
  },
35720
- async execute(args, directory) {
35721
- const result = await _internals49.qualityBudget(args, directory);
35770
+ async execute(args, directory, ctx) {
35771
+ const result = await _internals49.qualityBudget(args, directory, ctx?.abort);
35722
35772
  return JSON.stringify(result);
35723
35773
  }
35724
35774
  });
35725
35775
  var _internals49 = {
35726
- qualityBudget
35776
+ qualityBudget,
35777
+ computeQualityMetrics
35727
35778
  };
35728
35779
 
35729
35780
  // src/tools/sast-scan.ts
@@ -36582,15 +36633,75 @@ function executeRulesSync(filePath, content, language) {
36582
36633
  }
36583
36634
 
36584
36635
  // src/sast/semgrep.ts
36585
- init_logger();
36586
- import * as child_process6 from "child_process";
36587
36636
  import * as fs24 from "fs";
36588
36637
  import * as path61 from "path";
36589
36638
  var semgrepAvailableCache = null;
36639
+ var semgrepAvailabilityProbe = null;
36640
+ var semgrepAvailabilityGeneration = 0;
36590
36641
  var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
36591
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;
36592
36646
  var MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
36593
- 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
+ }
36594
36705
  var _internals50 = {
36595
36706
  isSemgrepAvailable,
36596
36707
  checkSemgrepAvailable,
@@ -36598,60 +36709,117 @@ var _internals50 = {
36598
36709
  runSemgrep,
36599
36710
  getRulesDirectory,
36600
36711
  hasBundledRules,
36601
- executeWithTimeout
36712
+ resolveExecutableFromPath,
36713
+ resolveSemgrepBinary,
36714
+ runExternalTool,
36715
+ getAvailabilityProbeCwd
36602
36716
  };
36603
36717
  function isSemgrepAvailable() {
36604
36718
  if (semgrepAvailableCache !== null) {
36605
36719
  return semgrepAvailableCache;
36606
36720
  }
36607
- try {
36608
- child_process6.execFileSync("semgrep", ["--version"], {
36609
- encoding: "utf-8",
36610
- stdio: "pipe"
36611
- });
36612
- semgrepAvailableCache = true;
36613
- return true;
36614
- } 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) {
36615
36731
  semgrepAvailableCache = false;
36616
36732
  return false;
36617
36733
  }
36618
- }
36619
- async function checkSemgrepAvailable() {
36620
- 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);
36621
36749
  }
36622
36750
  function resetSemgrepCache() {
36751
+ semgrepAvailabilityGeneration++;
36623
36752
  semgrepAvailableCache = null;
36753
+ semgrepAvailabilityProbe = null;
36624
36754
  }
36625
36755
  function parseSemgrepResults(semgrepOutput) {
36626
36756
  const findings = [];
36627
36757
  try {
36628
36758
  const parsed = JSON.parse(semgrepOutput);
36629
- const results = parsed.results || parsed;
36630
- if (!Array.isArray(results)) {
36631
- 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;
36632
36771
  }
36772
+ if (!Array.isArray(results))
36773
+ throw new Error("invalid Semgrep result shape");
36633
36774
  for (const result of results) {
36634
- if (!result || typeof result !== "object")
36635
- continue;
36636
- 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);
36637
36800
  findings.push({
36638
- rule_id: result.check_id || result.rule_id || "unknown",
36801
+ rule_id: ruleId,
36639
36802
  severity,
36640
- message: result.extra?.message || result.message || "Security issue detected",
36803
+ message: message ?? "Security issue detected",
36641
36804
  location: {
36642
- file: result.path || result.start?.filename || result.file || "unknown",
36643
- line: result.start?.line || result.line || 1,
36644
- column: result.start?.col || result.column
36805
+ file,
36806
+ line,
36807
+ column
36645
36808
  },
36646
- remediation: result.extra?.fix,
36647
- excerpt: result.extra?.lines || result.lines || ""
36809
+ remediation: extra?.fix,
36810
+ excerpt: extra?.lines ?? entry.lines ?? ""
36648
36811
  });
36649
36812
  }
36650
- } catch {
36651
- return [];
36813
+ } catch (error2) {
36814
+ if (error2 instanceof SemgrepScanError)
36815
+ throw error2;
36816
+ throw new Error("Semgrep returned invalid JSON output", { cause: error2 });
36652
36817
  }
36653
36818
  return findings;
36654
36819
  }
36820
+
36821
+ class SemgrepScanError extends Error {
36822
+ }
36655
36823
  function mapSemgrepSeverity(severity) {
36656
36824
  const severityLower = (severity || "").toLowerCase();
36657
36825
  switch (severityLower) {
@@ -36668,136 +36836,6 @@ function mapSemgrepSeverity(severity) {
36668
36836
  return "medium";
36669
36837
  }
36670
36838
  }
36671
- async function executeWithTimeout(command, args, options) {
36672
- const maxOutputBytes = options.maxOutputBytes ?? MAX_OUTPUT_BYTES;
36673
- return new Promise((resolve18) => {
36674
- const child = child_process6.spawn(command, args, {
36675
- shell: false,
36676
- cwd: options.cwd,
36677
- stdio: ["ignore", "pipe", "pipe"]
36678
- });
36679
- options.onSpawn?.(child.pid);
36680
- let stdout = "";
36681
- let stderr = "";
36682
- let stdoutBytes = 0;
36683
- let stderrBytes = 0;
36684
- let stdoutTruncated = false;
36685
- let stderrTruncated = false;
36686
- let settled = false;
36687
- let timeout;
36688
- let escalation;
36689
- const settle = (result) => {
36690
- if (settled)
36691
- return;
36692
- settled = true;
36693
- if (timeout)
36694
- clearTimeout(timeout);
36695
- if (escalation)
36696
- clearTimeout(escalation);
36697
- const truncated = stdoutTruncated || stderrTruncated;
36698
- if (child.exitCode === null && child.signalCode === null) {
36699
- try {
36700
- child.kill("SIGTERM");
36701
- } catch (e) {
36702
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36703
- log("[semgrep] child.kill failed:", e.message);
36704
- }
36705
- }
36706
- escalation = setTimeout(() => {
36707
- try {
36708
- child.kill("SIGKILL");
36709
- } catch (e) {
36710
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36711
- log("[semgrep] child.kill failed:", e.message);
36712
- }
36713
- }
36714
- }, KILL_GRACE_MS);
36715
- if (typeof escalation.unref === "function") {
36716
- escalation.unref();
36717
- }
36718
- }
36719
- resolve18({ ...result, truncated });
36720
- };
36721
- timeout = setTimeout(() => {
36722
- settle({
36723
- stdout,
36724
- stderr: "Process timed out",
36725
- exitCode: 124
36726
- });
36727
- }, options.timeoutMs);
36728
- if (typeof timeout.unref === "function") {
36729
- timeout.unref();
36730
- }
36731
- child.stdout?.on("data", (data) => {
36732
- if (stdoutTruncated)
36733
- return;
36734
- const chunk = data.toString();
36735
- const chunkBytes = Buffer.byteLength(chunk, "utf8");
36736
- if (stdoutBytes + chunkBytes > maxOutputBytes) {
36737
- const remaining = Math.max(0, maxOutputBytes - stdoutBytes);
36738
- stdout += Buffer.from(chunk, "utf8").subarray(0, remaining).toString("utf8");
36739
- stdoutBytes = maxOutputBytes;
36740
- stdoutTruncated = true;
36741
- settle({
36742
- stdout,
36743
- stderr,
36744
- exitCode: -1
36745
- });
36746
- try {
36747
- child.kill("SIGTERM");
36748
- } catch (e) {
36749
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36750
- log("[semgrep] child.kill failed:", e.message);
36751
- }
36752
- }
36753
- } else {
36754
- stdout += chunk;
36755
- stdoutBytes += chunkBytes;
36756
- }
36757
- });
36758
- child.stderr?.on("data", (data) => {
36759
- if (stderrTruncated)
36760
- return;
36761
- const chunk = data.toString();
36762
- const chunkBytes = Buffer.byteLength(chunk, "utf8");
36763
- if (stderrBytes + chunkBytes > maxOutputBytes) {
36764
- const remaining = Math.max(0, maxOutputBytes - stderrBytes);
36765
- stderr += Buffer.from(chunk, "utf8").subarray(0, remaining).toString("utf8");
36766
- stderrBytes = maxOutputBytes;
36767
- stderrTruncated = true;
36768
- settle({
36769
- stdout,
36770
- stderr,
36771
- exitCode: -1
36772
- });
36773
- try {
36774
- child.kill("SIGTERM");
36775
- } catch (e) {
36776
- if (e instanceof Error && !e.message.includes("ESRCH")) {
36777
- log("[semgrep] child.kill failed:", e.message);
36778
- }
36779
- }
36780
- } else {
36781
- stderr += chunk;
36782
- stderrBytes += chunkBytes;
36783
- }
36784
- });
36785
- child.on("close", (code) => {
36786
- settle({
36787
- stdout,
36788
- stderr,
36789
- exitCode: code ?? 0
36790
- });
36791
- });
36792
- child.on("error", (err) => {
36793
- settle({
36794
- stdout,
36795
- stderr: err.message,
36796
- exitCode: 1
36797
- });
36798
- });
36799
- });
36800
- }
36801
36839
  async function runSemgrep(options) {
36802
36840
  const files = options.files || [];
36803
36841
  const rulesDir = options.rulesDir || DEFAULT_RULES_DIR;
@@ -36809,7 +36847,24 @@ async function runSemgrep(options) {
36809
36847
  engine: "tier_a"
36810
36848
  };
36811
36849
  }
36812
- 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) {
36813
36868
  return {
36814
36869
  available: false,
36815
36870
  findings: [],
@@ -36827,11 +36882,16 @@ async function runSemgrep(options) {
36827
36882
  }
36828
36883
  args.push(...files);
36829
36884
  try {
36830
- const result = await executeWithTimeout("semgrep", args, {
36885
+ const result = await _internals50.runExternalTool({
36886
+ executable,
36887
+ args,
36888
+ cwd: options.cwd ?? _internals50.getAvailabilityProbeCwd(),
36831
36889
  timeoutMs,
36832
- cwd: options.cwd
36890
+ maxStdoutBytes: MAX_OUTPUT_BYTES,
36891
+ maxStderrBytes: MAX_OUTPUT_BYTES,
36892
+ abortSignal: options.abortSignal
36833
36893
  });
36834
- if (result.truncated) {
36894
+ if (result.stdoutTruncated || result.stderrTruncated) {
36835
36895
  return {
36836
36896
  available: true,
36837
36897
  findings: [],
@@ -36839,6 +36899,14 @@ async function runSemgrep(options) {
36839
36899
  engine: "tier_a"
36840
36900
  };
36841
36901
  }
36902
+ if (result.status !== "completed") {
36903
+ return {
36904
+ available: true,
36905
+ findings: [],
36906
+ error: describeExternalToolFailure(result.status),
36907
+ engine: "tier_a"
36908
+ };
36909
+ }
36842
36910
  if (result.exitCode !== 0) {
36843
36911
  if (result.exitCode === 1 && result.stdout) {
36844
36912
  const findings2 = parseSemgrepResults(result.stdout);
@@ -36851,7 +36919,7 @@ async function runSemgrep(options) {
36851
36919
  return {
36852
36920
  available: true,
36853
36921
  findings: [],
36854
- error: result.stderr || `Semgrep exited with code ${result.exitCode}`,
36922
+ error: `Semgrep exited with code ${result.exitCode}`,
36855
36923
  engine: "tier_a"
36856
36924
  };
36857
36925
  }
@@ -36862,11 +36930,11 @@ async function runSemgrep(options) {
36862
36930
  engine: "tier_a+tier_b"
36863
36931
  };
36864
36932
  } catch (error2) {
36865
- 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";
36866
36934
  return {
36867
36935
  available: true,
36868
36936
  findings: [],
36869
- error: errorMessage,
36937
+ error: safeError,
36870
36938
  engine: "tier_a"
36871
36939
  };
36872
36940
  }
@@ -36980,8 +37048,30 @@ function assignOccurrenceIndices(findings, directory) {
36980
37048
  };
36981
37049
  });
36982
37050
  }
36983
- 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) {
36984
37072
  for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
37073
+ if (abortSignal?.aborted)
37074
+ return null;
36985
37075
  try {
36986
37076
  const fd = fs25.openSync(lockPath, "wx");
36987
37077
  fs25.closeSync(fd);
@@ -36993,7 +37083,9 @@ async function acquireLock2(lockPath) {
36993
37083
  };
36994
37084
  } catch {
36995
37085
  if (attempt < LOCK_RETRY_DELAYS_MS.length) {
36996
- 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
+ }
36997
37089
  }
36998
37090
  }
36999
37091
  }
@@ -37028,9 +37120,19 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37028
37120
  message: e instanceof Error ? e.message : "Path validation failed"
37029
37121
  };
37030
37122
  }
37123
+ if (opts?.abortSignal?.aborted) {
37124
+ return { status: "error", message: "SAST baseline capture cancelled" };
37125
+ }
37031
37126
  fs25.mkdirSync(path62.dirname(baselinePath), { recursive: true });
37032
37127
  fs25.mkdirSync(path62.dirname(tempPath), { recursive: true });
37033
- 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
+ }
37034
37136
  try {
37035
37137
  let existing = null;
37036
37138
  try {
@@ -37094,7 +37196,14 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37094
37196
  message: `Baseline would exceed size cap (${json2.length} bytes > ${MAX_BASELINE_BYTES})`
37095
37197
  };
37096
37198
  }
37199
+ if (opts?.abortSignal?.aborted) {
37200
+ return { status: "error", message: "SAST baseline capture cancelled" };
37201
+ }
37097
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
+ }
37098
37207
  fs25.renameSync(tempPath, baselinePath);
37099
37208
  invalidateCachedArtifact(baselinePath);
37100
37209
  return {
@@ -37127,7 +37236,14 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
37127
37236
  message: `Baseline would exceed size cap (${json.length} bytes > ${MAX_BASELINE_BYTES})`
37128
37237
  };
37129
37238
  }
37239
+ if (opts?.abortSignal?.aborted) {
37240
+ return { status: "error", message: "SAST baseline capture cancelled" };
37241
+ }
37130
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
+ }
37131
37247
  fs25.renameSync(tempPath, baselinePath);
37132
37248
  invalidateCachedArtifact(baselinePath);
37133
37249
  return {
@@ -37249,6 +37365,36 @@ function countBySeverity(findings) {
37249
37365
  }
37250
37366
  return counts;
37251
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
+ }
37252
37398
  function scanFileWithTierA(filePath, language) {
37253
37399
  try {
37254
37400
  const content = fs26.readFileSync(filePath, "utf-8");
@@ -37274,7 +37420,8 @@ async function sastScan(input, directory, config) {
37274
37420
  severity_threshold = "medium",
37275
37421
  capture_baseline = false,
37276
37422
  phase,
37277
- offline_only = false
37423
+ offline_only = false,
37424
+ abort_signal
37278
37425
  } = input;
37279
37426
  if (config?.gates?.sast_scan?.enabled === false) {
37280
37427
  return {
@@ -37297,10 +37444,17 @@ async function sastScan(input, directory, config) {
37297
37444
  let filesScanned = 0;
37298
37445
  let _filesSkipped = 0;
37299
37446
  const scannedFilePaths = [];
37300
- 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);
37301
37451
  const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
37302
37452
  const filesByLanguage = new Map;
37453
+ let semgrepError = abort_signal?.aborted ? "SAST scan cancelled" : undefined;
37303
37454
  for (const filePath of changed_files) {
37455
+ if (abort_signal?.aborted) {
37456
+ return cancelledScanResult(allFindings, filesScanned);
37457
+ }
37304
37458
  if (typeof filePath !== "string" || !filePath) {
37305
37459
  _filesSkipped++;
37306
37460
  continue;
@@ -37355,6 +37509,10 @@ async function sastScan(input, directory, config) {
37355
37509
  if (semgrepAvailable && filesByLanguage.size > 0) {
37356
37510
  try {
37357
37511
  for (const [bucketKey, bucketFiles] of filesByLanguage.entries()) {
37512
+ if (abort_signal?.aborted) {
37513
+ semgrepError = "SAST scan cancelled";
37514
+ break;
37515
+ }
37358
37516
  if (bucketFiles.length === 0)
37359
37517
  continue;
37360
37518
  let semgrepResult;
@@ -37363,13 +37521,21 @@ async function sastScan(input, directory, config) {
37363
37521
  semgrepResult = await _internals52.runSemgrep({
37364
37522
  files: bucketFiles,
37365
37523
  lang,
37366
- useAutoConfig: true
37524
+ useAutoConfig: true,
37525
+ cwd: directory,
37526
+ abortSignal: abort_signal
37367
37527
  });
37368
37528
  } else {
37369
37529
  semgrepResult = await _internals52.runSemgrep({
37370
- files: bucketFiles
37530
+ files: bucketFiles,
37531
+ cwd: directory,
37532
+ abortSignal: abort_signal
37371
37533
  });
37372
37534
  }
37535
+ if (semgrepResult.error) {
37536
+ semgrepError = safeSemgrepDiagnostic(semgrepResult.error);
37537
+ break;
37538
+ }
37373
37539
  if (semgrepResult.findings.length > 0) {
37374
37540
  const semgrepFindings = semgrepResult.findings.map((f) => ({
37375
37541
  rule_id: f.rule_id,
@@ -37392,9 +37558,42 @@ async function sastScan(input, directory, config) {
37392
37558
  }
37393
37559
  }
37394
37560
  }
37395
- } catch (error2) {
37396
- 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);
37397
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
+ };
37398
37597
  }
37399
37598
  if (capture_baseline) {
37400
37599
  if (phase === undefined || !Number.isInteger(phase) || phase < 1) {
@@ -37425,7 +37624,10 @@ async function sastScan(input, directory, config) {
37425
37624
  };
37426
37625
  }
37427
37626
  const captureFindings = allFindings.slice(0, MAX_BASELINE_FINDINGS);
37428
- 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 });
37429
37631
  const captureStatus = captureResult.status === "written" ? "baseline_captured" : captureResult.status === "merged" ? "baseline_merged" : undefined;
37430
37632
  if (captureResult.status === "error") {
37431
37633
  warn(`SAST Baseline: capture failed \u2014 ${captureResult.message}`);
@@ -37437,6 +37639,9 @@ async function sastScan(input, directory, config) {
37437
37639
  findings_count: finalFindings2.length,
37438
37640
  findings_by_severity: countBySeverity(finalFindings2)
37439
37641
  };
37642
+ if (abort_signal?.aborted) {
37643
+ return cancelledScanResult(allFindings, filesScanned);
37644
+ }
37440
37645
  await saveEvidence(directory, "sast_scan", {
37441
37646
  task_id: "sast_scan",
37442
37647
  type: "sast",
@@ -37447,7 +37652,7 @@ async function sastScan(input, directory, config) {
37447
37652
  ...summary2,
37448
37653
  findings: finalFindings2,
37449
37654
  baseline_used: false
37450
- });
37655
+ }, abort_signal);
37451
37656
  return {
37452
37657
  verdict: "pass",
37453
37658
  findings: finalFindings2,
@@ -37515,6 +37720,9 @@ async function sastScan(input, directory, config) {
37515
37720
  findings_count: finalFindings.length,
37516
37721
  findings_by_severity: findingsBySeverity
37517
37722
  };
37723
+ if (abort_signal?.aborted) {
37724
+ return cancelledScanResult(allFindings, filesScanned);
37725
+ }
37518
37726
  await saveEvidence(directory, "sast_scan", {
37519
37727
  task_id: "sast_scan",
37520
37728
  type: "sast",
@@ -37529,7 +37737,7 @@ async function sastScan(input, directory, config) {
37529
37737
  pre_existing_findings: preExistingFindings,
37530
37738
  baseline_used: true
37531
37739
  }
37532
- });
37740
+ }, abort_signal);
37533
37741
  const result = {
37534
37742
  verdict,
37535
37743
  findings: finalFindings,
@@ -37553,7 +37761,7 @@ var sast_scan = createSwarmTool({
37553
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."),
37554
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.")
37555
37763
  },
37556
- execute: async (args, directory) => {
37764
+ execute: async (args, directory, ctx) => {
37557
37765
  let safeArgs;
37558
37766
  try {
37559
37767
  if (args && typeof args === "object") {
@@ -37606,7 +37814,8 @@ var sast_scan = createSwarmTool({
37606
37814
  changed_files: safeArgs.changed_files ?? [],
37607
37815
  severity_threshold: safeArgs.severity_threshold ?? "medium",
37608
37816
  capture_baseline: safeArgs.capture_baseline,
37609
- phase: safeArgs.phase
37817
+ phase: safeArgs.phase,
37818
+ abort_signal: ctx?.abort
37610
37819
  };
37611
37820
  const result = await _internals52.sastScan(input, directory);
37612
37821
  return JSON.stringify(result, null, 2);
@@ -37615,6 +37824,7 @@ var sast_scan = createSwarmTool({
37615
37824
  var _internals52 = {
37616
37825
  sastScan,
37617
37826
  sast_scan,
37827
+ checkSemgrepAvailable: (directory, abortSignal) => checkSemgrepAvailable(directory, abortSignal),
37618
37828
  isSemgrepAvailable: () => isSemgrepAvailable(),
37619
37829
  runSemgrep: (options) => runSemgrep(options)
37620
37830
  };
@@ -43166,6 +43376,22 @@ import * as fs30 from "fs";
43166
43376
  import * as path76 from "path";
43167
43377
  var MAX_OUTPUT_BYTES2 = 512000;
43168
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
+ };
43169
43395
  function validateArgs(args) {
43170
43396
  if (typeof args !== "object" || args === null)
43171
43397
  return false;
@@ -43174,112 +43400,533 @@ function validateArgs(args) {
43174
43400
  return false;
43175
43401
  return true;
43176
43402
  }
43177
- function getLinterCommand(linter, mode, projectDir) {
43178
- const isWindows = process.platform === "win32";
43179
- const binDir = path76.join(projectDir, "node_modules", ".bin");
43180
- const biomeBin = isWindows ? path76.join(binDir, "biome.EXE") : path76.join(binDir, "biome");
43181
- const eslintBin = isWindows ? path76.join(binDir, "eslint.cmd") : path76.join(binDir, "eslint");
43182
- switch (linter) {
43183
- case "biome":
43184
- if (mode === "fix") {
43185
- 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
+ };
43186
43571
  }
43187
- return isWindows ? [biomeBin, "check", "."] : [biomeBin, "check", "."];
43188
- case "eslint":
43189
- if (mode === "fix") {
43190
- 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;
43191
43592
  }
43192
- return isWindows ? [eslintBin, "."] : [eslintBin, "."];
43593
+ }
43193
43594
  }
43595
+ return null;
43194
43596
  }
43195
- function getAdditionalLinterCommand(linter, mode, cwd) {
43196
- const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
43197
- const gradlew = fs30.existsSync(path76.join(cwd, gradlewName)) ? path76.join(cwd, gradlewName) : null;
43198
- switch (linter) {
43199
- case "ruff":
43200
- return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
43201
- case "clippy":
43202
- return mode === "fix" ? ["cargo", "clippy", "--fix", "--allow-dirty"] : ["cargo", "clippy"];
43203
- case "golangci-lint":
43204
- return mode === "fix" ? ["golangci-lint", "run", "--fix"] : ["golangci-lint", "run"];
43205
- case "checkstyle":
43206
- if (gradlew) {
43207
- 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;
43208
43630
  }
43209
- if (isCommandAvailable("gradle")) {
43210
- 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;
43211
43642
  }
43212
- return ["mvn", "checkstyle:check"];
43213
- case "ktlint":
43214
- return mode === "fix" ? ["ktlint", "--format"] : ["ktlint"];
43215
- case "dotnet-format":
43216
- return mode === "fix" ? ["dotnet", "format"] : ["dotnet", "format", "--verify-no-changes"];
43217
- case "cppcheck":
43218
- return ["cppcheck", "--enable=all", "."];
43219
- case "swiftlint":
43220
- return mode === "fix" ? ["swiftlint", "--fix"] : ["swiftlint"];
43221
- case "dart-analyze":
43222
- return mode === "fix" ? ["dart", "fix"] : ["dart", "analyze"];
43223
- case "rubocop": {
43224
- const useBundle = isCommandAvailable("bundle");
43225
- const base = useBundle ? ["bundle", "exec", "rubocop"] : ["rubocop"];
43226
- return mode === "fix" ? [...base, "-A"] : base;
43227
- }
43228
- case "phpstan":
43229
- return [phpVendorBin(cwd, "phpstan"), "analyse"];
43230
- case "pint":
43231
- return mode === "fix" ? [phpVendorBin(cwd, "pint")] : [phpVendorBin(cwd, "pint"), "--test"];
43232
- case "php-cs-fixer":
43233
- return mode === "fix" ? [phpVendorBin(cwd, "php-cs-fixer"), "fix"] : [phpVendorBin(cwd, "php-cs-fixer"), "fix", "--dry-run", "--diff"];
43234
- case "phpcs":
43235
- 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
+ }
43236
43780
  }
43781
+ return null;
43237
43782
  }
43238
43783
  function detectRuff(cwd) {
43239
- if (fs30.existsSync(path76.join(cwd, "ruff.toml")))
43240
- return isCommandAvailable("ruff");
43784
+ if (_internals64.existsSync(path76.join(cwd, "ruff.toml")))
43785
+ return hasAdditionalCommand("ruff");
43241
43786
  try {
43242
43787
  const pyproject = path76.join(cwd, "pyproject.toml");
43243
- if (fs30.existsSync(pyproject)) {
43244
- const content = fs30.readFileSync(pyproject, "utf-8");
43788
+ if (_internals64.existsSync(pyproject)) {
43789
+ const content = _internals64.readFileSync(pyproject, "utf-8");
43245
43790
  if (content.includes("[tool.ruff]"))
43246
- return isCommandAvailable("ruff");
43791
+ return hasAdditionalCommand("ruff");
43247
43792
  }
43248
43793
  } catch {}
43249
43794
  return false;
43250
43795
  }
43251
43796
  function detectClippy(cwd) {
43252
- return fs30.existsSync(path76.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
43797
+ return _internals64.existsSync(path76.join(cwd, "Cargo.toml")) && hasAdditionalCommand("cargo");
43253
43798
  }
43254
43799
  function detectGolangciLint(cwd) {
43255
- return fs30.existsSync(path76.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
43800
+ return _internals64.existsSync(path76.join(cwd, "go.mod")) && hasAdditionalCommand("golangci-lint");
43256
43801
  }
43257
43802
  function readTextFileSafe(filePath) {
43258
43803
  try {
43259
- return fs30.readFileSync(filePath, "utf-8");
43804
+ return _internals64.readFileSync(filePath, "utf-8");
43260
43805
  } catch {
43261
43806
  return "";
43262
43807
  }
43263
43808
  }
43264
- function phpVendorBin(_cwd, name) {
43265
- const fileName = process.platform === "win32" ? `${name}.bat` : name;
43266
- 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;
43267
43920
  }
43268
- function phpVendorBinExists(cwd, name) {
43269
- const local = path76.join(cwd, "vendor", "bin", name);
43270
- const localWin = path76.join(cwd, "vendor", "bin", `${name}.bat`);
43271
- 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());
43272
43923
  }
43273
43924
  function detectCheckstyle(cwd) {
43274
- const hasMaven = fs30.existsSync(path76.join(cwd, "pom.xml"));
43275
- const gradleFiles = ["build.gradle", "build.gradle.kts"].map((file) => path76.join(cwd, file)).filter((file) => fs30.existsSync(file));
43276
- 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)));
43277
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradleCheckstyleSignal && (fs30.existsSync(path76.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
43278
- return (hasMaven || hasGradleCheckstyleSignal) && hasBinary;
43925
+ return resolveCheckstyleCommand(cwd) !== null;
43279
43926
  }
43280
43927
  function hasRootKotlinFile(cwd) {
43281
43928
  try {
43282
- 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"));
43283
43930
  } catch {
43284
43931
  return false;
43285
43932
  }
@@ -43289,59 +43936,59 @@ function buildGradleHasKotlinSignal(cwd) {
43289
43936
  return /\b(kotlin|org\.jetbrains\.kotlin|ktlint)\b/i.test(content);
43290
43937
  }
43291
43938
  function detectKtlint(cwd) {
43292
- const hasKotlin = fs30.existsSync(path76.join(cwd, "build.gradle.kts")) || fs30.existsSync(path76.join(cwd, "build.gradle")) && buildGradleHasKotlinSignal(cwd) || hasRootKotlinFile(cwd);
43293
- 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");
43294
43941
  }
43295
43942
  function detectPhpLinter(cwd) {
43296
- 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) {
43297
43944
  return "phpstan";
43298
43945
  }
43299
- if (fs30.existsSync(path76.join(cwd, "pint.json")) && phpVendorBinExists(cwd, "pint")) {
43946
+ if (_internals64.existsSync(path76.join(cwd, "pint.json")) && resolvePhpVendorCommand(cwd, "pint") !== null) {
43300
43947
  return "pint";
43301
43948
  }
43302
- 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) {
43303
43950
  return "php-cs-fixer";
43304
43951
  }
43305
- if (fs30.existsSync(path76.join(cwd, "phpcs.xml")) && phpVendorBinExists(cwd, "phpcs")) {
43952
+ if (_internals64.existsSync(path76.join(cwd, "phpcs.xml")) && resolvePhpVendorCommand(cwd, "phpcs") !== null) {
43306
43953
  return "phpcs";
43307
43954
  }
43308
43955
  return null;
43309
43956
  }
43310
43957
  function detectDotnetFormat(cwd) {
43311
43958
  try {
43312
- const files = fs30.readdirSync(cwd);
43959
+ const files = _internals64.readdirSync(cwd);
43313
43960
  const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
43314
- return hasCsproj && isCommandAvailable("dotnet");
43961
+ return hasCsproj && hasAdditionalCommand("dotnet");
43315
43962
  } catch {
43316
43963
  return false;
43317
43964
  }
43318
43965
  }
43319
43966
  function detectCppcheck(cwd) {
43320
- if (fs30.existsSync(path76.join(cwd, "CMakeLists.txt"))) {
43321
- return isCommandAvailable("cppcheck");
43967
+ if (_internals64.existsSync(path76.join(cwd, "CMakeLists.txt"))) {
43968
+ return hasAdditionalCommand("cppcheck");
43322
43969
  }
43323
43970
  try {
43324
43971
  const dirsToCheck = [cwd, path76.join(cwd, "src")];
43325
43972
  const hasCpp = dirsToCheck.some((dir) => {
43326
43973
  try {
43327
- 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));
43328
43975
  } catch {
43329
43976
  return false;
43330
43977
  }
43331
43978
  });
43332
- return hasCpp && isCommandAvailable("cppcheck");
43979
+ return hasCpp && hasAdditionalCommand("cppcheck");
43333
43980
  } catch {
43334
43981
  return false;
43335
43982
  }
43336
43983
  }
43337
43984
  function detectSwiftlint(cwd) {
43338
- return fs30.existsSync(path76.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
43985
+ return _internals64.existsSync(path76.join(cwd, "Package.swift")) && hasAdditionalCommand("swiftlint");
43339
43986
  }
43340
43987
  function detectDartAnalyze(cwd) {
43341
- return fs30.existsSync(path76.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
43988
+ return _internals64.existsSync(path76.join(cwd, "pubspec.yaml")) && hasAdditionalCommand("dart");
43342
43989
  }
43343
43990
  function detectRubocop(cwd) {
43344
- 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"));
43345
43992
  }
43346
43993
  function detectAdditionalLinter(cwd) {
43347
43994
  if (detectRuff(cwd))
@@ -43369,208 +44016,179 @@ function detectAdditionalLinter(cwd) {
43369
44016
  return "rubocop";
43370
44017
  return null;
43371
44018
  }
43372
- function findBinInAncestors(startDir, binName) {
43373
- let dir = startDir;
43374
- while (true) {
43375
- const candidate = path76.join(dir, "node_modules", ".bin", binName);
43376
- if (fs30.existsSync(candidate))
43377
- return candidate;
43378
- const parent = path76.dirname(dir);
43379
- if (parent === dir)
43380
- break;
43381
- dir = parent;
43382
- }
43383
- return null;
43384
- }
43385
- function findBinInEnvPath(binName) {
43386
- const searchPath = process.env.PATH ?? "";
43387
- for (const dir of searchPath.split(path76.delimiter)) {
43388
- if (!dir)
43389
- continue;
43390
- const candidate = path76.join(dir, binName);
43391
- if (fs30.existsSync(candidate))
43392
- return candidate;
43393
- }
43394
- return null;
43395
- }
43396
44019
  async function detectAvailableLinter(directory) {
43397
- if (!directory)
43398
- return null;
43399
- if (!fs30.existsSync(directory))
43400
- return null;
43401
- const projectDir = directory;
43402
- const isWindows = process.platform === "win32";
43403
- const biomeBin = isWindows ? path76.join(projectDir, "node_modules", ".bin", "biome.EXE") : path76.join(projectDir, "node_modules", ".bin", "biome");
43404
- const eslintBin = isWindows ? path76.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path76.join(projectDir, "node_modules", ".bin", "eslint");
43405
- const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
43406
- if (localResult)
43407
- return localResult;
43408
- const biomeAncestor = findBinInAncestors(path76.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
43409
- const eslintAncestor = findBinInAncestors(path76.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
43410
- if (biomeAncestor || eslintAncestor) {
43411
- return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
43412
- }
43413
- const pathBiome = findBinInEnvPath(isWindows ? "biome.EXE" : "biome");
43414
- const pathEslint = findBinInEnvPath(isWindows ? "eslint.cmd" : "eslint");
43415
- if (pathBiome || pathEslint) {
43416
- return _detectAvailableLinter(projectDir, pathBiome ?? biomeBin, pathEslint ?? eslintBin);
43417
- }
43418
- return null;
44020
+ const resolved = await detectResolvedLinter(directory);
44021
+ return resolved?.linter ?? null;
43419
44022
  }
43420
- async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
43421
- const DETECT_TIMEOUT = 2000;
43422
- try {
43423
- const biomeProc = bunSpawn([biomeBin, "--version"], {
43424
- stdout: "pipe",
43425
- stderr: "pipe"
43426
- });
43427
- const biomeExit = biomeProc.exited;
43428
- const timeout = new Promise((resolve23) => setTimeout(() => resolve23("timeout"), DETECT_TIMEOUT));
43429
- const result = await Promise.race([biomeExit, timeout]);
43430
- if (result === "timeout") {
43431
- biomeProc.kill();
43432
- } else if (biomeProc.exitCode === 0 && fs30.existsSync(biomeBin)) {
43433
- return "biome";
43434
- }
43435
- } catch {}
43436
- try {
43437
- const eslintProc = bunSpawn([eslintBin, "--version"], {
43438
- stdout: "pipe",
43439
- stderr: "pipe"
43440
- });
43441
- const eslintExit = eslintProc.exited;
43442
- const timeout = new Promise((resolve23) => setTimeout(() => resolve23("timeout"), DETECT_TIMEOUT));
43443
- const result = await Promise.race([eslintExit, timeout]);
43444
- if (result === "timeout") {
43445
- eslintProc.kill();
43446
- } else if (eslintProc.exitCode === 0 && fs30.existsSync(eslintBin)) {
43447
- return "eslint";
43448
- }
43449
- } catch {}
43450
- return null;
43451
- }
43452
- async function runLint(linter, mode, directory) {
43453
- const command = getLinterCommand(linter, mode, directory);
43454
- 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(" ");
43455
44027
  if (commandStr.length > MAX_COMMAND_LENGTH) {
43456
44028
  return {
43457
44029
  success: false,
43458
44030
  mode,
43459
- linter,
43460
- command,
44031
+ linter: command.linter,
44032
+ command: displayCommand,
43461
44033
  error: "Command exceeds maximum allowed length"
43462
44034
  };
43463
44035
  }
43464
- try {
43465
- const proc = bunSpawn(command, {
43466
- stdout: "pipe",
43467
- stderr: "pipe",
43468
- cwd: directory
43469
- });
43470
- const [stdout, stderr] = await Promise.all([
43471
- proc.stdout.text(),
43472
- proc.stderr.text()
43473
- ]);
43474
- const exitCode = await proc.exited;
43475
- let output = stdout;
43476
- if (stderr) {
43477
- output += (output ? `
43478
- ` : "") + 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"] : []);
43479
44096
  }
43480
- if (output.length > MAX_OUTPUT_BYTES2) {
43481
- output = `${output.slice(0, MAX_OUTPUT_BYTES2)}
43482
- ... (output truncated)`;
44097
+ case "phpstan": {
44098
+ const base = resolvePhpVendorCommand(cwd, "phpstan");
44099
+ return base ? [...base, "analyse"] : null;
43483
44100
  }
43484
- const result = {
43485
- success: true,
43486
- mode,
43487
- linter,
43488
- command,
43489
- exitCode,
43490
- output
43491
- };
43492
- if (exitCode === 0) {
43493
- result.message = `${linter} ${mode} completed successfully with no issues`;
43494
- } else if (mode === "fix") {
43495
- result.message = `${linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
43496
- } else {
43497
- 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;
43498
44104
  }
43499
- return result;
43500
- } 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) {
43501
44116
  return {
43502
44117
  success: false,
43503
44118
  mode,
43504
44119
  linter,
43505
- command,
43506
- error: error2 instanceof Error ? `Execution failed: ${error2.message}` : "Execution failed: unknown error"
44120
+ error: `No safely resolved ${linter} executable found`
43507
44121
  };
43508
44122
  }
44123
+ return runResolvedLint(resolved, mode, directory, abortSignal);
43509
44124
  }
43510
- async function runAdditionalLint(linter, mode, cwd) {
44125
+ async function runAdditionalLint(linter, mode, cwd, abortSignal) {
43511
44126
  const command = getAdditionalLinterCommand(linter, mode, cwd);
43512
- const commandStr = command.join(" ");
43513
- if (commandStr.length > MAX_COMMAND_LENGTH) {
44127
+ if (!command) {
43514
44128
  return {
43515
44129
  success: false,
43516
44130
  mode,
43517
44131
  linter,
43518
- command,
43519
- error: "Command exceeds maximum allowed length"
44132
+ error: `No safely executable ${linter} command found`
43520
44133
  };
43521
44134
  }
43522
- try {
43523
- const proc = bunSpawn(command, {
43524
- stdout: "pipe",
43525
- stderr: "pipe",
43526
- cwd
43527
- });
43528
- const [stdout, stderr] = await Promise.all([
43529
- proc.stdout.text(),
43530
- proc.stderr.text()
43531
- ]);
43532
- const exitCode = await proc.exited;
43533
- let output = stdout;
43534
- if (stderr) {
43535
- output += (output ? `
43536
- ` : "") + stderr;
43537
- }
43538
- if (output.length > MAX_OUTPUT_BYTES2) {
43539
- output = `${output.slice(0, MAX_OUTPUT_BYTES2)}
43540
- ... (output truncated)`;
43541
- }
43542
- const result = {
43543
- success: true,
44135
+ const commandStr = command.join(" ");
44136
+ if (commandStr.length > MAX_COMMAND_LENGTH) {
44137
+ return {
44138
+ success: false,
43544
44139
  mode,
43545
44140
  linter,
43546
44141
  command,
43547
- exitCode,
43548
- output
44142
+ error: "Command exceeds maximum allowed length"
43549
44143
  };
43550
- if (exitCode === 0) {
43551
- result.message = `${linter} ${mode} completed successfully with no issues`;
43552
- } else if (mode === "fix") {
43553
- result.message = `${linter} fix completed with exit code ${exitCode}. Run check mode to see remaining issues.`;
43554
- } else {
43555
- result.message = `${linter} check found issues (exit code ${exitCode}).`;
43556
- }
43557
- return result;
43558
- } catch (error2) {
44144
+ }
44145
+ const [executable, ...args] = command;
44146
+ if (isUnsafeShellWrapperExecutable(executable)) {
43559
44147
  return {
43560
44148
  success: false,
43561
44149
  mode,
43562
44150
  linter,
43563
44151
  command,
43564
- error: error2 instanceof Error ? `Execution failed: ${error2.message}` : "Execution failed: unknown error"
44152
+ error: `No safely executable ${linter} command found`
43565
44153
  };
43566
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;
43567
44185
  }
43568
44186
  var lint = createSwarmTool({
43569
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.",
43570
44188
  args: {
43571
44189
  mode: exports_external.enum(["fix", "check"]).describe('Linting mode: "check" for read-only lint check, "fix" to automatically apply fixes')
43572
44190
  },
43573
- async execute(args, directory) {
44191
+ async execute(args, directory, ctx) {
43574
44192
  if (!validateArgs(args)) {
43575
44193
  const errorResult2 = {
43576
44194
  success: false,
@@ -43589,15 +44207,15 @@ var lint = createSwarmTool({
43589
44207
  }
43590
44208
  const { mode } = args;
43591
44209
  const cwd = directory;
43592
- const linter = await _internals64.detectAvailableLinter(directory);
43593
- if (linter) {
43594
- 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);
43595
44213
  return JSON.stringify(result, null, 2);
43596
44214
  }
43597
44215
  const additionalLinter = _internals64.detectAdditionalLinter(cwd);
43598
44216
  if (additionalLinter) {
43599
44217
  warn(`[lint] Using ${additionalLinter} linter for this project`);
43600
- const result = await _internals64.runAdditionalLint(additionalLinter, mode, cwd);
44218
+ const result = await _internals64.runAdditionalLint(additionalLinter, mode, cwd, ctx?.abort);
43601
44219
  return JSON.stringify(result, null, 2);
43602
44220
  }
43603
44221
  const errorResult = {
@@ -43612,10 +44230,31 @@ For Rust: rustup component add clippy`
43612
44230
  }
43613
44231
  });
43614
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,
43615
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,
43616
44255
  runLint,
43617
- detectAdditionalLinter,
43618
- runAdditionalLint
44256
+ runResolvedLint,
44257
+ statSync: fs30.statSync
43619
44258
  };
43620
44259
 
43621
44260
  // src/tools/secretscan.ts
@@ -43979,8 +44618,19 @@ function createRedactedContext(line, findings) {
43979
44618
  return `${result.slice(0, edgeLength)}${truncationMarker}${result.slice(-remaining)}`;
43980
44619
  }
43981
44620
  var O_NOFOLLOW = process.platform !== "win32" ? fs31.constants.O_NOFOLLOW : undefined;
43982
- function readBoundedFile(filePath) {
43983
- 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;
43984
44634
  try {
43985
44635
  const flags = fs31.constants.O_RDONLY | (O_NOFOLLOW !== undefined ? O_NOFOLLOW : 0);
43986
44636
  fd = _internals65.openFile(filePath, flags);
@@ -43988,13 +44638,17 @@ function readBoundedFile(filePath) {
43988
44638
  return { status: "incomplete", reason: "read_error" };
43989
44639
  }
43990
44640
  let outcome;
44641
+ let closeFailed = false;
43991
44642
  try {
43992
44643
  const stat6 = _internals65.fstatFile(fd);
44644
+ const descriptorIdentity = getStableFileIdentity(stat6);
43993
44645
  const initialSize = stat6.size;
43994
44646
  if (!stat6.isFile()) {
43995
44647
  outcome = { status: "incomplete", reason: "non_file" };
44648
+ } else if (descriptorIdentity === null || descriptorIdentity !== expectedIdentity) {
44649
+ outcome = { status: "incomplete", reason: "read_error" };
43996
44650
  } else {
43997
- 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;
43998
44652
  const buffer = Buffer.allocUnsafe(bufferLength);
43999
44653
  let bytesRead = 0;
44000
44654
  while (bytesRead < buffer.length) {
@@ -44004,10 +44658,15 @@ function readBoundedFile(filePath) {
44004
44658
  bytesRead += count;
44005
44659
  }
44006
44660
  const postStat = _internals65.fstatFile(fd);
44007
- 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) {
44008
44667
  outcome = {
44009
44668
  status: "incomplete",
44010
- reason: postStat.size > MAX_FILE_SIZE_BYTES3 ? "oversized" : "read_error"
44669
+ reason: postStat.size > BigInt(MAX_FILE_SIZE_BYTES3) ? "oversized" : "read_error"
44011
44670
  };
44012
44671
  } else {
44013
44672
  outcome = {
@@ -44019,25 +44678,30 @@ function readBoundedFile(filePath) {
44019
44678
  }
44020
44679
  } catch {
44021
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
+ }
44022
44689
  }
44023
- try {
44024
- _internals65.closeFile(fd);
44025
- } catch {
44690
+ if (closeFailed)
44026
44691
  return { status: "incomplete", reason: "read_error" };
44027
- }
44028
44692
  return outcome;
44029
44693
  }
44030
44694
  function scanFileForSecrets(filePath, mode) {
44031
44695
  const findings = [];
44032
44696
  try {
44033
- const lstat3 = fs31.lstatSync(filePath);
44697
+ const lstat3 = _internals65.lstatFile(filePath);
44034
44698
  if (lstat3.isSymbolicLink()) {
44035
44699
  return mode === "explicit" ? { status: "incomplete", reason: "symlink" } : { status: "skipped", reason: "symlink" };
44036
44700
  }
44037
44701
  if (!lstat3.isFile()) {
44038
44702
  return { status: "incomplete", reason: "non_file" };
44039
44703
  }
44040
- const readOutcome = readBoundedFile(filePath);
44704
+ const readOutcome = readBoundedFile(filePath, lstat3);
44041
44705
  if (readOutcome.status === "incomplete")
44042
44706
  return readOutcome;
44043
44707
  const { buffer, truncated } = readOutcome;
@@ -44085,7 +44749,7 @@ function assertNever(value) {
44085
44749
  throw new Error(`Unhandled secretscan outcome: ${String(value)}`);
44086
44750
  }
44087
44751
  async function yieldToEventLoop() {
44088
- await new Promise((resolve24) => setImmediate(resolve24));
44752
+ await new Promise((resolve25) => setImmediate(resolve25));
44089
44753
  }
44090
44754
  function serializeSecretscanResult(result) {
44091
44755
  let findings = result.findings;
@@ -44124,14 +44788,14 @@ function isPathWithinScope(realPath, scanDir) {
44124
44788
  }
44125
44789
  var DISCOVERY_YIELD_INTERVAL = 100;
44126
44790
  function toSafeRelativePath(rootDir, targetPath) {
44127
- const relative10 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
44128
- if (!relative10 || relative10 === ".")
44791
+ const relative11 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
44792
+ if (!relative11 || relative11 === ".")
44129
44793
  return ".";
44130
- if (relative10.startsWith("..") || path77.isAbsolute(relative10)) {
44131
- const basename15 = path77.basename(targetPath).replace(/\\/g, "/");
44132
- return basename15 || ".";
44794
+ if (relative11.startsWith("..") || path77.isAbsolute(relative11)) {
44795
+ const basename16 = path77.basename(targetPath).replace(/\\/g, "/");
44796
+ return basename16 || ".";
44133
44797
  }
44134
- return relative10;
44798
+ return relative11;
44135
44799
  }
44136
44800
  function recordIncompletePath(target, rootDir, targetPath, reason) {
44137
44801
  if (target.length >= MAX_INCOMPLETE_PATHS) {
@@ -44713,10 +45377,10 @@ var _internals65 = {
44713
45377
  now: Date.now,
44714
45378
  yieldToEventLoop,
44715
45379
  openFile: fs31.openSync,
44716
- fstatFile: fs31.fstatSync,
45380
+ fstatFile: (fd) => fs31.fstatSync(fd, { bigint: true }),
44717
45381
  readFileChunk: fs31.readSync,
44718
45382
  closeFile: fs31.closeSync,
44719
- lstatFile: fs31.lstatSync,
45383
+ lstatFile: (path78) => fs31.lstatSync(path78, { bigint: true }),
44720
45384
  closeDirectory: (directory) => directory.closeSync()
44721
45385
  };
44722
45386
 
@@ -45720,7 +46384,7 @@ var DISPATCH_FRAMEWORK_MAP = {
45720
46384
  };
45721
46385
  async function detectTestFrameworkViaDispatch(cwd) {
45722
46386
  try {
45723
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46387
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45724
46388
  const backend = await pickBackend(cwd);
45725
46389
  if (!backend?.selectTestFramework)
45726
46390
  return "none";
@@ -45736,7 +46400,7 @@ async function buildTestCommandViaDispatch(framework, scope, files, coverage, ba
45736
46400
  if (framework === "none")
45737
46401
  return null;
45738
46402
  try {
45739
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46403
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45740
46404
  const backend = await pickBackend(baseDir);
45741
46405
  if (backend?.buildTestCommand) {
45742
46406
  const cmd = backend.buildTestCommand(framework, files, baseDir, {
@@ -45756,7 +46420,7 @@ async function parseTestOutputViaDispatch(framework, output, baseDir) {
45756
46420
  if (framework === "none")
45757
46421
  return null;
45758
46422
  try {
45759
- const { pickBackend } = await import("./dispatch-b1fge4xk.js");
46423
+ const { pickBackend } = await import("./dispatch-nbv0hezh.js");
45760
46424
  const backend = await pickBackend(baseDir);
45761
46425
  if (!backend?.parseTestOutput)
45762
46426
  return null;
@@ -45966,21 +46630,21 @@ function hasCompoundTestExtension(filename) {
45966
46630
  const lower = filename.toLowerCase();
45967
46631
  return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
45968
46632
  }
45969
- function isLanguageSpecificTestFile(basename16) {
45970
- const lower = basename16.toLowerCase();
46633
+ function isLanguageSpecificTestFile(basename17) {
46634
+ const lower = basename17.toLowerCase();
45971
46635
  if (lower.endsWith("_test.go"))
45972
46636
  return true;
45973
46637
  if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
45974
46638
  return true;
45975
46639
  if (lower.endsWith("_spec.rb"))
45976
46640
  return true;
45977
- 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")))
45978
46642
  return true;
45979
46643
  if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
45980
46644
  return true;
45981
- 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")))
45982
46646
  return true;
45983
- 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")))
45984
46648
  return true;
45985
46649
  if (lower.endsWith(".tests.ps1"))
45986
46650
  return true;
@@ -45988,23 +46652,23 @@ function isLanguageSpecificTestFile(basename16) {
45988
46652
  }
45989
46653
  function isConventionTestFilePath(filePath) {
45990
46654
  const normalizedPath = filePath.replace(/\\/g, "/");
45991
- const basename16 = path81.basename(filePath);
45992
- 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);
45993
46657
  }
45994
46658
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45995
46659
  const testFiles = [];
45996
46660
  for (const file of sourceFiles) {
45997
46661
  const absoluteFile = resolveWorkspacePath(file, workingDir);
45998
46662
  const relativeFile = path81.relative(workingDir, absoluteFile);
45999
- const basename16 = path81.basename(absoluteFile);
46663
+ const basename17 = path81.basename(absoluteFile);
46000
46664
  const dirname30 = path81.dirname(absoluteFile);
46001
46665
  const preferRelativeOutput = !path81.isAbsolute(file);
46002
46666
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
46003
46667
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
46004
46668
  continue;
46005
46669
  }
46006
- const nameWithoutExt = basename16.replace(/\.[^.]+$/, "");
46007
- const ext = path81.extname(basename16);
46670
+ const nameWithoutExt = basename17.replace(/\.[^.]+$/, "");
46671
+ const ext = path81.extname(basename17);
46008
46672
  const genericTestNames = [
46009
46673
  `${nameWithoutExt}.spec${ext}`,
46010
46674
  `${nameWithoutExt}.test${ext}`
@@ -46015,7 +46679,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
46015
46679
  ...languageSpecificTestNames
46016
46680
  ].map((candidateName) => path81.join(dirname30, candidateName));
46017
46681
  const testDirectoryNames = [
46018
- basename16,
46682
+ basename17,
46019
46683
  ...genericTestNames,
46020
46684
  ...languageSpecificTestNames
46021
46685
  ];
@@ -46280,13 +46944,13 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
46280
46944
  'Dir.glob("test/**/*_test.rb").sort.each { |f| require_relative f }'
46281
46945
  ];
46282
46946
  case "pest": {
46283
- const args = [phpVendorBin2("pest")];
46947
+ const args = [phpVendorBin("pest")];
46284
46948
  if (scope !== "all" && files.length > 0)
46285
46949
  args.push(...files);
46286
46950
  return args;
46287
46951
  }
46288
46952
  case "phpunit": {
46289
- const args = [phpVendorBin2("phpunit")];
46953
+ const args = [phpVendorBin("phpunit")];
46290
46954
  if (scope !== "all" && files.length > 0)
46291
46955
  args.push(...files);
46292
46956
  return args;
@@ -46301,7 +46965,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir, bail) {
46301
46965
  return null;
46302
46966
  }
46303
46967
  }
46304
- function phpVendorBin2(name) {
46968
+ function phpVendorBin(name) {
46305
46969
  return path81.join("vendor", "bin", process.platform === "win32" ? `${name}.bat` : name);
46306
46970
  }
46307
46971
  function mapFrameworkStatusToResult(status) {
@@ -46740,9 +47404,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd, bail
46740
47404
  stderr: "pipe",
46741
47405
  cwd
46742
47406
  });
46743
- const timeoutPromise = new Promise((resolve26) => setTimeout(() => {
47407
+ const timeoutPromise = new Promise((resolve27) => setTimeout(() => {
46744
47408
  proc.kill();
46745
- resolve26(-1);
47409
+ resolve27(-1);
46746
47410
  }, timeout_ms));
46747
47411
  const [exitCode, stdoutResult, stderrResult] = await Promise.all([
46748
47412
  Promise.race([proc.exited, timeoutPromise]),
@@ -46900,11 +47564,11 @@ function normalizeHistoryTestFile(testFile, workingDir) {
46900
47564
  const normalized = testFile.replace(/\\/g, "/");
46901
47565
  if (!path81.isAbsolute(testFile))
46902
47566
  return normalized;
46903
- const relative11 = path81.relative(workingDir, testFile);
46904
- if (relative11.startsWith("..") || path81.isAbsolute(relative11)) {
47567
+ const relative12 = path81.relative(workingDir, testFile);
47568
+ if (relative12.startsWith("..") || path81.isAbsolute(relative12)) {
46905
47569
  return normalized;
46906
47570
  }
46907
- return relative11.replace(/\\/g, "/");
47571
+ return relative12.replace(/\\/g, "/");
46908
47572
  }
46909
47573
  function combineAggregateResult(current, next) {
46910
47574
  if (current === "fail" || next === "fail")
@@ -48328,13 +48992,13 @@ class CircuitBreaker {
48328
48992
  if (this.config.callTimeoutMs <= 0) {
48329
48993
  return fn();
48330
48994
  }
48331
- return new Promise((resolve27, reject) => {
48995
+ return new Promise((resolve28, reject) => {
48332
48996
  const timeout = setTimeout(() => {
48333
48997
  reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
48334
48998
  }, this.config.callTimeoutMs);
48335
48999
  fn().then((result) => {
48336
49000
  clearTimeout(timeout);
48337
- resolve27(result);
49001
+ resolve28(result);
48338
49002
  }).catch((error2) => {
48339
49003
  clearTimeout(timeout);
48340
49004
  reject(error2);
@@ -48633,7 +49297,7 @@ class AutomationQueue {
48633
49297
 
48634
49298
  // src/background/worker.ts
48635
49299
  function sleep(ms) {
48636
- return new Promise((resolve27) => setTimeout(resolve27, ms));
49300
+ return new Promise((resolve28) => setTimeout(resolve28, ms));
48637
49301
  }
48638
49302
 
48639
49303
  class WorkerManager {
@@ -50583,7 +51247,7 @@ function resolveReviewFallbackModels(agentName, registry) {
50583
51247
  init_logger();
50584
51248
 
50585
51249
  // src/hooks/review-receipt-scope.ts
50586
- import * as child_process7 from "child_process";
51250
+ import * as child_process6 from "child_process";
50587
51251
  import { createHash as createHash14 } from "crypto";
50588
51252
  import * as fs42 from "fs";
50589
51253
  import * as path89 from "path";
@@ -50595,7 +51259,7 @@ var HEAD_OUTPUT_BYTES = 256;
50595
51259
  var HEAD_SHA_PATTERN = /^[0-9a-f]{40,64}$/i;
50596
51260
  var REVIEWER_TASK_SCOPE_DESCRIPTION = "reviewer-task-files-v1";
50597
51261
  var _internals74 = {
50598
- spawn: child_process7.spawn,
51262
+ spawn: child_process6.spawn,
50599
51263
  realpath: fs42.promises.realpath,
50600
51264
  lstatBigInt: (path90) => fs42.promises.lstat(path90, { bigint: true }),
50601
51265
  open: fs42.promises.open,
@@ -50610,8 +51274,8 @@ function hasControlCharacter2(value) {
50610
51274
  return false;
50611
51275
  }
50612
51276
  function isContained2(root, candidate) {
50613
- const relative14 = path89.relative(root, candidate);
50614
- 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);
50615
51279
  }
50616
51280
  function canonicalPath(relativePath2) {
50617
51281
  return relativePath2.split(path89.sep).join("/");
@@ -50667,7 +51331,7 @@ async function resolveHeadSha(directory) {
50667
51331
  timeout: HEAD_TIMEOUT_MS,
50668
51332
  windowsHide: true
50669
51333
  });
50670
- return await new Promise((resolve30) => {
51334
+ return await new Promise((resolve31) => {
50671
51335
  let settled = false;
50672
51336
  let stdoutBytes = 0;
50673
51337
  const stdoutChunks = [];
@@ -50677,7 +51341,7 @@ async function resolveHeadSha(directory) {
50677
51341
  settled = true;
50678
51342
  if (timeoutHandle !== undefined)
50679
51343
  clearTimeout(timeoutHandle);
50680
- resolve30(value);
51344
+ resolve31(value);
50681
51345
  };
50682
51346
  const killAndFail = () => {
50683
51347
  try {
@@ -51397,8 +52061,8 @@ function normalizedRelativePath(file) {
51397
52061
  return normalized.replace(/^\.\//, "");
51398
52062
  }
51399
52063
  function isWithinRoot2(root, candidate) {
51400
- const relative16 = path91.relative(root, candidate);
51401
- 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);
51402
52066
  }
51403
52067
  function rangesOverlap(start, end, ranges) {
51404
52068
  return ranges.some((range) => start <= range.end && end >= range.start);
@@ -51962,8 +52626,8 @@ function formatSelector(selector) {
51962
52626
  }
51963
52627
  }
51964
52628
  function isPathInside(parent, candidate) {
51965
- const relative17 = path92.relative(parent, candidate);
51966
- 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);
51967
52631
  }
51968
52632
  function sameFileSnapshot4(left, right) {
51969
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;
@@ -53058,14 +53722,14 @@ import * as path96 from "path";
53058
53722
  // src/services/skill-optimizer/store.ts
53059
53723
  import { createHash as createHash15, randomUUID as randomUUID10 } from "crypto";
53060
53724
  import {
53061
- closeSync as closeSync12,
53725
+ closeSync as closeSync13,
53062
53726
  existsSync as existsSync53,
53063
53727
  fsyncSync as fsyncSync3,
53064
53728
  mkdirSync as mkdirSync27,
53065
- openSync as openSync12,
53729
+ openSync as openSync13,
53066
53730
  readFileSync as readFileSync39,
53067
53731
  renameSync as renameSync18,
53068
- statSync as statSync15,
53732
+ statSync as statSync16,
53069
53733
  unlinkSync as unlinkSync20,
53070
53734
  writeFileSync as writeFileSync17,
53071
53735
  writeFileSync as writeFileSyncAsync
@@ -53131,12 +53795,12 @@ function ensureCandidateDir(directory, skillSlug, candidateId2) {
53131
53795
  return dir;
53132
53796
  }
53133
53797
  function writeFileFsyncedThenRename(tempPath, targetPath, data) {
53134
- const fd = openSync12(tempPath, "w");
53798
+ const fd = openSync13(tempPath, "w");
53135
53799
  try {
53136
53800
  writeFileSync17(fd, data, "utf8");
53137
53801
  fsyncSync3(fd);
53138
53802
  } finally {
53139
- closeSync12(fd);
53803
+ closeSync13(fd);
53140
53804
  }
53141
53805
  try {
53142
53806
  renameSync18(tempPath, targetPath);
@@ -53155,7 +53819,7 @@ var _internals79 = {
53155
53819
  renameSync: renameSync18,
53156
53820
  unlinkSync: unlinkSync20,
53157
53821
  existsSync: existsSync53,
53158
- statSync: statSync15
53822
+ statSync: statSync16
53159
53823
  };
53160
53824
  async function appendEvent(directory, eventInput) {
53161
53825
  if (!isValidSkillSlug(eventInput.skillSlug)) {
@@ -53973,17 +54637,17 @@ async function deterministicSeed(input) {
53973
54637
  }
53974
54638
 
53975
54639
  // src/services/skill-optimizer/smoke.ts
53976
- 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";
53977
54641
  import * as path98 from "path";
53978
54642
 
53979
54643
  // src/hooks/spawn-helper.ts
53980
- import * as child_process8 from "child_process";
54644
+ import * as child_process7 from "child_process";
53981
54645
  var WIN32_CMD_BINARIES = new Set(["npm", "npx", "pnpm", "yarn"]);
53982
54646
  var _internals82 = {
53983
- spawn: child_process8.spawn
54647
+ spawn: child_process7.spawn
53984
54648
  };
53985
54649
  function spawnAsync(command, cwd, timeoutMs) {
53986
- return new Promise((resolve33) => {
54650
+ return new Promise((resolve34) => {
53987
54651
  try {
53988
54652
  const [rawCmd, ...args] = command;
53989
54653
  const cmd = process.platform === "win32" && WIN32_CMD_BINARIES.has(rawCmd) && !rawCmd.includes(".") ? `${rawCmd}.cmd` : rawCmd;
@@ -54030,24 +54694,24 @@ function spawnAsync(command, cwd, timeoutMs) {
54030
54694
  try {
54031
54695
  proc.kill();
54032
54696
  } catch {}
54033
- resolve33(null);
54697
+ resolve34(null);
54034
54698
  }, timeoutMs);
54035
54699
  proc.on("close", (code) => {
54036
54700
  if (done)
54037
54701
  return;
54038
54702
  done = true;
54039
54703
  clearTimeout(timer);
54040
- resolve33({ exitCode: code ?? 1, stdout, stderr });
54704
+ resolve34({ exitCode: code ?? 1, stdout, stderr });
54041
54705
  });
54042
54706
  proc.on("error", () => {
54043
54707
  if (done)
54044
54708
  return;
54045
54709
  done = true;
54046
54710
  clearTimeout(timer);
54047
- resolve33(null);
54711
+ resolve34(null);
54048
54712
  });
54049
54713
  } catch {
54050
- resolve33(null);
54714
+ resolve34(null);
54051
54715
  }
54052
54716
  });
54053
54717
  }
@@ -54066,7 +54730,7 @@ var _internals83 = {
54066
54730
  const rel = path98.relative(path98.resolve(root), path98.resolve(target));
54067
54731
  return rel.startsWith("..") || path98.isAbsolute(rel);
54068
54732
  },
54069
- realpath: (p) => realpathSync9(p),
54733
+ realpath: (p) => realpathSync11(p),
54070
54734
  evaluateSkillChange,
54071
54735
  isRejectedSkillContent,
54072
54736
  spawnAsync
@@ -54430,7 +55094,7 @@ async function runValidationWithTransientRetry(input, candidateContent, baseline
54430
55094
  if (!input.dispatcher) {
54431
55095
  throw new Error("no evaluation dispatcher available (cannot run validation)");
54432
55096
  }
54433
- const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-w30gazew.js");
55097
+ const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-mzb3e2s6.js");
54434
55098
  const executor = createModelEvaluationExecutor2(input.dispatcher, input.sessionId);
54435
55099
  const result = await _internals84.evaluateCandidateV1({
54436
55100
  projectRoot: input.directory,
@@ -55303,11 +55967,18 @@ function makeInitialState() {
55303
55967
  };
55304
55968
  }
55305
55969
  var sessionStates = new Map;
55970
+ var MAX_TRACKED_COMPACTION_SESSIONS = 500;
55306
55971
  function getSessionState(sessionId) {
55307
55972
  let state = sessionStates.get(sessionId);
55308
- if (!state) {
55309
- state = makeInitialState();
55310
- 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
+ }
55311
55982
  }
55312
55983
  return state;
55313
55984
  }
@@ -55392,8 +56063,8 @@ async function getStatusData(directory, agents, sessionId) {
55392
56063
  agentCount,
55393
56064
  isLegacy: false,
55394
56065
  turboMode: hasActiveTurboMode(),
55395
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55396
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56066
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56067
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55397
56068
  compactionCount: metrics.compactionCount,
55398
56069
  lastSnapshotAt: metrics.lastSnapshotAt
55399
56070
  };
@@ -55409,8 +56080,8 @@ async function getStatusData(directory, agents, sessionId) {
55409
56080
  agentCount: Object.keys(agents).length,
55410
56081
  isLegacy: true,
55411
56082
  turboMode: hasActiveTurboMode(),
55412
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55413
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56083
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56084
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55414
56085
  compactionCount: metrics.compactionCount,
55415
56086
  lastSnapshotAt: metrics.lastSnapshotAt
55416
56087
  };
@@ -55429,8 +56100,8 @@ async function getStatusData(directory, agents, sessionId) {
55429
56100
  agentCount,
55430
56101
  isLegacy: true,
55431
56102
  turboMode: hasActiveTurboMode(),
55432
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
55433
- contextBudgetTokens: swarmState.lastBudgetTokens > 0 ? swarmState.lastBudgetTokens : null,
56103
+ contextBudgetPct: getDisplayBudget()?.pct ?? null,
56104
+ contextBudgetTokens: getDisplayBudget()?.tokens ?? null,
55434
56105
  compactionCount: metrics.compactionCount,
55435
56106
  lastSnapshotAt: metrics.lastSnapshotAt
55436
56107
  };
@@ -56254,7 +56925,7 @@ function buildDetailedHelp(commandName, entry) {
56254
56925
  async function handleHelpCommand(ctx) {
56255
56926
  const targetCommand = ctx.args.join(" ");
56256
56927
  if (!targetCommand) {
56257
- const { buildHelpText } = await import("./index-xka8s8jw.js");
56928
+ const { buildHelpText } = await import("./index-5hyq2mw6.js");
56258
56929
  return buildHelpText();
56259
56930
  }
56260
56931
  const tokens = targetCommand.split(/\s+/);
@@ -56263,7 +56934,7 @@ async function handleHelpCommand(ctx) {
56263
56934
  return _internals14.buildDetailedHelp(resolved.key, resolved.entry);
56264
56935
  }
56265
56936
  const similar = _internals14.findSimilarCommands(targetCommand);
56266
- const { buildHelpText: fullHelp } = await import("./index-xka8s8jw.js");
56937
+ const { buildHelpText: fullHelp } = await import("./index-5hyq2mw6.js");
56267
56938
  if (similar.length > 0) {
56268
56939
  return `Command '/swarm ${targetCommand}' not found.
56269
56940
 
@@ -56440,7 +57111,7 @@ var COMMAND_REGISTRY = {
56440
57111
  },
56441
57112
  "guardrail explain": {
56442
57113
  handler: async (ctx) => {
56443
- const { handleGuardrailExplain } = await import("./guardrail-explain-vb9k9zwm.js");
57114
+ const { handleGuardrailExplain } = await import("./guardrail-explain-y2wg00dp.js");
56444
57115
  return handleGuardrailExplain(ctx.directory, ctx.args);
56445
57116
  },
56446
57117
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -56450,7 +57121,7 @@ var COMMAND_REGISTRY = {
56450
57121
  },
56451
57122
  "guardrail-explain": {
56452
57123
  handler: async (ctx) => {
56453
- const { handleGuardrailExplain } = await import("./guardrail-explain-vb9k9zwm.js");
57124
+ const { handleGuardrailExplain } = await import("./guardrail-explain-y2wg00dp.js");
56454
57125
  return handleGuardrailExplain(ctx.directory, ctx.args);
56455
57126
  },
56456
57127
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -56460,7 +57131,7 @@ var COMMAND_REGISTRY = {
56460
57131
  },
56461
57132
  "guardrail-log": {
56462
57133
  handler: async (ctx) => {
56463
- const { handleGuardrailLog } = await import("./guardrail-log-0djnvfas.js");
57134
+ const { handleGuardrailLog } = await import("./guardrail-log-320grggj.js");
56464
57135
  return handleGuardrailLog(ctx.directory, ctx.args);
56465
57136
  },
56466
57137
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -56914,7 +57585,7 @@ Subcommands:
56914
57585
  handler: (ctx) => handleApprovePlanCriticCommand(ctx.directory, ctx.args, ctx.sessionID),
56915
57586
  description: "Record a MANUAL plan-critic approval to unblock the critic_pre_plan execution gate [reason...]",
56916
57587
  args: "[reason...]",
56917
- 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.',
56918
57589
  category: "utility",
56919
57590
  toolPolicy: "restricted"
56920
57591
  },
@@ -59899,14 +60570,6 @@ function getSwarmAgents(swarmId) {
59899
60570
  const id = swarmId ?? "default";
59900
60571
  return _swarmAgentsMap.get(id);
59901
60572
  }
59902
- var KNOWN_VARIANT_VALUES = new Set([
59903
- "low",
59904
- "medium",
59905
- "high",
59906
- "max",
59907
- "xhigh",
59908
- "thinking"
59909
- ]);
59910
60573
 
59911
60574
  // src/hooks/curator-llm-factory.ts
59912
60575
  init_schema();