opencode-swarm 6.21.0 → 6.21.2
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 +19 -1
- package/dist/background/plan-sync-worker.d.ts +5 -0
- package/dist/cli/index.js +22 -4
- package/dist/index.js +257 -60
- package/dist/tools/save-plan.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -688,6 +688,24 @@ When truncation is active, a footer is appended:
|
|
|
688
688
|
[output truncated to {maxLines} lines – use `tool_output.per_tool.<tool>` to adjust]
|
|
689
689
|
```
|
|
690
690
|
|
|
691
|
+
## Summarization Settings
|
|
692
|
+
|
|
693
|
+
Control how tool outputs are summarized for LLM context.
|
|
694
|
+
|
|
695
|
+
```json
|
|
696
|
+
{
|
|
697
|
+
"summaries": {
|
|
698
|
+
"threshold_bytes": 102400,
|
|
699
|
+
"exempt_tools": ["retrieve_summary", "task", "read"]
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
- **threshold_bytes** – Output size threshold in bytes before summarization is triggered (default 102400 = 100KB).
|
|
705
|
+
- **exempt_tools** – Tools whose outputs are never summarized. Defaults to `["retrieve_summary", "task", "read"]` to prevent re-summarization loops.
|
|
706
|
+
|
|
707
|
+
> **Note:** The `retrieve_summary` tool supports paginated retrieval via `offset` and `limit` parameters to fetch large summarized outputs in chunks.
|
|
708
|
+
|
|
691
709
|
---
|
|
692
710
|
|
|
693
711
|
### Disabling Agents
|
|
@@ -717,7 +735,7 @@ When truncation is active, a footer is appended:
|
|
|
717
735
|
| `/swarm evidence [task]` | Evidence bundles for a task or all tasks |
|
|
718
736
|
| `/swarm archive [--dry-run]` | Archive old evidence with retention policy |
|
|
719
737
|
| `/swarm benchmark` | Performance benchmarks |
|
|
720
|
-
| `/swarm retrieve [id]` | Retrieve auto-summarized tool outputs |
|
|
738
|
+
| `/swarm retrieve [id]` | Retrieve auto-summarized tool outputs (supports offset/limit pagination) |
|
|
721
739
|
| `/swarm reset --confirm` | Clear swarm state files |
|
|
722
740
|
| `/swarm preflight` | Run phase preflight checks |
|
|
723
741
|
| `/swarm config doctor [--fix]` | Config validation with optional auto-fix |
|
|
@@ -110,6 +110,11 @@ export declare class PlanSyncWorker {
|
|
|
110
110
|
* to prevent callback errors from affecting worker stability
|
|
111
111
|
*/
|
|
112
112
|
private safeCallback;
|
|
113
|
+
/**
|
|
114
|
+
* Advisory: check for unauthorized writes to plan.json outside of save_plan/savePlan
|
|
115
|
+
* Logs a warning if plan.json appears to have been modified after the write marker
|
|
116
|
+
*/
|
|
117
|
+
private checkForUnauthorizedWrite;
|
|
113
118
|
/**
|
|
114
119
|
* Wrap a promise with a timeout
|
|
115
120
|
*/
|
package/dist/cli/index.js
CHANGED
|
@@ -16231,6 +16231,17 @@ ${markdown}`;
|
|
|
16231
16231
|
unlinkSync(mdTempPath);
|
|
16232
16232
|
} catch {}
|
|
16233
16233
|
}
|
|
16234
|
+
try {
|
|
16235
|
+
const markerPath = path6.join(swarmDir, ".plan-write-marker");
|
|
16236
|
+
const tasksCount = validated.phases.reduce((sum, phase) => sum + phase.tasks.length, 0);
|
|
16237
|
+
const marker = JSON.stringify({
|
|
16238
|
+
source: "plan_manager",
|
|
16239
|
+
timestamp: new Date().toISOString(),
|
|
16240
|
+
phases_count: validated.phases.length,
|
|
16241
|
+
tasks_count: tasksCount
|
|
16242
|
+
});
|
|
16243
|
+
await Bun.write(markerPath, marker);
|
|
16244
|
+
} catch {}
|
|
16234
16245
|
}
|
|
16235
16246
|
function derivePlanMarkdown(plan) {
|
|
16236
16247
|
const statusMap = {
|
|
@@ -16865,11 +16876,11 @@ var PhaseCompleteConfigSchema = exports_external.object({
|
|
|
16865
16876
|
});
|
|
16866
16877
|
var SummaryConfigSchema = exports_external.object({
|
|
16867
16878
|
enabled: exports_external.boolean().default(true),
|
|
16868
|
-
threshold_bytes: exports_external.number().min(1024).max(1048576).default(
|
|
16879
|
+
threshold_bytes: exports_external.number().min(1024).max(1048576).default(102400),
|
|
16869
16880
|
max_summary_chars: exports_external.number().min(100).max(5000).default(1000),
|
|
16870
16881
|
max_stored_bytes: exports_external.number().min(10240).max(104857600).default(10485760),
|
|
16871
16882
|
retention_days: exports_external.number().min(1).max(365).default(7),
|
|
16872
|
-
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task"])
|
|
16883
|
+
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task", "read"])
|
|
16873
16884
|
});
|
|
16874
16885
|
var ReviewPassesConfigSchema = exports_external.object({
|
|
16875
16886
|
always_security_review: exports_external.boolean().default(false),
|
|
@@ -35655,7 +35666,7 @@ var HELP_TEXT = [
|
|
|
35655
35666
|
|
|
35656
35667
|
// src/cli/index.ts
|
|
35657
35668
|
var CONFIG_DIR = path14.join(process.env.XDG_CONFIG_HOME || path14.join(os2.homedir(), ".config"), "opencode");
|
|
35658
|
-
var OPENCODE_CONFIG_PATH = path14.join(CONFIG_DIR, "
|
|
35669
|
+
var OPENCODE_CONFIG_PATH = path14.join(CONFIG_DIR, "opencode.json");
|
|
35659
35670
|
var PLUGIN_CONFIG_PATH = path14.join(CONFIG_DIR, "opencode-swarm.json");
|
|
35660
35671
|
var PROMPTS_DIR = path14.join(CONFIG_DIR, "opencode-swarm");
|
|
35661
35672
|
function ensureDir(dir) {
|
|
@@ -35681,9 +35692,16 @@ async function install() {
|
|
|
35681
35692
|
`);
|
|
35682
35693
|
ensureDir(CONFIG_DIR);
|
|
35683
35694
|
ensureDir(PROMPTS_DIR);
|
|
35695
|
+
const LEGACY_CONFIG_PATH = path14.join(CONFIG_DIR, "config.json");
|
|
35684
35696
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
35685
35697
|
if (!opencodeConfig) {
|
|
35686
|
-
|
|
35698
|
+
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
35699
|
+
if (legacyConfig) {
|
|
35700
|
+
console.log("\u26A0 Migrating existing config from config.json to opencode.json...");
|
|
35701
|
+
opencodeConfig = legacyConfig;
|
|
35702
|
+
} else {
|
|
35703
|
+
opencodeConfig = {};
|
|
35704
|
+
}
|
|
35687
35705
|
}
|
|
35688
35706
|
if (!opencodeConfig.plugin) {
|
|
35689
35707
|
opencodeConfig.plugin = [];
|
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);
|
|
@@ -38306,7 +38317,8 @@ var KNOWN_SWARM_PREFIXES = [
|
|
|
38306
38317
|
"custom",
|
|
38307
38318
|
"team",
|
|
38308
38319
|
"project",
|
|
38309
|
-
"swarm"
|
|
38320
|
+
"swarm",
|
|
38321
|
+
"synthetic"
|
|
38310
38322
|
];
|
|
38311
38323
|
var SEPARATORS = ["_", "-", " "];
|
|
38312
38324
|
function stripKnownSwarmPrefix(agentName) {
|
|
@@ -38485,11 +38497,11 @@ var PhaseCompleteConfigSchema = exports_external.object({
|
|
|
38485
38497
|
});
|
|
38486
38498
|
var SummaryConfigSchema = exports_external.object({
|
|
38487
38499
|
enabled: exports_external.boolean().default(true),
|
|
38488
|
-
threshold_bytes: exports_external.number().min(1024).max(1048576).default(
|
|
38500
|
+
threshold_bytes: exports_external.number().min(1024).max(1048576).default(102400),
|
|
38489
38501
|
max_summary_chars: exports_external.number().min(100).max(5000).default(1000),
|
|
38490
38502
|
max_stored_bytes: exports_external.number().min(10240).max(104857600).default(10485760),
|
|
38491
38503
|
retention_days: exports_external.number().min(1).max(365).default(7),
|
|
38492
|
-
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task"])
|
|
38504
|
+
exempt_tools: exports_external.array(exports_external.string()).default(["retrieve_summary", "task", "read"])
|
|
38493
38505
|
});
|
|
38494
38506
|
var ReviewPassesConfigSchema = exports_external.object({
|
|
38495
38507
|
always_security_review: exports_external.boolean().default(false),
|
|
@@ -41666,6 +41678,7 @@ class PlanSyncWorker {
|
|
|
41666
41678
|
this.syncing = true;
|
|
41667
41679
|
try {
|
|
41668
41680
|
log("[PlanSyncWorker] Syncing plan...");
|
|
41681
|
+
this.checkForUnauthorizedWrite();
|
|
41669
41682
|
const plan = await this.withTimeout(loadPlan(this.directory), this.syncTimeoutMs, "Sync operation timed out");
|
|
41670
41683
|
if (plan) {
|
|
41671
41684
|
log("[PlanSyncWorker] Sync complete", {
|
|
@@ -41707,6 +41720,21 @@ class PlanSyncWorker {
|
|
|
41707
41720
|
}
|
|
41708
41721
|
}
|
|
41709
41722
|
}
|
|
41723
|
+
checkForUnauthorizedWrite() {
|
|
41724
|
+
try {
|
|
41725
|
+
const swarmDir = this.getSwarmDir();
|
|
41726
|
+
const planJsonPath = path6.join(swarmDir, "plan.json");
|
|
41727
|
+
const markerPath = path6.join(swarmDir, ".plan-write-marker");
|
|
41728
|
+
const planStats = fs3.statSync(planJsonPath);
|
|
41729
|
+
const planMtimeMs = planStats.mtimeMs;
|
|
41730
|
+
const markerContent = fs3.readFileSync(markerPath, "utf8");
|
|
41731
|
+
const marker = JSON.parse(markerContent);
|
|
41732
|
+
const markerTimestampMs = new Date(marker.timestamp).getTime();
|
|
41733
|
+
if (planMtimeMs > markerTimestampMs + 5000) {
|
|
41734
|
+
log("[PlanSyncWorker] WARNING: plan.json may have been written outside save_plan/savePlan - unauthorized direct write suspected", { planMtimeMs, markerTimestampMs });
|
|
41735
|
+
}
|
|
41736
|
+
} catch {}
|
|
41737
|
+
}
|
|
41710
41738
|
withTimeout(promise2, ms, timeoutMessage) {
|
|
41711
41739
|
return new Promise((resolve4, reject) => {
|
|
41712
41740
|
const timer = setTimeout(() => {
|
|
@@ -42074,6 +42102,9 @@ function recordPhaseAgentDispatch(sessionId, agentName) {
|
|
|
42074
42102
|
session.phaseAgentsDispatched.add(normalizedName);
|
|
42075
42103
|
}
|
|
42076
42104
|
function advanceTaskState(session, taskId, newState) {
|
|
42105
|
+
if (!session.taskWorkflowStates) {
|
|
42106
|
+
session.taskWorkflowStates = new Map;
|
|
42107
|
+
}
|
|
42077
42108
|
const STATE_ORDER = [
|
|
42078
42109
|
"idle",
|
|
42079
42110
|
"coder_delegated",
|
|
@@ -42094,6 +42125,9 @@ function advanceTaskState(session, taskId, newState) {
|
|
|
42094
42125
|
session.taskWorkflowStates.set(taskId, newState);
|
|
42095
42126
|
}
|
|
42096
42127
|
function getTaskState(session, taskId) {
|
|
42128
|
+
if (!session.taskWorkflowStates) {
|
|
42129
|
+
session.taskWorkflowStates = new Map;
|
|
42130
|
+
}
|
|
42097
42131
|
return session.taskWorkflowStates.get(taskId) ?? "idle";
|
|
42098
42132
|
}
|
|
42099
42133
|
|
|
@@ -42985,7 +43019,7 @@ async function handleDarkMatterCommand(directory, args2) {
|
|
|
42985
43019
|
|
|
42986
43020
|
// src/services/diagnose-service.ts
|
|
42987
43021
|
import { execSync } from "child_process";
|
|
42988
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as
|
|
43022
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
42989
43023
|
import path12 from "path";
|
|
42990
43024
|
init_manager();
|
|
42991
43025
|
init_utils2();
|
|
@@ -43291,7 +43325,7 @@ async function checkConfigParseability(directory) {
|
|
|
43291
43325
|
};
|
|
43292
43326
|
}
|
|
43293
43327
|
try {
|
|
43294
|
-
const content =
|
|
43328
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
43295
43329
|
JSON.parse(content);
|
|
43296
43330
|
return {
|
|
43297
43331
|
name: "Config Parseability",
|
|
@@ -43358,7 +43392,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
43358
43392
|
};
|
|
43359
43393
|
}
|
|
43360
43394
|
try {
|
|
43361
|
-
const content =
|
|
43395
|
+
const content = readFileSync4(manifestPath, "utf-8");
|
|
43362
43396
|
const parsed = JSON.parse(content);
|
|
43363
43397
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
43364
43398
|
return {
|
|
@@ -43410,7 +43444,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
43410
43444
|
};
|
|
43411
43445
|
}
|
|
43412
43446
|
try {
|
|
43413
|
-
const content =
|
|
43447
|
+
const content = readFileSync4(eventsPath, "utf-8");
|
|
43414
43448
|
const lines = content.split(`
|
|
43415
43449
|
`).filter((line) => line.trim() !== "");
|
|
43416
43450
|
let malformedCount = 0;
|
|
@@ -43451,7 +43485,7 @@ async function checkSteeringDirectives(directory) {
|
|
|
43451
43485
|
};
|
|
43452
43486
|
}
|
|
43453
43487
|
try {
|
|
43454
|
-
const content =
|
|
43488
|
+
const content = readFileSync4(eventsPath, "utf-8");
|
|
43455
43489
|
const lines = content.split(`
|
|
43456
43490
|
`).filter((line) => line.trim() !== "");
|
|
43457
43491
|
const directivesIssued = [];
|
|
@@ -44419,7 +44453,7 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
44419
44453
|
}
|
|
44420
44454
|
// src/hooks/knowledge-migrator.ts
|
|
44421
44455
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
44422
|
-
import { existsSync as existsSync8, readFileSync as
|
|
44456
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
44423
44457
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
44424
44458
|
import * as path16 from "path";
|
|
44425
44459
|
|
|
@@ -44976,7 +45010,7 @@ function inferProjectName(directory) {
|
|
|
44976
45010
|
const packageJsonPath = path16.join(directory, "package.json");
|
|
44977
45011
|
if (existsSync8(packageJsonPath)) {
|
|
44978
45012
|
try {
|
|
44979
|
-
const pkg = JSON.parse(
|
|
45013
|
+
const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
|
|
44980
45014
|
if (pkg.name && typeof pkg.name === "string") {
|
|
44981
45015
|
return pkg.name;
|
|
44982
45016
|
}
|
|
@@ -47161,7 +47195,7 @@ function shouldMaskToolOutput(msg, index, totalMessages, recentWindowSize, thres
|
|
|
47161
47195
|
return false;
|
|
47162
47196
|
}
|
|
47163
47197
|
const toolName = extractToolName(text);
|
|
47164
|
-
if (toolName && ["retrieve_summary", "task"].includes(toolName.toLowerCase())) {
|
|
47198
|
+
if (toolName && ["retrieve_summary", "task", "read"].includes(toolName.toLowerCase())) {
|
|
47165
47199
|
return false;
|
|
47166
47200
|
}
|
|
47167
47201
|
const age = totalMessages - 1 - index;
|
|
@@ -47235,6 +47269,24 @@ function createDelegationGateHook(config3) {
|
|
|
47235
47269
|
session.qaSkipCount = 0;
|
|
47236
47270
|
session.qaSkipTaskIds = [];
|
|
47237
47271
|
}
|
|
47272
|
+
if (hasReviewer && session.taskWorkflowStates) {
|
|
47273
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
47274
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
47275
|
+
try {
|
|
47276
|
+
advanceTaskState(session, taskId, "reviewer_run");
|
|
47277
|
+
} catch {}
|
|
47278
|
+
}
|
|
47279
|
+
}
|
|
47280
|
+
}
|
|
47281
|
+
if (hasReviewer && hasTestEngineer && session.taskWorkflowStates) {
|
|
47282
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
47283
|
+
if (state === "reviewer_run") {
|
|
47284
|
+
try {
|
|
47285
|
+
advanceTaskState(session, taskId, "tests_run");
|
|
47286
|
+
} catch {}
|
|
47287
|
+
}
|
|
47288
|
+
}
|
|
47289
|
+
}
|
|
47238
47290
|
}
|
|
47239
47291
|
}
|
|
47240
47292
|
};
|
|
@@ -47543,7 +47595,9 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
47543
47595
|
if (!isArchitect && guardrailsEnabled) {
|
|
47544
47596
|
beginInvocation(input.sessionID, agentName);
|
|
47545
47597
|
}
|
|
47546
|
-
|
|
47598
|
+
const delegationTrackerEnabled = config3.hooks?.delegation_tracker === true;
|
|
47599
|
+
const delegationGateEnabled = config3.hooks?.delegation_gate !== false;
|
|
47600
|
+
if ((delegationTrackerEnabled || delegationGateEnabled) && previousAgent && previousAgent !== agentName) {
|
|
47547
47601
|
const entry = {
|
|
47548
47602
|
from: previousAgent,
|
|
47549
47603
|
to: agentName,
|
|
@@ -47554,7 +47608,9 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
47554
47608
|
}
|
|
47555
47609
|
const chain = swarmState.delegationChains.get(input.sessionID);
|
|
47556
47610
|
chain?.push(entry);
|
|
47557
|
-
|
|
47611
|
+
if (delegationTrackerEnabled) {
|
|
47612
|
+
swarmState.pendingEvents++;
|
|
47613
|
+
}
|
|
47558
47614
|
}
|
|
47559
47615
|
};
|
|
47560
47616
|
}
|
|
@@ -47725,13 +47781,14 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47725
47781
|
if (typeof targetPath === "string" && targetPath.length > 0) {
|
|
47726
47782
|
const resolvedTarget = path25.resolve(directory, targetPath).toLowerCase();
|
|
47727
47783
|
const planMdPath = path25.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
47728
|
-
|
|
47729
|
-
|
|
47784
|
+
const planJsonPath = path25.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
47785
|
+
if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
|
|
47786
|
+
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.");
|
|
47730
47787
|
}
|
|
47731
47788
|
}
|
|
47732
47789
|
if (!targetPath && (input.tool === "apply_patch" || input.tool === "patch")) {
|
|
47733
47790
|
const patchText = args2?.input ?? args2?.patch ?? (Array.isArray(args2?.cmd) ? args2.cmd[1] : undefined);
|
|
47734
|
-
if (typeof patchText === "string") {
|
|
47791
|
+
if (typeof patchText === "string" && patchText.length <= 1e6) {
|
|
47735
47792
|
const patchPathPattern = /\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)/gi;
|
|
47736
47793
|
const diffPathPattern = /\+\+\+\s+b\/(.+)/gm;
|
|
47737
47794
|
const paths = new Set;
|
|
@@ -47743,11 +47800,41 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47743
47800
|
if (p !== "/dev/null")
|
|
47744
47801
|
paths.add(p);
|
|
47745
47802
|
}
|
|
47803
|
+
const gitDiffPathPattern = /^diff --git a\/(.+?) b\/(.+?)$/gm;
|
|
47804
|
+
for (const match of patchText.matchAll(gitDiffPathPattern)) {
|
|
47805
|
+
const aPath = match[1].trim();
|
|
47806
|
+
const bPath = match[2].trim();
|
|
47807
|
+
if (aPath !== "/dev/null")
|
|
47808
|
+
paths.add(aPath);
|
|
47809
|
+
if (bPath !== "/dev/null")
|
|
47810
|
+
paths.add(bPath);
|
|
47811
|
+
}
|
|
47812
|
+
const minusPathPattern = /^---\s+a\/(.+)$/gm;
|
|
47813
|
+
for (const match of patchText.matchAll(minusPathPattern)) {
|
|
47814
|
+
const p = match[1].trim();
|
|
47815
|
+
if (p !== "/dev/null")
|
|
47816
|
+
paths.add(p);
|
|
47817
|
+
}
|
|
47818
|
+
const traditionalMinusPattern = /^---\s+([^\s].+?)(?:\t.*)?$/gm;
|
|
47819
|
+
const traditionalPlusPattern = /^\+\+\+\s+([^\s].+?)(?:\t.*)?$/gm;
|
|
47820
|
+
for (const match of patchText.matchAll(traditionalMinusPattern)) {
|
|
47821
|
+
const p = match[1].trim();
|
|
47822
|
+
if (p !== "/dev/null" && !p.startsWith("a/") && !p.startsWith("b/")) {
|
|
47823
|
+
paths.add(p);
|
|
47824
|
+
}
|
|
47825
|
+
}
|
|
47826
|
+
for (const match of patchText.matchAll(traditionalPlusPattern)) {
|
|
47827
|
+
const p = match[1].trim();
|
|
47828
|
+
if (p !== "/dev/null" && !p.startsWith("a/") && !p.startsWith("b/")) {
|
|
47829
|
+
paths.add(p);
|
|
47830
|
+
}
|
|
47831
|
+
}
|
|
47746
47832
|
for (const p of paths) {
|
|
47747
47833
|
const resolvedP = path25.resolve(directory, p);
|
|
47748
|
-
const planMdPath = path25.resolve(directory, ".swarm", "plan.md");
|
|
47749
|
-
|
|
47750
|
-
|
|
47834
|
+
const planMdPath = path25.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
47835
|
+
const planJsonPath = path25.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
47836
|
+
if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
|
|
47837
|
+
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.");
|
|
47751
47838
|
}
|
|
47752
47839
|
if (isOutsideSwarmDir(p, directory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
|
|
47753
47840
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
@@ -47937,6 +48024,26 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
47937
48024
|
};
|
|
47938
48025
|
} else {
|
|
47939
48026
|
session.lastGateFailure = null;
|
|
48027
|
+
if (input.tool === "pre_check_batch") {
|
|
48028
|
+
const successStr = typeof output.output === "string" ? output.output : "";
|
|
48029
|
+
let isPassed = false;
|
|
48030
|
+
try {
|
|
48031
|
+
const result = JSON.parse(successStr);
|
|
48032
|
+
isPassed = result.gates_passed === true;
|
|
48033
|
+
} catch {
|
|
48034
|
+
isPassed = false;
|
|
48035
|
+
}
|
|
48036
|
+
if (isPassed && session.currentTaskId) {
|
|
48037
|
+
try {
|
|
48038
|
+
advanceTaskState(session, session.currentTaskId, "pre_check_passed");
|
|
48039
|
+
} catch (err2) {
|
|
48040
|
+
warn("Failed to advance task state after pre_check_batch pass", {
|
|
48041
|
+
taskId: session.currentTaskId,
|
|
48042
|
+
error: String(err2)
|
|
48043
|
+
});
|
|
48044
|
+
}
|
|
48045
|
+
}
|
|
48046
|
+
}
|
|
47940
48047
|
}
|
|
47941
48048
|
}
|
|
47942
48049
|
const inputArgs = inputArgsByCallID.get(input.callID);
|
|
@@ -49937,7 +50044,11 @@ function createToolSummarizerHook(config3, directory) {
|
|
|
49937
50044
|
if (typeof output.output !== "string" || output.output.length === 0) {
|
|
49938
50045
|
return;
|
|
49939
50046
|
}
|
|
49940
|
-
const exemptTools = config3.exempt_tools ?? [
|
|
50047
|
+
const exemptTools = config3.exempt_tools ?? [
|
|
50048
|
+
"retrieve_summary",
|
|
50049
|
+
"task",
|
|
50050
|
+
"read"
|
|
50051
|
+
];
|
|
49941
50052
|
if (exemptTools.includes(input.tool)) {
|
|
49942
50053
|
return;
|
|
49943
50054
|
}
|
|
@@ -57119,10 +57230,14 @@ var RETRIEVE_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
57119
57230
|
var retrieve_summary = tool({
|
|
57120
57231
|
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.",
|
|
57121
57232
|
args: {
|
|
57122
|
-
id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits.")
|
|
57233
|
+
id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits."),
|
|
57234
|
+
offset: tool.schema.number().min(0).default(0).describe("Line offset to start from (default: 0)."),
|
|
57235
|
+
limit: tool.schema.number().min(1).max(500).default(100).describe("Number of lines to return (default: 100, max: 500).")
|
|
57123
57236
|
},
|
|
57124
57237
|
async execute(args2, context) {
|
|
57125
57238
|
const directory = context.directory;
|
|
57239
|
+
const offset = args2.offset ?? 0;
|
|
57240
|
+
const limit = Math.min(args2.limit ?? 100, 500);
|
|
57126
57241
|
let sanitizedId;
|
|
57127
57242
|
try {
|
|
57128
57243
|
sanitizedId = sanitizeSummaryId(args2.id);
|
|
@@ -57141,13 +57256,46 @@ var retrieve_summary = tool({
|
|
|
57141
57256
|
if (fullOutput.length > RETRIEVE_MAX_BYTES) {
|
|
57142
57257
|
return `Error: summary content exceeds maximum size limit (10 MB).`;
|
|
57143
57258
|
}
|
|
57144
|
-
|
|
57259
|
+
if (fullOutput.length === 0) {
|
|
57260
|
+
return `--- No content (0 lines) ---
|
|
57261
|
+
|
|
57262
|
+
(Summary is empty)`;
|
|
57263
|
+
}
|
|
57264
|
+
const lines = fullOutput.split(`
|
|
57265
|
+
`);
|
|
57266
|
+
const totalLines = lines.length;
|
|
57267
|
+
const clampedOffset = Math.max(0, offset);
|
|
57268
|
+
if (clampedOffset >= totalLines) {
|
|
57269
|
+
const response2 = `--- Offset beyond range ---
|
|
57270
|
+
|
|
57271
|
+
(Range exhausted. Valid offset range: 0-${totalLines - 1})
|
|
57272
|
+
(Content has ${totalLines} line${totalLines === 1 ? "" : "s"})`;
|
|
57273
|
+
return response2;
|
|
57274
|
+
}
|
|
57275
|
+
const startLine = Math.min(clampedOffset, totalLines);
|
|
57276
|
+
const endLine = Math.min(startLine + limit, totalLines);
|
|
57277
|
+
const paginatedLines = lines.slice(startLine, endLine);
|
|
57278
|
+
const paginatedContent = paginatedLines.join(`
|
|
57279
|
+
`);
|
|
57280
|
+
const headerStart = startLine + 1;
|
|
57281
|
+
const headerEnd = endLine;
|
|
57282
|
+
const rangeHeader = `--- Lines ${headerStart}-${headerEnd} of ${totalLines} ---`;
|
|
57283
|
+
let response = `${rangeHeader}
|
|
57284
|
+
${paginatedContent}`;
|
|
57285
|
+
if (endLine < totalLines) {
|
|
57286
|
+
const remaining = totalLines - endLine;
|
|
57287
|
+
response += `
|
|
57288
|
+
|
|
57289
|
+
... ${remaining} more line${remaining === 1 ? "" : "s"}. Use offset=${endLine} to retrieve more.`;
|
|
57290
|
+
}
|
|
57291
|
+
return response;
|
|
57145
57292
|
}
|
|
57146
57293
|
});
|
|
57147
57294
|
// src/tools/save-plan.ts
|
|
57148
57295
|
init_tool();
|
|
57149
57296
|
init_manager2();
|
|
57150
57297
|
init_create_tool();
|
|
57298
|
+
import * as fs28 from "fs";
|
|
57151
57299
|
import * as path39 from "path";
|
|
57152
57300
|
function detectPlaceholderContent(args2) {
|
|
57153
57301
|
const issues = [];
|
|
@@ -57182,12 +57330,33 @@ function validateTargetWorkspace(target, source) {
|
|
|
57182
57330
|
return;
|
|
57183
57331
|
}
|
|
57184
57332
|
async function executeSavePlan(args2, fallbackDir) {
|
|
57333
|
+
const validationErrors = [];
|
|
57334
|
+
for (const phase of args2.phases) {
|
|
57335
|
+
if (!Number.isInteger(phase.id) || phase.id <= 0) {
|
|
57336
|
+
validationErrors.push(`Phase ${phase.id} has invalid id: must be a positive integer`);
|
|
57337
|
+
}
|
|
57338
|
+
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
57339
|
+
for (const task of phase.tasks) {
|
|
57340
|
+
if (!taskIdPattern.test(task.id)) {
|
|
57341
|
+
validationErrors.push(`Task '${task.id}' in phase ${phase.id} has invalid id format: must match N.M pattern (e.g. '1.1', '2.3')`);
|
|
57342
|
+
}
|
|
57343
|
+
}
|
|
57344
|
+
}
|
|
57345
|
+
if (validationErrors.length > 0) {
|
|
57346
|
+
return {
|
|
57347
|
+
success: false,
|
|
57348
|
+
message: "Plan rejected: invalid phase or task IDs",
|
|
57349
|
+
errors: validationErrors,
|
|
57350
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
57351
|
+
};
|
|
57352
|
+
}
|
|
57185
57353
|
const placeholderIssues = detectPlaceholderContent(args2);
|
|
57186
57354
|
if (placeholderIssues.length > 0) {
|
|
57187
57355
|
return {
|
|
57188
57356
|
success: false,
|
|
57189
57357
|
message: "Plan rejected: contains template placeholder content",
|
|
57190
|
-
errors: placeholderIssues
|
|
57358
|
+
errors: placeholderIssues,
|
|
57359
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
57191
57360
|
};
|
|
57192
57361
|
}
|
|
57193
57362
|
const targetWorkspace = args2.working_directory ?? fallbackDir;
|
|
@@ -57195,8 +57364,9 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
57195
57364
|
if (workspaceError) {
|
|
57196
57365
|
return {
|
|
57197
57366
|
success: false,
|
|
57198
|
-
message: "Target workspace validation failed",
|
|
57199
|
-
errors: [workspaceError]
|
|
57367
|
+
message: "Target workspace validation failed: provide working_directory parameter to save_plan",
|
|
57368
|
+
errors: [workspaceError],
|
|
57369
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
57200
57370
|
};
|
|
57201
57371
|
}
|
|
57202
57372
|
const plan = {
|
|
@@ -57229,6 +57399,16 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
57229
57399
|
const dir = targetWorkspace;
|
|
57230
57400
|
try {
|
|
57231
57401
|
await savePlan(dir, plan);
|
|
57402
|
+
try {
|
|
57403
|
+
const markerPath = path39.join(dir, ".swarm", ".plan-write-marker");
|
|
57404
|
+
const marker = JSON.stringify({
|
|
57405
|
+
source: "save_plan",
|
|
57406
|
+
timestamp: new Date().toISOString(),
|
|
57407
|
+
phases_count: plan.phases.length,
|
|
57408
|
+
tasks_count: tasksCount
|
|
57409
|
+
});
|
|
57410
|
+
await fs28.promises.writeFile(markerPath, marker, "utf8");
|
|
57411
|
+
} catch {}
|
|
57232
57412
|
return {
|
|
57233
57413
|
success: true,
|
|
57234
57414
|
message: "Plan saved successfully",
|
|
@@ -57239,8 +57419,9 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
57239
57419
|
} catch (error93) {
|
|
57240
57420
|
return {
|
|
57241
57421
|
success: false,
|
|
57242
|
-
message: "Failed to save plan",
|
|
57243
|
-
errors: [String(error93)]
|
|
57422
|
+
message: "Failed to save plan: retry with save_plan after resolving the error above",
|
|
57423
|
+
errors: [String(error93)],
|
|
57424
|
+
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
57244
57425
|
};
|
|
57245
57426
|
}
|
|
57246
57427
|
}
|
|
@@ -57269,7 +57450,7 @@ var save_plan = createSwarmTool({
|
|
|
57269
57450
|
// src/tools/sbom-generate.ts
|
|
57270
57451
|
init_dist();
|
|
57271
57452
|
init_manager();
|
|
57272
|
-
import * as
|
|
57453
|
+
import * as fs29 from "fs";
|
|
57273
57454
|
import * as path40 from "path";
|
|
57274
57455
|
|
|
57275
57456
|
// src/sbom/detectors/dart.ts
|
|
@@ -58116,7 +58297,7 @@ function findManifestFiles(rootDir) {
|
|
|
58116
58297
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
58117
58298
|
function searchDir(dir) {
|
|
58118
58299
|
try {
|
|
58119
|
-
const entries =
|
|
58300
|
+
const entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
58120
58301
|
for (const entry of entries) {
|
|
58121
58302
|
const fullPath = path40.join(dir, entry.name);
|
|
58122
58303
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
@@ -58145,7 +58326,7 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
58145
58326
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
58146
58327
|
for (const dir of directories) {
|
|
58147
58328
|
try {
|
|
58148
|
-
const entries =
|
|
58329
|
+
const entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
58149
58330
|
for (const entry of entries) {
|
|
58150
58331
|
const fullPath = path40.join(dir, entry.name);
|
|
58151
58332
|
if (entry.isFile()) {
|
|
@@ -58183,7 +58364,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
58183
58364
|
}
|
|
58184
58365
|
function ensureOutputDir(outputDir) {
|
|
58185
58366
|
try {
|
|
58186
|
-
|
|
58367
|
+
fs29.mkdirSync(outputDir, { recursive: true });
|
|
58187
58368
|
} catch (error93) {
|
|
58188
58369
|
if (!error93 || error93.code !== "EEXIST") {
|
|
58189
58370
|
throw error93;
|
|
@@ -58276,10 +58457,10 @@ var sbom_generate = createSwarmTool({
|
|
|
58276
58457
|
for (const manifestFile of manifestFiles) {
|
|
58277
58458
|
try {
|
|
58278
58459
|
const fullPath = path40.isAbsolute(manifestFile) ? manifestFile : path40.join(workingDir, manifestFile);
|
|
58279
|
-
if (!
|
|
58460
|
+
if (!fs29.existsSync(fullPath)) {
|
|
58280
58461
|
continue;
|
|
58281
58462
|
}
|
|
58282
|
-
const content =
|
|
58463
|
+
const content = fs29.readFileSync(fullPath, "utf-8");
|
|
58283
58464
|
const components = detectComponents(manifestFile, content);
|
|
58284
58465
|
processedFiles.push(manifestFile);
|
|
58285
58466
|
if (components.length > 0) {
|
|
@@ -58293,7 +58474,7 @@ var sbom_generate = createSwarmTool({
|
|
|
58293
58474
|
const bomJson = serializeCycloneDX(bom);
|
|
58294
58475
|
const filename = generateSbomFilename();
|
|
58295
58476
|
const outputPath = path40.join(outputDir, filename);
|
|
58296
|
-
|
|
58477
|
+
fs29.writeFileSync(outputPath, bomJson, "utf-8");
|
|
58297
58478
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
58298
58479
|
try {
|
|
58299
58480
|
const timestamp = new Date().toISOString();
|
|
@@ -58335,7 +58516,7 @@ var sbom_generate = createSwarmTool({
|
|
|
58335
58516
|
// src/tools/schema-drift.ts
|
|
58336
58517
|
init_dist();
|
|
58337
58518
|
init_create_tool();
|
|
58338
|
-
import * as
|
|
58519
|
+
import * as fs30 from "fs";
|
|
58339
58520
|
import * as path41 from "path";
|
|
58340
58521
|
var SPEC_CANDIDATES = [
|
|
58341
58522
|
"openapi.json",
|
|
@@ -58377,19 +58558,19 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
58377
58558
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
58378
58559
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
58379
58560
|
}
|
|
58380
|
-
const stats =
|
|
58561
|
+
const stats = fs30.statSync(resolvedPath);
|
|
58381
58562
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
58382
58563
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
58383
58564
|
}
|
|
58384
|
-
if (!
|
|
58565
|
+
if (!fs30.existsSync(resolvedPath)) {
|
|
58385
58566
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
58386
58567
|
}
|
|
58387
58568
|
return resolvedPath;
|
|
58388
58569
|
}
|
|
58389
58570
|
for (const candidate of SPEC_CANDIDATES) {
|
|
58390
58571
|
const candidatePath = path41.resolve(cwd, candidate);
|
|
58391
|
-
if (
|
|
58392
|
-
const stats =
|
|
58572
|
+
if (fs30.existsSync(candidatePath)) {
|
|
58573
|
+
const stats = fs30.statSync(candidatePath);
|
|
58393
58574
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
58394
58575
|
return candidatePath;
|
|
58395
58576
|
}
|
|
@@ -58398,7 +58579,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
58398
58579
|
return null;
|
|
58399
58580
|
}
|
|
58400
58581
|
function parseSpec(specFile) {
|
|
58401
|
-
const content =
|
|
58582
|
+
const content = fs30.readFileSync(specFile, "utf-8");
|
|
58402
58583
|
const ext = path41.extname(specFile).toLowerCase();
|
|
58403
58584
|
if (ext === ".json") {
|
|
58404
58585
|
return parseJsonSpec(content);
|
|
@@ -58465,7 +58646,7 @@ function extractRoutes(cwd) {
|
|
|
58465
58646
|
function walkDir(dir) {
|
|
58466
58647
|
let entries;
|
|
58467
58648
|
try {
|
|
58468
|
-
entries =
|
|
58649
|
+
entries = fs30.readdirSync(dir, { withFileTypes: true });
|
|
58469
58650
|
} catch {
|
|
58470
58651
|
return;
|
|
58471
58652
|
}
|
|
@@ -58498,7 +58679,7 @@ function extractRoutes(cwd) {
|
|
|
58498
58679
|
}
|
|
58499
58680
|
function extractRoutesFromFile(filePath) {
|
|
58500
58681
|
const routes = [];
|
|
58501
|
-
const content =
|
|
58682
|
+
const content = fs30.readFileSync(filePath, "utf-8");
|
|
58502
58683
|
const lines = content.split(/\r?\n/);
|
|
58503
58684
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
58504
58685
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -58649,7 +58830,7 @@ init_secretscan();
|
|
|
58649
58830
|
// src/tools/symbols.ts
|
|
58650
58831
|
init_tool();
|
|
58651
58832
|
init_create_tool();
|
|
58652
|
-
import * as
|
|
58833
|
+
import * as fs31 from "fs";
|
|
58653
58834
|
import * as path42 from "path";
|
|
58654
58835
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
58655
58836
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
@@ -58680,8 +58861,8 @@ function containsWindowsAttacks(str) {
|
|
|
58680
58861
|
function isPathInWorkspace(filePath, workspace) {
|
|
58681
58862
|
try {
|
|
58682
58863
|
const resolvedPath = path42.resolve(workspace, filePath);
|
|
58683
|
-
const realWorkspace =
|
|
58684
|
-
const realResolvedPath =
|
|
58864
|
+
const realWorkspace = fs31.realpathSync(workspace);
|
|
58865
|
+
const realResolvedPath = fs31.realpathSync(resolvedPath);
|
|
58685
58866
|
const relativePath = path42.relative(realWorkspace, realResolvedPath);
|
|
58686
58867
|
if (relativePath.startsWith("..") || path42.isAbsolute(relativePath)) {
|
|
58687
58868
|
return false;
|
|
@@ -58701,11 +58882,11 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
58701
58882
|
}
|
|
58702
58883
|
let content;
|
|
58703
58884
|
try {
|
|
58704
|
-
const stats =
|
|
58885
|
+
const stats = fs31.statSync(fullPath);
|
|
58705
58886
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
58706
58887
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
58707
58888
|
}
|
|
58708
|
-
content =
|
|
58889
|
+
content = fs31.readFileSync(fullPath, "utf-8");
|
|
58709
58890
|
} catch {
|
|
58710
58891
|
return [];
|
|
58711
58892
|
}
|
|
@@ -58853,11 +59034,11 @@ function extractPythonSymbols(filePath, cwd) {
|
|
|
58853
59034
|
}
|
|
58854
59035
|
let content;
|
|
58855
59036
|
try {
|
|
58856
|
-
const stats =
|
|
59037
|
+
const stats = fs31.statSync(fullPath);
|
|
58857
59038
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
58858
59039
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
58859
59040
|
}
|
|
58860
|
-
content =
|
|
59041
|
+
content = fs31.readFileSync(fullPath, "utf-8");
|
|
58861
59042
|
} catch {
|
|
58862
59043
|
return [];
|
|
58863
59044
|
}
|
|
@@ -59000,7 +59181,7 @@ init_test_runner();
|
|
|
59000
59181
|
// src/tools/todo-extract.ts
|
|
59001
59182
|
init_dist();
|
|
59002
59183
|
init_create_tool();
|
|
59003
|
-
import * as
|
|
59184
|
+
import * as fs32 from "fs";
|
|
59004
59185
|
import * as path43 from "path";
|
|
59005
59186
|
var MAX_TEXT_LENGTH = 200;
|
|
59006
59187
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
@@ -59096,7 +59277,7 @@ function isSupportedExtension(filePath) {
|
|
|
59096
59277
|
function findSourceFiles3(dir, files = []) {
|
|
59097
59278
|
let entries;
|
|
59098
59279
|
try {
|
|
59099
|
-
entries =
|
|
59280
|
+
entries = fs32.readdirSync(dir);
|
|
59100
59281
|
} catch {
|
|
59101
59282
|
return files;
|
|
59102
59283
|
}
|
|
@@ -59108,7 +59289,7 @@ function findSourceFiles3(dir, files = []) {
|
|
|
59108
59289
|
const fullPath = path43.join(dir, entry);
|
|
59109
59290
|
let stat2;
|
|
59110
59291
|
try {
|
|
59111
|
-
stat2 =
|
|
59292
|
+
stat2 = fs32.statSync(fullPath);
|
|
59112
59293
|
} catch {
|
|
59113
59294
|
continue;
|
|
59114
59295
|
}
|
|
@@ -59201,7 +59382,7 @@ var todo_extract = createSwarmTool({
|
|
|
59201
59382
|
return JSON.stringify(errorResult, null, 2);
|
|
59202
59383
|
}
|
|
59203
59384
|
const scanPath = resolvedPath;
|
|
59204
|
-
if (!
|
|
59385
|
+
if (!fs32.existsSync(scanPath)) {
|
|
59205
59386
|
const errorResult = {
|
|
59206
59387
|
error: `path not found: ${pathsInput}`,
|
|
59207
59388
|
total: 0,
|
|
@@ -59211,7 +59392,7 @@ var todo_extract = createSwarmTool({
|
|
|
59211
59392
|
return JSON.stringify(errorResult, null, 2);
|
|
59212
59393
|
}
|
|
59213
59394
|
const filesToScan = [];
|
|
59214
|
-
const stat2 =
|
|
59395
|
+
const stat2 = fs32.statSync(scanPath);
|
|
59215
59396
|
if (stat2.isFile()) {
|
|
59216
59397
|
if (isSupportedExtension(scanPath)) {
|
|
59217
59398
|
filesToScan.push(scanPath);
|
|
@@ -59230,11 +59411,11 @@ var todo_extract = createSwarmTool({
|
|
|
59230
59411
|
const allEntries = [];
|
|
59231
59412
|
for (const filePath of filesToScan) {
|
|
59232
59413
|
try {
|
|
59233
|
-
const fileStat =
|
|
59414
|
+
const fileStat = fs32.statSync(filePath);
|
|
59234
59415
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
59235
59416
|
continue;
|
|
59236
59417
|
}
|
|
59237
|
-
const content =
|
|
59418
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
59238
59419
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
59239
59420
|
allEntries.push(...entries);
|
|
59240
59421
|
} catch {}
|
|
@@ -59262,7 +59443,7 @@ var todo_extract = createSwarmTool({
|
|
|
59262
59443
|
// src/tools/update-task-status.ts
|
|
59263
59444
|
init_tool();
|
|
59264
59445
|
init_manager2();
|
|
59265
|
-
import * as
|
|
59446
|
+
import * as fs33 from "fs";
|
|
59266
59447
|
import * as path44 from "path";
|
|
59267
59448
|
init_create_tool();
|
|
59268
59449
|
var VALID_STATUSES = [
|
|
@@ -59295,9 +59476,15 @@ function checkReviewerGate(taskId) {
|
|
|
59295
59476
|
return { blocked: false, reason: "" };
|
|
59296
59477
|
}
|
|
59297
59478
|
}
|
|
59479
|
+
const stateEntries = [];
|
|
59480
|
+
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
59481
|
+
const state = getTaskState(session, taskId);
|
|
59482
|
+
stateEntries.push(`${sessionId}: ${state}`);
|
|
59483
|
+
}
|
|
59484
|
+
const currentStateStr = stateEntries.length > 0 ? stateEntries.join(", ") : "no active sessions";
|
|
59298
59485
|
return {
|
|
59299
59486
|
blocked: true,
|
|
59300
|
-
reason: `Task ${taskId} has not passed QA gates
|
|
59487
|
+
reason: `Task ${taskId} has not passed QA gates. Current state: [${currentStateStr}]. Required state: tests_run or complete. Do not write directly to plan files \u2014 use update_task_status after running mega_reviewer and mega_test_engineer.`
|
|
59301
59488
|
};
|
|
59302
59489
|
} catch {
|
|
59303
59490
|
return { blocked: false, reason: "" };
|
|
@@ -59320,6 +59507,16 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
59320
59507
|
errors: [taskIdError]
|
|
59321
59508
|
};
|
|
59322
59509
|
}
|
|
59510
|
+
if (args2.status === "in_progress") {
|
|
59511
|
+
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
59512
|
+
const currentState = getTaskState(session, args2.task_id);
|
|
59513
|
+
if (currentState === "idle") {
|
|
59514
|
+
try {
|
|
59515
|
+
advanceTaskState(session, args2.task_id, "coder_delegated");
|
|
59516
|
+
} catch {}
|
|
59517
|
+
}
|
|
59518
|
+
}
|
|
59519
|
+
}
|
|
59323
59520
|
if (args2.status === "completed") {
|
|
59324
59521
|
const reviewerCheck = checkReviewerGate(args2.task_id);
|
|
59325
59522
|
if (reviewerCheck.blocked) {
|
|
@@ -59360,9 +59557,9 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
59360
59557
|
}
|
|
59361
59558
|
const resolvedDir = path44.resolve(normalizedDir);
|
|
59362
59559
|
try {
|
|
59363
|
-
const realPath =
|
|
59560
|
+
const realPath = fs33.realpathSync(resolvedDir);
|
|
59364
59561
|
const planPath = path44.join(realPath, ".swarm", "plan.json");
|
|
59365
|
-
if (!
|
|
59562
|
+
if (!fs33.existsSync(planPath)) {
|
|
59366
59563
|
return {
|
|
59367
59564
|
success: false,
|
|
59368
59565
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.21.
|
|
3
|
+
"version": "6.21.2",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|