opencode-swarm 6.30.2 → 6.31.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 (34) hide show
  1. package/dist/cli/index.js +25 -12
  2. package/dist/config/schema.d.ts +22 -0
  3. package/dist/hooks/curator-drift-advisory.test.d.ts +1 -0
  4. package/dist/hooks/curator-drift.d.ts +1 -1
  5. package/dist/hooks/delegation-ledger.d.ts +37 -0
  6. package/dist/hooks/delegation-ledger.test.d.ts +11 -0
  7. package/dist/hooks/scope-guard-throw-behavior.test.d.ts +15 -0
  8. package/dist/hooks/scope-guard.adversarial.test.d.ts +1 -0
  9. package/dist/hooks/scope-guard.d.ts +43 -0
  10. package/dist/hooks/scope-guard.test.d.ts +14 -0
  11. package/dist/hooks/self-review.d.ts +14 -0
  12. package/dist/hooks/self-review.test.d.ts +14 -0
  13. package/dist/hooks/slop-detector.adversarial.test.d.ts +1 -0
  14. package/dist/hooks/watchdog.integration.test.d.ts +9 -0
  15. package/dist/index.js +918 -236
  16. package/dist/tools/curator-analyze.d.ts +7 -0
  17. package/dist/tools/curator-analyze.test.d.ts +1 -0
  18. package/dist/tools/diff.d.ts +2 -2
  19. package/dist/tools/gitingest.d.ts +2 -2
  20. package/dist/tools/imports.d.ts +2 -2
  21. package/dist/tools/index.d.ts +4 -0
  22. package/dist/tools/knowledge-add.d.ts +2 -0
  23. package/dist/tools/knowledge-recall.d.ts +2 -0
  24. package/dist/tools/knowledge-remove.d.ts +2 -0
  25. package/dist/tools/knowledge-tools.integration.test.d.ts +1 -0
  26. package/dist/tools/lint.d.ts +1 -1
  27. package/dist/tools/phase-complete.d.ts +1 -1
  28. package/dist/tools/pre-check-batch.d.ts +1 -1
  29. package/dist/tools/retrieve-summary.d.ts +2 -2
  30. package/dist/tools/secretscan.d.ts +2 -2
  31. package/dist/tools/test-runner.d.ts +1 -1
  32. package/dist/tools/update-task-status.adversarial.test.d.ts +13 -0
  33. package/dist/tools/update-task-status.test.d.ts +1 -0
  34. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14437,7 +14437,7 @@ function resolveGuardrailsConfig(config2, agentName) {
14437
14437
  };
14438
14438
  return resolved;
14439
14439
  }
14440
- var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PluginConfigSchema;
14440
+ var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PluginConfigSchema;
14441
14441
  var init_schema = __esm(() => {
14442
14442
  init_zod();
14443
14443
  init_constants();
@@ -14807,6 +14807,15 @@ var init_schema = __esm(() => {
14807
14807
  }).optional(),
14808
14808
  profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
14809
14809
  });
14810
+ WatchdogConfigSchema = exports_external.object({
14811
+ scope_guard: exports_external.boolean().default(true),
14812
+ skip_in_turbo: exports_external.boolean().default(false),
14813
+ delegation_ledger: exports_external.boolean().default(true)
14814
+ });
14815
+ SelfReviewConfigSchema = exports_external.object({
14816
+ enabled: exports_external.boolean().default(true),
14817
+ skip_in_turbo: exports_external.boolean().default(true)
14818
+ });
14810
14819
  ToolFilterConfigSchema = exports_external.object({
14811
14820
  enabled: exports_external.boolean().default(true),
14812
14821
  overrides: exports_external.record(exports_external.string(), exports_external.array(exports_external.string())).default({})
@@ -14878,7 +14887,8 @@ var init_schema = __esm(() => {
14878
14887
  enabled: exports_external.boolean().default(true),
14879
14888
  classThreshold: exports_external.number().int().min(1).default(3),
14880
14889
  commentStripThreshold: exports_external.number().int().min(1).default(5),
14881
- diffLineThreshold: exports_external.number().int().min(10).default(200)
14890
+ diffLineThreshold: exports_external.number().int().min(10).default(200),
14891
+ importHygieneThreshold: exports_external.number().int().min(1).default(2)
14882
14892
  });
14883
14893
  IncrementalVerifyConfigSchema = exports_external.object({
14884
14894
  enabled: exports_external.boolean().default(true),
@@ -14905,6 +14915,8 @@ var init_schema = __esm(() => {
14905
14915
  gates: GateConfigSchema.optional(),
14906
14916
  context_budget: ContextBudgetConfigSchema.optional(),
14907
14917
  guardrails: GuardrailsConfigSchema.optional(),
14918
+ watchdog: WatchdogConfigSchema.optional(),
14919
+ self_review: SelfReviewConfigSchema.optional(),
14908
14920
  tool_filter: ToolFilterConfigSchema.optional(),
14909
14921
  plan_cursor: PlanCursorConfigSchema.optional(),
14910
14922
  evidence: EvidenceConfigSchema.optional(),
@@ -33414,14 +33426,14 @@ function detectAdditionalLinter(cwd) {
33414
33426
  return null;
33415
33427
  }
33416
33428
  async function detectAvailableLinter(directory) {
33417
- const DETECT_TIMEOUT = 2000;
33418
- const projectDir = directory ?? process.cwd();
33429
+ const _DETECT_TIMEOUT = 2000;
33430
+ const projectDir = directory || process.cwd();
33419
33431
  const isWindows = process.platform === "win32";
33420
33432
  const biomeBin = isWindows ? path22.join(projectDir, "node_modules", ".bin", "biome.EXE") : path22.join(projectDir, "node_modules", ".bin", "biome");
33421
33433
  const eslintBin = isWindows ? path22.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path22.join(projectDir, "node_modules", ".bin", "eslint");
33422
33434
  return _detectAvailableLinter(projectDir, biomeBin, eslintBin);
33423
33435
  }
33424
- async function _detectAvailableLinter(projectDir, biomeBin, eslintBin) {
33436
+ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
33425
33437
  const DETECT_TIMEOUT = 2000;
33426
33438
  try {
33427
33439
  const biomeProc = Bun.spawn(["npx", "biome", "--version"], {
@@ -33962,6 +33974,7 @@ async function runSecretscan(directory) {
33962
33974
  var MAX_FILE_PATH_LENGTH = 500, MAX_FILE_SIZE_BYTES, MAX_FILES_SCANNED = 1000, MAX_FINDINGS = 100, MAX_OUTPUT_BYTES2 = 512000, MAX_LINE_LENGTH = 1e4, MAX_CONTENT_BYTES, BINARY_SIGNATURES, BINARY_PREFIX_BYTES = 4, BINARY_NULL_CHECK_BYTES = 8192, BINARY_NULL_THRESHOLD = 0.1, DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_EXTENSIONS, SECRET_PATTERNS, O_NOFOLLOW, secretscan;
33963
33975
  var init_secretscan = __esm(() => {
33964
33976
  init_dist();
33977
+ init_create_tool();
33965
33978
  MAX_FILE_SIZE_BYTES = 512 * 1024;
33966
33979
  MAX_CONTENT_BYTES = 50 * 1024;
33967
33980
  BINARY_SIGNATURES = [
@@ -34134,19 +34147,20 @@ var init_secretscan = __esm(() => {
34134
34147
  }
34135
34148
  ];
34136
34149
  O_NOFOLLOW = process.platform !== "win32" ? fs11.constants.O_NOFOLLOW : undefined;
34137
- secretscan = tool({
34150
+ secretscan = createSwarmTool({
34138
34151
  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.",
34139
34152
  args: {
34140
34153
  directory: tool.schema.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
34141
34154
  exclude: tool.schema.array(tool.schema.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
34142
34155
  },
34143
- async execute(args2, _context) {
34156
+ async execute(args2, _directory, _ctx) {
34157
+ const typedArgs = args2;
34144
34158
  let directory;
34145
34159
  let exclude;
34146
34160
  try {
34147
- if (args2 && typeof args2 === "object") {
34148
- directory = args2.directory;
34149
- exclude = args2.exclude;
34161
+ if (typedArgs && typeof typedArgs === "object") {
34162
+ directory = typedArgs.directory;
34163
+ exclude = typedArgs.exclude;
34150
34164
  }
34151
34165
  } catch {}
34152
34166
  if (directory === undefined) {
@@ -35001,7 +35015,7 @@ function parseTestOutput(framework, output) {
35001
35015
  return { totals, coveragePercent };
35002
35016
  }
35003
35017
  async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
35004
- const command = buildTestCommand(framework, scope, files, coverage, cwd ?? process.cwd());
35018
+ const command = buildTestCommand(framework, scope, files, coverage, cwd);
35005
35019
  if (!command) {
35006
35020
  return {
35007
35021
  success: false,
@@ -35026,7 +35040,7 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
35026
35040
  const proc = Bun.spawn(command, {
35027
35041
  stdout: "pipe",
35028
35042
  stderr: "pipe",
35029
- cwd: cwd || process.cwd()
35043
+ cwd
35030
35044
  });
35031
35045
  const exitPromise = proc.exited;
35032
35046
  const timeoutPromise = new Promise((resolve8) => setTimeout(() => {
@@ -35558,7 +35572,7 @@ async function runLintCheck(dir, linter, timeoutMs) {
35558
35572
  async function runTestsCheck(_dir, scope, timeoutMs) {
35559
35573
  const startTime = Date.now();
35560
35574
  try {
35561
- const result = await runTests("none", scope, [], false, timeoutMs);
35575
+ const result = await runTests("none", scope, [], false, timeoutMs, _dir);
35562
35576
  if (!result.success) {
35563
35577
  return {
35564
35578
  type: "tests",
@@ -37581,8 +37595,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37581
37595
  var moduleRtn;
37582
37596
  var Module = moduleArg;
37583
37597
  var readyPromiseResolve, readyPromiseReject;
37584
- var readyPromise = new Promise((resolve15, reject) => {
37585
- readyPromiseResolve = resolve15;
37598
+ var readyPromise = new Promise((resolve16, reject) => {
37599
+ readyPromiseResolve = resolve16;
37586
37600
  readyPromiseReject = reject;
37587
37601
  });
37588
37602
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -37604,11 +37618,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37604
37618
  throw toThrow;
37605
37619
  }, "quit_");
37606
37620
  var scriptDirectory = "";
37607
- function locateFile(path46) {
37621
+ function locateFile(path47) {
37608
37622
  if (Module["locateFile"]) {
37609
- return Module["locateFile"](path46, scriptDirectory);
37623
+ return Module["locateFile"](path47, scriptDirectory);
37610
37624
  }
37611
- return scriptDirectory + path46;
37625
+ return scriptDirectory + path47;
37612
37626
  }
37613
37627
  __name(locateFile, "locateFile");
37614
37628
  var readAsync, readBinary;
@@ -37662,13 +37676,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37662
37676
  }
37663
37677
  readAsync = /* @__PURE__ */ __name(async (url3) => {
37664
37678
  if (isFileURI(url3)) {
37665
- return new Promise((resolve15, reject) => {
37679
+ return new Promise((resolve16, reject) => {
37666
37680
  var xhr = new XMLHttpRequest;
37667
37681
  xhr.open("GET", url3, true);
37668
37682
  xhr.responseType = "arraybuffer";
37669
37683
  xhr.onload = () => {
37670
37684
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
37671
- resolve15(xhr.response);
37685
+ resolve16(xhr.response);
37672
37686
  return;
37673
37687
  }
37674
37688
  reject(xhr.status);
@@ -37888,10 +37902,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37888
37902
  __name(receiveInstantiationResult, "receiveInstantiationResult");
37889
37903
  var info2 = getWasmImports();
37890
37904
  if (Module["instantiateWasm"]) {
37891
- return new Promise((resolve15, reject) => {
37905
+ return new Promise((resolve16, reject) => {
37892
37906
  Module["instantiateWasm"](info2, (mod, inst) => {
37893
37907
  receiveInstance(mod, inst);
37894
- resolve15(mod.exports);
37908
+ resolve16(mod.exports);
37895
37909
  });
37896
37910
  });
37897
37911
  }
@@ -39356,7 +39370,7 @@ var init_runtime = __esm(() => {
39356
39370
  });
39357
39371
 
39358
39372
  // src/index.ts
39359
- import * as path56 from "path";
39373
+ import * as path57 from "path";
39360
39374
 
39361
39375
  // src/agents/index.ts
39362
39376
  init_config();
@@ -39664,8 +39678,6 @@ function planStatusToWorkflowState(status) {
39664
39678
  return "coder_delegated";
39665
39679
  case "completed":
39666
39680
  return "complete";
39667
- case "pending":
39668
- case "blocked":
39669
39681
  default:
39670
39682
  return "idle";
39671
39683
  }
@@ -39679,10 +39691,10 @@ function evidenceToWorkflowState(evidence) {
39679
39691
  return "complete";
39680
39692
  }
39681
39693
  }
39682
- if (gates["test_engineer"] != null) {
39694
+ if (gates.test_engineer != null) {
39683
39695
  return "tests_run";
39684
39696
  }
39685
- if (gates["reviewer"] != null) {
39697
+ if (gates.reviewer != null) {
39686
39698
  return "reviewer_run";
39687
39699
  }
39688
39700
  if (Object.keys(gates).length > 0) {
@@ -42765,7 +42777,10 @@ class PlanSyncWorker {
42765
42777
  lastStat = null;
42766
42778
  disposed = false;
42767
42779
  constructor(options = {}) {
42768
- this.directory = options.directory ?? process.cwd();
42780
+ if (!options.directory) {
42781
+ console.warn("[plan-sync-worker] No directory provided, falling back to process.cwd()");
42782
+ }
42783
+ this.directory = options.directory || process.cwd();
42769
42784
  this.debounceMs = options.debounceMs ?? 300;
42770
42785
  this.pollIntervalMs = options.pollIntervalMs ?? 2000;
42771
42786
  this.syncTimeoutMs = options.syncTimeoutMs ?? 30000;
@@ -49210,6 +49225,7 @@ function createCompactionCustomizerHook(config3, directory) {
49210
49225
  output.context.push(`[SWARM PATTERNS] ${patterns}`);
49211
49226
  }
49212
49227
  }
49228
+ output.context.push("[KNOWLEDGE TOOLS] You have persistent knowledge tools: knowledge_recall (search for relevant past decisions), knowledge_add (store a new lesson), knowledge_remove (delete outdated entries). Use knowledge_recall when past context would help.");
49213
49229
  })
49214
49230
  };
49215
49231
  }
@@ -49870,10 +49886,11 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
49870
49886
  let directory;
49871
49887
  let guardrailsConfig;
49872
49888
  if (directoryOrConfig && typeof directoryOrConfig === "object" && "enabled" in directoryOrConfig) {
49889
+ console.warn("[guardrails] Legacy call without directory, falling back to process.cwd()");
49873
49890
  directory = process.cwd();
49874
49891
  guardrailsConfig = directoryOrConfig;
49875
49892
  } else {
49876
- directory = directoryOrConfig ?? process.cwd();
49893
+ directory = directoryOrConfig || process.cwd();
49877
49894
  guardrailsConfig = config3;
49878
49895
  }
49879
49896
  if (guardrailsConfig?.enabled === false) {
@@ -49925,9 +49942,27 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
49925
49942
  const agentName2 = typeof loopArgs?.subagent_type === "string" ? loopArgs.subagent_type : "agent";
49926
49943
  const loopSession = swarmState.agentSessions.get(input.sessionID);
49927
49944
  if (loopSession) {
49945
+ const loopPattern = loopResult.pattern;
49946
+ const modifiedFiles = loopSession.modifiedFilesThisCoderTask ?? [];
49947
+ const accomplishmentSummary = modifiedFiles.length > 0 ? `Modified ${modifiedFiles.length} file(s): ${modifiedFiles.slice(0, 3).join(", ")}${modifiedFiles.length > 3 ? "..." : ""}` : "No files modified yet";
49948
+ const alternativeSuggestions = {
49949
+ coder: "Try a different task spec, simplify the constraint, or escalate to user",
49950
+ reviewer: "Try a different review dimension or escalate to user",
49951
+ test_engineer: "Run a specific test file with targeted scope",
49952
+ explorer: "Narrow the search scope or check a specific file directly"
49953
+ };
49954
+ const cleanAgent = stripKnownSwarmPrefix(agentName2).toLowerCase();
49955
+ const suggestion = alternativeSuggestions[cleanAgent] ?? "Try a different agent, different instructions, or escalate to the user";
49928
49956
  loopSession.loopWarningPending = {
49929
49957
  agent: agentName2,
49930
- message: `LOOP DETECTED: You have delegated to ${agentName2} with the same pattern 3 times. Change your approach \u2014 try a different agent, different instructions, or escalate to the user.`,
49958
+ message: [
49959
+ `LOOP DETECTED: Pattern "${loopPattern}" repeated 3 times.`,
49960
+ `Agent: ${agentName2}`,
49961
+ `Accomplished: ${accomplishmentSummary}`,
49962
+ `Suggested action: ${suggestion}`,
49963
+ `If still stuck after trying alternatives, escalate to the user.`
49964
+ ].join(`
49965
+ `),
49931
49966
  timestamp: Date.now()
49932
49967
  };
49933
49968
  }
@@ -50326,7 +50361,7 @@ ${pending.message}
50326
50361
  ${joined}
50327
50362
  [/ADVISORIES]
50328
50363
 
50329
- ` + textPart2.text;
50364
+ ${textPart2.text}`;
50330
50365
  }
50331
50366
  session.pendingAdvisoryMessages = [];
50332
50367
  } else if (!isArchitectSession && session && (session.pendingAdvisoryMessages?.length ?? 0) > 0) {
@@ -51346,7 +51381,7 @@ function createPipelineTrackerHook(config3, directory) {
51346
51381
  return;
51347
51382
  let phaseNumber = null;
51348
51383
  try {
51349
- const plan = await loadPlan(directory ?? process.cwd());
51384
+ const plan = await loadPlan(directory || process.cwd());
51350
51385
  if (plan) {
51351
51386
  const phaseString = extractCurrentPhaseFromPlan2(plan);
51352
51387
  phaseNumber = parsePhaseNumber(phaseString);
@@ -52993,6 +53028,97 @@ function createDarkMatterDetectorHook(directory) {
52993
53028
  return safeHook(hook);
52994
53029
  }
52995
53030
 
53031
+ // src/hooks/delegation-ledger.ts
53032
+ var ledgerBySession = new Map;
53033
+ var callStartTimes = new Map;
53034
+ function createDelegationLedgerHook(config3, _directory, injectAdvisory) {
53035
+ const enabled = config3.enabled ?? true;
53036
+ return {
53037
+ toolAfter: async (input, output) => {
53038
+ if (!enabled)
53039
+ return;
53040
+ const sessionId = input.sessionID;
53041
+ const startKey = `${sessionId}:${input.callID}`;
53042
+ const startTime = callStartTimes.get(startKey) ?? Date.now();
53043
+ callStartTimes.delete(startKey);
53044
+ const duration_ms = Date.now() - startTime;
53045
+ const args2 = input.args ?? {};
53046
+ const file3 = typeof args2.path === "string" ? args2.path : typeof args2.filePath === "string" ? args2.filePath : typeof args2.file === "string" ? args2.file : undefined;
53047
+ const session = swarmState.agentSessions.get(sessionId);
53048
+ const agentName = swarmState.activeAgent?.get(sessionId) ?? session?.agentName ?? "unknown";
53049
+ const outputStr = String(output.output ?? "");
53050
+ const success3 = !outputStr.startsWith("Error:") && !outputStr.startsWith("error: ");
53051
+ const entry = {
53052
+ agent: agentName,
53053
+ tool: input.tool,
53054
+ file: file3,
53055
+ duration_ms,
53056
+ success: success3,
53057
+ timestamp: Date.now()
53058
+ };
53059
+ const existing = ledgerBySession.get(sessionId) ?? [];
53060
+ existing.push(entry);
53061
+ ledgerBySession.set(sessionId, existing);
53062
+ },
53063
+ onArchitectResume: (architectSessionId) => {
53064
+ if (!enabled)
53065
+ return;
53066
+ const allEntries = [];
53067
+ for (const [sessionId, entries] of ledgerBySession) {
53068
+ if (sessionId === architectSessionId)
53069
+ continue;
53070
+ allEntries.push(...entries);
53071
+ }
53072
+ if (allEntries.length === 0)
53073
+ return;
53074
+ for (const sessionId of ledgerBySession.keys()) {
53075
+ if (sessionId !== architectSessionId) {
53076
+ ledgerBySession.delete(sessionId);
53077
+ }
53078
+ }
53079
+ const toolCallCount = allEntries.length;
53080
+ const filesModified = [
53081
+ ...new Set(allEntries.filter((e) => isWriteTool2(e.tool) && e.file).map((e) => e.file))
53082
+ ];
53083
+ const filesRead = [
53084
+ ...new Set(allEntries.filter((e) => isReadTool(e.tool) && e.file).map((e) => e.file))
53085
+ ];
53086
+ const failedCalls = allEntries.filter((e) => !e.success).length;
53087
+ const scopeViolations = allEntries.filter((e) => e.tool.includes("scope")).length;
53088
+ const summary = [
53089
+ `DELEGATION SUMMARY:`,
53090
+ ` Tool calls: ${toolCallCount}${failedCalls > 0 ? ` (${failedCalls} failed)` : ""}`,
53091
+ filesModified.length > 0 ? ` Files modified: ${filesModified.slice(0, 5).join(", ")}${filesModified.length > 5 ? ` (+${filesModified.length - 5} more)` : ""}` : null,
53092
+ filesRead.length > 0 ? ` Files read: ${filesRead.slice(0, 5).join(", ")}${filesRead.length > 5 ? ` (+${filesRead.length - 5} more)` : ""}` : null,
53093
+ scopeViolations > 0 ? ` \u26A0\uFE0F ${scopeViolations} scope violation(s) detected` : null
53094
+ ].filter(Boolean).join(`
53095
+ `);
53096
+ try {
53097
+ injectAdvisory(architectSessionId, summary);
53098
+ } catch {}
53099
+ }
53100
+ };
53101
+ }
53102
+ var WRITE_TOOL_PATTERNS = [
53103
+ "write",
53104
+ "edit",
53105
+ "patch",
53106
+ "create",
53107
+ "insert",
53108
+ "replace",
53109
+ "append",
53110
+ "prepend"
53111
+ ];
53112
+ function isWriteTool2(toolName) {
53113
+ const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
53114
+ return WRITE_TOOL_PATTERNS.some((p) => normalized.includes(p));
53115
+ }
53116
+ var READ_TOOL_PATTERNS = ["read", "cat", "view", "fetch", "get"];
53117
+ function isReadTool(toolName) {
53118
+ const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
53119
+ return READ_TOOL_PATTERNS.some((p) => normalized.includes(p));
53120
+ }
53121
+
52996
53122
  // src/hooks/incremental-verify.ts
52997
53123
  import * as fs21 from "fs";
52998
53124
  import * as path34 from "path";
@@ -53851,7 +53977,7 @@ async function writeDriftReport(directory, report) {
53851
53977
  }
53852
53978
  return filePath;
53853
53979
  }
53854
- async function runCriticDriftCheck(directory, phase, curatorResult, config3) {
53980
+ async function runCriticDriftCheck(directory, phase, curatorResult, config3, injectAdvisory) {
53855
53981
  try {
53856
53982
  const planMd = await readSwarmFileAsync(directory, "plan.md");
53857
53983
  const specMd = await readSwarmFileAsync(directory, "spec.md");
@@ -53911,6 +54037,12 @@ async function runCriticDriftCheck(directory, phase, curatorResult, config3) {
53911
54037
  drift_score: driftScore,
53912
54038
  report_path: reportPath
53913
54039
  });
54040
+ if (injectAdvisory && alignment !== "ALIGNED" && driftScore > 0) {
54041
+ try {
54042
+ const advisoryText = `CURATOR DRIFT DETECTED (phase ${phase}, score ${driftScore.toFixed(2)}): ${injectionSummary.slice(0, 300)}. Review .swarm/${DRIFT_REPORT_PREFIX}${phase}.json and address spec alignment before proceeding.`;
54043
+ injectAdvisory(advisoryText);
54044
+ } catch {}
54045
+ }
53914
54046
  const injectionText = injectionSummary;
53915
54047
  return {
53916
54048
  phase,
@@ -54110,9 +54242,127 @@ ${injectionText}`;
54110
54242
  });
54111
54243
  }
54112
54244
 
54245
+ // src/hooks/scope-guard.ts
54246
+ init_constants();
54247
+ init_schema();
54248
+ import * as path37 from "path";
54249
+ var WRITE_TOOLS = new Set([
54250
+ "write",
54251
+ "edit",
54252
+ "patch",
54253
+ "apply_patch",
54254
+ "create_file",
54255
+ "insert",
54256
+ "replace",
54257
+ "append",
54258
+ "prepend"
54259
+ ]);
54260
+ function createScopeGuardHook(config3, _directory, injectAdvisory) {
54261
+ const enabled = config3.enabled ?? true;
54262
+ const _skipInTurbo = config3.skip_in_turbo ?? false;
54263
+ return {
54264
+ toolBefore: async (input, output) => {
54265
+ if (!enabled)
54266
+ return;
54267
+ const toolName = input.tool.replace(/^[^:]+[:.]/, "");
54268
+ if (!WRITE_TOOLS.has(toolName))
54269
+ return;
54270
+ const sessionId = input.sessionID;
54271
+ const activeAgent = swarmState.activeAgent.get(sessionId);
54272
+ const session = swarmState.agentSessions.get(sessionId);
54273
+ const agentName = activeAgent ?? session?.agentName ?? "unknown";
54274
+ const isArchitect2 = stripKnownSwarmPrefix(agentName) === ORCHESTRATOR_NAME;
54275
+ if (isArchitect2)
54276
+ return;
54277
+ const declaredScope = session?.declaredCoderScope;
54278
+ if (!declaredScope || declaredScope.length === 0)
54279
+ return;
54280
+ const argsObj = output.args;
54281
+ const rawFilePath = argsObj?.path ?? argsObj?.filePath ?? argsObj?.file;
54282
+ if (typeof rawFilePath !== "string" || !rawFilePath)
54283
+ return;
54284
+ const filePath = rawFilePath.replace(/[\r\n\t]/g, "_").split(String.fromCharCode(27)).join("_").replace(/\[[\d;]*m/g, "");
54285
+ if (!isFileInScope(filePath, declaredScope)) {
54286
+ const taskId = session?.currentTaskId ?? "unknown";
54287
+ const violationMessage = `SCOPE VIOLATION: ${agentName} attempted to modify '${filePath}' which is not in declared scope for task ${taskId}. Declared scope: [${declaredScope.slice(0, 3).join(", ")}${declaredScope.length > 3 ? "..." : ""}]`;
54288
+ if (session) {
54289
+ session.lastScopeViolation = violationMessage;
54290
+ session.scopeViolationDetected = true;
54291
+ }
54292
+ if (injectAdvisory) {
54293
+ for (const [archSessionId, archSession] of swarmState.agentSessions) {
54294
+ const archAgent = swarmState.activeAgent.get(archSessionId) ?? archSession.agentName;
54295
+ if (stripKnownSwarmPrefix(archAgent) === ORCHESTRATOR_NAME) {
54296
+ try {
54297
+ injectAdvisory(archSessionId, `[SCOPE GUARD] ${violationMessage}`);
54298
+ } catch {}
54299
+ break;
54300
+ }
54301
+ }
54302
+ }
54303
+ throw new Error(violationMessage);
54304
+ }
54305
+ }
54306
+ };
54307
+ }
54308
+ function isFileInScope(filePath, scopeEntries) {
54309
+ const resolvedFile = path37.resolve(filePath);
54310
+ return scopeEntries.some((scope) => {
54311
+ const resolvedScope = path37.resolve(scope);
54312
+ if (resolvedFile === resolvedScope)
54313
+ return true;
54314
+ const rel = path37.relative(resolvedScope, resolvedFile);
54315
+ return rel.length > 0 && !rel.startsWith("..") && !path37.isAbsolute(rel);
54316
+ });
54317
+ }
54318
+
54319
+ // src/hooks/self-review.ts
54320
+ init_constants();
54321
+ init_schema();
54322
+ function createSelfReviewHook(config3, injectAdvisory) {
54323
+ const enabled = config3.enabled ?? true;
54324
+ const skipInTurbo = config3.skip_in_turbo ?? true;
54325
+ return {
54326
+ toolAfter: async (input, output) => {
54327
+ if (!enabled)
54328
+ return;
54329
+ const toolName = input.tool.replace(/^[^:]+[:.]/, "");
54330
+ if (toolName !== "update_task_status")
54331
+ return;
54332
+ const args2 = output.args;
54333
+ if (args2?.status !== "in_progress")
54334
+ return;
54335
+ const taskId = typeof args2?.task_id === "string" ? args2.task_id : "unknown";
54336
+ const sessionId = input.sessionID;
54337
+ const agentName = swarmState.activeAgent.get(sessionId) ?? swarmState.agentSessions.get(sessionId)?.agentName ?? "";
54338
+ if (stripKnownSwarmPrefix(agentName) !== ORCHESTRATOR_NAME)
54339
+ return;
54340
+ if (skipInTurbo) {
54341
+ const session = swarmState.agentSessions.get(sessionId);
54342
+ if (session && session.turboMode === true)
54343
+ return;
54344
+ }
54345
+ const advisory = [
54346
+ `[SELF-REVIEW] Task ${taskId} is now delegated to coder.`,
54347
+ `After coder completes, review for:`,
54348
+ ` \u2022 Broken conditionals (inverted if/else, wrong comparisons)`,
54349
+ ` \u2022 Off-by-one errors (array bounds, loop indices)`,
54350
+ ` \u2022 Assumptions contradicting existing codebase patterns`,
54351
+ ` \u2022 Missing error handling (uncaught exceptions, unhandled promises)`,
54352
+ ` \u2022 Scope creep (changes outside the declared task spec)`,
54353
+ `Delegate to critic with self-review focus before marking complete.`
54354
+ ].join(`
54355
+ `);
54356
+ try {
54357
+ injectAdvisory(sessionId, advisory);
54358
+ } catch {}
54359
+ }
54360
+ };
54361
+ }
54362
+
54113
54363
  // src/hooks/slop-detector.ts
54114
54364
  import * as fs23 from "fs";
54115
- import * as path37 from "path";
54365
+ import * as path38 from "path";
54116
54366
  var WRITE_EDIT_TOOLS = new Set([
54117
54367
  "write",
54118
54368
  "edit",
@@ -54162,7 +54412,7 @@ function walkFiles(dir, exts, deadline) {
54162
54412
  break;
54163
54413
  if (entry.isSymbolicLink())
54164
54414
  continue;
54165
- const full = path37.join(dir, entry.name);
54415
+ const full = path38.join(dir, entry.name);
54166
54416
  if (entry.isDirectory()) {
54167
54417
  if (entry.name === "node_modules" || entry.name === ".git")
54168
54418
  continue;
@@ -54177,7 +54427,7 @@ function walkFiles(dir, exts, deadline) {
54177
54427
  return results;
54178
54428
  }
54179
54429
  function checkDeadExports(content, projectDir, startTime) {
54180
- const hasPackageJson = fs23.existsSync(path37.join(projectDir, "package.json"));
54430
+ const hasPackageJson = fs23.existsSync(path38.join(projectDir, "package.json"));
54181
54431
  if (!hasPackageJson)
54182
54432
  return null;
54183
54433
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -54217,6 +54467,61 @@ function checkDeadExports(content, projectDir, startTime) {
54217
54467
  detail: `New exports not found in any import: ${deadExports.slice(0, 3).join(", ")}. Verify these are intentionally exported.`
54218
54468
  };
54219
54469
  }
54470
+ function checkStaleImports(content, threshold) {
54471
+ const lines = content.split(`
54472
+ `);
54473
+ const importLines = [];
54474
+ const importIdentifiers = [];
54475
+ const namedImportRe = /^(?:\+)?import\s*\{([^}]+)\}\s*from\s*['"][^'"]+['"]/;
54476
+ const defaultImportRe = /^(?:\+)?import\s+(\w+)\s+from\s*['"][^'"]+['"]/;
54477
+ const nsImportRe = /^(?:\+)?import\s+\*\s+as\s+(\w+)\s+from\s*['"][^'"]+['"]/;
54478
+ for (let i2 = 0;i2 < lines.length; i2++) {
54479
+ const line = lines[i2].replace(/^[+-]/, "");
54480
+ const trimmed = line.trim();
54481
+ if (trimmed.startsWith("export {") || trimmed.startsWith("export type {"))
54482
+ continue;
54483
+ const named = namedImportRe.exec(trimmed);
54484
+ if (named) {
54485
+ importLines.push(i2);
54486
+ for (const part of named[1].split(",")) {
54487
+ const cleaned = part.trim().replace(/^type\s+/, "").split(/\s+as\s+/).pop()?.trim();
54488
+ if (cleaned && /^\w+$/.test(cleaned) && cleaned.length >= 2) {
54489
+ importIdentifiers.push(cleaned);
54490
+ }
54491
+ }
54492
+ continue;
54493
+ }
54494
+ const ns = nsImportRe.exec(trimmed);
54495
+ if (ns?.[1]) {
54496
+ importLines.push(i2);
54497
+ importIdentifiers.push(ns[1]);
54498
+ continue;
54499
+ }
54500
+ const def = defaultImportRe.exec(trimmed);
54501
+ if (def?.[1] && def[1] !== "type") {
54502
+ importLines.push(i2);
54503
+ importIdentifiers.push(def[1]);
54504
+ }
54505
+ }
54506
+ if (importIdentifiers.length === 0)
54507
+ return null;
54508
+ const bodyLines = lines.filter((_, i2) => !importLines.includes(i2));
54509
+ const body2 = bodyLines.join(`
54510
+ `);
54511
+ const staleImports = [];
54512
+ for (const id of importIdentifiers) {
54513
+ const usageRe = new RegExp(`\\b${id}\\b`);
54514
+ if (!usageRe.test(body2)) {
54515
+ staleImports.push(id);
54516
+ }
54517
+ }
54518
+ if (staleImports.length < threshold)
54519
+ return null;
54520
+ return {
54521
+ type: "stale_import",
54522
+ detail: `${staleImports.length} unused import identifier(s): ${staleImports.slice(0, 3).join(", ")}${staleImports.length > 3 ? "..." : ""}. Remove stale imports.`
54523
+ };
54524
+ }
54220
54525
  function createSlopDetectorHook(config3, projectDir, injectSystemMessage) {
54221
54526
  return {
54222
54527
  toolAfter: async (input, output) => {
@@ -54259,6 +54564,11 @@ function createSlopDetectorHook(config3, projectDir, injectSystemMessage) {
54259
54564
  findings.push(dead);
54260
54565
  } catch {}
54261
54566
  }
54567
+ try {
54568
+ const stale = checkStaleImports(content, config3.importHygieneThreshold ?? 2);
54569
+ if (stale)
54570
+ findings.push(stale);
54571
+ } catch {}
54262
54572
  if (findings.length === 0)
54263
54573
  return;
54264
54574
  const findingText = findings.map((f) => ` \u2022 ${f.type}: ${f.detail}`).join(`
@@ -54640,7 +54950,7 @@ var build_check = createSwarmTool({
54640
54950
  init_dist();
54641
54951
  init_create_tool();
54642
54952
  import * as fs25 from "fs";
54643
- import * as path38 from "path";
54953
+ import * as path39 from "path";
54644
54954
  var EVIDENCE_DIR = ".swarm/evidence";
54645
54955
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
54646
54956
  function isValidTaskId3(taskId) {
@@ -54657,9 +54967,9 @@ function isValidTaskId3(taskId) {
54657
54967
  return TASK_ID_PATTERN2.test(taskId);
54658
54968
  }
54659
54969
  function isPathWithinSwarm(filePath, workspaceRoot) {
54660
- const normalizedWorkspace = path38.resolve(workspaceRoot);
54661
- const swarmPath = path38.join(normalizedWorkspace, ".swarm", "evidence");
54662
- const normalizedPath = path38.resolve(filePath);
54970
+ const normalizedWorkspace = path39.resolve(workspaceRoot);
54971
+ const swarmPath = path39.join(normalizedWorkspace, ".swarm", "evidence");
54972
+ const normalizedPath = path39.resolve(filePath);
54663
54973
  return normalizedPath.startsWith(swarmPath);
54664
54974
  }
54665
54975
  function readEvidenceFile(evidencePath) {
@@ -54720,7 +55030,7 @@ var check_gate_status = createSwarmTool({
54720
55030
  };
54721
55031
  return JSON.stringify(errorResult, null, 2);
54722
55032
  }
54723
- const evidencePath = path38.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
55033
+ const evidencePath = path39.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54724
55034
  if (!isPathWithinSwarm(evidencePath, directory)) {
54725
55035
  const errorResult = {
54726
55036
  taskId: taskIdInput,
@@ -54780,7 +55090,7 @@ var check_gate_status = createSwarmTool({
54780
55090
  init_dist();
54781
55091
  init_create_tool();
54782
55092
  import * as fs26 from "fs";
54783
- import * as path39 from "path";
55093
+ import * as path40 from "path";
54784
55094
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
54785
55095
  var DEFAULT_DAYS = 90;
54786
55096
  var DEFAULT_TOP_N = 20;
@@ -54924,7 +55234,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54924
55234
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
54925
55235
  const filteredChurn = new Map;
54926
55236
  for (const [file3, count] of churnMap) {
54927
- const ext = path39.extname(file3).toLowerCase();
55237
+ const ext = path40.extname(file3).toLowerCase();
54928
55238
  if (extSet.has(ext)) {
54929
55239
  filteredChurn.set(file3, count);
54930
55240
  }
@@ -54935,7 +55245,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54935
55245
  for (const [file3, churnCount] of filteredChurn) {
54936
55246
  let fullPath = file3;
54937
55247
  if (!fs26.existsSync(fullPath)) {
54938
- fullPath = path39.join(cwd, file3);
55248
+ fullPath = path40.join(cwd, file3);
54939
55249
  }
54940
55250
  const complexity = getComplexityForFile(fullPath);
54941
55251
  if (complexity !== null) {
@@ -55080,10 +55390,71 @@ var complexity_hotspots = createSwarmTool({
55080
55390
  }
55081
55391
  }
55082
55392
  });
55393
+ // src/tools/curator-analyze.ts
55394
+ init_dist();
55395
+ init_config();
55396
+ init_schema();
55397
+ init_create_tool();
55398
+ var curator_analyze = createSwarmTool({
55399
+ description: "Run curator phase analysis and optionally apply knowledge recommendations. " + "Call this after reviewing a phase to apply knowledge updates. " + "If recommendations is provided, applies them via applyCuratorKnowledgeUpdates.",
55400
+ args: {
55401
+ phase: tool.schema.number().int().min(1).describe("Phase number to analyze"),
55402
+ recommendations: tool.schema.array(tool.schema.object({
55403
+ action: tool.schema.enum([
55404
+ "promote",
55405
+ "archive",
55406
+ "flag_contradiction"
55407
+ ]),
55408
+ entry_id: tool.schema.string().optional(),
55409
+ lesson: tool.schema.string(),
55410
+ reason: tool.schema.string()
55411
+ })).optional().describe("Knowledge recommendations to apply. If omitted, only collects digest data.")
55412
+ },
55413
+ execute: async (args2, directory) => {
55414
+ const typedArgs = args2;
55415
+ try {
55416
+ if (!Number.isInteger(typedArgs.phase) || typedArgs.phase < 1) {
55417
+ return JSON.stringify({ error: "phase must be a positive integer >= 1" }, null, 2);
55418
+ }
55419
+ if (typedArgs.recommendations) {
55420
+ const validActions = ["promote", "archive", "flag_contradiction"];
55421
+ for (const rec of typedArgs.recommendations) {
55422
+ if (!validActions.includes(rec.action)) {
55423
+ return JSON.stringify({
55424
+ error: `Invalid recommendation action: ${rec.action}`
55425
+ }, null, 2);
55426
+ }
55427
+ }
55428
+ }
55429
+ const { config: config3 } = loadPluginConfigWithMeta(directory);
55430
+ const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
55431
+ const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
55432
+ const curatorResult = await runCuratorPhase(directory, typedArgs.phase, [], curatorConfig, {});
55433
+ let applied = 0;
55434
+ let skipped = 0;
55435
+ if (typedArgs.recommendations && typedArgs.recommendations.length > 0) {
55436
+ const result = await applyCuratorKnowledgeUpdates(directory, typedArgs.recommendations, knowledgeConfig);
55437
+ applied = result.applied;
55438
+ skipped = result.skipped;
55439
+ }
55440
+ return JSON.stringify({
55441
+ phase_digest: curatorResult.digest,
55442
+ compliance_count: curatorResult.compliance.length,
55443
+ applied,
55444
+ skipped
55445
+ }, null, 2);
55446
+ } catch (error93) {
55447
+ return JSON.stringify({
55448
+ error: String(error93),
55449
+ phase: typedArgs.phase
55450
+ }, null, 2);
55451
+ }
55452
+ }
55453
+ });
55083
55454
  // src/tools/declare-scope.ts
55084
55455
  init_tool();
55085
55456
  import * as fs27 from "fs";
55086
- import * as path40 from "path";
55457
+ import * as path41 from "path";
55087
55458
  init_create_tool();
55088
55459
  function validateTaskIdFormat(taskId) {
55089
55460
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -55162,8 +55533,8 @@ async function executeDeclareScope(args2, fallbackDir) {
55162
55533
  };
55163
55534
  }
55164
55535
  }
55165
- normalizedDir = path40.normalize(args2.working_directory);
55166
- const pathParts = normalizedDir.split(path40.sep);
55536
+ normalizedDir = path41.normalize(args2.working_directory);
55537
+ const pathParts = normalizedDir.split(path41.sep);
55167
55538
  if (pathParts.includes("..")) {
55168
55539
  return {
55169
55540
  success: false,
@@ -55173,10 +55544,10 @@ async function executeDeclareScope(args2, fallbackDir) {
55173
55544
  ]
55174
55545
  };
55175
55546
  }
55176
- const resolvedDir = path40.resolve(normalizedDir);
55547
+ const resolvedDir = path41.resolve(normalizedDir);
55177
55548
  try {
55178
55549
  const realPath = fs27.realpathSync(resolvedDir);
55179
- const planPath2 = path40.join(realPath, ".swarm", "plan.json");
55550
+ const planPath2 = path41.join(realPath, ".swarm", "plan.json");
55180
55551
  if (!fs27.existsSync(planPath2)) {
55181
55552
  return {
55182
55553
  success: false,
@@ -55196,8 +55567,11 @@ async function executeDeclareScope(args2, fallbackDir) {
55196
55567
  };
55197
55568
  }
55198
55569
  }
55199
- const directory = normalizedDir ?? fallbackDir ?? process.cwd();
55200
- const planPath = path40.resolve(directory, ".swarm", "plan.json");
55570
+ if (!fallbackDir) {
55571
+ console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
55572
+ }
55573
+ const directory = normalizedDir || fallbackDir || process.cwd();
55574
+ const planPath = path41.resolve(directory, ".swarm", "plan.json");
55201
55575
  if (!fs27.existsSync(planPath)) {
55202
55576
  return {
55203
55577
  success: false,
@@ -55260,6 +55634,7 @@ var declare_scope = createSwarmTool({
55260
55634
  });
55261
55635
  // src/tools/diff.ts
55262
55636
  init_dist();
55637
+ init_create_tool();
55263
55638
  import { execFileSync } from "child_process";
55264
55639
  var MAX_DIFF_LINES = 500;
55265
55640
  var DIFF_TIMEOUT_MS = 30000;
@@ -55287,34 +55662,35 @@ function validateBase(base) {
55287
55662
  function validatePaths(paths) {
55288
55663
  if (!paths)
55289
55664
  return null;
55290
- for (const path41 of paths) {
55291
- if (!path41 || path41.length === 0) {
55665
+ for (const path42 of paths) {
55666
+ if (!path42 || path42.length === 0) {
55292
55667
  return "empty path not allowed";
55293
55668
  }
55294
- if (path41.length > MAX_PATH_LENGTH) {
55669
+ if (path42.length > MAX_PATH_LENGTH) {
55295
55670
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
55296
55671
  }
55297
- if (SHELL_METACHARACTERS2.test(path41)) {
55672
+ if (SHELL_METACHARACTERS2.test(path42)) {
55298
55673
  return "path contains shell metacharacters";
55299
55674
  }
55300
- if (path41.startsWith("-")) {
55675
+ if (path42.startsWith("-")) {
55301
55676
  return 'path cannot start with "-" (option-like arguments not allowed)';
55302
55677
  }
55303
- if (CONTROL_CHAR_PATTERN2.test(path41)) {
55678
+ if (CONTROL_CHAR_PATTERN2.test(path42)) {
55304
55679
  return "path contains control characters";
55305
55680
  }
55306
55681
  }
55307
55682
  return null;
55308
55683
  }
55309
- var diff = tool({
55684
+ var diff = createSwarmTool({
55310
55685
  description: "Analyze git diff for changed files, exports, interfaces, and function signatures. Returns structured output with contract change detection.",
55311
55686
  args: {
55312
55687
  base: tool.schema.string().optional().describe('Base ref to diff against (default: HEAD). Use "staged" for staged changes, "unstaged" for working tree changes.'),
55313
55688
  paths: tool.schema.array(tool.schema.string()).optional().describe("Optional file paths to restrict diff scope.")
55314
55689
  },
55315
- async execute(args2, context) {
55690
+ async execute(args2, directory, _ctx) {
55691
+ const typedArgs = args2;
55316
55692
  try {
55317
- if (!context.directory || typeof context.directory !== "string" || context.directory.trim() === "") {
55693
+ if (!directory || typeof directory !== "string" || directory.trim() === "") {
55318
55694
  const errorResult = {
55319
55695
  error: "project directory is required but was not provided",
55320
55696
  files: [],
@@ -55323,7 +55699,7 @@ var diff = tool({
55323
55699
  };
55324
55700
  return JSON.stringify(errorResult, null, 2);
55325
55701
  }
55326
- const base = args2.base ?? "HEAD";
55702
+ const base = typedArgs.base ?? "HEAD";
55327
55703
  const baseValidationError = validateBase(base);
55328
55704
  if (baseValidationError) {
55329
55705
  const errorResult = {
@@ -55334,7 +55710,7 @@ var diff = tool({
55334
55710
  };
55335
55711
  return JSON.stringify(errorResult, null, 2);
55336
55712
  }
55337
- const pathsValidationError = validatePaths(args2.paths);
55713
+ const pathsValidationError = validatePaths(typedArgs.paths);
55338
55714
  if (pathsValidationError) {
55339
55715
  const errorResult = {
55340
55716
  error: `invalid paths: ${pathsValidationError}`,
@@ -55354,21 +55730,21 @@ var diff = tool({
55354
55730
  }
55355
55731
  const numstatArgs = [...gitArgs, "--numstat"];
55356
55732
  const fullDiffArgs = [...gitArgs, "-U3"];
55357
- if (args2.paths?.length) {
55358
- numstatArgs.push("--", ...args2.paths);
55359
- fullDiffArgs.push("--", ...args2.paths);
55733
+ if (typedArgs.paths?.length) {
55734
+ numstatArgs.push("--", ...typedArgs.paths);
55735
+ fullDiffArgs.push("--", ...typedArgs.paths);
55360
55736
  }
55361
55737
  const numstatOutput = execFileSync("git", numstatArgs, {
55362
55738
  encoding: "utf-8",
55363
55739
  timeout: DIFF_TIMEOUT_MS,
55364
55740
  maxBuffer: MAX_BUFFER_BYTES,
55365
- cwd: context.directory
55741
+ cwd: directory
55366
55742
  });
55367
55743
  const fullDiffOutput = execFileSync("git", fullDiffArgs, {
55368
55744
  encoding: "utf-8",
55369
55745
  timeout: DIFF_TIMEOUT_MS,
55370
55746
  maxBuffer: MAX_BUFFER_BYTES,
55371
- cwd: context.directory
55747
+ cwd: directory
55372
55748
  });
55373
55749
  const files = [];
55374
55750
  const numstatLines = numstatOutput.split(`
@@ -55380,8 +55756,8 @@ var diff = tool({
55380
55756
  if (parts2.length >= 3) {
55381
55757
  const additions = parseInt(parts2[0], 10) || 0;
55382
55758
  const deletions = parseInt(parts2[1], 10) || 0;
55383
- const path41 = parts2[2];
55384
- files.push({ path: path41, additions, deletions });
55759
+ const path42 = parts2[2];
55760
+ files.push({ path: path42, additions, deletions });
55385
55761
  }
55386
55762
  }
55387
55763
  const contractChanges = [];
@@ -55611,7 +55987,7 @@ Use these as DOMAIN values when delegating to @sme.`;
55611
55987
  init_dist();
55612
55988
  init_create_tool();
55613
55989
  import * as fs28 from "fs";
55614
- import * as path41 from "path";
55990
+ import * as path42 from "path";
55615
55991
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
55616
55992
  var MAX_EVIDENCE_FILES = 1000;
55617
55993
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -55641,9 +56017,9 @@ function validateRequiredTypes(input) {
55641
56017
  return null;
55642
56018
  }
55643
56019
  function isPathWithinSwarm2(filePath, cwd) {
55644
- const normalizedCwd = path41.resolve(cwd);
55645
- const swarmPath = path41.join(normalizedCwd, ".swarm");
55646
- const normalizedPath = path41.resolve(filePath);
56020
+ const normalizedCwd = path42.resolve(cwd);
56021
+ const swarmPath = path42.join(normalizedCwd, ".swarm");
56022
+ const normalizedPath = path42.resolve(filePath);
55647
56023
  return normalizedPath.startsWith(swarmPath);
55648
56024
  }
55649
56025
  function parseCompletedTasks(planContent) {
@@ -55673,10 +56049,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
55673
56049
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
55674
56050
  continue;
55675
56051
  }
55676
- const filePath = path41.join(evidenceDir, filename);
56052
+ const filePath = path42.join(evidenceDir, filename);
55677
56053
  try {
55678
- const resolvedPath = path41.resolve(filePath);
55679
- const evidenceDirResolved = path41.resolve(evidenceDir);
56054
+ const resolvedPath = path42.resolve(filePath);
56055
+ const evidenceDirResolved = path42.resolve(evidenceDir);
55680
56056
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
55681
56057
  continue;
55682
56058
  }
@@ -55794,7 +56170,7 @@ var evidence_check = createSwarmTool({
55794
56170
  return JSON.stringify(errorResult, null, 2);
55795
56171
  }
55796
56172
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
55797
- const planPath = path41.join(cwd, PLAN_FILE);
56173
+ const planPath = path42.join(cwd, PLAN_FILE);
55798
56174
  if (!isPathWithinSwarm2(planPath, cwd)) {
55799
56175
  const errorResult = {
55800
56176
  error: "plan file path validation failed",
@@ -55826,7 +56202,7 @@ var evidence_check = createSwarmTool({
55826
56202
  };
55827
56203
  return JSON.stringify(result2, null, 2);
55828
56204
  }
55829
- const evidenceDir = path41.join(cwd, EVIDENCE_DIR2);
56205
+ const evidenceDir = path42.join(cwd, EVIDENCE_DIR2);
55830
56206
  const evidence = readEvidenceFiles(evidenceDir, cwd);
55831
56207
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
55832
56208
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -55844,7 +56220,7 @@ var evidence_check = createSwarmTool({
55844
56220
  init_tool();
55845
56221
  init_create_tool();
55846
56222
  import * as fs29 from "fs";
55847
- import * as path42 from "path";
56223
+ import * as path43 from "path";
55848
56224
  var EXT_MAP = {
55849
56225
  python: ".py",
55850
56226
  py: ".py",
@@ -55925,12 +56301,12 @@ var extract_code_blocks = createSwarmTool({
55925
56301
  if (prefix) {
55926
56302
  filename = `${prefix}_${filename}`;
55927
56303
  }
55928
- let filepath = path42.join(targetDir, filename);
55929
- const base = path42.basename(filepath, path42.extname(filepath));
55930
- const ext = path42.extname(filepath);
56304
+ let filepath = path43.join(targetDir, filename);
56305
+ const base = path43.basename(filepath, path43.extname(filepath));
56306
+ const ext = path43.extname(filepath);
55931
56307
  let counter = 1;
55932
56308
  while (fs29.existsSync(filepath)) {
55933
- filepath = path42.join(targetDir, `${base}_${counter}${ext}`);
56309
+ filepath = path43.join(targetDir, `${base}_${counter}${ext}`);
55934
56310
  counter++;
55935
56311
  }
55936
56312
  try {
@@ -55960,10 +56336,11 @@ Errors:
55960
56336
  });
55961
56337
  // src/tools/gitingest.ts
55962
56338
  init_dist();
56339
+ init_create_tool();
55963
56340
  var GITINGEST_TIMEOUT_MS = 1e4;
55964
56341
  var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
55965
56342
  var GITINGEST_MAX_RETRIES = 2;
55966
- var delay = (ms) => new Promise((resolve14) => setTimeout(resolve14, ms));
56343
+ var delay = (ms) => new Promise((resolve15) => setTimeout(resolve15, ms));
55967
56344
  async function fetchGitingest(args2) {
55968
56345
  for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
55969
56346
  try {
@@ -56033,7 +56410,7 @@ ${data.content}`;
56033
56410
  }
56034
56411
  throw new Error("gitingest request failed after retries");
56035
56412
  }
56036
- var gitingest = tool({
56413
+ var gitingest = createSwarmTool({
56037
56414
  description: "Fetch a GitHub repository's full content via gitingest.com. Returns summary, directory tree, and file contents optimized for LLM analysis. Use when you need to understand an external repository's structure or code.",
56038
56415
  args: {
56039
56416
  url: tool.schema.string().describe("GitHub repository URL (e.g., https://github.com/owner/repo)"),
@@ -56041,14 +56418,16 @@ var gitingest = tool({
56041
56418
  pattern: tool.schema.string().optional().describe("Glob pattern to filter files (e.g., '*.ts' or 'src/**/*.py')"),
56042
56419
  patternType: tool.schema.enum(["include", "exclude"]).optional().describe("Whether pattern includes or excludes matching files (default: exclude)")
56043
56420
  },
56044
- async execute(args2, _context) {
56045
- return fetchGitingest(args2);
56421
+ async execute(args2, _directory, _ctx) {
56422
+ const typedArgs = args2;
56423
+ return fetchGitingest(typedArgs);
56046
56424
  }
56047
56425
  });
56048
56426
  // src/tools/imports.ts
56049
56427
  init_dist();
56428
+ init_create_tool();
56050
56429
  import * as fs30 from "fs";
56051
- import * as path43 from "path";
56430
+ import * as path44 from "path";
56052
56431
  var MAX_FILE_PATH_LENGTH2 = 500;
56053
56432
  var MAX_SYMBOL_LENGTH = 256;
56054
56433
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -56102,7 +56481,7 @@ function validateSymbolInput(symbol3) {
56102
56481
  return null;
56103
56482
  }
56104
56483
  function isBinaryFile2(filePath, buffer) {
56105
- const ext = path43.extname(filePath).toLowerCase();
56484
+ const ext = path44.extname(filePath).toLowerCase();
56106
56485
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
56107
56486
  return false;
56108
56487
  }
@@ -56126,15 +56505,15 @@ function parseImports(content, targetFile, targetSymbol) {
56126
56505
  const imports = [];
56127
56506
  let _resolvedTarget;
56128
56507
  try {
56129
- _resolvedTarget = path43.resolve(targetFile);
56508
+ _resolvedTarget = path44.resolve(targetFile);
56130
56509
  } catch {
56131
56510
  _resolvedTarget = targetFile;
56132
56511
  }
56133
- const targetBasename = path43.basename(targetFile, path43.extname(targetFile));
56512
+ const targetBasename = path44.basename(targetFile, path44.extname(targetFile));
56134
56513
  const targetWithExt = targetFile;
56135
56514
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
56136
- const normalizedTargetWithExt = path43.normalize(targetWithExt).replace(/\\/g, "/");
56137
- const normalizedTargetWithoutExt = path43.normalize(targetWithoutExt).replace(/\\/g, "/");
56515
+ const normalizedTargetWithExt = path44.normalize(targetWithExt).replace(/\\/g, "/");
56516
+ const normalizedTargetWithoutExt = path44.normalize(targetWithoutExt).replace(/\\/g, "/");
56138
56517
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
56139
56518
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
56140
56519
  const modulePath = match[1] || match[2] || match[3];
@@ -56157,9 +56536,9 @@ function parseImports(content, targetFile, targetSymbol) {
56157
56536
  }
56158
56537
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
56159
56538
  let isMatch = false;
56160
- const _targetDir = path43.dirname(targetFile);
56161
- const targetExt = path43.extname(targetFile);
56162
- const targetBasenameNoExt = path43.basename(targetFile, targetExt);
56539
+ const _targetDir = path44.dirname(targetFile);
56540
+ const targetExt = path44.extname(targetFile);
56541
+ const targetBasenameNoExt = path44.basename(targetFile, targetExt);
56163
56542
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
56164
56543
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
56165
56544
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -56227,10 +56606,10 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56227
56606
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
56228
56607
  for (const entry of entries) {
56229
56608
  if (SKIP_DIRECTORIES2.has(entry)) {
56230
- stats.skippedDirs.push(path43.join(dir, entry));
56609
+ stats.skippedDirs.push(path44.join(dir, entry));
56231
56610
  continue;
56232
56611
  }
56233
- const fullPath = path43.join(dir, entry);
56612
+ const fullPath = path44.join(dir, entry);
56234
56613
  let stat2;
56235
56614
  try {
56236
56615
  stat2 = fs30.statSync(fullPath);
@@ -56244,7 +56623,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56244
56623
  if (stat2.isDirectory()) {
56245
56624
  findSourceFiles(fullPath, files, stats);
56246
56625
  } else if (stat2.isFile()) {
56247
- const ext = path43.extname(fullPath).toLowerCase();
56626
+ const ext = path44.extname(fullPath).toLowerCase();
56248
56627
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
56249
56628
  files.push(fullPath);
56250
56629
  }
@@ -56252,19 +56631,20 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56252
56631
  }
56253
56632
  return files;
56254
56633
  }
56255
- var imports = tool({
56634
+ var imports = createSwarmTool({
56256
56635
  description: "Find all consumers that import from a given file. Returns JSON with file path, line numbers, and import metadata for each consumer. Useful for understanding dependency relationships.",
56257
56636
  args: {
56258
56637
  file: tool.schema.string().describe('Source file path to find importers for (e.g., "./src/utils.ts")'),
56259
56638
  symbol: tool.schema.string().optional().describe('Optional specific symbol to filter imports (e.g., "MyClass")')
56260
56639
  },
56261
- async execute(args2, _context) {
56640
+ async execute(args2, _directory, _ctx) {
56641
+ const typedArgs = args2;
56262
56642
  let file3;
56263
56643
  let symbol3;
56264
56644
  try {
56265
- if (args2 && typeof args2 === "object") {
56266
- file3 = args2.file;
56267
- symbol3 = args2.symbol;
56645
+ if (typedArgs && typeof typedArgs === "object") {
56646
+ file3 = typedArgs.file;
56647
+ symbol3 = typedArgs.symbol;
56268
56648
  }
56269
56649
  } catch {}
56270
56650
  if (file3 === undefined) {
@@ -56300,7 +56680,7 @@ var imports = tool({
56300
56680
  return JSON.stringify(errorResult, null, 2);
56301
56681
  }
56302
56682
  try {
56303
- const targetFile = path43.resolve(file3);
56683
+ const targetFile = path44.resolve(file3);
56304
56684
  if (!fs30.existsSync(targetFile)) {
56305
56685
  const errorResult = {
56306
56686
  error: `target file not found: ${file3}`,
@@ -56322,7 +56702,7 @@ var imports = tool({
56322
56702
  };
56323
56703
  return JSON.stringify(errorResult, null, 2);
56324
56704
  }
56325
- const baseDir = path43.dirname(targetFile);
56705
+ const baseDir = path44.dirname(targetFile);
56326
56706
  const scanStats = {
56327
56707
  skippedDirs: [],
56328
56708
  skippedFiles: 0,
@@ -56405,13 +56785,120 @@ var imports = tool({
56405
56785
  }
56406
56786
  }
56407
56787
  });
56788
+ // src/tools/knowledge-add.ts
56789
+ init_dist();
56790
+ init_create_tool();
56791
+ var VALID_CATEGORIES2 = [
56792
+ "process",
56793
+ "architecture",
56794
+ "tooling",
56795
+ "security",
56796
+ "testing",
56797
+ "debugging",
56798
+ "performance",
56799
+ "integration",
56800
+ "other"
56801
+ ];
56802
+ var knowledgeAdd = createSwarmTool({
56803
+ description: "Store a new lesson in the knowledge base for future reference. The lesson will be available for retrieval via knowledge_recall.",
56804
+ args: {
56805
+ lesson: tool.schema.string().min(15).max(280).describe("The lesson to store (15-280 characters)"),
56806
+ category: tool.schema.enum(VALID_CATEGORIES2).describe("Knowledge category for the lesson"),
56807
+ tags: tool.schema.array(tool.schema.string()).optional().describe("Optional tags for better searchability"),
56808
+ scope: tool.schema.string().optional().describe("Scope of the lesson (global or stack:<name>)")
56809
+ },
56810
+ execute: async (args2, directory) => {
56811
+ let lessonInput;
56812
+ let categoryInput;
56813
+ let tagsInput;
56814
+ let scopeInput;
56815
+ try {
56816
+ if (args2 && typeof args2 === "object") {
56817
+ const obj = args2;
56818
+ lessonInput = obj.lesson;
56819
+ categoryInput = obj.category;
56820
+ tagsInput = obj.tags;
56821
+ scopeInput = obj.scope;
56822
+ }
56823
+ } catch {}
56824
+ if (typeof lessonInput !== "string") {
56825
+ return JSON.stringify({
56826
+ success: false,
56827
+ error: "lesson must be a string"
56828
+ });
56829
+ }
56830
+ const lesson = lessonInput;
56831
+ if (lesson.length < 15 || lesson.length > 280) {
56832
+ return JSON.stringify({
56833
+ success: false,
56834
+ error: "lesson must be between 15 and 280 characters"
56835
+ });
56836
+ }
56837
+ if (typeof categoryInput !== "string") {
56838
+ return JSON.stringify({
56839
+ success: false,
56840
+ error: "category must be a string"
56841
+ });
56842
+ }
56843
+ const category = categoryInput;
56844
+ if (!VALID_CATEGORIES2.includes(category)) {
56845
+ return JSON.stringify({
56846
+ success: false,
56847
+ error: `category must be one of: ${VALID_CATEGORIES2.join(", ")}`
56848
+ });
56849
+ }
56850
+ let tags = [];
56851
+ if (tagsInput !== undefined) {
56852
+ if (Array.isArray(tagsInput)) {
56853
+ tags = tagsInput.filter((t) => typeof t === "string").slice(0, 20);
56854
+ }
56855
+ }
56856
+ const scope = typeof scopeInput === "string" && scopeInput.length > 0 ? scopeInput : "global";
56857
+ const entry = {
56858
+ id: crypto.randomUUID(),
56859
+ tier: "swarm",
56860
+ lesson,
56861
+ category,
56862
+ tags,
56863
+ scope,
56864
+ confidence: 0.5,
56865
+ status: "candidate",
56866
+ confirmed_by: [],
56867
+ project_name: "",
56868
+ retrieval_outcomes: {
56869
+ applied_count: 0,
56870
+ succeeded_after_count: 0,
56871
+ failed_after_count: 0
56872
+ },
56873
+ schema_version: 1,
56874
+ created_at: new Date().toISOString(),
56875
+ updated_at: new Date().toISOString(),
56876
+ auto_generated: true,
56877
+ hive_eligible: false
56878
+ };
56879
+ try {
56880
+ await appendKnowledge(resolveSwarmKnowledgePath(directory), entry);
56881
+ } catch (err2) {
56882
+ const message = err2 instanceof Error ? err2.message : "Unknown error";
56883
+ return JSON.stringify({
56884
+ success: false,
56885
+ error: message
56886
+ });
56887
+ }
56888
+ return JSON.stringify({
56889
+ success: true,
56890
+ id: entry.id,
56891
+ category
56892
+ });
56893
+ }
56894
+ });
56408
56895
  // src/tools/knowledge-query.ts
56409
56896
  init_dist();
56410
56897
  import { existsSync as existsSync28 } from "fs";
56411
56898
  init_create_tool();
56412
56899
  var DEFAULT_LIMIT = 10;
56413
56900
  var MAX_LESSON_LENGTH = 200;
56414
- var VALID_CATEGORIES2 = [
56901
+ var VALID_CATEGORIES3 = [
56415
56902
  "process",
56416
56903
  "architecture",
56417
56904
  "tooling",
@@ -56446,7 +56933,7 @@ function validateCategoryInput(category) {
56446
56933
  if (typeof category !== "string")
56447
56934
  return null;
56448
56935
  const normalized = category.toLowerCase().trim();
56449
- if (VALID_CATEGORIES2.includes(normalized)) {
56936
+ if (VALID_CATEGORIES3.includes(normalized)) {
56450
56937
  return normalized;
56451
56938
  }
56452
56939
  return null;
@@ -56634,6 +57121,147 @@ var knowledge_query = createSwarmTool({
56634
57121
  `);
56635
57122
  }
56636
57123
  });
57124
+ // src/tools/knowledge-recall.ts
57125
+ init_dist();
57126
+ init_create_tool();
57127
+ var knowledgeRecall = createSwarmTool({
57128
+ description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
57129
+ args: {
57130
+ query: tool.schema.string().min(3).describe("Natural language search query"),
57131
+ top_n: tool.schema.number().int().min(1).max(20).optional().describe("Maximum results to return (default: 5)"),
57132
+ tier: tool.schema.enum(["all", "swarm", "hive"]).optional().describe("Knowledge tier to search (default: 'all')")
57133
+ },
57134
+ execute: async (args2, directory) => {
57135
+ let queryInput;
57136
+ let topNInput;
57137
+ let tierInput;
57138
+ try {
57139
+ if (args2 && typeof args2 === "object") {
57140
+ const obj = args2;
57141
+ queryInput = obj.query;
57142
+ topNInput = obj.top_n;
57143
+ tierInput = obj.tier;
57144
+ }
57145
+ } catch {}
57146
+ if (typeof queryInput !== "string" || queryInput.length < 3) {
57147
+ return JSON.stringify({
57148
+ results: [],
57149
+ total: 0,
57150
+ error: "query must be a string with at least 3 characters"
57151
+ });
57152
+ }
57153
+ let topN = 5;
57154
+ if (topNInput !== undefined) {
57155
+ if (typeof topNInput === "number" && Number.isInteger(topNInput)) {
57156
+ topN = Math.max(1, Math.min(20, topNInput));
57157
+ }
57158
+ }
57159
+ let tier = "all";
57160
+ if (tierInput !== undefined && typeof tierInput === "string") {
57161
+ if (tierInput === "swarm" || tierInput === "hive") {
57162
+ tier = tierInput;
57163
+ }
57164
+ }
57165
+ const swarmPath = resolveSwarmKnowledgePath(directory);
57166
+ const hivePath = resolveHiveKnowledgePath();
57167
+ const [swarmEntries, hiveEntries] = await Promise.all([
57168
+ readKnowledge(swarmPath),
57169
+ readKnowledge(hivePath)
57170
+ ]);
57171
+ let entries = [];
57172
+ if (tier === "all" || tier === "swarm") {
57173
+ entries = entries.concat(swarmEntries);
57174
+ }
57175
+ if (tier === "all" || tier === "hive") {
57176
+ entries = entries.concat(hiveEntries);
57177
+ }
57178
+ if (entries.length === 0) {
57179
+ const result2 = { results: [], total: 0 };
57180
+ return JSON.stringify(result2);
57181
+ }
57182
+ const normalizedQuery = normalize2(queryInput);
57183
+ const queryBigrams = wordBigrams(normalizedQuery);
57184
+ const scoredEntries = entries.map((entry) => {
57185
+ const entryText = `${entry.lesson} ${entry.tags.join(" ")} ${entry.category}`;
57186
+ const entryBigrams = wordBigrams(entryText);
57187
+ const textScore = jaccardBigram(queryBigrams, entryBigrams);
57188
+ const boost = entry.status === "established" ? 0.1 : entry.status === "promoted" ? 0.05 : 0;
57189
+ const finalScore = textScore + boost;
57190
+ return {
57191
+ id: entry.id,
57192
+ confidence: entry.confidence,
57193
+ category: entry.category,
57194
+ lesson: entry.lesson,
57195
+ score: finalScore
57196
+ };
57197
+ });
57198
+ scoredEntries.sort((a, b) => b.score - a.score);
57199
+ const topResults = scoredEntries.slice(0, topN);
57200
+ const result = {
57201
+ results: topResults,
57202
+ total: topResults.length
57203
+ };
57204
+ return JSON.stringify(result);
57205
+ }
57206
+ });
57207
+ // src/tools/knowledge-remove.ts
57208
+ init_dist();
57209
+ init_create_tool();
57210
+ var knowledgeRemove = createSwarmTool({
57211
+ description: "Delete an outdated knowledge entry by ID. Double-deletion is idempotent \u2014 removing a non-existent entry returns a clear message without error.",
57212
+ args: {
57213
+ id: tool.schema.string().min(1).describe("UUID of the knowledge entry to remove")
57214
+ },
57215
+ execute: async (args2, directory) => {
57216
+ let idInput;
57217
+ try {
57218
+ if (args2 && typeof args2 === "object") {
57219
+ const obj = args2;
57220
+ idInput = obj.id;
57221
+ }
57222
+ } catch {}
57223
+ if (typeof idInput !== "string" || idInput.length < 1) {
57224
+ return JSON.stringify({
57225
+ success: false,
57226
+ error: "id must be a non-empty string"
57227
+ });
57228
+ }
57229
+ const id = idInput;
57230
+ const swarmPath = resolveSwarmKnowledgePath(directory);
57231
+ let entries;
57232
+ try {
57233
+ entries = await readKnowledge(swarmPath);
57234
+ } catch (err2) {
57235
+ const message = err2 instanceof Error ? err2.message : "Unknown error";
57236
+ return JSON.stringify({
57237
+ success: false,
57238
+ error: message
57239
+ });
57240
+ }
57241
+ const originalCount = entries.length;
57242
+ entries = entries.filter((entry) => entry.id !== id);
57243
+ if (entries.length === originalCount) {
57244
+ return JSON.stringify({
57245
+ success: false,
57246
+ message: "entry not found"
57247
+ });
57248
+ }
57249
+ try {
57250
+ await rewriteKnowledge(swarmPath, entries);
57251
+ } catch (err2) {
57252
+ const message = err2 instanceof Error ? err2.message : "Unknown error";
57253
+ return JSON.stringify({
57254
+ success: false,
57255
+ error: message
57256
+ });
57257
+ }
57258
+ return JSON.stringify({
57259
+ success: true,
57260
+ removed: 1,
57261
+ remaining: entries.length
57262
+ });
57263
+ }
57264
+ });
56637
57265
 
56638
57266
  // src/tools/index.ts
56639
57267
  init_lint();
@@ -56644,7 +57272,7 @@ init_config();
56644
57272
  init_schema();
56645
57273
  init_manager();
56646
57274
  import * as fs31 from "fs";
56647
- import * as path44 from "path";
57275
+ import * as path45 from "path";
56648
57276
  init_utils2();
56649
57277
  init_create_tool();
56650
57278
  function safeWarn(message, error93) {
@@ -56701,7 +57329,7 @@ function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSess
56701
57329
  function isValidRetroEntry(entry, phase) {
56702
57330
  return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
56703
57331
  }
56704
- async function executePhaseComplete(args2, workingDirectory) {
57332
+ async function executePhaseComplete(args2, workingDirectory, directory) {
56705
57333
  const phase = Number(args2.phase);
56706
57334
  const summary = args2.summary;
56707
57335
  const sessionID = args2.sessionID;
@@ -56729,7 +57357,7 @@ async function executePhaseComplete(args2, workingDirectory) {
56729
57357
  const phaseReferenceTimestamp = session.lastPhaseCompleteTimestamp ?? 0;
56730
57358
  const crossSessionResult = collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, sessionID);
56731
57359
  const agentsDispatched = Array.from(crossSessionResult.agents).sort();
56732
- const dir = workingDirectory ?? process.cwd();
57360
+ const dir = workingDirectory || directory || process.cwd();
56733
57361
  const { config: config3 } = loadPluginConfigWithMeta(dir);
56734
57362
  let phaseCompleteConfig;
56735
57363
  try {
@@ -56862,7 +57490,7 @@ async function executePhaseComplete(args2, workingDirectory) {
56862
57490
  };
56863
57491
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
56864
57492
  try {
56865
- const projectName = path44.basename(dir);
57493
+ const projectName = path45.basename(dir);
56866
57494
  await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
56867
57495
  } catch (error93) {
56868
57496
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
@@ -56874,7 +57502,17 @@ async function executePhaseComplete(args2, workingDirectory) {
56874
57502
  if (curatorConfig.enabled && curatorConfig.phase_enabled) {
56875
57503
  const curatorResult = await runCuratorPhase(dir, phase, agentsDispatched, curatorConfig, {});
56876
57504
  await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, knowledgeConfig);
56877
- await runCriticDriftCheck(dir, phase, curatorResult, curatorConfig);
57505
+ const driftResult = await runCriticDriftCheck(dir, phase, curatorResult, curatorConfig);
57506
+ const callerSessionState = swarmState.agentSessions.get(sessionID);
57507
+ if (callerSessionState) {
57508
+ callerSessionState.pendingAdvisoryMessages ??= [];
57509
+ const digestSummary = curatorResult.digest?.summary ? curatorResult.digest.summary.slice(0, 200) : "Phase analysis complete";
57510
+ const complianceNote = curatorResult.compliance.length > 0 ? ` (${curatorResult.compliance.length} compliance observation(s))` : "";
57511
+ callerSessionState.pendingAdvisoryMessages.push(`[CURATOR] Phase ${phase} digest: ${digestSummary}${complianceNote}. Call curator_analyze with recommendations to apply knowledge updates from this phase.`);
57512
+ if (driftResult.report.drift_score > 0) {
57513
+ callerSessionState.pendingAdvisoryMessages.push(`[CURATOR DRIFT DETECTED (phase ${phase}, score ${driftResult.report.drift_score})]: ${driftResult.injection_text.slice(0, 300)}. Review ${driftResult.report_path} and address spec alignment before proceeding.`);
57514
+ }
57515
+ }
56878
57516
  if (curatorResult.compliance.length > 0 && !curatorConfig.suppress_warnings) {
56879
57517
  const complianceLines = curatorResult.compliance.map((obs) => `[${obs.severity.toUpperCase()}] ${obs.description}`).slice(0, 5);
56880
57518
  complianceWarnings = complianceLines;
@@ -57002,7 +57640,7 @@ var phase_complete = createSwarmTool({
57002
57640
  warnings: ["Failed to parse arguments"]
57003
57641
  }, null, 2);
57004
57642
  }
57005
- return executePhaseComplete(phaseCompleteArgs, directory);
57643
+ return executePhaseComplete(phaseCompleteArgs, undefined, directory);
57006
57644
  }
57007
57645
  });
57008
57646
  // src/tools/pkg-audit.ts
@@ -57011,7 +57649,7 @@ init_discovery();
57011
57649
  init_utils();
57012
57650
  init_create_tool();
57013
57651
  import * as fs32 from "fs";
57014
- import * as path45 from "path";
57652
+ import * as path46 from "path";
57015
57653
  var MAX_OUTPUT_BYTES5 = 52428800;
57016
57654
  var AUDIT_TIMEOUT_MS = 120000;
57017
57655
  function isValidEcosystem(value) {
@@ -57029,16 +57667,16 @@ function validateArgs3(args2) {
57029
57667
  function detectEcosystems(directory) {
57030
57668
  const ecosystems = [];
57031
57669
  const cwd = directory;
57032
- if (fs32.existsSync(path45.join(cwd, "package.json"))) {
57670
+ if (fs32.existsSync(path46.join(cwd, "package.json"))) {
57033
57671
  ecosystems.push("npm");
57034
57672
  }
57035
- if (fs32.existsSync(path45.join(cwd, "pyproject.toml")) || fs32.existsSync(path45.join(cwd, "requirements.txt"))) {
57673
+ if (fs32.existsSync(path46.join(cwd, "pyproject.toml")) || fs32.existsSync(path46.join(cwd, "requirements.txt"))) {
57036
57674
  ecosystems.push("pip");
57037
57675
  }
57038
- if (fs32.existsSync(path45.join(cwd, "Cargo.toml"))) {
57676
+ if (fs32.existsSync(path46.join(cwd, "Cargo.toml"))) {
57039
57677
  ecosystems.push("cargo");
57040
57678
  }
57041
- if (fs32.existsSync(path45.join(cwd, "go.mod"))) {
57679
+ if (fs32.existsSync(path46.join(cwd, "go.mod"))) {
57042
57680
  ecosystems.push("go");
57043
57681
  }
57044
57682
  try {
@@ -57047,10 +57685,10 @@ function detectEcosystems(directory) {
57047
57685
  ecosystems.push("dotnet");
57048
57686
  }
57049
57687
  } catch {}
57050
- if (fs32.existsSync(path45.join(cwd, "Gemfile")) || fs32.existsSync(path45.join(cwd, "Gemfile.lock"))) {
57688
+ if (fs32.existsSync(path46.join(cwd, "Gemfile")) || fs32.existsSync(path46.join(cwd, "Gemfile.lock"))) {
57051
57689
  ecosystems.push("ruby");
57052
57690
  }
57053
- if (fs32.existsSync(path45.join(cwd, "pubspec.yaml"))) {
57691
+ if (fs32.existsSync(path46.join(cwd, "pubspec.yaml"))) {
57054
57692
  ecosystems.push("dart");
57055
57693
  }
57056
57694
  return ecosystems;
@@ -57063,7 +57701,7 @@ async function runNpmAudit(directory) {
57063
57701
  stderr: "pipe",
57064
57702
  cwd: directory
57065
57703
  });
57066
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
57704
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57067
57705
  const result = await Promise.race([
57068
57706
  Promise.all([
57069
57707
  new Response(proc.stdout).text(),
@@ -57186,7 +57824,7 @@ async function runPipAudit(directory) {
57186
57824
  stderr: "pipe",
57187
57825
  cwd: directory
57188
57826
  });
57189
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
57827
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57190
57828
  const result = await Promise.race([
57191
57829
  Promise.all([
57192
57830
  new Response(proc.stdout).text(),
@@ -57317,7 +57955,7 @@ async function runCargoAudit(directory) {
57317
57955
  stderr: "pipe",
57318
57956
  cwd: directory
57319
57957
  });
57320
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
57958
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57321
57959
  const result = await Promise.race([
57322
57960
  Promise.all([
57323
57961
  new Response(proc.stdout).text(),
@@ -57444,7 +58082,7 @@ async function runGoAudit(directory) {
57444
58082
  stderr: "pipe",
57445
58083
  cwd: directory
57446
58084
  });
57447
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
58085
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57448
58086
  const result = await Promise.race([
57449
58087
  Promise.all([
57450
58088
  new Response(proc.stdout).text(),
@@ -57580,7 +58218,7 @@ async function runDotnetAudit(directory) {
57580
58218
  stderr: "pipe",
57581
58219
  cwd: directory
57582
58220
  });
57583
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
58221
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57584
58222
  const result = await Promise.race([
57585
58223
  Promise.all([
57586
58224
  new Response(proc.stdout).text(),
@@ -57699,7 +58337,7 @@ async function runBundleAudit(directory) {
57699
58337
  stderr: "pipe",
57700
58338
  cwd: directory
57701
58339
  });
57702
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
58340
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57703
58341
  const result = await Promise.race([
57704
58342
  Promise.all([
57705
58343
  new Response(proc.stdout).text(),
@@ -57846,7 +58484,7 @@ async function runDartAudit(directory) {
57846
58484
  stderr: "pipe",
57847
58485
  cwd: directory
57848
58486
  });
57849
- const timeoutPromise = new Promise((resolve15) => setTimeout(() => resolve15("timeout"), AUDIT_TIMEOUT_MS));
58487
+ const timeoutPromise = new Promise((resolve16) => setTimeout(() => resolve16("timeout"), AUDIT_TIMEOUT_MS));
57850
58488
  const result = await Promise.race([
57851
58489
  Promise.all([
57852
58490
  new Response(proc.stdout).text(),
@@ -58113,7 +58751,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
58113
58751
  // src/tools/pre-check-batch.ts
58114
58752
  init_dist();
58115
58753
  import * as fs35 from "fs";
58116
- import * as path48 from "path";
58754
+ import * as path49 from "path";
58117
58755
 
58118
58756
  // node_modules/yocto-queue/index.js
58119
58757
  class Node2 {
@@ -58204,26 +58842,26 @@ function pLimit(concurrency) {
58204
58842
  activeCount--;
58205
58843
  resumeNext();
58206
58844
  };
58207
- const run2 = async (function_, resolve15, arguments_2) => {
58845
+ const run2 = async (function_, resolve16, arguments_2) => {
58208
58846
  const result = (async () => function_(...arguments_2))();
58209
- resolve15(result);
58847
+ resolve16(result);
58210
58848
  try {
58211
58849
  await result;
58212
58850
  } catch {}
58213
58851
  next();
58214
58852
  };
58215
- const enqueue = (function_, resolve15, reject, arguments_2) => {
58853
+ const enqueue = (function_, resolve16, reject, arguments_2) => {
58216
58854
  const queueItem = { reject };
58217
58855
  new Promise((internalResolve) => {
58218
58856
  queueItem.run = internalResolve;
58219
58857
  queue.enqueue(queueItem);
58220
- }).then(run2.bind(undefined, function_, resolve15, arguments_2));
58858
+ }).then(run2.bind(undefined, function_, resolve16, arguments_2));
58221
58859
  if (activeCount < concurrency) {
58222
58860
  resumeNext();
58223
58861
  }
58224
58862
  };
58225
- const generator = (function_, ...arguments_2) => new Promise((resolve15, reject) => {
58226
- enqueue(function_, resolve15, reject, arguments_2);
58863
+ const generator = (function_, ...arguments_2) => new Promise((resolve16, reject) => {
58864
+ enqueue(function_, resolve16, reject, arguments_2);
58227
58865
  });
58228
58866
  Object.defineProperties(generator, {
58229
58867
  activeCount: {
@@ -58281,7 +58919,7 @@ init_manager();
58281
58919
 
58282
58920
  // src/quality/metrics.ts
58283
58921
  import * as fs33 from "fs";
58284
- import * as path46 from "path";
58922
+ import * as path47 from "path";
58285
58923
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
58286
58924
  var MIN_DUPLICATION_LINES = 10;
58287
58925
  function estimateCyclomaticComplexity(content) {
@@ -58333,7 +58971,7 @@ async function computeComplexityDelta(files, workingDir) {
58333
58971
  let totalComplexity = 0;
58334
58972
  const analyzedFiles = [];
58335
58973
  for (const file3 of files) {
58336
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58974
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58337
58975
  if (!fs33.existsSync(fullPath)) {
58338
58976
  continue;
58339
58977
  }
@@ -58456,7 +59094,7 @@ function countGoExports(content) {
58456
59094
  function getExportCountForFile(filePath) {
58457
59095
  try {
58458
59096
  const content = fs33.readFileSync(filePath, "utf-8");
58459
- const ext = path46.extname(filePath).toLowerCase();
59097
+ const ext = path47.extname(filePath).toLowerCase();
58460
59098
  switch (ext) {
58461
59099
  case ".ts":
58462
59100
  case ".tsx":
@@ -58482,7 +59120,7 @@ async function computePublicApiDelta(files, workingDir) {
58482
59120
  let totalExports = 0;
58483
59121
  const analyzedFiles = [];
58484
59122
  for (const file3 of files) {
58485
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
59123
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58486
59124
  if (!fs33.existsSync(fullPath)) {
58487
59125
  continue;
58488
59126
  }
@@ -58516,7 +59154,7 @@ async function computeDuplicationRatio(files, workingDir) {
58516
59154
  let duplicateLines = 0;
58517
59155
  const analyzedFiles = [];
58518
59156
  for (const file3 of files) {
58519
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
59157
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58520
59158
  if (!fs33.existsSync(fullPath)) {
58521
59159
  continue;
58522
59160
  }
@@ -58549,8 +59187,8 @@ function countCodeLines(content) {
58549
59187
  return lines.length;
58550
59188
  }
58551
59189
  function isTestFile(filePath) {
58552
- const basename8 = path46.basename(filePath);
58553
- const _ext = path46.extname(filePath).toLowerCase();
59190
+ const basename8 = path47.basename(filePath);
59191
+ const _ext = path47.extname(filePath).toLowerCase();
58554
59192
  const testPatterns = [
58555
59193
  ".test.",
58556
59194
  ".spec.",
@@ -58631,8 +59269,8 @@ function matchGlobSegment(globSegments, pathSegments) {
58631
59269
  }
58632
59270
  return gIndex === globSegments.length && pIndex === pathSegments.length;
58633
59271
  }
58634
- function matchesGlobSegment(path47, glob) {
58635
- const normalizedPath = path47.replace(/\\/g, "/");
59272
+ function matchesGlobSegment(path48, glob) {
59273
+ const normalizedPath = path48.replace(/\\/g, "/");
58636
59274
  const normalizedGlob = glob.replace(/\\/g, "/");
58637
59275
  if (normalizedPath.includes("//")) {
58638
59276
  return false;
@@ -58663,8 +59301,8 @@ function simpleGlobToRegex2(glob) {
58663
59301
  function hasGlobstar(glob) {
58664
59302
  return glob.includes("**");
58665
59303
  }
58666
- function globMatches(path47, glob) {
58667
- const normalizedPath = path47.replace(/\\/g, "/");
59304
+ function globMatches(path48, glob) {
59305
+ const normalizedPath = path48.replace(/\\/g, "/");
58668
59306
  if (!glob || glob === "") {
58669
59307
  if (normalizedPath.includes("//")) {
58670
59308
  return false;
@@ -58700,7 +59338,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
58700
59338
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58701
59339
  let testLines = 0;
58702
59340
  let codeLines = 0;
58703
- const srcDir = path46.join(workingDir, "src");
59341
+ const srcDir = path47.join(workingDir, "src");
58704
59342
  if (fs33.existsSync(srcDir)) {
58705
59343
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
58706
59344
  codeLines += lines;
@@ -58708,14 +59346,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58708
59346
  }
58709
59347
  const possibleSrcDirs = ["lib", "app", "source", "core"];
58710
59348
  for (const dir of possibleSrcDirs) {
58711
- const dirPath = path46.join(workingDir, dir);
59349
+ const dirPath = path47.join(workingDir, dir);
58712
59350
  if (fs33.existsSync(dirPath)) {
58713
59351
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
58714
59352
  codeLines += lines;
58715
59353
  });
58716
59354
  }
58717
59355
  }
58718
- const testsDir = path46.join(workingDir, "tests");
59356
+ const testsDir = path47.join(workingDir, "tests");
58719
59357
  if (fs33.existsSync(testsDir)) {
58720
59358
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
58721
59359
  testLines += lines;
@@ -58723,7 +59361,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58723
59361
  }
58724
59362
  const possibleTestDirs = ["test", "__tests__", "specs"];
58725
59363
  for (const dir of possibleTestDirs) {
58726
- const dirPath = path46.join(workingDir, dir);
59364
+ const dirPath = path47.join(workingDir, dir);
58727
59365
  if (fs33.existsSync(dirPath) && dirPath !== testsDir) {
58728
59366
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
58729
59367
  testLines += lines;
@@ -58738,15 +59376,15 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58738
59376
  try {
58739
59377
  const entries = fs33.readdirSync(dirPath, { withFileTypes: true });
58740
59378
  for (const entry of entries) {
58741
- const fullPath = path46.join(dirPath, entry.name);
59379
+ const fullPath = path47.join(dirPath, entry.name);
58742
59380
  if (entry.isDirectory()) {
58743
59381
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
58744
59382
  continue;
58745
59383
  }
58746
59384
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
58747
59385
  } else if (entry.isFile()) {
58748
- const relativePath = fullPath.replace(`${process.cwd()}/`, "");
58749
- const ext = path46.extname(entry.name).toLowerCase();
59386
+ const relativePath = fullPath.replace(`${dirPath}/`, "");
59387
+ const ext = path47.extname(entry.name).toLowerCase();
58750
59388
  const validExts = [
58751
59389
  ".ts",
58752
59390
  ".tsx",
@@ -58997,7 +59635,7 @@ init_dist();
58997
59635
  init_manager();
58998
59636
  init_detector();
58999
59637
  import * as fs34 from "fs";
59000
- import * as path47 from "path";
59638
+ import * as path48 from "path";
59001
59639
  import { extname as extname9 } from "path";
59002
59640
 
59003
59641
  // src/sast/rules/c.ts
@@ -59745,7 +60383,7 @@ function mapSemgrepSeverity(severity) {
59745
60383
  }
59746
60384
  }
59747
60385
  async function executeWithTimeout(command, args2, options) {
59748
- return new Promise((resolve15) => {
60386
+ return new Promise((resolve16) => {
59749
60387
  const child = spawn2(command, args2, {
59750
60388
  shell: false,
59751
60389
  cwd: options.cwd
@@ -59754,7 +60392,7 @@ async function executeWithTimeout(command, args2, options) {
59754
60392
  let stderr = "";
59755
60393
  const timeout = setTimeout(() => {
59756
60394
  child.kill("SIGTERM");
59757
- resolve15({
60395
+ resolve16({
59758
60396
  stdout,
59759
60397
  stderr: "Process timed out",
59760
60398
  exitCode: 124
@@ -59768,7 +60406,7 @@ async function executeWithTimeout(command, args2, options) {
59768
60406
  });
59769
60407
  child.on("close", (code) => {
59770
60408
  clearTimeout(timeout);
59771
- resolve15({
60409
+ resolve16({
59772
60410
  stdout,
59773
60411
  stderr,
59774
60412
  exitCode: code ?? 0
@@ -59776,7 +60414,7 @@ async function executeWithTimeout(command, args2, options) {
59776
60414
  });
59777
60415
  child.on("error", (err2) => {
59778
60416
  clearTimeout(timeout);
59779
- resolve15({
60417
+ resolve16({
59780
60418
  stdout,
59781
60419
  stderr: err2.message,
59782
60420
  exitCode: 1
@@ -59960,7 +60598,7 @@ async function sastScan(input, directory, config3) {
59960
60598
  _filesSkipped++;
59961
60599
  continue;
59962
60600
  }
59963
- const resolvedPath = path47.isAbsolute(filePath) ? filePath : path47.resolve(directory, filePath);
60601
+ const resolvedPath = path48.isAbsolute(filePath) ? filePath : path48.resolve(directory, filePath);
59964
60602
  if (!fs34.existsSync(resolvedPath)) {
59965
60603
  _filesSkipped++;
59966
60604
  continue;
@@ -60159,20 +60797,20 @@ function validatePath(inputPath, baseDir, workspaceDir) {
60159
60797
  let resolved;
60160
60798
  const isWinAbs = isWindowsAbsolutePath(inputPath);
60161
60799
  if (isWinAbs) {
60162
- resolved = path48.win32.resolve(inputPath);
60163
- } else if (path48.isAbsolute(inputPath)) {
60164
- resolved = path48.resolve(inputPath);
60800
+ resolved = path49.win32.resolve(inputPath);
60801
+ } else if (path49.isAbsolute(inputPath)) {
60802
+ resolved = path49.resolve(inputPath);
60165
60803
  } else {
60166
- resolved = path48.resolve(baseDir, inputPath);
60804
+ resolved = path49.resolve(baseDir, inputPath);
60167
60805
  }
60168
- const workspaceResolved = path48.resolve(workspaceDir);
60169
- let relative5;
60806
+ const workspaceResolved = path49.resolve(workspaceDir);
60807
+ let relative6;
60170
60808
  if (isWinAbs) {
60171
- relative5 = path48.win32.relative(workspaceResolved, resolved);
60809
+ relative6 = path49.win32.relative(workspaceResolved, resolved);
60172
60810
  } else {
60173
- relative5 = path48.relative(workspaceResolved, resolved);
60811
+ relative6 = path49.relative(workspaceResolved, resolved);
60174
60812
  }
60175
- if (relative5.startsWith("..")) {
60813
+ if (relative6.startsWith("..")) {
60176
60814
  return "path traversal detected";
60177
60815
  }
60178
60816
  return null;
@@ -60231,13 +60869,13 @@ async function runLintWrapped(files, directory, _config) {
60231
60869
  }
60232
60870
  async function runLintOnFiles(linter, files, workspaceDir) {
60233
60871
  const isWindows = process.platform === "win32";
60234
- const binDir = path48.join(workspaceDir, "node_modules", ".bin");
60872
+ const binDir = path49.join(workspaceDir, "node_modules", ".bin");
60235
60873
  const validatedFiles = [];
60236
60874
  for (const file3 of files) {
60237
60875
  if (typeof file3 !== "string") {
60238
60876
  continue;
60239
60877
  }
60240
- const resolvedPath = path48.resolve(file3);
60878
+ const resolvedPath = path49.resolve(file3);
60241
60879
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
60242
60880
  if (validationError) {
60243
60881
  continue;
@@ -60255,10 +60893,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
60255
60893
  }
60256
60894
  let command;
60257
60895
  if (linter === "biome") {
60258
- const biomeBin = isWindows ? path48.join(binDir, "biome.EXE") : path48.join(binDir, "biome");
60896
+ const biomeBin = isWindows ? path49.join(binDir, "biome.EXE") : path49.join(binDir, "biome");
60259
60897
  command = [biomeBin, "check", ...validatedFiles];
60260
60898
  } else {
60261
- const eslintBin = isWindows ? path48.join(binDir, "eslint.cmd") : path48.join(binDir, "eslint");
60899
+ const eslintBin = isWindows ? path49.join(binDir, "eslint.cmd") : path49.join(binDir, "eslint");
60262
60900
  command = [eslintBin, ...validatedFiles];
60263
60901
  }
60264
60902
  try {
@@ -60395,7 +61033,7 @@ async function runSecretscanWithFiles(files, directory) {
60395
61033
  skippedFiles++;
60396
61034
  continue;
60397
61035
  }
60398
- const resolvedPath = path48.resolve(file3);
61036
+ const resolvedPath = path49.resolve(file3);
60399
61037
  const validationError = validatePath(resolvedPath, directory, directory);
60400
61038
  if (validationError) {
60401
61039
  skippedFiles++;
@@ -60413,7 +61051,7 @@ async function runSecretscanWithFiles(files, directory) {
60413
61051
  };
60414
61052
  }
60415
61053
  for (const file3 of validatedFiles) {
60416
- const ext = path48.extname(file3).toLowerCase();
61054
+ const ext = path49.extname(file3).toLowerCase();
60417
61055
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
60418
61056
  skippedFiles++;
60419
61057
  continue;
@@ -60531,8 +61169,8 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
60531
61169
  };
60532
61170
  }
60533
61171
  }
60534
- async function runPreCheckBatch(input, workspaceDir) {
60535
- const effectiveWorkspaceDir = workspaceDir || input.directory || process.cwd();
61172
+ async function runPreCheckBatch(input, workspaceDir, contextDir) {
61173
+ const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
60536
61174
  const { files, directory, sast_threshold = "medium", config: config3 } = input;
60537
61175
  const dirError = validateDirectory3(directory, effectiveWorkspaceDir);
60538
61176
  if (dirError) {
@@ -60572,7 +61210,7 @@ async function runPreCheckBatch(input, workspaceDir) {
60572
61210
  warn(`pre_check_batch: Invalid file path: ${file3}`);
60573
61211
  continue;
60574
61212
  }
60575
- changedFiles.push(path48.resolve(directory, file3));
61213
+ changedFiles.push(path49.resolve(directory, file3));
60576
61214
  }
60577
61215
  if (changedFiles.length === 0) {
60578
61216
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -60723,7 +61361,7 @@ var pre_check_batch = createSwarmTool({
60723
61361
  };
60724
61362
  return JSON.stringify(errorResult, null, 2);
60725
61363
  }
60726
- const resolvedDirectory = path48.resolve(typedArgs.directory);
61364
+ const resolvedDirectory = path49.resolve(typedArgs.directory);
60727
61365
  const workspaceAnchor = resolvedDirectory;
60728
61366
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
60729
61367
  if (dirError) {
@@ -60743,7 +61381,7 @@ var pre_check_batch = createSwarmTool({
60743
61381
  directory: resolvedDirectory,
60744
61382
  sast_threshold: typedArgs.sast_threshold,
60745
61383
  config: typedArgs.config
60746
- }, workspaceAnchor);
61384
+ }, workspaceAnchor, directory);
60747
61385
  return JSON.stringify(result, null, 2);
60748
61386
  } catch (error93) {
60749
61387
  const errorMessage = error93 instanceof Error ? error93.message : "Unknown error";
@@ -60761,21 +61399,22 @@ var pre_check_batch = createSwarmTool({
60761
61399
  });
60762
61400
  // src/tools/retrieve-summary.ts
60763
61401
  init_dist();
61402
+ init_create_tool();
60764
61403
  var RETRIEVE_MAX_BYTES = 10 * 1024 * 1024;
60765
- var retrieve_summary = tool({
61404
+ var retrieve_summary = createSwarmTool({
60766
61405
  description: "Retrieve the full content of a stored tool output summary by its ID (e.g. S1, S2). Use this when a prior tool output was summarized and you need the full content.",
60767
61406
  args: {
60768
61407
  id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits."),
60769
61408
  offset: tool.schema.number().min(0).default(0).describe("Line offset to start from (default: 0)."),
60770
61409
  limit: tool.schema.number().min(1).max(500).default(100).describe("Number of lines to return (default: 100, max: 500).")
60771
61410
  },
60772
- async execute(args2, context) {
60773
- const directory = context.directory;
60774
- const offset = args2.offset ?? 0;
60775
- const limit = Math.min(args2.limit ?? 100, 500);
61411
+ async execute(args2, directory, _ctx) {
61412
+ const typedArgs = args2;
61413
+ const offset = typedArgs.offset ?? 0;
61414
+ const limit = Math.min(typedArgs.limit ?? 100, 500);
60776
61415
  let sanitizedId;
60777
61416
  try {
60778
- sanitizedId = sanitizeSummaryId(args2.id);
61417
+ sanitizedId = sanitizeSummaryId(typedArgs.id);
60779
61418
  } catch {
60780
61419
  return "Error: invalid summary ID format. Expected format: S followed by digits (e.g. S1, S2, S99).";
60781
61420
  }
@@ -60831,7 +61470,7 @@ init_tool();
60831
61470
  init_manager2();
60832
61471
  init_create_tool();
60833
61472
  import * as fs36 from "fs";
60834
- import * as path49 from "path";
61473
+ import * as path50 from "path";
60835
61474
  function detectPlaceholderContent(args2) {
60836
61475
  const issues = [];
60837
61476
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -60935,7 +61574,7 @@ async function executeSavePlan(args2, fallbackDir) {
60935
61574
  try {
60936
61575
  await savePlan(dir, plan);
60937
61576
  try {
60938
- const markerPath = path49.join(dir, ".swarm", ".plan-write-marker");
61577
+ const markerPath = path50.join(dir, ".swarm", ".plan-write-marker");
60939
61578
  const marker = JSON.stringify({
60940
61579
  source: "save_plan",
60941
61580
  timestamp: new Date().toISOString(),
@@ -60947,7 +61586,7 @@ async function executeSavePlan(args2, fallbackDir) {
60947
61586
  return {
60948
61587
  success: true,
60949
61588
  message: "Plan saved successfully",
60950
- plan_path: path49.join(dir, ".swarm", "plan.json"),
61589
+ plan_path: path50.join(dir, ".swarm", "plan.json"),
60951
61590
  phases_count: plan.phases.length,
60952
61591
  tasks_count: tasksCount
60953
61592
  };
@@ -60986,7 +61625,7 @@ var save_plan = createSwarmTool({
60986
61625
  init_dist();
60987
61626
  init_manager();
60988
61627
  import * as fs37 from "fs";
60989
- import * as path50 from "path";
61628
+ import * as path51 from "path";
60990
61629
 
60991
61630
  // src/sbom/detectors/index.ts
60992
61631
  init_utils();
@@ -61834,7 +62473,7 @@ function findManifestFiles(rootDir) {
61834
62473
  try {
61835
62474
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61836
62475
  for (const entry of entries) {
61837
- const fullPath = path50.join(dir, entry.name);
62476
+ const fullPath = path51.join(dir, entry.name);
61838
62477
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
61839
62478
  continue;
61840
62479
  }
@@ -61843,7 +62482,7 @@ function findManifestFiles(rootDir) {
61843
62482
  } else if (entry.isFile()) {
61844
62483
  for (const pattern of patterns) {
61845
62484
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61846
- manifestFiles.push(path50.relative(rootDir, fullPath));
62485
+ manifestFiles.push(path51.relative(rootDir, fullPath));
61847
62486
  break;
61848
62487
  }
61849
62488
  }
@@ -61861,11 +62500,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61861
62500
  try {
61862
62501
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61863
62502
  for (const entry of entries) {
61864
- const fullPath = path50.join(dir, entry.name);
62503
+ const fullPath = path51.join(dir, entry.name);
61865
62504
  if (entry.isFile()) {
61866
62505
  for (const pattern of patterns) {
61867
62506
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61868
- found.push(path50.relative(workingDir, fullPath));
62507
+ found.push(path51.relative(workingDir, fullPath));
61869
62508
  break;
61870
62509
  }
61871
62510
  }
@@ -61878,11 +62517,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61878
62517
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
61879
62518
  const dirs = new Set;
61880
62519
  for (const file3 of changedFiles) {
61881
- let currentDir = path50.dirname(file3);
62520
+ let currentDir = path51.dirname(file3);
61882
62521
  while (true) {
61883
- if (currentDir && currentDir !== "." && currentDir !== path50.sep) {
61884
- dirs.add(path50.join(workingDir, currentDir));
61885
- const parent = path50.dirname(currentDir);
62522
+ if (currentDir && currentDir !== "." && currentDir !== path51.sep) {
62523
+ dirs.add(path51.join(workingDir, currentDir));
62524
+ const parent = path51.dirname(currentDir);
61886
62525
  if (parent === currentDir)
61887
62526
  break;
61888
62527
  currentDir = parent;
@@ -61966,7 +62605,7 @@ var sbom_generate = createSwarmTool({
61966
62605
  const changedFiles = obj.changed_files;
61967
62606
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
61968
62607
  const workingDir = directory;
61969
- const outputDir = path50.isAbsolute(relativeOutputDir) ? relativeOutputDir : path50.join(workingDir, relativeOutputDir);
62608
+ const outputDir = path51.isAbsolute(relativeOutputDir) ? relativeOutputDir : path51.join(workingDir, relativeOutputDir);
61970
62609
  let manifestFiles = [];
61971
62610
  if (scope === "all") {
61972
62611
  manifestFiles = findManifestFiles(workingDir);
@@ -61989,7 +62628,7 @@ var sbom_generate = createSwarmTool({
61989
62628
  const processedFiles = [];
61990
62629
  for (const manifestFile of manifestFiles) {
61991
62630
  try {
61992
- const fullPath = path50.isAbsolute(manifestFile) ? manifestFile : path50.join(workingDir, manifestFile);
62631
+ const fullPath = path51.isAbsolute(manifestFile) ? manifestFile : path51.join(workingDir, manifestFile);
61993
62632
  if (!fs37.existsSync(fullPath)) {
61994
62633
  continue;
61995
62634
  }
@@ -62006,7 +62645,7 @@ var sbom_generate = createSwarmTool({
62006
62645
  const bom = generateCycloneDX(allComponents);
62007
62646
  const bomJson = serializeCycloneDX(bom);
62008
62647
  const filename = generateSbomFilename();
62009
- const outputPath = path50.join(outputDir, filename);
62648
+ const outputPath = path51.join(outputDir, filename);
62010
62649
  fs37.writeFileSync(outputPath, bomJson, "utf-8");
62011
62650
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
62012
62651
  try {
@@ -62050,7 +62689,7 @@ var sbom_generate = createSwarmTool({
62050
62689
  init_dist();
62051
62690
  init_create_tool();
62052
62691
  import * as fs38 from "fs";
62053
- import * as path51 from "path";
62692
+ import * as path52 from "path";
62054
62693
  var SPEC_CANDIDATES = [
62055
62694
  "openapi.json",
62056
62695
  "openapi.yaml",
@@ -62082,12 +62721,12 @@ function normalizePath2(p) {
62082
62721
  }
62083
62722
  function discoverSpecFile(cwd, specFileArg) {
62084
62723
  if (specFileArg) {
62085
- const resolvedPath = path51.resolve(cwd, specFileArg);
62086
- const normalizedCwd = cwd.endsWith(path51.sep) ? cwd : cwd + path51.sep;
62724
+ const resolvedPath = path52.resolve(cwd, specFileArg);
62725
+ const normalizedCwd = cwd.endsWith(path52.sep) ? cwd : cwd + path52.sep;
62087
62726
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
62088
62727
  throw new Error("Invalid spec_file: path traversal detected");
62089
62728
  }
62090
- const ext = path51.extname(resolvedPath).toLowerCase();
62729
+ const ext = path52.extname(resolvedPath).toLowerCase();
62091
62730
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
62092
62731
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
62093
62732
  }
@@ -62101,7 +62740,7 @@ function discoverSpecFile(cwd, specFileArg) {
62101
62740
  return resolvedPath;
62102
62741
  }
62103
62742
  for (const candidate of SPEC_CANDIDATES) {
62104
- const candidatePath = path51.resolve(cwd, candidate);
62743
+ const candidatePath = path52.resolve(cwd, candidate);
62105
62744
  if (fs38.existsSync(candidatePath)) {
62106
62745
  const stats = fs38.statSync(candidatePath);
62107
62746
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -62113,7 +62752,7 @@ function discoverSpecFile(cwd, specFileArg) {
62113
62752
  }
62114
62753
  function parseSpec(specFile) {
62115
62754
  const content = fs38.readFileSync(specFile, "utf-8");
62116
- const ext = path51.extname(specFile).toLowerCase();
62755
+ const ext = path52.extname(specFile).toLowerCase();
62117
62756
  if (ext === ".json") {
62118
62757
  return parseJsonSpec(content);
62119
62758
  }
@@ -62189,7 +62828,7 @@ function extractRoutes(cwd) {
62189
62828
  return;
62190
62829
  }
62191
62830
  for (const entry of entries) {
62192
- const fullPath = path51.join(dir, entry.name);
62831
+ const fullPath = path52.join(dir, entry.name);
62193
62832
  if (entry.isSymbolicLink()) {
62194
62833
  continue;
62195
62834
  }
@@ -62199,7 +62838,7 @@ function extractRoutes(cwd) {
62199
62838
  }
62200
62839
  walkDir(fullPath);
62201
62840
  } else if (entry.isFile()) {
62202
- const ext = path51.extname(entry.name).toLowerCase();
62841
+ const ext = path52.extname(entry.name).toLowerCase();
62203
62842
  const baseName = entry.name.toLowerCase();
62204
62843
  if (![".ts", ".js", ".mjs"].includes(ext)) {
62205
62844
  continue;
@@ -62369,7 +63008,7 @@ init_secretscan();
62369
63008
  init_tool();
62370
63009
  init_create_tool();
62371
63010
  import * as fs39 from "fs";
62372
- import * as path52 from "path";
63011
+ import * as path53 from "path";
62373
63012
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
62374
63013
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
62375
63014
  function containsControlCharacters(str) {
@@ -62398,11 +63037,11 @@ function containsWindowsAttacks(str) {
62398
63037
  }
62399
63038
  function isPathInWorkspace(filePath, workspace) {
62400
63039
  try {
62401
- const resolvedPath = path52.resolve(workspace, filePath);
63040
+ const resolvedPath = path53.resolve(workspace, filePath);
62402
63041
  const realWorkspace = fs39.realpathSync(workspace);
62403
63042
  const realResolvedPath = fs39.realpathSync(resolvedPath);
62404
- const relativePath = path52.relative(realWorkspace, realResolvedPath);
62405
- if (relativePath.startsWith("..") || path52.isAbsolute(relativePath)) {
63043
+ const relativePath = path53.relative(realWorkspace, realResolvedPath);
63044
+ if (relativePath.startsWith("..") || path53.isAbsolute(relativePath)) {
62406
63045
  return false;
62407
63046
  }
62408
63047
  return true;
@@ -62414,7 +63053,7 @@ function validatePathForRead(filePath, workspace) {
62414
63053
  return isPathInWorkspace(filePath, workspace);
62415
63054
  }
62416
63055
  function extractTSSymbols(filePath, cwd) {
62417
- const fullPath = path52.join(cwd, filePath);
63056
+ const fullPath = path53.join(cwd, filePath);
62418
63057
  if (!validatePathForRead(fullPath, cwd)) {
62419
63058
  return [];
62420
63059
  }
@@ -62566,7 +63205,7 @@ function extractTSSymbols(filePath, cwd) {
62566
63205
  });
62567
63206
  }
62568
63207
  function extractPythonSymbols(filePath, cwd) {
62569
- const fullPath = path52.join(cwd, filePath);
63208
+ const fullPath = path53.join(cwd, filePath);
62570
63209
  if (!validatePathForRead(fullPath, cwd)) {
62571
63210
  return [];
62572
63211
  }
@@ -62649,7 +63288,7 @@ var symbols = createSwarmTool({
62649
63288
  }, null, 2);
62650
63289
  }
62651
63290
  const cwd = directory;
62652
- const ext = path52.extname(file3);
63291
+ const ext = path53.extname(file3);
62653
63292
  if (containsControlCharacters(file3)) {
62654
63293
  return JSON.stringify({
62655
63294
  file: file3,
@@ -62721,7 +63360,7 @@ init_dist();
62721
63360
  init_utils();
62722
63361
  init_create_tool();
62723
63362
  import * as fs40 from "fs";
62724
- import * as path53 from "path";
63363
+ import * as path54 from "path";
62725
63364
  var MAX_TEXT_LENGTH = 200;
62726
63365
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
62727
63366
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -62792,9 +63431,9 @@ function validatePathsInput(paths, cwd) {
62792
63431
  return { error: "paths contains path traversal", resolvedPath: null };
62793
63432
  }
62794
63433
  try {
62795
- const resolvedPath = path53.resolve(paths);
62796
- const normalizedCwd = path53.resolve(cwd);
62797
- const normalizedResolved = path53.resolve(resolvedPath);
63434
+ const resolvedPath = path54.resolve(paths);
63435
+ const normalizedCwd = path54.resolve(cwd);
63436
+ const normalizedResolved = path54.resolve(resolvedPath);
62798
63437
  if (!normalizedResolved.startsWith(normalizedCwd)) {
62799
63438
  return {
62800
63439
  error: "paths must be within the current working directory",
@@ -62810,7 +63449,7 @@ function validatePathsInput(paths, cwd) {
62810
63449
  }
62811
63450
  }
62812
63451
  function isSupportedExtension(filePath) {
62813
- const ext = path53.extname(filePath).toLowerCase();
63452
+ const ext = path54.extname(filePath).toLowerCase();
62814
63453
  return SUPPORTED_EXTENSIONS2.has(ext);
62815
63454
  }
62816
63455
  function findSourceFiles2(dir, files = []) {
@@ -62825,7 +63464,7 @@ function findSourceFiles2(dir, files = []) {
62825
63464
  if (SKIP_DIRECTORIES3.has(entry)) {
62826
63465
  continue;
62827
63466
  }
62828
- const fullPath = path53.join(dir, entry);
63467
+ const fullPath = path54.join(dir, entry);
62829
63468
  let stat2;
62830
63469
  try {
62831
63470
  stat2 = fs40.statSync(fullPath);
@@ -62937,7 +63576,7 @@ var todo_extract = createSwarmTool({
62937
63576
  filesToScan.push(scanPath);
62938
63577
  } else {
62939
63578
  const errorResult = {
62940
- error: `unsupported file extension: ${path53.extname(scanPath)}`,
63579
+ error: `unsupported file extension: ${path54.extname(scanPath)}`,
62941
63580
  total: 0,
62942
63581
  byPriority: { high: 0, medium: 0, low: 0 },
62943
63582
  entries: []
@@ -62983,14 +63622,14 @@ var todo_extract = createSwarmTool({
62983
63622
  init_tool();
62984
63623
  init_schema();
62985
63624
  import * as fs42 from "fs";
62986
- import * as path55 from "path";
63625
+ import * as path56 from "path";
62987
63626
 
62988
63627
  // src/hooks/diff-scope.ts
62989
63628
  import * as fs41 from "fs";
62990
- import * as path54 from "path";
63629
+ import * as path55 from "path";
62991
63630
  function getDeclaredScope(taskId, directory) {
62992
63631
  try {
62993
- const planPath = path54.join(directory, ".swarm", "plan.json");
63632
+ const planPath = path55.join(directory, ".swarm", "plan.json");
62994
63633
  if (!fs41.existsSync(planPath))
62995
63634
  return null;
62996
63635
  const raw = fs41.readFileSync(planPath, "utf-8");
@@ -63105,7 +63744,7 @@ var TIER_3_PATTERNS = [
63105
63744
  ];
63106
63745
  function matchesTier3Pattern(files) {
63107
63746
  for (const file3 of files) {
63108
- const fileName = path55.basename(file3);
63747
+ const fileName = path56.basename(file3);
63109
63748
  for (const pattern of TIER_3_PATTERNS) {
63110
63749
  if (pattern.test(fileName)) {
63111
63750
  return true;
@@ -63127,7 +63766,7 @@ function checkReviewerGate(taskId, workingDirectory) {
63127
63766
  if (hasActiveTurboMode2()) {
63128
63767
  const resolvedDir2 = workingDirectory ?? process.cwd();
63129
63768
  try {
63130
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
63769
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
63131
63770
  const planRaw = fs42.readFileSync(planPath, "utf-8");
63132
63771
  const plan = JSON.parse(planRaw);
63133
63772
  for (const planPhase of plan.phases ?? []) {
@@ -63147,7 +63786,7 @@ function checkReviewerGate(taskId, workingDirectory) {
63147
63786
  }
63148
63787
  const resolvedDir = workingDirectory ?? process.cwd();
63149
63788
  try {
63150
- const evidencePath = path55.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
63789
+ const evidencePath = path56.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
63151
63790
  const raw = fs42.readFileSync(evidencePath, "utf-8");
63152
63791
  const evidence = JSON.parse(raw);
63153
63792
  if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
@@ -63188,7 +63827,7 @@ function checkReviewerGate(taskId, workingDirectory) {
63188
63827
  }
63189
63828
  try {
63190
63829
  const resolvedDir2 = workingDirectory ?? process.cwd();
63191
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
63830
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
63192
63831
  const planRaw = fs42.readFileSync(planPath, "utf-8");
63193
63832
  const plan = JSON.parse(planRaw);
63194
63833
  for (const planPhase of plan.phases ?? []) {
@@ -63370,8 +64009,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63370
64009
  };
63371
64010
  }
63372
64011
  }
63373
- normalizedDir = path55.normalize(args2.working_directory);
63374
- const pathParts = normalizedDir.split(path55.sep);
64012
+ normalizedDir = path56.normalize(args2.working_directory);
64013
+ const pathParts = normalizedDir.split(path56.sep);
63375
64014
  if (pathParts.includes("..")) {
63376
64015
  return {
63377
64016
  success: false,
@@ -63381,10 +64020,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63381
64020
  ]
63382
64021
  };
63383
64022
  }
63384
- const resolvedDir = path55.resolve(normalizedDir);
64023
+ const resolvedDir = path56.resolve(normalizedDir);
63385
64024
  try {
63386
64025
  const realPath = fs42.realpathSync(resolvedDir);
63387
- const planPath = path55.join(realPath, ".swarm", "plan.json");
64026
+ const planPath = path56.join(realPath, ".swarm", "plan.json");
63388
64027
  if (!fs42.existsSync(planPath)) {
63389
64028
  return {
63390
64029
  success: false,
@@ -63405,7 +64044,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63405
64044
  };
63406
64045
  }
63407
64046
  } else {
63408
- directory = fallbackDir ?? process.cwd();
64047
+ if (!fallbackDir) {
64048
+ console.warn("[update-task-status] fallbackDir is undefined, falling back to process.cwd()");
64049
+ }
64050
+ directory = fallbackDir || process.cwd();
63409
64051
  }
63410
64052
  if (args2.status === "completed") {
63411
64053
  recoverTaskStateFromDelegations(args2.task_id);
@@ -63523,6 +64165,24 @@ var OpenCodeSwarm = async (ctx) => {
63523
64165
  }
63524
64166
  const delegationHandler = createDelegationTrackerHook(config3, guardrailsConfig.enabled);
63525
64167
  const guardrailsHooks = createGuardrailsHooks(ctx.directory, guardrailsConfig);
64168
+ const watchdogConfig = WatchdogConfigSchema.parse(config3.watchdog ?? {});
64169
+ const advisoryInjector = (sessionId, message) => {
64170
+ const s = swarmState.agentSessions.get(sessionId);
64171
+ if (s) {
64172
+ s.pendingAdvisoryMessages ??= [];
64173
+ s.pendingAdvisoryMessages.push(message);
64174
+ }
64175
+ };
64176
+ const scopeGuardHook = createScopeGuardHook({
64177
+ enabled: watchdogConfig.scope_guard,
64178
+ skip_in_turbo: watchdogConfig.skip_in_turbo
64179
+ }, ctx.directory, advisoryInjector);
64180
+ const delegationLedgerHook = createDelegationLedgerHook({ enabled: watchdogConfig.delegation_ledger }, ctx.directory, advisoryInjector);
64181
+ const selfReviewConfig = SelfReviewConfigSchema.parse(config3.self_review ?? {});
64182
+ const selfReviewHook = createSelfReviewHook({
64183
+ enabled: selfReviewConfig.enabled,
64184
+ skip_in_turbo: selfReviewConfig.skip_in_turbo
64185
+ }, advisoryInjector);
63526
64186
  const summaryConfig = SummaryConfigSchema.parse(config3.summaries ?? {});
63527
64187
  const toolSummarizerHook = createToolSummarizerHook(summaryConfig, ctx.directory);
63528
64188
  const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
@@ -63536,7 +64196,8 @@ var OpenCodeSwarm = async (ctx) => {
63536
64196
  enabled: true,
63537
64197
  classThreshold: 3,
63538
64198
  commentStripThreshold: 5,
63539
- diffLineThreshold: 200
64199
+ diffLineThreshold: 200,
64200
+ importHygieneThreshold: 2
63540
64201
  }, ctx.directory, (sessionId, message) => {
63541
64202
  const s = swarmState.agentSessions.get(sessionId);
63542
64203
  if (s) {
@@ -63579,7 +64240,7 @@ var OpenCodeSwarm = async (ctx) => {
63579
64240
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
63580
64241
  preflightTriggerManager = new PTM(automationConfig);
63581
64242
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
63582
- const swarmDir = path56.resolve(ctx.directory, ".swarm");
64243
+ const swarmDir = path57.resolve(ctx.directory, ".swarm");
63583
64244
  statusArtifact = new ASA(swarmDir);
63584
64245
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
63585
64246
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -63676,6 +64337,10 @@ var OpenCodeSwarm = async (ctx) => {
63676
64337
  check_gate_status,
63677
64338
  checkpoint,
63678
64339
  complexity_hotspots,
64340
+ curator_analyze,
64341
+ knowledgeAdd,
64342
+ knowledgeRecall,
64343
+ knowledgeRemove,
63679
64344
  detect_domains,
63680
64345
  evidence_check,
63681
64346
  extract_code_blocks,
@@ -63805,6 +64470,20 @@ var OpenCodeSwarm = async (ctx) => {
63805
64470
  });
63806
64471
  },
63807
64472
  "experimental.chat.messages.transform": composeHandlers(...[
64473
+ (input, _output) => {
64474
+ const p = input;
64475
+ if (p.sessionID) {
64476
+ const archAgent = swarmState.activeAgent.get(p.sessionID);
64477
+ const archSession = swarmState.agentSessions.get(p.sessionID);
64478
+ const agentName = archAgent ?? archSession?.agentName ?? "";
64479
+ if (stripKnownSwarmPrefix(agentName) === ORCHESTRATOR_NAME) {
64480
+ try {
64481
+ delegationLedgerHook.onArchitectResume(p.sessionID);
64482
+ } catch {}
64483
+ }
64484
+ }
64485
+ return Promise.resolve();
64486
+ },
63808
64487
  pipelineHook["experimental.chat.messages.transform"],
63809
64488
  contextBudgetHandler,
63810
64489
  guardrailsHooks.messagesTransform,
@@ -63841,6 +64520,7 @@ var OpenCodeSwarm = async (ctx) => {
63841
64520
  }
63842
64521
  }
63843
64522
  await guardrailsHooks.toolBefore(input, output);
64523
+ await scopeGuardHook.toolBefore(input, output);
63844
64524
  if (swarmState.lastBudgetPct >= 50) {
63845
64525
  const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
63846
64526
  if (!pressureSession.contextPressureWarningSent) {
@@ -63854,6 +64534,8 @@ var OpenCodeSwarm = async (ctx) => {
63854
64534
  "tool.execute.after": async (input, output) => {
63855
64535
  await activityHooks.toolAfter(input, output);
63856
64536
  await guardrailsHooks.toolAfter(input, output);
64537
+ await safeHook(delegationLedgerHook.toolAfter)(input, output);
64538
+ await safeHook(selfReviewHook.toolAfter)(input, output);
63857
64539
  await safeHook(delegationGateHooks.toolAfter)(input, output);
63858
64540
  if (knowledgeCuratorHook)
63859
64541
  await safeHook(knowledgeCuratorHook)(input, output);