opencode-swarm 6.20.2 → 6.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36285,8 +36285,8 @@ var init_tree_sitter = __esm(() => {
36285
36285
  bytes = Promise.resolve(input);
36286
36286
  } else {
36287
36287
  if (globalThis.process?.versions.node) {
36288
- const fs24 = await import("fs/promises");
36289
- bytes = fs24.readFile(input);
36288
+ const fs25 = await import("fs/promises");
36289
+ bytes = fs25.readFile(input);
36290
36290
  } else {
36291
36291
  bytes = fetch(input).then((response) => response.arrayBuffer().then((buffer) => {
36292
36292
  if (response.ok) {
@@ -36318,8 +36318,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
36318
36318
  var moduleRtn;
36319
36319
  var Module = moduleArg;
36320
36320
  var readyPromiseResolve, readyPromiseReject;
36321
- var readyPromise = new Promise((resolve12, reject) => {
36322
- readyPromiseResolve = resolve12;
36321
+ var readyPromise = new Promise((resolve13, reject) => {
36322
+ readyPromiseResolve = resolve13;
36323
36323
  readyPromiseReject = reject;
36324
36324
  });
36325
36325
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -36341,11 +36341,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
36341
36341
  throw toThrow;
36342
36342
  }, "quit_");
36343
36343
  var scriptDirectory = "";
36344
- function locateFile(path35) {
36344
+ function locateFile(path36) {
36345
36345
  if (Module["locateFile"]) {
36346
- return Module["locateFile"](path35, scriptDirectory);
36346
+ return Module["locateFile"](path36, scriptDirectory);
36347
36347
  }
36348
- return scriptDirectory + path35;
36348
+ return scriptDirectory + path36;
36349
36349
  }
36350
36350
  __name(locateFile, "locateFile");
36351
36351
  var readAsync, readBinary;
@@ -36399,13 +36399,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
36399
36399
  }
36400
36400
  readAsync = /* @__PURE__ */ __name(async (url3) => {
36401
36401
  if (isFileURI(url3)) {
36402
- return new Promise((resolve12, reject) => {
36402
+ return new Promise((resolve13, reject) => {
36403
36403
  var xhr = new XMLHttpRequest;
36404
36404
  xhr.open("GET", url3, true);
36405
36405
  xhr.responseType = "arraybuffer";
36406
36406
  xhr.onload = () => {
36407
36407
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
36408
- resolve12(xhr.response);
36408
+ resolve13(xhr.response);
36409
36409
  return;
36410
36410
  }
36411
36411
  reject(xhr.status);
@@ -36625,10 +36625,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
36625
36625
  __name(receiveInstantiationResult, "receiveInstantiationResult");
36626
36626
  var info2 = getWasmImports();
36627
36627
  if (Module["instantiateWasm"]) {
36628
- return new Promise((resolve12, reject) => {
36628
+ return new Promise((resolve13, reject) => {
36629
36629
  Module["instantiateWasm"](info2, (mod, inst) => {
36630
36630
  receiveInstance(mod, inst);
36631
- resolve12(mod.exports);
36631
+ resolve13(mod.exports);
36632
36632
  });
36633
36633
  });
36634
36634
  }
@@ -38093,7 +38093,7 @@ var init_runtime = __esm(() => {
38093
38093
  });
38094
38094
 
38095
38095
  // src/index.ts
38096
- import * as path44 from "path";
38096
+ import * as path45 from "path";
38097
38097
 
38098
38098
  // src/tools/tool-names.ts
38099
38099
  var TOOL_NAMES = [
@@ -38123,7 +38123,8 @@ var TOOL_NAMES = [
38123
38123
  "phase_complete",
38124
38124
  "save_plan",
38125
38125
  "update_task_status",
38126
- "write_retro"
38126
+ "write_retro",
38127
+ "declare_scope"
38127
38128
  ];
38128
38129
  var TOOL_NAME_SET = new Set(TOOL_NAMES);
38129
38130
 
@@ -38163,7 +38164,8 @@ var AGENT_TOOL_MAP = {
38163
38164
  "test_runner",
38164
38165
  "todo_extract",
38165
38166
  "update_task_status",
38166
- "write_retro"
38167
+ "write_retro",
38168
+ "declare_scope"
38167
38169
  ],
38168
38170
  explorer: [
38169
38171
  "complexity_hotspots",
@@ -38276,6 +38278,13 @@ var DEFAULT_SCORING_CONFIG = {
38276
38278
  json: 0.35
38277
38279
  }
38278
38280
  };
38281
+ var LOW_CAPABILITY_MODELS = ["mini", "nano", "small", "free"];
38282
+ function isLowCapabilityModel(modelId) {
38283
+ if (!modelId)
38284
+ return false;
38285
+ const lower = modelId.toLowerCase();
38286
+ return LOW_CAPABILITY_MODELS.some((substr) => lower.includes(substr));
38287
+ }
38279
38288
 
38280
38289
  // src/config/index.ts
38281
38290
  init_evidence_schema();
@@ -38942,6 +38951,7 @@ CODER'S TOOLS: write, edit, patch, apply_patch, create_file, insert, replace \u2
38942
38951
  If a tool modifies a file, it is a CODER tool. Delegate.
38943
38952
  2. ONE agent per message. Send, STOP, wait for response.
38944
38953
  3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
38954
+ <!-- BEHAVIORAL_GUIDANCE_START -->
38945
38955
  BATCHING DETECTION \u2014 you are batching if your coder delegation contains ANY of:
38946
38956
  - The word "and" connecting two actions ("update X AND add Y")
38947
38957
  - Multiple FILE paths ("FILE: src/a.ts, src/b.ts, src/c.ts")
@@ -38956,6 +38966,8 @@ A failure in one part blocks the entire batch, wasting all the work.
38956
38966
 
38957
38967
  SPLIT RULE: If your delegation draft has "and" in the TASK line, split it.
38958
38968
  Two small delegations with two QA gates > one large delegation with one QA gate.
38969
+ <!-- BEHAVIORAL_GUIDANCE_END -->
38970
+ <!-- BEHAVIORAL_GUIDANCE_START -->
38959
38971
  4. ARCHITECT CODING BOUNDARIES \u2014 Only code yourself after {{QA_RETRY_LIMIT}} {{AGENT_PREFIX}}coder failures on same task.
38960
38972
  These thoughts are WRONG and must be ignored:
38961
38973
  \u2717 "It's just a schema change / config flag / one-liner / column / field / import" \u2192 delegate to {{AGENT_PREFIX}}coder
@@ -38972,6 +38984,7 @@ Two small delegations with two QA gates > one large delegation with one QA gate.
38972
38984
  If you catch yourself reaching for a code editing tool: STOP. Delegate to {{AGENT_PREFIX}}coder.
38973
38985
  Zero {{AGENT_PREFIX}}coder failures on this task = zero justification for self-coding.
38974
38986
  Self-coding without {{QA_RETRY_LIMIT}} failures is a Rule 1 violation.
38987
+ <!-- BEHAVIORAL_GUIDANCE_END -->
38975
38988
  5. NEVER store your swarm identity, swarm ID, or agent prefix in memory blocks. Your identity comes ONLY from your system prompt. Memory blocks are for project knowledge only (NOT .swarm/ plan/context files \u2014 those are persistent project files).
38976
38989
  6. **CRITIC GATE (Execute BEFORE any implementation work)**:
38977
38990
  - When you first create a plan, IMMEDIATELY delegate the full plan to {{AGENT_PREFIX}}critic for review
@@ -39128,6 +39141,7 @@ Your message MUST NOT contain:
39128
39141
 
39129
39142
  Delegation is a handoff, not a negotiation. State facts, let agents decide.
39130
39143
 
39144
+ <!-- BEHAVIORAL_GUIDANCE_START -->
39131
39145
  PARTIAL GATE RATIONALIZATIONS \u2014 automated gates \u2260 agent review. Running SOME gates is NOT compliance:
39132
39146
  \u2717 "I ran pre_check_batch so the code is verified" \u2192 pre_check_batch does NOT replace {{AGENT_PREFIX}}reviewer or {{AGENT_PREFIX}}test_engineer
39133
39147
  \u2717 "syntax_check passed, good enough" \u2192 syntax_check catches syntax. Reviewer catches logic. Test_engineer catches behavior. All three are required.
@@ -39138,6 +39152,7 @@ PARTIAL GATE RATIONALIZATIONS \u2014 automated gates \u2260 agent review. Runnin
39138
39152
 
39139
39153
  Running syntax_check + pre_check_batch without reviewer + test_engineer is a PARTIAL GATE VIOLATION.
39140
39154
  It is the same severity as skipping all gates. The QA gate is ALL steps or NONE.
39155
+ <!-- BEHAVIORAL_GUIDANCE_END -->
39141
39156
 
39142
39157
  8. **COVERAGE CHECK**: After adversarial tests pass, check if test_engineer reports coverage < 70%. If so, delegate {{AGENT_PREFIX}}test_engineer for an additional test pass targeting uncovered paths. This is a soft guideline; use judgment for trivial tasks.
39143
39158
  9. **UI/UX DESIGN GATE**: Before delegating UI tasks to {{AGENT_PREFIX}}coder, check if the task involves UI components. Trigger conditions (ANY match):
@@ -41886,7 +41901,13 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
41886
41901
  lastPhaseCompletePhase: 0,
41887
41902
  phaseAgentsDispatched: new Set,
41888
41903
  qaSkipCount: 0,
41889
- qaSkipTaskIds: []
41904
+ qaSkipTaskIds: [],
41905
+ taskWorkflowStates: new Map,
41906
+ lastGateOutcome: null,
41907
+ declaredCoderScope: null,
41908
+ lastScopeViolation: null,
41909
+ scopeViolationDetected: false,
41910
+ modifiedFilesThisCoderTask: []
41890
41911
  };
41891
41912
  swarmState.agentSessions.set(sessionId, sessionState);
41892
41913
  swarmState.activeAgent.set(sessionId, agentName);
@@ -41955,6 +41976,24 @@ function ensureAgentSession(sessionId, agentName) {
41955
41976
  if (!session.qaSkipTaskIds) {
41956
41977
  session.qaSkipTaskIds = [];
41957
41978
  }
41979
+ if (!session.taskWorkflowStates) {
41980
+ session.taskWorkflowStates = new Map;
41981
+ }
41982
+ if (session.lastGateOutcome === undefined) {
41983
+ session.lastGateOutcome = null;
41984
+ }
41985
+ if (session.declaredCoderScope === undefined) {
41986
+ session.declaredCoderScope = null;
41987
+ }
41988
+ if (session.lastScopeViolation === undefined) {
41989
+ session.lastScopeViolation = null;
41990
+ }
41991
+ if (session.modifiedFilesThisCoderTask === undefined) {
41992
+ session.modifiedFilesThisCoderTask = [];
41993
+ }
41994
+ if (session.scopeViolationDetected === undefined) {
41995
+ session.scopeViolationDetected = false;
41996
+ }
41958
41997
  session.lastToolCallTime = now;
41959
41998
  return session;
41960
41999
  }
@@ -42034,6 +42073,29 @@ function recordPhaseAgentDispatch(sessionId, agentName) {
42034
42073
  const normalizedName = stripKnownSwarmPrefix(agentName);
42035
42074
  session.phaseAgentsDispatched.add(normalizedName);
42036
42075
  }
42076
+ function advanceTaskState(session, taskId, newState) {
42077
+ const STATE_ORDER = [
42078
+ "idle",
42079
+ "coder_delegated",
42080
+ "pre_check_passed",
42081
+ "reviewer_run",
42082
+ "tests_run",
42083
+ "complete"
42084
+ ];
42085
+ const current = session.taskWorkflowStates.get(taskId) ?? "idle";
42086
+ const currentIndex = STATE_ORDER.indexOf(current);
42087
+ const newIndex = STATE_ORDER.indexOf(newState);
42088
+ if (newIndex <= currentIndex) {
42089
+ throw new Error(`INVALID_TASK_STATE_TRANSITION: ${taskId} ${current} \u2192 ${newState}`);
42090
+ }
42091
+ if (newState === "complete" && current !== "tests_run") {
42092
+ throw new Error(`INVALID_TASK_STATE_TRANSITION: ${taskId} cannot reach complete from ${current} \u2014 must pass through tests_run first`);
42093
+ }
42094
+ session.taskWorkflowStates.set(taskId, newState);
42095
+ }
42096
+ function getTaskState(session, taskId) {
42097
+ return session.taskWorkflowStates.get(taskId) ?? "idle";
42098
+ }
42037
42099
 
42038
42100
  // src/commands/benchmark.ts
42039
42101
  init_utils();
@@ -47149,9 +47211,27 @@ function createDelegationGateHook(config3) {
47149
47211
  if (normalized === "Task" || normalized === "task") {
47150
47212
  const delegationChain = swarmState.delegationChains.get(input.sessionID);
47151
47213
  if (delegationChain && delegationChain.length > 0) {
47152
- const lastDelegation = delegationChain[delegationChain.length - 1];
47153
- const target = stripKnownSwarmPrefix(lastDelegation.to);
47154
- if (target === "reviewer" || target === "test_engineer") {
47214
+ let lastCoderIndex = -1;
47215
+ for (let i2 = delegationChain.length - 1;i2 >= 0; i2--) {
47216
+ const target = stripKnownSwarmPrefix(delegationChain[i2].to);
47217
+ if (target.includes("coder")) {
47218
+ lastCoderIndex = i2;
47219
+ break;
47220
+ }
47221
+ }
47222
+ if (lastCoderIndex === -1)
47223
+ return;
47224
+ const afterCoder = delegationChain.slice(lastCoderIndex);
47225
+ let hasReviewer = false;
47226
+ let hasTestEngineer = false;
47227
+ for (const delegation of afterCoder) {
47228
+ const target = stripKnownSwarmPrefix(delegation.to);
47229
+ if (target === "reviewer")
47230
+ hasReviewer = true;
47231
+ if (target === "test_engineer")
47232
+ hasTestEngineer = true;
47233
+ }
47234
+ if (hasReviewer && hasTestEngineer) {
47155
47235
  session.qaSkipCount = 0;
47156
47236
  session.qaSkipTaskIds = [];
47157
47237
  }
@@ -47184,14 +47264,71 @@ function createDelegationGateHook(config3) {
47184
47264
  return;
47185
47265
  const textPart = lastUserMessage.parts[textPartIndex];
47186
47266
  const text = textPart.text ?? "";
47267
+ const taskDisclosureSessionID = lastUserMessage.info?.sessionID;
47268
+ if (taskDisclosureSessionID) {
47269
+ const taskSession = ensureAgentSession(taskDisclosureSessionID);
47270
+ const currentTaskIdForWindow = taskSession.currentTaskId;
47271
+ if (currentTaskIdForWindow) {
47272
+ const taskLineRegex = /^[ \t]*-[ \t]*(?:\[[ x]\][ \t]+)?(\d+\.\d+(?:\.\d+)*)[:. ].*/gm;
47273
+ const taskLines = [];
47274
+ taskLineRegex.lastIndex = 0;
47275
+ let regexMatch = taskLineRegex.exec(text);
47276
+ while (regexMatch !== null) {
47277
+ taskLines.push({
47278
+ line: regexMatch[0],
47279
+ taskId: regexMatch[1],
47280
+ index: regexMatch.index
47281
+ });
47282
+ regexMatch = taskLineRegex.exec(text);
47283
+ }
47284
+ if (taskLines.length > 5) {
47285
+ const currentIdx = taskLines.findIndex((t) => t.taskId === currentTaskIdForWindow);
47286
+ const windowStart = Math.max(0, currentIdx - 2);
47287
+ const windowEnd = Math.min(taskLines.length - 1, currentIdx + 3);
47288
+ const visibleTasks = taskLines.slice(windowStart, windowEnd + 1);
47289
+ const hiddenBefore = windowStart;
47290
+ const hiddenAfter = taskLines.length - 1 - windowEnd;
47291
+ const totalTasks = taskLines.length;
47292
+ const visibleCount = visibleTasks.length;
47293
+ const firstTaskIndex = taskLines[0].index;
47294
+ const lastTask = taskLines[taskLines.length - 1];
47295
+ const lastTaskEnd = lastTask.index + lastTask.line.length;
47296
+ const before = text.slice(0, firstTaskIndex);
47297
+ const after = text.slice(lastTaskEnd);
47298
+ const visibleLines = visibleTasks.map((t) => t.line).join(`
47299
+ `);
47300
+ const trimComment = `[Task window: showing ${visibleCount} of ${totalTasks} tasks]`;
47301
+ const trimmedMiddle = (hiddenBefore > 0 ? `[...${hiddenBefore} tasks hidden...]
47302
+ ` : "") + visibleLines + (hiddenAfter > 0 ? `
47303
+ [...${hiddenAfter} tasks hidden...]` : "");
47304
+ textPart.text = `${before}${trimmedMiddle}
47305
+ ${trimComment}${after}`;
47306
+ }
47307
+ }
47308
+ }
47187
47309
  const sessionID = lastUserMessage.info?.sessionID;
47188
47310
  const taskIdMatch = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
47189
47311
  const currentTaskId = taskIdMatch ? taskIdMatch[1].trim() : null;
47190
47312
  const coderDelegationPattern = /(?:^|\n)\s*(?:\w+_)?coder\s*\n\s*TASK:/i;
47191
47313
  const isCoderDelegation = coderDelegationPattern.test(text);
47314
+ const priorCoderTaskId = sessionID ? ensureAgentSession(sessionID).lastCoderDelegationTaskId ?? null : null;
47192
47315
  if (sessionID && isCoderDelegation && currentTaskId) {
47193
47316
  const session = ensureAgentSession(sessionID);
47194
47317
  session.lastCoderDelegationTaskId = currentTaskId;
47318
+ const fileDirPattern = /^FILE:\s*(.+)$/gm;
47319
+ const declaredFiles = [];
47320
+ for (const match of text.matchAll(fileDirPattern)) {
47321
+ const filePath = match[1].trim();
47322
+ if (filePath.length > 0 && !declaredFiles.includes(filePath)) {
47323
+ declaredFiles.push(filePath);
47324
+ }
47325
+ }
47326
+ session.declaredCoderScope = declaredFiles.length > 0 ? declaredFiles : null;
47327
+ try {
47328
+ advanceTaskState(session, currentTaskId, "coder_delegated");
47329
+ } catch (err2) {
47330
+ console.warn(`[delegation-gate] state machine warn: ${err2 instanceof Error ? err2.message : String(err2)}`);
47331
+ }
47195
47332
  }
47196
47333
  if (sessionID && !isCoderDelegation && currentTaskId) {
47197
47334
  const session = ensureAgentSession(sessionID);
@@ -47203,6 +47340,29 @@ Rule 1: DELEGATE all coding to coder. You do NOT write code.`;
47203
47340
  ${text}`;
47204
47341
  }
47205
47342
  }
47343
+ {
47344
+ const deliberationSessionID = lastUserMessage.info?.sessionID;
47345
+ if (deliberationSessionID) {
47346
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(deliberationSessionID)) {} else {
47347
+ const deliberationSession = ensureAgentSession(deliberationSessionID);
47348
+ const lastGate = deliberationSession.lastGateOutcome;
47349
+ let preamble;
47350
+ if (lastGate) {
47351
+ const gateResult = lastGate.passed ? "PASSED" : "FAILED";
47352
+ const sanitizedGate = lastGate.gate.replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
47353
+ const sanitizedTaskId = lastGate.taskId.replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 32);
47354
+ preamble = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
47355
+ [DELIBERATE: Before proceeding \u2014 what is the SINGLE next task? What gates must it pass?]`;
47356
+ } else {
47357
+ preamble = `[DELIBERATE: Identify the first task from the plan. What gates must it pass before marking complete?]`;
47358
+ }
47359
+ const currentText = textPart.text ?? "";
47360
+ textPart.text = `${preamble}
47361
+
47362
+ ${currentText}`;
47363
+ }
47364
+ }
47365
+ }
47206
47366
  if (!isCoderDelegation)
47207
47367
  return;
47208
47368
  const warnings = [];
@@ -47245,8 +47405,9 @@ ${text}`;
47245
47405
  const betweenCoders = delegationChain.slice(prevCoderIndex + 1);
47246
47406
  const hasReviewer = betweenCoders.some((d) => stripKnownSwarmPrefix(d.to) === "reviewer");
47247
47407
  const hasTestEngineer = betweenCoders.some((d) => stripKnownSwarmPrefix(d.to) === "test_engineer");
47248
- if (!hasReviewer || !hasTestEngineer) {
47249
- const session = ensureAgentSession(sessionID);
47408
+ const session = ensureAgentSession(sessionID);
47409
+ const priorTaskStuckAtCoder = priorCoderTaskId !== null && getTaskState(session, priorCoderTaskId) === "coder_delegated";
47410
+ if (!hasReviewer || !hasTestEngineer || priorTaskStuckAtCoder) {
47250
47411
  if (session.qaSkipCount >= 1) {
47251
47412
  const skippedTasks = session.qaSkipTaskIds.join(", ");
47252
47413
  throw new Error(`\uD83D\uDED1 QA GATE ENFORCEMENT: ${session.qaSkipCount + 1} consecutive coder delegations without reviewer/test_engineer. ` + `Skipped tasks: [${skippedTasks}]. ` + `DELEGATE to reviewer and test_engineer NOW before any further coder work.`);
@@ -47506,6 +47667,16 @@ function getCurrentTaskId(sessionId) {
47506
47667
  const session = swarmState.agentSessions.get(sessionId);
47507
47668
  return session?.currentTaskId ?? `${sessionId}:unknown`;
47508
47669
  }
47670
+ function isInDeclaredScope(filePath, scopeEntries) {
47671
+ const resolvedFile = path25.resolve(filePath);
47672
+ return scopeEntries.some((scope) => {
47673
+ const resolvedScope = path25.resolve(scope);
47674
+ if (resolvedFile === resolvedScope)
47675
+ return true;
47676
+ const rel = path25.relative(resolvedScope, resolvedFile);
47677
+ return rel.length > 0 && !rel.startsWith("..") && !path25.isAbsolute(rel);
47678
+ });
47679
+ }
47509
47680
  function createGuardrailsHooks(directoryOrConfig, config3) {
47510
47681
  let directory;
47511
47682
  let guardrailsConfig;
@@ -47528,7 +47699,27 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
47528
47699
  return {
47529
47700
  toolBefore: async (input, output) => {
47530
47701
  const currentSession = swarmState.agentSessions.get(input.sessionID);
47531
- if (currentSession?.delegationActive) {} else if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
47702
+ if (currentSession?.delegationActive) {
47703
+ if (isWriteTool(input.tool)) {
47704
+ const delegArgs = output.args;
47705
+ const delegTargetPath = delegArgs?.filePath ?? delegArgs?.path ?? delegArgs?.file ?? delegArgs?.target;
47706
+ if (typeof delegTargetPath === "string" && delegTargetPath.length > 0) {
47707
+ if (!currentSession.modifiedFilesThisCoderTask.includes(delegTargetPath)) {
47708
+ currentSession.modifiedFilesThisCoderTask.push(delegTargetPath);
47709
+ }
47710
+ }
47711
+ }
47712
+ } else if (isArchitect(input.sessionID)) {
47713
+ const coderDelegArgs = output.args;
47714
+ const coderDeleg = isAgentDelegation(input.tool, coderDelegArgs);
47715
+ if (coderDeleg.isDelegation && coderDeleg.targetAgent === "coder") {
47716
+ const coderSession = swarmState.agentSessions.get(input.sessionID);
47717
+ if (coderSession) {
47718
+ coderSession.modifiedFilesThisCoderTask = [];
47719
+ }
47720
+ }
47721
+ }
47722
+ if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
47532
47723
  const args2 = output.args;
47533
47724
  const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
47534
47725
  if (typeof targetPath === "string" && targetPath.length > 0) {
@@ -47574,7 +47765,7 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
47574
47765
  }
47575
47766
  }
47576
47767
  }
47577
- if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath, directory) && (isSourceCodePath(targetPath) || hasTraversalSegments(targetPath))) {
47768
+ if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, directory) && isSourceCodePath(path25.relative(directory, path25.resolve(directory, targetPath)))) {
47578
47769
  const session2 = swarmState.agentSessions.get(input.sessionID);
47579
47770
  if (session2) {
47580
47771
  session2.architectWriteCount++;
@@ -47766,6 +47957,15 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
47766
47957
  if (delegation.isDelegation && delegation.targetAgent === "coder" && session.lastCoderDelegationTaskId) {
47767
47958
  session.currentTaskId = session.lastCoderDelegationTaskId;
47768
47959
  session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
47960
+ if (session.declaredCoderScope !== null) {
47961
+ const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
47962
+ if (undeclaredFiles.length > 2) {
47963
+ const safeTaskId = String(session.currentTaskId ?? "").replace(/[\r\n\t]/g, "_");
47964
+ session.lastScopeViolation = `Scope violation for task ${safeTaskId}: ` + `${undeclaredFiles.length} undeclared files modified: ` + undeclaredFiles.join(", ");
47965
+ session.scopeViolationDetected = true;
47966
+ }
47967
+ }
47968
+ session.modifiedFilesThisCoderTask = [];
47769
47969
  }
47770
47970
  }
47771
47971
  const window2 = getActiveWindow(input.sessionID);
@@ -47789,6 +47989,26 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
47789
47989
  if (!sessionId) {
47790
47990
  return;
47791
47991
  }
47992
+ {
47993
+ const { modelID } = extractModelInfo(messages);
47994
+ if (modelID && isLowCapabilityModel(modelID)) {
47995
+ for (const msg of messages) {
47996
+ if (msg.info?.role !== "system")
47997
+ continue;
47998
+ for (const part of msg.parts) {
47999
+ try {
48000
+ if (part == null)
48001
+ continue;
48002
+ if (part.type !== "text" || typeof part.text !== "string")
48003
+ continue;
48004
+ if (!part.text.includes("<!-- BEHAVIORAL_GUIDANCE_START -->"))
48005
+ continue;
48006
+ part.text = part.text.replace(/<!--\s*BEHAVIORAL_GUIDANCE_START\s*-->[\s\S]*?<!--\s*BEHAVIORAL_GUIDANCE_END\s*-->/g, "[Enforcement: programmatic gates active]");
48007
+ } catch {}
48008
+ }
48009
+ }
48010
+ }
48011
+ }
47792
48012
  const session = swarmState.agentSessions.get(sessionId);
47793
48013
  const activeAgent = swarmState.activeAgent.get(sessionId);
47794
48014
  const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
@@ -47861,6 +48081,16 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
47861
48081
  }
47862
48082
  }
47863
48083
  }
48084
+ if (isArchitectSessionForGates && session && session.scopeViolationDetected) {
48085
+ session.scopeViolationDetected = false;
48086
+ const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
48087
+ if (textPart2 && session.lastScopeViolation) {
48088
+ textPart2.text = `\u26A0\uFE0F SCOPE VIOLATION: ${session.lastScopeViolation}
48089
+ ` + `Only modify files within your declared scope. Request scope expansion from architect if needed.
48090
+
48091
+ ` + textPart2.text;
48092
+ }
48093
+ }
47864
48094
  if (isArchitectSessionForGates && session && session.catastrophicPhaseWarnings) {
47865
48095
  try {
47866
48096
  const plan = await loadPlan(directory);
@@ -50700,7 +50930,12 @@ function deserializeAgentSession(s) {
50700
50930
  lastPhaseCompletePhase: s.lastPhaseCompletePhase ?? 0,
50701
50931
  phaseAgentsDispatched,
50702
50932
  qaSkipCount: s.qaSkipCount ?? 0,
50703
- qaSkipTaskIds: s.qaSkipTaskIds ?? []
50933
+ qaSkipTaskIds: s.qaSkipTaskIds ?? [],
50934
+ taskWorkflowStates: new Map,
50935
+ lastGateOutcome: null,
50936
+ declaredCoderScope: null,
50937
+ lastScopeViolation: null,
50938
+ modifiedFilesThisCoderTask: []
50704
50939
  };
50705
50940
  }
50706
50941
  async function readSnapshot(directory) {
@@ -51512,6 +51747,184 @@ var complexity_hotspots = createSwarmTool({
51512
51747
  }
51513
51748
  }
51514
51749
  });
51750
+ // src/tools/declare-scope.ts
51751
+ init_tool();
51752
+ import * as fs19 from "fs";
51753
+ import * as path30 from "path";
51754
+ init_create_tool();
51755
+ function validateTaskIdFormat(taskId) {
51756
+ const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
51757
+ if (!taskIdPattern.test(taskId)) {
51758
+ return `Invalid taskId "${taskId}". Must match pattern N.M or N.M.P (e.g., "1.1", "1.2.3")`;
51759
+ }
51760
+ return;
51761
+ }
51762
+ function validateFiles(files) {
51763
+ const errors5 = [];
51764
+ for (const file3 of files) {
51765
+ if (file3.includes("\x00")) {
51766
+ errors5.push(`Invalid file "${file3}": null bytes are not allowed`);
51767
+ }
51768
+ if (file3.includes("..")) {
51769
+ errors5.push(`Invalid file "${file3}": path traversal sequences (..) are not allowed`);
51770
+ }
51771
+ if (file3.length > 4096) {
51772
+ errors5.push(`Invalid file "${file3}": path exceeds maximum length of 4096 characters`);
51773
+ }
51774
+ }
51775
+ return errors5;
51776
+ }
51777
+ async function executeDeclareScope(args2, fallbackDir) {
51778
+ const taskIdError = validateTaskIdFormat(args2.taskId);
51779
+ if (taskIdError) {
51780
+ return {
51781
+ success: false,
51782
+ message: "Validation failed",
51783
+ errors: [taskIdError]
51784
+ };
51785
+ }
51786
+ if (!Array.isArray(args2.files) || args2.files.length === 0) {
51787
+ return {
51788
+ success: false,
51789
+ message: "Validation failed",
51790
+ errors: ["files must be a non-empty array"]
51791
+ };
51792
+ }
51793
+ const fileErrors = validateFiles(args2.files);
51794
+ if (fileErrors.length > 0) {
51795
+ return {
51796
+ success: false,
51797
+ message: "Validation failed",
51798
+ errors: fileErrors
51799
+ };
51800
+ }
51801
+ if (args2.whitelist) {
51802
+ const whitelistErrors = validateFiles(args2.whitelist);
51803
+ if (whitelistErrors.length > 0) {
51804
+ return {
51805
+ success: false,
51806
+ message: "Validation failed",
51807
+ errors: whitelistErrors
51808
+ };
51809
+ }
51810
+ }
51811
+ let normalizedDir;
51812
+ if (args2.working_directory != null) {
51813
+ if (args2.working_directory.includes("\x00")) {
51814
+ return {
51815
+ success: false,
51816
+ message: "Invalid working_directory: null bytes are not allowed",
51817
+ errors: ["Invalid working_directory: null bytes are not allowed"]
51818
+ };
51819
+ }
51820
+ if (process.platform === "win32") {
51821
+ const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
51822
+ if (devicePathPattern.test(args2.working_directory)) {
51823
+ return {
51824
+ success: false,
51825
+ message: "Invalid working_directory: Windows device paths are not allowed",
51826
+ errors: [
51827
+ "Invalid working_directory: Windows device paths are not allowed"
51828
+ ]
51829
+ };
51830
+ }
51831
+ }
51832
+ normalizedDir = path30.normalize(args2.working_directory);
51833
+ const pathParts = normalizedDir.split(path30.sep);
51834
+ if (pathParts.includes("..")) {
51835
+ return {
51836
+ success: false,
51837
+ message: "Invalid working_directory: path traversal sequences (..) are not allowed",
51838
+ errors: [
51839
+ "Invalid working_directory: path traversal sequences (..) are not allowed"
51840
+ ]
51841
+ };
51842
+ }
51843
+ const resolvedDir = path30.resolve(normalizedDir);
51844
+ try {
51845
+ const realPath = fs19.realpathSync(resolvedDir);
51846
+ const planPath2 = path30.join(realPath, ".swarm", "plan.json");
51847
+ if (!fs19.existsSync(planPath2)) {
51848
+ return {
51849
+ success: false,
51850
+ message: `Invalid working_directory: plan not found in "${realPath}"`,
51851
+ errors: [
51852
+ `Invalid working_directory: plan not found in "${realPath}"`
51853
+ ]
51854
+ };
51855
+ }
51856
+ } catch {
51857
+ return {
51858
+ success: false,
51859
+ message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
51860
+ errors: [
51861
+ `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
51862
+ ]
51863
+ };
51864
+ }
51865
+ }
51866
+ const directory = normalizedDir ?? fallbackDir ?? process.cwd();
51867
+ const planPath = path30.resolve(directory, ".swarm", "plan.json");
51868
+ if (!fs19.existsSync(planPath)) {
51869
+ return {
51870
+ success: false,
51871
+ message: "No plan found",
51872
+ errors: ["plan.json not found"]
51873
+ };
51874
+ }
51875
+ let planContent;
51876
+ try {
51877
+ planContent = JSON.parse(fs19.readFileSync(planPath, "utf-8"));
51878
+ } catch {
51879
+ return {
51880
+ success: false,
51881
+ message: "Failed to parse plan.json",
51882
+ errors: ["plan.json is not valid JSON"]
51883
+ };
51884
+ }
51885
+ const allTasks = planContent.phases?.flatMap((p) => p.tasks ?? []) ?? [];
51886
+ const taskExists = allTasks.some((t) => t.id === args2.taskId);
51887
+ if (!taskExists) {
51888
+ return {
51889
+ success: false,
51890
+ message: `Task ${args2.taskId} not found in plan`,
51891
+ errors: [`Task ${args2.taskId} does not exist in plan.json`]
51892
+ };
51893
+ }
51894
+ for (const [_sessionId, session] of swarmState.agentSessions) {
51895
+ const taskState = getTaskState(session, args2.taskId);
51896
+ if (taskState === "complete") {
51897
+ return {
51898
+ success: false,
51899
+ message: `Task ${args2.taskId} is already completed`,
51900
+ errors: [`Cannot declare scope for completed task ${args2.taskId}`]
51901
+ };
51902
+ }
51903
+ }
51904
+ const mergedFiles = [...args2.files, ...args2.whitelist ?? []];
51905
+ for (const [_sessionId, session] of swarmState.agentSessions) {
51906
+ session.declaredCoderScope = mergedFiles;
51907
+ session.lastScopeViolation = null;
51908
+ }
51909
+ return {
51910
+ success: true,
51911
+ message: "Scope declared successfully",
51912
+ taskId: args2.taskId,
51913
+ fileCount: mergedFiles.length
51914
+ };
51915
+ }
51916
+ var declare_scope = createSwarmTool({
51917
+ description: "Declare the file scope for the next coder delegation. " + "Sets the list of files the coder is permitted to modify for a specific task. " + "Must be called before delegating to mega_coder to enable scope containment checking.",
51918
+ args: {
51919
+ taskId: tool.schema.string().min(1).regex(/^\d+\.\d+(\.\d+)*$/, "Task ID must be in N.M or N.M.P format").describe('Task ID for which scope is being declared, e.g. "1.1", "1.2.3"'),
51920
+ files: tool.schema.array(tool.schema.string().min(1).max(4096)).min(1).describe("Array of file paths the coder is permitted to modify"),
51921
+ whitelist: tool.schema.array(tool.schema.string().min(1).max(4096)).optional().describe("Additional file paths to whitelist (merged with files)"),
51922
+ working_directory: tool.schema.string().optional().describe("Working directory where the plan is located")
51923
+ },
51924
+ execute: async (args2, _directory) => {
51925
+ return JSON.stringify(await executeDeclareScope(args2, _directory), null, 2);
51926
+ }
51927
+ });
51515
51928
  // src/tools/diff.ts
51516
51929
  init_dist();
51517
51930
  import { execFileSync } from "child_process";
@@ -51541,20 +51954,20 @@ function validateBase(base) {
51541
51954
  function validatePaths(paths) {
51542
51955
  if (!paths)
51543
51956
  return null;
51544
- for (const path30 of paths) {
51545
- if (!path30 || path30.length === 0) {
51957
+ for (const path31 of paths) {
51958
+ if (!path31 || path31.length === 0) {
51546
51959
  return "empty path not allowed";
51547
51960
  }
51548
- if (path30.length > MAX_PATH_LENGTH) {
51961
+ if (path31.length > MAX_PATH_LENGTH) {
51549
51962
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
51550
51963
  }
51551
- if (SHELL_METACHARACTERS2.test(path30)) {
51964
+ if (SHELL_METACHARACTERS2.test(path31)) {
51552
51965
  return "path contains shell metacharacters";
51553
51966
  }
51554
- if (path30.startsWith("-")) {
51967
+ if (path31.startsWith("-")) {
51555
51968
  return 'path cannot start with "-" (option-like arguments not allowed)';
51556
51969
  }
51557
- if (CONTROL_CHAR_PATTERN2.test(path30)) {
51970
+ if (CONTROL_CHAR_PATTERN2.test(path31)) {
51558
51971
  return "path contains control characters";
51559
51972
  }
51560
51973
  }
@@ -51634,8 +52047,8 @@ var diff = tool({
51634
52047
  if (parts2.length >= 3) {
51635
52048
  const additions = parseInt(parts2[0], 10) || 0;
51636
52049
  const deletions = parseInt(parts2[1], 10) || 0;
51637
- const path30 = parts2[2];
51638
- files.push({ path: path30, additions, deletions });
52050
+ const path31 = parts2[2];
52051
+ files.push({ path: path31, additions, deletions });
51639
52052
  }
51640
52053
  }
51641
52054
  const contractChanges = [];
@@ -51864,8 +52277,8 @@ Use these as DOMAIN values when delegating to @sme.`;
51864
52277
  // src/tools/evidence-check.ts
51865
52278
  init_dist();
51866
52279
  init_create_tool();
51867
- import * as fs19 from "fs";
51868
- import * as path30 from "path";
52280
+ import * as fs20 from "fs";
52281
+ import * as path31 from "path";
51869
52282
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
51870
52283
  var MAX_EVIDENCE_FILES = 1000;
51871
52284
  var EVIDENCE_DIR = ".swarm/evidence";
@@ -51888,9 +52301,9 @@ function validateRequiredTypes(input) {
51888
52301
  return null;
51889
52302
  }
51890
52303
  function isPathWithinSwarm(filePath, cwd) {
51891
- const normalizedCwd = path30.resolve(cwd);
51892
- const swarmPath = path30.join(normalizedCwd, ".swarm");
51893
- const normalizedPath = path30.resolve(filePath);
52304
+ const normalizedCwd = path31.resolve(cwd);
52305
+ const swarmPath = path31.join(normalizedCwd, ".swarm");
52306
+ const normalizedPath = path31.resolve(filePath);
51894
52307
  return normalizedPath.startsWith(swarmPath);
51895
52308
  }
51896
52309
  function parseCompletedTasks(planContent) {
@@ -51906,12 +52319,12 @@ function parseCompletedTasks(planContent) {
51906
52319
  }
51907
52320
  function readEvidenceFiles(evidenceDir, _cwd) {
51908
52321
  const evidence = [];
51909
- if (!fs19.existsSync(evidenceDir) || !fs19.statSync(evidenceDir).isDirectory()) {
52322
+ if (!fs20.existsSync(evidenceDir) || !fs20.statSync(evidenceDir).isDirectory()) {
51910
52323
  return evidence;
51911
52324
  }
51912
52325
  let files;
51913
52326
  try {
51914
- files = fs19.readdirSync(evidenceDir);
52327
+ files = fs20.readdirSync(evidenceDir);
51915
52328
  } catch {
51916
52329
  return evidence;
51917
52330
  }
@@ -51920,14 +52333,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
51920
52333
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
51921
52334
  continue;
51922
52335
  }
51923
- const filePath = path30.join(evidenceDir, filename);
52336
+ const filePath = path31.join(evidenceDir, filename);
51924
52337
  try {
51925
- const resolvedPath = path30.resolve(filePath);
51926
- const evidenceDirResolved = path30.resolve(evidenceDir);
52338
+ const resolvedPath = path31.resolve(filePath);
52339
+ const evidenceDirResolved = path31.resolve(evidenceDir);
51927
52340
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
51928
52341
  continue;
51929
52342
  }
51930
- const stat2 = fs19.lstatSync(filePath);
52343
+ const stat2 = fs20.lstatSync(filePath);
51931
52344
  if (!stat2.isFile()) {
51932
52345
  continue;
51933
52346
  }
@@ -51936,7 +52349,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
51936
52349
  }
51937
52350
  let fileStat;
51938
52351
  try {
51939
- fileStat = fs19.statSync(filePath);
52352
+ fileStat = fs20.statSync(filePath);
51940
52353
  if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
51941
52354
  continue;
51942
52355
  }
@@ -51945,7 +52358,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
51945
52358
  }
51946
52359
  let content;
51947
52360
  try {
51948
- content = fs19.readFileSync(filePath, "utf-8");
52361
+ content = fs20.readFileSync(filePath, "utf-8");
51949
52362
  } catch {
51950
52363
  continue;
51951
52364
  }
@@ -52030,7 +52443,7 @@ var evidence_check = createSwarmTool({
52030
52443
  return JSON.stringify(errorResult, null, 2);
52031
52444
  }
52032
52445
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
52033
- const planPath = path30.join(cwd, PLAN_FILE);
52446
+ const planPath = path31.join(cwd, PLAN_FILE);
52034
52447
  if (!isPathWithinSwarm(planPath, cwd)) {
52035
52448
  const errorResult = {
52036
52449
  error: "plan file path validation failed",
@@ -52044,7 +52457,7 @@ var evidence_check = createSwarmTool({
52044
52457
  }
52045
52458
  let planContent;
52046
52459
  try {
52047
- planContent = fs19.readFileSync(planPath, "utf-8");
52460
+ planContent = fs20.readFileSync(planPath, "utf-8");
52048
52461
  } catch {
52049
52462
  const result2 = {
52050
52463
  message: "No completed tasks found in plan.",
@@ -52062,7 +52475,7 @@ var evidence_check = createSwarmTool({
52062
52475
  };
52063
52476
  return JSON.stringify(result2, null, 2);
52064
52477
  }
52065
- const evidenceDir = path30.join(cwd, EVIDENCE_DIR);
52478
+ const evidenceDir = path31.join(cwd, EVIDENCE_DIR);
52066
52479
  const evidence = readEvidenceFiles(evidenceDir, cwd);
52067
52480
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
52068
52481
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -52079,8 +52492,8 @@ var evidence_check = createSwarmTool({
52079
52492
  // src/tools/file-extractor.ts
52080
52493
  init_tool();
52081
52494
  init_create_tool();
52082
- import * as fs20 from "fs";
52083
- import * as path31 from "path";
52495
+ import * as fs21 from "fs";
52496
+ import * as path32 from "path";
52084
52497
  var EXT_MAP = {
52085
52498
  python: ".py",
52086
52499
  py: ".py",
@@ -52142,8 +52555,8 @@ var extract_code_blocks = createSwarmTool({
52142
52555
  execute: async (args2, directory) => {
52143
52556
  const { content, output_dir, prefix } = args2;
52144
52557
  const targetDir = output_dir || directory;
52145
- if (!fs20.existsSync(targetDir)) {
52146
- fs20.mkdirSync(targetDir, { recursive: true });
52558
+ if (!fs21.existsSync(targetDir)) {
52559
+ fs21.mkdirSync(targetDir, { recursive: true });
52147
52560
  }
52148
52561
  if (!content) {
52149
52562
  return "Error: content is required";
@@ -52161,16 +52574,16 @@ var extract_code_blocks = createSwarmTool({
52161
52574
  if (prefix) {
52162
52575
  filename = `${prefix}_${filename}`;
52163
52576
  }
52164
- let filepath = path31.join(targetDir, filename);
52165
- const base = path31.basename(filepath, path31.extname(filepath));
52166
- const ext = path31.extname(filepath);
52577
+ let filepath = path32.join(targetDir, filename);
52578
+ const base = path32.basename(filepath, path32.extname(filepath));
52579
+ const ext = path32.extname(filepath);
52167
52580
  let counter = 1;
52168
- while (fs20.existsSync(filepath)) {
52169
- filepath = path31.join(targetDir, `${base}_${counter}${ext}`);
52581
+ while (fs21.existsSync(filepath)) {
52582
+ filepath = path32.join(targetDir, `${base}_${counter}${ext}`);
52170
52583
  counter++;
52171
52584
  }
52172
52585
  try {
52173
- fs20.writeFileSync(filepath, code.trim(), "utf-8");
52586
+ fs21.writeFileSync(filepath, code.trim(), "utf-8");
52174
52587
  savedFiles.push(filepath);
52175
52588
  } catch (error93) {
52176
52589
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -52199,7 +52612,7 @@ init_dist();
52199
52612
  var GITINGEST_TIMEOUT_MS = 1e4;
52200
52613
  var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
52201
52614
  var GITINGEST_MAX_RETRIES = 2;
52202
- var delay = (ms) => new Promise((resolve11) => setTimeout(resolve11, ms));
52615
+ var delay = (ms) => new Promise((resolve12) => setTimeout(resolve12, ms));
52203
52616
  async function fetchGitingest(args2) {
52204
52617
  for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
52205
52618
  try {
@@ -52278,8 +52691,8 @@ var gitingest = tool({
52278
52691
  });
52279
52692
  // src/tools/imports.ts
52280
52693
  init_dist();
52281
- import * as fs21 from "fs";
52282
- import * as path32 from "path";
52694
+ import * as fs22 from "fs";
52695
+ import * as path33 from "path";
52283
52696
  var MAX_FILE_PATH_LENGTH2 = 500;
52284
52697
  var MAX_SYMBOL_LENGTH = 256;
52285
52698
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -52333,7 +52746,7 @@ function validateSymbolInput(symbol3) {
52333
52746
  return null;
52334
52747
  }
52335
52748
  function isBinaryFile2(filePath, buffer) {
52336
- const ext = path32.extname(filePath).toLowerCase();
52749
+ const ext = path33.extname(filePath).toLowerCase();
52337
52750
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
52338
52751
  return false;
52339
52752
  }
@@ -52357,15 +52770,15 @@ function parseImports(content, targetFile, targetSymbol) {
52357
52770
  const imports = [];
52358
52771
  let _resolvedTarget;
52359
52772
  try {
52360
- _resolvedTarget = path32.resolve(targetFile);
52773
+ _resolvedTarget = path33.resolve(targetFile);
52361
52774
  } catch {
52362
52775
  _resolvedTarget = targetFile;
52363
52776
  }
52364
- const targetBasename = path32.basename(targetFile, path32.extname(targetFile));
52777
+ const targetBasename = path33.basename(targetFile, path33.extname(targetFile));
52365
52778
  const targetWithExt = targetFile;
52366
52779
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
52367
- const normalizedTargetWithExt = path32.normalize(targetWithExt).replace(/\\/g, "/");
52368
- const normalizedTargetWithoutExt = path32.normalize(targetWithoutExt).replace(/\\/g, "/");
52780
+ const normalizedTargetWithExt = path33.normalize(targetWithExt).replace(/\\/g, "/");
52781
+ const normalizedTargetWithoutExt = path33.normalize(targetWithoutExt).replace(/\\/g, "/");
52369
52782
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
52370
52783
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
52371
52784
  const modulePath = match[1] || match[2] || match[3];
@@ -52388,9 +52801,9 @@ function parseImports(content, targetFile, targetSymbol) {
52388
52801
  }
52389
52802
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
52390
52803
  let isMatch = false;
52391
- const _targetDir = path32.dirname(targetFile);
52392
- const targetExt = path32.extname(targetFile);
52393
- const targetBasenameNoExt = path32.basename(targetFile, targetExt);
52804
+ const _targetDir = path33.dirname(targetFile);
52805
+ const targetExt = path33.extname(targetFile);
52806
+ const targetBasenameNoExt = path33.basename(targetFile, targetExt);
52394
52807
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
52395
52808
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
52396
52809
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -52447,7 +52860,7 @@ var SKIP_DIRECTORIES2 = new Set([
52447
52860
  function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
52448
52861
  let entries;
52449
52862
  try {
52450
- entries = fs21.readdirSync(dir);
52863
+ entries = fs22.readdirSync(dir);
52451
52864
  } catch (e) {
52452
52865
  stats.fileErrors.push({
52453
52866
  path: dir,
@@ -52458,13 +52871,13 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
52458
52871
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
52459
52872
  for (const entry of entries) {
52460
52873
  if (SKIP_DIRECTORIES2.has(entry)) {
52461
- stats.skippedDirs.push(path32.join(dir, entry));
52874
+ stats.skippedDirs.push(path33.join(dir, entry));
52462
52875
  continue;
52463
52876
  }
52464
- const fullPath = path32.join(dir, entry);
52877
+ const fullPath = path33.join(dir, entry);
52465
52878
  let stat2;
52466
52879
  try {
52467
- stat2 = fs21.statSync(fullPath);
52880
+ stat2 = fs22.statSync(fullPath);
52468
52881
  } catch (e) {
52469
52882
  stats.fileErrors.push({
52470
52883
  path: fullPath,
@@ -52475,7 +52888,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
52475
52888
  if (stat2.isDirectory()) {
52476
52889
  findSourceFiles2(fullPath, files, stats);
52477
52890
  } else if (stat2.isFile()) {
52478
- const ext = path32.extname(fullPath).toLowerCase();
52891
+ const ext = path33.extname(fullPath).toLowerCase();
52479
52892
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
52480
52893
  files.push(fullPath);
52481
52894
  }
@@ -52531,8 +52944,8 @@ var imports = tool({
52531
52944
  return JSON.stringify(errorResult, null, 2);
52532
52945
  }
52533
52946
  try {
52534
- const targetFile = path32.resolve(file3);
52535
- if (!fs21.existsSync(targetFile)) {
52947
+ const targetFile = path33.resolve(file3);
52948
+ if (!fs22.existsSync(targetFile)) {
52536
52949
  const errorResult = {
52537
52950
  error: `target file not found: ${file3}`,
52538
52951
  target: file3,
@@ -52542,7 +52955,7 @@ var imports = tool({
52542
52955
  };
52543
52956
  return JSON.stringify(errorResult, null, 2);
52544
52957
  }
52545
- const targetStat = fs21.statSync(targetFile);
52958
+ const targetStat = fs22.statSync(targetFile);
52546
52959
  if (!targetStat.isFile()) {
52547
52960
  const errorResult = {
52548
52961
  error: "target must be a file, not a directory",
@@ -52553,7 +52966,7 @@ var imports = tool({
52553
52966
  };
52554
52967
  return JSON.stringify(errorResult, null, 2);
52555
52968
  }
52556
- const baseDir = path32.dirname(targetFile);
52969
+ const baseDir = path33.dirname(targetFile);
52557
52970
  const scanStats = {
52558
52971
  skippedDirs: [],
52559
52972
  skippedFiles: 0,
@@ -52568,12 +52981,12 @@ var imports = tool({
52568
52981
  if (consumers.length >= MAX_CONSUMERS)
52569
52982
  break;
52570
52983
  try {
52571
- const stat2 = fs21.statSync(filePath);
52984
+ const stat2 = fs22.statSync(filePath);
52572
52985
  if (stat2.size > MAX_FILE_SIZE_BYTES4) {
52573
52986
  skippedFileCount++;
52574
52987
  continue;
52575
52988
  }
52576
- const buffer = fs21.readFileSync(filePath);
52989
+ const buffer = fs22.readFileSync(filePath);
52577
52990
  if (isBinaryFile2(filePath, buffer)) {
52578
52991
  skippedFileCount++;
52579
52992
  continue;
@@ -52642,8 +53055,8 @@ init_lint();
52642
53055
 
52643
53056
  // src/tools/phase-complete.ts
52644
53057
  init_dist();
52645
- import * as fs22 from "fs";
52646
- import * as path33 from "path";
53058
+ import * as fs23 from "fs";
53059
+ import * as path34 from "path";
52647
53060
  init_manager();
52648
53061
  init_utils2();
52649
53062
  init_create_tool();
@@ -52833,7 +53246,7 @@ async function executePhaseComplete(args2, workingDirectory) {
52833
53246
  }
52834
53247
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
52835
53248
  try {
52836
- const projectName = path33.basename(dir);
53249
+ const projectName = path34.basename(dir);
52837
53250
  const knowledgeConfig = {
52838
53251
  enabled: true,
52839
53252
  swarm_max_entries: 100,
@@ -52894,7 +53307,7 @@ async function executePhaseComplete(args2, workingDirectory) {
52894
53307
  };
52895
53308
  try {
52896
53309
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
52897
- fs22.appendFileSync(eventsPath, `${JSON.stringify(event)}
53310
+ fs23.appendFileSync(eventsPath, `${JSON.stringify(event)}
52898
53311
  `, "utf-8");
52899
53312
  } catch (writeError) {
52900
53313
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -52952,8 +53365,8 @@ init_dist();
52952
53365
  init_discovery();
52953
53366
  init_utils();
52954
53367
  init_create_tool();
52955
- import * as fs23 from "fs";
52956
- import * as path34 from "path";
53368
+ import * as fs24 from "fs";
53369
+ import * as path35 from "path";
52957
53370
  var MAX_OUTPUT_BYTES5 = 52428800;
52958
53371
  var AUDIT_TIMEOUT_MS = 120000;
52959
53372
  function isValidEcosystem(value) {
@@ -52971,28 +53384,28 @@ function validateArgs3(args2) {
52971
53384
  function detectEcosystems(directory) {
52972
53385
  const ecosystems = [];
52973
53386
  const cwd = directory;
52974
- if (fs23.existsSync(path34.join(cwd, "package.json"))) {
53387
+ if (fs24.existsSync(path35.join(cwd, "package.json"))) {
52975
53388
  ecosystems.push("npm");
52976
53389
  }
52977
- if (fs23.existsSync(path34.join(cwd, "pyproject.toml")) || fs23.existsSync(path34.join(cwd, "requirements.txt"))) {
53390
+ if (fs24.existsSync(path35.join(cwd, "pyproject.toml")) || fs24.existsSync(path35.join(cwd, "requirements.txt"))) {
52978
53391
  ecosystems.push("pip");
52979
53392
  }
52980
- if (fs23.existsSync(path34.join(cwd, "Cargo.toml"))) {
53393
+ if (fs24.existsSync(path35.join(cwd, "Cargo.toml"))) {
52981
53394
  ecosystems.push("cargo");
52982
53395
  }
52983
- if (fs23.existsSync(path34.join(cwd, "go.mod"))) {
53396
+ if (fs24.existsSync(path35.join(cwd, "go.mod"))) {
52984
53397
  ecosystems.push("go");
52985
53398
  }
52986
53399
  try {
52987
- const files = fs23.readdirSync(cwd);
53400
+ const files = fs24.readdirSync(cwd);
52988
53401
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
52989
53402
  ecosystems.push("dotnet");
52990
53403
  }
52991
53404
  } catch {}
52992
- if (fs23.existsSync(path34.join(cwd, "Gemfile")) || fs23.existsSync(path34.join(cwd, "Gemfile.lock"))) {
53405
+ if (fs24.existsSync(path35.join(cwd, "Gemfile")) || fs24.existsSync(path35.join(cwd, "Gemfile.lock"))) {
52993
53406
  ecosystems.push("ruby");
52994
53407
  }
52995
- if (fs23.existsSync(path34.join(cwd, "pubspec.yaml"))) {
53408
+ if (fs24.existsSync(path35.join(cwd, "pubspec.yaml"))) {
52996
53409
  ecosystems.push("dart");
52997
53410
  }
52998
53411
  return ecosystems;
@@ -53005,7 +53418,7 @@ async function runNpmAudit(directory) {
53005
53418
  stderr: "pipe",
53006
53419
  cwd: directory
53007
53420
  });
53008
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
53421
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53009
53422
  const result = await Promise.race([
53010
53423
  Promise.all([
53011
53424
  new Response(proc.stdout).text(),
@@ -53128,7 +53541,7 @@ async function runPipAudit(directory) {
53128
53541
  stderr: "pipe",
53129
53542
  cwd: directory
53130
53543
  });
53131
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
53544
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53132
53545
  const result = await Promise.race([
53133
53546
  Promise.all([
53134
53547
  new Response(proc.stdout).text(),
@@ -53259,7 +53672,7 @@ async function runCargoAudit(directory) {
53259
53672
  stderr: "pipe",
53260
53673
  cwd: directory
53261
53674
  });
53262
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
53675
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53263
53676
  const result = await Promise.race([
53264
53677
  Promise.all([
53265
53678
  new Response(proc.stdout).text(),
@@ -53386,7 +53799,7 @@ async function runGoAudit(directory) {
53386
53799
  stderr: "pipe",
53387
53800
  cwd: directory
53388
53801
  });
53389
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
53802
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53390
53803
  const result = await Promise.race([
53391
53804
  Promise.all([
53392
53805
  new Response(proc.stdout).text(),
@@ -53522,7 +53935,7 @@ async function runDotnetAudit(directory) {
53522
53935
  stderr: "pipe",
53523
53936
  cwd: directory
53524
53937
  });
53525
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
53938
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53526
53939
  const result = await Promise.race([
53527
53940
  Promise.all([
53528
53941
  new Response(proc.stdout).text(),
@@ -53641,7 +54054,7 @@ async function runBundleAudit(directory) {
53641
54054
  stderr: "pipe",
53642
54055
  cwd: directory
53643
54056
  });
53644
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
54057
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53645
54058
  const result = await Promise.race([
53646
54059
  Promise.all([
53647
54060
  new Response(proc.stdout).text(),
@@ -53788,7 +54201,7 @@ async function runDartAudit(directory) {
53788
54201
  stderr: "pipe",
53789
54202
  cwd: directory
53790
54203
  });
53791
- const timeoutPromise = new Promise((resolve12) => setTimeout(() => resolve12("timeout"), AUDIT_TIMEOUT_MS));
54204
+ const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
53792
54205
  const result = await Promise.race([
53793
54206
  Promise.all([
53794
54207
  new Response(proc.stdout).text(),
@@ -54053,8 +54466,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
54053
54466
  ]);
54054
54467
  // src/tools/pre-check-batch.ts
54055
54468
  init_dist();
54056
- import * as fs26 from "fs";
54057
- import * as path37 from "path";
54469
+ import * as fs27 from "fs";
54470
+ import * as path38 from "path";
54058
54471
 
54059
54472
  // node_modules/yocto-queue/index.js
54060
54473
  class Node2 {
@@ -54145,26 +54558,26 @@ function pLimit(concurrency) {
54145
54558
  activeCount--;
54146
54559
  resumeNext();
54147
54560
  };
54148
- const run2 = async (function_, resolve12, arguments_2) => {
54561
+ const run2 = async (function_, resolve13, arguments_2) => {
54149
54562
  const result = (async () => function_(...arguments_2))();
54150
- resolve12(result);
54563
+ resolve13(result);
54151
54564
  try {
54152
54565
  await result;
54153
54566
  } catch {}
54154
54567
  next();
54155
54568
  };
54156
- const enqueue = (function_, resolve12, reject, arguments_2) => {
54569
+ const enqueue = (function_, resolve13, reject, arguments_2) => {
54157
54570
  const queueItem = { reject };
54158
54571
  new Promise((internalResolve) => {
54159
54572
  queueItem.run = internalResolve;
54160
54573
  queue.enqueue(queueItem);
54161
- }).then(run2.bind(undefined, function_, resolve12, arguments_2));
54574
+ }).then(run2.bind(undefined, function_, resolve13, arguments_2));
54162
54575
  if (activeCount < concurrency) {
54163
54576
  resumeNext();
54164
54577
  }
54165
54578
  };
54166
- const generator = (function_, ...arguments_2) => new Promise((resolve12, reject) => {
54167
- enqueue(function_, resolve12, reject, arguments_2);
54579
+ const generator = (function_, ...arguments_2) => new Promise((resolve13, reject) => {
54580
+ enqueue(function_, resolve13, reject, arguments_2);
54168
54581
  });
54169
54582
  Object.defineProperties(generator, {
54170
54583
  activeCount: {
@@ -54221,8 +54634,8 @@ init_lint();
54221
54634
  init_manager();
54222
54635
 
54223
54636
  // src/quality/metrics.ts
54224
- import * as fs24 from "fs";
54225
- import * as path35 from "path";
54637
+ import * as fs25 from "fs";
54638
+ import * as path36 from "path";
54226
54639
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
54227
54640
  var MIN_DUPLICATION_LINES = 10;
54228
54641
  function estimateCyclomaticComplexity(content) {
@@ -54260,11 +54673,11 @@ function estimateCyclomaticComplexity(content) {
54260
54673
  }
54261
54674
  function getComplexityForFile2(filePath) {
54262
54675
  try {
54263
- const stat2 = fs24.statSync(filePath);
54676
+ const stat2 = fs25.statSync(filePath);
54264
54677
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
54265
54678
  return null;
54266
54679
  }
54267
- const content = fs24.readFileSync(filePath, "utf-8");
54680
+ const content = fs25.readFileSync(filePath, "utf-8");
54268
54681
  return estimateCyclomaticComplexity(content);
54269
54682
  } catch {
54270
54683
  return null;
@@ -54274,8 +54687,8 @@ async function computeComplexityDelta(files, workingDir) {
54274
54687
  let totalComplexity = 0;
54275
54688
  const analyzedFiles = [];
54276
54689
  for (const file3 of files) {
54277
- const fullPath = path35.isAbsolute(file3) ? file3 : path35.join(workingDir, file3);
54278
- if (!fs24.existsSync(fullPath)) {
54690
+ const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
54691
+ if (!fs25.existsSync(fullPath)) {
54279
54692
  continue;
54280
54693
  }
54281
54694
  const complexity = getComplexityForFile2(fullPath);
@@ -54396,8 +54809,8 @@ function countGoExports(content) {
54396
54809
  }
54397
54810
  function getExportCountForFile(filePath) {
54398
54811
  try {
54399
- const content = fs24.readFileSync(filePath, "utf-8");
54400
- const ext = path35.extname(filePath).toLowerCase();
54812
+ const content = fs25.readFileSync(filePath, "utf-8");
54813
+ const ext = path36.extname(filePath).toLowerCase();
54401
54814
  switch (ext) {
54402
54815
  case ".ts":
54403
54816
  case ".tsx":
@@ -54423,8 +54836,8 @@ async function computePublicApiDelta(files, workingDir) {
54423
54836
  let totalExports = 0;
54424
54837
  const analyzedFiles = [];
54425
54838
  for (const file3 of files) {
54426
- const fullPath = path35.isAbsolute(file3) ? file3 : path35.join(workingDir, file3);
54427
- if (!fs24.existsSync(fullPath)) {
54839
+ const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
54840
+ if (!fs25.existsSync(fullPath)) {
54428
54841
  continue;
54429
54842
  }
54430
54843
  const exports = getExportCountForFile(fullPath);
@@ -54457,16 +54870,16 @@ async function computeDuplicationRatio(files, workingDir) {
54457
54870
  let duplicateLines = 0;
54458
54871
  const analyzedFiles = [];
54459
54872
  for (const file3 of files) {
54460
- const fullPath = path35.isAbsolute(file3) ? file3 : path35.join(workingDir, file3);
54461
- if (!fs24.existsSync(fullPath)) {
54873
+ const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
54874
+ if (!fs25.existsSync(fullPath)) {
54462
54875
  continue;
54463
54876
  }
54464
54877
  try {
54465
- const stat2 = fs24.statSync(fullPath);
54878
+ const stat2 = fs25.statSync(fullPath);
54466
54879
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
54467
54880
  continue;
54468
54881
  }
54469
- const content = fs24.readFileSync(fullPath, "utf-8");
54882
+ const content = fs25.readFileSync(fullPath, "utf-8");
54470
54883
  const lines = content.split(`
54471
54884
  `).filter((line) => line.trim().length > 0);
54472
54885
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -54490,8 +54903,8 @@ function countCodeLines(content) {
54490
54903
  return lines.length;
54491
54904
  }
54492
54905
  function isTestFile(filePath) {
54493
- const basename8 = path35.basename(filePath);
54494
- const _ext = path35.extname(filePath).toLowerCase();
54906
+ const basename8 = path36.basename(filePath);
54907
+ const _ext = path36.extname(filePath).toLowerCase();
54495
54908
  const testPatterns = [
54496
54909
  ".test.",
54497
54910
  ".spec.",
@@ -54572,8 +54985,8 @@ function matchGlobSegment(globSegments, pathSegments) {
54572
54985
  }
54573
54986
  return gIndex === globSegments.length && pIndex === pathSegments.length;
54574
54987
  }
54575
- function matchesGlobSegment(path36, glob) {
54576
- const normalizedPath = path36.replace(/\\/g, "/");
54988
+ function matchesGlobSegment(path37, glob) {
54989
+ const normalizedPath = path37.replace(/\\/g, "/");
54577
54990
  const normalizedGlob = glob.replace(/\\/g, "/");
54578
54991
  if (normalizedPath.includes("//")) {
54579
54992
  return false;
@@ -54604,8 +55017,8 @@ function simpleGlobToRegex(glob) {
54604
55017
  function hasGlobstar(glob) {
54605
55018
  return glob.includes("**");
54606
55019
  }
54607
- function globMatches(path36, glob) {
54608
- const normalizedPath = path36.replace(/\\/g, "/");
55020
+ function globMatches(path37, glob) {
55021
+ const normalizedPath = path37.replace(/\\/g, "/");
54609
55022
  if (!glob || glob === "") {
54610
55023
  if (normalizedPath.includes("//")) {
54611
55024
  return false;
@@ -54641,31 +55054,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
54641
55054
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
54642
55055
  let testLines = 0;
54643
55056
  let codeLines = 0;
54644
- const srcDir = path35.join(workingDir, "src");
54645
- if (fs24.existsSync(srcDir)) {
55057
+ const srcDir = path36.join(workingDir, "src");
55058
+ if (fs25.existsSync(srcDir)) {
54646
55059
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
54647
55060
  codeLines += lines;
54648
55061
  });
54649
55062
  }
54650
55063
  const possibleSrcDirs = ["lib", "app", "source", "core"];
54651
55064
  for (const dir of possibleSrcDirs) {
54652
- const dirPath = path35.join(workingDir, dir);
54653
- if (fs24.existsSync(dirPath)) {
55065
+ const dirPath = path36.join(workingDir, dir);
55066
+ if (fs25.existsSync(dirPath)) {
54654
55067
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
54655
55068
  codeLines += lines;
54656
55069
  });
54657
55070
  }
54658
55071
  }
54659
- const testsDir = path35.join(workingDir, "tests");
54660
- if (fs24.existsSync(testsDir)) {
55072
+ const testsDir = path36.join(workingDir, "tests");
55073
+ if (fs25.existsSync(testsDir)) {
54661
55074
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
54662
55075
  testLines += lines;
54663
55076
  });
54664
55077
  }
54665
55078
  const possibleTestDirs = ["test", "__tests__", "specs"];
54666
55079
  for (const dir of possibleTestDirs) {
54667
- const dirPath = path35.join(workingDir, dir);
54668
- if (fs24.existsSync(dirPath) && dirPath !== testsDir) {
55080
+ const dirPath = path36.join(workingDir, dir);
55081
+ if (fs25.existsSync(dirPath) && dirPath !== testsDir) {
54669
55082
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
54670
55083
  testLines += lines;
54671
55084
  });
@@ -54677,9 +55090,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
54677
55090
  }
54678
55091
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
54679
55092
  try {
54680
- const entries = fs24.readdirSync(dirPath, { withFileTypes: true });
55093
+ const entries = fs25.readdirSync(dirPath, { withFileTypes: true });
54681
55094
  for (const entry of entries) {
54682
- const fullPath = path35.join(dirPath, entry.name);
55095
+ const fullPath = path36.join(dirPath, entry.name);
54683
55096
  if (entry.isDirectory()) {
54684
55097
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
54685
55098
  continue;
@@ -54687,7 +55100,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
54687
55100
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
54688
55101
  } else if (entry.isFile()) {
54689
55102
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
54690
- const ext = path35.extname(entry.name).toLowerCase();
55103
+ const ext = path36.extname(entry.name).toLowerCase();
54691
55104
  const validExts = [
54692
55105
  ".ts",
54693
55106
  ".tsx",
@@ -54723,7 +55136,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
54723
55136
  continue;
54724
55137
  }
54725
55138
  try {
54726
- const content = fs24.readFileSync(fullPath, "utf-8");
55139
+ const content = fs25.readFileSync(fullPath, "utf-8");
54727
55140
  const lines = countCodeLines(content);
54728
55141
  callback(lines);
54729
55142
  } catch {}
@@ -54937,8 +55350,8 @@ async function qualityBudget(input, directory) {
54937
55350
  init_dist();
54938
55351
  init_manager();
54939
55352
  init_detector();
54940
- import * as fs25 from "fs";
54941
- import * as path36 from "path";
55353
+ import * as fs26 from "fs";
55354
+ import * as path37 from "path";
54942
55355
  import { extname as extname9 } from "path";
54943
55356
 
54944
55357
  // src/sast/rules/c.ts
@@ -55686,7 +56099,7 @@ function mapSemgrepSeverity(severity) {
55686
56099
  }
55687
56100
  }
55688
56101
  async function executeWithTimeout(command, args2, options) {
55689
- return new Promise((resolve12) => {
56102
+ return new Promise((resolve13) => {
55690
56103
  const child = spawn(command, args2, {
55691
56104
  shell: false,
55692
56105
  cwd: options.cwd
@@ -55695,7 +56108,7 @@ async function executeWithTimeout(command, args2, options) {
55695
56108
  let stderr = "";
55696
56109
  const timeout = setTimeout(() => {
55697
56110
  child.kill("SIGTERM");
55698
- resolve12({
56111
+ resolve13({
55699
56112
  stdout,
55700
56113
  stderr: "Process timed out",
55701
56114
  exitCode: 124
@@ -55709,7 +56122,7 @@ async function executeWithTimeout(command, args2, options) {
55709
56122
  });
55710
56123
  child.on("close", (code) => {
55711
56124
  clearTimeout(timeout);
55712
- resolve12({
56125
+ resolve13({
55713
56126
  stdout,
55714
56127
  stderr,
55715
56128
  exitCode: code ?? 0
@@ -55717,7 +56130,7 @@ async function executeWithTimeout(command, args2, options) {
55717
56130
  });
55718
56131
  child.on("error", (err2) => {
55719
56132
  clearTimeout(timeout);
55720
- resolve12({
56133
+ resolve13({
55721
56134
  stdout,
55722
56135
  stderr: err2.message,
55723
56136
  exitCode: 1
@@ -55805,17 +56218,17 @@ var SEVERITY_ORDER = {
55805
56218
  };
55806
56219
  function shouldSkipFile(filePath) {
55807
56220
  try {
55808
- const stats = fs25.statSync(filePath);
56221
+ const stats = fs26.statSync(filePath);
55809
56222
  if (stats.size > MAX_FILE_SIZE_BYTES6) {
55810
56223
  return { skip: true, reason: "file too large" };
55811
56224
  }
55812
56225
  if (stats.size === 0) {
55813
56226
  return { skip: true, reason: "empty file" };
55814
56227
  }
55815
- const fd = fs25.openSync(filePath, "r");
56228
+ const fd = fs26.openSync(filePath, "r");
55816
56229
  const buffer = Buffer.alloc(8192);
55817
- const bytesRead = fs25.readSync(fd, buffer, 0, 8192, 0);
55818
- fs25.closeSync(fd);
56230
+ const bytesRead = fs26.readSync(fd, buffer, 0, 8192, 0);
56231
+ fs26.closeSync(fd);
55819
56232
  if (bytesRead > 0) {
55820
56233
  let nullCount = 0;
55821
56234
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -55854,7 +56267,7 @@ function countBySeverity(findings) {
55854
56267
  }
55855
56268
  function scanFileWithTierA(filePath, language) {
55856
56269
  try {
55857
- const content = fs25.readFileSync(filePath, "utf-8");
56270
+ const content = fs26.readFileSync(filePath, "utf-8");
55858
56271
  const findings = executeRulesSync(filePath, content, language);
55859
56272
  return findings.map((f) => ({
55860
56273
  rule_id: f.rule_id,
@@ -55901,8 +56314,8 @@ async function sastScan(input, directory, config3) {
55901
56314
  _filesSkipped++;
55902
56315
  continue;
55903
56316
  }
55904
- const resolvedPath = path36.isAbsolute(filePath) ? filePath : path36.resolve(directory, filePath);
55905
- if (!fs25.existsSync(resolvedPath)) {
56317
+ const resolvedPath = path37.isAbsolute(filePath) ? filePath : path37.resolve(directory, filePath);
56318
+ if (!fs26.existsSync(resolvedPath)) {
55906
56319
  _filesSkipped++;
55907
56320
  continue;
55908
56321
  }
@@ -56100,18 +56513,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
56100
56513
  let resolved;
56101
56514
  const isWinAbs = isWindowsAbsolutePath(inputPath);
56102
56515
  if (isWinAbs) {
56103
- resolved = path37.win32.resolve(inputPath);
56104
- } else if (path37.isAbsolute(inputPath)) {
56105
- resolved = path37.resolve(inputPath);
56516
+ resolved = path38.win32.resolve(inputPath);
56517
+ } else if (path38.isAbsolute(inputPath)) {
56518
+ resolved = path38.resolve(inputPath);
56106
56519
  } else {
56107
- resolved = path37.resolve(baseDir, inputPath);
56520
+ resolved = path38.resolve(baseDir, inputPath);
56108
56521
  }
56109
- const workspaceResolved = path37.resolve(workspaceDir);
56522
+ const workspaceResolved = path38.resolve(workspaceDir);
56110
56523
  let relative4;
56111
56524
  if (isWinAbs) {
56112
- relative4 = path37.win32.relative(workspaceResolved, resolved);
56525
+ relative4 = path38.win32.relative(workspaceResolved, resolved);
56113
56526
  } else {
56114
- relative4 = path37.relative(workspaceResolved, resolved);
56527
+ relative4 = path38.relative(workspaceResolved, resolved);
56115
56528
  }
56116
56529
  if (relative4.startsWith("..")) {
56117
56530
  return "path traversal detected";
@@ -56172,13 +56585,13 @@ async function runLintWrapped(files, directory, _config) {
56172
56585
  }
56173
56586
  async function runLintOnFiles(linter, files, workspaceDir) {
56174
56587
  const isWindows = process.platform === "win32";
56175
- const binDir = path37.join(workspaceDir, "node_modules", ".bin");
56588
+ const binDir = path38.join(workspaceDir, "node_modules", ".bin");
56176
56589
  const validatedFiles = [];
56177
56590
  for (const file3 of files) {
56178
56591
  if (typeof file3 !== "string") {
56179
56592
  continue;
56180
56593
  }
56181
- const resolvedPath = path37.resolve(file3);
56594
+ const resolvedPath = path38.resolve(file3);
56182
56595
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
56183
56596
  if (validationError) {
56184
56597
  continue;
@@ -56196,10 +56609,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
56196
56609
  }
56197
56610
  let command;
56198
56611
  if (linter === "biome") {
56199
- const biomeBin = isWindows ? path37.join(binDir, "biome.EXE") : path37.join(binDir, "biome");
56612
+ const biomeBin = isWindows ? path38.join(binDir, "biome.EXE") : path38.join(binDir, "biome");
56200
56613
  command = [biomeBin, "check", ...validatedFiles];
56201
56614
  } else {
56202
- const eslintBin = isWindows ? path37.join(binDir, "eslint.cmd") : path37.join(binDir, "eslint");
56615
+ const eslintBin = isWindows ? path38.join(binDir, "eslint.cmd") : path38.join(binDir, "eslint");
56203
56616
  command = [eslintBin, ...validatedFiles];
56204
56617
  }
56205
56618
  try {
@@ -56336,7 +56749,7 @@ async function runSecretscanWithFiles(files, directory) {
56336
56749
  skippedFiles++;
56337
56750
  continue;
56338
56751
  }
56339
- const resolvedPath = path37.resolve(file3);
56752
+ const resolvedPath = path38.resolve(file3);
56340
56753
  const validationError = validatePath(resolvedPath, directory, directory);
56341
56754
  if (validationError) {
56342
56755
  skippedFiles++;
@@ -56354,14 +56767,14 @@ async function runSecretscanWithFiles(files, directory) {
56354
56767
  };
56355
56768
  }
56356
56769
  for (const file3 of validatedFiles) {
56357
- const ext = path37.extname(file3).toLowerCase();
56770
+ const ext = path38.extname(file3).toLowerCase();
56358
56771
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
56359
56772
  skippedFiles++;
56360
56773
  continue;
56361
56774
  }
56362
56775
  let stat2;
56363
56776
  try {
56364
- stat2 = fs26.statSync(file3);
56777
+ stat2 = fs27.statSync(file3);
56365
56778
  } catch {
56366
56779
  skippedFiles++;
56367
56780
  continue;
@@ -56372,7 +56785,7 @@ async function runSecretscanWithFiles(files, directory) {
56372
56785
  }
56373
56786
  let content;
56374
56787
  try {
56375
- const buffer = fs26.readFileSync(file3);
56788
+ const buffer = fs27.readFileSync(file3);
56376
56789
  if (buffer.includes(0)) {
56377
56790
  skippedFiles++;
56378
56791
  continue;
@@ -56513,7 +56926,7 @@ async function runPreCheckBatch(input, workspaceDir) {
56513
56926
  warn(`pre_check_batch: Invalid file path: ${file3}`);
56514
56927
  continue;
56515
56928
  }
56516
- changedFiles.push(path37.resolve(directory, file3));
56929
+ changedFiles.push(path38.resolve(directory, file3));
56517
56930
  }
56518
56931
  if (changedFiles.length === 0) {
56519
56932
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -56664,7 +57077,7 @@ var pre_check_batch = createSwarmTool({
56664
57077
  };
56665
57078
  return JSON.stringify(errorResult, null, 2);
56666
57079
  }
56667
- const resolvedDirectory = path37.resolve(typedArgs.directory);
57080
+ const resolvedDirectory = path38.resolve(typedArgs.directory);
56668
57081
  const workspaceAnchor = resolvedDirectory;
56669
57082
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
56670
57083
  if (dirError) {
@@ -56735,7 +57148,7 @@ var retrieve_summary = tool({
56735
57148
  init_tool();
56736
57149
  init_manager2();
56737
57150
  init_create_tool();
56738
- import * as path38 from "path";
57151
+ import * as path39 from "path";
56739
57152
  function detectPlaceholderContent(args2) {
56740
57153
  const issues = [];
56741
57154
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -56819,7 +57232,7 @@ async function executeSavePlan(args2, fallbackDir) {
56819
57232
  return {
56820
57233
  success: true,
56821
57234
  message: "Plan saved successfully",
56822
- plan_path: path38.join(dir, ".swarm", "plan.json"),
57235
+ plan_path: path39.join(dir, ".swarm", "plan.json"),
56823
57236
  phases_count: plan.phases.length,
56824
57237
  tasks_count: tasksCount
56825
57238
  };
@@ -56856,8 +57269,8 @@ var save_plan = createSwarmTool({
56856
57269
  // src/tools/sbom-generate.ts
56857
57270
  init_dist();
56858
57271
  init_manager();
56859
- import * as fs27 from "fs";
56860
- import * as path39 from "path";
57272
+ import * as fs28 from "fs";
57273
+ import * as path40 from "path";
56861
57274
 
56862
57275
  // src/sbom/detectors/dart.ts
56863
57276
  function parsePubspecLock(content) {
@@ -57703,9 +58116,9 @@ function findManifestFiles(rootDir) {
57703
58116
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
57704
58117
  function searchDir(dir) {
57705
58118
  try {
57706
- const entries = fs27.readdirSync(dir, { withFileTypes: true });
58119
+ const entries = fs28.readdirSync(dir, { withFileTypes: true });
57707
58120
  for (const entry of entries) {
57708
- const fullPath = path39.join(dir, entry.name);
58121
+ const fullPath = path40.join(dir, entry.name);
57709
58122
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
57710
58123
  continue;
57711
58124
  }
@@ -57715,7 +58128,7 @@ function findManifestFiles(rootDir) {
57715
58128
  for (const pattern of patterns) {
57716
58129
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
57717
58130
  if (new RegExp(regex, "i").test(entry.name)) {
57718
- manifestFiles.push(path39.relative(cwd, fullPath));
58131
+ manifestFiles.push(path40.relative(cwd, fullPath));
57719
58132
  break;
57720
58133
  }
57721
58134
  }
@@ -57732,14 +58145,14 @@ function findManifestFilesInDirs(directories, workingDir) {
57732
58145
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
57733
58146
  for (const dir of directories) {
57734
58147
  try {
57735
- const entries = fs27.readdirSync(dir, { withFileTypes: true });
58148
+ const entries = fs28.readdirSync(dir, { withFileTypes: true });
57736
58149
  for (const entry of entries) {
57737
- const fullPath = path39.join(dir, entry.name);
58150
+ const fullPath = path40.join(dir, entry.name);
57738
58151
  if (entry.isFile()) {
57739
58152
  for (const pattern of patterns) {
57740
58153
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
57741
58154
  if (new RegExp(regex, "i").test(entry.name)) {
57742
- found.push(path39.relative(workingDir, fullPath));
58155
+ found.push(path40.relative(workingDir, fullPath));
57743
58156
  break;
57744
58157
  }
57745
58158
  }
@@ -57752,11 +58165,11 @@ function findManifestFilesInDirs(directories, workingDir) {
57752
58165
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
57753
58166
  const dirs = new Set;
57754
58167
  for (const file3 of changedFiles) {
57755
- let currentDir = path39.dirname(file3);
58168
+ let currentDir = path40.dirname(file3);
57756
58169
  while (true) {
57757
- if (currentDir && currentDir !== "." && currentDir !== path39.sep) {
57758
- dirs.add(path39.join(workingDir, currentDir));
57759
- const parent = path39.dirname(currentDir);
58170
+ if (currentDir && currentDir !== "." && currentDir !== path40.sep) {
58171
+ dirs.add(path40.join(workingDir, currentDir));
58172
+ const parent = path40.dirname(currentDir);
57760
58173
  if (parent === currentDir)
57761
58174
  break;
57762
58175
  currentDir = parent;
@@ -57770,7 +58183,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
57770
58183
  }
57771
58184
  function ensureOutputDir(outputDir) {
57772
58185
  try {
57773
- fs27.mkdirSync(outputDir, { recursive: true });
58186
+ fs28.mkdirSync(outputDir, { recursive: true });
57774
58187
  } catch (error93) {
57775
58188
  if (!error93 || error93.code !== "EEXIST") {
57776
58189
  throw error93;
@@ -57862,11 +58275,11 @@ var sbom_generate = createSwarmTool({
57862
58275
  const processedFiles = [];
57863
58276
  for (const manifestFile of manifestFiles) {
57864
58277
  try {
57865
- const fullPath = path39.isAbsolute(manifestFile) ? manifestFile : path39.join(workingDir, manifestFile);
57866
- if (!fs27.existsSync(fullPath)) {
58278
+ const fullPath = path40.isAbsolute(manifestFile) ? manifestFile : path40.join(workingDir, manifestFile);
58279
+ if (!fs28.existsSync(fullPath)) {
57867
58280
  continue;
57868
58281
  }
57869
- const content = fs27.readFileSync(fullPath, "utf-8");
58282
+ const content = fs28.readFileSync(fullPath, "utf-8");
57870
58283
  const components = detectComponents(manifestFile, content);
57871
58284
  processedFiles.push(manifestFile);
57872
58285
  if (components.length > 0) {
@@ -57879,8 +58292,8 @@ var sbom_generate = createSwarmTool({
57879
58292
  const bom = generateCycloneDX(allComponents);
57880
58293
  const bomJson = serializeCycloneDX(bom);
57881
58294
  const filename = generateSbomFilename();
57882
- const outputPath = path39.join(outputDir, filename);
57883
- fs27.writeFileSync(outputPath, bomJson, "utf-8");
58295
+ const outputPath = path40.join(outputDir, filename);
58296
+ fs28.writeFileSync(outputPath, bomJson, "utf-8");
57884
58297
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
57885
58298
  try {
57886
58299
  const timestamp = new Date().toISOString();
@@ -57922,8 +58335,8 @@ var sbom_generate = createSwarmTool({
57922
58335
  // src/tools/schema-drift.ts
57923
58336
  init_dist();
57924
58337
  init_create_tool();
57925
- import * as fs28 from "fs";
57926
- import * as path40 from "path";
58338
+ import * as fs29 from "fs";
58339
+ import * as path41 from "path";
57927
58340
  var SPEC_CANDIDATES = [
57928
58341
  "openapi.json",
57929
58342
  "openapi.yaml",
@@ -57955,28 +58368,28 @@ function normalizePath2(p) {
57955
58368
  }
57956
58369
  function discoverSpecFile(cwd, specFileArg) {
57957
58370
  if (specFileArg) {
57958
- const resolvedPath = path40.resolve(cwd, specFileArg);
57959
- const normalizedCwd = cwd.endsWith(path40.sep) ? cwd : cwd + path40.sep;
58371
+ const resolvedPath = path41.resolve(cwd, specFileArg);
58372
+ const normalizedCwd = cwd.endsWith(path41.sep) ? cwd : cwd + path41.sep;
57960
58373
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
57961
58374
  throw new Error("Invalid spec_file: path traversal detected");
57962
58375
  }
57963
- const ext = path40.extname(resolvedPath).toLowerCase();
58376
+ const ext = path41.extname(resolvedPath).toLowerCase();
57964
58377
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
57965
58378
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
57966
58379
  }
57967
- const stats = fs28.statSync(resolvedPath);
58380
+ const stats = fs29.statSync(resolvedPath);
57968
58381
  if (stats.size > MAX_SPEC_SIZE) {
57969
58382
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
57970
58383
  }
57971
- if (!fs28.existsSync(resolvedPath)) {
58384
+ if (!fs29.existsSync(resolvedPath)) {
57972
58385
  throw new Error(`Spec file not found: ${resolvedPath}`);
57973
58386
  }
57974
58387
  return resolvedPath;
57975
58388
  }
57976
58389
  for (const candidate of SPEC_CANDIDATES) {
57977
- const candidatePath = path40.resolve(cwd, candidate);
57978
- if (fs28.existsSync(candidatePath)) {
57979
- const stats = fs28.statSync(candidatePath);
58390
+ const candidatePath = path41.resolve(cwd, candidate);
58391
+ if (fs29.existsSync(candidatePath)) {
58392
+ const stats = fs29.statSync(candidatePath);
57980
58393
  if (stats.size <= MAX_SPEC_SIZE) {
57981
58394
  return candidatePath;
57982
58395
  }
@@ -57985,8 +58398,8 @@ function discoverSpecFile(cwd, specFileArg) {
57985
58398
  return null;
57986
58399
  }
57987
58400
  function parseSpec(specFile) {
57988
- const content = fs28.readFileSync(specFile, "utf-8");
57989
- const ext = path40.extname(specFile).toLowerCase();
58401
+ const content = fs29.readFileSync(specFile, "utf-8");
58402
+ const ext = path41.extname(specFile).toLowerCase();
57990
58403
  if (ext === ".json") {
57991
58404
  return parseJsonSpec(content);
57992
58405
  }
@@ -58052,12 +58465,12 @@ function extractRoutes(cwd) {
58052
58465
  function walkDir(dir) {
58053
58466
  let entries;
58054
58467
  try {
58055
- entries = fs28.readdirSync(dir, { withFileTypes: true });
58468
+ entries = fs29.readdirSync(dir, { withFileTypes: true });
58056
58469
  } catch {
58057
58470
  return;
58058
58471
  }
58059
58472
  for (const entry of entries) {
58060
- const fullPath = path40.join(dir, entry.name);
58473
+ const fullPath = path41.join(dir, entry.name);
58061
58474
  if (entry.isSymbolicLink()) {
58062
58475
  continue;
58063
58476
  }
@@ -58067,7 +58480,7 @@ function extractRoutes(cwd) {
58067
58480
  }
58068
58481
  walkDir(fullPath);
58069
58482
  } else if (entry.isFile()) {
58070
- const ext = path40.extname(entry.name).toLowerCase();
58483
+ const ext = path41.extname(entry.name).toLowerCase();
58071
58484
  const baseName = entry.name.toLowerCase();
58072
58485
  if (![".ts", ".js", ".mjs"].includes(ext)) {
58073
58486
  continue;
@@ -58085,7 +58498,7 @@ function extractRoutes(cwd) {
58085
58498
  }
58086
58499
  function extractRoutesFromFile(filePath) {
58087
58500
  const routes = [];
58088
- const content = fs28.readFileSync(filePath, "utf-8");
58501
+ const content = fs29.readFileSync(filePath, "utf-8");
58089
58502
  const lines = content.split(/\r?\n/);
58090
58503
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
58091
58504
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -58236,8 +58649,8 @@ init_secretscan();
58236
58649
  // src/tools/symbols.ts
58237
58650
  init_tool();
58238
58651
  init_create_tool();
58239
- import * as fs29 from "fs";
58240
- import * as path41 from "path";
58652
+ import * as fs30 from "fs";
58653
+ import * as path42 from "path";
58241
58654
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
58242
58655
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
58243
58656
  function containsControlCharacters(str) {
@@ -58266,11 +58679,11 @@ function containsWindowsAttacks(str) {
58266
58679
  }
58267
58680
  function isPathInWorkspace(filePath, workspace) {
58268
58681
  try {
58269
- const resolvedPath = path41.resolve(workspace, filePath);
58270
- const realWorkspace = fs29.realpathSync(workspace);
58271
- const realResolvedPath = fs29.realpathSync(resolvedPath);
58272
- const relativePath = path41.relative(realWorkspace, realResolvedPath);
58273
- if (relativePath.startsWith("..") || path41.isAbsolute(relativePath)) {
58682
+ const resolvedPath = path42.resolve(workspace, filePath);
58683
+ const realWorkspace = fs30.realpathSync(workspace);
58684
+ const realResolvedPath = fs30.realpathSync(resolvedPath);
58685
+ const relativePath = path42.relative(realWorkspace, realResolvedPath);
58686
+ if (relativePath.startsWith("..") || path42.isAbsolute(relativePath)) {
58274
58687
  return false;
58275
58688
  }
58276
58689
  return true;
@@ -58282,17 +58695,17 @@ function validatePathForRead(filePath, workspace) {
58282
58695
  return isPathInWorkspace(filePath, workspace);
58283
58696
  }
58284
58697
  function extractTSSymbols(filePath, cwd) {
58285
- const fullPath = path41.join(cwd, filePath);
58698
+ const fullPath = path42.join(cwd, filePath);
58286
58699
  if (!validatePathForRead(fullPath, cwd)) {
58287
58700
  return [];
58288
58701
  }
58289
58702
  let content;
58290
58703
  try {
58291
- const stats = fs29.statSync(fullPath);
58704
+ const stats = fs30.statSync(fullPath);
58292
58705
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
58293
58706
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
58294
58707
  }
58295
- content = fs29.readFileSync(fullPath, "utf-8");
58708
+ content = fs30.readFileSync(fullPath, "utf-8");
58296
58709
  } catch {
58297
58710
  return [];
58298
58711
  }
@@ -58434,17 +58847,17 @@ function extractTSSymbols(filePath, cwd) {
58434
58847
  });
58435
58848
  }
58436
58849
  function extractPythonSymbols(filePath, cwd) {
58437
- const fullPath = path41.join(cwd, filePath);
58850
+ const fullPath = path42.join(cwd, filePath);
58438
58851
  if (!validatePathForRead(fullPath, cwd)) {
58439
58852
  return [];
58440
58853
  }
58441
58854
  let content;
58442
58855
  try {
58443
- const stats = fs29.statSync(fullPath);
58856
+ const stats = fs30.statSync(fullPath);
58444
58857
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
58445
58858
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
58446
58859
  }
58447
- content = fs29.readFileSync(fullPath, "utf-8");
58860
+ content = fs30.readFileSync(fullPath, "utf-8");
58448
58861
  } catch {
58449
58862
  return [];
58450
58863
  }
@@ -58517,7 +58930,7 @@ var symbols = createSwarmTool({
58517
58930
  }, null, 2);
58518
58931
  }
58519
58932
  const cwd = directory;
58520
- const ext = path41.extname(file3);
58933
+ const ext = path42.extname(file3);
58521
58934
  if (containsControlCharacters(file3)) {
58522
58935
  return JSON.stringify({
58523
58936
  file: file3,
@@ -58584,128 +58997,6 @@ var MAX_FILE_SIZE2 = 5 * 1024 * 1024;
58584
58997
  // src/tools/index.ts
58585
58998
  init_test_runner();
58586
58999
 
58587
- // src/tools/update-task-status.ts
58588
- init_tool();
58589
- init_manager2();
58590
- init_create_tool();
58591
- import * as fs30 from "fs";
58592
- import * as path42 from "path";
58593
- var VALID_STATUSES = [
58594
- "pending",
58595
- "in_progress",
58596
- "completed",
58597
- "blocked"
58598
- ];
58599
- function validateStatus(status) {
58600
- if (!VALID_STATUSES.includes(status)) {
58601
- return `Invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`;
58602
- }
58603
- return;
58604
- }
58605
- function validateTaskId(taskId) {
58606
- const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
58607
- if (!taskIdPattern.test(taskId)) {
58608
- return `Invalid task_id "${taskId}". Must match pattern N.M or N.M.P (e.g., "1.1", "1.2.3")`;
58609
- }
58610
- return;
58611
- }
58612
- async function executeUpdateTaskStatus(args2, fallbackDir) {
58613
- const statusError = validateStatus(args2.status);
58614
- if (statusError) {
58615
- return {
58616
- success: false,
58617
- message: "Validation failed",
58618
- errors: [statusError]
58619
- };
58620
- }
58621
- const taskIdError = validateTaskId(args2.task_id);
58622
- if (taskIdError) {
58623
- return {
58624
- success: false,
58625
- message: "Validation failed",
58626
- errors: [taskIdError]
58627
- };
58628
- }
58629
- let normalizedDir;
58630
- if (args2.working_directory != null) {
58631
- if (args2.working_directory.includes("\x00")) {
58632
- return {
58633
- success: false,
58634
- message: "Invalid working_directory: null bytes are not allowed"
58635
- };
58636
- }
58637
- if (process.platform === "win32") {
58638
- const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
58639
- if (devicePathPattern.test(args2.working_directory)) {
58640
- return {
58641
- success: false,
58642
- message: "Invalid working_directory: Windows device paths are not allowed"
58643
- };
58644
- }
58645
- }
58646
- normalizedDir = path42.normalize(args2.working_directory);
58647
- const pathParts = normalizedDir.split(path42.sep);
58648
- if (pathParts.includes("..")) {
58649
- return {
58650
- success: false,
58651
- message: "Invalid working_directory: path traversal sequences (..) are not allowed",
58652
- errors: [
58653
- "Invalid working_directory: path traversal sequences (..) are not allowed"
58654
- ]
58655
- };
58656
- }
58657
- const resolvedDir = path42.resolve(normalizedDir);
58658
- try {
58659
- const realPath = fs30.realpathSync(resolvedDir);
58660
- const planPath = path42.join(realPath, ".swarm", "plan.json");
58661
- if (!fs30.existsSync(planPath)) {
58662
- return {
58663
- success: false,
58664
- message: `Invalid working_directory: plan not found in "${realPath}"`,
58665
- errors: [
58666
- `Invalid working_directory: plan not found in "${realPath}"`
58667
- ]
58668
- };
58669
- }
58670
- } catch {
58671
- return {
58672
- success: false,
58673
- message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
58674
- errors: [
58675
- `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
58676
- ]
58677
- };
58678
- }
58679
- }
58680
- const directory = normalizedDir ?? fallbackDir ?? process.cwd();
58681
- try {
58682
- const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
58683
- return {
58684
- success: true,
58685
- message: "Task status updated successfully",
58686
- task_id: args2.task_id,
58687
- new_status: args2.status,
58688
- current_phase: updatedPlan.current_phase
58689
- };
58690
- } catch (error93) {
58691
- return {
58692
- success: false,
58693
- message: "Failed to update task status",
58694
- errors: [String(error93)]
58695
- };
58696
- }
58697
- }
58698
- var update_task_status = createSwarmTool({
58699
- description: "Update the status of a specific task in the implementation plan. " + "Task status can be one of: pending, in_progress, completed, blocked.",
58700
- args: {
58701
- task_id: tool.schema.string().min(1).regex(/^\d+\.\d+(\.\d+)*$/, "Task ID must be in N.M or N.M.P format").describe('Task ID in N.M format, e.g. "1.1", "1.2.3"'),
58702
- status: tool.schema.enum(["pending", "in_progress", "completed", "blocked"]).describe("New status for the task: pending, in_progress, completed, or blocked"),
58703
- working_directory: tool.schema.string().optional().describe("Working directory where the plan is located")
58704
- },
58705
- execute: async (args2, _directory) => {
58706
- return JSON.stringify(await executeUpdateTaskStatus(args2, _directory), null, 2);
58707
- }
58708
- });
58709
59000
  // src/tools/todo-extract.ts
58710
59001
  init_dist();
58711
59002
  init_create_tool();
@@ -58968,6 +59259,157 @@ var todo_extract = createSwarmTool({
58968
59259
  return JSON.stringify(result, null, 2);
58969
59260
  }
58970
59261
  });
59262
+ // src/tools/update-task-status.ts
59263
+ init_tool();
59264
+ init_manager2();
59265
+ import * as fs32 from "fs";
59266
+ import * as path44 from "path";
59267
+ init_create_tool();
59268
+ var VALID_STATUSES = [
59269
+ "pending",
59270
+ "in_progress",
59271
+ "completed",
59272
+ "blocked"
59273
+ ];
59274
+ function validateStatus(status) {
59275
+ if (!VALID_STATUSES.includes(status)) {
59276
+ return `Invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`;
59277
+ }
59278
+ return;
59279
+ }
59280
+ function validateTaskId(taskId) {
59281
+ const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
59282
+ if (!taskIdPattern.test(taskId)) {
59283
+ return `Invalid task_id "${taskId}". Must match pattern N.M or N.M.P (e.g., "1.1", "1.2.3")`;
59284
+ }
59285
+ return;
59286
+ }
59287
+ function checkReviewerGate(taskId) {
59288
+ try {
59289
+ if (swarmState.agentSessions.size === 0) {
59290
+ return { blocked: false, reason: "" };
59291
+ }
59292
+ for (const [_sessionId, session] of swarmState.agentSessions) {
59293
+ const state = getTaskState(session, taskId);
59294
+ if (state === "tests_run" || state === "complete") {
59295
+ return { blocked: false, reason: "" };
59296
+ }
59297
+ }
59298
+ return {
59299
+ blocked: true,
59300
+ reason: `Task ${taskId} has not passed QA gates (state machine requires tests_run or complete, current state indicates gates not yet passed). Call mega_reviewer and mega_test_engineer before marking task as completed.`
59301
+ };
59302
+ } catch {
59303
+ return { blocked: false, reason: "" };
59304
+ }
59305
+ }
59306
+ async function executeUpdateTaskStatus(args2, fallbackDir) {
59307
+ const statusError = validateStatus(args2.status);
59308
+ if (statusError) {
59309
+ return {
59310
+ success: false,
59311
+ message: "Validation failed",
59312
+ errors: [statusError]
59313
+ };
59314
+ }
59315
+ const taskIdError = validateTaskId(args2.task_id);
59316
+ if (taskIdError) {
59317
+ return {
59318
+ success: false,
59319
+ message: "Validation failed",
59320
+ errors: [taskIdError]
59321
+ };
59322
+ }
59323
+ if (args2.status === "completed") {
59324
+ const reviewerCheck = checkReviewerGate(args2.task_id);
59325
+ if (reviewerCheck.blocked) {
59326
+ return {
59327
+ success: false,
59328
+ message: "Gate check failed: reviewer delegation required before marking task as completed",
59329
+ errors: [reviewerCheck.reason]
59330
+ };
59331
+ }
59332
+ }
59333
+ let normalizedDir;
59334
+ if (args2.working_directory != null) {
59335
+ if (args2.working_directory.includes("\x00")) {
59336
+ return {
59337
+ success: false,
59338
+ message: "Invalid working_directory: null bytes are not allowed"
59339
+ };
59340
+ }
59341
+ if (process.platform === "win32") {
59342
+ const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
59343
+ if (devicePathPattern.test(args2.working_directory)) {
59344
+ return {
59345
+ success: false,
59346
+ message: "Invalid working_directory: Windows device paths are not allowed"
59347
+ };
59348
+ }
59349
+ }
59350
+ normalizedDir = path44.normalize(args2.working_directory);
59351
+ const pathParts = normalizedDir.split(path44.sep);
59352
+ if (pathParts.includes("..")) {
59353
+ return {
59354
+ success: false,
59355
+ message: "Invalid working_directory: path traversal sequences (..) are not allowed",
59356
+ errors: [
59357
+ "Invalid working_directory: path traversal sequences (..) are not allowed"
59358
+ ]
59359
+ };
59360
+ }
59361
+ const resolvedDir = path44.resolve(normalizedDir);
59362
+ try {
59363
+ const realPath = fs32.realpathSync(resolvedDir);
59364
+ const planPath = path44.join(realPath, ".swarm", "plan.json");
59365
+ if (!fs32.existsSync(planPath)) {
59366
+ return {
59367
+ success: false,
59368
+ message: `Invalid working_directory: plan not found in "${realPath}"`,
59369
+ errors: [
59370
+ `Invalid working_directory: plan not found in "${realPath}"`
59371
+ ]
59372
+ };
59373
+ }
59374
+ } catch {
59375
+ return {
59376
+ success: false,
59377
+ message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
59378
+ errors: [
59379
+ `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
59380
+ ]
59381
+ };
59382
+ }
59383
+ }
59384
+ const directory = normalizedDir ?? fallbackDir ?? process.cwd();
59385
+ try {
59386
+ const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
59387
+ return {
59388
+ success: true,
59389
+ message: "Task status updated successfully",
59390
+ task_id: args2.task_id,
59391
+ new_status: args2.status,
59392
+ current_phase: updatedPlan.current_phase
59393
+ };
59394
+ } catch (error93) {
59395
+ return {
59396
+ success: false,
59397
+ message: "Failed to update task status",
59398
+ errors: [String(error93)]
59399
+ };
59400
+ }
59401
+ }
59402
+ var update_task_status = createSwarmTool({
59403
+ description: "Update the status of a specific task in the implementation plan. " + "Task status can be one of: pending, in_progress, completed, blocked.",
59404
+ args: {
59405
+ task_id: tool.schema.string().min(1).regex(/^\d+\.\d+(\.\d+)*$/, "Task ID must be in N.M or N.M.P format").describe('Task ID in N.M format, e.g. "1.1", "1.2.3"'),
59406
+ status: tool.schema.enum(["pending", "in_progress", "completed", "blocked"]).describe("New status for the task: pending, in_progress, completed, or blocked"),
59407
+ working_directory: tool.schema.string().optional().describe("Working directory where the plan is located")
59408
+ },
59409
+ execute: async (args2, _directory) => {
59410
+ return JSON.stringify(await executeUpdateTaskStatus(args2, _directory), null, 2);
59411
+ }
59412
+ });
58971
59413
  // src/index.ts
58972
59414
  init_utils();
58973
59415
 
@@ -59051,7 +59493,7 @@ var OpenCodeSwarm = async (ctx) => {
59051
59493
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
59052
59494
  preflightTriggerManager = new PTM(automationConfig);
59053
59495
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
59054
- const swarmDir = path44.resolve(ctx.directory, ".swarm");
59496
+ const swarmDir = path45.resolve(ctx.directory, ".swarm");
59055
59497
  statusArtifact = new ASA(swarmDir);
59056
59498
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
59057
59499
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -59165,7 +59607,8 @@ var OpenCodeSwarm = async (ctx) => {
59165
59607
  test_runner,
59166
59608
  todo_extract,
59167
59609
  update_task_status,
59168
- write_retro
59610
+ write_retro,
59611
+ declare_scope
59169
59612
  },
59170
59613
  config: async (opencodeConfig) => {
59171
59614
  if (!opencodeConfig.agent) {