opencode-swarm 7.99.5 → 7.99.7

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.
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.99.5",
72
+ version: "7.99.7",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -17516,21 +17516,6 @@ var init_bundled_skills = __esm(() => {
17516
17516
  syncedProjectSkillTargets = new Set;
17517
17517
  });
17518
17518
 
17519
- // src/utils/errors.ts
17520
- var SwarmError;
17521
- var init_errors3 = __esm(() => {
17522
- SwarmError = class SwarmError extends Error {
17523
- code;
17524
- guidance;
17525
- constructor(message, code, guidance) {
17526
- super(message);
17527
- this.name = "SwarmError";
17528
- this.code = code;
17529
- this.guidance = guidance;
17530
- }
17531
- };
17532
- });
17533
-
17534
17519
  // src/utils/logger.ts
17535
17520
  function isDebug() {
17536
17521
  return process.env.OPENCODE_SWARM_DEBUG === "1";
@@ -17573,6 +17558,21 @@ function error48(message, data) {
17573
17558
  }
17574
17559
  var init_logger = () => {};
17575
17560
 
17561
+ // src/utils/errors.ts
17562
+ var SwarmError;
17563
+ var init_errors3 = __esm(() => {
17564
+ SwarmError = class SwarmError extends Error {
17565
+ code;
17566
+ guidance;
17567
+ constructor(message, code, guidance) {
17568
+ super(message);
17569
+ this.name = "SwarmError";
17570
+ this.code = code;
17571
+ this.guidance = guidance;
17572
+ }
17573
+ };
17574
+ });
17575
+
17576
17576
  // src/utils/regex.ts
17577
17577
  function escapeRegex2(s) {
17578
17578
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -18141,6 +18141,9 @@ var init_swarm_artifact_cache = __esm(() => {
18141
18141
  // src/hooks/utils.ts
18142
18142
  import * as fs4 from "node:fs";
18143
18143
  import * as path5 from "node:path";
18144
+ function isFailClosedHandler(value) {
18145
+ return typeof value === "function" && Object.hasOwn(value, "__failClosed") && value.__failClosed === true;
18146
+ }
18144
18147
  function safeHook(fn2) {
18145
18148
  return async (input, output) => {
18146
18149
  try {
@@ -18162,6 +18165,9 @@ function composeHandlers(...fns) {
18162
18165
  }
18163
18166
  return async (input, output) => {
18164
18167
  for (const fn2 of fns) {
18168
+ if (isFailClosedHandler(fn2)) {
18169
+ throw new Error("composeHandlers cannot wrap fail-closed handlers; use composeBlockingHandlers or await them directly");
18170
+ }
18165
18171
  const safeFn = _internals3.safeHook(fn2);
18166
18172
  await safeFn(input, output);
18167
18173
  }
@@ -43377,6 +43383,10 @@ function createToolBeforeHandler(ctx) {
43377
43383
  return;
43378
43384
  const executor = await getExecutor();
43379
43385
  if (!executor || !executor.isAvailable()) {
43386
+ if (!hasWarnedSandboxUnavailable) {
43387
+ hasWarnedSandboxUnavailable = true;
43388
+ warn("[guardrails] sandbox executor unavailable; shell commands will run unsandboxed");
43389
+ }
43380
43390
  appendGuardrailDecision({
43381
43391
  type: "sandbox_skip",
43382
43392
  ts: new Date().toISOString(),
@@ -43875,6 +43885,15 @@ function createToolBeforeHandler(ctx) {
43875
43885
  handleDelegatedWriteTracking(input.sessionID, input.tool, output.args);
43876
43886
  handleLoopDetection(input.sessionID, input.tool, output.args);
43877
43887
  handleTestSuiteBlocking(input.tool, output.args);
43888
+ const agentNameForSandbox = (() => {
43889
+ const rawAgent = swarmState.activeAgent.get(input.sessionID);
43890
+ return rawAgent ? stripKnownSwarmPrefix(rawAgent) : "unknown";
43891
+ })();
43892
+ const rawShellCommand = (() => {
43893
+ const bashArgs = output.args;
43894
+ return typeof bashArgs?.command === "string" ? bashArgs.command : "";
43895
+ })();
43896
+ await applySandboxExecution(input.sessionID, input.tool, output.args, agentNameForSandbox, rawShellCommand, shellAuditPath, shellAuditEnabled);
43878
43897
  const normalizedAuditTool = normalizeToolName(input.tool).toLowerCase();
43879
43898
  if (normalizedAuditTool === "bash" || normalizedAuditTool === "shell") {
43880
43899
  appendGuardrailDecision({
@@ -44221,17 +44240,10 @@ function createToolBeforeHandler(ctx) {
44221
44240
  elapsedMinutes,
44222
44241
  repetitionCount
44223
44242
  });
44224
- await applySandboxExecution(input.sessionID, input.tool, output.args, (() => {
44225
- const rawAgent = swarmState.activeAgent.get(input.sessionID);
44226
- return rawAgent ? stripKnownSwarmPrefix(rawAgent) : "unknown";
44227
- })(), (() => {
44228
- const bashArgs = output.args;
44229
- const rawCmd = typeof bashArgs?.command === "string" ? bashArgs.command : "";
44230
- return rawCmd;
44231
- })(), shellAuditPath, shellAuditEnabled);
44232
44243
  setStoredInputArgs(input.callID, output.args);
44233
44244
  };
44234
44245
  }
44246
+ var hasWarnedSandboxUnavailable = false;
44235
44247
  var init_tool_before = __esm(() => {
44236
44248
  init_tool_policy();
44237
44249
  init_constants();
@@ -76654,26 +76666,45 @@ function writeVersionCache(entry) {
76654
76666
  writeFileSync13(cacheFile(), JSON.stringify(entry, null, 2), "utf-8");
76655
76667
  } catch {}
76656
76668
  }
76669
+ function parseVersion(version3) {
76670
+ const trimmed = version3.trim();
76671
+ const match = SEMVER_REGEX.exec(trimmed);
76672
+ if (!match)
76673
+ return null;
76674
+ return {
76675
+ major: Number.parseInt(match[1], 10),
76676
+ minor: Number.parseInt(match[2], 10),
76677
+ patch: Number.parseInt(match[3], 10),
76678
+ prerelease: match[4] ?? null
76679
+ };
76680
+ }
76681
+ function normalizeVersionCandidate(version3) {
76682
+ if (typeof version3 !== "string")
76683
+ return null;
76684
+ const trimmed = version3.trim();
76685
+ return SEMVER_REGEX.test(trimmed) ? trimmed : null;
76686
+ }
76657
76687
  function compareVersions(a, b) {
76658
- const [aBase, aPre] = a.split("-", 2);
76659
- const [bBase, bPre] = b.split("-", 2);
76660
- const aParts = aBase.split(".").map((n) => Number.parseInt(n, 10) || 0);
76661
- const bParts = bBase.split(".").map((n) => Number.parseInt(n, 10) || 0);
76662
- const len = Math.max(aParts.length, bParts.length);
76663
- for (let i = 0;i < len; i++) {
76664
- const av = aParts[i] ?? 0;
76665
- const bv = bParts[i] ?? 0;
76666
- if (av > bv)
76667
- return 1;
76668
- if (av < bv)
76669
- return -1;
76688
+ const aParsed = parseVersion(a);
76689
+ const bParsed = parseVersion(b);
76690
+ if (!aParsed || !bParsed)
76691
+ return 0;
76692
+ if (aParsed.major !== bParsed.major) {
76693
+ return aParsed.major > bParsed.major ? 1 : -1;
76670
76694
  }
76671
- if (aPre && !bPre)
76695
+ if (aParsed.minor !== bParsed.minor) {
76696
+ return aParsed.minor > bParsed.minor ? 1 : -1;
76697
+ }
76698
+ if (aParsed.patch !== bParsed.patch) {
76699
+ return aParsed.patch > bParsed.patch ? 1 : -1;
76700
+ }
76701
+ if (aParsed.prerelease && !bParsed.prerelease)
76672
76702
  return -1;
76673
- if (!aPre && bPre)
76703
+ if (!aParsed.prerelease && bParsed.prerelease)
76674
76704
  return 1;
76675
- if (aPre && bPre)
76676
- return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;
76705
+ if (aParsed.prerelease && bParsed.prerelease) {
76706
+ return aParsed.prerelease < bParsed.prerelease ? -1 : aParsed.prerelease > bParsed.prerelease ? 1 : 0;
76707
+ }
76677
76708
  return 0;
76678
76709
  }
76679
76710
  async function fetchLatestVersion(signal) {
@@ -76731,7 +76762,8 @@ async function runVersionCheck(runningVersion, emitWarning, now, fetchImpl) {
76731
76762
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
76732
76763
  let npmLatest = null;
76733
76764
  try {
76734
- npmLatest = await fetchImpl(controller.signal);
76765
+ const fetchedVersion = await fetchImpl(controller.signal);
76766
+ npmLatest = normalizeVersionCandidate(fetchedVersion);
76735
76767
  } finally {
76736
76768
  clearTimeout(timeout);
76737
76769
  }
@@ -76745,9 +76777,10 @@ function maybeWarn(runningVersion, npmLatest, emitWarning) {
76745
76777
  emitWarning(`[opencode-swarm] Update available: ${runningVersion} → ${npmLatest}. ` + "OpenCode caches plugins indefinitely. Run `bunx opencode-swarm update` to refresh.");
76746
76778
  }
76747
76779
  }
76748
- var NPM_REGISTRY_URL = "https://registry.npmjs.org/opencode-swarm/latest", CHECK_INTERVAL_MS, FETCH_TIMEOUT_MS = 5000, MAX_RESPONSE_BYTES, STRICT_SEMVER, _checkLatched = false, _internals43;
76780
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/opencode-swarm/latest", CHECK_INTERVAL_MS, FETCH_TIMEOUT_MS = 5000, SEMVER_REGEX, MAX_RESPONSE_BYTES, STRICT_SEMVER, _checkLatched = false, _internals43;
76749
76781
  var init_version_check = __esm(() => {
76750
76782
  CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
76783
+ SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
76751
76784
  MAX_RESPONSE_BYTES = 256 * 1024;
76752
76785
  STRICT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
76753
76786
  _internals43 = {
@@ -101085,6 +101118,13 @@ function findSimilarCommands(query) {
101085
101118
  scored.sort((a, b) => a.score - b.score);
101086
101119
  return scored.slice(0, 3).map((s) => s.cmd);
101087
101120
  }
101121
+ function emitValidationWarnings(prefix, warnings) {
101122
+ if (warnings.length === 0)
101123
+ return;
101124
+ warn(`${prefix}:
101125
+ ${warnings.join(`
101126
+ `)}`);
101127
+ }
101088
101128
  function buildDetailedHelp(commandName, entry) {
101089
101129
  const lines = [];
101090
101130
  lines.push(`## /swarm ${commandName}`, "");
@@ -101219,6 +101259,7 @@ function resolveCommand(tokens) {
101219
101259
  var COMMAND_REGISTRY, VALID_COMMANDS, _internals15, validation;
101220
101260
  var init_registry = __esm(() => {
101221
101261
  init_bundled_skills();
101262
+ init_logger();
101222
101263
  init_acknowledge_spec_drift();
101223
101264
  init_agents();
101224
101265
  init_archive();
@@ -102147,6 +102188,7 @@ Subcommands:
102147
102188
  handleHelpCommand,
102148
102189
  validateAliases,
102149
102190
  validateToolPolicy,
102191
+ emitValidationWarnings,
102150
102192
  resolveCommand,
102151
102193
  levenshteinDistance: levenshteinDistance2,
102152
102194
  findSimilarCommands,
@@ -102158,20 +102200,12 @@ Subcommands:
102158
102200
  ${validation.errors.join(`
102159
102201
  `)}`);
102160
102202
  }
102161
- if (validation.warnings.length > 0) {
102162
- console.warn(`COMMAND_REGISTRY alias warnings:
102163
- ${validation.warnings.join(`
102164
- `)}`);
102165
- }
102203
+ _internals15.emitValidationWarnings("COMMAND_REGISTRY alias warnings", validation.warnings);
102166
102204
  try {
102167
102205
  const toolPolicyValidation = _internals15.validateToolPolicy();
102168
- if (toolPolicyValidation.warnings.length > 0) {
102169
- console.warn(`COMMAND_REGISTRY toolPolicy warnings:
102170
- ${toolPolicyValidation.warnings.join(`
102171
- `)}`);
102172
- }
102206
+ _internals15.emitValidationWarnings("COMMAND_REGISTRY toolPolicy warnings", toolPolicyValidation.warnings);
102173
102207
  } catch (e) {
102174
- console.warn(`COMMAND_REGISTRY toolPolicy validation failed (non-fatal): ${e.message}`);
102208
+ warn(`COMMAND_REGISTRY toolPolicy validation failed (non-fatal): ${e.message}`);
102175
102209
  }
102176
102210
  });
102177
102211
 
@@ -116575,6 +116609,7 @@ var SUPPORTED_EXTENSIONS = new Set(LANGUAGE_REGISTRY.getAll().filter((p) => !p.p
116575
116609
  var DEFAULT_WALK_FILE_CAP = 1e4;
116576
116610
  var DEFAULT_WALK_BUDGET_MS = 5000;
116577
116611
  var ASYNC_WALK_YIELD_INTERVAL = 200;
116612
+ var MAX_DIAGNOSTIC_ENTRIES = 200;
116578
116613
  var EXTENSION_TO_LANGUAGE = {};
116579
116614
  for (const profile of LANGUAGE_REGISTRY.getAll()) {
116580
116615
  if (profile.parserOnly)
@@ -116705,6 +116740,64 @@ function isRefusedWorkspaceRoot(target) {
116705
116740
  }
116706
116741
  return refused.has(resolved);
116707
116742
  }
116743
+ function createEmptyDiagnostics() {
116744
+ return {
116745
+ extractionFailures: [],
116746
+ unresolvedImports: [],
116747
+ oversizedFiles: [],
116748
+ unsupportedFiles: [],
116749
+ binaryFiles: [],
116750
+ unreadableFiles: [],
116751
+ lowConfidenceEdgeCount: 0
116752
+ };
116753
+ }
116754
+ function diagnosticsHaveEntries(diagnostics) {
116755
+ return (diagnostics.extractionFailures?.length ?? 0) > 0 || (diagnostics.unresolvedImports?.length ?? 0) > 0 || (diagnostics.oversizedFiles?.length ?? 0) > 0 || (diagnostics.unsupportedFiles?.length ?? 0) > 0 || (diagnostics.binaryFiles?.length ?? 0) > 0 || (diagnostics.unreadableFiles?.length ?? 0) > 0 || (diagnostics.lowConfidenceEdgeCount ?? 0) > 0;
116756
+ }
116757
+ function pushCapped(target, value) {
116758
+ if (target.length < MAX_DIAGNOSTIC_ENTRIES) {
116759
+ target.push(value);
116760
+ }
116761
+ }
116762
+ function mergeDiagnostics(target, source) {
116763
+ if (!source)
116764
+ return;
116765
+ for (const entry of source.extractionFailures ?? []) {
116766
+ pushCapped(target.extractionFailures, entry);
116767
+ }
116768
+ for (const entry of source.unresolvedImports ?? []) {
116769
+ pushCapped(target.unresolvedImports, entry);
116770
+ }
116771
+ for (const entry of source.oversizedFiles ?? []) {
116772
+ pushCapped(target.oversizedFiles, entry);
116773
+ }
116774
+ for (const entry of source.unsupportedFiles ?? []) {
116775
+ pushCapped(target.unsupportedFiles, entry);
116776
+ }
116777
+ for (const entry of source.binaryFiles ?? []) {
116778
+ pushCapped(target.binaryFiles, entry);
116779
+ }
116780
+ for (const entry of source.unreadableFiles ?? []) {
116781
+ pushCapped(target.unreadableFiles, entry);
116782
+ }
116783
+ target.lowConfidenceEdgeCount += source.lowConfidenceEdgeCount ?? 0;
116784
+ }
116785
+ function isRelativeImportSpecifier(specifier) {
116786
+ return specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../");
116787
+ }
116788
+ function unresolvedRelativeImportsFor(parsedImports, filePath, absoluteRoot) {
116789
+ const unresolved = [];
116790
+ const moduleName = toModuleName(filePath, absoluteRoot);
116791
+ for (const parsed of parsedImports) {
116792
+ if (!isRelativeImportSpecifier(parsed.specifier))
116793
+ continue;
116794
+ const resolvedTarget = resolveModuleSpecifier(absoluteRoot, filePath, parsed.specifier);
116795
+ if (resolvedTarget === null) {
116796
+ unresolved.push({ file: moduleName, specifier: parsed.specifier });
116797
+ }
116798
+ }
116799
+ return unresolved;
116800
+ }
116708
116801
  var REGEX_ALLOWED_AFTER = new Set("(,=:[!&|?{};*+-~^<>%".split(""));
116709
116802
  function stripComments2(content) {
116710
116803
  let out = "";
@@ -116897,6 +116990,28 @@ function computeUsedSymbols(strippedContent, bindings) {
116897
116990
  }
116898
116991
  return [...used].sort((a, b) => a.localeCompare(b));
116899
116992
  }
116993
+ function usedSymbolsForImport(parsed, strippedContent) {
116994
+ if (parsed.importType === "namespace" || parsed.importType === "sideeffect" || parsed.importType === "require") {
116995
+ return;
116996
+ }
116997
+ if (parsed.reExport) {
116998
+ return [...new Set(parsed.bindings.map((b) => b.imported))].sort((a, b) => a.localeCompare(b));
116999
+ }
117000
+ return computeUsedSymbols(strippedContent, parsed.bindings);
117001
+ }
117002
+ function collectExports(symbols2) {
117003
+ const exported = symbols2.filter((s) => s.exported);
117004
+ const exports = exported.map((s) => s.signature === `default ${s.name}` ? "default" : s.name);
117005
+ const exportLines = {};
117006
+ for (let i = 0;i < exported.length; i++) {
117007
+ const s = exported[i];
117008
+ const name = exports[i];
117009
+ if (typeof s.line === "number" && Number.isFinite(s.line) && exportLines[name] === undefined) {
117010
+ exportLines[name] = s.line;
117011
+ }
117012
+ }
117013
+ return { exports, exportLines };
117014
+ }
116900
117015
  function parseImportedSymbols(matchedString, importType) {
116901
117016
  if (importType === "namespace")
116902
117017
  return ["*"];
@@ -117023,44 +117138,126 @@ function isBinaryContent(content) {
117023
117138
  }
117024
117139
  return false;
117025
117140
  }
117141
+ function scanFile(filePath, absoluteRoot, maxFileSize) {
117142
+ let content;
117143
+ let fileStats;
117144
+ try {
117145
+ fileStats = fsSync8.statSync(filePath);
117146
+ if (fileStats.size > maxFileSize) {
117147
+ return { node: null, edges: [] };
117148
+ }
117149
+ content = fsSync8.readFileSync(filePath, "utf-8");
117150
+ } catch {
117151
+ return { node: null, edges: [] };
117152
+ }
117153
+ if (isBinaryContent(content)) {
117154
+ return { node: null, edges: [] };
117155
+ }
117156
+ const ext = path134.extname(filePath).toLowerCase();
117157
+ let exports = [];
117158
+ let exportLines = {};
117159
+ try {
117160
+ if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
117161
+ const relativePath = path134.relative(absoluteRoot, filePath);
117162
+ ({ exports, exportLines } = collectExports(_internals82.extractTSSymbols(relativePath, absoluteRoot)));
117163
+ } else if (ext === ".py") {
117164
+ const relativePath = path134.relative(absoluteRoot, filePath);
117165
+ ({ exports, exportLines } = collectExports(_internals82.extractPythonSymbols(relativePath, absoluteRoot)));
117166
+ }
117167
+ const parsedImports = _internals82.parseFileImports(content);
117168
+ const strippedForUsage = parsedImports.length > 0 ? _internals82.stripComments(content) : "";
117169
+ const moduleName = toModuleName(filePath, absoluteRoot);
117170
+ const node = {
117171
+ filePath,
117172
+ moduleName,
117173
+ exports,
117174
+ ...Object.keys(exportLines).length > 0 ? { exportLines } : {},
117175
+ imports: parsedImports.map((p) => p.specifier),
117176
+ language: getLanguage(filePath),
117177
+ mtime: fileStats.mtime.toISOString(),
117178
+ ontology: _internals82.extractFileOntology({
117179
+ moduleName,
117180
+ filePath,
117181
+ content,
117182
+ language: getLanguage(filePath),
117183
+ exports,
117184
+ imports: parsedImports.map((p) => p.specifier)
117185
+ })
117186
+ };
117187
+ const edges = [];
117188
+ const sortedImports = [...parsedImports].sort((a, b) => a.specifier.localeCompare(b.specifier));
117189
+ for (const parsed of sortedImports) {
117190
+ const resolvedTarget = resolveModuleSpecifier(absoluteRoot, filePath, parsed.specifier);
117191
+ if (resolvedTarget !== null) {
117192
+ const usedSymbols = usedSymbolsForImport(parsed, strippedForUsage);
117193
+ edges.push({
117194
+ source: filePath,
117195
+ target: resolvedTarget,
117196
+ importSpecifier: parsed.specifier,
117197
+ importType: parsed.importType,
117198
+ importedSymbols: parsed.importedSymbols,
117199
+ ...usedSymbols !== undefined ? { usedSymbols } : {}
117200
+ });
117201
+ }
117202
+ }
117203
+ return { node, edges };
117204
+ } catch {
117205
+ return { node: null, edges: [] };
117206
+ }
117207
+ }
117026
117208
  async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
117027
117209
  let content;
117028
117210
  let fileStats;
117029
117211
  try {
117030
117212
  fileStats = fsSync8.statSync(filePath);
117031
117213
  if (fileStats.size > maxFileSize) {
117032
- return { node: null, edges: [], symbolEdges: [] };
117214
+ return {
117215
+ node: null,
117216
+ edges: [],
117217
+ symbolEdges: [],
117218
+ diagnostics: { oversizedFiles: [toModuleName(filePath, absoluteRoot)] }
117219
+ };
117033
117220
  }
117034
117221
  content = fsSync8.readFileSync(filePath, "utf-8");
117035
117222
  } catch {
117036
- return { node: null, edges: [], symbolEdges: [] };
117223
+ return {
117224
+ node: null,
117225
+ edges: [],
117226
+ symbolEdges: [],
117227
+ diagnostics: { unreadableFiles: [toModuleName(filePath, absoluteRoot)] }
117228
+ };
117037
117229
  }
117038
117230
  if (isBinaryContent(content)) {
117039
- return { node: null, edges: [], symbolEdges: [] };
117231
+ return {
117232
+ node: null,
117233
+ edges: [],
117234
+ symbolEdges: [],
117235
+ diagnostics: { binaryFiles: [toModuleName(filePath, absoluteRoot)] }
117236
+ };
117040
117237
  }
117041
117238
  const grammarId = getLanguage(filePath);
117042
117239
  const facts = await _internals82.extractFileSymbols(grammarId, content);
117043
117240
  if (facts === null) {
117044
- const moduleName2 = toModuleName(filePath, absoluteRoot);
117241
+ const fallback = scanFile(filePath, absoluteRoot, maxFileSize);
117242
+ let parsedImports = [];
117243
+ try {
117244
+ parsedImports = _internals82.parseFileImports(content);
117245
+ } catch {
117246
+ parsedImports = [];
117247
+ }
117045
117248
  return {
117046
- node: {
117047
- filePath,
117048
- moduleName: moduleName2,
117049
- exports: [],
117050
- imports: [],
117051
- language: grammarId,
117052
- mtime: fileStats.mtime.toISOString(),
117053
- ontology: _internals82.extractFileOntology({
117054
- moduleName: moduleName2,
117055
- filePath,
117056
- content,
117057
- language: grammarId,
117058
- exports: [],
117059
- imports: []
117060
- })
117061
- },
117062
- edges: [],
117063
- symbolEdges: []
117249
+ ...fallback,
117250
+ symbolEdges: [],
117251
+ diagnostics: {
117252
+ extractionFailures: [
117253
+ {
117254
+ file: toModuleName(filePath, absoluteRoot),
117255
+ language: grammarId,
117256
+ reason: "symbol_extraction_failed"
117257
+ }
117258
+ ],
117259
+ unresolvedImports: unresolvedRelativeImportsFor(parsedImports, filePath, absoluteRoot)
117260
+ }
117064
117261
  };
117065
117262
  }
117066
117263
  const exportedDefs = facts.defs.filter((d) => d.exported);
@@ -117111,6 +117308,10 @@ async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
117111
117308
  });
117112
117309
  }
117113
117310
  }
117311
+ const unresolvedImports = sortedImports.filter((imp) => isRelativeImportSpecifier(imp.specifier)).filter((imp) => resolveModuleSpecifier(absoluteRoot, filePath, imp.specifier) === null).map((imp) => ({
117312
+ file: moduleName,
117313
+ specifier: imp.specifier
117314
+ }));
117114
117315
  const symbolEdges = [];
117115
117316
  const localToImported = new Map;
117116
117317
  for (const imp of facts.imports) {
@@ -117141,7 +117342,12 @@ async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
117141
117342
  toSymbol: mapping.imported
117142
117343
  });
117143
117344
  }
117144
- return { node, edges, symbolEdges };
117345
+ return {
117346
+ node,
117347
+ edges,
117348
+ symbolEdges,
117349
+ diagnostics: unresolvedImports.length > 0 ? { unresolvedImports } : undefined
117350
+ };
117145
117351
  }
117146
117352
  async function buildWorkspaceGraphAsync(workspaceRoot, options) {
117147
117353
  validateWorkspace(workspaceRoot);
@@ -117163,6 +117369,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
117163
117369
  skippedFiles: 0,
117164
117370
  truncated: false
117165
117371
  };
117372
+ const diagnostics = createEmptyDiagnostics();
117166
117373
  const sourceFiles = await findSourceFilesAsync(absoluteRoot, stats2, {
117167
117374
  walkBudgetMs,
117168
117375
  maxFiles,
@@ -117183,6 +117390,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
117183
117390
  let processedSinceYield = 0;
117184
117391
  for (const filePath of sourceFiles) {
117185
117392
  const result = await scanFileAsync(filePath, absoluteRoot, maxFileSize);
117393
+ mergeDiagnostics(diagnostics, result.diagnostics);
117186
117394
  if (result.node) {
117187
117395
  let appended = false;
117188
117396
  try {
@@ -117223,6 +117431,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
117223
117431
  if (allSymbolEdges.length > 0) {
117224
117432
  graph.symbolEdges = allSymbolEdges;
117225
117433
  }
117434
+ graph.diagnostics = diagnosticsHaveEntries(diagnostics) ? diagnostics : createEmptyDiagnostics();
117226
117435
  if (stats2.skippedFiles > 0 || stats2.skippedDirs > 0 || stats2.truncated) {
117227
117436
  log(`[repo-graph] Scan stats: ${stats2.filesScanned} files scanned, ` + `${stats2.skippedFiles} files skipped, ${stats2.skippedDirs} dirs skipped` + (stats2.truncated ? ", TRUNCATED" : ""));
117228
117437
  }
@@ -117264,7 +117473,11 @@ import * as fsPromises7 from "node:fs/promises";
117264
117473
  import * as path138 from "node:path";
117265
117474
 
117266
117475
  // src/tools/repo-graph/query.ts
117476
+ init_path_security();
117477
+ import * as fsSync9 from "node:fs";
117267
117478
  import * as path136 from "node:path";
117479
+ var GRAPH_HEALTH_OUTPUT_LIMIT = 50;
117480
+ var MAX_HEALTH_PATH_LENGTH = 500;
117268
117481
  var cachedReverseIndex = null;
117269
117482
  function normalizeLookupPath(input) {
117270
117483
  return normalizeGraphPath(input).replace(/^(?:\.\/)+/, "");
@@ -117363,6 +117576,139 @@ function isGraphFresh(graph, maxAgeMs = 5 * 60 * 1000) {
117363
117576
  return false;
117364
117577
  return Date.now() - built <= maxAgeMs;
117365
117578
  }
117579
+ function isSafeHealthPath(value) {
117580
+ if (typeof value !== "string")
117581
+ return false;
117582
+ if (value.length === 0 || value.length > MAX_HEALTH_PATH_LENGTH)
117583
+ return false;
117584
+ if (containsControlChars(value) || containsPathTraversal(value))
117585
+ return false;
117586
+ if (path136.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value))
117587
+ return false;
117588
+ return true;
117589
+ }
117590
+ function isSafeHealthText(value) {
117591
+ if (typeof value !== "string")
117592
+ return false;
117593
+ if (value.length === 0 || value.length > MAX_HEALTH_PATH_LENGTH)
117594
+ return false;
117595
+ if (containsControlChars(value))
117596
+ return false;
117597
+ return true;
117598
+ }
117599
+ function cap(entries) {
117600
+ return entries.slice(0, GRAPH_HEALTH_OUTPUT_LIMIT);
117601
+ }
117602
+ function sanitizeExtractionFailures(value) {
117603
+ if (!Array.isArray(value))
117604
+ return [];
117605
+ const entries = [];
117606
+ for (const raw of value) {
117607
+ if (!raw || typeof raw !== "object")
117608
+ continue;
117609
+ const entry = raw;
117610
+ if (isSafeHealthPath(entry.file) && isSafeHealthText(entry.language) && isSafeHealthText(entry.reason)) {
117611
+ entries.push({
117612
+ file: entry.file,
117613
+ language: entry.language,
117614
+ reason: entry.reason
117615
+ });
117616
+ }
117617
+ }
117618
+ return cap(entries);
117619
+ }
117620
+ function sanitizeUnresolvedImports(value) {
117621
+ if (!Array.isArray(value))
117622
+ return [];
117623
+ const entries = [];
117624
+ for (const raw of value) {
117625
+ if (!raw || typeof raw !== "object")
117626
+ continue;
117627
+ const entry = raw;
117628
+ if (isSafeHealthPath(entry.file) && isSafeHealthText(entry.specifier)) {
117629
+ entries.push({ file: entry.file, specifier: entry.specifier });
117630
+ }
117631
+ }
117632
+ return cap(entries);
117633
+ }
117634
+ function sanitizePathList(value) {
117635
+ if (!Array.isArray(value))
117636
+ return [];
117637
+ return cap(value.filter(isSafeHealthPath));
117638
+ }
117639
+ function getStaleFiles(graph, workspaceRoot) {
117640
+ const built = Date.parse(graph.metadata.generatedAt);
117641
+ if (!Number.isFinite(built))
117642
+ return [];
117643
+ const root = workspaceRoot ?? graph.workspaceRoot;
117644
+ const stale = [];
117645
+ for (const node of Object.values(graph.nodes)) {
117646
+ const moduleName = normalizeGraphPath(node.moduleName);
117647
+ if (!isSafeHealthPath(moduleName))
117648
+ continue;
117649
+ const filePath = path136.join(root, moduleName);
117650
+ try {
117651
+ if (fsSync9.statSync(filePath).mtimeMs > built) {
117652
+ stale.push(moduleName);
117653
+ if (stale.length >= GRAPH_HEALTH_OUTPUT_LIMIT)
117654
+ break;
117655
+ }
117656
+ } catch {}
117657
+ }
117658
+ return stale;
117659
+ }
117660
+ function getGraphHealth(graph, workspaceRoot) {
117661
+ if (!graph) {
117662
+ return {
117663
+ schemaVersion: null,
117664
+ fresh: false,
117665
+ staleFiles: [],
117666
+ extractionFailures: [],
117667
+ unresolvedImports: [],
117668
+ oversizedFiles: [],
117669
+ unsupportedFiles: [],
117670
+ binaryFiles: [],
117671
+ unreadableFiles: [],
117672
+ lowConfidenceEdgeCount: 0,
117673
+ notes: [
117674
+ 'No repo graph found at .swarm/repo-graph.json. Run repo_map with action="build" first.'
117675
+ ]
117676
+ };
117677
+ }
117678
+ const diagnostics = graph.diagnostics;
117679
+ const fresh = isGraphFresh(graph);
117680
+ const staleFiles = getStaleFiles(graph, workspaceRoot);
117681
+ const notes = [];
117682
+ if (!fresh || staleFiles.length > 0) {
117683
+ notes.push('Graph is stale. Run repo_map with action="build" to refresh.');
117684
+ }
117685
+ if (!diagnostics) {
117686
+ notes.push('Graph has no recorded diagnostics. Rebuild with repo_map action="build" to collect health details.');
117687
+ }
117688
+ const binaryFiles = sanitizePathList(diagnostics?.binaryFiles);
117689
+ const binaryCount = binaryFiles.length;
117690
+ if (binaryCount > 0) {
117691
+ notes.push(`${binaryCount} binary files skipped during last build.`);
117692
+ }
117693
+ const unreadableFiles = sanitizePathList(diagnostics?.unreadableFiles);
117694
+ const unreadableCount = unreadableFiles.length;
117695
+ if (unreadableCount > 0) {
117696
+ notes.push(`${unreadableCount} unreadable files skipped during last build.`);
117697
+ }
117698
+ return {
117699
+ schemaVersion: graph.schema_version,
117700
+ fresh,
117701
+ staleFiles,
117702
+ extractionFailures: sanitizeExtractionFailures(diagnostics?.extractionFailures),
117703
+ unresolvedImports: sanitizeUnresolvedImports(diagnostics?.unresolvedImports),
117704
+ oversizedFiles: sanitizePathList(diagnostics?.oversizedFiles),
117705
+ unsupportedFiles: sanitizePathList(diagnostics?.unsupportedFiles),
117706
+ binaryFiles,
117707
+ unreadableFiles,
117708
+ lowConfidenceEdgeCount: typeof diagnostics?.lowConfidenceEdgeCount === "number" && Number.isFinite(diagnostics.lowConfidenceEdgeCount) && diagnostics.lowConfidenceEdgeCount > 0 ? Math.floor(diagnostics.lowConfidenceEdgeCount) : 0,
117709
+ notes
117710
+ };
117711
+ }
117366
117712
  function getImporters(graph, filePath) {
117367
117713
  const node = getGraphNode(graph, filePath);
117368
117714
  if (!node)
@@ -117951,7 +118297,7 @@ function buildOntologyPreflightPacket(graph, filePaths = [], options = {}) {
117951
118297
  init_utils2();
117952
118298
  init_logger();
117953
118299
  init_path_security();
117954
- import { constants as constants5, existsSync as existsSync77, readFileSync as readFileSync53, statSync as statSync27 } from "node:fs";
118300
+ import { constants as constants5, existsSync as existsSync77, readFileSync as readFileSync53, statSync as statSync28 } from "node:fs";
117955
118301
  import * as fsPromises6 from "node:fs/promises";
117956
118302
  import * as path137 from "node:path";
117957
118303
  var WINDOWS_RENAME_MAX_RETRIES2 = 5;
@@ -118076,7 +118422,7 @@ function loadGraphSync(workspace) {
118076
118422
  const graphPath = getGraphPath(workspace);
118077
118423
  if (!existsSync77(graphPath))
118078
118424
  return null;
118079
- const stats2 = statSync27(graphPath);
118425
+ const stats2 = statSync28(graphPath);
118080
118426
  const content = readFileSync53(graphPath, "utf-8");
118081
118427
  if (content.includes("\x00") || content.includes("�")) {
118082
118428
  throw Object.assign(new Error("repo-graph.json contains null bytes or invalid encoding"), { code: "CORRUPTION" });
@@ -123102,9 +123448,9 @@ function parseDelegateDirectiveBlock(text) {
123102
123448
  }
123103
123449
  async function injectForDelegate(params) {
123104
123450
  const { directory, agent, taskTitle, sessionId, config: config3 } = params;
123105
- const cap = config3.delegate_max_inject_count ?? 8;
123451
+ const cap2 = config3.delegate_max_inject_count ?? 8;
123106
123452
  const expectedTools = params.expectedTools && params.expectedTools.length > 0 ? params.expectedTools : defaultExpectedToolsForAgent(agent);
123107
- if (cap <= 0)
123453
+ if (cap2 <= 0)
123108
123454
  return { entries: [], trace_id: "" };
123109
123455
  const role = stripKnownSwarmPrefix(agent).toLowerCase();
123110
123456
  const firstTool = expectedTools.length > 0 ? expectedTools[0] : undefined;
@@ -123128,11 +123474,11 @@ async function injectForDelegate(params) {
123128
123474
  tier: "all",
123129
123475
  applyScopeFilter: true,
123130
123476
  applyRoleScope: false,
123131
- maxResults: Math.max(40, cap * 4),
123477
+ maxResults: Math.max(40, cap2 * 4),
123132
123478
  emitEvent: false
123133
123479
  });
123134
123480
  const scoped = search.results.filter((e) => matchesDelegateScope(e, role, expectedTools));
123135
- const capped = scoped.slice(0, cap);
123481
+ const capped = scoped.slice(0, cap2);
123136
123482
  if (capped.length > 0) {
123137
123483
  const ranks = {};
123138
123484
  const scores = {};
@@ -129968,7 +130314,7 @@ ${body}`);
129968
130314
  init_zod();
129969
130315
  init_task_file();
129970
130316
  import { appendFileSync as appendFileSync18, existsSync as existsSync95, mkdirSync as mkdirSync42, readFileSync as readFileSync64 } from "node:fs";
129971
- import { join as join131 } from "node:path";
130317
+ import { join as join132 } from "node:path";
129972
130318
  var EVIDENCE_DIR2 = ".swarm/evidence";
129973
130319
  var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
129974
130320
  var COUNCIL_GATE_NAME = "council";
@@ -130006,7 +130352,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
130006
130352
  if (!VALID_TASK_ID.test(synthesis.taskId)) {
130007
130353
  throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
130008
130354
  }
130009
- const dir = join131(workingDir, EVIDENCE_DIR2);
130355
+ const dir = join132(workingDir, EVIDENCE_DIR2);
130010
130356
  mkdirSync42(dir, { recursive: true });
130011
130357
  const filePath = taskEvidencePath(workingDir, synthesis.taskId);
130012
130358
  await _internals96.withTaskEvidenceLock(workingDir, synthesis.taskId, COUNCIL_AGENT_ID, async () => {
@@ -130042,7 +130388,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
130042
130388
  await atomicWriteFile(filePath, JSON.stringify(updated, null, 2));
130043
130389
  });
130044
130390
  try {
130045
- const councilDir = join131(workingDir, ".swarm", "council");
130391
+ const councilDir = join132(workingDir, ".swarm", "council");
130046
130392
  mkdirSync42(councilDir, { recursive: true });
130047
130393
  const auditLine = JSON.stringify({
130048
130394
  round: synthesis.roundNumber,
@@ -130050,7 +130396,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
130050
130396
  timestamp: synthesis.timestamp,
130051
130397
  vetoedBy: synthesis.vetoedBy
130052
130398
  });
130053
- appendFileSync18(join131(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
130399
+ appendFileSync18(join132(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
130054
130400
  `);
130055
130401
  } catch (auditError) {
130056
130402
  console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
@@ -130407,7 +130753,7 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
130407
130753
  init_zod();
130408
130754
  init_task_file();
130409
130755
  import { existsSync as existsSync96, mkdirSync as mkdirSync43, readFileSync as readFileSync65 } from "node:fs";
130410
- import { join as join132 } from "node:path";
130756
+ import { join as join133 } from "node:path";
130411
130757
  var COUNCIL_DIR = ".swarm/council";
130412
130758
  var CouncilCriteriaSchema = exports_external.object({
130413
130759
  taskId: exports_external.string(),
@@ -130419,17 +130765,17 @@ var CouncilCriteriaSchema = exports_external.object({
130419
130765
  declaredAt: exports_external.string()
130420
130766
  });
130421
130767
  async function writeCriteria(workingDir, taskId, criteria) {
130422
- const dir = join132(workingDir, COUNCIL_DIR);
130768
+ const dir = join133(workingDir, COUNCIL_DIR);
130423
130769
  mkdirSync43(dir, { recursive: true });
130424
130770
  const payload = {
130425
130771
  taskId,
130426
130772
  criteria,
130427
130773
  declaredAt: new Date().toISOString()
130428
130774
  };
130429
- await atomicWriteFile(join132(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
130775
+ await atomicWriteFile(join133(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
130430
130776
  }
130431
130777
  function readCriteria(workingDir, taskId) {
130432
- const filePath = join132(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
130778
+ const filePath = join133(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
130433
130779
  if (!existsSync96(filePath))
130434
130780
  return null;
130435
130781
  try {
@@ -149644,7 +149990,8 @@ var VALID_ACTIONS = [
149644
149990
  "preflight_packet",
149645
149991
  "callers",
149646
149992
  "dead_exports",
149647
- "context_pack"
149993
+ "context_pack",
149994
+ "graph_health"
149648
149995
  ];
149649
149996
  var MAX_FILE_PATH_LENGTH3 = 500;
149650
149997
  var MAX_SYMBOL_LENGTH2 = 256;
@@ -149708,7 +150055,7 @@ async function loadOrError(directory, action) {
149708
150055
  }
149709
150056
  }
149710
150057
  var repo_map = createSwarmTool({
149711
- description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning), ' + '"callers" (files that reference an exported symbol, call-site granularity; needs file+symbol), ' + '"dead_exports" (advisory: exported symbols with no detected in-repo reference; results are review candidates, not delete directives), ' + '"context_pack" (token-budgeted slice of source spans for a target symbol — definition + transitive callers/callees; advisory/conservative; needs file+symbol; uses max_depth for traversal depth, top_n for span cap). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers. " + 'Note: "callers"/"dead_exports"/"context_pack" use conservative regex analysis (TS/JS/Python) and cannot see ' + 'dynamic dispatch or namespace/barrel re-export usage; "dead_exports" results are review candidates, not delete directives.',
150058
+ description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning), ' + '"callers" (files that reference an exported symbol, call-site granularity; needs file+symbol), ' + '"dead_exports" (advisory: exported symbols with no detected in-repo reference; results are review candidates, not delete directives), ' + '"context_pack" (token-budgeted slice of source spans for a target symbol — definition + transitive callers/callees; advisory/conservative; needs file+symbol; uses max_depth for traversal depth, top_n for span cap), ' + '"graph_health" (freshness and bounded extraction diagnostics; no file required). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers. " + 'Note: "callers"/"dead_exports"/"context_pack" use conservative regex analysis (TS/JS/Python) and cannot see ' + 'dynamic dispatch or namespace/barrel re-export usage; "dead_exports" results are review candidates, not delete directives.',
149712
150059
  args: {
149713
150060
  action: exports_external.enum([
149714
150061
  "build",
@@ -149722,8 +150069,9 @@ var repo_map = createSwarmTool({
149722
150069
  "preflight_packet",
149723
150070
  "callers",
149724
150071
  "dead_exports",
149725
- "context_pack"
149726
- ]).describe('Query action: "build" | "importers" | "dependencies" | "blast_radius" | "localization" | "key_files" | "ontology" | "package_boundaries" | "preflight_packet" | "callers" | "dead_exports" | "context_pack"'),
150072
+ "context_pack",
150073
+ "graph_health"
150074
+ ]).describe('Query action: "build" | "importers" | "dependencies" | "blast_radius" | "localization" | "key_files" | "ontology" | "package_boundaries" | "preflight_packet" | "callers" | "dead_exports" | "context_pack" | "graph_health"'),
149727
150075
  file: exports_external.string().optional().describe("Target file (workspace-relative or absolute). Required for importers/dependencies/localization/ontology. Optional for preflight_packet."),
149728
150076
  files: exports_external.array(exports_external.string()).optional().describe("Multiple target files for blast_radius/preflight_packet. If omitted, falls back to `file`."),
149729
150077
  symbol: exports_external.string().optional().describe('Exported symbol name. Restricts consumers on action="importers"; required for action="callers"/"context_pack".'),
@@ -149758,6 +150106,15 @@ var repo_map = createSwarmTool({
149758
150106
  return err(action, `build failed: ${message}`);
149759
150107
  }
149760
150108
  }
150109
+ if (action === "graph_health") {
150110
+ try {
150111
+ const graph2 = await loadGraph(directory);
150112
+ return ok(action, { ...getGraphHealth(graph2, directory) });
150113
+ } catch (e) {
150114
+ const message = e instanceof Error ? e.message : String(e);
150115
+ return err(action, `failed to load repo graph: ${message}`);
150116
+ }
150117
+ }
149761
150118
  const loaded = await loadOrError(directory, action);
149762
150119
  if (!loaded.ok)
149763
150120
  return loaded.response;
@@ -153111,7 +153468,7 @@ init_skill_generator();
153111
153468
  init_create_tool();
153112
153469
  import { existsSync as existsSync119 } from "node:fs";
153113
153470
  import { readdir as readdir12, readFile as readFile33 } from "node:fs/promises";
153114
- import { join as join166 } from "node:path";
153471
+ import { join as join167 } from "node:path";
153115
153472
  var run_stale_reconciliation = createSwarmTool({
153116
153473
  description: "Reconcile skills against the knowledge store. clear=false: mark skills stale when source knowledge is archived or deleted. clear=true: clear stale.marker on affected active skills (proposal files under .swarm/skills/proposals are scanned but not modified — they are drafts, not yet active skills).",
153117
153474
  args: {
@@ -153138,8 +153495,8 @@ var run_stale_reconciliation = createSwarmTool({
153138
153495
  } catch {}
153139
153496
  const skillEntries = [];
153140
153497
  for (const dir of [
153141
- join166(directory, ".opencode", "skills", "generated"),
153142
- join166(directory, ".swarm", "skills", "proposals")
153498
+ join167(directory, ".opencode", "skills", "generated"),
153499
+ join167(directory, ".swarm", "skills", "proposals")
153143
153500
  ]) {
153144
153501
  if (!_internals125.existsSync(dir))
153145
153502
  continue;
@@ -153148,14 +153505,14 @@ var run_stale_reconciliation = createSwarmTool({
153148
153505
  if (entry.isDirectory()) {
153149
153506
  skillEntries.push({
153150
153507
  slug: entry.name,
153151
- path: join166(dir, entry.name),
153508
+ path: join167(dir, entry.name),
153152
153509
  isProposal: false
153153
153510
  });
153154
153511
  } else if (entry.name.endsWith(".md")) {
153155
153512
  const slug = entry.name.replace(/\.md$/, "");
153156
153513
  skillEntries.push({
153157
153514
  slug,
153158
- path: join166(dir, entry.name),
153515
+ path: join167(dir, entry.name),
153159
153516
  isProposal: true
153160
153517
  });
153161
153518
  }
@@ -153163,7 +153520,7 @@ var run_stale_reconciliation = createSwarmTool({
153163
153520
  }
153164
153521
  const results = [];
153165
153522
  for (const { slug, path: path210, isProposal } of skillEntries) {
153166
- const skillMdPath = isProposal ? path210 : join166(path210, "SKILL.md");
153523
+ const skillMdPath = isProposal ? path210 : join167(path210, "SKILL.md");
153167
153524
  if (!_internals125.existsSync(skillMdPath))
153168
153525
  continue;
153169
153526
  const content = await _internals125.readFile(skillMdPath, "utf-8");
@@ -153176,7 +153533,7 @@ var run_stale_reconciliation = createSwarmTool({
153176
153533
  continue;
153177
153534
  if (args2.clear) {
153178
153535
  if (!isProposal) {
153179
- const markerPath = join166(path210, "stale.marker");
153536
+ const markerPath = join167(path210, "stale.marker");
153180
153537
  if (_internals125.existsSync(markerPath)) {
153181
153538
  try {
153182
153539
  await _internals125.clearSkillStale(path210);
@@ -158389,14 +158746,6 @@ async function initializeOpenCodeSwarm(ctx) {
158389
158746
  prEventCleanup?.();
158390
158747
  };
158391
158748
  process.on("exit", cleanupAutomation);
158392
- process.once("SIGINT", () => {
158393
- cleanupAutomation();
158394
- process.exit(130);
158395
- });
158396
- process.once("SIGTERM", () => {
158397
- cleanupAutomation();
158398
- process.exit(143);
158399
- });
158400
158749
  if (shouldRunOnStartup(automationConfig)) {
158401
158750
  const enableAutofix = automationConfig.capabilities?.config_doctor_autofix === true;
158402
158751
  Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor)).then(({ runConfigDoctorWithFixes: runConfigDoctorWithFixes2 }) => {
@@ -158412,9 +158761,19 @@ async function initializeOpenCodeSwarm(ctx) {
158412
158761
  try {
158413
158762
  const autoFixableCount = doctorResult.result.findings.filter((f) => f.autoFixable).length;
158414
158763
  if (!enableAutofix && autoFixableCount > 0) {
158415
- console.warn(`[opencode-swarm] Config Doctor found ${autoFixableCount} auto-fixable issue(s). Run /swarm config doctor --fix to apply.`);
158764
+ const msg = `[opencode-swarm] Config Doctor found ${autoFixableCount} auto-fixable issue(s). Run /swarm config doctor --fix to apply.`;
158765
+ if (!config3.quiet) {
158766
+ console.warn(msg);
158767
+ } else {
158768
+ addDeferredWarning(msg);
158769
+ }
158416
158770
  } else if (enableAutofix && doctorResult.appliedFixes.length > 0) {
158417
- console.warn(`[opencode-swarm] Config Doctor applied ${doctorResult.appliedFixes.length} fix(es) automatically.`);
158771
+ const msg = `[opencode-swarm] Config Doctor applied ${doctorResult.appliedFixes.length} fix(es) automatically.`;
158772
+ if (!config3.quiet) {
158773
+ console.warn(msg);
158774
+ } else {
158775
+ addDeferredWarning(msg);
158776
+ }
158418
158777
  }
158419
158778
  } catch {}
158420
158779
  }
@@ -158957,7 +159316,9 @@ async function initializeOpenCodeSwarm(ctx) {
158957
159316
  argsRecord.prompt = `SKILLS: none
158958
159317
 
158959
159318
  ${promptRaw}`;
158960
- console.warn("[skill-propagation-gate] No skills above threshold 0.5 — injected SKILLS: none");
159319
+ if (!config3.quiet) {
159320
+ console.warn("[skill-propagation-gate] No skills above threshold 0.5 — injected SKILLS: none");
159321
+ }
158961
159322
  } else {
158962
159323
  const topSkills = qualified.slice(0, 5);
158963
159324
  const skillPaths = topSkills.map((s) => {
@@ -158975,7 +159336,9 @@ ${promptRaw}`;
158975
159336
  ${promptRaw}`;
158976
159337
  argsRecord.prompt = newPrompt;
158977
159338
  const skillNames = topSkills.map((s) => `${path225.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
158978
- console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
159339
+ if (!config3.quiet) {
159340
+ console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
159341
+ }
158979
159342
  for (const skill of topSkills) {
158980
159343
  try {
158981
159344
  appendSkillUsageEntry(ctx.directory, {