opencode-swarm 7.138.0 → 7.138.2

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 (47) hide show
  1. package/dist/background/candidate-contract.d.ts +31 -0
  2. package/dist/background/pending-delegations.d.ts +7 -0
  3. package/dist/cli/{config-doctor-mb81xytp.js → config-doctor-hzb0bck9.js} +2 -2
  4. package/dist/cli/{curation-policy-qx23sc8b.js → curation-policy-aj37yvz8.js} +2 -2
  5. package/dist/cli/{curator-b9ascpbh.js → curator-jt6dza8y.js} +15 -15
  6. package/dist/cli/{curator-llm-factory-r47q1hbd.js → curator-llm-factory-m9f4h8t4.js} +15 -15
  7. package/dist/cli/{evidence-summary-service-1azhyeft.js → evidence-summary-service-6cttrkzw.js} +2 -2
  8. package/dist/cli/{guardrail-explain-ph1n3ryc.js → guardrail-explain-rks41rpx.js} +16 -16
  9. package/dist/cli/{guardrail-log-ghgqaw3w.js → guardrail-log-h39jxftx.js} +3 -3
  10. package/dist/cli/{hive-promoter-7x2zzhb7.js → hive-promoter-34y2x4vt.js} +15 -15
  11. package/dist/cli/{index-7wzyf4dr.js → index-49ygvqvm.js} +4 -4
  12. package/dist/cli/{index-sp7s1vk3.js → index-89dwrz8n.js} +712 -233
  13. package/dist/cli/{index-2vzgvscz.js → index-8emf08eb.js} +3 -3
  14. package/dist/cli/{index-9zf5gfca.js → index-bakfqtye.js} +1 -1
  15. package/dist/cli/{index-z55s5719.js → index-d01d4102.js} +2 -2
  16. package/dist/cli/{index-ca6qmxd8.js → index-da98750t.js} +1 -1
  17. package/dist/cli/{index-6nenabrm.js → index-exq36v6w.js} +1 -1
  18. package/dist/cli/{index-k5jrywpr.js → index-gprxjmm7.js} +6 -1
  19. package/dist/cli/{index-bf0j2wd2.js → index-gpxg7wx8.js} +2 -2
  20. package/dist/cli/{index-npgjk6vc.js → index-k6cq9wwh.js} +1 -1
  21. package/dist/cli/{index-0s4gr6qh.js → index-q344kj84.js} +2 -1
  22. package/dist/cli/{index-4xs9e82s.js → index-qg5smxv9.js} +3 -3
  23. package/dist/cli/{index-xz0zjrdr.js → index-qt3jjjw9.js} +1 -1
  24. package/dist/cli/{index-cdx3aqj5.js → index-r9pn41rj.js} +1 -1
  25. package/dist/cli/{index-ag7ppc40.js → index-s7ac0xrf.js} +17 -17
  26. package/dist/cli/{index-hmapndcz.js → index-xm07cqy2.js} +5 -5
  27. package/dist/cli/{index-fmh15wxb.js → index-y06a43sk.js} +1 -1
  28. package/dist/cli/{index-whf5ecbw.js → index-yxswbd1t.js} +1 -1
  29. package/dist/cli/{index-7kgfvt3h.js → index-ze4we97d.js} +1 -1
  30. package/dist/cli/index.js +15 -15
  31. package/dist/cli/{knowledge-escalator-pqskjp6y.js → knowledge-escalator-qmpfh5yz.js} +3 -3
  32. package/dist/cli/{knowledge-events-ahpxnqjd.js → knowledge-events-7neg8x0p.js} +1 -1
  33. package/dist/cli/{knowledge-store-27aynbeg.js → knowledge-store-a6d4625r.js} +1 -1
  34. package/dist/cli/{knowledge-validator-4saz53kh.js → knowledge-validator-q1qrdyvx.js} +4 -4
  35. package/dist/cli/{pending-delegations-ztb67amh.js → pending-delegations-7dbc0j2a.js} +1 -1
  36. package/dist/cli/{scan-cursor-ngykkd7f.js → scan-cursor-3tqshkb2.js} +2 -2
  37. package/dist/cli/{schema-p82ntfy9.js → schema-mtyaeh18.js} +1 -1
  38. package/dist/cli/{skill-generator-r19a1zje.js → skill-generator-t3m5qajm.js} +5 -5
  39. package/dist/cli/{worktree-collision-ownership-rc6pe17g.js → worktree-collision-ownership-hjrt5hx2.js} +1 -1
  40. package/dist/config/evidence-schema.d.ts +15 -0
  41. package/dist/hooks/pr-workflow-gate.d.ts +41 -1
  42. package/dist/index.js +41 -28
  43. package/dist/services/preflight-service.d.ts +2 -0
  44. package/dist/tools/dispatch-lanes.d.ts +7 -0
  45. package/dist/tools/pre-check-batch.d.ts +2 -0
  46. package/dist/tools/secretscan.d.ts +17 -0
  47. package/package.json +1 -1
@@ -16,13 +16,13 @@ import {
16
16
  } from "./index-rrxcfxrd.js";
17
17
  import {
18
18
  stableCanonicalStringify
19
- } from "./index-xz0zjrdr.js";
19
+ } from "./index-qt3jjjw9.js";
20
20
  import {
21
21
  detectStraySwarmDirs,
22
22
  readDoctorArtifact,
23
23
  removeStraySwarmDir,
24
24
  runConfigDoctor
25
- } from "./index-ca6qmxd8.js";
25
+ } from "./index-da98750t.js";
26
26
  import {
27
27
  MAX_TRANSIENT_RETRIES,
28
28
  archiveEvidence,
@@ -59,7 +59,7 @@ import {
59
59
  savePlan,
60
60
  transientBackoff,
61
61
  validateProjectRoot
62
- } from "./index-fmh15wxb.js";
62
+ } from "./index-y06a43sk.js";
63
63
  import {
64
64
  EvaluationTaskV1Schema,
65
65
  GateAuditManifestV1Schema,
@@ -114,15 +114,15 @@ import {
114
114
  retireSkill,
115
115
  selectCandidateEntries,
116
116
  syncBundledProjectSkillsIfMissingAsync
117
- } from "./index-4xs9e82s.js";
117
+ } from "./index-qg5smxv9.js";
118
118
  import {
119
119
  readRecentEscalations
120
- } from "./index-hmapndcz.js";
120
+ } from "./index-xm07cqy2.js";
121
121
  import {
122
122
  alreadyCuratedThisGeneration,
123
123
  claimNextScanBatch,
124
124
  resolveCohortId
125
- } from "./index-whf5ecbw.js";
125
+ } from "./index-yxswbd1t.js";
126
126
  import {
127
127
  CURATOR_INIT_PROMPT,
128
128
  CURATOR_PHASE_PROMPT,
@@ -175,11 +175,11 @@ import {
175
175
  validateActionableFields,
176
176
  validateLesson,
177
177
  validateSkillPath
178
- } from "./index-z55s5719.js";
178
+ } from "./index-d01d4102.js";
179
179
  import {
180
180
  authorizeCuration,
181
181
  buildConfigFingerprintInput
182
- } from "./index-7kgfvt3h.js";
182
+ } from "./index-ze4we97d.js";
183
183
  import {
184
184
  cohortConfigFingerprint
185
185
  } from "./index-rtry5xyf.js";
@@ -212,7 +212,7 @@ import {
212
212
  transactFile,
213
213
  transactKnowledge,
214
214
  wordBigrams
215
- } from "./index-7wzyf4dr.js";
215
+ } from "./index-49ygvqvm.js";
216
216
  import {
217
217
  MAX_EVENT_LOG_ENTRIES,
218
218
  _internals,
@@ -222,7 +222,7 @@ import {
222
222
  recordHiveKnowledgeEvent,
223
223
  recordKnowledgeEvent,
224
224
  resolveKnowledgeEventsPath
225
- } from "./index-cdx3aqj5.js";
225
+ } from "./index-r9pn41rj.js";
226
226
  import {
227
227
  resolveHiveDataDir,
228
228
  resolveHiveEventsPath,
@@ -247,10 +247,10 @@ import {
247
247
  init_loader,
248
248
  loadPluginConfig,
249
249
  loadPluginConfigWithMeta
250
- } from "./index-npgjk6vc.js";
250
+ } from "./index-k6cq9wwh.js";
251
251
  import {
252
252
  RetrospectiveEvidenceSchema
253
- } from "./index-k5jrywpr.js";
253
+ } from "./index-gprxjmm7.js";
254
254
  import {
255
255
  DEFAULT_SKILL_OPT_CONFIG,
256
256
  FALLBACK_MODELS_MAX,
@@ -263,7 +263,7 @@ import {
263
263
  package_default,
264
264
  resolveAutoReviewConfig,
265
265
  stripKnownSwarmPrefix
266
- } from "./index-6nenabrm.js";
266
+ } from "./index-exq36v6w.js";
267
267
  import {
268
268
  KNOWLEDGE_SCHEMA_VERSION,
269
269
  OUTCOME_BLOCK_THRESHOLD,
@@ -320,7 +320,7 @@ import {
320
320
  import {
321
321
  findByBatchId,
322
322
  readDelegations
323
- } from "./index-0s4gr6qh.js";
323
+ } from "./index-q344kj84.js";
324
324
  import {
325
325
  readSwarmFileAsync,
326
326
  safeHook,
@@ -477,6 +477,52 @@ function removeCandidateCodeFences(text) {
477
477
  return lines.join(`
478
478
  `);
479
479
  }
480
+ function declaredCanonicalFamilies(source) {
481
+ const families = [];
482
+ for (const line of source.split(/\r?\n/)) {
483
+ const family = candidateHeaderFamily(splitPipeFields(line).map((field) => field.trim()));
484
+ if (family && !families.includes(family))
485
+ families.push(family);
486
+ }
487
+ return families;
488
+ }
489
+ function normalizeCandidateArtifact(rawText, fallbackFamily) {
490
+ const text = removeCandidateCodeFences(rawText);
491
+ const header = selectCandidateHeader(text.split(/\r?\n/));
492
+ if (header?.markerBearing && header.family !== null) {
493
+ return { text, synthesizedHeader: false };
494
+ }
495
+ if (declaredCanonicalFamilies(text).length > 0) {
496
+ return { text, synthesizedHeader: false };
497
+ }
498
+ if (declaredCanonicalFamilies(rawText).some((family) => family !== fallbackFamily)) {
499
+ return { text, synthesizedHeader: false };
500
+ }
501
+ const lines = text.split(/\r?\n/);
502
+ const hasSalvageableRow = lines.some((line) => {
503
+ if (analyzeCandidateLine(line, fallbackFamily)?.valid === true)
504
+ return true;
505
+ const fields = splitPipeFields(line).map((field) => field.trim());
506
+ if (fields[0] !== CLEAN_MARKER)
507
+ return false;
508
+ return analyzeCleanFields(fields, fallbackFamily).valid;
509
+ });
510
+ if (!hasSalvageableRow)
511
+ return { text, synthesizedHeader: false };
512
+ const firstSalvageableIndex = lines.findIndex((line) => {
513
+ const marker = splitPipeFields(line)[0]?.trim();
514
+ return marker === CANDIDATE_MARKER || marker === CLEAN_MARKER;
515
+ });
516
+ return {
517
+ text: [
518
+ ...lines.slice(0, firstSalvageableIndex),
519
+ CANDIDATE_HEADERS[fallbackFamily],
520
+ ...lines.slice(firstSalvageableIndex)
521
+ ].join(`
522
+ `),
523
+ synthesizedHeader: true
524
+ };
525
+ }
480
526
  function analyzeCandidateLaneContext(context) {
481
527
  const issues = [];
482
528
  const baseBound = Boolean(context.expected_lane || context.expected_lanes);
@@ -687,6 +733,12 @@ function analyzeCleanFields(fields, family, expectedLane) {
687
733
  evidence
688
734
  };
689
735
  }
736
+ function analyzeCandidateLine(line, family) {
737
+ const fields = splitPipeFields(line).map((field) => field.trim());
738
+ if (fields[0] !== "[CANDIDATE]")
739
+ return null;
740
+ return analyzeCandidateFields(fields.slice(1), family);
741
+ }
690
742
 
691
743
  // src/background/candidate-parser.ts
692
744
  init_zod();
@@ -1276,7 +1328,7 @@ ${trimmed}`;
1276
1328
  if (cleanAttestation) {
1277
1329
  formatFamiliesDetected.add(cleanAttestation.row_format_family);
1278
1330
  }
1279
- const acceptedCandidates = cleanErrorCode ? [] : candidates;
1331
+ const acceptedCandidates = candidates;
1280
1332
  const parseErrors = parseErrorDetails.length;
1281
1333
  const envelope = buildInvocationEnvelope(input, flags, Array.from(formatFamiliesDetected), acceptedCandidates.length, parseErrors, malformedRows, cleanAttestation ? 1 : 0);
1282
1334
  const diagnostics = {
@@ -2811,6 +2863,9 @@ var _test_exports = {
2811
2863
  minimumConsolidatedLaneCover,
2812
2864
  analyzePrReviewBatchRecordIntegrity,
2813
2865
  MAX_COVER_UNIVERSE_BITS,
2866
+ extractCandidateIds,
2867
+ parseCanonicalCandidateRows,
2868
+ resolvePrReviewRowFamily,
2814
2869
  workflowGateStateRelativePath,
2815
2870
  workflowGateStateLockRelativePath,
2816
2871
  workflowCheckoutMutationLockRelativePath,
@@ -3138,13 +3193,13 @@ function derivePrReviewCandidateInventory(directory, state, ctx) {
3138
3193
  const recordOwnedLanes = record.ownedWorkflowLanes?.length ? record.ownedWorkflowLanes : record.workflowLane ? [record.workflowLane] : undefined;
3139
3194
  if (!artifact || !workflowArtifactHasContractMarker(directory, state, record, source.mode, record.workflowLane ?? source.workflowLane ?? source.laneId, undefined, ctx.revisionDigest, recordOwnedLanes))
3140
3195
  continue;
3141
- if (source.workflowLane && (!recordOwnedLanes?.includes(source.workflowLane) || !prReviewDiscoveryArtifactCoversLane(artifact.text, source.workflowLane, recordOwnedLanes)))
3196
+ if (source.workflowLane && (!recordOwnedLanes?.includes(source.workflowLane) || !prReviewDiscoveryArtifactCoversLane(artifact.text, source.workflowLane, recordOwnedLanes, source.mode)))
3142
3197
  continue;
3143
3198
  resolvedArtifact = true;
3144
3199
  const laneKey = `${source.batchId}\x00${source.laneId}`;
3145
3200
  if (!extractedLaneKeys.has(laneKey)) {
3146
3201
  extractedLaneKeys.add(laneKey);
3147
- candidateIds.push(...extractCandidateIds(artifact.text, source.mode === "swarm-pr-review:micro" ? "micro_lane" : "base_explorer", source.creditedLanes));
3202
+ candidateIds.push(...extractCandidateIds(artifact.text, resolvePrReviewRowFamily(source.workflowLane, source.mode), source.creditedLanes));
3148
3203
  }
3149
3204
  }
3150
3205
  if (source.mode === "swarm-pr-review:micro" && !resolvedArtifact) {
@@ -3164,11 +3219,19 @@ function appendBoundedCandidateIssue(issues, message) {
3164
3219
  issues.push("additional malformed candidate diagnostics omitted");
3165
3220
  }
3166
3221
  }
3222
+ function resolvePrReviewRowFamily(workflowLane, mode) {
3223
+ if (mode === "swarm-pr-review:base")
3224
+ return "base_explorer";
3225
+ if (mode === "swarm-pr-review:micro" || mode === "swarm-pr-review:council") {
3226
+ return "micro_lane";
3227
+ }
3228
+ return workflowLane !== undefined && PR_REVIEW_BASE_DIMENSION_IDS.includes(workflowLane) ? "base_explorer" : "micro_lane";
3229
+ }
3167
3230
  function parseCanonicalCandidateRows(text, fallbackFamily) {
3168
3231
  const rows = [];
3169
3232
  const issues = [];
3170
3233
  let headerFamily = null;
3171
- for (const [index, line] of removeCandidateCodeFences(text).split(/\r?\n/).entries()) {
3234
+ for (const [index, line] of normalizeCandidateArtifact(text, fallbackFamily).text.split(/\r?\n/).entries()) {
3172
3235
  const fields = splitPipeFields(line).map((field) => field.trim());
3173
3236
  const detectedHeaderFamily = candidateHeaderFamily(fields);
3174
3237
  if (detectedHeaderFamily) {
@@ -3699,7 +3762,7 @@ function workflowArtifactHasContractMarker(directory, state, record, expectedMod
3699
3762
  const coveredLanes = ownedWorkflowLanes?.length ? ownedWorkflowLanes : [expectedWorkflowLane];
3700
3763
  const coverageAnalyses = coveredLanes.map((coveredLane) => ({
3701
3764
  coveredLane,
3702
- analysis: analyzePrReviewDiscoveryArtifact(artifact.text, coveredLane, coveredLanes)
3765
+ analysis: analyzePrReviewDiscoveryArtifact(artifact.text, coveredLane, coveredLanes, expectedMode)
3703
3766
  }));
3704
3767
  if (coverageAnalyses.some(({ analysis }) => !analysis.covered)) {
3705
3768
  for (const { coveredLane, analysis } of coverageAnalyses) {
@@ -3715,7 +3778,7 @@ function workflowArtifactHasContractMarker(directory, state, record, expectedMod
3715
3778
  if (coveredLanes.length > 1) {
3716
3779
  const seenEvidence = new Map;
3717
3780
  for (const coveredLane of coveredLanes) {
3718
- const evidence2 = extractLaneCoverageEvidenceText(artifact.text, coveredLane, coveredLanes);
3781
+ const evidence2 = extractLaneCoverageEvidenceText(artifact.text, coveredLane, coveredLanes, expectedMode);
3719
3782
  if (evidence2 === null)
3720
3783
  continue;
3721
3784
  const priorLane = seenEvidence.get(evidence2);
@@ -3726,10 +3789,12 @@ function workflowArtifactHasContractMarker(directory, state, record, expectedMod
3726
3789
  }
3727
3790
  return true;
3728
3791
  }
3729
- function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane]) {
3730
- const fallbackFamily = PR_REVIEW_BASE_DIMENSION_IDS.includes(expectedWorkflowLane) ? "base_explorer" : "micro_lane";
3792
+ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane], mode) {
3793
+ const fallbackFamily = resolvePrReviewRowFamily(expectedWorkflowLane, mode);
3731
3794
  const issues = [];
3732
- const canonicalText = removeCandidateCodeFences(text);
3795
+ const normalized = normalizeCandidateArtifact(text, fallbackFamily);
3796
+ const canonicalText = normalized.text;
3797
+ const salvaged = normalized.synthesizedHeader;
3733
3798
  const header = selectCandidateHeader(canonicalText.split(/\r?\n/));
3734
3799
  if (header === null || !header.markerBearing || header.family !== fallbackFamily) {
3735
3800
  appendBoundedCandidateIssue(issues, `field header: expected one exact canonical ${fallbackFamily} [CANDIDATE] header before discovery rows`);
@@ -3737,6 +3802,7 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
3737
3802
  covered: false,
3738
3803
  evidence: null,
3739
3804
  issues,
3805
+ salvaged,
3740
3806
  failurePredicate: "discovery.header"
3741
3807
  };
3742
3808
  }
@@ -3771,11 +3837,15 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
3771
3837
  for (const detail of parsed.diagnostics.parse_error_details) {
3772
3838
  appendBoundedCandidateIssue(issues, `row ${detail.row_index + 1} field ${detail.field}: ${detail.message}`);
3773
3839
  }
3774
- if (parsed.error || parsed.diagnostics.parse_error_details.length > 0) {
3840
+ const hasParseFailure = Boolean(parsed.error) || parsed.diagnostics.parse_error_details.length > 0;
3841
+ const extractedCandidateIds = parseCanonicalCandidateRows(canonicalText, fallbackFamily).rows.map((row) => row.candidateId);
3842
+ const hasDuplicateCandidateId = new Set(extractedCandidateIds).size !== extractedCandidateIds.length;
3843
+ if (hasDuplicateCandidateId) {
3775
3844
  return {
3776
3845
  covered: false,
3777
3846
  evidence: null,
3778
3847
  issues,
3848
+ salvaged,
3779
3849
  failurePredicate: "discovery.row"
3780
3850
  };
3781
3851
  }
@@ -3792,7 +3862,8 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
3792
3862
  matchingCandidate.impact_context ?? matchingCandidate.invariant_violated,
3793
3863
  matchingCandidate.confidence
3794
3864
  ].join("\x00"),
3795
- issues
3865
+ issues,
3866
+ salvaged
3796
3867
  };
3797
3868
  }
3798
3869
  const clean = parsed.clean_attestation;
@@ -3800,14 +3871,16 @@ function analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkf
3800
3871
  return {
3801
3872
  covered: true,
3802
3873
  evidence: `${clean.coverage_scope}\x00${clean.evidence}`,
3803
- issues
3874
+ issues,
3875
+ salvaged
3804
3876
  };
3805
3877
  }
3806
3878
  return {
3807
3879
  covered: false,
3808
3880
  evidence: null,
3809
3881
  issues,
3810
- failurePredicate: "discovery.coverage"
3882
+ salvaged,
3883
+ failurePredicate: hasParseFailure ? "discovery.row" : "discovery.coverage"
3811
3884
  };
3812
3885
  }
3813
3886
  function validatePrReviewDiscoveryLaneCompletion(input) {
@@ -3845,8 +3918,18 @@ function validatePrReviewDiscoveryLaneCompletion(input) {
3845
3918
  const artifact = input.artifact;
3846
3919
  const analyses = ownedWorkflowLanes.map((workflowLane) => ({
3847
3920
  workflowLane,
3848
- analysis: analyzePrReviewDiscoveryArtifact(artifact.text, workflowLane, ownedWorkflowLanes)
3921
+ analysis: analyzePrReviewDiscoveryArtifact(artifact.text, workflowLane, ownedWorkflowLanes, input.expected.mode)
3849
3922
  }));
3923
+ const salvagedLanes = [];
3924
+ for (const { workflowLane, analysis } of analyses) {
3925
+ if (!analysis.covered)
3926
+ continue;
3927
+ if (!analysis.salvaged && analysis.issues.length === 0)
3928
+ continue;
3929
+ salvagedLanes.push(workflowLane);
3930
+ const reason = analysis.salvaged ? `canonical ${resolvePrReviewRowFamily(workflowLane, input.expected.mode)} header was absent and was synthesized from valid marker rows` : "one or more rows were dropped as malformed or out-of-ownership";
3931
+ warn(`[pr-workflow-gate] discovery artifact salvaged: batch=${input.record.batchId ?? "(missing)"} lane=${input.record.laneId ?? "(missing)"} workflow_lane=${workflowLane} \u2014 ${reason}; retained coverage from the valid rows. Dropped-row diagnostics: ${analysis.issues.join("; ") || "(none)"}`);
3932
+ }
3850
3933
  const failedCoverage = analyses.find(({ analysis }) => !analysis.covered);
3851
3934
  if (failedCoverage) {
3852
3935
  return failedLaneValidation(failedCoverage.analysis.failurePredicate ?? "discovery.coverage", `one valid ${input.expected.mode === "swarm-pr-review:base" ? "base_explorer" : "micro_lane"} [CANDIDATE] row or [CLEAN] attestation for ${failedCoverage.workflowLane}`, failedCoverage.analysis.issues.join("; ") || "no matching lane row");
@@ -3863,13 +3946,13 @@ function validatePrReviewDiscoveryLaneCompletion(input) {
3863
3946
  seenEvidence.set(analysis.evidence, workflowLane);
3864
3947
  }
3865
3948
  }
3866
- return { ok: true };
3949
+ return salvagedLanes.length > 0 ? { ok: true, salvaged: salvagedLanes } : { ok: true };
3867
3950
  }
3868
- function prReviewDiscoveryArtifactCoversLane(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane]) {
3869
- return analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes).covered;
3951
+ function prReviewDiscoveryArtifactCoversLane(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane], mode) {
3952
+ return analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes, mode).covered;
3870
3953
  }
3871
- function extractLaneCoverageEvidenceText(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane]) {
3872
- return analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes).evidence;
3954
+ function extractLaneCoverageEvidenceText(text, expectedWorkflowLane, ownedWorkflowLanes = [expectedWorkflowLane], mode) {
3955
+ return analyzePrReviewDiscoveryArtifact(text, expectedWorkflowLane, ownedWorkflowLanes, mode).evidence;
3873
3956
  }
3874
3957
  var REVIEWER_CLASSIFICATIONS = new Set([
3875
3958
  "CONFIRMED",
@@ -18784,7 +18867,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
18784
18867
  };
18785
18868
  }
18786
18869
  async function sweepActiveNearDuplicates(directory, options = {}) {
18787
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
18870
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-8emf08eb.js");
18788
18871
  const { config } = loadPluginConfigWithMeta2(directory);
18789
18872
  const sweepConfig = config.learning?.dedup_sweep;
18790
18873
  if (sweepConfig?.enabled === false)
@@ -20377,7 +20460,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
20377
20460
  }
20378
20461
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
20379
20462
  try {
20380
- const skillModule = await import("./skill-generator-r19a1zje.js");
20463
+ const skillModule = await import("./skill-generator-t3m5qajm.js");
20381
20464
  for (const cand of skillCandidates) {
20382
20465
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
20383
20466
  continue;
@@ -20837,7 +20920,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
20837
20920
  } catch {}
20838
20921
  }
20839
20922
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
20840
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-pqskjp6y.js");
20923
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-qmpfh5yz.js");
20841
20924
  for (const { id } of contradictedEntries) {
20842
20925
  try {
20843
20926
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -21656,7 +21739,7 @@ async function runCuratorPostMortem(directory, options = {}) {
21656
21739
  warnings.push("Failed to collect knowledge summary.");
21657
21740
  }
21658
21741
  try {
21659
- const { getScanStatus } = await import("./scan-cursor-ngykkd7f.js");
21742
+ const { getScanStatus } = await import("./scan-cursor-3tqshkb2.js");
21660
21743
  const scanStatus = await getScanStatus(directory);
21661
21744
  if (scanStatus.remaining_estimate > 0) {
21662
21745
  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.`);
@@ -21828,9 +21911,9 @@ var _internals24 = {
21828
21911
  verifyPostMortemKnowledgeActions,
21829
21912
  repairPostMortemActions,
21830
21913
  loadDefaultKnowledgeConfig: async (directory) => {
21831
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
21914
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-mtyaeh18.js");
21832
21915
  try {
21833
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
21916
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-8emf08eb.js");
21834
21917
  const loaded = loadPluginConfigWithMeta2(directory);
21835
21918
  return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
21836
21919
  } catch {
@@ -21838,11 +21921,11 @@ var _internals24 = {
21838
21921
  }
21839
21922
  },
21840
21923
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
21841
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-b9ascpbh.js");
21924
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-jt6dza8y.js");
21842
21925
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
21843
21926
  },
21844
21927
  checkHivePromotions: async (entries, knowledgeConfig, directory) => {
21845
- const { checkHivePromotions } = await import("./hive-promoter-7x2zzhb7.js");
21928
+ const { checkHivePromotions } = await import("./hive-promoter-34y2x4vt.js");
21846
21929
  return checkHivePromotions(entries, knowledgeConfig, directory);
21847
21930
  },
21848
21931
  applyProposalTriage: async (directory, triage) => {
@@ -21851,7 +21934,7 @@ var _internals24 = {
21851
21934
  activateProposal,
21852
21935
  listSkills: listSkills2,
21853
21936
  sanitizeSlug
21854
- } = await import("./skill-generator-r19a1zje.js");
21937
+ } = await import("./skill-generator-t3m5qajm.js");
21855
21938
  const result = {
21856
21939
  approved: [],
21857
21940
  rejected: [],
@@ -24478,9 +24561,9 @@ function hashContent(content) {
24478
24561
  return createHash9("sha1").update(content).digest("hex");
24479
24562
  }
24480
24563
  async function loadConfigForPolicyCurator(directory) {
24481
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
24564
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-mtyaeh18.js");
24482
24565
  try {
24483
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
24566
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-8emf08eb.js");
24484
24567
  const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
24485
24568
  return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
24486
24569
  } catch {
@@ -28440,7 +28523,7 @@ async function runFinalizeStage(ctx) {
28440
28523
  }
28441
28524
  }
28442
28525
  try {
28443
- const { CuratorConfigSchema: CCS } = await import("./schema-p82ntfy9.js");
28526
+ const { CuratorConfigSchema: CCS } = await import("./schema-mtyaeh18.js");
28444
28527
  const { config: pmLoadedConfig } = _internals36.loadPluginConfigWithMeta(ctx.directory);
28445
28528
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
28446
28529
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -30711,9 +30794,9 @@ var _internals41 = {
30711
30794
  loadPluginConfigWithMeta,
30712
30795
  loadCuratorDeps: async () => {
30713
30796
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
30714
- import("./schema-p82ntfy9.js"),
30715
- import("./curator-b9ascpbh.js"),
30716
- import("./curator-llm-factory-r47q1hbd.js")
30797
+ import("./schema-mtyaeh18.js"),
30798
+ import("./curator-jt6dza8y.js"),
30799
+ import("./curator-llm-factory-m9f4h8t4.js")
30717
30800
  ]);
30718
30801
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
30719
30802
  }
@@ -31618,7 +31701,7 @@ async function computeCurationDiagnostics(directory, entries) {
31618
31701
  let scanCompleted = false;
31619
31702
  let scanRemaining = 0;
31620
31703
  try {
31621
- const { getScanStatus } = await import("./scan-cursor-ngykkd7f.js");
31704
+ const { getScanStatus } = await import("./scan-cursor-3tqshkb2.js");
31622
31705
  const status = await getScanStatus(directory);
31623
31706
  scanGeneration = status.generation;
31624
31707
  scanCompleted = status.completed;
@@ -31628,17 +31711,17 @@ async function computeCurationDiagnostics(directory, entries) {
31628
31711
  try {
31629
31712
  const { isLinked: isLinked2 } = await import("./knowledge-link-xc6yhxmz.js");
31630
31713
  if (isLinked2(directory)) {
31631
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
31714
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-mtyaeh18.js");
31632
31715
  let config;
31633
31716
  try {
31634
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
31717
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-8emf08eb.js");
31635
31718
  const loaded = loadPluginConfigWithMeta2(directory);
31636
31719
  config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
31637
31720
  } catch {
31638
31721
  config = KnowledgeConfigSchema2.parse({});
31639
31722
  }
31640
31723
  const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
31641
- const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-qx23sc8b.js");
31724
+ const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-aj37yvz8.js");
31642
31725
  const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
31643
31726
  const cohortFp = await readCohortConfigFingerprint(directory);
31644
31727
  configMatch = cohortFp === null ? null : currentFp === cohortFp;
@@ -32849,7 +32932,7 @@ async function handleDoctorCommand(directory, args) {
32849
32932
  const result = runConfigDoctor(config, directory);
32850
32933
  let output;
32851
32934
  if (enableAutoFix && result.hasAutoFixableIssues) {
32852
- const { runConfigDoctorWithFixes } = await import("./config-doctor-mb81xytp.js");
32935
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-hzb0bck9.js");
32853
32936
  const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
32854
32937
  applyLossy: true
32855
32938
  });
@@ -33686,7 +33769,7 @@ async function handleEvidenceCommand(directory, args) {
33686
33769
  return formatTaskEvidenceMarkdown(evidenceData);
33687
33770
  }
33688
33771
  async function handleEvidenceSummaryCommand(directory) {
33689
- const { buildEvidenceSummary } = await import("./evidence-summary-service-1azhyeft.js");
33772
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-6cttrkzw.js");
33690
33773
  const artifact = await buildEvidenceSummary(directory);
33691
33774
  if (!artifact) {
33692
33775
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -43362,10 +43445,14 @@ import * as path77 from "path";
43362
43445
  var MAX_FILE_PATH_LENGTH = 500;
43363
43446
  var MAX_FILE_SIZE_BYTES3 = 512 * 1024;
43364
43447
  var MAX_FILES_SCANNED2 = 1000;
43448
+ var MAX_EXPLICIT_FILES_SCANNED = 100;
43365
43449
  var MAX_FINDINGS2 = 100;
43366
43450
  var MAX_OUTPUT_BYTES3 = 512000;
43367
- var MAX_LINE_LENGTH = 1e4;
43368
- var MAX_CONTENT_BYTES = 50 * 1024;
43451
+ var MAX_CONTEXT_CHARS = 1000;
43452
+ var MAX_LONG_LINE_CHARS = 1e4;
43453
+ var MAX_INCOMPLETE_PATHS = 25;
43454
+ var MAX_DISCOVERY_DIRECTORIES = 1e4;
43455
+ var SCAN_TIME_BUDGET_MS = 55000;
43369
43456
  var BINARY_SIGNATURES = [
43370
43457
  0,
43371
43458
  2303741511,
@@ -43413,6 +43500,7 @@ var DEFAULT_EXCLUDE_EXTENSIONS = new Set([
43413
43500
  ".gz",
43414
43501
  ".rar",
43415
43502
  ".7z",
43503
+ ".wasm",
43416
43504
  ".exe",
43417
43505
  ".dll",
43418
43506
  ".so",
@@ -43651,9 +43739,6 @@ function isBinaryFile(filePath, buffer) {
43651
43739
  }
43652
43740
  function scanLineForSecrets(line, _lineNum) {
43653
43741
  const results = [];
43654
- if (line.length > MAX_LINE_LENGTH) {
43655
- return results;
43656
- }
43657
43742
  for (const pattern of SECRET_PATTERNS2) {
43658
43743
  pattern.regex.lastIndex = 0;
43659
43744
  for (let match = pattern.regex.exec(line);match !== null; match = pattern.regex.exec(line)) {
@@ -43672,8 +43757,8 @@ function scanLineForSecrets(line, _lineNum) {
43672
43757
  }
43673
43758
  }
43674
43759
  }
43675
- const valueMatch = line.match(/(?:secret|key|token|password|cred|credential)\s*[=:]\s*["']?([a-zA-Z0-9+/=_-]{20,100})["']?/i);
43676
- if (valueMatch && isHighEntropyString(valueMatch[1])) {
43760
+ const valueMatch = line.match(/((?:secret|key|token|password|cred|credential))\s*[=:]\s*["']?([a-zA-Z0-9+/=_-]{20,100})["']?/i);
43761
+ if (valueMatch && isHighEntropyString(valueMatch[2])) {
43677
43762
  const matchStart = valueMatch.index || 0;
43678
43763
  const matchEnd = matchStart + valueMatch[0].length;
43679
43764
  const hasOverlap = results.some((r) => !(r.matchEnd <= matchStart || r.matchStart >= matchEnd));
@@ -43682,7 +43767,7 @@ function scanLineForSecrets(line, _lineNum) {
43682
43767
  type: "high_entropy",
43683
43768
  confidence: "low",
43684
43769
  severity: "medium",
43685
- redacted: `${valueMatch[0].split("=")[0].trim()}=[HIGH_ENTROPY]`,
43770
+ redacted: `${valueMatch[1]}=[HIGH_ENTROPY]`,
43686
43771
  matchStart,
43687
43772
  matchEnd
43688
43773
  });
@@ -43692,42 +43777,95 @@ function scanLineForSecrets(line, _lineNum) {
43692
43777
  }
43693
43778
  function createRedactedContext(line, findings) {
43694
43779
  if (findings.length === 0)
43695
- return line;
43780
+ return "";
43696
43781
  const sorted = [...findings].sort((a, b) => a.matchStart - b.matchStart);
43697
43782
  let result = "";
43698
43783
  let lastEnd = 0;
43699
43784
  for (const finding of sorted) {
43700
- result += line.slice(lastEnd, finding.matchStart);
43701
- result += finding.redacted;
43702
- lastEnd = finding.matchEnd;
43785
+ if (finding.matchEnd <= lastEnd)
43786
+ continue;
43787
+ if (finding.matchStart >= lastEnd) {
43788
+ result += line.slice(lastEnd, finding.matchStart);
43789
+ result += "[REDACTED]";
43790
+ }
43791
+ lastEnd = Math.max(lastEnd, finding.matchEnd);
43703
43792
  }
43704
43793
  result += line.slice(lastEnd);
43705
- return result;
43794
+ if (result.length <= MAX_CONTEXT_CHARS)
43795
+ return result;
43796
+ const truncationMarker = "...[context truncated]...";
43797
+ const edgeLength = Math.floor((MAX_CONTEXT_CHARS - truncationMarker.length) / 2);
43798
+ const remaining = MAX_CONTEXT_CHARS - truncationMarker.length - edgeLength;
43799
+ return `${result.slice(0, edgeLength)}${truncationMarker}${result.slice(-remaining)}`;
43706
43800
  }
43707
43801
  var O_NOFOLLOW = process.platform !== "win32" ? fs31.constants.O_NOFOLLOW : undefined;
43708
- function scanFileForSecrets(filePath) {
43802
+ function readBoundedFile(filePath) {
43803
+ let fd;
43804
+ try {
43805
+ const flags = fs31.constants.O_RDONLY | (O_NOFOLLOW !== undefined ? O_NOFOLLOW : 0);
43806
+ fd = _internals64.openFile(filePath, flags);
43807
+ } catch {
43808
+ return { status: "incomplete", reason: "read_error" };
43809
+ }
43810
+ let outcome;
43811
+ try {
43812
+ const stat6 = _internals64.fstatFile(fd);
43813
+ const initialSize = stat6.size;
43814
+ if (!stat6.isFile()) {
43815
+ outcome = { status: "incomplete", reason: "non_file" };
43816
+ } else {
43817
+ const bufferLength = Math.min(initialSize + 1, MAX_FILE_SIZE_BYTES3 + 1);
43818
+ const buffer = Buffer.allocUnsafe(bufferLength);
43819
+ let bytesRead = 0;
43820
+ while (bytesRead < buffer.length) {
43821
+ const count = _internals64.readFileChunk(fd, buffer, bytesRead, buffer.length - bytesRead, null);
43822
+ if (count === 0)
43823
+ break;
43824
+ bytesRead += count;
43825
+ }
43826
+ const postStat = _internals64.fstatFile(fd);
43827
+ if (postStat.size !== initialSize) {
43828
+ outcome = {
43829
+ status: "incomplete",
43830
+ reason: postStat.size > MAX_FILE_SIZE_BYTES3 ? "oversized" : "read_error"
43831
+ };
43832
+ } else {
43833
+ outcome = {
43834
+ status: "ok",
43835
+ buffer: buffer.subarray(0, bytesRead),
43836
+ truncated: initialSize > MAX_FILE_SIZE_BYTES3
43837
+ };
43838
+ }
43839
+ }
43840
+ } catch {
43841
+ outcome = { status: "incomplete", reason: "read_error" };
43842
+ }
43843
+ try {
43844
+ _internals64.closeFile(fd);
43845
+ } catch {
43846
+ return { status: "incomplete", reason: "read_error" };
43847
+ }
43848
+ return outcome;
43849
+ }
43850
+ function scanFileForSecrets(filePath, mode) {
43709
43851
  const findings = [];
43710
43852
  try {
43711
43853
  const lstat3 = fs31.lstatSync(filePath);
43712
43854
  if (lstat3.isSymbolicLink()) {
43713
- return findings;
43855
+ return mode === "explicit" ? { status: "incomplete", reason: "symlink" } : { status: "skipped", reason: "symlink" };
43714
43856
  }
43715
- if (lstat3.size > MAX_FILE_SIZE_BYTES3) {
43716
- return findings;
43717
- }
43718
- let buffer;
43719
- if (O_NOFOLLOW !== undefined) {
43720
- const fd = fs31.openSync(filePath, "r", O_NOFOLLOW);
43721
- try {
43722
- buffer = fs31.readFileSync(fd);
43723
- } finally {
43724
- fs31.closeSync(fd);
43725
- }
43726
- } else {
43727
- buffer = fs31.readFileSync(filePath);
43857
+ if (!lstat3.isFile()) {
43858
+ return { status: "incomplete", reason: "non_file" };
43728
43859
  }
43860
+ const readOutcome = readBoundedFile(filePath);
43861
+ if (readOutcome.status === "incomplete")
43862
+ return readOutcome;
43863
+ const { buffer, truncated } = readOutcome;
43729
43864
  if (isBinaryFile(filePath, buffer)) {
43730
- return findings;
43865
+ return { status: "skipped", reason: "binary" };
43866
+ }
43867
+ if (truncated) {
43868
+ return { status: "incomplete", reason: "oversized" };
43731
43869
  }
43732
43870
  let content;
43733
43871
  if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
@@ -43736,13 +43874,16 @@ function scanFileForSecrets(filePath) {
43736
43874
  content = buffer.toString("utf-8");
43737
43875
  }
43738
43876
  if (content.includes("\x00")) {
43739
- return findings;
43877
+ return { status: "skipped", reason: "binary" };
43740
43878
  }
43741
- const scanContent = content.slice(0, MAX_CONTENT_BYTES);
43742
- const lines = scanContent.split(`
43879
+ const lines = content.split(`
43743
43880
  `);
43744
43881
  for (let i = 0;i < lines.length; i++) {
43882
+ if (mode === "standalone" && lines[i].length > MAX_LONG_LINE_CHARS) {
43883
+ continue;
43884
+ }
43745
43885
  const lineResults = scanLineForSecrets(lines[i], i + 1);
43886
+ const context = createRedactedContext(lines[i], lineResults);
43746
43887
  for (const result of lineResults) {
43747
43888
  findings.push({
43748
43889
  path: filePath,
@@ -43751,12 +43892,43 @@ function scanFileForSecrets(filePath) {
43751
43892
  confidence: result.confidence,
43752
43893
  severity: result.severity,
43753
43894
  redacted: result.redacted,
43754
- context: createRedactedContext(lines[i], [result])
43895
+ context
43755
43896
  });
43756
43897
  }
43757
43898
  }
43758
- } catch {}
43759
- return findings;
43899
+ return { status: "scanned", findings };
43900
+ } catch {
43901
+ return { status: "incomplete", reason: "read_error" };
43902
+ }
43903
+ }
43904
+ function assertNever(value) {
43905
+ throw new Error(`Unhandled secretscan outcome: ${String(value)}`);
43906
+ }
43907
+ async function yieldToEventLoop() {
43908
+ await new Promise((resolve24) => setImmediate(resolve24));
43909
+ }
43910
+ function serializeSecretscanResult(result) {
43911
+ let findings = result.findings;
43912
+ let candidate = result;
43913
+ let json = JSON.stringify(candidate, null, 2);
43914
+ while (Buffer.byteLength(json, "utf8") > MAX_OUTPUT_BYTES3 && findings.length > 0) {
43915
+ findings = findings.slice(0, Math.floor(findings.length / 2));
43916
+ candidate = {
43917
+ ...result,
43918
+ findings,
43919
+ message: "Output truncated due to size limits."
43920
+ };
43921
+ json = JSON.stringify(candidate, null, 2);
43922
+ }
43923
+ if (Buffer.byteLength(json, "utf8") > MAX_OUTPUT_BYTES3) {
43924
+ candidate = {
43925
+ ...result,
43926
+ findings: [],
43927
+ message: "Output truncated due to size limits."
43928
+ };
43929
+ json = JSON.stringify(candidate, null, 2);
43930
+ }
43931
+ return json;
43760
43932
  }
43761
43933
  function isSymlinkLoop(realPath, visited) {
43762
43934
  if (visited.has(realPath)) {
@@ -43770,75 +43942,168 @@ function isPathWithinScope(realPath, scanDir) {
43770
43942
  const resolvedRealPath = path77.resolve(realPath);
43771
43943
  return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path77.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
43772
43944
  }
43773
- function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
43774
- skippedDirs: 0,
43775
- skippedFiles: 0,
43776
- fileErrors: 0,
43777
- symlinkSkipped: 0
43778
- }) {
43779
- const files = [];
43780
- let entries;
43781
- try {
43782
- entries = fs31.readdirSync(dir);
43783
- } catch {
43784
- stats.fileErrors++;
43785
- return files;
43786
- }
43787
- entries.sort((a, b) => {
43788
- const aLower = a.toLowerCase();
43789
- const bLower = b.toLowerCase();
43790
- if (aLower < bLower)
43791
- return -1;
43792
- if (aLower > bLower)
43793
- return 1;
43794
- return a.localeCompare(b);
43795
- });
43796
- for (const entry of entries) {
43797
- const fullPath = path77.join(dir, entry);
43798
- const relPath = path77.relative(scanDir, fullPath).replace(/\\/g, "/");
43799
- if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
43800
- stats.skippedDirs++;
43801
- continue;
43945
+ var DISCOVERY_YIELD_INTERVAL = 100;
43946
+ function toSafeRelativePath(rootDir, targetPath) {
43947
+ const relative10 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
43948
+ if (!relative10 || relative10 === ".")
43949
+ return ".";
43950
+ if (relative10.startsWith("..") || path77.isAbsolute(relative10)) {
43951
+ const basename15 = path77.basename(targetPath).replace(/\\/g, "/");
43952
+ return basename15 || ".";
43953
+ }
43954
+ return relative10;
43955
+ }
43956
+ function recordIncompletePath(target, rootDir, targetPath, reason) {
43957
+ if (target.length >= MAX_INCOMPLETE_PATHS) {
43958
+ target[MAX_INCOMPLETE_PATHS - 1] = { path: ".", reason: "truncated" };
43959
+ return;
43960
+ }
43961
+ target.push({ path: toSafeRelativePath(rootDir, targetPath), reason });
43962
+ }
43963
+ function compareFilePaths(a, b) {
43964
+ const aLower = a.toLowerCase();
43965
+ const bLower = b.toLowerCase();
43966
+ if (aLower < bLower)
43967
+ return -1;
43968
+ if (aLower > bLower)
43969
+ return 1;
43970
+ if (a < b)
43971
+ return -1;
43972
+ if (a > b)
43973
+ return 1;
43974
+ return 0;
43975
+ }
43976
+ function addBoundedCandidate(state, filePath) {
43977
+ state.totalCandidates++;
43978
+ const heap = state.files;
43979
+ if (heap.length < MAX_FILES_SCANNED2) {
43980
+ heap.push(filePath);
43981
+ let index2 = heap.length - 1;
43982
+ while (index2 > 0) {
43983
+ const parent = Math.floor((index2 - 1) / 2);
43984
+ if (compareFilePaths(heap[parent], heap[index2]) >= 0)
43985
+ break;
43986
+ [heap[parent], heap[index2]] = [heap[index2], heap[parent]];
43987
+ index2 = parent;
43988
+ }
43989
+ return;
43990
+ }
43991
+ if (compareFilePaths(filePath, heap[0]) >= 0)
43992
+ return;
43993
+ heap[0] = filePath;
43994
+ let index = 0;
43995
+ while (true) {
43996
+ const left = index * 2 + 1;
43997
+ const right = left + 1;
43998
+ let largest = index;
43999
+ if (left < heap.length && compareFilePaths(heap[left], heap[largest]) > 0) {
44000
+ largest = left;
43802
44001
  }
43803
- let lstat3;
44002
+ if (right < heap.length && compareFilePaths(heap[right], heap[largest]) > 0) {
44003
+ largest = right;
44004
+ }
44005
+ if (largest === index)
44006
+ break;
44007
+ [heap[index], heap[largest]] = [heap[largest], heap[index]];
44008
+ index = largest;
44009
+ }
44010
+ }
44011
+ async function collectScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats, state) {
44012
+ const pending = [dir];
44013
+ while (pending.length > 0) {
44014
+ if (state.deadlineExceeded || state.cleanupFailed)
44015
+ return;
44016
+ const currentDir = pending.pop();
44017
+ if (!currentDir)
44018
+ continue;
44019
+ let directory;
43804
44020
  try {
43805
- lstat3 = fs31.lstatSync(fullPath);
44021
+ directory = fs31.opendirSync(currentDir);
43806
44022
  } catch {
43807
44023
  stats.fileErrors++;
44024
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "read_error");
43808
44025
  continue;
43809
44026
  }
43810
- if (lstat3.isSymbolicLink()) {
43811
- stats.symlinkSkipped++;
43812
- continue;
43813
- }
43814
- if (lstat3.isDirectory()) {
43815
- let realPath;
43816
- try {
43817
- realPath = fs31.realpathSync(fullPath);
43818
- } catch {
43819
- stats.fileErrors++;
43820
- continue;
43821
- }
43822
- if (isSymlinkLoop(realPath, visited)) {
43823
- stats.symlinkSkipped++;
43824
- continue;
43825
- }
43826
- if (!isPathWithinScope(realPath, scanDir)) {
43827
- stats.symlinkSkipped++;
43828
- continue;
44027
+ let stoppedEarly = false;
44028
+ try {
44029
+ while (true) {
44030
+ if (state.entriesSinceYield >= DISCOVERY_YIELD_INTERVAL) {
44031
+ state.entriesSinceYield = 0;
44032
+ await _internals64.yieldToEventLoop();
44033
+ if (_internals64.now() >= state.deadline) {
44034
+ state.deadlineExceeded = true;
44035
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "deadline");
44036
+ stoppedEarly = true;
44037
+ break;
44038
+ }
44039
+ }
44040
+ const entry = directory.readSync();
44041
+ if (!entry)
44042
+ break;
44043
+ state.entriesSinceYield++;
44044
+ const fullPath = path77.join(currentDir, entry.name);
44045
+ const relPath = path77.relative(scanDir, fullPath).replace(/\\/g, "/");
44046
+ if (isExcluded(entry.name, relPath, excludeExact, excludeGlobs)) {
44047
+ stats.skippedDirs++;
44048
+ continue;
44049
+ }
44050
+ let lstat3;
44051
+ try {
44052
+ lstat3 = fs31.lstatSync(fullPath);
44053
+ } catch {
44054
+ stats.fileErrors++;
44055
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "read_error");
44056
+ continue;
44057
+ }
44058
+ if (lstat3.isSymbolicLink()) {
44059
+ stats.symlinkSkipped++;
44060
+ continue;
44061
+ }
44062
+ if (lstat3.isDirectory()) {
44063
+ if (state.directoriesQueued >= MAX_DISCOVERY_DIRECTORIES) {
44064
+ state.unexaminedDirectories++;
44065
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "directory_limit");
44066
+ continue;
44067
+ }
44068
+ let realPath;
44069
+ try {
44070
+ realPath = fs31.realpathSync(fullPath);
44071
+ } catch {
44072
+ stats.fileErrors++;
44073
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "read_error");
44074
+ continue;
44075
+ }
44076
+ if (isSymlinkLoop(realPath, visited) || !isPathWithinScope(realPath, scanDir)) {
44077
+ stats.symlinkSkipped++;
44078
+ continue;
44079
+ }
44080
+ pending.push(fullPath);
44081
+ state.directoriesQueued++;
44082
+ } else if (lstat3.isFile()) {
44083
+ const ext = path77.extname(fullPath).toLowerCase();
44084
+ if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
44085
+ addBoundedCandidate(state, fullPath);
44086
+ } else {
44087
+ stats.skippedFiles++;
44088
+ }
44089
+ } else {
44090
+ stats.fileErrors++;
44091
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "non_file");
44092
+ }
43829
44093
  }
43830
- const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
43831
- files.push(...subFiles);
43832
- } else if (lstat3.isFile()) {
43833
- const ext = path77.extname(fullPath).toLowerCase();
43834
- if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
43835
- files.push(fullPath);
43836
- } else {
43837
- stats.skippedFiles++;
44094
+ } finally {
44095
+ try {
44096
+ _internals64.closeDirectory(directory);
44097
+ } catch (error2) {
44098
+ if (error2.code !== "ERR_DIR_CLOSED") {
44099
+ state.cleanupFailed = true;
44100
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "cleanup_failed");
44101
+ }
43838
44102
  }
43839
44103
  }
44104
+ if (stoppedEarly)
44105
+ break;
43840
44106
  }
43841
- return files;
43842
44107
  }
43843
44108
  var secretscan = createSwarmTool({
43844
44109
  description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
@@ -43896,17 +44161,21 @@ var secretscan = createSwarmTool({
43896
44161
  }
43897
44162
  }
43898
44163
  try {
43899
- const _scanDirRaw = path77.resolve(directory);
44164
+ const scanDirRaw = path77.resolve(directory);
43900
44165
  const scanDir = (() => {
43901
44166
  try {
43902
- return fs31.realpathSync(_scanDirRaw);
44167
+ return fs31.realpathSync(scanDirRaw);
43903
44168
  } catch {
43904
- return _scanDirRaw;
44169
+ return scanDirRaw;
43905
44170
  }
43906
44171
  })();
43907
- if (!fs31.existsSync(scanDir)) {
44172
+ let dirStat;
44173
+ try {
44174
+ dirStat = fs31.lstatSync(scanDir);
44175
+ } catch (error2) {
44176
+ const err = error2;
43908
44177
  const errorResult = {
43909
- error: "directory not found",
44178
+ error: err.code === "ENOENT" ? "directory not found" : `scan failed: ${err.message || "unable to inspect directory"}`,
43910
44179
  scan_dir: directory,
43911
44180
  findings: [],
43912
44181
  count: 0,
@@ -43915,7 +44184,6 @@ var secretscan = createSwarmTool({
43915
44184
  };
43916
44185
  return JSON.stringify(errorResult, null, 2);
43917
44186
  }
43918
- const dirStat = fs31.statSync(scanDir);
43919
44187
  if (!dirStat.isDirectory()) {
43920
44188
  const errorResult = {
43921
44189
  error: "target must be a directory, not a file",
@@ -43947,36 +44215,74 @@ var secretscan = createSwarmTool({
43947
44215
  symlinkSkipped: 0
43948
44216
  };
43949
44217
  const visited = new Set;
43950
- const files = findScannableFiles(scanDir, excludeExact, excludeGlobs, scanDir, visited, stats);
43951
- files.sort((a, b) => {
43952
- const aLower = a.toLowerCase();
43953
- const bLower = b.toLowerCase();
43954
- if (aLower < bLower)
43955
- return -1;
43956
- if (aLower > bLower)
43957
- return 1;
43958
- return a.localeCompare(b);
43959
- });
43960
- const filesToScan = files.slice(0, MAX_FILES_SCANNED2);
44218
+ const deadline = _internals64.now() + SCAN_TIME_BUDGET_MS;
44219
+ const discovery = {
44220
+ files: [],
44221
+ totalCandidates: 0,
44222
+ deadline,
44223
+ deadlineExceeded: false,
44224
+ cleanupFailed: false,
44225
+ entriesSinceYield: 0,
44226
+ incompletePaths: [],
44227
+ directoriesQueued: 1,
44228
+ unexaminedDirectories: 0
44229
+ };
44230
+ await _internals64.yieldToEventLoop();
44231
+ await collectScannableFiles(scanDir, excludeExact, excludeGlobs, scanDir, visited, stats, discovery);
44232
+ const files = discovery.files;
44233
+ files.sort(compareFilePaths);
44234
+ const filesToScan = files;
44235
+ if (discovery.totalCandidates > filesToScan.length) {
44236
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "max_files");
44237
+ }
44238
+ if (discovery.deadlineExceeded) {
44239
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "deadline");
44240
+ }
44241
+ if (discovery.cleanupFailed) {
44242
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "cleanup_failed");
44243
+ }
43961
44244
  const allFindings = [];
43962
44245
  let filesScanned = 0;
43963
44246
  let skippedFiles = stats.skippedFiles;
43964
- for (const filePath of filesToScan) {
43965
- if (allFindings.length >= MAX_FINDINGS2)
44247
+ let incompleteFiles = stats.fileErrors + Math.max(0, discovery.totalCandidates - filesToScan.length) + (discovery.deadlineExceeded || discovery.cleanupFailed ? 1 : 0) + discovery.unexaminedDirectories;
44248
+ for (let index = 0;index < filesToScan.length; index++) {
44249
+ if (_internals64.now() >= deadline) {
44250
+ for (const remaining of filesToScan.slice(index)) {
44251
+ recordIncompletePath(discovery.incompletePaths, scanDir, remaining, "deadline");
44252
+ }
44253
+ incompleteFiles += filesToScan.length - index;
43966
44254
  break;
43967
- const fileFindings = scanFileForSecrets(filePath);
43968
- try {
43969
- const stat6 = fs31.statSync(filePath);
43970
- if (stat6.size > MAX_FILE_SIZE_BYTES3) {
44255
+ }
44256
+ if (allFindings.length >= MAX_FINDINGS2) {
44257
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "truncated");
44258
+ incompleteFiles += filesToScan.length - index;
44259
+ break;
44260
+ }
44261
+ const outcome = scanFileForSecrets(filesToScan[index], "standalone");
44262
+ switch (outcome.status) {
44263
+ case "scanned":
44264
+ filesScanned++;
44265
+ break;
44266
+ case "skipped":
43971
44267
  skippedFiles++;
43972
- continue;
43973
- }
43974
- } catch {}
43975
- filesScanned++;
43976
- for (const finding of fileFindings) {
43977
- if (allFindings.length >= MAX_FINDINGS2)
43978
44268
  break;
43979
- allFindings.push(finding);
44269
+ case "incomplete":
44270
+ skippedFiles++;
44271
+ incompleteFiles++;
44272
+ recordIncompletePath(discovery.incompletePaths, scanDir, filesToScan[index], outcome.reason);
44273
+ break;
44274
+ default:
44275
+ assertNever(outcome);
44276
+ }
44277
+ if (outcome.status === "scanned") {
44278
+ for (const finding of outcome.findings) {
44279
+ if (allFindings.length >= MAX_FINDINGS2)
44280
+ break;
44281
+ allFindings.push(finding);
44282
+ }
44283
+ }
44284
+ if (index + 1 < filesToScan.length) {
44285
+ await _internals64.yieldToEventLoop();
43980
44286
  }
43981
44287
  }
43982
44288
  allFindings.sort((a, b) => {
@@ -43997,31 +44303,33 @@ var secretscan = createSwarmTool({
43997
44303
  findings: allFindings,
43998
44304
  count: allFindings.length,
43999
44305
  files_scanned: filesScanned,
44000
- skipped_files: skippedFiles + stats.fileErrors + stats.symlinkSkipped
44306
+ skipped_files: skippedFiles + stats.fileErrors + stats.symlinkSkipped,
44307
+ incomplete_files: incompleteFiles,
44308
+ incomplete_paths: discovery.incompletePaths
44001
44309
  };
44002
44310
  const parts = [];
44003
- if (files.length > MAX_FILES_SCANNED2) {
44004
- parts.push(`Found ${files.length} files, scanned ${MAX_FILES_SCANNED2}`);
44311
+ if (discovery.totalCandidates > MAX_FILES_SCANNED2) {
44312
+ parts.push(`Found ${discovery.totalCandidates} files, selected ${MAX_FILES_SCANNED2}`);
44313
+ }
44314
+ if (discovery.deadlineExceeded) {
44315
+ parts.push("Discovery stopped at the scan deadline");
44316
+ }
44317
+ if (discovery.cleanupFailed) {
44318
+ parts.push("Discovery directory cleanup failed");
44005
44319
  }
44006
44320
  if (allFindings.length >= MAX_FINDINGS2) {
44007
44321
  parts.push(`Results limited to ${MAX_FINDINGS2} findings`);
44008
44322
  }
44323
+ if (incompleteFiles > 0) {
44324
+ parts.push(`${incompleteFiles} files were not completely scanned`);
44325
+ }
44009
44326
  if (skippedFiles > 0 || stats.fileErrors > 0 || stats.symlinkSkipped > 0) {
44010
44327
  parts.push(`${skippedFiles + stats.fileErrors + stats.symlinkSkipped} files skipped (binary/oversized/symlinks/errors)`);
44011
44328
  }
44012
44329
  if (parts.length > 0) {
44013
44330
  result.message = `${parts.join("; ")}.`;
44014
44331
  }
44015
- let jsonOutput = JSON.stringify(result, null, 2);
44016
- if (jsonOutput.length > MAX_OUTPUT_BYTES3) {
44017
- const truncatedResult = {
44018
- ...result,
44019
- findings: result.findings.slice(0, Math.floor(MAX_OUTPUT_BYTES3 * 0.8 / 200)),
44020
- message: "Output truncated due to size limits."
44021
- };
44022
- jsonOutput = JSON.stringify(truncatedResult, null, 2);
44023
- }
44024
- return jsonOutput;
44332
+ return serializeSecretscanResult(result);
44025
44333
  } catch (e) {
44026
44334
  const errorResult = {
44027
44335
  error: e instanceof Error ? `scan failed: ${e.message || "internal error"}` : "scan failed: unknown error",
@@ -44057,14 +44365,89 @@ async function runSecretscanOnFiles(files, directory) {
44057
44365
  const findings = [];
44058
44366
  let filesScanned = 0;
44059
44367
  let skippedFiles = 0;
44060
- for (const file of files) {
44368
+ const incompletePaths = [];
44369
+ const rawRoot = path77.resolve(directory);
44370
+ const canonicalRoot = (() => {
44371
+ try {
44372
+ return fs31.realpathSync(rawRoot);
44373
+ } catch {
44374
+ return rawRoot;
44375
+ }
44376
+ })();
44377
+ let rootStat;
44378
+ try {
44379
+ rootStat = fs31.lstatSync(canonicalRoot);
44380
+ } catch (error2) {
44381
+ const err = error2;
44382
+ return {
44383
+ error: err.code === "ENOENT" ? "directory not found" : `scan failed: ${err.message || "unable to inspect directory"}`,
44384
+ scan_dir: directory,
44385
+ findings: [],
44386
+ count: 0,
44387
+ files_scanned: 0,
44388
+ skipped_files: 0
44389
+ };
44390
+ }
44391
+ if (!rootStat.isDirectory()) {
44392
+ throw new Error("target must be a directory");
44393
+ }
44394
+ const filesToScan = files.slice(0, MAX_EXPLICIT_FILES_SCANNED);
44395
+ let incompleteFiles = Math.max(0, files.length - filesToScan.length);
44396
+ if (files.length > filesToScan.length) {
44397
+ recordIncompletePath(incompletePaths, canonicalRoot, canonicalRoot, "max_files");
44398
+ }
44399
+ const deadline = _internals64.now() + SCAN_TIME_BUDGET_MS;
44400
+ await _internals64.yieldToEventLoop();
44401
+ for (let index = 0;index < filesToScan.length; index++) {
44402
+ if (_internals64.now() >= deadline) {
44403
+ for (const remaining of filesToScan.slice(index)) {
44404
+ const remainingPath = path77.isAbsolute(remaining) ? path77.resolve(remaining) : path77.resolve(rawRoot, remaining);
44405
+ recordIncompletePath(incompletePaths, canonicalRoot, remainingPath, "deadline");
44406
+ }
44407
+ incompleteFiles += filesToScan.length - index;
44408
+ break;
44409
+ }
44410
+ if (findings.length >= MAX_FINDINGS2) {
44411
+ recordIncompletePath(incompletePaths, canonicalRoot, canonicalRoot, "truncated");
44412
+ incompleteFiles += filesToScan.length - index;
44413
+ break;
44414
+ }
44415
+ const file = filesToScan[index];
44061
44416
  if (typeof file !== "string") {
44062
44417
  skippedFiles++;
44063
44418
  continue;
44064
44419
  }
44065
- const resolvedPath = path77.isAbsolute(file) ? path77.resolve(file) : path77.resolve(directory, file);
44066
- if (!isPathWithinScope(resolvedPath, directory)) {
44420
+ const resolvedPath = path77.isAbsolute(file) ? path77.resolve(file) : path77.resolve(rawRoot, file);
44421
+ if (!isPathWithinScope(resolvedPath, rawRoot)) {
44067
44422
  skippedFiles++;
44423
+ incompleteFiles++;
44424
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "scope_escape");
44425
+ continue;
44426
+ }
44427
+ let lstat3;
44428
+ try {
44429
+ lstat3 = _internals64.lstatFile(resolvedPath);
44430
+ } catch (error2) {
44431
+ const err = error2;
44432
+ if (err.code === "ENOENT") {
44433
+ skippedFiles++;
44434
+ continue;
44435
+ }
44436
+ skippedFiles++;
44437
+ incompleteFiles++;
44438
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "read_error");
44439
+ continue;
44440
+ }
44441
+ if (lstat3.isSymbolicLink()) {
44442
+ skippedFiles++;
44443
+ incompleteFiles++;
44444
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "symlink");
44445
+ continue;
44446
+ }
44447
+ if (!lstat3.isFile()) {
44448
+ skippedFiles++;
44449
+ incompleteFiles++;
44450
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "non_file");
44068
44451
  continue;
44069
44452
  }
44070
44453
  const ext = path77.extname(resolvedPath).toLowerCase();
@@ -44072,16 +44455,47 @@ async function runSecretscanOnFiles(files, directory) {
44072
44455
  skippedFiles++;
44073
44456
  continue;
44074
44457
  }
44075
- if (!fs31.existsSync(resolvedPath)) {
44458
+ let scanPath = resolvedPath;
44459
+ try {
44460
+ scanPath = fs31.realpathSync(resolvedPath);
44461
+ } catch (error2) {
44462
+ const err = error2;
44463
+ if (err.code === "ENOENT") {
44464
+ skippedFiles++;
44465
+ continue;
44466
+ }
44076
44467
  skippedFiles++;
44468
+ incompleteFiles++;
44469
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "read_error");
44077
44470
  continue;
44078
44471
  }
44079
- const fileFindings = scanFileForSecrets(resolvedPath);
44080
- filesScanned++;
44081
- findings.push(...fileFindings);
44082
- if (findings.length >= MAX_FINDINGS2) {
44083
- findings.length = MAX_FINDINGS2;
44084
- break;
44472
+ if (!isPathWithinScope(scanPath, canonicalRoot)) {
44473
+ skippedFiles++;
44474
+ incompleteFiles++;
44475
+ recordIncompletePath(incompletePaths, canonicalRoot, scanPath, "scope_escape");
44476
+ continue;
44477
+ }
44478
+ const outcome = scanFileForSecrets(scanPath, "explicit");
44479
+ switch (outcome.status) {
44480
+ case "scanned":
44481
+ filesScanned++;
44482
+ findings.push(...outcome.findings);
44483
+ if (findings.length > MAX_FINDINGS2)
44484
+ findings.length = MAX_FINDINGS2;
44485
+ break;
44486
+ case "skipped":
44487
+ skippedFiles++;
44488
+ break;
44489
+ case "incomplete":
44490
+ skippedFiles++;
44491
+ incompleteFiles++;
44492
+ recordIncompletePath(incompletePaths, canonicalRoot, scanPath, outcome.reason);
44493
+ break;
44494
+ default:
44495
+ assertNever(outcome);
44496
+ }
44497
+ if (index + 1 < filesToScan.length) {
44498
+ await _internals64.yieldToEventLoop();
44085
44499
  }
44086
44500
  }
44087
44501
  findings.sort((a, b) => {
@@ -44096,7 +44510,9 @@ async function runSecretscanOnFiles(files, directory) {
44096
44510
  findings,
44097
44511
  count: findings.length,
44098
44512
  files_scanned: filesScanned,
44099
- skipped_files: skippedFiles
44513
+ skipped_files: skippedFiles,
44514
+ incomplete_files: incompleteFiles,
44515
+ incomplete_paths: incompletePaths
44100
44516
  };
44101
44517
  } catch (e) {
44102
44518
  return {
@@ -44113,7 +44529,15 @@ var _internals64 = {
44113
44529
  secretscan,
44114
44530
  runSecretscan,
44115
44531
  runSecretscanOnFiles,
44116
- SECRET_PATTERNS: SECRET_PATTERNS2
44532
+ SECRET_PATTERNS: SECRET_PATTERNS2,
44533
+ now: Date.now,
44534
+ yieldToEventLoop,
44535
+ openFile: fs31.openSync,
44536
+ fstatFile: fs31.fstatSync,
44537
+ readFileChunk: fs31.readSync,
44538
+ closeFile: fs31.closeSync,
44539
+ lstatFile: fs31.lstatSync,
44540
+ closeDirectory: (directory) => directory.closeSync()
44117
44541
  };
44118
44542
 
44119
44543
  // src/tools/test-runner.ts
@@ -45362,21 +45786,21 @@ function hasCompoundTestExtension(filename) {
45362
45786
  const lower = filename.toLowerCase();
45363
45787
  return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
45364
45788
  }
45365
- function isLanguageSpecificTestFile(basename15) {
45366
- const lower = basename15.toLowerCase();
45789
+ function isLanguageSpecificTestFile(basename16) {
45790
+ const lower = basename16.toLowerCase();
45367
45791
  if (lower.endsWith("_test.go"))
45368
45792
  return true;
45369
45793
  if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
45370
45794
  return true;
45371
45795
  if (lower.endsWith("_spec.rb"))
45372
45796
  return true;
45373
- if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename15) || basename15.endsWith("Test.java") || basename15.endsWith("Tests.java") || lower.endsWith("it.java")))
45797
+ if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.java") || basename16.endsWith("Tests.java") || lower.endsWith("it.java")))
45374
45798
  return true;
45375
45799
  if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
45376
45800
  return true;
45377
- if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename15) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
45801
+ if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename16) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
45378
45802
  return true;
45379
- if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename15) || basename15.endsWith("Test.php") || basename15.endsWith("Tests.php")))
45803
+ if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.php") || basename16.endsWith("Tests.php")))
45380
45804
  return true;
45381
45805
  if (lower.endsWith(".tests.ps1"))
45382
45806
  return true;
@@ -45384,23 +45808,23 @@ function isLanguageSpecificTestFile(basename15) {
45384
45808
  }
45385
45809
  function isConventionTestFilePath(filePath) {
45386
45810
  const normalizedPath = filePath.replace(/\\/g, "/");
45387
- const basename15 = path81.basename(filePath);
45388
- return hasCompoundTestExtension(basename15) || basename15.includes(".spec.") || basename15.includes(".test.") || isLanguageSpecificTestFile(basename15) || isTestDirectoryPath(normalizedPath);
45811
+ const basename16 = path81.basename(filePath);
45812
+ return hasCompoundTestExtension(basename16) || basename16.includes(".spec.") || basename16.includes(".test.") || isLanguageSpecificTestFile(basename16) || isTestDirectoryPath(normalizedPath);
45389
45813
  }
45390
45814
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45391
45815
  const testFiles = [];
45392
45816
  for (const file of sourceFiles) {
45393
45817
  const absoluteFile = resolveWorkspacePath(file, workingDir);
45394
45818
  const relativeFile = path81.relative(workingDir, absoluteFile);
45395
- const basename15 = path81.basename(absoluteFile);
45819
+ const basename16 = path81.basename(absoluteFile);
45396
45820
  const dirname30 = path81.dirname(absoluteFile);
45397
45821
  const preferRelativeOutput = !path81.isAbsolute(file);
45398
45822
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
45399
45823
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
45400
45824
  continue;
45401
45825
  }
45402
- const nameWithoutExt = basename15.replace(/\.[^.]+$/, "");
45403
- const ext = path81.extname(basename15);
45826
+ const nameWithoutExt = basename16.replace(/\.[^.]+$/, "");
45827
+ const ext = path81.extname(basename16);
45404
45828
  const genericTestNames = [
45405
45829
  `${nameWithoutExt}.spec${ext}`,
45406
45830
  `${nameWithoutExt}.test${ext}`
@@ -45411,7 +45835,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45411
45835
  ...languageSpecificTestNames
45412
45836
  ].map((candidateName) => path81.join(dirname30, candidateName));
45413
45837
  const testDirectoryNames = [
45414
- basename15,
45838
+ basename16,
45415
45839
  ...genericTestNames,
45416
45840
  ...languageSpecificTestNames
45417
45841
  ];
@@ -46782,6 +47206,13 @@ var DEFAULT_CONFIG = {
46782
47206
  testScope: "convention",
46783
47207
  linter: "biome"
46784
47208
  };
47209
+ function formatSecretscanIncompletePaths(incompletePaths, maxEntries = 3) {
47210
+ const visible = incompletePaths.slice(0, maxEntries).map((entry) => {
47211
+ return `${entry.path} (${entry.reason})`;
47212
+ });
47213
+ const suffix = incompletePaths.length > maxEntries ? ` (+${incompletePaths.length - maxEntries} more)` : "";
47214
+ return `${visible.join("; ")}${suffix}`;
47215
+ }
46785
47216
  function validateDirectoryPath(dir) {
46786
47217
  if (!dir || typeof dir !== "string") {
46787
47218
  throw new Error("Directory path is required");
@@ -47045,28 +47476,72 @@ async function runTestsCheck(_dir, scope, timeoutMs) {
47045
47476
  async function runSecretsCheck(dir, timeoutMs) {
47046
47477
  const startTime = Date.now();
47047
47478
  try {
47048
- const secretsPromise = runSecretscan(dir);
47479
+ const secretsPromise = _internals68.runSecretscan(dir);
47049
47480
  const timeoutPromise = new Promise((_, reject) => {
47050
47481
  setTimeout(() => {
47051
47482
  reject(new Error(`Secrets check timed out after ${timeoutMs}ms`));
47052
47483
  }, timeoutMs);
47053
47484
  });
47054
- const result = await Promise.race([
47055
- secretsPromise,
47056
- timeoutPromise
47057
- ]);
47058
- if (result.findings && result.findings.length > 0) {
47485
+ const result = await Promise.race([secretsPromise, timeoutPromise]);
47486
+ if ("error" in result) {
47487
+ return {
47488
+ type: "secrets",
47489
+ status: "fail",
47490
+ message: `Secrets check failed: ${result.error}`,
47491
+ details: {
47492
+ error: result.error,
47493
+ filesScanned: result.files_scanned,
47494
+ incompleteFiles: 0,
47495
+ incompletePaths: []
47496
+ },
47497
+ durationMs: Date.now() - startTime
47498
+ };
47499
+ }
47500
+ const findingsCount = Math.max(result.count, result.findings.length);
47501
+ if (typeof result.incomplete_files !== "number" || !Array.isArray(result.incomplete_paths)) {
47502
+ return {
47503
+ type: "secrets",
47504
+ status: "fail",
47505
+ message: "Secrets check failed: scanner returned incomplete coverage metadata",
47506
+ details: { filesScanned: result.files_scanned },
47507
+ durationMs: Date.now() - startTime
47508
+ };
47509
+ }
47510
+ const incompleteFiles = result.incomplete_files;
47511
+ const incompletePaths = result.incomplete_paths;
47512
+ const hasCountMismatch = result.count !== result.findings.length;
47513
+ const hasIncompleteCoverage = incompleteFiles > 0 || incompletePaths.length > 0;
47514
+ const hasZeroCoverage = result.files_scanned === 0;
47515
+ const failures = [];
47516
+ if (findingsCount > 0) {
47059
47517
  const critical = result.findings.filter((f) => f.severity === "critical").length;
47060
47518
  const high = result.findings.filter((f) => f.severity === "high").length;
47519
+ failures.push(`Found ${findingsCount} secret(s): ${critical} critical, ${high} high`);
47520
+ }
47521
+ if (hasIncompleteCoverage) {
47522
+ const incompleteSummary = incompletePaths.length > 0 ? `; incomplete paths: ${formatSecretscanIncompletePaths(incompletePaths)}` : "";
47523
+ failures.push(`${incompleteFiles} incomplete file(s)${incompleteSummary}`);
47524
+ }
47525
+ if (hasCountMismatch) {
47526
+ failures.push(`findings/count mismatch (reported ${result.count}, actual ${result.findings.length})`);
47527
+ }
47528
+ if (hasZeroCoverage) {
47529
+ failures.push("zero requested files scanned");
47530
+ }
47531
+ if (failures.length > 0) {
47061
47532
  return {
47062
47533
  type: "secrets",
47063
47534
  status: "fail",
47064
- message: `Found ${result.findings.length} secret(s): ${critical} critical, ${high} high`,
47535
+ message: `Secrets check failed: ${failures.join("; ")}`,
47065
47536
  details: {
47066
- count: result.count,
47067
- critical,
47068
- high,
47069
- filesScanned: result.files_scanned
47537
+ count: findingsCount,
47538
+ reportedCount: result.count,
47539
+ critical: result.findings.filter((f) => f.severity === "critical").length,
47540
+ high: result.findings.filter((f) => f.severity === "high").length,
47541
+ filesScanned: result.files_scanned,
47542
+ skippedFiles: result.skipped_files,
47543
+ incompleteFiles,
47544
+ incompletePaths
47070
47545
  },
47071
47546
  durationMs: Date.now() - startTime
47072
47547
  };
@@ -47076,7 +47551,10 @@ async function runSecretsCheck(dir, timeoutMs) {
47076
47551
  status: "pass",
47077
47552
  message: "No secrets detected",
47078
47553
  details: {
47079
- filesScanned: result.files_scanned
47554
+ filesScanned: result.files_scanned,
47555
+ skippedFiles: result.skipped_files,
47556
+ incompleteFiles,
47557
+ incompletePaths
47080
47558
  },
47081
47559
  durationMs: Date.now() - startTime
47082
47560
  };
@@ -47404,6 +47882,7 @@ var _internals68 = {
47404
47882
  runVersionCheck,
47405
47883
  runLintCheck,
47406
47884
  runTestsCheck,
47885
+ runSecretscan,
47407
47886
  runSecretsCheck,
47408
47887
  runEvidenceCheck,
47409
47888
  runRequirementCoverageCheck
@@ -52403,7 +52882,7 @@ import {
52403
52882
  openSync as openSync12,
52404
52883
  readFileSync as readFileSync39,
52405
52884
  renameSync as renameSync18,
52406
- statSync as statSync16,
52885
+ statSync as statSync15,
52407
52886
  unlinkSync as unlinkSync20,
52408
52887
  writeFileSync as writeFileSync17,
52409
52888
  writeFileSync as writeFileSyncAsync
@@ -52493,7 +52972,7 @@ var _internals78 = {
52493
52972
  renameSync: renameSync18,
52494
52973
  unlinkSync: unlinkSync20,
52495
52974
  existsSync: existsSync53,
52496
- statSync: statSync16
52975
+ statSync: statSync15
52497
52976
  };
52498
52977
  async function appendEvent(directory, eventInput) {
52499
52978
  if (!isValidSkillSlug(eventInput.skillSlug)) {
@@ -55596,7 +56075,7 @@ function buildDetailedHelp(commandName, entry) {
55596
56075
  async function handleHelpCommand(ctx) {
55597
56076
  const targetCommand = ctx.args.join(" ");
55598
56077
  if (!targetCommand) {
55599
- const { buildHelpText } = await import("./index-ag7ppc40.js");
56078
+ const { buildHelpText } = await import("./index-s7ac0xrf.js");
55600
56079
  return buildHelpText();
55601
56080
  }
55602
56081
  const tokens = targetCommand.split(/\s+/);
@@ -55605,7 +56084,7 @@ async function handleHelpCommand(ctx) {
55605
56084
  return _internals14.buildDetailedHelp(resolved.key, resolved.entry);
55606
56085
  }
55607
56086
  const similar = _internals14.findSimilarCommands(targetCommand);
55608
- const { buildHelpText: fullHelp } = await import("./index-ag7ppc40.js");
56087
+ const { buildHelpText: fullHelp } = await import("./index-s7ac0xrf.js");
55609
56088
  if (similar.length > 0) {
55610
56089
  return `Command '/swarm ${targetCommand}' not found.
55611
56090
 
@@ -55782,7 +56261,7 @@ var COMMAND_REGISTRY = {
55782
56261
  },
55783
56262
  "guardrail explain": {
55784
56263
  handler: async (ctx) => {
55785
- const { handleGuardrailExplain } = await import("./guardrail-explain-ph1n3ryc.js");
56264
+ const { handleGuardrailExplain } = await import("./guardrail-explain-rks41rpx.js");
55786
56265
  return handleGuardrailExplain(ctx.directory, ctx.args);
55787
56266
  },
55788
56267
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -55792,7 +56271,7 @@ var COMMAND_REGISTRY = {
55792
56271
  },
55793
56272
  "guardrail-explain": {
55794
56273
  handler: async (ctx) => {
55795
- const { handleGuardrailExplain } = await import("./guardrail-explain-ph1n3ryc.js");
56274
+ const { handleGuardrailExplain } = await import("./guardrail-explain-rks41rpx.js");
55796
56275
  return handleGuardrailExplain(ctx.directory, ctx.args);
55797
56276
  },
55798
56277
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -55802,7 +56281,7 @@ var COMMAND_REGISTRY = {
55802
56281
  },
55803
56282
  "guardrail-log": {
55804
56283
  handler: async (ctx) => {
55805
- const { handleGuardrailLog } = await import("./guardrail-log-ghgqaw3w.js");
56284
+ const { handleGuardrailLog } = await import("./guardrail-log-h39jxftx.js");
55806
56285
  return handleGuardrailLog(ctx.directory, ctx.args);
55807
56286
  },
55808
56287
  description: "Read the guardrail decision log (use --blocks-only for blocks)",