opencode-swarm 7.138.0 → 7.138.1

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 (40) hide show
  1. package/dist/cli/{config-doctor-mb81xytp.js → config-doctor-m79sx8b2.js} +2 -2
  2. package/dist/cli/{curation-policy-qx23sc8b.js → curation-policy-dkx4s5va.js} +2 -2
  3. package/dist/cli/{curator-b9ascpbh.js → curator-jjf5x6bn.js} +14 -14
  4. package/dist/cli/{curator-llm-factory-r47q1hbd.js → curator-llm-factory-sprc6pm3.js} +14 -14
  5. package/dist/cli/{evidence-summary-service-1azhyeft.js → evidence-summary-service-6cttrkzw.js} +2 -2
  6. package/dist/cli/{guardrail-explain-ph1n3ryc.js → guardrail-explain-zs3hq02c.js} +15 -15
  7. package/dist/cli/{guardrail-log-ghgqaw3w.js → guardrail-log-90dnv508.js} +3 -3
  8. package/dist/cli/{hive-promoter-7x2zzhb7.js → hive-promoter-zkay2ffy.js} +14 -14
  9. package/dist/cli/{index-9zf5gfca.js → index-06zw53mf.js} +1 -1
  10. package/dist/cli/{index-bf0j2wd2.js → index-5fdztrpt.js} +2 -2
  11. package/dist/cli/{index-ag7ppc40.js → index-6f6nt0c9.js} +16 -16
  12. package/dist/cli/{index-7wzyf4dr.js → index-8xqnp3gw.js} +4 -4
  13. package/dist/cli/{index-7kgfvt3h.js → index-9vvskh03.js} +1 -1
  14. package/dist/cli/{index-whf5ecbw.js → index-aeyktf9q.js} +1 -1
  15. package/dist/cli/{index-z55s5719.js → index-d4t92fa3.js} +2 -2
  16. package/dist/cli/{index-xz0zjrdr.js → index-e2eedz1b.js} +1 -1
  17. package/dist/cli/{index-6nenabrm.js → index-e78pqbkj.js} +1 -1
  18. package/dist/cli/{index-k5jrywpr.js → index-gprxjmm7.js} +6 -1
  19. package/dist/cli/{index-cdx3aqj5.js → index-kqzzdxc1.js} +1 -1
  20. package/dist/cli/{index-sp7s1vk3.js → index-mnw8g9bs.js} +609 -213
  21. package/dist/cli/{index-4xs9e82s.js → index-mzqgtzxq.js} +3 -3
  22. package/dist/cli/{index-hmapndcz.js → index-navw2ezf.js} +5 -5
  23. package/dist/cli/{index-npgjk6vc.js → index-p7fn5byt.js} +1 -1
  24. package/dist/cli/{index-2vzgvscz.js → index-vmff7b40.js} +3 -3
  25. package/dist/cli/{index-ca6qmxd8.js → index-vp3mwsd1.js} +1 -1
  26. package/dist/cli/{index-fmh15wxb.js → index-y06a43sk.js} +1 -1
  27. package/dist/cli/index.js +14 -14
  28. package/dist/cli/{knowledge-escalator-pqskjp6y.js → knowledge-escalator-6y5x8vat.js} +3 -3
  29. package/dist/cli/{knowledge-events-ahpxnqjd.js → knowledge-events-bnr4weyn.js} +1 -1
  30. package/dist/cli/{knowledge-store-27aynbeg.js → knowledge-store-4mcmb2rj.js} +1 -1
  31. package/dist/cli/{knowledge-validator-4saz53kh.js → knowledge-validator-pf377mtx.js} +4 -4
  32. package/dist/cli/{scan-cursor-ngykkd7f.js → scan-cursor-we0561wc.js} +2 -2
  33. package/dist/cli/{schema-p82ntfy9.js → schema-1xtxr4d9.js} +1 -1
  34. package/dist/cli/{skill-generator-r19a1zje.js → skill-generator-kns0dae2.js} +5 -5
  35. package/dist/config/evidence-schema.d.ts +15 -0
  36. package/dist/index.js +20 -20
  37. package/dist/services/preflight-service.d.ts +2 -0
  38. package/dist/tools/pre-check-batch.d.ts +2 -0
  39. package/dist/tools/secretscan.d.ts +17 -0
  40. 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-e2eedz1b.js";
20
20
  import {
21
21
  detectStraySwarmDirs,
22
22
  readDoctorArtifact,
23
23
  removeStraySwarmDir,
24
24
  runConfigDoctor
25
- } from "./index-ca6qmxd8.js";
25
+ } from "./index-vp3mwsd1.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-mzqgtzxq.js";
118
118
  import {
119
119
  readRecentEscalations
120
- } from "./index-hmapndcz.js";
120
+ } from "./index-navw2ezf.js";
121
121
  import {
122
122
  alreadyCuratedThisGeneration,
123
123
  claimNextScanBatch,
124
124
  resolveCohortId
125
- } from "./index-whf5ecbw.js";
125
+ } from "./index-aeyktf9q.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-d4t92fa3.js";
179
179
  import {
180
180
  authorizeCuration,
181
181
  buildConfigFingerprintInput
182
- } from "./index-7kgfvt3h.js";
182
+ } from "./index-9vvskh03.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-8xqnp3gw.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-kqzzdxc1.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-p7fn5byt.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-e78pqbkj.js";
267
267
  import {
268
268
  KNOWLEDGE_SCHEMA_VERSION,
269
269
  OUTCOME_BLOCK_THRESHOLD,
@@ -18784,7 +18784,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
18784
18784
  };
18785
18785
  }
18786
18786
  async function sweepActiveNearDuplicates(directory, options = {}) {
18787
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
18787
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vmff7b40.js");
18788
18788
  const { config } = loadPluginConfigWithMeta2(directory);
18789
18789
  const sweepConfig = config.learning?.dedup_sweep;
18790
18790
  if (sweepConfig?.enabled === false)
@@ -20377,7 +20377,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
20377
20377
  }
20378
20378
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
20379
20379
  try {
20380
- const skillModule = await import("./skill-generator-r19a1zje.js");
20380
+ const skillModule = await import("./skill-generator-kns0dae2.js");
20381
20381
  for (const cand of skillCandidates) {
20382
20382
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
20383
20383
  continue;
@@ -20837,7 +20837,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
20837
20837
  } catch {}
20838
20838
  }
20839
20839
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
20840
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-pqskjp6y.js");
20840
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-6y5x8vat.js");
20841
20841
  for (const { id } of contradictedEntries) {
20842
20842
  try {
20843
20843
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -21656,7 +21656,7 @@ async function runCuratorPostMortem(directory, options = {}) {
21656
21656
  warnings.push("Failed to collect knowledge summary.");
21657
21657
  }
21658
21658
  try {
21659
- const { getScanStatus } = await import("./scan-cursor-ngykkd7f.js");
21659
+ const { getScanStatus } = await import("./scan-cursor-we0561wc.js");
21660
21660
  const scanStatus = await getScanStatus(directory);
21661
21661
  if (scanStatus.remaining_estimate > 0) {
21662
21662
  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 +21828,9 @@ var _internals24 = {
21828
21828
  verifyPostMortemKnowledgeActions,
21829
21829
  repairPostMortemActions,
21830
21830
  loadDefaultKnowledgeConfig: async (directory) => {
21831
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
21831
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-1xtxr4d9.js");
21832
21832
  try {
21833
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
21833
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vmff7b40.js");
21834
21834
  const loaded = loadPluginConfigWithMeta2(directory);
21835
21835
  return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
21836
21836
  } catch {
@@ -21838,11 +21838,11 @@ var _internals24 = {
21838
21838
  }
21839
21839
  },
21840
21840
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
21841
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-b9ascpbh.js");
21841
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-jjf5x6bn.js");
21842
21842
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
21843
21843
  },
21844
21844
  checkHivePromotions: async (entries, knowledgeConfig, directory) => {
21845
- const { checkHivePromotions } = await import("./hive-promoter-7x2zzhb7.js");
21845
+ const { checkHivePromotions } = await import("./hive-promoter-zkay2ffy.js");
21846
21846
  return checkHivePromotions(entries, knowledgeConfig, directory);
21847
21847
  },
21848
21848
  applyProposalTriage: async (directory, triage) => {
@@ -21851,7 +21851,7 @@ var _internals24 = {
21851
21851
  activateProposal,
21852
21852
  listSkills: listSkills2,
21853
21853
  sanitizeSlug
21854
- } = await import("./skill-generator-r19a1zje.js");
21854
+ } = await import("./skill-generator-kns0dae2.js");
21855
21855
  const result = {
21856
21856
  approved: [],
21857
21857
  rejected: [],
@@ -24478,9 +24478,9 @@ function hashContent(content) {
24478
24478
  return createHash9("sha1").update(content).digest("hex");
24479
24479
  }
24480
24480
  async function loadConfigForPolicyCurator(directory) {
24481
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
24481
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-1xtxr4d9.js");
24482
24482
  try {
24483
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
24483
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vmff7b40.js");
24484
24484
  const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
24485
24485
  return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
24486
24486
  } catch {
@@ -28440,7 +28440,7 @@ async function runFinalizeStage(ctx) {
28440
28440
  }
28441
28441
  }
28442
28442
  try {
28443
- const { CuratorConfigSchema: CCS } = await import("./schema-p82ntfy9.js");
28443
+ const { CuratorConfigSchema: CCS } = await import("./schema-1xtxr4d9.js");
28444
28444
  const { config: pmLoadedConfig } = _internals36.loadPluginConfigWithMeta(ctx.directory);
28445
28445
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
28446
28446
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -30711,9 +30711,9 @@ var _internals41 = {
30711
30711
  loadPluginConfigWithMeta,
30712
30712
  loadCuratorDeps: async () => {
30713
30713
  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")
30714
+ import("./schema-1xtxr4d9.js"),
30715
+ import("./curator-jjf5x6bn.js"),
30716
+ import("./curator-llm-factory-sprc6pm3.js")
30717
30717
  ]);
30718
30718
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
30719
30719
  }
@@ -31618,7 +31618,7 @@ async function computeCurationDiagnostics(directory, entries) {
31618
31618
  let scanCompleted = false;
31619
31619
  let scanRemaining = 0;
31620
31620
  try {
31621
- const { getScanStatus } = await import("./scan-cursor-ngykkd7f.js");
31621
+ const { getScanStatus } = await import("./scan-cursor-we0561wc.js");
31622
31622
  const status = await getScanStatus(directory);
31623
31623
  scanGeneration = status.generation;
31624
31624
  scanCompleted = status.completed;
@@ -31628,17 +31628,17 @@ async function computeCurationDiagnostics(directory, entries) {
31628
31628
  try {
31629
31629
  const { isLinked: isLinked2 } = await import("./knowledge-link-xc6yhxmz.js");
31630
31630
  if (isLinked2(directory)) {
31631
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-p82ntfy9.js");
31631
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-1xtxr4d9.js");
31632
31632
  let config;
31633
31633
  try {
31634
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-2vzgvscz.js");
31634
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-vmff7b40.js");
31635
31635
  const loaded = loadPluginConfigWithMeta2(directory);
31636
31636
  config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
31637
31637
  } catch {
31638
31638
  config = KnowledgeConfigSchema2.parse({});
31639
31639
  }
31640
31640
  const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
31641
- const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-qx23sc8b.js");
31641
+ const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-dkx4s5va.js");
31642
31642
  const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
31643
31643
  const cohortFp = await readCohortConfigFingerprint(directory);
31644
31644
  configMatch = cohortFp === null ? null : currentFp === cohortFp;
@@ -32849,7 +32849,7 @@ async function handleDoctorCommand(directory, args) {
32849
32849
  const result = runConfigDoctor(config, directory);
32850
32850
  let output;
32851
32851
  if (enableAutoFix && result.hasAutoFixableIssues) {
32852
- const { runConfigDoctorWithFixes } = await import("./config-doctor-mb81xytp.js");
32852
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-m79sx8b2.js");
32853
32853
  const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
32854
32854
  applyLossy: true
32855
32855
  });
@@ -33686,7 +33686,7 @@ async function handleEvidenceCommand(directory, args) {
33686
33686
  return formatTaskEvidenceMarkdown(evidenceData);
33687
33687
  }
33688
33688
  async function handleEvidenceSummaryCommand(directory) {
33689
- const { buildEvidenceSummary } = await import("./evidence-summary-service-1azhyeft.js");
33689
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-6cttrkzw.js");
33690
33690
  const artifact = await buildEvidenceSummary(directory);
33691
33691
  if (!artifact) {
33692
33692
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -43362,10 +43362,14 @@ import * as path77 from "path";
43362
43362
  var MAX_FILE_PATH_LENGTH = 500;
43363
43363
  var MAX_FILE_SIZE_BYTES3 = 512 * 1024;
43364
43364
  var MAX_FILES_SCANNED2 = 1000;
43365
+ var MAX_EXPLICIT_FILES_SCANNED = 100;
43365
43366
  var MAX_FINDINGS2 = 100;
43366
43367
  var MAX_OUTPUT_BYTES3 = 512000;
43367
- var MAX_LINE_LENGTH = 1e4;
43368
- var MAX_CONTENT_BYTES = 50 * 1024;
43368
+ var MAX_CONTEXT_CHARS = 1000;
43369
+ var MAX_LONG_LINE_CHARS = 1e4;
43370
+ var MAX_INCOMPLETE_PATHS = 25;
43371
+ var MAX_DISCOVERY_DIRECTORIES = 1e4;
43372
+ var SCAN_TIME_BUDGET_MS = 55000;
43369
43373
  var BINARY_SIGNATURES = [
43370
43374
  0,
43371
43375
  2303741511,
@@ -43413,6 +43417,7 @@ var DEFAULT_EXCLUDE_EXTENSIONS = new Set([
43413
43417
  ".gz",
43414
43418
  ".rar",
43415
43419
  ".7z",
43420
+ ".wasm",
43416
43421
  ".exe",
43417
43422
  ".dll",
43418
43423
  ".so",
@@ -43651,9 +43656,6 @@ function isBinaryFile(filePath, buffer) {
43651
43656
  }
43652
43657
  function scanLineForSecrets(line, _lineNum) {
43653
43658
  const results = [];
43654
- if (line.length > MAX_LINE_LENGTH) {
43655
- return results;
43656
- }
43657
43659
  for (const pattern of SECRET_PATTERNS2) {
43658
43660
  pattern.regex.lastIndex = 0;
43659
43661
  for (let match = pattern.regex.exec(line);match !== null; match = pattern.regex.exec(line)) {
@@ -43672,8 +43674,8 @@ function scanLineForSecrets(line, _lineNum) {
43672
43674
  }
43673
43675
  }
43674
43676
  }
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])) {
43677
+ const valueMatch = line.match(/((?:secret|key|token|password|cred|credential))\s*[=:]\s*["']?([a-zA-Z0-9+/=_-]{20,100})["']?/i);
43678
+ if (valueMatch && isHighEntropyString(valueMatch[2])) {
43677
43679
  const matchStart = valueMatch.index || 0;
43678
43680
  const matchEnd = matchStart + valueMatch[0].length;
43679
43681
  const hasOverlap = results.some((r) => !(r.matchEnd <= matchStart || r.matchStart >= matchEnd));
@@ -43682,7 +43684,7 @@ function scanLineForSecrets(line, _lineNum) {
43682
43684
  type: "high_entropy",
43683
43685
  confidence: "low",
43684
43686
  severity: "medium",
43685
- redacted: `${valueMatch[0].split("=")[0].trim()}=[HIGH_ENTROPY]`,
43687
+ redacted: `${valueMatch[1]}=[HIGH_ENTROPY]`,
43686
43688
  matchStart,
43687
43689
  matchEnd
43688
43690
  });
@@ -43692,42 +43694,95 @@ function scanLineForSecrets(line, _lineNum) {
43692
43694
  }
43693
43695
  function createRedactedContext(line, findings) {
43694
43696
  if (findings.length === 0)
43695
- return line;
43697
+ return "";
43696
43698
  const sorted = [...findings].sort((a, b) => a.matchStart - b.matchStart);
43697
43699
  let result = "";
43698
43700
  let lastEnd = 0;
43699
43701
  for (const finding of sorted) {
43700
- result += line.slice(lastEnd, finding.matchStart);
43701
- result += finding.redacted;
43702
- lastEnd = finding.matchEnd;
43702
+ if (finding.matchEnd <= lastEnd)
43703
+ continue;
43704
+ if (finding.matchStart >= lastEnd) {
43705
+ result += line.slice(lastEnd, finding.matchStart);
43706
+ result += "[REDACTED]";
43707
+ }
43708
+ lastEnd = Math.max(lastEnd, finding.matchEnd);
43703
43709
  }
43704
43710
  result += line.slice(lastEnd);
43705
- return result;
43711
+ if (result.length <= MAX_CONTEXT_CHARS)
43712
+ return result;
43713
+ const truncationMarker = "...[context truncated]...";
43714
+ const edgeLength = Math.floor((MAX_CONTEXT_CHARS - truncationMarker.length) / 2);
43715
+ const remaining = MAX_CONTEXT_CHARS - truncationMarker.length - edgeLength;
43716
+ return `${result.slice(0, edgeLength)}${truncationMarker}${result.slice(-remaining)}`;
43706
43717
  }
43707
43718
  var O_NOFOLLOW = process.platform !== "win32" ? fs31.constants.O_NOFOLLOW : undefined;
43708
- function scanFileForSecrets(filePath) {
43719
+ function readBoundedFile(filePath) {
43720
+ let fd;
43721
+ try {
43722
+ const flags = fs31.constants.O_RDONLY | (O_NOFOLLOW !== undefined ? O_NOFOLLOW : 0);
43723
+ fd = _internals64.openFile(filePath, flags);
43724
+ } catch {
43725
+ return { status: "incomplete", reason: "read_error" };
43726
+ }
43727
+ let outcome;
43728
+ try {
43729
+ const stat6 = _internals64.fstatFile(fd);
43730
+ const initialSize = stat6.size;
43731
+ if (!stat6.isFile()) {
43732
+ outcome = { status: "incomplete", reason: "non_file" };
43733
+ } else {
43734
+ const bufferLength = Math.min(initialSize + 1, MAX_FILE_SIZE_BYTES3 + 1);
43735
+ const buffer = Buffer.allocUnsafe(bufferLength);
43736
+ let bytesRead = 0;
43737
+ while (bytesRead < buffer.length) {
43738
+ const count = _internals64.readFileChunk(fd, buffer, bytesRead, buffer.length - bytesRead, null);
43739
+ if (count === 0)
43740
+ break;
43741
+ bytesRead += count;
43742
+ }
43743
+ const postStat = _internals64.fstatFile(fd);
43744
+ if (postStat.size !== initialSize) {
43745
+ outcome = {
43746
+ status: "incomplete",
43747
+ reason: postStat.size > MAX_FILE_SIZE_BYTES3 ? "oversized" : "read_error"
43748
+ };
43749
+ } else {
43750
+ outcome = {
43751
+ status: "ok",
43752
+ buffer: buffer.subarray(0, bytesRead),
43753
+ truncated: initialSize > MAX_FILE_SIZE_BYTES3
43754
+ };
43755
+ }
43756
+ }
43757
+ } catch {
43758
+ outcome = { status: "incomplete", reason: "read_error" };
43759
+ }
43760
+ try {
43761
+ _internals64.closeFile(fd);
43762
+ } catch {
43763
+ return { status: "incomplete", reason: "read_error" };
43764
+ }
43765
+ return outcome;
43766
+ }
43767
+ function scanFileForSecrets(filePath, mode) {
43709
43768
  const findings = [];
43710
43769
  try {
43711
43770
  const lstat3 = fs31.lstatSync(filePath);
43712
43771
  if (lstat3.isSymbolicLink()) {
43713
- return findings;
43772
+ return mode === "explicit" ? { status: "incomplete", reason: "symlink" } : { status: "skipped", reason: "symlink" };
43714
43773
  }
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);
43774
+ if (!lstat3.isFile()) {
43775
+ return { status: "incomplete", reason: "non_file" };
43728
43776
  }
43777
+ const readOutcome = readBoundedFile(filePath);
43778
+ if (readOutcome.status === "incomplete")
43779
+ return readOutcome;
43780
+ const { buffer, truncated } = readOutcome;
43729
43781
  if (isBinaryFile(filePath, buffer)) {
43730
- return findings;
43782
+ return { status: "skipped", reason: "binary" };
43783
+ }
43784
+ if (truncated) {
43785
+ return { status: "incomplete", reason: "oversized" };
43731
43786
  }
43732
43787
  let content;
43733
43788
  if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
@@ -43736,13 +43791,16 @@ function scanFileForSecrets(filePath) {
43736
43791
  content = buffer.toString("utf-8");
43737
43792
  }
43738
43793
  if (content.includes("\x00")) {
43739
- return findings;
43794
+ return { status: "skipped", reason: "binary" };
43740
43795
  }
43741
- const scanContent = content.slice(0, MAX_CONTENT_BYTES);
43742
- const lines = scanContent.split(`
43796
+ const lines = content.split(`
43743
43797
  `);
43744
43798
  for (let i = 0;i < lines.length; i++) {
43799
+ if (mode === "standalone" && lines[i].length > MAX_LONG_LINE_CHARS) {
43800
+ continue;
43801
+ }
43745
43802
  const lineResults = scanLineForSecrets(lines[i], i + 1);
43803
+ const context = createRedactedContext(lines[i], lineResults);
43746
43804
  for (const result of lineResults) {
43747
43805
  findings.push({
43748
43806
  path: filePath,
@@ -43751,12 +43809,43 @@ function scanFileForSecrets(filePath) {
43751
43809
  confidence: result.confidence,
43752
43810
  severity: result.severity,
43753
43811
  redacted: result.redacted,
43754
- context: createRedactedContext(lines[i], [result])
43812
+ context
43755
43813
  });
43756
43814
  }
43757
43815
  }
43758
- } catch {}
43759
- return findings;
43816
+ return { status: "scanned", findings };
43817
+ } catch {
43818
+ return { status: "incomplete", reason: "read_error" };
43819
+ }
43820
+ }
43821
+ function assertNever(value) {
43822
+ throw new Error(`Unhandled secretscan outcome: ${String(value)}`);
43823
+ }
43824
+ async function yieldToEventLoop() {
43825
+ await new Promise((resolve24) => setImmediate(resolve24));
43826
+ }
43827
+ function serializeSecretscanResult(result) {
43828
+ let findings = result.findings;
43829
+ let candidate = result;
43830
+ let json = JSON.stringify(candidate, null, 2);
43831
+ while (Buffer.byteLength(json, "utf8") > MAX_OUTPUT_BYTES3 && findings.length > 0) {
43832
+ findings = findings.slice(0, Math.floor(findings.length / 2));
43833
+ candidate = {
43834
+ ...result,
43835
+ findings,
43836
+ message: "Output truncated due to size limits."
43837
+ };
43838
+ json = JSON.stringify(candidate, null, 2);
43839
+ }
43840
+ if (Buffer.byteLength(json, "utf8") > MAX_OUTPUT_BYTES3) {
43841
+ candidate = {
43842
+ ...result,
43843
+ findings: [],
43844
+ message: "Output truncated due to size limits."
43845
+ };
43846
+ json = JSON.stringify(candidate, null, 2);
43847
+ }
43848
+ return json;
43760
43849
  }
43761
43850
  function isSymlinkLoop(realPath, visited) {
43762
43851
  if (visited.has(realPath)) {
@@ -43770,75 +43859,168 @@ function isPathWithinScope(realPath, scanDir) {
43770
43859
  const resolvedRealPath = path77.resolve(realPath);
43771
43860
  return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path77.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
43772
43861
  }
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;
43862
+ var DISCOVERY_YIELD_INTERVAL = 100;
43863
+ function toSafeRelativePath(rootDir, targetPath) {
43864
+ const relative10 = path77.relative(rootDir, targetPath).replace(/\\/g, "/");
43865
+ if (!relative10 || relative10 === ".")
43866
+ return ".";
43867
+ if (relative10.startsWith("..") || path77.isAbsolute(relative10)) {
43868
+ const basename15 = path77.basename(targetPath).replace(/\\/g, "/");
43869
+ return basename15 || ".";
43870
+ }
43871
+ return relative10;
43872
+ }
43873
+ function recordIncompletePath(target, rootDir, targetPath, reason) {
43874
+ if (target.length >= MAX_INCOMPLETE_PATHS) {
43875
+ target[MAX_INCOMPLETE_PATHS - 1] = { path: ".", reason: "truncated" };
43876
+ return;
43877
+ }
43878
+ target.push({ path: toSafeRelativePath(rootDir, targetPath), reason });
43879
+ }
43880
+ function compareFilePaths(a, b) {
43881
+ const aLower = a.toLowerCase();
43882
+ const bLower = b.toLowerCase();
43883
+ if (aLower < bLower)
43884
+ return -1;
43885
+ if (aLower > bLower)
43886
+ return 1;
43887
+ if (a < b)
43888
+ return -1;
43889
+ if (a > b)
43890
+ return 1;
43891
+ return 0;
43892
+ }
43893
+ function addBoundedCandidate(state, filePath) {
43894
+ state.totalCandidates++;
43895
+ const heap = state.files;
43896
+ if (heap.length < MAX_FILES_SCANNED2) {
43897
+ heap.push(filePath);
43898
+ let index2 = heap.length - 1;
43899
+ while (index2 > 0) {
43900
+ const parent = Math.floor((index2 - 1) / 2);
43901
+ if (compareFilePaths(heap[parent], heap[index2]) >= 0)
43902
+ break;
43903
+ [heap[parent], heap[index2]] = [heap[index2], heap[parent]];
43904
+ index2 = parent;
43905
+ }
43906
+ return;
43907
+ }
43908
+ if (compareFilePaths(filePath, heap[0]) >= 0)
43909
+ return;
43910
+ heap[0] = filePath;
43911
+ let index = 0;
43912
+ while (true) {
43913
+ const left = index * 2 + 1;
43914
+ const right = left + 1;
43915
+ let largest = index;
43916
+ if (left < heap.length && compareFilePaths(heap[left], heap[largest]) > 0) {
43917
+ largest = left;
43918
+ }
43919
+ if (right < heap.length && compareFilePaths(heap[right], heap[largest]) > 0) {
43920
+ largest = right;
43802
43921
  }
43803
- let lstat3;
43922
+ if (largest === index)
43923
+ break;
43924
+ [heap[index], heap[largest]] = [heap[largest], heap[index]];
43925
+ index = largest;
43926
+ }
43927
+ }
43928
+ async function collectScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats, state) {
43929
+ const pending = [dir];
43930
+ while (pending.length > 0) {
43931
+ if (state.deadlineExceeded || state.cleanupFailed)
43932
+ return;
43933
+ const currentDir = pending.pop();
43934
+ if (!currentDir)
43935
+ continue;
43936
+ let directory;
43804
43937
  try {
43805
- lstat3 = fs31.lstatSync(fullPath);
43938
+ directory = fs31.opendirSync(currentDir);
43806
43939
  } catch {
43807
43940
  stats.fileErrors++;
43941
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "read_error");
43808
43942
  continue;
43809
43943
  }
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;
43944
+ let stoppedEarly = false;
43945
+ try {
43946
+ while (true) {
43947
+ if (state.entriesSinceYield >= DISCOVERY_YIELD_INTERVAL) {
43948
+ state.entriesSinceYield = 0;
43949
+ await _internals64.yieldToEventLoop();
43950
+ if (_internals64.now() >= state.deadline) {
43951
+ state.deadlineExceeded = true;
43952
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "deadline");
43953
+ stoppedEarly = true;
43954
+ break;
43955
+ }
43956
+ }
43957
+ const entry = directory.readSync();
43958
+ if (!entry)
43959
+ break;
43960
+ state.entriesSinceYield++;
43961
+ const fullPath = path77.join(currentDir, entry.name);
43962
+ const relPath = path77.relative(scanDir, fullPath).replace(/\\/g, "/");
43963
+ if (isExcluded(entry.name, relPath, excludeExact, excludeGlobs)) {
43964
+ stats.skippedDirs++;
43965
+ continue;
43966
+ }
43967
+ let lstat3;
43968
+ try {
43969
+ lstat3 = fs31.lstatSync(fullPath);
43970
+ } catch {
43971
+ stats.fileErrors++;
43972
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "read_error");
43973
+ continue;
43974
+ }
43975
+ if (lstat3.isSymbolicLink()) {
43976
+ stats.symlinkSkipped++;
43977
+ continue;
43978
+ }
43979
+ if (lstat3.isDirectory()) {
43980
+ if (state.directoriesQueued >= MAX_DISCOVERY_DIRECTORIES) {
43981
+ state.unexaminedDirectories++;
43982
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "directory_limit");
43983
+ continue;
43984
+ }
43985
+ let realPath;
43986
+ try {
43987
+ realPath = fs31.realpathSync(fullPath);
43988
+ } catch {
43989
+ stats.fileErrors++;
43990
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "read_error");
43991
+ continue;
43992
+ }
43993
+ if (isSymlinkLoop(realPath, visited) || !isPathWithinScope(realPath, scanDir)) {
43994
+ stats.symlinkSkipped++;
43995
+ continue;
43996
+ }
43997
+ pending.push(fullPath);
43998
+ state.directoriesQueued++;
43999
+ } else if (lstat3.isFile()) {
44000
+ const ext = path77.extname(fullPath).toLowerCase();
44001
+ if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
44002
+ addBoundedCandidate(state, fullPath);
44003
+ } else {
44004
+ stats.skippedFiles++;
44005
+ }
44006
+ } else {
44007
+ stats.fileErrors++;
44008
+ recordIncompletePath(state.incompletePaths, scanDir, fullPath, "non_file");
44009
+ }
43829
44010
  }
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++;
44011
+ } finally {
44012
+ try {
44013
+ _internals64.closeDirectory(directory);
44014
+ } catch (error2) {
44015
+ if (error2.code !== "ERR_DIR_CLOSED") {
44016
+ state.cleanupFailed = true;
44017
+ recordIncompletePath(state.incompletePaths, scanDir, currentDir, "cleanup_failed");
44018
+ }
43838
44019
  }
43839
44020
  }
44021
+ if (stoppedEarly)
44022
+ break;
43840
44023
  }
43841
- return files;
43842
44024
  }
43843
44025
  var secretscan = createSwarmTool({
43844
44026
  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 +44078,21 @@ var secretscan = createSwarmTool({
43896
44078
  }
43897
44079
  }
43898
44080
  try {
43899
- const _scanDirRaw = path77.resolve(directory);
44081
+ const scanDirRaw = path77.resolve(directory);
43900
44082
  const scanDir = (() => {
43901
44083
  try {
43902
- return fs31.realpathSync(_scanDirRaw);
44084
+ return fs31.realpathSync(scanDirRaw);
43903
44085
  } catch {
43904
- return _scanDirRaw;
44086
+ return scanDirRaw;
43905
44087
  }
43906
44088
  })();
43907
- if (!fs31.existsSync(scanDir)) {
44089
+ let dirStat;
44090
+ try {
44091
+ dirStat = fs31.lstatSync(scanDir);
44092
+ } catch (error2) {
44093
+ const err = error2;
43908
44094
  const errorResult = {
43909
- error: "directory not found",
44095
+ error: err.code === "ENOENT" ? "directory not found" : `scan failed: ${err.message || "unable to inspect directory"}`,
43910
44096
  scan_dir: directory,
43911
44097
  findings: [],
43912
44098
  count: 0,
@@ -43915,7 +44101,6 @@ var secretscan = createSwarmTool({
43915
44101
  };
43916
44102
  return JSON.stringify(errorResult, null, 2);
43917
44103
  }
43918
- const dirStat = fs31.statSync(scanDir);
43919
44104
  if (!dirStat.isDirectory()) {
43920
44105
  const errorResult = {
43921
44106
  error: "target must be a directory, not a file",
@@ -43947,36 +44132,74 @@ var secretscan = createSwarmTool({
43947
44132
  symlinkSkipped: 0
43948
44133
  };
43949
44134
  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);
44135
+ const deadline = _internals64.now() + SCAN_TIME_BUDGET_MS;
44136
+ const discovery = {
44137
+ files: [],
44138
+ totalCandidates: 0,
44139
+ deadline,
44140
+ deadlineExceeded: false,
44141
+ cleanupFailed: false,
44142
+ entriesSinceYield: 0,
44143
+ incompletePaths: [],
44144
+ directoriesQueued: 1,
44145
+ unexaminedDirectories: 0
44146
+ };
44147
+ await _internals64.yieldToEventLoop();
44148
+ await collectScannableFiles(scanDir, excludeExact, excludeGlobs, scanDir, visited, stats, discovery);
44149
+ const files = discovery.files;
44150
+ files.sort(compareFilePaths);
44151
+ const filesToScan = files;
44152
+ if (discovery.totalCandidates > filesToScan.length) {
44153
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "max_files");
44154
+ }
44155
+ if (discovery.deadlineExceeded) {
44156
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "deadline");
44157
+ }
44158
+ if (discovery.cleanupFailed) {
44159
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "cleanup_failed");
44160
+ }
43961
44161
  const allFindings = [];
43962
44162
  let filesScanned = 0;
43963
44163
  let skippedFiles = stats.skippedFiles;
43964
- for (const filePath of filesToScan) {
43965
- if (allFindings.length >= MAX_FINDINGS2)
44164
+ let incompleteFiles = stats.fileErrors + Math.max(0, discovery.totalCandidates - filesToScan.length) + (discovery.deadlineExceeded || discovery.cleanupFailed ? 1 : 0) + discovery.unexaminedDirectories;
44165
+ for (let index = 0;index < filesToScan.length; index++) {
44166
+ if (_internals64.now() >= deadline) {
44167
+ for (const remaining of filesToScan.slice(index)) {
44168
+ recordIncompletePath(discovery.incompletePaths, scanDir, remaining, "deadline");
44169
+ }
44170
+ incompleteFiles += filesToScan.length - index;
43966
44171
  break;
43967
- const fileFindings = scanFileForSecrets(filePath);
43968
- try {
43969
- const stat6 = fs31.statSync(filePath);
43970
- if (stat6.size > MAX_FILE_SIZE_BYTES3) {
44172
+ }
44173
+ if (allFindings.length >= MAX_FINDINGS2) {
44174
+ recordIncompletePath(discovery.incompletePaths, scanDir, scanDir, "truncated");
44175
+ incompleteFiles += filesToScan.length - index;
44176
+ break;
44177
+ }
44178
+ const outcome = scanFileForSecrets(filesToScan[index], "standalone");
44179
+ switch (outcome.status) {
44180
+ case "scanned":
44181
+ filesScanned++;
44182
+ break;
44183
+ case "skipped":
43971
44184
  skippedFiles++;
43972
- continue;
43973
- }
43974
- } catch {}
43975
- filesScanned++;
43976
- for (const finding of fileFindings) {
43977
- if (allFindings.length >= MAX_FINDINGS2)
43978
44185
  break;
43979
- allFindings.push(finding);
44186
+ case "incomplete":
44187
+ skippedFiles++;
44188
+ incompleteFiles++;
44189
+ recordIncompletePath(discovery.incompletePaths, scanDir, filesToScan[index], outcome.reason);
44190
+ break;
44191
+ default:
44192
+ assertNever(outcome);
44193
+ }
44194
+ if (outcome.status === "scanned") {
44195
+ for (const finding of outcome.findings) {
44196
+ if (allFindings.length >= MAX_FINDINGS2)
44197
+ break;
44198
+ allFindings.push(finding);
44199
+ }
44200
+ }
44201
+ if (index + 1 < filesToScan.length) {
44202
+ await _internals64.yieldToEventLoop();
43980
44203
  }
43981
44204
  }
43982
44205
  allFindings.sort((a, b) => {
@@ -43997,31 +44220,33 @@ var secretscan = createSwarmTool({
43997
44220
  findings: allFindings,
43998
44221
  count: allFindings.length,
43999
44222
  files_scanned: filesScanned,
44000
- skipped_files: skippedFiles + stats.fileErrors + stats.symlinkSkipped
44223
+ skipped_files: skippedFiles + stats.fileErrors + stats.symlinkSkipped,
44224
+ incomplete_files: incompleteFiles,
44225
+ incomplete_paths: discovery.incompletePaths
44001
44226
  };
44002
44227
  const parts = [];
44003
- if (files.length > MAX_FILES_SCANNED2) {
44004
- parts.push(`Found ${files.length} files, scanned ${MAX_FILES_SCANNED2}`);
44228
+ if (discovery.totalCandidates > MAX_FILES_SCANNED2) {
44229
+ parts.push(`Found ${discovery.totalCandidates} files, selected ${MAX_FILES_SCANNED2}`);
44230
+ }
44231
+ if (discovery.deadlineExceeded) {
44232
+ parts.push("Discovery stopped at the scan deadline");
44233
+ }
44234
+ if (discovery.cleanupFailed) {
44235
+ parts.push("Discovery directory cleanup failed");
44005
44236
  }
44006
44237
  if (allFindings.length >= MAX_FINDINGS2) {
44007
44238
  parts.push(`Results limited to ${MAX_FINDINGS2} findings`);
44008
44239
  }
44240
+ if (incompleteFiles > 0) {
44241
+ parts.push(`${incompleteFiles} files were not completely scanned`);
44242
+ }
44009
44243
  if (skippedFiles > 0 || stats.fileErrors > 0 || stats.symlinkSkipped > 0) {
44010
44244
  parts.push(`${skippedFiles + stats.fileErrors + stats.symlinkSkipped} files skipped (binary/oversized/symlinks/errors)`);
44011
44245
  }
44012
44246
  if (parts.length > 0) {
44013
44247
  result.message = `${parts.join("; ")}.`;
44014
44248
  }
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;
44249
+ return serializeSecretscanResult(result);
44025
44250
  } catch (e) {
44026
44251
  const errorResult = {
44027
44252
  error: e instanceof Error ? `scan failed: ${e.message || "internal error"}` : "scan failed: unknown error",
@@ -44057,14 +44282,89 @@ async function runSecretscanOnFiles(files, directory) {
44057
44282
  const findings = [];
44058
44283
  let filesScanned = 0;
44059
44284
  let skippedFiles = 0;
44060
- for (const file of files) {
44285
+ const incompletePaths = [];
44286
+ const rawRoot = path77.resolve(directory);
44287
+ const canonicalRoot = (() => {
44288
+ try {
44289
+ return fs31.realpathSync(rawRoot);
44290
+ } catch {
44291
+ return rawRoot;
44292
+ }
44293
+ })();
44294
+ let rootStat;
44295
+ try {
44296
+ rootStat = fs31.lstatSync(canonicalRoot);
44297
+ } catch (error2) {
44298
+ const err = error2;
44299
+ return {
44300
+ error: err.code === "ENOENT" ? "directory not found" : `scan failed: ${err.message || "unable to inspect directory"}`,
44301
+ scan_dir: directory,
44302
+ findings: [],
44303
+ count: 0,
44304
+ files_scanned: 0,
44305
+ skipped_files: 0
44306
+ };
44307
+ }
44308
+ if (!rootStat.isDirectory()) {
44309
+ throw new Error("target must be a directory");
44310
+ }
44311
+ const filesToScan = files.slice(0, MAX_EXPLICIT_FILES_SCANNED);
44312
+ let incompleteFiles = Math.max(0, files.length - filesToScan.length);
44313
+ if (files.length > filesToScan.length) {
44314
+ recordIncompletePath(incompletePaths, canonicalRoot, canonicalRoot, "max_files");
44315
+ }
44316
+ const deadline = _internals64.now() + SCAN_TIME_BUDGET_MS;
44317
+ await _internals64.yieldToEventLoop();
44318
+ for (let index = 0;index < filesToScan.length; index++) {
44319
+ if (_internals64.now() >= deadline) {
44320
+ for (const remaining of filesToScan.slice(index)) {
44321
+ const remainingPath = path77.isAbsolute(remaining) ? path77.resolve(remaining) : path77.resolve(rawRoot, remaining);
44322
+ recordIncompletePath(incompletePaths, canonicalRoot, remainingPath, "deadline");
44323
+ }
44324
+ incompleteFiles += filesToScan.length - index;
44325
+ break;
44326
+ }
44327
+ if (findings.length >= MAX_FINDINGS2) {
44328
+ recordIncompletePath(incompletePaths, canonicalRoot, canonicalRoot, "truncated");
44329
+ incompleteFiles += filesToScan.length - index;
44330
+ break;
44331
+ }
44332
+ const file = filesToScan[index];
44061
44333
  if (typeof file !== "string") {
44062
44334
  skippedFiles++;
44063
44335
  continue;
44064
44336
  }
44065
- const resolvedPath = path77.isAbsolute(file) ? path77.resolve(file) : path77.resolve(directory, file);
44066
- if (!isPathWithinScope(resolvedPath, directory)) {
44337
+ const resolvedPath = path77.isAbsolute(file) ? path77.resolve(file) : path77.resolve(rawRoot, file);
44338
+ if (!isPathWithinScope(resolvedPath, rawRoot)) {
44339
+ skippedFiles++;
44340
+ incompleteFiles++;
44341
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "scope_escape");
44342
+ continue;
44343
+ }
44344
+ let lstat3;
44345
+ try {
44346
+ lstat3 = _internals64.lstatFile(resolvedPath);
44347
+ } catch (error2) {
44348
+ const err = error2;
44349
+ if (err.code === "ENOENT") {
44350
+ skippedFiles++;
44351
+ continue;
44352
+ }
44353
+ skippedFiles++;
44354
+ incompleteFiles++;
44355
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "read_error");
44356
+ continue;
44357
+ }
44358
+ if (lstat3.isSymbolicLink()) {
44067
44359
  skippedFiles++;
44360
+ incompleteFiles++;
44361
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "symlink");
44362
+ continue;
44363
+ }
44364
+ if (!lstat3.isFile()) {
44365
+ skippedFiles++;
44366
+ incompleteFiles++;
44367
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "non_file");
44068
44368
  continue;
44069
44369
  }
44070
44370
  const ext = path77.extname(resolvedPath).toLowerCase();
@@ -44072,16 +44372,47 @@ async function runSecretscanOnFiles(files, directory) {
44072
44372
  skippedFiles++;
44073
44373
  continue;
44074
44374
  }
44075
- if (!fs31.existsSync(resolvedPath)) {
44375
+ let scanPath = resolvedPath;
44376
+ try {
44377
+ scanPath = fs31.realpathSync(resolvedPath);
44378
+ } catch (error2) {
44379
+ const err = error2;
44380
+ if (err.code === "ENOENT") {
44381
+ skippedFiles++;
44382
+ continue;
44383
+ }
44076
44384
  skippedFiles++;
44385
+ incompleteFiles++;
44386
+ recordIncompletePath(incompletePaths, canonicalRoot, resolvedPath, "read_error");
44077
44387
  continue;
44078
44388
  }
44079
- const fileFindings = scanFileForSecrets(resolvedPath);
44080
- filesScanned++;
44081
- findings.push(...fileFindings);
44082
- if (findings.length >= MAX_FINDINGS2) {
44083
- findings.length = MAX_FINDINGS2;
44084
- break;
44389
+ if (!isPathWithinScope(scanPath, canonicalRoot)) {
44390
+ skippedFiles++;
44391
+ incompleteFiles++;
44392
+ recordIncompletePath(incompletePaths, canonicalRoot, scanPath, "scope_escape");
44393
+ continue;
44394
+ }
44395
+ const outcome = scanFileForSecrets(scanPath, "explicit");
44396
+ switch (outcome.status) {
44397
+ case "scanned":
44398
+ filesScanned++;
44399
+ findings.push(...outcome.findings);
44400
+ if (findings.length > MAX_FINDINGS2)
44401
+ findings.length = MAX_FINDINGS2;
44402
+ break;
44403
+ case "skipped":
44404
+ skippedFiles++;
44405
+ break;
44406
+ case "incomplete":
44407
+ skippedFiles++;
44408
+ incompleteFiles++;
44409
+ recordIncompletePath(incompletePaths, canonicalRoot, scanPath, outcome.reason);
44410
+ break;
44411
+ default:
44412
+ assertNever(outcome);
44413
+ }
44414
+ if (index + 1 < filesToScan.length) {
44415
+ await _internals64.yieldToEventLoop();
44085
44416
  }
44086
44417
  }
44087
44418
  findings.sort((a, b) => {
@@ -44096,7 +44427,9 @@ async function runSecretscanOnFiles(files, directory) {
44096
44427
  findings,
44097
44428
  count: findings.length,
44098
44429
  files_scanned: filesScanned,
44099
- skipped_files: skippedFiles
44430
+ skipped_files: skippedFiles,
44431
+ incomplete_files: incompleteFiles,
44432
+ incomplete_paths: incompletePaths
44100
44433
  };
44101
44434
  } catch (e) {
44102
44435
  return {
@@ -44113,7 +44446,15 @@ var _internals64 = {
44113
44446
  secretscan,
44114
44447
  runSecretscan,
44115
44448
  runSecretscanOnFiles,
44116
- SECRET_PATTERNS: SECRET_PATTERNS2
44449
+ SECRET_PATTERNS: SECRET_PATTERNS2,
44450
+ now: Date.now,
44451
+ yieldToEventLoop,
44452
+ openFile: fs31.openSync,
44453
+ fstatFile: fs31.fstatSync,
44454
+ readFileChunk: fs31.readSync,
44455
+ closeFile: fs31.closeSync,
44456
+ lstatFile: fs31.lstatSync,
44457
+ closeDirectory: (directory) => directory.closeSync()
44117
44458
  };
44118
44459
 
44119
44460
  // src/tools/test-runner.ts
@@ -45362,21 +45703,21 @@ function hasCompoundTestExtension(filename) {
45362
45703
  const lower = filename.toLowerCase();
45363
45704
  return COMPOUND_TEST_EXTENSIONS.some((ext) => lower.endsWith(ext));
45364
45705
  }
45365
- function isLanguageSpecificTestFile(basename15) {
45366
- const lower = basename15.toLowerCase();
45706
+ function isLanguageSpecificTestFile(basename16) {
45707
+ const lower = basename16.toLowerCase();
45367
45708
  if (lower.endsWith("_test.go"))
45368
45709
  return true;
45369
45710
  if (lower.endsWith(".py") && (lower.startsWith("test_") || lower.endsWith("_test.py")))
45370
45711
  return true;
45371
45712
  if (lower.endsWith("_spec.rb"))
45372
45713
  return true;
45373
- if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename15) || basename15.endsWith("Test.java") || basename15.endsWith("Tests.java") || lower.endsWith("it.java")))
45714
+ if (lower.endsWith(".java") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.java") || basename16.endsWith("Tests.java") || lower.endsWith("it.java")))
45374
45715
  return true;
45375
45716
  if (lower.endsWith(".cs") && (lower.endsWith("test.cs") || lower.endsWith("tests.cs")))
45376
45717
  return true;
45377
- if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename15) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
45718
+ if (lower.endsWith(".kt") && (/^Test[A-Z]/.test(basename16) || lower.endsWith("test.kt") || lower.endsWith("tests.kt")))
45378
45719
  return true;
45379
- if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename15) || basename15.endsWith("Test.php") || basename15.endsWith("Tests.php")))
45720
+ if (lower.endsWith(".php") && (/^Test[A-Z]/.test(basename16) || basename16.endsWith("Test.php") || basename16.endsWith("Tests.php")))
45380
45721
  return true;
45381
45722
  if (lower.endsWith(".tests.ps1"))
45382
45723
  return true;
@@ -45384,23 +45725,23 @@ function isLanguageSpecificTestFile(basename15) {
45384
45725
  }
45385
45726
  function isConventionTestFilePath(filePath) {
45386
45727
  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);
45728
+ const basename16 = path81.basename(filePath);
45729
+ return hasCompoundTestExtension(basename16) || basename16.includes(".spec.") || basename16.includes(".test.") || isLanguageSpecificTestFile(basename16) || isTestDirectoryPath(normalizedPath);
45389
45730
  }
45390
45731
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45391
45732
  const testFiles = [];
45392
45733
  for (const file of sourceFiles) {
45393
45734
  const absoluteFile = resolveWorkspacePath(file, workingDir);
45394
45735
  const relativeFile = path81.relative(workingDir, absoluteFile);
45395
- const basename15 = path81.basename(absoluteFile);
45736
+ const basename16 = path81.basename(absoluteFile);
45396
45737
  const dirname30 = path81.dirname(absoluteFile);
45397
45738
  const preferRelativeOutput = !path81.isAbsolute(file);
45398
45739
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file)) {
45399
45740
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
45400
45741
  continue;
45401
45742
  }
45402
- const nameWithoutExt = basename15.replace(/\.[^.]+$/, "");
45403
- const ext = path81.extname(basename15);
45743
+ const nameWithoutExt = basename16.replace(/\.[^.]+$/, "");
45744
+ const ext = path81.extname(basename16);
45404
45745
  const genericTestNames = [
45405
45746
  `${nameWithoutExt}.spec${ext}`,
45406
45747
  `${nameWithoutExt}.test${ext}`
@@ -45411,7 +45752,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45411
45752
  ...languageSpecificTestNames
45412
45753
  ].map((candidateName) => path81.join(dirname30, candidateName));
45413
45754
  const testDirectoryNames = [
45414
- basename15,
45755
+ basename16,
45415
45756
  ...genericTestNames,
45416
45757
  ...languageSpecificTestNames
45417
45758
  ];
@@ -46782,6 +47123,13 @@ var DEFAULT_CONFIG = {
46782
47123
  testScope: "convention",
46783
47124
  linter: "biome"
46784
47125
  };
47126
+ function formatSecretscanIncompletePaths(incompletePaths, maxEntries = 3) {
47127
+ const visible = incompletePaths.slice(0, maxEntries).map((entry) => {
47128
+ return `${entry.path} (${entry.reason})`;
47129
+ });
47130
+ const suffix = incompletePaths.length > maxEntries ? ` (+${incompletePaths.length - maxEntries} more)` : "";
47131
+ return `${visible.join("; ")}${suffix}`;
47132
+ }
46785
47133
  function validateDirectoryPath(dir) {
46786
47134
  if (!dir || typeof dir !== "string") {
46787
47135
  throw new Error("Directory path is required");
@@ -47045,28 +47393,72 @@ async function runTestsCheck(_dir, scope, timeoutMs) {
47045
47393
  async function runSecretsCheck(dir, timeoutMs) {
47046
47394
  const startTime = Date.now();
47047
47395
  try {
47048
- const secretsPromise = runSecretscan(dir);
47396
+ const secretsPromise = _internals68.runSecretscan(dir);
47049
47397
  const timeoutPromise = new Promise((_, reject) => {
47050
47398
  setTimeout(() => {
47051
47399
  reject(new Error(`Secrets check timed out after ${timeoutMs}ms`));
47052
47400
  }, timeoutMs);
47053
47401
  });
47054
- const result = await Promise.race([
47055
- secretsPromise,
47056
- timeoutPromise
47057
- ]);
47058
- if (result.findings && result.findings.length > 0) {
47402
+ const result = await Promise.race([secretsPromise, timeoutPromise]);
47403
+ if ("error" in result) {
47404
+ return {
47405
+ type: "secrets",
47406
+ status: "fail",
47407
+ message: `Secrets check failed: ${result.error}`,
47408
+ details: {
47409
+ error: result.error,
47410
+ filesScanned: result.files_scanned,
47411
+ incompleteFiles: 0,
47412
+ incompletePaths: []
47413
+ },
47414
+ durationMs: Date.now() - startTime
47415
+ };
47416
+ }
47417
+ const findingsCount = Math.max(result.count, result.findings.length);
47418
+ if (typeof result.incomplete_files !== "number" || !Array.isArray(result.incomplete_paths)) {
47419
+ return {
47420
+ type: "secrets",
47421
+ status: "fail",
47422
+ message: "Secrets check failed: scanner returned incomplete coverage metadata",
47423
+ details: { filesScanned: result.files_scanned },
47424
+ durationMs: Date.now() - startTime
47425
+ };
47426
+ }
47427
+ const incompleteFiles = result.incomplete_files;
47428
+ const incompletePaths = result.incomplete_paths;
47429
+ const hasCountMismatch = result.count !== result.findings.length;
47430
+ const hasIncompleteCoverage = incompleteFiles > 0 || incompletePaths.length > 0;
47431
+ const hasZeroCoverage = result.files_scanned === 0;
47432
+ const failures = [];
47433
+ if (findingsCount > 0) {
47059
47434
  const critical = result.findings.filter((f) => f.severity === "critical").length;
47060
47435
  const high = result.findings.filter((f) => f.severity === "high").length;
47436
+ failures.push(`Found ${findingsCount} secret(s): ${critical} critical, ${high} high`);
47437
+ }
47438
+ if (hasIncompleteCoverage) {
47439
+ const incompleteSummary = incompletePaths.length > 0 ? `; incomplete paths: ${formatSecretscanIncompletePaths(incompletePaths)}` : "";
47440
+ failures.push(`${incompleteFiles} incomplete file(s)${incompleteSummary}`);
47441
+ }
47442
+ if (hasCountMismatch) {
47443
+ failures.push(`findings/count mismatch (reported ${result.count}, actual ${result.findings.length})`);
47444
+ }
47445
+ if (hasZeroCoverage) {
47446
+ failures.push("zero requested files scanned");
47447
+ }
47448
+ if (failures.length > 0) {
47061
47449
  return {
47062
47450
  type: "secrets",
47063
47451
  status: "fail",
47064
- message: `Found ${result.findings.length} secret(s): ${critical} critical, ${high} high`,
47452
+ message: `Secrets check failed: ${failures.join("; ")}`,
47065
47453
  details: {
47066
- count: result.count,
47067
- critical,
47068
- high,
47069
- filesScanned: result.files_scanned
47454
+ count: findingsCount,
47455
+ reportedCount: result.count,
47456
+ critical: result.findings.filter((f) => f.severity === "critical").length,
47457
+ high: result.findings.filter((f) => f.severity === "high").length,
47458
+ filesScanned: result.files_scanned,
47459
+ skippedFiles: result.skipped_files,
47460
+ incompleteFiles,
47461
+ incompletePaths
47070
47462
  },
47071
47463
  durationMs: Date.now() - startTime
47072
47464
  };
@@ -47076,7 +47468,10 @@ async function runSecretsCheck(dir, timeoutMs) {
47076
47468
  status: "pass",
47077
47469
  message: "No secrets detected",
47078
47470
  details: {
47079
- filesScanned: result.files_scanned
47471
+ filesScanned: result.files_scanned,
47472
+ skippedFiles: result.skipped_files,
47473
+ incompleteFiles,
47474
+ incompletePaths
47080
47475
  },
47081
47476
  durationMs: Date.now() - startTime
47082
47477
  };
@@ -47404,6 +47799,7 @@ var _internals68 = {
47404
47799
  runVersionCheck,
47405
47800
  runLintCheck,
47406
47801
  runTestsCheck,
47802
+ runSecretscan,
47407
47803
  runSecretsCheck,
47408
47804
  runEvidenceCheck,
47409
47805
  runRequirementCoverageCheck
@@ -52403,7 +52799,7 @@ import {
52403
52799
  openSync as openSync12,
52404
52800
  readFileSync as readFileSync39,
52405
52801
  renameSync as renameSync18,
52406
- statSync as statSync16,
52802
+ statSync as statSync15,
52407
52803
  unlinkSync as unlinkSync20,
52408
52804
  writeFileSync as writeFileSync17,
52409
52805
  writeFileSync as writeFileSyncAsync
@@ -52493,7 +52889,7 @@ var _internals78 = {
52493
52889
  renameSync: renameSync18,
52494
52890
  unlinkSync: unlinkSync20,
52495
52891
  existsSync: existsSync53,
52496
- statSync: statSync16
52892
+ statSync: statSync15
52497
52893
  };
52498
52894
  async function appendEvent(directory, eventInput) {
52499
52895
  if (!isValidSkillSlug(eventInput.skillSlug)) {
@@ -55596,7 +55992,7 @@ function buildDetailedHelp(commandName, entry) {
55596
55992
  async function handleHelpCommand(ctx) {
55597
55993
  const targetCommand = ctx.args.join(" ");
55598
55994
  if (!targetCommand) {
55599
- const { buildHelpText } = await import("./index-ag7ppc40.js");
55995
+ const { buildHelpText } = await import("./index-6f6nt0c9.js");
55600
55996
  return buildHelpText();
55601
55997
  }
55602
55998
  const tokens = targetCommand.split(/\s+/);
@@ -55605,7 +56001,7 @@ async function handleHelpCommand(ctx) {
55605
56001
  return _internals14.buildDetailedHelp(resolved.key, resolved.entry);
55606
56002
  }
55607
56003
  const similar = _internals14.findSimilarCommands(targetCommand);
55608
- const { buildHelpText: fullHelp } = await import("./index-ag7ppc40.js");
56004
+ const { buildHelpText: fullHelp } = await import("./index-6f6nt0c9.js");
55609
56005
  if (similar.length > 0) {
55610
56006
  return `Command '/swarm ${targetCommand}' not found.
55611
56007
 
@@ -55782,7 +56178,7 @@ var COMMAND_REGISTRY = {
55782
56178
  },
55783
56179
  "guardrail explain": {
55784
56180
  handler: async (ctx) => {
55785
- const { handleGuardrailExplain } = await import("./guardrail-explain-ph1n3ryc.js");
56181
+ const { handleGuardrailExplain } = await import("./guardrail-explain-zs3hq02c.js");
55786
56182
  return handleGuardrailExplain(ctx.directory, ctx.args);
55787
56183
  },
55788
56184
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -55792,7 +56188,7 @@ var COMMAND_REGISTRY = {
55792
56188
  },
55793
56189
  "guardrail-explain": {
55794
56190
  handler: async (ctx) => {
55795
- const { handleGuardrailExplain } = await import("./guardrail-explain-ph1n3ryc.js");
56191
+ const { handleGuardrailExplain } = await import("./guardrail-explain-zs3hq02c.js");
55796
56192
  return handleGuardrailExplain(ctx.directory, ctx.args);
55797
56193
  },
55798
56194
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -55802,7 +56198,7 @@ var COMMAND_REGISTRY = {
55802
56198
  },
55803
56199
  "guardrail-log": {
55804
56200
  handler: async (ctx) => {
55805
- const { handleGuardrailLog } = await import("./guardrail-log-ghgqaw3w.js");
56201
+ const { handleGuardrailLog } = await import("./guardrail-log-90dnv508.js");
55806
56202
  return handleGuardrailLog(ctx.directory, ctx.args);
55807
56203
  },
55808
56204
  description: "Read the guardrail decision log (use --blocks-only for blocks)",