opencode-swarm 6.20.3 → 6.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -2
- package/dist/background/plan-sync-worker.d.ts +5 -0
- package/dist/cli/index.js +17 -4
- package/dist/config/constants.d.ts +14 -0
- package/dist/index.js +984 -373
- package/dist/state.d.ts +42 -0
- package/dist/tools/declare-scope.d.ts +49 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/save-plan.d.ts +1 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14841,6 +14841,17 @@ ${markdown}`;
|
|
|
14841
14841
|
unlinkSync(mdTempPath);
|
|
14842
14842
|
} catch {}
|
|
14843
14843
|
}
|
|
14844
|
+
try {
|
|
14845
|
+
const markerPath = path4.join(swarmDir, ".plan-write-marker");
|
|
14846
|
+
const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
14847
|
+
const marker = JSON.stringify({
|
|
14848
|
+
source: "plan_manager",
|
|
14849
|
+
timestamp: new Date().toISOString(),
|
|
14850
|
+
phases_count: validated.phases.length,
|
|
14851
|
+
tasks_count: tasksCount
|
|
14852
|
+
});
|
|
14853
|
+
await Bun.write(markerPath, marker);
|
|
14854
|
+
} catch {}
|
|
14844
14855
|
}
|
|
14845
14856
|
async function updateTaskStatus(directory, taskId, status) {
|
|
14846
14857
|
const plan = await loadPlan(directory);
|
|
@@ -36285,8 +36296,8 @@ var init_tree_sitter = __esm(() => {
|
|
|
36285
36296
|
bytes = Promise.resolve(input);
|
|
36286
36297
|
} else {
|
|
36287
36298
|
if (globalThis.process?.versions.node) {
|
|
36288
|
-
const
|
|
36289
|
-
bytes =
|
|
36299
|
+
const fs25 = await import("fs/promises");
|
|
36300
|
+
bytes = fs25.readFile(input);
|
|
36290
36301
|
} else {
|
|
36291
36302
|
bytes = fetch(input).then((response) => response.arrayBuffer().then((buffer) => {
|
|
36292
36303
|
if (response.ok) {
|
|
@@ -36318,8 +36329,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
36318
36329
|
var moduleRtn;
|
|
36319
36330
|
var Module = moduleArg;
|
|
36320
36331
|
var readyPromiseResolve, readyPromiseReject;
|
|
36321
|
-
var readyPromise = new Promise((
|
|
36322
|
-
readyPromiseResolve =
|
|
36332
|
+
var readyPromise = new Promise((resolve13, reject) => {
|
|
36333
|
+
readyPromiseResolve = resolve13;
|
|
36323
36334
|
readyPromiseReject = reject;
|
|
36324
36335
|
});
|
|
36325
36336
|
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
|
@@ -36341,11 +36352,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
36341
36352
|
throw toThrow;
|
|
36342
36353
|
}, "quit_");
|
|
36343
36354
|
var scriptDirectory = "";
|
|
36344
|
-
function locateFile(
|
|
36355
|
+
function locateFile(path36) {
|
|
36345
36356
|
if (Module["locateFile"]) {
|
|
36346
|
-
return Module["locateFile"](
|
|
36357
|
+
return Module["locateFile"](path36, scriptDirectory);
|
|
36347
36358
|
}
|
|
36348
|
-
return scriptDirectory +
|
|
36359
|
+
return scriptDirectory + path36;
|
|
36349
36360
|
}
|
|
36350
36361
|
__name(locateFile, "locateFile");
|
|
36351
36362
|
var readAsync, readBinary;
|
|
@@ -36399,13 +36410,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
36399
36410
|
}
|
|
36400
36411
|
readAsync = /* @__PURE__ */ __name(async (url3) => {
|
|
36401
36412
|
if (isFileURI(url3)) {
|
|
36402
|
-
return new Promise((
|
|
36413
|
+
return new Promise((resolve13, reject) => {
|
|
36403
36414
|
var xhr = new XMLHttpRequest;
|
|
36404
36415
|
xhr.open("GET", url3, true);
|
|
36405
36416
|
xhr.responseType = "arraybuffer";
|
|
36406
36417
|
xhr.onload = () => {
|
|
36407
36418
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
36408
|
-
|
|
36419
|
+
resolve13(xhr.response);
|
|
36409
36420
|
return;
|
|
36410
36421
|
}
|
|
36411
36422
|
reject(xhr.status);
|
|
@@ -36625,10 +36636,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
36625
36636
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
36626
36637
|
var info2 = getWasmImports();
|
|
36627
36638
|
if (Module["instantiateWasm"]) {
|
|
36628
|
-
return new Promise((
|
|
36639
|
+
return new Promise((resolve13, reject) => {
|
|
36629
36640
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
36630
36641
|
receiveInstance(mod, inst);
|
|
36631
|
-
|
|
36642
|
+
resolve13(mod.exports);
|
|
36632
36643
|
});
|
|
36633
36644
|
});
|
|
36634
36645
|
}
|
|
@@ -38093,7 +38104,7 @@ var init_runtime = __esm(() => {
|
|
|
38093
38104
|
});
|
|
38094
38105
|
|
|
38095
38106
|
// src/index.ts
|
|
38096
|
-
import * as
|
|
38107
|
+
import * as path45 from "path";
|
|
38097
38108
|
|
|
38098
38109
|
// src/tools/tool-names.ts
|
|
38099
38110
|
var TOOL_NAMES = [
|
|
@@ -38123,7 +38134,8 @@ var TOOL_NAMES = [
|
|
|
38123
38134
|
"phase_complete",
|
|
38124
38135
|
"save_plan",
|
|
38125
38136
|
"update_task_status",
|
|
38126
|
-
"write_retro"
|
|
38137
|
+
"write_retro",
|
|
38138
|
+
"declare_scope"
|
|
38127
38139
|
];
|
|
38128
38140
|
var TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
38129
38141
|
|
|
@@ -38163,7 +38175,8 @@ var AGENT_TOOL_MAP = {
|
|
|
38163
38175
|
"test_runner",
|
|
38164
38176
|
"todo_extract",
|
|
38165
38177
|
"update_task_status",
|
|
38166
|
-
"write_retro"
|
|
38178
|
+
"write_retro",
|
|
38179
|
+
"declare_scope"
|
|
38167
38180
|
],
|
|
38168
38181
|
explorer: [
|
|
38169
38182
|
"complexity_hotspots",
|
|
@@ -38276,6 +38289,13 @@ var DEFAULT_SCORING_CONFIG = {
|
|
|
38276
38289
|
json: 0.35
|
|
38277
38290
|
}
|
|
38278
38291
|
};
|
|
38292
|
+
var LOW_CAPABILITY_MODELS = ["mini", "nano", "small", "free"];
|
|
38293
|
+
function isLowCapabilityModel(modelId) {
|
|
38294
|
+
if (!modelId)
|
|
38295
|
+
return false;
|
|
38296
|
+
const lower = modelId.toLowerCase();
|
|
38297
|
+
return LOW_CAPABILITY_MODELS.some((substr) => lower.includes(substr));
|
|
38298
|
+
}
|
|
38279
38299
|
|
|
38280
38300
|
// src/config/index.ts
|
|
38281
38301
|
init_evidence_schema();
|
|
@@ -38476,11 +38496,11 @@ var PhaseCompleteConfigSchema = exports_external.object({
|
|
|
38476
38496
|
});
|
|
38477
38497
|
var SummaryConfigSchema = exports_external.object({
|
|
38478
38498
|
enabled: exports_external.boolean().default(true),
|
|
38479
|
-
threshold_bytes: exports_external.number().min(1024).max(1048576).default(
|
|
38499
|
+
threshold_bytes: exports_external.number().min(1024).max(1048576).default(102400),
|
|
38480
38500
|
max_summary_chars: exports_external.number().min(100).max(5000).default(1000),
|
|
38481
38501
|
max_stored_bytes: exports_external.number().min(10240).max(104857600).default(10485760),
|
|
38482
38502
|
retention_days: exports_external.number().min(1).max(365).default(7),
|
|
38483
|
-
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task"])
|
|
38503
|
+
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task", "read"])
|
|
38484
38504
|
});
|
|
38485
38505
|
var ReviewPassesConfigSchema = exports_external.object({
|
|
38486
38506
|
always_security_review: exports_external.boolean().default(false),
|
|
@@ -38942,6 +38962,7 @@ CODER'S TOOLS: write, edit, patch, apply_patch, create_file, insert, replace \u2
|
|
|
38942
38962
|
If a tool modifies a file, it is a CODER tool. Delegate.
|
|
38943
38963
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
38944
38964
|
3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
|
|
38965
|
+
<!-- BEHAVIORAL_GUIDANCE_START -->
|
|
38945
38966
|
BATCHING DETECTION \u2014 you are batching if your coder delegation contains ANY of:
|
|
38946
38967
|
- The word "and" connecting two actions ("update X AND add Y")
|
|
38947
38968
|
- Multiple FILE paths ("FILE: src/a.ts, src/b.ts, src/c.ts")
|
|
@@ -38956,6 +38977,8 @@ A failure in one part blocks the entire batch, wasting all the work.
|
|
|
38956
38977
|
|
|
38957
38978
|
SPLIT RULE: If your delegation draft has "and" in the TASK line, split it.
|
|
38958
38979
|
Two small delegations with two QA gates > one large delegation with one QA gate.
|
|
38980
|
+
<!-- BEHAVIORAL_GUIDANCE_END -->
|
|
38981
|
+
<!-- BEHAVIORAL_GUIDANCE_START -->
|
|
38959
38982
|
4. ARCHITECT CODING BOUNDARIES \u2014 Only code yourself after {{QA_RETRY_LIMIT}} {{AGENT_PREFIX}}coder failures on same task.
|
|
38960
38983
|
These thoughts are WRONG and must be ignored:
|
|
38961
38984
|
\u2717 "It's just a schema change / config flag / one-liner / column / field / import" \u2192 delegate to {{AGENT_PREFIX}}coder
|
|
@@ -38972,6 +38995,7 @@ Two small delegations with two QA gates > one large delegation with one QA gate.
|
|
|
38972
38995
|
If you catch yourself reaching for a code editing tool: STOP. Delegate to {{AGENT_PREFIX}}coder.
|
|
38973
38996
|
Zero {{AGENT_PREFIX}}coder failures on this task = zero justification for self-coding.
|
|
38974
38997
|
Self-coding without {{QA_RETRY_LIMIT}} failures is a Rule 1 violation.
|
|
38998
|
+
<!-- BEHAVIORAL_GUIDANCE_END -->
|
|
38975
38999
|
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
39000
|
6. **CRITIC GATE (Execute BEFORE any implementation work)**:
|
|
38977
39001
|
- When you first create a plan, IMMEDIATELY delegate the full plan to {{AGENT_PREFIX}}critic for review
|
|
@@ -39128,6 +39152,7 @@ Your message MUST NOT contain:
|
|
|
39128
39152
|
|
|
39129
39153
|
Delegation is a handoff, not a negotiation. State facts, let agents decide.
|
|
39130
39154
|
|
|
39155
|
+
<!-- BEHAVIORAL_GUIDANCE_START -->
|
|
39131
39156
|
PARTIAL GATE RATIONALIZATIONS \u2014 automated gates \u2260 agent review. Running SOME gates is NOT compliance:
|
|
39132
39157
|
\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
39158
|
\u2717 "syntax_check passed, good enough" \u2192 syntax_check catches syntax. Reviewer catches logic. Test_engineer catches behavior. All three are required.
|
|
@@ -39138,6 +39163,7 @@ PARTIAL GATE RATIONALIZATIONS \u2014 automated gates \u2260 agent review. Runnin
|
|
|
39138
39163
|
|
|
39139
39164
|
Running syntax_check + pre_check_batch without reviewer + test_engineer is a PARTIAL GATE VIOLATION.
|
|
39140
39165
|
It is the same severity as skipping all gates. The QA gate is ALL steps or NONE.
|
|
39166
|
+
<!-- BEHAVIORAL_GUIDANCE_END -->
|
|
39141
39167
|
|
|
39142
39168
|
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
39169
|
9. **UI/UX DESIGN GATE**: Before delegating UI tasks to {{AGENT_PREFIX}}coder, check if the task involves UI components. Trigger conditions (ANY match):
|
|
@@ -41651,6 +41677,7 @@ class PlanSyncWorker {
|
|
|
41651
41677
|
this.syncing = true;
|
|
41652
41678
|
try {
|
|
41653
41679
|
log("[PlanSyncWorker] Syncing plan...");
|
|
41680
|
+
this.checkForUnauthorizedWrite();
|
|
41654
41681
|
const plan = await this.withTimeout(loadPlan(this.directory), this.syncTimeoutMs, "Sync operation timed out");
|
|
41655
41682
|
if (plan) {
|
|
41656
41683
|
log("[PlanSyncWorker] Sync complete", {
|
|
@@ -41692,6 +41719,21 @@ class PlanSyncWorker {
|
|
|
41692
41719
|
}
|
|
41693
41720
|
}
|
|
41694
41721
|
}
|
|
41722
|
+
checkForUnauthorizedWrite() {
|
|
41723
|
+
try {
|
|
41724
|
+
const swarmDir = this.getSwarmDir();
|
|
41725
|
+
const planJsonPath = path6.join(swarmDir, "plan.json");
|
|
41726
|
+
const markerPath = path6.join(swarmDir, ".plan-write-marker");
|
|
41727
|
+
const planStats = fs3.statSync(planJsonPath);
|
|
41728
|
+
const planMtimeMs = planStats.mtimeMs;
|
|
41729
|
+
const markerContent = fs3.readFileSync(markerPath, "utf8");
|
|
41730
|
+
const marker = JSON.parse(markerContent);
|
|
41731
|
+
const markerTimestampMs = new Date(marker.timestamp).getTime();
|
|
41732
|
+
if (planMtimeMs > markerTimestampMs + 5000) {
|
|
41733
|
+
log("[PlanSyncWorker] WARNING: plan.json may have been written outside save_plan/savePlan - unauthorized direct write suspected", { planMtimeMs, markerTimestampMs });
|
|
41734
|
+
}
|
|
41735
|
+
} catch {}
|
|
41736
|
+
}
|
|
41695
41737
|
withTimeout(promise2, ms, timeoutMessage) {
|
|
41696
41738
|
return new Promise((resolve4, reject) => {
|
|
41697
41739
|
const timer = setTimeout(() => {
|
|
@@ -41886,7 +41928,13 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
|
41886
41928
|
lastPhaseCompletePhase: 0,
|
|
41887
41929
|
phaseAgentsDispatched: new Set,
|
|
41888
41930
|
qaSkipCount: 0,
|
|
41889
|
-
qaSkipTaskIds: []
|
|
41931
|
+
qaSkipTaskIds: [],
|
|
41932
|
+
taskWorkflowStates: new Map,
|
|
41933
|
+
lastGateOutcome: null,
|
|
41934
|
+
declaredCoderScope: null,
|
|
41935
|
+
lastScopeViolation: null,
|
|
41936
|
+
scopeViolationDetected: false,
|
|
41937
|
+
modifiedFilesThisCoderTask: []
|
|
41890
41938
|
};
|
|
41891
41939
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
41892
41940
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -41955,6 +42003,24 @@ function ensureAgentSession(sessionId, agentName) {
|
|
|
41955
42003
|
if (!session.qaSkipTaskIds) {
|
|
41956
42004
|
session.qaSkipTaskIds = [];
|
|
41957
42005
|
}
|
|
42006
|
+
if (!session.taskWorkflowStates) {
|
|
42007
|
+
session.taskWorkflowStates = new Map;
|
|
42008
|
+
}
|
|
42009
|
+
if (session.lastGateOutcome === undefined) {
|
|
42010
|
+
session.lastGateOutcome = null;
|
|
42011
|
+
}
|
|
42012
|
+
if (session.declaredCoderScope === undefined) {
|
|
42013
|
+
session.declaredCoderScope = null;
|
|
42014
|
+
}
|
|
42015
|
+
if (session.lastScopeViolation === undefined) {
|
|
42016
|
+
session.lastScopeViolation = null;
|
|
42017
|
+
}
|
|
42018
|
+
if (session.modifiedFilesThisCoderTask === undefined) {
|
|
42019
|
+
session.modifiedFilesThisCoderTask = [];
|
|
42020
|
+
}
|
|
42021
|
+
if (session.scopeViolationDetected === undefined) {
|
|
42022
|
+
session.scopeViolationDetected = false;
|
|
42023
|
+
}
|
|
41958
42024
|
session.lastToolCallTime = now;
|
|
41959
42025
|
return session;
|
|
41960
42026
|
}
|
|
@@ -42034,6 +42100,35 @@ function recordPhaseAgentDispatch(sessionId, agentName) {
|
|
|
42034
42100
|
const normalizedName = stripKnownSwarmPrefix(agentName);
|
|
42035
42101
|
session.phaseAgentsDispatched.add(normalizedName);
|
|
42036
42102
|
}
|
|
42103
|
+
function advanceTaskState(session, taskId, newState) {
|
|
42104
|
+
if (!session.taskWorkflowStates) {
|
|
42105
|
+
session.taskWorkflowStates = new Map;
|
|
42106
|
+
}
|
|
42107
|
+
const STATE_ORDER = [
|
|
42108
|
+
"idle",
|
|
42109
|
+
"coder_delegated",
|
|
42110
|
+
"pre_check_passed",
|
|
42111
|
+
"reviewer_run",
|
|
42112
|
+
"tests_run",
|
|
42113
|
+
"complete"
|
|
42114
|
+
];
|
|
42115
|
+
const current = session.taskWorkflowStates.get(taskId) ?? "idle";
|
|
42116
|
+
const currentIndex = STATE_ORDER.indexOf(current);
|
|
42117
|
+
const newIndex = STATE_ORDER.indexOf(newState);
|
|
42118
|
+
if (newIndex <= currentIndex) {
|
|
42119
|
+
throw new Error(`INVALID_TASK_STATE_TRANSITION: ${taskId} ${current} \u2192 ${newState}`);
|
|
42120
|
+
}
|
|
42121
|
+
if (newState === "complete" && current !== "tests_run") {
|
|
42122
|
+
throw new Error(`INVALID_TASK_STATE_TRANSITION: ${taskId} cannot reach complete from ${current} \u2014 must pass through tests_run first`);
|
|
42123
|
+
}
|
|
42124
|
+
session.taskWorkflowStates.set(taskId, newState);
|
|
42125
|
+
}
|
|
42126
|
+
function getTaskState(session, taskId) {
|
|
42127
|
+
if (!session.taskWorkflowStates) {
|
|
42128
|
+
session.taskWorkflowStates = new Map;
|
|
42129
|
+
}
|
|
42130
|
+
return session.taskWorkflowStates.get(taskId) ?? "idle";
|
|
42131
|
+
}
|
|
42037
42132
|
|
|
42038
42133
|
// src/commands/benchmark.ts
|
|
42039
42134
|
init_utils();
|
|
@@ -42923,7 +43018,7 @@ async function handleDarkMatterCommand(directory, args2) {
|
|
|
42923
43018
|
|
|
42924
43019
|
// src/services/diagnose-service.ts
|
|
42925
43020
|
import { execSync } from "child_process";
|
|
42926
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as
|
|
43021
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
42927
43022
|
import path12 from "path";
|
|
42928
43023
|
init_manager();
|
|
42929
43024
|
init_utils2();
|
|
@@ -43229,7 +43324,7 @@ async function checkConfigParseability(directory) {
|
|
|
43229
43324
|
};
|
|
43230
43325
|
}
|
|
43231
43326
|
try {
|
|
43232
|
-
const content =
|
|
43327
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
43233
43328
|
JSON.parse(content);
|
|
43234
43329
|
return {
|
|
43235
43330
|
name: "Config Parseability",
|
|
@@ -43296,7 +43391,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
43296
43391
|
};
|
|
43297
43392
|
}
|
|
43298
43393
|
try {
|
|
43299
|
-
const content =
|
|
43394
|
+
const content = readFileSync4(manifestPath, "utf-8");
|
|
43300
43395
|
const parsed = JSON.parse(content);
|
|
43301
43396
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
43302
43397
|
return {
|
|
@@ -43348,7 +43443,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
43348
43443
|
};
|
|
43349
43444
|
}
|
|
43350
43445
|
try {
|
|
43351
|
-
const content =
|
|
43446
|
+
const content = readFileSync4(eventsPath, "utf-8");
|
|
43352
43447
|
const lines = content.split(`
|
|
43353
43448
|
`).filter((line) => line.trim() !== "");
|
|
43354
43449
|
let malformedCount = 0;
|
|
@@ -43389,7 +43484,7 @@ async function checkSteeringDirectives(directory) {
|
|
|
43389
43484
|
};
|
|
43390
43485
|
}
|
|
43391
43486
|
try {
|
|
43392
|
-
const content =
|
|
43487
|
+
const content = readFileSync4(eventsPath, "utf-8");
|
|
43393
43488
|
const lines = content.split(`
|
|
43394
43489
|
`).filter((line) => line.trim() !== "");
|
|
43395
43490
|
const directivesIssued = [];
|
|
@@ -44357,7 +44452,7 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
44357
44452
|
}
|
|
44358
44453
|
// src/hooks/knowledge-migrator.ts
|
|
44359
44454
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
44360
|
-
import { existsSync as existsSync8, readFileSync as
|
|
44455
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
44361
44456
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
44362
44457
|
import * as path16 from "path";
|
|
44363
44458
|
|
|
@@ -44914,7 +45009,7 @@ function inferProjectName(directory) {
|
|
|
44914
45009
|
const packageJsonPath = path16.join(directory, "package.json");
|
|
44915
45010
|
if (existsSync8(packageJsonPath)) {
|
|
44916
45011
|
try {
|
|
44917
|
-
const pkg = JSON.parse(
|
|
45012
|
+
const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
|
|
44918
45013
|
if (pkg.name && typeof pkg.name === "string") {
|
|
44919
45014
|
return pkg.name;
|
|
44920
45015
|
}
|
|
@@ -47099,7 +47194,7 @@ function shouldMaskToolOutput(msg, index, totalMessages, recentWindowSize, thres
|
|
|
47099
47194
|
return false;
|
|
47100
47195
|
}
|
|
47101
47196
|
const toolName = extractToolName(text);
|
|
47102
|
-
if (toolName && ["retrieve_summary", "task"].includes(toolName.toLowerCase())) {
|
|
47197
|
+
if (toolName && ["retrieve_summary", "task", "read"].includes(toolName.toLowerCase())) {
|
|
47103
47198
|
return false;
|
|
47104
47199
|
}
|
|
47105
47200
|
const age = totalMessages - 1 - index;
|
|
@@ -47149,12 +47244,40 @@ function createDelegationGateHook(config3) {
|
|
|
47149
47244
|
if (normalized === "Task" || normalized === "task") {
|
|
47150
47245
|
const delegationChain = swarmState.delegationChains.get(input.sessionID);
|
|
47151
47246
|
if (delegationChain && delegationChain.length > 0) {
|
|
47152
|
-
|
|
47153
|
-
|
|
47154
|
-
|
|
47247
|
+
let lastCoderIndex = -1;
|
|
47248
|
+
for (let i2 = delegationChain.length - 1;i2 >= 0; i2--) {
|
|
47249
|
+
const target = stripKnownSwarmPrefix(delegationChain[i2].to);
|
|
47250
|
+
if (target.includes("coder")) {
|
|
47251
|
+
lastCoderIndex = i2;
|
|
47252
|
+
break;
|
|
47253
|
+
}
|
|
47254
|
+
}
|
|
47255
|
+
if (lastCoderIndex === -1)
|
|
47256
|
+
return;
|
|
47257
|
+
const afterCoder = delegationChain.slice(lastCoderIndex);
|
|
47258
|
+
let hasReviewer = false;
|
|
47259
|
+
let hasTestEngineer = false;
|
|
47260
|
+
for (const delegation of afterCoder) {
|
|
47261
|
+
const target = stripKnownSwarmPrefix(delegation.to);
|
|
47262
|
+
if (target === "reviewer")
|
|
47263
|
+
hasReviewer = true;
|
|
47264
|
+
if (target === "test_engineer")
|
|
47265
|
+
hasTestEngineer = true;
|
|
47266
|
+
}
|
|
47267
|
+
if (hasReviewer && hasTestEngineer) {
|
|
47155
47268
|
session.qaSkipCount = 0;
|
|
47156
47269
|
session.qaSkipTaskIds = [];
|
|
47157
47270
|
}
|
|
47271
|
+
if (hasReviewer && session.currentTaskId) {
|
|
47272
|
+
try {
|
|
47273
|
+
advanceTaskState(session, session.currentTaskId, "reviewer_run");
|
|
47274
|
+
} catch {}
|
|
47275
|
+
}
|
|
47276
|
+
if (hasReviewer && hasTestEngineer && session.currentTaskId) {
|
|
47277
|
+
try {
|
|
47278
|
+
advanceTaskState(session, session.currentTaskId, "tests_run");
|
|
47279
|
+
} catch {}
|
|
47280
|
+
}
|
|
47158
47281
|
}
|
|
47159
47282
|
}
|
|
47160
47283
|
};
|
|
@@ -47184,14 +47307,71 @@ function createDelegationGateHook(config3) {
|
|
|
47184
47307
|
return;
|
|
47185
47308
|
const textPart = lastUserMessage.parts[textPartIndex];
|
|
47186
47309
|
const text = textPart.text ?? "";
|
|
47310
|
+
const taskDisclosureSessionID = lastUserMessage.info?.sessionID;
|
|
47311
|
+
if (taskDisclosureSessionID) {
|
|
47312
|
+
const taskSession = ensureAgentSession(taskDisclosureSessionID);
|
|
47313
|
+
const currentTaskIdForWindow = taskSession.currentTaskId;
|
|
47314
|
+
if (currentTaskIdForWindow) {
|
|
47315
|
+
const taskLineRegex = /^[ \t]*-[ \t]*(?:\[[ x]\][ \t]+)?(\d+\.\d+(?:\.\d+)*)[:. ].*/gm;
|
|
47316
|
+
const taskLines = [];
|
|
47317
|
+
taskLineRegex.lastIndex = 0;
|
|
47318
|
+
let regexMatch = taskLineRegex.exec(text);
|
|
47319
|
+
while (regexMatch !== null) {
|
|
47320
|
+
taskLines.push({
|
|
47321
|
+
line: regexMatch[0],
|
|
47322
|
+
taskId: regexMatch[1],
|
|
47323
|
+
index: regexMatch.index
|
|
47324
|
+
});
|
|
47325
|
+
regexMatch = taskLineRegex.exec(text);
|
|
47326
|
+
}
|
|
47327
|
+
if (taskLines.length > 5) {
|
|
47328
|
+
const currentIdx = taskLines.findIndex((t) => t.taskId === currentTaskIdForWindow);
|
|
47329
|
+
const windowStart = Math.max(0, currentIdx - 2);
|
|
47330
|
+
const windowEnd = Math.min(taskLines.length - 1, currentIdx + 3);
|
|
47331
|
+
const visibleTasks = taskLines.slice(windowStart, windowEnd + 1);
|
|
47332
|
+
const hiddenBefore = windowStart;
|
|
47333
|
+
const hiddenAfter = taskLines.length - 1 - windowEnd;
|
|
47334
|
+
const totalTasks = taskLines.length;
|
|
47335
|
+
const visibleCount = visibleTasks.length;
|
|
47336
|
+
const firstTaskIndex = taskLines[0].index;
|
|
47337
|
+
const lastTask = taskLines[taskLines.length - 1];
|
|
47338
|
+
const lastTaskEnd = lastTask.index + lastTask.line.length;
|
|
47339
|
+
const before = text.slice(0, firstTaskIndex);
|
|
47340
|
+
const after = text.slice(lastTaskEnd);
|
|
47341
|
+
const visibleLines = visibleTasks.map((t) => t.line).join(`
|
|
47342
|
+
`);
|
|
47343
|
+
const trimComment = `[Task window: showing ${visibleCount} of ${totalTasks} tasks]`;
|
|
47344
|
+
const trimmedMiddle = (hiddenBefore > 0 ? `[...${hiddenBefore} tasks hidden...]
|
|
47345
|
+
` : "") + visibleLines + (hiddenAfter > 0 ? `
|
|
47346
|
+
[...${hiddenAfter} tasks hidden...]` : "");
|
|
47347
|
+
textPart.text = `${before}${trimmedMiddle}
|
|
47348
|
+
${trimComment}${after}`;
|
|
47349
|
+
}
|
|
47350
|
+
}
|
|
47351
|
+
}
|
|
47187
47352
|
const sessionID = lastUserMessage.info?.sessionID;
|
|
47188
47353
|
const taskIdMatch = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
|
|
47189
47354
|
const currentTaskId = taskIdMatch ? taskIdMatch[1].trim() : null;
|
|
47190
47355
|
const coderDelegationPattern = /(?:^|\n)\s*(?:\w+_)?coder\s*\n\s*TASK:/i;
|
|
47191
47356
|
const isCoderDelegation = coderDelegationPattern.test(text);
|
|
47357
|
+
const priorCoderTaskId = sessionID ? ensureAgentSession(sessionID).lastCoderDelegationTaskId ?? null : null;
|
|
47192
47358
|
if (sessionID && isCoderDelegation && currentTaskId) {
|
|
47193
47359
|
const session = ensureAgentSession(sessionID);
|
|
47194
47360
|
session.lastCoderDelegationTaskId = currentTaskId;
|
|
47361
|
+
const fileDirPattern = /^FILE:\s*(.+)$/gm;
|
|
47362
|
+
const declaredFiles = [];
|
|
47363
|
+
for (const match of text.matchAll(fileDirPattern)) {
|
|
47364
|
+
const filePath = match[1].trim();
|
|
47365
|
+
if (filePath.length > 0 && !declaredFiles.includes(filePath)) {
|
|
47366
|
+
declaredFiles.push(filePath);
|
|
47367
|
+
}
|
|
47368
|
+
}
|
|
47369
|
+
session.declaredCoderScope = declaredFiles.length > 0 ? declaredFiles : null;
|
|
47370
|
+
try {
|
|
47371
|
+
advanceTaskState(session, currentTaskId, "coder_delegated");
|
|
47372
|
+
} catch (err2) {
|
|
47373
|
+
console.warn(`[delegation-gate] state machine warn: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
47374
|
+
}
|
|
47195
47375
|
}
|
|
47196
47376
|
if (sessionID && !isCoderDelegation && currentTaskId) {
|
|
47197
47377
|
const session = ensureAgentSession(sessionID);
|
|
@@ -47203,6 +47383,29 @@ Rule 1: DELEGATE all coding to coder. You do NOT write code.`;
|
|
|
47203
47383
|
${text}`;
|
|
47204
47384
|
}
|
|
47205
47385
|
}
|
|
47386
|
+
{
|
|
47387
|
+
const deliberationSessionID = lastUserMessage.info?.sessionID;
|
|
47388
|
+
if (deliberationSessionID) {
|
|
47389
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(deliberationSessionID)) {} else {
|
|
47390
|
+
const deliberationSession = ensureAgentSession(deliberationSessionID);
|
|
47391
|
+
const lastGate = deliberationSession.lastGateOutcome;
|
|
47392
|
+
let preamble;
|
|
47393
|
+
if (lastGate) {
|
|
47394
|
+
const gateResult = lastGate.passed ? "PASSED" : "FAILED";
|
|
47395
|
+
const sanitizedGate = lastGate.gate.replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
|
|
47396
|
+
const sanitizedTaskId = lastGate.taskId.replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 32);
|
|
47397
|
+
preamble = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
|
|
47398
|
+
[DELIBERATE: Before proceeding \u2014 what is the SINGLE next task? What gates must it pass?]`;
|
|
47399
|
+
} else {
|
|
47400
|
+
preamble = `[DELIBERATE: Identify the first task from the plan. What gates must it pass before marking complete?]`;
|
|
47401
|
+
}
|
|
47402
|
+
const currentText = textPart.text ?? "";
|
|
47403
|
+
textPart.text = `${preamble}
|
|
47404
|
+
|
|
47405
|
+
${currentText}`;
|
|
47406
|
+
}
|
|
47407
|
+
}
|
|
47408
|
+
}
|
|
47206
47409
|
if (!isCoderDelegation)
|
|
47207
47410
|
return;
|
|
47208
47411
|
const warnings = [];
|
|
@@ -47245,8 +47448,9 @@ ${text}`;
|
|
|
47245
47448
|
const betweenCoders = delegationChain.slice(prevCoderIndex + 1);
|
|
47246
47449
|
const hasReviewer = betweenCoders.some((d) => stripKnownSwarmPrefix(d.to) === "reviewer");
|
|
47247
47450
|
const hasTestEngineer = betweenCoders.some((d) => stripKnownSwarmPrefix(d.to) === "test_engineer");
|
|
47248
|
-
|
|
47249
|
-
|
|
47451
|
+
const session = ensureAgentSession(sessionID);
|
|
47452
|
+
const priorTaskStuckAtCoder = priorCoderTaskId !== null && getTaskState(session, priorCoderTaskId) === "coder_delegated";
|
|
47453
|
+
if (!hasReviewer || !hasTestEngineer || priorTaskStuckAtCoder) {
|
|
47250
47454
|
if (session.qaSkipCount >= 1) {
|
|
47251
47455
|
const skippedTasks = session.qaSkipTaskIds.join(", ");
|
|
47252
47456
|
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 +47710,16 @@ function getCurrentTaskId(sessionId) {
|
|
|
47506
47710
|
const session = swarmState.agentSessions.get(sessionId);
|
|
47507
47711
|
return session?.currentTaskId ?? `${sessionId}:unknown`;
|
|
47508
47712
|
}
|
|
47713
|
+
function isInDeclaredScope(filePath, scopeEntries) {
|
|
47714
|
+
const resolvedFile = path25.resolve(filePath);
|
|
47715
|
+
return scopeEntries.some((scope) => {
|
|
47716
|
+
const resolvedScope = path25.resolve(scope);
|
|
47717
|
+
if (resolvedFile === resolvedScope)
|
|
47718
|
+
return true;
|
|
47719
|
+
const rel = path25.relative(resolvedScope, resolvedFile);
|
|
47720
|
+
return rel.length > 0 && !rel.startsWith("..") && !path25.isAbsolute(rel);
|
|
47721
|
+
});
|
|
47722
|
+
}
|
|
47509
47723
|
function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
47510
47724
|
let directory;
|
|
47511
47725
|
let guardrailsConfig;
|
|
@@ -47528,19 +47742,40 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47528
47742
|
return {
|
|
47529
47743
|
toolBefore: async (input, output) => {
|
|
47530
47744
|
const currentSession = swarmState.agentSessions.get(input.sessionID);
|
|
47531
|
-
if (currentSession?.delegationActive) {
|
|
47745
|
+
if (currentSession?.delegationActive) {
|
|
47746
|
+
if (isWriteTool(input.tool)) {
|
|
47747
|
+
const delegArgs = output.args;
|
|
47748
|
+
const delegTargetPath = delegArgs?.filePath ?? delegArgs?.path ?? delegArgs?.file ?? delegArgs?.target;
|
|
47749
|
+
if (typeof delegTargetPath === "string" && delegTargetPath.length > 0) {
|
|
47750
|
+
if (!currentSession.modifiedFilesThisCoderTask.includes(delegTargetPath)) {
|
|
47751
|
+
currentSession.modifiedFilesThisCoderTask.push(delegTargetPath);
|
|
47752
|
+
}
|
|
47753
|
+
}
|
|
47754
|
+
}
|
|
47755
|
+
} else if (isArchitect(input.sessionID)) {
|
|
47756
|
+
const coderDelegArgs = output.args;
|
|
47757
|
+
const coderDeleg = isAgentDelegation(input.tool, coderDelegArgs);
|
|
47758
|
+
if (coderDeleg.isDelegation && coderDeleg.targetAgent === "coder") {
|
|
47759
|
+
const coderSession = swarmState.agentSessions.get(input.sessionID);
|
|
47760
|
+
if (coderSession) {
|
|
47761
|
+
coderSession.modifiedFilesThisCoderTask = [];
|
|
47762
|
+
}
|
|
47763
|
+
}
|
|
47764
|
+
}
|
|
47765
|
+
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
47532
47766
|
const args2 = output.args;
|
|
47533
47767
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
47534
47768
|
if (typeof targetPath === "string" && targetPath.length > 0) {
|
|
47535
47769
|
const resolvedTarget = path25.resolve(directory, targetPath).toLowerCase();
|
|
47536
47770
|
const planMdPath = path25.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
47537
|
-
|
|
47538
|
-
|
|
47771
|
+
const planJsonPath = path25.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
47772
|
+
if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
|
|
47773
|
+
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
47539
47774
|
}
|
|
47540
47775
|
}
|
|
47541
47776
|
if (!targetPath && (input.tool === "apply_patch" || input.tool === "patch")) {
|
|
47542
47777
|
const patchText = args2?.input ?? args2?.patch ?? (Array.isArray(args2?.cmd) ? args2.cmd[1] : undefined);
|
|
47543
|
-
if (typeof patchText === "string") {
|
|
47778
|
+
if (typeof patchText === "string" && patchText.length <= 1e6) {
|
|
47544
47779
|
const patchPathPattern = /\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)/gi;
|
|
47545
47780
|
const diffPathPattern = /\+\+\+\s+b\/(.+)/gm;
|
|
47546
47781
|
const paths = new Set;
|
|
@@ -47552,11 +47787,41 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47552
47787
|
if (p !== "/dev/null")
|
|
47553
47788
|
paths.add(p);
|
|
47554
47789
|
}
|
|
47790
|
+
const gitDiffPathPattern = /^diff --git a\/(.+?) b\/(.+?)$/gm;
|
|
47791
|
+
for (const match of patchText.matchAll(gitDiffPathPattern)) {
|
|
47792
|
+
const aPath = match[1].trim();
|
|
47793
|
+
const bPath = match[2].trim();
|
|
47794
|
+
if (aPath !== "/dev/null")
|
|
47795
|
+
paths.add(aPath);
|
|
47796
|
+
if (bPath !== "/dev/null")
|
|
47797
|
+
paths.add(bPath);
|
|
47798
|
+
}
|
|
47799
|
+
const minusPathPattern = /^---\s+a\/(.+)$/gm;
|
|
47800
|
+
for (const match of patchText.matchAll(minusPathPattern)) {
|
|
47801
|
+
const p = match[1].trim();
|
|
47802
|
+
if (p !== "/dev/null")
|
|
47803
|
+
paths.add(p);
|
|
47804
|
+
}
|
|
47805
|
+
const traditionalMinusPattern = /^---\s+([^\s].+?)(?:\t.*)?$/gm;
|
|
47806
|
+
const traditionalPlusPattern = /^\+\+\+\s+([^\s].+?)(?:\t.*)?$/gm;
|
|
47807
|
+
for (const match of patchText.matchAll(traditionalMinusPattern)) {
|
|
47808
|
+
const p = match[1].trim();
|
|
47809
|
+
if (p !== "/dev/null" && !p.startsWith("a/") && !p.startsWith("b/")) {
|
|
47810
|
+
paths.add(p);
|
|
47811
|
+
}
|
|
47812
|
+
}
|
|
47813
|
+
for (const match of patchText.matchAll(traditionalPlusPattern)) {
|
|
47814
|
+
const p = match[1].trim();
|
|
47815
|
+
if (p !== "/dev/null" && !p.startsWith("a/") && !p.startsWith("b/")) {
|
|
47816
|
+
paths.add(p);
|
|
47817
|
+
}
|
|
47818
|
+
}
|
|
47555
47819
|
for (const p of paths) {
|
|
47556
47820
|
const resolvedP = path25.resolve(directory, p);
|
|
47557
|
-
const planMdPath = path25.resolve(directory, ".swarm", "plan.md");
|
|
47558
|
-
|
|
47559
|
-
|
|
47821
|
+
const planMdPath = path25.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
47822
|
+
const planJsonPath = path25.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
47823
|
+
if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
|
|
47824
|
+
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
47560
47825
|
}
|
|
47561
47826
|
if (isOutsideSwarmDir(p, directory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
|
|
47562
47827
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
@@ -47746,6 +48011,26 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47746
48011
|
};
|
|
47747
48012
|
} else {
|
|
47748
48013
|
session.lastGateFailure = null;
|
|
48014
|
+
if (input.tool === "pre_check_batch") {
|
|
48015
|
+
const successStr = typeof output.output === "string" ? output.output : "";
|
|
48016
|
+
let isPassed = false;
|
|
48017
|
+
try {
|
|
48018
|
+
const result = JSON.parse(successStr);
|
|
48019
|
+
isPassed = result.gates_passed === true;
|
|
48020
|
+
} catch {
|
|
48021
|
+
isPassed = false;
|
|
48022
|
+
}
|
|
48023
|
+
if (isPassed && session.currentTaskId) {
|
|
48024
|
+
try {
|
|
48025
|
+
advanceTaskState(session, session.currentTaskId, "pre_check_passed");
|
|
48026
|
+
} catch (err2) {
|
|
48027
|
+
warn("Failed to advance task state after pre_check_batch pass", {
|
|
48028
|
+
taskId: session.currentTaskId,
|
|
48029
|
+
error: String(err2)
|
|
48030
|
+
});
|
|
48031
|
+
}
|
|
48032
|
+
}
|
|
48033
|
+
}
|
|
47749
48034
|
}
|
|
47750
48035
|
}
|
|
47751
48036
|
const inputArgs = inputArgsByCallID.get(input.callID);
|
|
@@ -47766,6 +48051,15 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47766
48051
|
if (delegation.isDelegation && delegation.targetAgent === "coder" && session.lastCoderDelegationTaskId) {
|
|
47767
48052
|
session.currentTaskId = session.lastCoderDelegationTaskId;
|
|
47768
48053
|
session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
|
|
48054
|
+
if (session.declaredCoderScope !== null) {
|
|
48055
|
+
const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
|
|
48056
|
+
if (undeclaredFiles.length > 2) {
|
|
48057
|
+
const safeTaskId = String(session.currentTaskId ?? "").replace(/[\r\n\t]/g, "_");
|
|
48058
|
+
session.lastScopeViolation = `Scope violation for task ${safeTaskId}: ` + `${undeclaredFiles.length} undeclared files modified: ` + undeclaredFiles.join(", ");
|
|
48059
|
+
session.scopeViolationDetected = true;
|
|
48060
|
+
}
|
|
48061
|
+
}
|
|
48062
|
+
session.modifiedFilesThisCoderTask = [];
|
|
47769
48063
|
}
|
|
47770
48064
|
}
|
|
47771
48065
|
const window2 = getActiveWindow(input.sessionID);
|
|
@@ -47789,6 +48083,26 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47789
48083
|
if (!sessionId) {
|
|
47790
48084
|
return;
|
|
47791
48085
|
}
|
|
48086
|
+
{
|
|
48087
|
+
const { modelID } = extractModelInfo(messages);
|
|
48088
|
+
if (modelID && isLowCapabilityModel(modelID)) {
|
|
48089
|
+
for (const msg of messages) {
|
|
48090
|
+
if (msg.info?.role !== "system")
|
|
48091
|
+
continue;
|
|
48092
|
+
for (const part of msg.parts) {
|
|
48093
|
+
try {
|
|
48094
|
+
if (part == null)
|
|
48095
|
+
continue;
|
|
48096
|
+
if (part.type !== "text" || typeof part.text !== "string")
|
|
48097
|
+
continue;
|
|
48098
|
+
if (!part.text.includes("<!-- BEHAVIORAL_GUIDANCE_START -->"))
|
|
48099
|
+
continue;
|
|
48100
|
+
part.text = part.text.replace(/<!--\s*BEHAVIORAL_GUIDANCE_START\s*-->[\s\S]*?<!--\s*BEHAVIORAL_GUIDANCE_END\s*-->/g, "[Enforcement: programmatic gates active]");
|
|
48101
|
+
} catch {}
|
|
48102
|
+
}
|
|
48103
|
+
}
|
|
48104
|
+
}
|
|
48105
|
+
}
|
|
47792
48106
|
const session = swarmState.agentSessions.get(sessionId);
|
|
47793
48107
|
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
47794
48108
|
const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
@@ -47861,6 +48175,16 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47861
48175
|
}
|
|
47862
48176
|
}
|
|
47863
48177
|
}
|
|
48178
|
+
if (isArchitectSessionForGates && session && session.scopeViolationDetected) {
|
|
48179
|
+
session.scopeViolationDetected = false;
|
|
48180
|
+
const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
48181
|
+
if (textPart2 && session.lastScopeViolation) {
|
|
48182
|
+
textPart2.text = `\u26A0\uFE0F SCOPE VIOLATION: ${session.lastScopeViolation}
|
|
48183
|
+
` + `Only modify files within your declared scope. Request scope expansion from architect if needed.
|
|
48184
|
+
|
|
48185
|
+
` + textPart2.text;
|
|
48186
|
+
}
|
|
48187
|
+
}
|
|
47864
48188
|
if (isArchitectSessionForGates && session && session.catastrophicPhaseWarnings) {
|
|
47865
48189
|
try {
|
|
47866
48190
|
const plan = await loadPlan(directory);
|
|
@@ -49707,7 +50031,11 @@ function createToolSummarizerHook(config3, directory) {
|
|
|
49707
50031
|
if (typeof output.output !== "string" || output.output.length === 0) {
|
|
49708
50032
|
return;
|
|
49709
50033
|
}
|
|
49710
|
-
const exemptTools = config3.exempt_tools ?? [
|
|
50034
|
+
const exemptTools = config3.exempt_tools ?? [
|
|
50035
|
+
"retrieve_summary",
|
|
50036
|
+
"task",
|
|
50037
|
+
"read"
|
|
50038
|
+
];
|
|
49711
50039
|
if (exemptTools.includes(input.tool)) {
|
|
49712
50040
|
return;
|
|
49713
50041
|
}
|
|
@@ -50700,7 +51028,12 @@ function deserializeAgentSession(s) {
|
|
|
50700
51028
|
lastPhaseCompletePhase: s.lastPhaseCompletePhase ?? 0,
|
|
50701
51029
|
phaseAgentsDispatched,
|
|
50702
51030
|
qaSkipCount: s.qaSkipCount ?? 0,
|
|
50703
|
-
qaSkipTaskIds: s.qaSkipTaskIds ?? []
|
|
51031
|
+
qaSkipTaskIds: s.qaSkipTaskIds ?? [],
|
|
51032
|
+
taskWorkflowStates: new Map,
|
|
51033
|
+
lastGateOutcome: null,
|
|
51034
|
+
declaredCoderScope: null,
|
|
51035
|
+
lastScopeViolation: null,
|
|
51036
|
+
modifiedFilesThisCoderTask: []
|
|
50704
51037
|
};
|
|
50705
51038
|
}
|
|
50706
51039
|
async function readSnapshot(directory) {
|
|
@@ -51512,6 +51845,184 @@ var complexity_hotspots = createSwarmTool({
|
|
|
51512
51845
|
}
|
|
51513
51846
|
}
|
|
51514
51847
|
});
|
|
51848
|
+
// src/tools/declare-scope.ts
|
|
51849
|
+
init_tool();
|
|
51850
|
+
import * as fs19 from "fs";
|
|
51851
|
+
import * as path30 from "path";
|
|
51852
|
+
init_create_tool();
|
|
51853
|
+
function validateTaskIdFormat(taskId) {
|
|
51854
|
+
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
51855
|
+
if (!taskIdPattern.test(taskId)) {
|
|
51856
|
+
return `Invalid taskId "${taskId}". Must match pattern N.M or N.M.P (e.g., "1.1", "1.2.3")`;
|
|
51857
|
+
}
|
|
51858
|
+
return;
|
|
51859
|
+
}
|
|
51860
|
+
function validateFiles(files) {
|
|
51861
|
+
const errors5 = [];
|
|
51862
|
+
for (const file3 of files) {
|
|
51863
|
+
if (file3.includes("\x00")) {
|
|
51864
|
+
errors5.push(`Invalid file "${file3}": null bytes are not allowed`);
|
|
51865
|
+
}
|
|
51866
|
+
if (file3.includes("..")) {
|
|
51867
|
+
errors5.push(`Invalid file "${file3}": path traversal sequences (..) are not allowed`);
|
|
51868
|
+
}
|
|
51869
|
+
if (file3.length > 4096) {
|
|
51870
|
+
errors5.push(`Invalid file "${file3}": path exceeds maximum length of 4096 characters`);
|
|
51871
|
+
}
|
|
51872
|
+
}
|
|
51873
|
+
return errors5;
|
|
51874
|
+
}
|
|
51875
|
+
async function executeDeclareScope(args2, fallbackDir) {
|
|
51876
|
+
const taskIdError = validateTaskIdFormat(args2.taskId);
|
|
51877
|
+
if (taskIdError) {
|
|
51878
|
+
return {
|
|
51879
|
+
success: false,
|
|
51880
|
+
message: "Validation failed",
|
|
51881
|
+
errors: [taskIdError]
|
|
51882
|
+
};
|
|
51883
|
+
}
|
|
51884
|
+
if (!Array.isArray(args2.files) || args2.files.length === 0) {
|
|
51885
|
+
return {
|
|
51886
|
+
success: false,
|
|
51887
|
+
message: "Validation failed",
|
|
51888
|
+
errors: ["files must be a non-empty array"]
|
|
51889
|
+
};
|
|
51890
|
+
}
|
|
51891
|
+
const fileErrors = validateFiles(args2.files);
|
|
51892
|
+
if (fileErrors.length > 0) {
|
|
51893
|
+
return {
|
|
51894
|
+
success: false,
|
|
51895
|
+
message: "Validation failed",
|
|
51896
|
+
errors: fileErrors
|
|
51897
|
+
};
|
|
51898
|
+
}
|
|
51899
|
+
if (args2.whitelist) {
|
|
51900
|
+
const whitelistErrors = validateFiles(args2.whitelist);
|
|
51901
|
+
if (whitelistErrors.length > 0) {
|
|
51902
|
+
return {
|
|
51903
|
+
success: false,
|
|
51904
|
+
message: "Validation failed",
|
|
51905
|
+
errors: whitelistErrors
|
|
51906
|
+
};
|
|
51907
|
+
}
|
|
51908
|
+
}
|
|
51909
|
+
let normalizedDir;
|
|
51910
|
+
if (args2.working_directory != null) {
|
|
51911
|
+
if (args2.working_directory.includes("\x00")) {
|
|
51912
|
+
return {
|
|
51913
|
+
success: false,
|
|
51914
|
+
message: "Invalid working_directory: null bytes are not allowed",
|
|
51915
|
+
errors: ["Invalid working_directory: null bytes are not allowed"]
|
|
51916
|
+
};
|
|
51917
|
+
}
|
|
51918
|
+
if (process.platform === "win32") {
|
|
51919
|
+
const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
|
|
51920
|
+
if (devicePathPattern.test(args2.working_directory)) {
|
|
51921
|
+
return {
|
|
51922
|
+
success: false,
|
|
51923
|
+
message: "Invalid working_directory: Windows device paths are not allowed",
|
|
51924
|
+
errors: [
|
|
51925
|
+
"Invalid working_directory: Windows device paths are not allowed"
|
|
51926
|
+
]
|
|
51927
|
+
};
|
|
51928
|
+
}
|
|
51929
|
+
}
|
|
51930
|
+
normalizedDir = path30.normalize(args2.working_directory);
|
|
51931
|
+
const pathParts = normalizedDir.split(path30.sep);
|
|
51932
|
+
if (pathParts.includes("..")) {
|
|
51933
|
+
return {
|
|
51934
|
+
success: false,
|
|
51935
|
+
message: "Invalid working_directory: path traversal sequences (..) are not allowed",
|
|
51936
|
+
errors: [
|
|
51937
|
+
"Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
51938
|
+
]
|
|
51939
|
+
};
|
|
51940
|
+
}
|
|
51941
|
+
const resolvedDir = path30.resolve(normalizedDir);
|
|
51942
|
+
try {
|
|
51943
|
+
const realPath = fs19.realpathSync(resolvedDir);
|
|
51944
|
+
const planPath2 = path30.join(realPath, ".swarm", "plan.json");
|
|
51945
|
+
if (!fs19.existsSync(planPath2)) {
|
|
51946
|
+
return {
|
|
51947
|
+
success: false,
|
|
51948
|
+
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
51949
|
+
errors: [
|
|
51950
|
+
`Invalid working_directory: plan not found in "${realPath}"`
|
|
51951
|
+
]
|
|
51952
|
+
};
|
|
51953
|
+
}
|
|
51954
|
+
} catch {
|
|
51955
|
+
return {
|
|
51956
|
+
success: false,
|
|
51957
|
+
message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
|
|
51958
|
+
errors: [
|
|
51959
|
+
`Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
|
|
51960
|
+
]
|
|
51961
|
+
};
|
|
51962
|
+
}
|
|
51963
|
+
}
|
|
51964
|
+
const directory = normalizedDir ?? fallbackDir ?? process.cwd();
|
|
51965
|
+
const planPath = path30.resolve(directory, ".swarm", "plan.json");
|
|
51966
|
+
if (!fs19.existsSync(planPath)) {
|
|
51967
|
+
return {
|
|
51968
|
+
success: false,
|
|
51969
|
+
message: "No plan found",
|
|
51970
|
+
errors: ["plan.json not found"]
|
|
51971
|
+
};
|
|
51972
|
+
}
|
|
51973
|
+
let planContent;
|
|
51974
|
+
try {
|
|
51975
|
+
planContent = JSON.parse(fs19.readFileSync(planPath, "utf-8"));
|
|
51976
|
+
} catch {
|
|
51977
|
+
return {
|
|
51978
|
+
success: false,
|
|
51979
|
+
message: "Failed to parse plan.json",
|
|
51980
|
+
errors: ["plan.json is not valid JSON"]
|
|
51981
|
+
};
|
|
51982
|
+
}
|
|
51983
|
+
const allTasks = planContent.phases?.flatMap((p) => p.tasks ?? []) ?? [];
|
|
51984
|
+
const taskExists = allTasks.some((t) => t.id === args2.taskId);
|
|
51985
|
+
if (!taskExists) {
|
|
51986
|
+
return {
|
|
51987
|
+
success: false,
|
|
51988
|
+
message: `Task ${args2.taskId} not found in plan`,
|
|
51989
|
+
errors: [`Task ${args2.taskId} does not exist in plan.json`]
|
|
51990
|
+
};
|
|
51991
|
+
}
|
|
51992
|
+
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
51993
|
+
const taskState = getTaskState(session, args2.taskId);
|
|
51994
|
+
if (taskState === "complete") {
|
|
51995
|
+
return {
|
|
51996
|
+
success: false,
|
|
51997
|
+
message: `Task ${args2.taskId} is already completed`,
|
|
51998
|
+
errors: [`Cannot declare scope for completed task ${args2.taskId}`]
|
|
51999
|
+
};
|
|
52000
|
+
}
|
|
52001
|
+
}
|
|
52002
|
+
const mergedFiles = [...args2.files, ...args2.whitelist ?? []];
|
|
52003
|
+
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
52004
|
+
session.declaredCoderScope = mergedFiles;
|
|
52005
|
+
session.lastScopeViolation = null;
|
|
52006
|
+
}
|
|
52007
|
+
return {
|
|
52008
|
+
success: true,
|
|
52009
|
+
message: "Scope declared successfully",
|
|
52010
|
+
taskId: args2.taskId,
|
|
52011
|
+
fileCount: mergedFiles.length
|
|
52012
|
+
};
|
|
52013
|
+
}
|
|
52014
|
+
var declare_scope = createSwarmTool({
|
|
52015
|
+
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.",
|
|
52016
|
+
args: {
|
|
52017
|
+
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"'),
|
|
52018
|
+
files: tool.schema.array(tool.schema.string().min(1).max(4096)).min(1).describe("Array of file paths the coder is permitted to modify"),
|
|
52019
|
+
whitelist: tool.schema.array(tool.schema.string().min(1).max(4096)).optional().describe("Additional file paths to whitelist (merged with files)"),
|
|
52020
|
+
working_directory: tool.schema.string().optional().describe("Working directory where the plan is located")
|
|
52021
|
+
},
|
|
52022
|
+
execute: async (args2, _directory) => {
|
|
52023
|
+
return JSON.stringify(await executeDeclareScope(args2, _directory), null, 2);
|
|
52024
|
+
}
|
|
52025
|
+
});
|
|
51515
52026
|
// src/tools/diff.ts
|
|
51516
52027
|
init_dist();
|
|
51517
52028
|
import { execFileSync } from "child_process";
|
|
@@ -51541,20 +52052,20 @@ function validateBase(base) {
|
|
|
51541
52052
|
function validatePaths(paths) {
|
|
51542
52053
|
if (!paths)
|
|
51543
52054
|
return null;
|
|
51544
|
-
for (const
|
|
51545
|
-
if (!
|
|
52055
|
+
for (const path31 of paths) {
|
|
52056
|
+
if (!path31 || path31.length === 0) {
|
|
51546
52057
|
return "empty path not allowed";
|
|
51547
52058
|
}
|
|
51548
|
-
if (
|
|
52059
|
+
if (path31.length > MAX_PATH_LENGTH) {
|
|
51549
52060
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
51550
52061
|
}
|
|
51551
|
-
if (SHELL_METACHARACTERS2.test(
|
|
52062
|
+
if (SHELL_METACHARACTERS2.test(path31)) {
|
|
51552
52063
|
return "path contains shell metacharacters";
|
|
51553
52064
|
}
|
|
51554
|
-
if (
|
|
52065
|
+
if (path31.startsWith("-")) {
|
|
51555
52066
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
51556
52067
|
}
|
|
51557
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
52068
|
+
if (CONTROL_CHAR_PATTERN2.test(path31)) {
|
|
51558
52069
|
return "path contains control characters";
|
|
51559
52070
|
}
|
|
51560
52071
|
}
|
|
@@ -51634,8 +52145,8 @@ var diff = tool({
|
|
|
51634
52145
|
if (parts2.length >= 3) {
|
|
51635
52146
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
51636
52147
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
51637
|
-
const
|
|
51638
|
-
files.push({ path:
|
|
52148
|
+
const path31 = parts2[2];
|
|
52149
|
+
files.push({ path: path31, additions, deletions });
|
|
51639
52150
|
}
|
|
51640
52151
|
}
|
|
51641
52152
|
const contractChanges = [];
|
|
@@ -51864,8 +52375,8 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
51864
52375
|
// src/tools/evidence-check.ts
|
|
51865
52376
|
init_dist();
|
|
51866
52377
|
init_create_tool();
|
|
51867
|
-
import * as
|
|
51868
|
-
import * as
|
|
52378
|
+
import * as fs20 from "fs";
|
|
52379
|
+
import * as path31 from "path";
|
|
51869
52380
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
51870
52381
|
var MAX_EVIDENCE_FILES = 1000;
|
|
51871
52382
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
@@ -51888,9 +52399,9 @@ function validateRequiredTypes(input) {
|
|
|
51888
52399
|
return null;
|
|
51889
52400
|
}
|
|
51890
52401
|
function isPathWithinSwarm(filePath, cwd) {
|
|
51891
|
-
const normalizedCwd =
|
|
51892
|
-
const swarmPath =
|
|
51893
|
-
const normalizedPath =
|
|
52402
|
+
const normalizedCwd = path31.resolve(cwd);
|
|
52403
|
+
const swarmPath = path31.join(normalizedCwd, ".swarm");
|
|
52404
|
+
const normalizedPath = path31.resolve(filePath);
|
|
51894
52405
|
return normalizedPath.startsWith(swarmPath);
|
|
51895
52406
|
}
|
|
51896
52407
|
function parseCompletedTasks(planContent) {
|
|
@@ -51906,12 +52417,12 @@ function parseCompletedTasks(planContent) {
|
|
|
51906
52417
|
}
|
|
51907
52418
|
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
51908
52419
|
const evidence = [];
|
|
51909
|
-
if (!
|
|
52420
|
+
if (!fs20.existsSync(evidenceDir) || !fs20.statSync(evidenceDir).isDirectory()) {
|
|
51910
52421
|
return evidence;
|
|
51911
52422
|
}
|
|
51912
52423
|
let files;
|
|
51913
52424
|
try {
|
|
51914
|
-
files =
|
|
52425
|
+
files = fs20.readdirSync(evidenceDir);
|
|
51915
52426
|
} catch {
|
|
51916
52427
|
return evidence;
|
|
51917
52428
|
}
|
|
@@ -51920,14 +52431,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
51920
52431
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
51921
52432
|
continue;
|
|
51922
52433
|
}
|
|
51923
|
-
const filePath =
|
|
52434
|
+
const filePath = path31.join(evidenceDir, filename);
|
|
51924
52435
|
try {
|
|
51925
|
-
const resolvedPath =
|
|
51926
|
-
const evidenceDirResolved =
|
|
52436
|
+
const resolvedPath = path31.resolve(filePath);
|
|
52437
|
+
const evidenceDirResolved = path31.resolve(evidenceDir);
|
|
51927
52438
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
51928
52439
|
continue;
|
|
51929
52440
|
}
|
|
51930
|
-
const stat2 =
|
|
52441
|
+
const stat2 = fs20.lstatSync(filePath);
|
|
51931
52442
|
if (!stat2.isFile()) {
|
|
51932
52443
|
continue;
|
|
51933
52444
|
}
|
|
@@ -51936,7 +52447,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
51936
52447
|
}
|
|
51937
52448
|
let fileStat;
|
|
51938
52449
|
try {
|
|
51939
|
-
fileStat =
|
|
52450
|
+
fileStat = fs20.statSync(filePath);
|
|
51940
52451
|
if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
|
|
51941
52452
|
continue;
|
|
51942
52453
|
}
|
|
@@ -51945,7 +52456,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
51945
52456
|
}
|
|
51946
52457
|
let content;
|
|
51947
52458
|
try {
|
|
51948
|
-
content =
|
|
52459
|
+
content = fs20.readFileSync(filePath, "utf-8");
|
|
51949
52460
|
} catch {
|
|
51950
52461
|
continue;
|
|
51951
52462
|
}
|
|
@@ -52030,7 +52541,7 @@ var evidence_check = createSwarmTool({
|
|
|
52030
52541
|
return JSON.stringify(errorResult, null, 2);
|
|
52031
52542
|
}
|
|
52032
52543
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
52033
|
-
const planPath =
|
|
52544
|
+
const planPath = path31.join(cwd, PLAN_FILE);
|
|
52034
52545
|
if (!isPathWithinSwarm(planPath, cwd)) {
|
|
52035
52546
|
const errorResult = {
|
|
52036
52547
|
error: "plan file path validation failed",
|
|
@@ -52044,7 +52555,7 @@ var evidence_check = createSwarmTool({
|
|
|
52044
52555
|
}
|
|
52045
52556
|
let planContent;
|
|
52046
52557
|
try {
|
|
52047
|
-
planContent =
|
|
52558
|
+
planContent = fs20.readFileSync(planPath, "utf-8");
|
|
52048
52559
|
} catch {
|
|
52049
52560
|
const result2 = {
|
|
52050
52561
|
message: "No completed tasks found in plan.",
|
|
@@ -52062,7 +52573,7 @@ var evidence_check = createSwarmTool({
|
|
|
52062
52573
|
};
|
|
52063
52574
|
return JSON.stringify(result2, null, 2);
|
|
52064
52575
|
}
|
|
52065
|
-
const evidenceDir =
|
|
52576
|
+
const evidenceDir = path31.join(cwd, EVIDENCE_DIR);
|
|
52066
52577
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
52067
52578
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
52068
52579
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -52079,8 +52590,8 @@ var evidence_check = createSwarmTool({
|
|
|
52079
52590
|
// src/tools/file-extractor.ts
|
|
52080
52591
|
init_tool();
|
|
52081
52592
|
init_create_tool();
|
|
52082
|
-
import * as
|
|
52083
|
-
import * as
|
|
52593
|
+
import * as fs21 from "fs";
|
|
52594
|
+
import * as path32 from "path";
|
|
52084
52595
|
var EXT_MAP = {
|
|
52085
52596
|
python: ".py",
|
|
52086
52597
|
py: ".py",
|
|
@@ -52142,8 +52653,8 @@ var extract_code_blocks = createSwarmTool({
|
|
|
52142
52653
|
execute: async (args2, directory) => {
|
|
52143
52654
|
const { content, output_dir, prefix } = args2;
|
|
52144
52655
|
const targetDir = output_dir || directory;
|
|
52145
|
-
if (!
|
|
52146
|
-
|
|
52656
|
+
if (!fs21.existsSync(targetDir)) {
|
|
52657
|
+
fs21.mkdirSync(targetDir, { recursive: true });
|
|
52147
52658
|
}
|
|
52148
52659
|
if (!content) {
|
|
52149
52660
|
return "Error: content is required";
|
|
@@ -52161,16 +52672,16 @@ var extract_code_blocks = createSwarmTool({
|
|
|
52161
52672
|
if (prefix) {
|
|
52162
52673
|
filename = `${prefix}_${filename}`;
|
|
52163
52674
|
}
|
|
52164
|
-
let filepath =
|
|
52165
|
-
const base =
|
|
52166
|
-
const ext =
|
|
52675
|
+
let filepath = path32.join(targetDir, filename);
|
|
52676
|
+
const base = path32.basename(filepath, path32.extname(filepath));
|
|
52677
|
+
const ext = path32.extname(filepath);
|
|
52167
52678
|
let counter = 1;
|
|
52168
|
-
while (
|
|
52169
|
-
filepath =
|
|
52679
|
+
while (fs21.existsSync(filepath)) {
|
|
52680
|
+
filepath = path32.join(targetDir, `${base}_${counter}${ext}`);
|
|
52170
52681
|
counter++;
|
|
52171
52682
|
}
|
|
52172
52683
|
try {
|
|
52173
|
-
|
|
52684
|
+
fs21.writeFileSync(filepath, code.trim(), "utf-8");
|
|
52174
52685
|
savedFiles.push(filepath);
|
|
52175
52686
|
} catch (error93) {
|
|
52176
52687
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
@@ -52199,7 +52710,7 @@ init_dist();
|
|
|
52199
52710
|
var GITINGEST_TIMEOUT_MS = 1e4;
|
|
52200
52711
|
var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
|
|
52201
52712
|
var GITINGEST_MAX_RETRIES = 2;
|
|
52202
|
-
var delay = (ms) => new Promise((
|
|
52713
|
+
var delay = (ms) => new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
52203
52714
|
async function fetchGitingest(args2) {
|
|
52204
52715
|
for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
|
|
52205
52716
|
try {
|
|
@@ -52278,8 +52789,8 @@ var gitingest = tool({
|
|
|
52278
52789
|
});
|
|
52279
52790
|
// src/tools/imports.ts
|
|
52280
52791
|
init_dist();
|
|
52281
|
-
import * as
|
|
52282
|
-
import * as
|
|
52792
|
+
import * as fs22 from "fs";
|
|
52793
|
+
import * as path33 from "path";
|
|
52283
52794
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
52284
52795
|
var MAX_SYMBOL_LENGTH = 256;
|
|
52285
52796
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -52333,7 +52844,7 @@ function validateSymbolInput(symbol3) {
|
|
|
52333
52844
|
return null;
|
|
52334
52845
|
}
|
|
52335
52846
|
function isBinaryFile2(filePath, buffer) {
|
|
52336
|
-
const ext =
|
|
52847
|
+
const ext = path33.extname(filePath).toLowerCase();
|
|
52337
52848
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
52338
52849
|
return false;
|
|
52339
52850
|
}
|
|
@@ -52357,15 +52868,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
52357
52868
|
const imports = [];
|
|
52358
52869
|
let _resolvedTarget;
|
|
52359
52870
|
try {
|
|
52360
|
-
_resolvedTarget =
|
|
52871
|
+
_resolvedTarget = path33.resolve(targetFile);
|
|
52361
52872
|
} catch {
|
|
52362
52873
|
_resolvedTarget = targetFile;
|
|
52363
52874
|
}
|
|
52364
|
-
const targetBasename =
|
|
52875
|
+
const targetBasename = path33.basename(targetFile, path33.extname(targetFile));
|
|
52365
52876
|
const targetWithExt = targetFile;
|
|
52366
52877
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
52367
|
-
const normalizedTargetWithExt =
|
|
52368
|
-
const normalizedTargetWithoutExt =
|
|
52878
|
+
const normalizedTargetWithExt = path33.normalize(targetWithExt).replace(/\\/g, "/");
|
|
52879
|
+
const normalizedTargetWithoutExt = path33.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
52369
52880
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
52370
52881
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
52371
52882
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -52388,9 +52899,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
52388
52899
|
}
|
|
52389
52900
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
52390
52901
|
let isMatch = false;
|
|
52391
|
-
const _targetDir =
|
|
52392
|
-
const targetExt =
|
|
52393
|
-
const targetBasenameNoExt =
|
|
52902
|
+
const _targetDir = path33.dirname(targetFile);
|
|
52903
|
+
const targetExt = path33.extname(targetFile);
|
|
52904
|
+
const targetBasenameNoExt = path33.basename(targetFile, targetExt);
|
|
52394
52905
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
52395
52906
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
52396
52907
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -52447,7 +52958,7 @@ var SKIP_DIRECTORIES2 = new Set([
|
|
|
52447
52958
|
function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
|
|
52448
52959
|
let entries;
|
|
52449
52960
|
try {
|
|
52450
|
-
entries =
|
|
52961
|
+
entries = fs22.readdirSync(dir);
|
|
52451
52962
|
} catch (e) {
|
|
52452
52963
|
stats.fileErrors.push({
|
|
52453
52964
|
path: dir,
|
|
@@ -52458,13 +52969,13 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
52458
52969
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
52459
52970
|
for (const entry of entries) {
|
|
52460
52971
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
52461
|
-
stats.skippedDirs.push(
|
|
52972
|
+
stats.skippedDirs.push(path33.join(dir, entry));
|
|
52462
52973
|
continue;
|
|
52463
52974
|
}
|
|
52464
|
-
const fullPath =
|
|
52975
|
+
const fullPath = path33.join(dir, entry);
|
|
52465
52976
|
let stat2;
|
|
52466
52977
|
try {
|
|
52467
|
-
stat2 =
|
|
52978
|
+
stat2 = fs22.statSync(fullPath);
|
|
52468
52979
|
} catch (e) {
|
|
52469
52980
|
stats.fileErrors.push({
|
|
52470
52981
|
path: fullPath,
|
|
@@ -52475,7 +52986,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
52475
52986
|
if (stat2.isDirectory()) {
|
|
52476
52987
|
findSourceFiles2(fullPath, files, stats);
|
|
52477
52988
|
} else if (stat2.isFile()) {
|
|
52478
|
-
const ext =
|
|
52989
|
+
const ext = path33.extname(fullPath).toLowerCase();
|
|
52479
52990
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
52480
52991
|
files.push(fullPath);
|
|
52481
52992
|
}
|
|
@@ -52531,8 +53042,8 @@ var imports = tool({
|
|
|
52531
53042
|
return JSON.stringify(errorResult, null, 2);
|
|
52532
53043
|
}
|
|
52533
53044
|
try {
|
|
52534
|
-
const targetFile =
|
|
52535
|
-
if (!
|
|
53045
|
+
const targetFile = path33.resolve(file3);
|
|
53046
|
+
if (!fs22.existsSync(targetFile)) {
|
|
52536
53047
|
const errorResult = {
|
|
52537
53048
|
error: `target file not found: ${file3}`,
|
|
52538
53049
|
target: file3,
|
|
@@ -52542,7 +53053,7 @@ var imports = tool({
|
|
|
52542
53053
|
};
|
|
52543
53054
|
return JSON.stringify(errorResult, null, 2);
|
|
52544
53055
|
}
|
|
52545
|
-
const targetStat =
|
|
53056
|
+
const targetStat = fs22.statSync(targetFile);
|
|
52546
53057
|
if (!targetStat.isFile()) {
|
|
52547
53058
|
const errorResult = {
|
|
52548
53059
|
error: "target must be a file, not a directory",
|
|
@@ -52553,7 +53064,7 @@ var imports = tool({
|
|
|
52553
53064
|
};
|
|
52554
53065
|
return JSON.stringify(errorResult, null, 2);
|
|
52555
53066
|
}
|
|
52556
|
-
const baseDir =
|
|
53067
|
+
const baseDir = path33.dirname(targetFile);
|
|
52557
53068
|
const scanStats = {
|
|
52558
53069
|
skippedDirs: [],
|
|
52559
53070
|
skippedFiles: 0,
|
|
@@ -52568,12 +53079,12 @@ var imports = tool({
|
|
|
52568
53079
|
if (consumers.length >= MAX_CONSUMERS)
|
|
52569
53080
|
break;
|
|
52570
53081
|
try {
|
|
52571
|
-
const stat2 =
|
|
53082
|
+
const stat2 = fs22.statSync(filePath);
|
|
52572
53083
|
if (stat2.size > MAX_FILE_SIZE_BYTES4) {
|
|
52573
53084
|
skippedFileCount++;
|
|
52574
53085
|
continue;
|
|
52575
53086
|
}
|
|
52576
|
-
const buffer =
|
|
53087
|
+
const buffer = fs22.readFileSync(filePath);
|
|
52577
53088
|
if (isBinaryFile2(filePath, buffer)) {
|
|
52578
53089
|
skippedFileCount++;
|
|
52579
53090
|
continue;
|
|
@@ -52642,8 +53153,8 @@ init_lint();
|
|
|
52642
53153
|
|
|
52643
53154
|
// src/tools/phase-complete.ts
|
|
52644
53155
|
init_dist();
|
|
52645
|
-
import * as
|
|
52646
|
-
import * as
|
|
53156
|
+
import * as fs23 from "fs";
|
|
53157
|
+
import * as path34 from "path";
|
|
52647
53158
|
init_manager();
|
|
52648
53159
|
init_utils2();
|
|
52649
53160
|
init_create_tool();
|
|
@@ -52833,7 +53344,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
52833
53344
|
}
|
|
52834
53345
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
52835
53346
|
try {
|
|
52836
|
-
const projectName =
|
|
53347
|
+
const projectName = path34.basename(dir);
|
|
52837
53348
|
const knowledgeConfig = {
|
|
52838
53349
|
enabled: true,
|
|
52839
53350
|
swarm_max_entries: 100,
|
|
@@ -52894,7 +53405,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
52894
53405
|
};
|
|
52895
53406
|
try {
|
|
52896
53407
|
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
52897
|
-
|
|
53408
|
+
fs23.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
52898
53409
|
`, "utf-8");
|
|
52899
53410
|
} catch (writeError) {
|
|
52900
53411
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -52952,8 +53463,8 @@ init_dist();
|
|
|
52952
53463
|
init_discovery();
|
|
52953
53464
|
init_utils();
|
|
52954
53465
|
init_create_tool();
|
|
52955
|
-
import * as
|
|
52956
|
-
import * as
|
|
53466
|
+
import * as fs24 from "fs";
|
|
53467
|
+
import * as path35 from "path";
|
|
52957
53468
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
52958
53469
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
52959
53470
|
function isValidEcosystem(value) {
|
|
@@ -52971,28 +53482,28 @@ function validateArgs3(args2) {
|
|
|
52971
53482
|
function detectEcosystems(directory) {
|
|
52972
53483
|
const ecosystems = [];
|
|
52973
53484
|
const cwd = directory;
|
|
52974
|
-
if (
|
|
53485
|
+
if (fs24.existsSync(path35.join(cwd, "package.json"))) {
|
|
52975
53486
|
ecosystems.push("npm");
|
|
52976
53487
|
}
|
|
52977
|
-
if (
|
|
53488
|
+
if (fs24.existsSync(path35.join(cwd, "pyproject.toml")) || fs24.existsSync(path35.join(cwd, "requirements.txt"))) {
|
|
52978
53489
|
ecosystems.push("pip");
|
|
52979
53490
|
}
|
|
52980
|
-
if (
|
|
53491
|
+
if (fs24.existsSync(path35.join(cwd, "Cargo.toml"))) {
|
|
52981
53492
|
ecosystems.push("cargo");
|
|
52982
53493
|
}
|
|
52983
|
-
if (
|
|
53494
|
+
if (fs24.existsSync(path35.join(cwd, "go.mod"))) {
|
|
52984
53495
|
ecosystems.push("go");
|
|
52985
53496
|
}
|
|
52986
53497
|
try {
|
|
52987
|
-
const files =
|
|
53498
|
+
const files = fs24.readdirSync(cwd);
|
|
52988
53499
|
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
52989
53500
|
ecosystems.push("dotnet");
|
|
52990
53501
|
}
|
|
52991
53502
|
} catch {}
|
|
52992
|
-
if (
|
|
53503
|
+
if (fs24.existsSync(path35.join(cwd, "Gemfile")) || fs24.existsSync(path35.join(cwd, "Gemfile.lock"))) {
|
|
52993
53504
|
ecosystems.push("ruby");
|
|
52994
53505
|
}
|
|
52995
|
-
if (
|
|
53506
|
+
if (fs24.existsSync(path35.join(cwd, "pubspec.yaml"))) {
|
|
52996
53507
|
ecosystems.push("dart");
|
|
52997
53508
|
}
|
|
52998
53509
|
return ecosystems;
|
|
@@ -53005,7 +53516,7 @@ async function runNpmAudit(directory) {
|
|
|
53005
53516
|
stderr: "pipe",
|
|
53006
53517
|
cwd: directory
|
|
53007
53518
|
});
|
|
53008
|
-
const timeoutPromise = new Promise((
|
|
53519
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53009
53520
|
const result = await Promise.race([
|
|
53010
53521
|
Promise.all([
|
|
53011
53522
|
new Response(proc.stdout).text(),
|
|
@@ -53128,7 +53639,7 @@ async function runPipAudit(directory) {
|
|
|
53128
53639
|
stderr: "pipe",
|
|
53129
53640
|
cwd: directory
|
|
53130
53641
|
});
|
|
53131
|
-
const timeoutPromise = new Promise((
|
|
53642
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53132
53643
|
const result = await Promise.race([
|
|
53133
53644
|
Promise.all([
|
|
53134
53645
|
new Response(proc.stdout).text(),
|
|
@@ -53259,7 +53770,7 @@ async function runCargoAudit(directory) {
|
|
|
53259
53770
|
stderr: "pipe",
|
|
53260
53771
|
cwd: directory
|
|
53261
53772
|
});
|
|
53262
|
-
const timeoutPromise = new Promise((
|
|
53773
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53263
53774
|
const result = await Promise.race([
|
|
53264
53775
|
Promise.all([
|
|
53265
53776
|
new Response(proc.stdout).text(),
|
|
@@ -53386,7 +53897,7 @@ async function runGoAudit(directory) {
|
|
|
53386
53897
|
stderr: "pipe",
|
|
53387
53898
|
cwd: directory
|
|
53388
53899
|
});
|
|
53389
|
-
const timeoutPromise = new Promise((
|
|
53900
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53390
53901
|
const result = await Promise.race([
|
|
53391
53902
|
Promise.all([
|
|
53392
53903
|
new Response(proc.stdout).text(),
|
|
@@ -53522,7 +54033,7 @@ async function runDotnetAudit(directory) {
|
|
|
53522
54033
|
stderr: "pipe",
|
|
53523
54034
|
cwd: directory
|
|
53524
54035
|
});
|
|
53525
|
-
const timeoutPromise = new Promise((
|
|
54036
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53526
54037
|
const result = await Promise.race([
|
|
53527
54038
|
Promise.all([
|
|
53528
54039
|
new Response(proc.stdout).text(),
|
|
@@ -53641,7 +54152,7 @@ async function runBundleAudit(directory) {
|
|
|
53641
54152
|
stderr: "pipe",
|
|
53642
54153
|
cwd: directory
|
|
53643
54154
|
});
|
|
53644
|
-
const timeoutPromise = new Promise((
|
|
54155
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53645
54156
|
const result = await Promise.race([
|
|
53646
54157
|
Promise.all([
|
|
53647
54158
|
new Response(proc.stdout).text(),
|
|
@@ -53788,7 +54299,7 @@ async function runDartAudit(directory) {
|
|
|
53788
54299
|
stderr: "pipe",
|
|
53789
54300
|
cwd: directory
|
|
53790
54301
|
});
|
|
53791
|
-
const timeoutPromise = new Promise((
|
|
54302
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13("timeout"), AUDIT_TIMEOUT_MS));
|
|
53792
54303
|
const result = await Promise.race([
|
|
53793
54304
|
Promise.all([
|
|
53794
54305
|
new Response(proc.stdout).text(),
|
|
@@ -54053,8 +54564,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
54053
54564
|
]);
|
|
54054
54565
|
// src/tools/pre-check-batch.ts
|
|
54055
54566
|
init_dist();
|
|
54056
|
-
import * as
|
|
54057
|
-
import * as
|
|
54567
|
+
import * as fs27 from "fs";
|
|
54568
|
+
import * as path38 from "path";
|
|
54058
54569
|
|
|
54059
54570
|
// node_modules/yocto-queue/index.js
|
|
54060
54571
|
class Node2 {
|
|
@@ -54145,26 +54656,26 @@ function pLimit(concurrency) {
|
|
|
54145
54656
|
activeCount--;
|
|
54146
54657
|
resumeNext();
|
|
54147
54658
|
};
|
|
54148
|
-
const run2 = async (function_,
|
|
54659
|
+
const run2 = async (function_, resolve13, arguments_2) => {
|
|
54149
54660
|
const result = (async () => function_(...arguments_2))();
|
|
54150
|
-
|
|
54661
|
+
resolve13(result);
|
|
54151
54662
|
try {
|
|
54152
54663
|
await result;
|
|
54153
54664
|
} catch {}
|
|
54154
54665
|
next();
|
|
54155
54666
|
};
|
|
54156
|
-
const enqueue = (function_,
|
|
54667
|
+
const enqueue = (function_, resolve13, reject, arguments_2) => {
|
|
54157
54668
|
const queueItem = { reject };
|
|
54158
54669
|
new Promise((internalResolve) => {
|
|
54159
54670
|
queueItem.run = internalResolve;
|
|
54160
54671
|
queue.enqueue(queueItem);
|
|
54161
|
-
}).then(run2.bind(undefined, function_,
|
|
54672
|
+
}).then(run2.bind(undefined, function_, resolve13, arguments_2));
|
|
54162
54673
|
if (activeCount < concurrency) {
|
|
54163
54674
|
resumeNext();
|
|
54164
54675
|
}
|
|
54165
54676
|
};
|
|
54166
|
-
const generator = (function_, ...arguments_2) => new Promise((
|
|
54167
|
-
enqueue(function_,
|
|
54677
|
+
const generator = (function_, ...arguments_2) => new Promise((resolve13, reject) => {
|
|
54678
|
+
enqueue(function_, resolve13, reject, arguments_2);
|
|
54168
54679
|
});
|
|
54169
54680
|
Object.defineProperties(generator, {
|
|
54170
54681
|
activeCount: {
|
|
@@ -54221,8 +54732,8 @@ init_lint();
|
|
|
54221
54732
|
init_manager();
|
|
54222
54733
|
|
|
54223
54734
|
// src/quality/metrics.ts
|
|
54224
|
-
import * as
|
|
54225
|
-
import * as
|
|
54735
|
+
import * as fs25 from "fs";
|
|
54736
|
+
import * as path36 from "path";
|
|
54226
54737
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
54227
54738
|
var MIN_DUPLICATION_LINES = 10;
|
|
54228
54739
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -54260,11 +54771,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
54260
54771
|
}
|
|
54261
54772
|
function getComplexityForFile2(filePath) {
|
|
54262
54773
|
try {
|
|
54263
|
-
const stat2 =
|
|
54774
|
+
const stat2 = fs25.statSync(filePath);
|
|
54264
54775
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
54265
54776
|
return null;
|
|
54266
54777
|
}
|
|
54267
|
-
const content =
|
|
54778
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
54268
54779
|
return estimateCyclomaticComplexity(content);
|
|
54269
54780
|
} catch {
|
|
54270
54781
|
return null;
|
|
@@ -54274,8 +54785,8 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
54274
54785
|
let totalComplexity = 0;
|
|
54275
54786
|
const analyzedFiles = [];
|
|
54276
54787
|
for (const file3 of files) {
|
|
54277
|
-
const fullPath =
|
|
54278
|
-
if (!
|
|
54788
|
+
const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
|
|
54789
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54279
54790
|
continue;
|
|
54280
54791
|
}
|
|
54281
54792
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -54396,8 +54907,8 @@ function countGoExports(content) {
|
|
|
54396
54907
|
}
|
|
54397
54908
|
function getExportCountForFile(filePath) {
|
|
54398
54909
|
try {
|
|
54399
|
-
const content =
|
|
54400
|
-
const ext =
|
|
54910
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
54911
|
+
const ext = path36.extname(filePath).toLowerCase();
|
|
54401
54912
|
switch (ext) {
|
|
54402
54913
|
case ".ts":
|
|
54403
54914
|
case ".tsx":
|
|
@@ -54423,8 +54934,8 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
54423
54934
|
let totalExports = 0;
|
|
54424
54935
|
const analyzedFiles = [];
|
|
54425
54936
|
for (const file3 of files) {
|
|
54426
|
-
const fullPath =
|
|
54427
|
-
if (!
|
|
54937
|
+
const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
|
|
54938
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54428
54939
|
continue;
|
|
54429
54940
|
}
|
|
54430
54941
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -54457,16 +54968,16 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
54457
54968
|
let duplicateLines = 0;
|
|
54458
54969
|
const analyzedFiles = [];
|
|
54459
54970
|
for (const file3 of files) {
|
|
54460
|
-
const fullPath =
|
|
54461
|
-
if (!
|
|
54971
|
+
const fullPath = path36.isAbsolute(file3) ? file3 : path36.join(workingDir, file3);
|
|
54972
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54462
54973
|
continue;
|
|
54463
54974
|
}
|
|
54464
54975
|
try {
|
|
54465
|
-
const stat2 =
|
|
54976
|
+
const stat2 = fs25.statSync(fullPath);
|
|
54466
54977
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
54467
54978
|
continue;
|
|
54468
54979
|
}
|
|
54469
|
-
const content =
|
|
54980
|
+
const content = fs25.readFileSync(fullPath, "utf-8");
|
|
54470
54981
|
const lines = content.split(`
|
|
54471
54982
|
`).filter((line) => line.trim().length > 0);
|
|
54472
54983
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -54490,8 +55001,8 @@ function countCodeLines(content) {
|
|
|
54490
55001
|
return lines.length;
|
|
54491
55002
|
}
|
|
54492
55003
|
function isTestFile(filePath) {
|
|
54493
|
-
const basename8 =
|
|
54494
|
-
const _ext =
|
|
55004
|
+
const basename8 = path36.basename(filePath);
|
|
55005
|
+
const _ext = path36.extname(filePath).toLowerCase();
|
|
54495
55006
|
const testPatterns = [
|
|
54496
55007
|
".test.",
|
|
54497
55008
|
".spec.",
|
|
@@ -54572,8 +55083,8 @@ function matchGlobSegment(globSegments, pathSegments) {
|
|
|
54572
55083
|
}
|
|
54573
55084
|
return gIndex === globSegments.length && pIndex === pathSegments.length;
|
|
54574
55085
|
}
|
|
54575
|
-
function matchesGlobSegment(
|
|
54576
|
-
const normalizedPath =
|
|
55086
|
+
function matchesGlobSegment(path37, glob) {
|
|
55087
|
+
const normalizedPath = path37.replace(/\\/g, "/");
|
|
54577
55088
|
const normalizedGlob = glob.replace(/\\/g, "/");
|
|
54578
55089
|
if (normalizedPath.includes("//")) {
|
|
54579
55090
|
return false;
|
|
@@ -54604,8 +55115,8 @@ function simpleGlobToRegex(glob) {
|
|
|
54604
55115
|
function hasGlobstar(glob) {
|
|
54605
55116
|
return glob.includes("**");
|
|
54606
55117
|
}
|
|
54607
|
-
function globMatches(
|
|
54608
|
-
const normalizedPath =
|
|
55118
|
+
function globMatches(path37, glob) {
|
|
55119
|
+
const normalizedPath = path37.replace(/\\/g, "/");
|
|
54609
55120
|
if (!glob || glob === "") {
|
|
54610
55121
|
if (normalizedPath.includes("//")) {
|
|
54611
55122
|
return false;
|
|
@@ -54641,31 +55152,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
54641
55152
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
54642
55153
|
let testLines = 0;
|
|
54643
55154
|
let codeLines = 0;
|
|
54644
|
-
const srcDir =
|
|
54645
|
-
if (
|
|
55155
|
+
const srcDir = path36.join(workingDir, "src");
|
|
55156
|
+
if (fs25.existsSync(srcDir)) {
|
|
54646
55157
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
54647
55158
|
codeLines += lines;
|
|
54648
55159
|
});
|
|
54649
55160
|
}
|
|
54650
55161
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
54651
55162
|
for (const dir of possibleSrcDirs) {
|
|
54652
|
-
const dirPath =
|
|
54653
|
-
if (
|
|
55163
|
+
const dirPath = path36.join(workingDir, dir);
|
|
55164
|
+
if (fs25.existsSync(dirPath)) {
|
|
54654
55165
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
54655
55166
|
codeLines += lines;
|
|
54656
55167
|
});
|
|
54657
55168
|
}
|
|
54658
55169
|
}
|
|
54659
|
-
const testsDir =
|
|
54660
|
-
if (
|
|
55170
|
+
const testsDir = path36.join(workingDir, "tests");
|
|
55171
|
+
if (fs25.existsSync(testsDir)) {
|
|
54661
55172
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
54662
55173
|
testLines += lines;
|
|
54663
55174
|
});
|
|
54664
55175
|
}
|
|
54665
55176
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
54666
55177
|
for (const dir of possibleTestDirs) {
|
|
54667
|
-
const dirPath =
|
|
54668
|
-
if (
|
|
55178
|
+
const dirPath = path36.join(workingDir, dir);
|
|
55179
|
+
if (fs25.existsSync(dirPath) && dirPath !== testsDir) {
|
|
54669
55180
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
54670
55181
|
testLines += lines;
|
|
54671
55182
|
});
|
|
@@ -54677,9 +55188,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
54677
55188
|
}
|
|
54678
55189
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
54679
55190
|
try {
|
|
54680
|
-
const entries =
|
|
55191
|
+
const entries = fs25.readdirSync(dirPath, { withFileTypes: true });
|
|
54681
55192
|
for (const entry of entries) {
|
|
54682
|
-
const fullPath =
|
|
55193
|
+
const fullPath = path36.join(dirPath, entry.name);
|
|
54683
55194
|
if (entry.isDirectory()) {
|
|
54684
55195
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
54685
55196
|
continue;
|
|
@@ -54687,7 +55198,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
54687
55198
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
54688
55199
|
} else if (entry.isFile()) {
|
|
54689
55200
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
54690
|
-
const ext =
|
|
55201
|
+
const ext = path36.extname(entry.name).toLowerCase();
|
|
54691
55202
|
const validExts = [
|
|
54692
55203
|
".ts",
|
|
54693
55204
|
".tsx",
|
|
@@ -54723,7 +55234,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
54723
55234
|
continue;
|
|
54724
55235
|
}
|
|
54725
55236
|
try {
|
|
54726
|
-
const content =
|
|
55237
|
+
const content = fs25.readFileSync(fullPath, "utf-8");
|
|
54727
55238
|
const lines = countCodeLines(content);
|
|
54728
55239
|
callback(lines);
|
|
54729
55240
|
} catch {}
|
|
@@ -54937,8 +55448,8 @@ async function qualityBudget(input, directory) {
|
|
|
54937
55448
|
init_dist();
|
|
54938
55449
|
init_manager();
|
|
54939
55450
|
init_detector();
|
|
54940
|
-
import * as
|
|
54941
|
-
import * as
|
|
55451
|
+
import * as fs26 from "fs";
|
|
55452
|
+
import * as path37 from "path";
|
|
54942
55453
|
import { extname as extname9 } from "path";
|
|
54943
55454
|
|
|
54944
55455
|
// src/sast/rules/c.ts
|
|
@@ -55686,7 +56197,7 @@ function mapSemgrepSeverity(severity) {
|
|
|
55686
56197
|
}
|
|
55687
56198
|
}
|
|
55688
56199
|
async function executeWithTimeout(command, args2, options) {
|
|
55689
|
-
return new Promise((
|
|
56200
|
+
return new Promise((resolve13) => {
|
|
55690
56201
|
const child = spawn(command, args2, {
|
|
55691
56202
|
shell: false,
|
|
55692
56203
|
cwd: options.cwd
|
|
@@ -55695,7 +56206,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
55695
56206
|
let stderr = "";
|
|
55696
56207
|
const timeout = setTimeout(() => {
|
|
55697
56208
|
child.kill("SIGTERM");
|
|
55698
|
-
|
|
56209
|
+
resolve13({
|
|
55699
56210
|
stdout,
|
|
55700
56211
|
stderr: "Process timed out",
|
|
55701
56212
|
exitCode: 124
|
|
@@ -55709,7 +56220,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
55709
56220
|
});
|
|
55710
56221
|
child.on("close", (code) => {
|
|
55711
56222
|
clearTimeout(timeout);
|
|
55712
|
-
|
|
56223
|
+
resolve13({
|
|
55713
56224
|
stdout,
|
|
55714
56225
|
stderr,
|
|
55715
56226
|
exitCode: code ?? 0
|
|
@@ -55717,7 +56228,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
55717
56228
|
});
|
|
55718
56229
|
child.on("error", (err2) => {
|
|
55719
56230
|
clearTimeout(timeout);
|
|
55720
|
-
|
|
56231
|
+
resolve13({
|
|
55721
56232
|
stdout,
|
|
55722
56233
|
stderr: err2.message,
|
|
55723
56234
|
exitCode: 1
|
|
@@ -55805,17 +56316,17 @@ var SEVERITY_ORDER = {
|
|
|
55805
56316
|
};
|
|
55806
56317
|
function shouldSkipFile(filePath) {
|
|
55807
56318
|
try {
|
|
55808
|
-
const stats =
|
|
56319
|
+
const stats = fs26.statSync(filePath);
|
|
55809
56320
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
55810
56321
|
return { skip: true, reason: "file too large" };
|
|
55811
56322
|
}
|
|
55812
56323
|
if (stats.size === 0) {
|
|
55813
56324
|
return { skip: true, reason: "empty file" };
|
|
55814
56325
|
}
|
|
55815
|
-
const fd =
|
|
56326
|
+
const fd = fs26.openSync(filePath, "r");
|
|
55816
56327
|
const buffer = Buffer.alloc(8192);
|
|
55817
|
-
const bytesRead =
|
|
55818
|
-
|
|
56328
|
+
const bytesRead = fs26.readSync(fd, buffer, 0, 8192, 0);
|
|
56329
|
+
fs26.closeSync(fd);
|
|
55819
56330
|
if (bytesRead > 0) {
|
|
55820
56331
|
let nullCount = 0;
|
|
55821
56332
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -55854,7 +56365,7 @@ function countBySeverity(findings) {
|
|
|
55854
56365
|
}
|
|
55855
56366
|
function scanFileWithTierA(filePath, language) {
|
|
55856
56367
|
try {
|
|
55857
|
-
const content =
|
|
56368
|
+
const content = fs26.readFileSync(filePath, "utf-8");
|
|
55858
56369
|
const findings = executeRulesSync(filePath, content, language);
|
|
55859
56370
|
return findings.map((f) => ({
|
|
55860
56371
|
rule_id: f.rule_id,
|
|
@@ -55901,8 +56412,8 @@ async function sastScan(input, directory, config3) {
|
|
|
55901
56412
|
_filesSkipped++;
|
|
55902
56413
|
continue;
|
|
55903
56414
|
}
|
|
55904
|
-
const resolvedPath =
|
|
55905
|
-
if (!
|
|
56415
|
+
const resolvedPath = path37.isAbsolute(filePath) ? filePath : path37.resolve(directory, filePath);
|
|
56416
|
+
if (!fs26.existsSync(resolvedPath)) {
|
|
55906
56417
|
_filesSkipped++;
|
|
55907
56418
|
continue;
|
|
55908
56419
|
}
|
|
@@ -56100,18 +56611,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
56100
56611
|
let resolved;
|
|
56101
56612
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
56102
56613
|
if (isWinAbs) {
|
|
56103
|
-
resolved =
|
|
56104
|
-
} else if (
|
|
56105
|
-
resolved =
|
|
56614
|
+
resolved = path38.win32.resolve(inputPath);
|
|
56615
|
+
} else if (path38.isAbsolute(inputPath)) {
|
|
56616
|
+
resolved = path38.resolve(inputPath);
|
|
56106
56617
|
} else {
|
|
56107
|
-
resolved =
|
|
56618
|
+
resolved = path38.resolve(baseDir, inputPath);
|
|
56108
56619
|
}
|
|
56109
|
-
const workspaceResolved =
|
|
56620
|
+
const workspaceResolved = path38.resolve(workspaceDir);
|
|
56110
56621
|
let relative4;
|
|
56111
56622
|
if (isWinAbs) {
|
|
56112
|
-
relative4 =
|
|
56623
|
+
relative4 = path38.win32.relative(workspaceResolved, resolved);
|
|
56113
56624
|
} else {
|
|
56114
|
-
relative4 =
|
|
56625
|
+
relative4 = path38.relative(workspaceResolved, resolved);
|
|
56115
56626
|
}
|
|
56116
56627
|
if (relative4.startsWith("..")) {
|
|
56117
56628
|
return "path traversal detected";
|
|
@@ -56172,13 +56683,13 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
56172
56683
|
}
|
|
56173
56684
|
async function runLintOnFiles(linter, files, workspaceDir) {
|
|
56174
56685
|
const isWindows = process.platform === "win32";
|
|
56175
|
-
const binDir =
|
|
56686
|
+
const binDir = path38.join(workspaceDir, "node_modules", ".bin");
|
|
56176
56687
|
const validatedFiles = [];
|
|
56177
56688
|
for (const file3 of files) {
|
|
56178
56689
|
if (typeof file3 !== "string") {
|
|
56179
56690
|
continue;
|
|
56180
56691
|
}
|
|
56181
|
-
const resolvedPath =
|
|
56692
|
+
const resolvedPath = path38.resolve(file3);
|
|
56182
56693
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
56183
56694
|
if (validationError) {
|
|
56184
56695
|
continue;
|
|
@@ -56196,10 +56707,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
56196
56707
|
}
|
|
56197
56708
|
let command;
|
|
56198
56709
|
if (linter === "biome") {
|
|
56199
|
-
const biomeBin = isWindows ?
|
|
56710
|
+
const biomeBin = isWindows ? path38.join(binDir, "biome.EXE") : path38.join(binDir, "biome");
|
|
56200
56711
|
command = [biomeBin, "check", ...validatedFiles];
|
|
56201
56712
|
} else {
|
|
56202
|
-
const eslintBin = isWindows ?
|
|
56713
|
+
const eslintBin = isWindows ? path38.join(binDir, "eslint.cmd") : path38.join(binDir, "eslint");
|
|
56203
56714
|
command = [eslintBin, ...validatedFiles];
|
|
56204
56715
|
}
|
|
56205
56716
|
try {
|
|
@@ -56336,7 +56847,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
56336
56847
|
skippedFiles++;
|
|
56337
56848
|
continue;
|
|
56338
56849
|
}
|
|
56339
|
-
const resolvedPath =
|
|
56850
|
+
const resolvedPath = path38.resolve(file3);
|
|
56340
56851
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
56341
56852
|
if (validationError) {
|
|
56342
56853
|
skippedFiles++;
|
|
@@ -56354,14 +56865,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
56354
56865
|
};
|
|
56355
56866
|
}
|
|
56356
56867
|
for (const file3 of validatedFiles) {
|
|
56357
|
-
const ext =
|
|
56868
|
+
const ext = path38.extname(file3).toLowerCase();
|
|
56358
56869
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
56359
56870
|
skippedFiles++;
|
|
56360
56871
|
continue;
|
|
56361
56872
|
}
|
|
56362
56873
|
let stat2;
|
|
56363
56874
|
try {
|
|
56364
|
-
stat2 =
|
|
56875
|
+
stat2 = fs27.statSync(file3);
|
|
56365
56876
|
} catch {
|
|
56366
56877
|
skippedFiles++;
|
|
56367
56878
|
continue;
|
|
@@ -56372,7 +56883,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
56372
56883
|
}
|
|
56373
56884
|
let content;
|
|
56374
56885
|
try {
|
|
56375
|
-
const buffer =
|
|
56886
|
+
const buffer = fs27.readFileSync(file3);
|
|
56376
56887
|
if (buffer.includes(0)) {
|
|
56377
56888
|
skippedFiles++;
|
|
56378
56889
|
continue;
|
|
@@ -56513,7 +57024,7 @@ async function runPreCheckBatch(input, workspaceDir) {
|
|
|
56513
57024
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
56514
57025
|
continue;
|
|
56515
57026
|
}
|
|
56516
|
-
changedFiles.push(
|
|
57027
|
+
changedFiles.push(path38.resolve(directory, file3));
|
|
56517
57028
|
}
|
|
56518
57029
|
if (changedFiles.length === 0) {
|
|
56519
57030
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -56664,7 +57175,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
56664
57175
|
};
|
|
56665
57176
|
return JSON.stringify(errorResult, null, 2);
|
|
56666
57177
|
}
|
|
56667
|
-
const resolvedDirectory =
|
|
57178
|
+
const resolvedDirectory = path38.resolve(typedArgs.directory);
|
|
56668
57179
|
const workspaceAnchor = resolvedDirectory;
|
|
56669
57180
|
const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
|
|
56670
57181
|
if (dirError) {
|
|
@@ -56706,10 +57217,14 @@ var RETRIEVE_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
56706
57217
|
var retrieve_summary = tool({
|
|
56707
57218
|
description: "Retrieve the full content of a stored tool output summary by its ID (e.g. S1, S2). Use this when a prior tool output was summarized and you need the full content.",
|
|
56708
57219
|
args: {
|
|
56709
|
-
id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits.")
|
|
57220
|
+
id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits."),
|
|
57221
|
+
offset: tool.schema.number().min(0).default(0).describe("Line offset to start from (default: 0)."),
|
|
57222
|
+
limit: tool.schema.number().min(1).max(500).default(100).describe("Number of lines to return (default: 100, max: 500).")
|
|
56710
57223
|
},
|
|
56711
57224
|
async execute(args2, context) {
|
|
56712
57225
|
const directory = context.directory;
|
|
57226
|
+
const offset = args2.offset ?? 0;
|
|
57227
|
+
const limit = Math.min(args2.limit ?? 100, 500);
|
|
56713
57228
|
let sanitizedId;
|
|
56714
57229
|
try {
|
|
56715
57230
|
sanitizedId = sanitizeSummaryId(args2.id);
|
|
@@ -56728,14 +57243,47 @@ var retrieve_summary = tool({
|
|
|
56728
57243
|
if (fullOutput.length > RETRIEVE_MAX_BYTES) {
|
|
56729
57244
|
return `Error: summary content exceeds maximum size limit (10 MB).`;
|
|
56730
57245
|
}
|
|
56731
|
-
|
|
57246
|
+
if (fullOutput.length === 0) {
|
|
57247
|
+
return `--- No content (0 lines) ---
|
|
57248
|
+
|
|
57249
|
+
(Summary is empty)`;
|
|
57250
|
+
}
|
|
57251
|
+
const lines = fullOutput.split(`
|
|
57252
|
+
`);
|
|
57253
|
+
const totalLines = lines.length;
|
|
57254
|
+
const clampedOffset = Math.max(0, offset);
|
|
57255
|
+
if (clampedOffset >= totalLines) {
|
|
57256
|
+
const response2 = `--- Offset beyond range ---
|
|
57257
|
+
|
|
57258
|
+
(Range exhausted. Valid offset range: 0-${totalLines - 1})
|
|
57259
|
+
(Content has ${totalLines} line${totalLines === 1 ? "" : "s"})`;
|
|
57260
|
+
return response2;
|
|
57261
|
+
}
|
|
57262
|
+
const startLine = Math.min(clampedOffset, totalLines);
|
|
57263
|
+
const endLine = Math.min(startLine + limit, totalLines);
|
|
57264
|
+
const paginatedLines = lines.slice(startLine, endLine);
|
|
57265
|
+
const paginatedContent = paginatedLines.join(`
|
|
57266
|
+
`);
|
|
57267
|
+
const headerStart = startLine + 1;
|
|
57268
|
+
const headerEnd = endLine;
|
|
57269
|
+
const rangeHeader = `--- Lines ${headerStart}-${headerEnd} of ${totalLines} ---`;
|
|
57270
|
+
let response = `${rangeHeader}
|
|
57271
|
+
${paginatedContent}`;
|
|
57272
|
+
if (endLine < totalLines) {
|
|
57273
|
+
const remaining = totalLines - endLine;
|
|
57274
|
+
response += `
|
|
57275
|
+
|
|
57276
|
+
... ${remaining} more line${remaining === 1 ? "" : "s"}. Use offset=${endLine} to retrieve more.`;
|
|
57277
|
+
}
|
|
57278
|
+
return response;
|
|
56732
57279
|
}
|
|
56733
57280
|
});
|
|
56734
57281
|
// src/tools/save-plan.ts
|
|
56735
57282
|
init_tool();
|
|
56736
57283
|
init_manager2();
|
|
56737
57284
|
init_create_tool();
|
|
56738
|
-
import * as
|
|
57285
|
+
import * as fs28 from "fs";
|
|
57286
|
+
import * as path39 from "path";
|
|
56739
57287
|
function detectPlaceholderContent(args2) {
|
|
56740
57288
|
const issues = [];
|
|
56741
57289
|
const placeholderPattern = /^\[\w[\w\s]*\]$/;
|
|
@@ -56769,12 +57317,33 @@ function validateTargetWorkspace(target, source) {
|
|
|
56769
57317
|
return;
|
|
56770
57318
|
}
|
|
56771
57319
|
async function executeSavePlan(args2, fallbackDir) {
|
|
57320
|
+
const validationErrors = [];
|
|
57321
|
+
for (const phase of args2.phases) {
|
|
57322
|
+
if (!Number.isInteger(phase.id) || phase.id <= 0) {
|
|
57323
|
+
validationErrors.push(`Phase ${phase.id} has invalid id: must be a positive integer`);
|
|
57324
|
+
}
|
|
57325
|
+
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
57326
|
+
for (const task of phase.tasks) {
|
|
57327
|
+
if (!taskIdPattern.test(task.id)) {
|
|
57328
|
+
validationErrors.push(`Task '${task.id}' in phase ${phase.id} has invalid id format: must match N.M pattern (e.g. '1.1', '2.3')`);
|
|
57329
|
+
}
|
|
57330
|
+
}
|
|
57331
|
+
}
|
|
57332
|
+
if (validationErrors.length > 0) {
|
|
57333
|
+
return {
|
|
57334
|
+
success: false,
|
|
57335
|
+
message: "Plan rejected: invalid phase or task IDs",
|
|
57336
|
+
errors: validationErrors,
|
|
57337
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
57338
|
+
};
|
|
57339
|
+
}
|
|
56772
57340
|
const placeholderIssues = detectPlaceholderContent(args2);
|
|
56773
57341
|
if (placeholderIssues.length > 0) {
|
|
56774
57342
|
return {
|
|
56775
57343
|
success: false,
|
|
56776
57344
|
message: "Plan rejected: contains template placeholder content",
|
|
56777
|
-
errors: placeholderIssues
|
|
57345
|
+
errors: placeholderIssues,
|
|
57346
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
56778
57347
|
};
|
|
56779
57348
|
}
|
|
56780
57349
|
const targetWorkspace = args2.working_directory ?? fallbackDir;
|
|
@@ -56782,8 +57351,9 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
56782
57351
|
if (workspaceError) {
|
|
56783
57352
|
return {
|
|
56784
57353
|
success: false,
|
|
56785
|
-
message: "Target workspace validation failed",
|
|
56786
|
-
errors: [workspaceError]
|
|
57354
|
+
message: "Target workspace validation failed: provide working_directory parameter to save_plan",
|
|
57355
|
+
errors: [workspaceError],
|
|
57356
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
56787
57357
|
};
|
|
56788
57358
|
}
|
|
56789
57359
|
const plan = {
|
|
@@ -56816,18 +57386,29 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
56816
57386
|
const dir = targetWorkspace;
|
|
56817
57387
|
try {
|
|
56818
57388
|
await savePlan(dir, plan);
|
|
57389
|
+
try {
|
|
57390
|
+
const markerPath = path39.join(dir, ".swarm", ".plan-write-marker");
|
|
57391
|
+
const marker = JSON.stringify({
|
|
57392
|
+
source: "save_plan",
|
|
57393
|
+
timestamp: new Date().toISOString(),
|
|
57394
|
+
phases_count: plan.phases.length,
|
|
57395
|
+
tasks_count: tasksCount
|
|
57396
|
+
});
|
|
57397
|
+
await fs28.promises.writeFile(markerPath, marker, "utf8");
|
|
57398
|
+
} catch {}
|
|
56819
57399
|
return {
|
|
56820
57400
|
success: true,
|
|
56821
57401
|
message: "Plan saved successfully",
|
|
56822
|
-
plan_path:
|
|
57402
|
+
plan_path: path39.join(dir, ".swarm", "plan.json"),
|
|
56823
57403
|
phases_count: plan.phases.length,
|
|
56824
57404
|
tasks_count: tasksCount
|
|
56825
57405
|
};
|
|
56826
57406
|
} catch (error93) {
|
|
56827
57407
|
return {
|
|
56828
57408
|
success: false,
|
|
56829
|
-
message: "Failed to save plan",
|
|
56830
|
-
errors: [String(error93)]
|
|
57409
|
+
message: "Failed to save plan: retry with save_plan after resolving the error above",
|
|
57410
|
+
errors: [String(error93)],
|
|
57411
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
56831
57412
|
};
|
|
56832
57413
|
}
|
|
56833
57414
|
}
|
|
@@ -56856,8 +57437,8 @@ var save_plan = createSwarmTool({
|
|
|
56856
57437
|
// src/tools/sbom-generate.ts
|
|
56857
57438
|
init_dist();
|
|
56858
57439
|
init_manager();
|
|
56859
|
-
import * as
|
|
56860
|
-
import * as
|
|
57440
|
+
import * as fs29 from "fs";
|
|
57441
|
+
import * as path40 from "path";
|
|
56861
57442
|
|
|
56862
57443
|
// src/sbom/detectors/dart.ts
|
|
56863
57444
|
function parsePubspecLock(content) {
|
|
@@ -57703,9 +58284,9 @@ function findManifestFiles(rootDir) {
|
|
|
57703
58284
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
57704
58285
|
function searchDir(dir) {
|
|
57705
58286
|
try {
|
|
57706
|
-
const entries =
|
|
58287
|
+
const entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
57707
58288
|
for (const entry of entries) {
|
|
57708
|
-
const fullPath =
|
|
58289
|
+
const fullPath = path40.join(dir, entry.name);
|
|
57709
58290
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
57710
58291
|
continue;
|
|
57711
58292
|
}
|
|
@@ -57715,7 +58296,7 @@ function findManifestFiles(rootDir) {
|
|
|
57715
58296
|
for (const pattern of patterns) {
|
|
57716
58297
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
57717
58298
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
57718
|
-
manifestFiles.push(
|
|
58299
|
+
manifestFiles.push(path40.relative(cwd, fullPath));
|
|
57719
58300
|
break;
|
|
57720
58301
|
}
|
|
57721
58302
|
}
|
|
@@ -57732,14 +58313,14 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
57732
58313
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
57733
58314
|
for (const dir of directories) {
|
|
57734
58315
|
try {
|
|
57735
|
-
const entries =
|
|
58316
|
+
const entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
57736
58317
|
for (const entry of entries) {
|
|
57737
|
-
const fullPath =
|
|
58318
|
+
const fullPath = path40.join(dir, entry.name);
|
|
57738
58319
|
if (entry.isFile()) {
|
|
57739
58320
|
for (const pattern of patterns) {
|
|
57740
58321
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
57741
58322
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
57742
|
-
found.push(
|
|
58323
|
+
found.push(path40.relative(workingDir, fullPath));
|
|
57743
58324
|
break;
|
|
57744
58325
|
}
|
|
57745
58326
|
}
|
|
@@ -57752,11 +58333,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
57752
58333
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
57753
58334
|
const dirs = new Set;
|
|
57754
58335
|
for (const file3 of changedFiles) {
|
|
57755
|
-
let currentDir =
|
|
58336
|
+
let currentDir = path40.dirname(file3);
|
|
57756
58337
|
while (true) {
|
|
57757
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
57758
|
-
dirs.add(
|
|
57759
|
-
const parent =
|
|
58338
|
+
if (currentDir && currentDir !== "." && currentDir !== path40.sep) {
|
|
58339
|
+
dirs.add(path40.join(workingDir, currentDir));
|
|
58340
|
+
const parent = path40.dirname(currentDir);
|
|
57760
58341
|
if (parent === currentDir)
|
|
57761
58342
|
break;
|
|
57762
58343
|
currentDir = parent;
|
|
@@ -57770,7 +58351,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
57770
58351
|
}
|
|
57771
58352
|
function ensureOutputDir(outputDir) {
|
|
57772
58353
|
try {
|
|
57773
|
-
|
|
58354
|
+
fs29.mkdirSync(outputDir, { recursive: true });
|
|
57774
58355
|
} catch (error93) {
|
|
57775
58356
|
if (!error93 || error93.code !== "EEXIST") {
|
|
57776
58357
|
throw error93;
|
|
@@ -57862,11 +58443,11 @@ var sbom_generate = createSwarmTool({
|
|
|
57862
58443
|
const processedFiles = [];
|
|
57863
58444
|
for (const manifestFile of manifestFiles) {
|
|
57864
58445
|
try {
|
|
57865
|
-
const fullPath =
|
|
57866
|
-
if (!
|
|
58446
|
+
const fullPath = path40.isAbsolute(manifestFile) ? manifestFile : path40.join(workingDir, manifestFile);
|
|
58447
|
+
if (!fs29.existsSync(fullPath)) {
|
|
57867
58448
|
continue;
|
|
57868
58449
|
}
|
|
57869
|
-
const content =
|
|
58450
|
+
const content = fs29.readFileSync(fullPath, "utf-8");
|
|
57870
58451
|
const components = detectComponents(manifestFile, content);
|
|
57871
58452
|
processedFiles.push(manifestFile);
|
|
57872
58453
|
if (components.length > 0) {
|
|
@@ -57879,8 +58460,8 @@ var sbom_generate = createSwarmTool({
|
|
|
57879
58460
|
const bom = generateCycloneDX(allComponents);
|
|
57880
58461
|
const bomJson = serializeCycloneDX(bom);
|
|
57881
58462
|
const filename = generateSbomFilename();
|
|
57882
|
-
const outputPath =
|
|
57883
|
-
|
|
58463
|
+
const outputPath = path40.join(outputDir, filename);
|
|
58464
|
+
fs29.writeFileSync(outputPath, bomJson, "utf-8");
|
|
57884
58465
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
57885
58466
|
try {
|
|
57886
58467
|
const timestamp = new Date().toISOString();
|
|
@@ -57922,8 +58503,8 @@ var sbom_generate = createSwarmTool({
|
|
|
57922
58503
|
// src/tools/schema-drift.ts
|
|
57923
58504
|
init_dist();
|
|
57924
58505
|
init_create_tool();
|
|
57925
|
-
import * as
|
|
57926
|
-
import * as
|
|
58506
|
+
import * as fs30 from "fs";
|
|
58507
|
+
import * as path41 from "path";
|
|
57927
58508
|
var SPEC_CANDIDATES = [
|
|
57928
58509
|
"openapi.json",
|
|
57929
58510
|
"openapi.yaml",
|
|
@@ -57955,28 +58536,28 @@ function normalizePath2(p) {
|
|
|
57955
58536
|
}
|
|
57956
58537
|
function discoverSpecFile(cwd, specFileArg) {
|
|
57957
58538
|
if (specFileArg) {
|
|
57958
|
-
const resolvedPath =
|
|
57959
|
-
const normalizedCwd = cwd.endsWith(
|
|
58539
|
+
const resolvedPath = path41.resolve(cwd, specFileArg);
|
|
58540
|
+
const normalizedCwd = cwd.endsWith(path41.sep) ? cwd : cwd + path41.sep;
|
|
57960
58541
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
57961
58542
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
57962
58543
|
}
|
|
57963
|
-
const ext =
|
|
58544
|
+
const ext = path41.extname(resolvedPath).toLowerCase();
|
|
57964
58545
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
57965
58546
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
57966
58547
|
}
|
|
57967
|
-
const stats =
|
|
58548
|
+
const stats = fs30.statSync(resolvedPath);
|
|
57968
58549
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
57969
58550
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
57970
58551
|
}
|
|
57971
|
-
if (!
|
|
58552
|
+
if (!fs30.existsSync(resolvedPath)) {
|
|
57972
58553
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
57973
58554
|
}
|
|
57974
58555
|
return resolvedPath;
|
|
57975
58556
|
}
|
|
57976
58557
|
for (const candidate of SPEC_CANDIDATES) {
|
|
57977
|
-
const candidatePath =
|
|
57978
|
-
if (
|
|
57979
|
-
const stats =
|
|
58558
|
+
const candidatePath = path41.resolve(cwd, candidate);
|
|
58559
|
+
if (fs30.existsSync(candidatePath)) {
|
|
58560
|
+
const stats = fs30.statSync(candidatePath);
|
|
57980
58561
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
57981
58562
|
return candidatePath;
|
|
57982
58563
|
}
|
|
@@ -57985,8 +58566,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
57985
58566
|
return null;
|
|
57986
58567
|
}
|
|
57987
58568
|
function parseSpec(specFile) {
|
|
57988
|
-
const content =
|
|
57989
|
-
const ext =
|
|
58569
|
+
const content = fs30.readFileSync(specFile, "utf-8");
|
|
58570
|
+
const ext = path41.extname(specFile).toLowerCase();
|
|
57990
58571
|
if (ext === ".json") {
|
|
57991
58572
|
return parseJsonSpec(content);
|
|
57992
58573
|
}
|
|
@@ -58052,12 +58633,12 @@ function extractRoutes(cwd) {
|
|
|
58052
58633
|
function walkDir(dir) {
|
|
58053
58634
|
let entries;
|
|
58054
58635
|
try {
|
|
58055
|
-
entries =
|
|
58636
|
+
entries = fs30.readdirSync(dir, { withFileTypes: true });
|
|
58056
58637
|
} catch {
|
|
58057
58638
|
return;
|
|
58058
58639
|
}
|
|
58059
58640
|
for (const entry of entries) {
|
|
58060
|
-
const fullPath =
|
|
58641
|
+
const fullPath = path41.join(dir, entry.name);
|
|
58061
58642
|
if (entry.isSymbolicLink()) {
|
|
58062
58643
|
continue;
|
|
58063
58644
|
}
|
|
@@ -58067,7 +58648,7 @@ function extractRoutes(cwd) {
|
|
|
58067
58648
|
}
|
|
58068
58649
|
walkDir(fullPath);
|
|
58069
58650
|
} else if (entry.isFile()) {
|
|
58070
|
-
const ext =
|
|
58651
|
+
const ext = path41.extname(entry.name).toLowerCase();
|
|
58071
58652
|
const baseName = entry.name.toLowerCase();
|
|
58072
58653
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
58073
58654
|
continue;
|
|
@@ -58085,7 +58666,7 @@ function extractRoutes(cwd) {
|
|
|
58085
58666
|
}
|
|
58086
58667
|
function extractRoutesFromFile(filePath) {
|
|
58087
58668
|
const routes = [];
|
|
58088
|
-
const content =
|
|
58669
|
+
const content = fs30.readFileSync(filePath, "utf-8");
|
|
58089
58670
|
const lines = content.split(/\r?\n/);
|
|
58090
58671
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
58091
58672
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -58236,8 +58817,8 @@ init_secretscan();
|
|
|
58236
58817
|
// src/tools/symbols.ts
|
|
58237
58818
|
init_tool();
|
|
58238
58819
|
init_create_tool();
|
|
58239
|
-
import * as
|
|
58240
|
-
import * as
|
|
58820
|
+
import * as fs31 from "fs";
|
|
58821
|
+
import * as path42 from "path";
|
|
58241
58822
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
58242
58823
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
58243
58824
|
function containsControlCharacters(str) {
|
|
@@ -58266,11 +58847,11 @@ function containsWindowsAttacks(str) {
|
|
|
58266
58847
|
}
|
|
58267
58848
|
function isPathInWorkspace(filePath, workspace) {
|
|
58268
58849
|
try {
|
|
58269
|
-
const resolvedPath =
|
|
58270
|
-
const realWorkspace =
|
|
58271
|
-
const realResolvedPath =
|
|
58272
|
-
const relativePath =
|
|
58273
|
-
if (relativePath.startsWith("..") ||
|
|
58850
|
+
const resolvedPath = path42.resolve(workspace, filePath);
|
|
58851
|
+
const realWorkspace = fs31.realpathSync(workspace);
|
|
58852
|
+
const realResolvedPath = fs31.realpathSync(resolvedPath);
|
|
58853
|
+
const relativePath = path42.relative(realWorkspace, realResolvedPath);
|
|
58854
|
+
if (relativePath.startsWith("..") || path42.isAbsolute(relativePath)) {
|
|
58274
58855
|
return false;
|
|
58275
58856
|
}
|
|
58276
58857
|
return true;
|
|
@@ -58282,17 +58863,17 @@ function validatePathForRead(filePath, workspace) {
|
|
|
58282
58863
|
return isPathInWorkspace(filePath, workspace);
|
|
58283
58864
|
}
|
|
58284
58865
|
function extractTSSymbols(filePath, cwd) {
|
|
58285
|
-
const fullPath =
|
|
58866
|
+
const fullPath = path42.join(cwd, filePath);
|
|
58286
58867
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
58287
58868
|
return [];
|
|
58288
58869
|
}
|
|
58289
58870
|
let content;
|
|
58290
58871
|
try {
|
|
58291
|
-
const stats =
|
|
58872
|
+
const stats = fs31.statSync(fullPath);
|
|
58292
58873
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
58293
58874
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
58294
58875
|
}
|
|
58295
|
-
content =
|
|
58876
|
+
content = fs31.readFileSync(fullPath, "utf-8");
|
|
58296
58877
|
} catch {
|
|
58297
58878
|
return [];
|
|
58298
58879
|
}
|
|
@@ -58434,17 +59015,17 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
58434
59015
|
});
|
|
58435
59016
|
}
|
|
58436
59017
|
function extractPythonSymbols(filePath, cwd) {
|
|
58437
|
-
const fullPath =
|
|
59018
|
+
const fullPath = path42.join(cwd, filePath);
|
|
58438
59019
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
58439
59020
|
return [];
|
|
58440
59021
|
}
|
|
58441
59022
|
let content;
|
|
58442
59023
|
try {
|
|
58443
|
-
const stats =
|
|
59024
|
+
const stats = fs31.statSync(fullPath);
|
|
58444
59025
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
58445
59026
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
58446
59027
|
}
|
|
58447
|
-
content =
|
|
59028
|
+
content = fs31.readFileSync(fullPath, "utf-8");
|
|
58448
59029
|
} catch {
|
|
58449
59030
|
return [];
|
|
58450
59031
|
}
|
|
@@ -58517,7 +59098,7 @@ var symbols = createSwarmTool({
|
|
|
58517
59098
|
}, null, 2);
|
|
58518
59099
|
}
|
|
58519
59100
|
const cwd = directory;
|
|
58520
|
-
const ext =
|
|
59101
|
+
const ext = path42.extname(file3);
|
|
58521
59102
|
if (containsControlCharacters(file3)) {
|
|
58522
59103
|
return JSON.stringify({
|
|
58523
59104
|
file: file3,
|
|
@@ -58584,132 +59165,10 @@ var MAX_FILE_SIZE2 = 5 * 1024 * 1024;
|
|
|
58584
59165
|
// src/tools/index.ts
|
|
58585
59166
|
init_test_runner();
|
|
58586
59167
|
|
|
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
59168
|
// src/tools/todo-extract.ts
|
|
58710
59169
|
init_dist();
|
|
58711
59170
|
init_create_tool();
|
|
58712
|
-
import * as
|
|
59171
|
+
import * as fs32 from "fs";
|
|
58713
59172
|
import * as path43 from "path";
|
|
58714
59173
|
var MAX_TEXT_LENGTH = 200;
|
|
58715
59174
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
@@ -58805,7 +59264,7 @@ function isSupportedExtension(filePath) {
|
|
|
58805
59264
|
function findSourceFiles3(dir, files = []) {
|
|
58806
59265
|
let entries;
|
|
58807
59266
|
try {
|
|
58808
|
-
entries =
|
|
59267
|
+
entries = fs32.readdirSync(dir);
|
|
58809
59268
|
} catch {
|
|
58810
59269
|
return files;
|
|
58811
59270
|
}
|
|
@@ -58817,7 +59276,7 @@ function findSourceFiles3(dir, files = []) {
|
|
|
58817
59276
|
const fullPath = path43.join(dir, entry);
|
|
58818
59277
|
let stat2;
|
|
58819
59278
|
try {
|
|
58820
|
-
stat2 =
|
|
59279
|
+
stat2 = fs32.statSync(fullPath);
|
|
58821
59280
|
} catch {
|
|
58822
59281
|
continue;
|
|
58823
59282
|
}
|
|
@@ -58910,7 +59369,7 @@ var todo_extract = createSwarmTool({
|
|
|
58910
59369
|
return JSON.stringify(errorResult, null, 2);
|
|
58911
59370
|
}
|
|
58912
59371
|
const scanPath = resolvedPath;
|
|
58913
|
-
if (!
|
|
59372
|
+
if (!fs32.existsSync(scanPath)) {
|
|
58914
59373
|
const errorResult = {
|
|
58915
59374
|
error: `path not found: ${pathsInput}`,
|
|
58916
59375
|
total: 0,
|
|
@@ -58920,7 +59379,7 @@ var todo_extract = createSwarmTool({
|
|
|
58920
59379
|
return JSON.stringify(errorResult, null, 2);
|
|
58921
59380
|
}
|
|
58922
59381
|
const filesToScan = [];
|
|
58923
|
-
const stat2 =
|
|
59382
|
+
const stat2 = fs32.statSync(scanPath);
|
|
58924
59383
|
if (stat2.isFile()) {
|
|
58925
59384
|
if (isSupportedExtension(scanPath)) {
|
|
58926
59385
|
filesToScan.push(scanPath);
|
|
@@ -58939,11 +59398,11 @@ var todo_extract = createSwarmTool({
|
|
|
58939
59398
|
const allEntries = [];
|
|
58940
59399
|
for (const filePath of filesToScan) {
|
|
58941
59400
|
try {
|
|
58942
|
-
const fileStat =
|
|
59401
|
+
const fileStat = fs32.statSync(filePath);
|
|
58943
59402
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
58944
59403
|
continue;
|
|
58945
59404
|
}
|
|
58946
|
-
const content =
|
|
59405
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
58947
59406
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
58948
59407
|
allEntries.push(...entries);
|
|
58949
59408
|
} catch {}
|
|
@@ -58968,6 +59427,157 @@ var todo_extract = createSwarmTool({
|
|
|
58968
59427
|
return JSON.stringify(result, null, 2);
|
|
58969
59428
|
}
|
|
58970
59429
|
});
|
|
59430
|
+
// src/tools/update-task-status.ts
|
|
59431
|
+
init_tool();
|
|
59432
|
+
init_manager2();
|
|
59433
|
+
import * as fs33 from "fs";
|
|
59434
|
+
import * as path44 from "path";
|
|
59435
|
+
init_create_tool();
|
|
59436
|
+
var VALID_STATUSES = [
|
|
59437
|
+
"pending",
|
|
59438
|
+
"in_progress",
|
|
59439
|
+
"completed",
|
|
59440
|
+
"blocked"
|
|
59441
|
+
];
|
|
59442
|
+
function validateStatus(status) {
|
|
59443
|
+
if (!VALID_STATUSES.includes(status)) {
|
|
59444
|
+
return `Invalid status "${status}". Must be one of: ${VALID_STATUSES.join(", ")}`;
|
|
59445
|
+
}
|
|
59446
|
+
return;
|
|
59447
|
+
}
|
|
59448
|
+
function validateTaskId(taskId) {
|
|
59449
|
+
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
59450
|
+
if (!taskIdPattern.test(taskId)) {
|
|
59451
|
+
return `Invalid task_id "${taskId}". Must match pattern N.M or N.M.P (e.g., "1.1", "1.2.3")`;
|
|
59452
|
+
}
|
|
59453
|
+
return;
|
|
59454
|
+
}
|
|
59455
|
+
function checkReviewerGate(taskId) {
|
|
59456
|
+
try {
|
|
59457
|
+
if (swarmState.agentSessions.size === 0) {
|
|
59458
|
+
return { blocked: false, reason: "" };
|
|
59459
|
+
}
|
|
59460
|
+
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
59461
|
+
const state = getTaskState(session, taskId);
|
|
59462
|
+
if (state === "tests_run" || state === "complete") {
|
|
59463
|
+
return { blocked: false, reason: "" };
|
|
59464
|
+
}
|
|
59465
|
+
}
|
|
59466
|
+
return {
|
|
59467
|
+
blocked: true,
|
|
59468
|
+
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.`
|
|
59469
|
+
};
|
|
59470
|
+
} catch {
|
|
59471
|
+
return { blocked: false, reason: "" };
|
|
59472
|
+
}
|
|
59473
|
+
}
|
|
59474
|
+
async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
59475
|
+
const statusError = validateStatus(args2.status);
|
|
59476
|
+
if (statusError) {
|
|
59477
|
+
return {
|
|
59478
|
+
success: false,
|
|
59479
|
+
message: "Validation failed",
|
|
59480
|
+
errors: [statusError]
|
|
59481
|
+
};
|
|
59482
|
+
}
|
|
59483
|
+
const taskIdError = validateTaskId(args2.task_id);
|
|
59484
|
+
if (taskIdError) {
|
|
59485
|
+
return {
|
|
59486
|
+
success: false,
|
|
59487
|
+
message: "Validation failed",
|
|
59488
|
+
errors: [taskIdError]
|
|
59489
|
+
};
|
|
59490
|
+
}
|
|
59491
|
+
if (args2.status === "completed") {
|
|
59492
|
+
const reviewerCheck = checkReviewerGate(args2.task_id);
|
|
59493
|
+
if (reviewerCheck.blocked) {
|
|
59494
|
+
return {
|
|
59495
|
+
success: false,
|
|
59496
|
+
message: "Gate check failed: reviewer delegation required before marking task as completed",
|
|
59497
|
+
errors: [reviewerCheck.reason]
|
|
59498
|
+
};
|
|
59499
|
+
}
|
|
59500
|
+
}
|
|
59501
|
+
let normalizedDir;
|
|
59502
|
+
if (args2.working_directory != null) {
|
|
59503
|
+
if (args2.working_directory.includes("\x00")) {
|
|
59504
|
+
return {
|
|
59505
|
+
success: false,
|
|
59506
|
+
message: "Invalid working_directory: null bytes are not allowed"
|
|
59507
|
+
};
|
|
59508
|
+
}
|
|
59509
|
+
if (process.platform === "win32") {
|
|
59510
|
+
const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
|
|
59511
|
+
if (devicePathPattern.test(args2.working_directory)) {
|
|
59512
|
+
return {
|
|
59513
|
+
success: false,
|
|
59514
|
+
message: "Invalid working_directory: Windows device paths are not allowed"
|
|
59515
|
+
};
|
|
59516
|
+
}
|
|
59517
|
+
}
|
|
59518
|
+
normalizedDir = path44.normalize(args2.working_directory);
|
|
59519
|
+
const pathParts = normalizedDir.split(path44.sep);
|
|
59520
|
+
if (pathParts.includes("..")) {
|
|
59521
|
+
return {
|
|
59522
|
+
success: false,
|
|
59523
|
+
message: "Invalid working_directory: path traversal sequences (..) are not allowed",
|
|
59524
|
+
errors: [
|
|
59525
|
+
"Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
59526
|
+
]
|
|
59527
|
+
};
|
|
59528
|
+
}
|
|
59529
|
+
const resolvedDir = path44.resolve(normalizedDir);
|
|
59530
|
+
try {
|
|
59531
|
+
const realPath = fs33.realpathSync(resolvedDir);
|
|
59532
|
+
const planPath = path44.join(realPath, ".swarm", "plan.json");
|
|
59533
|
+
if (!fs33.existsSync(planPath)) {
|
|
59534
|
+
return {
|
|
59535
|
+
success: false,
|
|
59536
|
+
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
59537
|
+
errors: [
|
|
59538
|
+
`Invalid working_directory: plan not found in "${realPath}"`
|
|
59539
|
+
]
|
|
59540
|
+
};
|
|
59541
|
+
}
|
|
59542
|
+
} catch {
|
|
59543
|
+
return {
|
|
59544
|
+
success: false,
|
|
59545
|
+
message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
|
|
59546
|
+
errors: [
|
|
59547
|
+
`Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
|
|
59548
|
+
]
|
|
59549
|
+
};
|
|
59550
|
+
}
|
|
59551
|
+
}
|
|
59552
|
+
const directory = normalizedDir ?? fallbackDir ?? process.cwd();
|
|
59553
|
+
try {
|
|
59554
|
+
const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
|
|
59555
|
+
return {
|
|
59556
|
+
success: true,
|
|
59557
|
+
message: "Task status updated successfully",
|
|
59558
|
+
task_id: args2.task_id,
|
|
59559
|
+
new_status: args2.status,
|
|
59560
|
+
current_phase: updatedPlan.current_phase
|
|
59561
|
+
};
|
|
59562
|
+
} catch (error93) {
|
|
59563
|
+
return {
|
|
59564
|
+
success: false,
|
|
59565
|
+
message: "Failed to update task status",
|
|
59566
|
+
errors: [String(error93)]
|
|
59567
|
+
};
|
|
59568
|
+
}
|
|
59569
|
+
}
|
|
59570
|
+
var update_task_status = createSwarmTool({
|
|
59571
|
+
description: "Update the status of a specific task in the implementation plan. " + "Task status can be one of: pending, in_progress, completed, blocked.",
|
|
59572
|
+
args: {
|
|
59573
|
+
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"'),
|
|
59574
|
+
status: tool.schema.enum(["pending", "in_progress", "completed", "blocked"]).describe("New status for the task: pending, in_progress, completed, or blocked"),
|
|
59575
|
+
working_directory: tool.schema.string().optional().describe("Working directory where the plan is located")
|
|
59576
|
+
},
|
|
59577
|
+
execute: async (args2, _directory) => {
|
|
59578
|
+
return JSON.stringify(await executeUpdateTaskStatus(args2, _directory), null, 2);
|
|
59579
|
+
}
|
|
59580
|
+
});
|
|
58971
59581
|
// src/index.ts
|
|
58972
59582
|
init_utils();
|
|
58973
59583
|
|
|
@@ -59051,7 +59661,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
59051
59661
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
59052
59662
|
preflightTriggerManager = new PTM(automationConfig);
|
|
59053
59663
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
59054
|
-
const swarmDir =
|
|
59664
|
+
const swarmDir = path45.resolve(ctx.directory, ".swarm");
|
|
59055
59665
|
statusArtifact = new ASA(swarmDir);
|
|
59056
59666
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
59057
59667
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -59165,7 +59775,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
59165
59775
|
test_runner,
|
|
59166
59776
|
todo_extract,
|
|
59167
59777
|
update_task_status,
|
|
59168
|
-
write_retro
|
|
59778
|
+
write_retro,
|
|
59779
|
+
declare_scope
|
|
59169
59780
|
},
|
|
59170
59781
|
config: async (opencodeConfig) => {
|
|
59171
59782
|
if (!opencodeConfig.agent) {
|