opencode-swarm 6.38.0 → 6.40.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 +1 -1
- package/dist/cli/index.js +246 -94
- package/dist/commands/index.d.ts +1 -1
- package/dist/config/plan-schema.d.ts +2 -0
- package/dist/config/schema.d.ts +3 -2
- package/dist/index.js +1335 -921
- package/dist/services/compaction-service.d.ts +2 -2
- package/dist/session/snapshot-reader.d.ts +8 -0
- package/dist/tools/lint.d.ts +1 -2
- package/dist/tools/save-plan.d.ts +1 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/utils/path-security.d.ts +26 -0
- package/package.json +4 -1
- /package/dist/commands/{write_retro.d.ts → write-retro.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Most AI coding tools let one model write code and ask that same model whether th
|
|
|
27
27
|
### Key Features
|
|
28
28
|
|
|
29
29
|
- 🏗️ **11 specialized agents** — architect, coder, reviewer, test engineer, critic, critic_sounding_board, critic_drift_verifier, explorer, SME, docs, designer
|
|
30
|
-
- 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval
|
|
30
|
+
- 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval (bypassed in turbo mode)
|
|
31
31
|
- 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion (bypassed in turbo mode)
|
|
32
32
|
- 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
|
|
33
33
|
- 🌐 **11 languages** — TypeScript, Python, Go, Rust, Java, Kotlin, C#, C/C++, Swift, Dart, Ruby
|
package/dist/cli/index.js
CHANGED
|
@@ -14590,7 +14590,8 @@ var init_plan_schema = __esm(() => {
|
|
|
14590
14590
|
id: exports_external.number().int().min(1),
|
|
14591
14591
|
name: exports_external.string().min(1),
|
|
14592
14592
|
status: PhaseStatusSchema.default("pending"),
|
|
14593
|
-
tasks: exports_external.array(TaskSchema).default([])
|
|
14593
|
+
tasks: exports_external.array(TaskSchema).default([]),
|
|
14594
|
+
required_agents: exports_external.array(exports_external.string()).optional()
|
|
14594
14595
|
});
|
|
14595
14596
|
PlanSchema = exports_external.object({
|
|
14596
14597
|
schema_version: exports_external.literal("1.0.0"),
|
|
@@ -17319,8 +17320,8 @@ function getTaskBlockers(task, summary, status) {
|
|
|
17319
17320
|
}
|
|
17320
17321
|
return blockers;
|
|
17321
17322
|
}
|
|
17322
|
-
async function buildTaskSummary(task, taskId) {
|
|
17323
|
-
const result = await loadEvidence(
|
|
17323
|
+
async function buildTaskSummary(directory, task, taskId) {
|
|
17324
|
+
const result = await loadEvidence(directory, taskId);
|
|
17324
17325
|
const bundle = result.status === "found" ? result.bundle : null;
|
|
17325
17326
|
const phase = task?.phase ?? 0;
|
|
17326
17327
|
const status = getTaskStatus(task, bundle);
|
|
@@ -17349,18 +17350,18 @@ async function buildTaskSummary(task, taskId) {
|
|
|
17349
17350
|
lastEvidenceTimestamp: lastTimestamp
|
|
17350
17351
|
};
|
|
17351
17352
|
}
|
|
17352
|
-
async function buildPhaseSummary(phase) {
|
|
17353
|
-
const taskIds = await listEvidenceTaskIds(
|
|
17353
|
+
async function buildPhaseSummary(directory, phase) {
|
|
17354
|
+
const taskIds = await listEvidenceTaskIds(directory);
|
|
17354
17355
|
const phaseTaskIds = new Set(phase.tasks.map((t) => t.id));
|
|
17355
17356
|
const taskSummaries = [];
|
|
17356
17357
|
const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
|
|
17357
17358
|
for (const task of phase.tasks) {
|
|
17358
|
-
const summary = await buildTaskSummary(task, task.id);
|
|
17359
|
+
const summary = await buildTaskSummary(directory, task, task.id);
|
|
17359
17360
|
taskSummaries.push(summary);
|
|
17360
17361
|
}
|
|
17361
17362
|
const extraTaskIds = taskIds.filter((id) => !phaseTaskIds.has(id));
|
|
17362
17363
|
for (const taskId of extraTaskIds) {
|
|
17363
|
-
const summary = await buildTaskSummary(undefined, taskId);
|
|
17364
|
+
const summary = await buildTaskSummary(directory, undefined, taskId);
|
|
17364
17365
|
if (summary.phase === phase.id) {
|
|
17365
17366
|
taskSummaries.push(summary);
|
|
17366
17367
|
}
|
|
@@ -17461,7 +17462,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
|
|
|
17461
17462
|
let totalTasks = 0;
|
|
17462
17463
|
let completedTasks = 0;
|
|
17463
17464
|
for (const phase of phasesToProcess) {
|
|
17464
|
-
const summary = await buildPhaseSummary(phase);
|
|
17465
|
+
const summary = await buildPhaseSummary(directory, phase);
|
|
17465
17466
|
phaseSummaries.push(summary);
|
|
17466
17467
|
totalTasks += summary.totalTasks;
|
|
17467
17468
|
completedTasks += summary.completedTasks;
|
|
@@ -17620,7 +17621,13 @@ var TOOL_NAMES = [
|
|
|
17620
17621
|
"update_task_status",
|
|
17621
17622
|
"write_retro",
|
|
17622
17623
|
"declare_scope",
|
|
17623
|
-
"knowledge_query"
|
|
17624
|
+
"knowledge_query",
|
|
17625
|
+
"doc_scan",
|
|
17626
|
+
"doc_extract",
|
|
17627
|
+
"curator_analyze",
|
|
17628
|
+
"knowledgeAdd",
|
|
17629
|
+
"knowledgeRecall",
|
|
17630
|
+
"knowledgeRemove"
|
|
17624
17631
|
];
|
|
17625
17632
|
var TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
17626
17633
|
|
|
@@ -17645,6 +17652,7 @@ var AGENT_TOOL_MAP = {
|
|
|
17645
17652
|
architect: [
|
|
17646
17653
|
"checkpoint",
|
|
17647
17654
|
"check_gate_status",
|
|
17655
|
+
"completion_verify",
|
|
17648
17656
|
"complexity_hotspots",
|
|
17649
17657
|
"detect_domains",
|
|
17650
17658
|
"evidence_check",
|
|
@@ -17656,6 +17664,7 @@ var AGENT_TOOL_MAP = {
|
|
|
17656
17664
|
"diff",
|
|
17657
17665
|
"pkg_audit",
|
|
17658
17666
|
"pre_check_batch",
|
|
17667
|
+
"quality_budget",
|
|
17659
17668
|
"retrieve_summary",
|
|
17660
17669
|
"save_plan",
|
|
17661
17670
|
"schema_drift",
|
|
@@ -17665,7 +17674,19 @@ var AGENT_TOOL_MAP = {
|
|
|
17665
17674
|
"todo_extract",
|
|
17666
17675
|
"update_task_status",
|
|
17667
17676
|
"write_retro",
|
|
17668
|
-
"declare_scope"
|
|
17677
|
+
"declare_scope",
|
|
17678
|
+
"sast_scan",
|
|
17679
|
+
"sbom_generate",
|
|
17680
|
+
"build_check",
|
|
17681
|
+
"syntax_check",
|
|
17682
|
+
"placeholder_scan",
|
|
17683
|
+
"phase_complete",
|
|
17684
|
+
"doc_scan",
|
|
17685
|
+
"doc_extract",
|
|
17686
|
+
"curator_analyze",
|
|
17687
|
+
"knowledgeAdd",
|
|
17688
|
+
"knowledgeRecall",
|
|
17689
|
+
"knowledgeRemove"
|
|
17669
17690
|
],
|
|
17670
17691
|
explorer: [
|
|
17671
17692
|
"complexity_hotspots",
|
|
@@ -17676,7 +17697,9 @@ var AGENT_TOOL_MAP = {
|
|
|
17676
17697
|
"retrieve_summary",
|
|
17677
17698
|
"schema_drift",
|
|
17678
17699
|
"symbols",
|
|
17679
|
-
"todo_extract"
|
|
17700
|
+
"todo_extract",
|
|
17701
|
+
"doc_scan",
|
|
17702
|
+
"knowledgeRecall"
|
|
17680
17703
|
],
|
|
17681
17704
|
coder: [
|
|
17682
17705
|
"diff",
|
|
@@ -17684,7 +17707,11 @@ var AGENT_TOOL_MAP = {
|
|
|
17684
17707
|
"lint",
|
|
17685
17708
|
"symbols",
|
|
17686
17709
|
"extract_code_blocks",
|
|
17687
|
-
"retrieve_summary"
|
|
17710
|
+
"retrieve_summary",
|
|
17711
|
+
"build_check",
|
|
17712
|
+
"syntax_check",
|
|
17713
|
+
"knowledgeAdd",
|
|
17714
|
+
"knowledgeRecall"
|
|
17688
17715
|
],
|
|
17689
17716
|
test_engineer: [
|
|
17690
17717
|
"test_runner",
|
|
@@ -17694,7 +17721,9 @@ var AGENT_TOOL_MAP = {
|
|
|
17694
17721
|
"retrieve_summary",
|
|
17695
17722
|
"imports",
|
|
17696
17723
|
"complexity_hotspots",
|
|
17697
|
-
"pkg_audit"
|
|
17724
|
+
"pkg_audit",
|
|
17725
|
+
"build_check",
|
|
17726
|
+
"syntax_check"
|
|
17698
17727
|
],
|
|
17699
17728
|
sme: [
|
|
17700
17729
|
"complexity_hotspots",
|
|
@@ -17703,7 +17732,8 @@ var AGENT_TOOL_MAP = {
|
|
|
17703
17732
|
"imports",
|
|
17704
17733
|
"retrieve_summary",
|
|
17705
17734
|
"schema_drift",
|
|
17706
|
-
"symbols"
|
|
17735
|
+
"symbols",
|
|
17736
|
+
"knowledgeRecall"
|
|
17707
17737
|
],
|
|
17708
17738
|
reviewer: [
|
|
17709
17739
|
"diff",
|
|
@@ -17716,28 +17746,34 @@ var AGENT_TOOL_MAP = {
|
|
|
17716
17746
|
"complexity_hotspots",
|
|
17717
17747
|
"retrieve_summary",
|
|
17718
17748
|
"extract_code_blocks",
|
|
17719
|
-
"test_runner"
|
|
17749
|
+
"test_runner",
|
|
17750
|
+
"sast_scan",
|
|
17751
|
+
"placeholder_scan",
|
|
17752
|
+
"knowledgeRecall"
|
|
17720
17753
|
],
|
|
17721
17754
|
critic: [
|
|
17722
17755
|
"complexity_hotspots",
|
|
17723
17756
|
"detect_domains",
|
|
17724
17757
|
"imports",
|
|
17725
17758
|
"retrieve_summary",
|
|
17726
|
-
"symbols"
|
|
17759
|
+
"symbols",
|
|
17760
|
+
"knowledgeRecall"
|
|
17727
17761
|
],
|
|
17728
17762
|
critic_sounding_board: [
|
|
17729
17763
|
"complexity_hotspots",
|
|
17730
17764
|
"detect_domains",
|
|
17731
17765
|
"imports",
|
|
17732
17766
|
"retrieve_summary",
|
|
17733
|
-
"symbols"
|
|
17767
|
+
"symbols",
|
|
17768
|
+
"knowledgeRecall"
|
|
17734
17769
|
],
|
|
17735
17770
|
critic_drift_verifier: [
|
|
17736
17771
|
"complexity_hotspots",
|
|
17737
17772
|
"detect_domains",
|
|
17738
17773
|
"imports",
|
|
17739
17774
|
"retrieve_summary",
|
|
17740
|
-
"symbols"
|
|
17775
|
+
"symbols",
|
|
17776
|
+
"knowledgeRecall"
|
|
17741
17777
|
],
|
|
17742
17778
|
docs: [
|
|
17743
17779
|
"detect_domains",
|
|
@@ -17747,9 +17783,15 @@ var AGENT_TOOL_MAP = {
|
|
|
17747
17783
|
"retrieve_summary",
|
|
17748
17784
|
"schema_drift",
|
|
17749
17785
|
"symbols",
|
|
17750
|
-
"todo_extract"
|
|
17786
|
+
"todo_extract",
|
|
17787
|
+
"knowledgeRecall"
|
|
17751
17788
|
],
|
|
17752
|
-
designer: [
|
|
17789
|
+
designer: [
|
|
17790
|
+
"extract_code_blocks",
|
|
17791
|
+
"retrieve_summary",
|
|
17792
|
+
"symbols",
|
|
17793
|
+
"knowledgeRecall"
|
|
17794
|
+
]
|
|
17753
17795
|
};
|
|
17754
17796
|
for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
17755
17797
|
const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
|
|
@@ -18138,8 +18180,8 @@ var PlanCursorConfigSchema = exports_external.object({
|
|
|
18138
18180
|
});
|
|
18139
18181
|
var CheckpointConfigSchema = exports_external.object({
|
|
18140
18182
|
enabled: exports_external.boolean().default(true),
|
|
18141
|
-
auto_checkpoint_threshold: exports_external.number().min(1).max(20).default(3)
|
|
18142
|
-
});
|
|
18183
|
+
auto_checkpoint_threshold: exports_external.number().int().min(1).max(20).default(3)
|
|
18184
|
+
}).strict();
|
|
18143
18185
|
var AutomationModeSchema = exports_external.enum(["manual", "hybrid", "auto"]);
|
|
18144
18186
|
var AutomationCapabilitiesSchema = exports_external.object({
|
|
18145
18187
|
plan_sync: exports_external.boolean().default(true),
|
|
@@ -18259,7 +18301,8 @@ var PluginConfigSchema = exports_external.object({
|
|
|
18259
18301
|
block_on_threshold: exports_external.boolean().default(false).describe("If true, block phase completion when threshold exceeded. Default: advisory only.")
|
|
18260
18302
|
}).optional(),
|
|
18261
18303
|
incremental_verify: IncrementalVerifyConfigSchema.optional(),
|
|
18262
|
-
compaction_service: CompactionConfigSchema.optional()
|
|
18304
|
+
compaction_service: CompactionConfigSchema.optional(),
|
|
18305
|
+
turbo_mode: exports_external.boolean().default(false).optional()
|
|
18263
18306
|
});
|
|
18264
18307
|
|
|
18265
18308
|
// src/config/loader.ts
|
|
@@ -18362,6 +18405,15 @@ function loadPluginConfig(directory) {
|
|
|
18362
18405
|
}
|
|
18363
18406
|
return result.data;
|
|
18364
18407
|
}
|
|
18408
|
+
function loadPluginConfigWithMeta(directory) {
|
|
18409
|
+
const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
18410
|
+
const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
|
|
18411
|
+
const userResult = loadRawConfigFromPath(userConfigPath);
|
|
18412
|
+
const projectResult = loadRawConfigFromPath(projectConfigPath);
|
|
18413
|
+
const loadedFromFile = userResult.fileExisted || projectResult.fileExisted;
|
|
18414
|
+
const config2 = loadPluginConfig(directory);
|
|
18415
|
+
return { config: config2, loadedFromFile };
|
|
18416
|
+
}
|
|
18365
18417
|
|
|
18366
18418
|
// src/commands/archive.ts
|
|
18367
18419
|
init_manager();
|
|
@@ -18784,6 +18836,9 @@ async function handleBenchmarkCommand(directory, args) {
|
|
|
18784
18836
|
`);
|
|
18785
18837
|
}
|
|
18786
18838
|
|
|
18839
|
+
// src/commands/checkpoint.ts
|
|
18840
|
+
init_zod();
|
|
18841
|
+
|
|
18787
18842
|
// src/tools/checkpoint.ts
|
|
18788
18843
|
import { spawnSync } from "child_process";
|
|
18789
18844
|
import * as fs3 from "fs";
|
|
@@ -31109,6 +31164,10 @@ function tool(input) {
|
|
|
31109
31164
|
return input;
|
|
31110
31165
|
}
|
|
31111
31166
|
tool.schema = exports_external2;
|
|
31167
|
+
|
|
31168
|
+
// src/config/index.ts
|
|
31169
|
+
init_evidence_schema();
|
|
31170
|
+
init_plan_schema();
|
|
31112
31171
|
// src/tools/create-tool.ts
|
|
31113
31172
|
function createSwarmTool(opts) {
|
|
31114
31173
|
return tool({
|
|
@@ -31233,6 +31292,11 @@ function isGitRepo() {
|
|
|
31233
31292
|
}
|
|
31234
31293
|
function handleSave(label, directory) {
|
|
31235
31294
|
try {
|
|
31295
|
+
let maxCheckpoints = 20;
|
|
31296
|
+
try {
|
|
31297
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
31298
|
+
maxCheckpoints = config3.checkpoint?.auto_checkpoint_threshold ?? maxCheckpoints;
|
|
31299
|
+
} catch {}
|
|
31236
31300
|
const log2 = readCheckpointLog(directory);
|
|
31237
31301
|
const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
|
|
31238
31302
|
if (existingCheckpoint) {
|
|
@@ -31352,7 +31416,7 @@ var checkpoint = createSwarmTool({
|
|
|
31352
31416
|
let label;
|
|
31353
31417
|
try {
|
|
31354
31418
|
action = String(args.action);
|
|
31355
|
-
label = args.label !== undefined ? String(args.label) : undefined;
|
|
31419
|
+
label = args.label !== undefined && args.label !== null ? String(args.label) : undefined;
|
|
31356
31420
|
} catch {
|
|
31357
31421
|
return JSON.stringify({
|
|
31358
31422
|
action: "unknown",
|
|
@@ -31405,6 +31469,22 @@ var checkpoint = createSwarmTool({
|
|
|
31405
31469
|
});
|
|
31406
31470
|
|
|
31407
31471
|
// src/commands/checkpoint.ts
|
|
31472
|
+
var CheckpointResultSchema = exports_external.object({
|
|
31473
|
+
action: exports_external.string().optional(),
|
|
31474
|
+
success: exports_external.boolean(),
|
|
31475
|
+
error: exports_external.string().optional(),
|
|
31476
|
+
checkpoints: exports_external.array(exports_external.unknown()).optional()
|
|
31477
|
+
}).passthrough();
|
|
31478
|
+
function safeParseResult(result) {
|
|
31479
|
+
const parsed = CheckpointResultSchema.safeParse(JSON.parse(result));
|
|
31480
|
+
if (!parsed.success) {
|
|
31481
|
+
return {
|
|
31482
|
+
success: false,
|
|
31483
|
+
error: `Invalid response: ${parsed.error.message}`
|
|
31484
|
+
};
|
|
31485
|
+
}
|
|
31486
|
+
return parsed.data;
|
|
31487
|
+
}
|
|
31408
31488
|
async function handleCheckpointCommand(directory, args) {
|
|
31409
31489
|
const subcommand = args[0] || "list";
|
|
31410
31490
|
const label = args[1];
|
|
@@ -31427,7 +31507,7 @@ async function handleSave2(directory, label) {
|
|
|
31427
31507
|
const result = await checkpoint.execute({ action: "save", label }, {
|
|
31428
31508
|
directory
|
|
31429
31509
|
});
|
|
31430
|
-
const parsed =
|
|
31510
|
+
const parsed = safeParseResult(result);
|
|
31431
31511
|
if (parsed.success) {
|
|
31432
31512
|
return `\u2713 Checkpoint saved: "${label}"`;
|
|
31433
31513
|
} else {
|
|
@@ -31446,7 +31526,7 @@ async function handleRestore2(directory, label) {
|
|
|
31446
31526
|
const result = await checkpoint.execute({ action: "restore", label }, {
|
|
31447
31527
|
directory
|
|
31448
31528
|
});
|
|
31449
|
-
const parsed =
|
|
31529
|
+
const parsed = safeParseResult(result);
|
|
31450
31530
|
if (parsed.success) {
|
|
31451
31531
|
return `\u2713 Restored to checkpoint: "${label}"`;
|
|
31452
31532
|
} else {
|
|
@@ -31465,7 +31545,7 @@ async function handleDelete2(directory, label) {
|
|
|
31465
31545
|
const result = await checkpoint.execute({ action: "delete", label }, {
|
|
31466
31546
|
directory
|
|
31467
31547
|
});
|
|
31468
|
-
const parsed =
|
|
31548
|
+
const parsed = safeParseResult(result);
|
|
31469
31549
|
if (parsed.success) {
|
|
31470
31550
|
return `\u2713 Checkpoint deleted: "${label}"`;
|
|
31471
31551
|
} else {
|
|
@@ -31481,7 +31561,7 @@ async function handleList2(directory) {
|
|
|
31481
31561
|
const result = await checkpoint.execute({ action: "list" }, {
|
|
31482
31562
|
directory
|
|
31483
31563
|
});
|
|
31484
|
-
const parsed =
|
|
31564
|
+
const parsed = safeParseResult(result);
|
|
31485
31565
|
if (!parsed.success) {
|
|
31486
31566
|
return `Error: ${parsed.error || "Failed to list checkpoints"}`;
|
|
31487
31567
|
}
|
|
@@ -34179,7 +34259,8 @@ async function extractFromLegacy(directory) {
|
|
|
34179
34259
|
mappedStatus = "complete";
|
|
34180
34260
|
else if (status === "IN PROGRESS")
|
|
34181
34261
|
mappedStatus = "in_progress";
|
|
34182
|
-
const headerLineIndex =
|
|
34262
|
+
const headerLineIndex = planContent.substring(0, match.index).split(`
|
|
34263
|
+
`).length - 1;
|
|
34183
34264
|
let completed = 0;
|
|
34184
34265
|
let total = 0;
|
|
34185
34266
|
if (headerLineIndex !== -1) {
|
|
@@ -34574,9 +34655,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
34574
34655
|
return {
|
|
34575
34656
|
hasPlan: true,
|
|
34576
34657
|
fullMarkdown,
|
|
34577
|
-
requestedPhase:
|
|
34658
|
+
requestedPhase: null,
|
|
34578
34659
|
phaseMarkdown: null,
|
|
34579
|
-
errorMessage:
|
|
34660
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
34580
34661
|
isLegacy: false
|
|
34581
34662
|
};
|
|
34582
34663
|
}
|
|
@@ -34627,9 +34708,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
34627
34708
|
return {
|
|
34628
34709
|
hasPlan: true,
|
|
34629
34710
|
fullMarkdown: planContent,
|
|
34630
|
-
requestedPhase:
|
|
34711
|
+
requestedPhase: null,
|
|
34631
34712
|
phaseMarkdown: null,
|
|
34632
|
-
errorMessage:
|
|
34713
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
34633
34714
|
isLegacy: true
|
|
34634
34715
|
};
|
|
34635
34716
|
}
|
|
@@ -35568,6 +35649,59 @@ LANGUAGE_REGISTRY.register({
|
|
|
35568
35649
|
]
|
|
35569
35650
|
}
|
|
35570
35651
|
});
|
|
35652
|
+
LANGUAGE_REGISTRY.register({
|
|
35653
|
+
id: "php",
|
|
35654
|
+
displayName: "PHP",
|
|
35655
|
+
tier: 3,
|
|
35656
|
+
extensions: [".php", ".phtml"],
|
|
35657
|
+
treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
|
|
35658
|
+
build: {
|
|
35659
|
+
detectFiles: ["composer.json"],
|
|
35660
|
+
commands: []
|
|
35661
|
+
},
|
|
35662
|
+
test: {
|
|
35663
|
+
detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
|
|
35664
|
+
frameworks: [
|
|
35665
|
+
{
|
|
35666
|
+
name: "PHPUnit",
|
|
35667
|
+
detect: "phpunit.xml",
|
|
35668
|
+
cmd: "vendor/bin/phpunit",
|
|
35669
|
+
priority: 1
|
|
35670
|
+
}
|
|
35671
|
+
]
|
|
35672
|
+
},
|
|
35673
|
+
lint: {
|
|
35674
|
+
detectFiles: [".php-cs-fixer.php", "phpcs.xml"],
|
|
35675
|
+
linters: [
|
|
35676
|
+
{
|
|
35677
|
+
name: "PHP-CS-Fixer",
|
|
35678
|
+
detect: ".php-cs-fixer.php",
|
|
35679
|
+
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
35680
|
+
priority: 1
|
|
35681
|
+
}
|
|
35682
|
+
]
|
|
35683
|
+
},
|
|
35684
|
+
audit: {
|
|
35685
|
+
detectFiles: ["composer.lock"],
|
|
35686
|
+
command: "composer audit --format=json",
|
|
35687
|
+
outputFormat: "json"
|
|
35688
|
+
},
|
|
35689
|
+
sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
|
|
35690
|
+
prompts: {
|
|
35691
|
+
coderConstraints: [
|
|
35692
|
+
"Follow PSR-12 coding standards",
|
|
35693
|
+
"Use strict types declaration: declare(strict_types=1)",
|
|
35694
|
+
"Prefer type hints and return type declarations on all functions",
|
|
35695
|
+
"Use dependency injection over static methods and singletons"
|
|
35696
|
+
],
|
|
35697
|
+
reviewerChecklist: [
|
|
35698
|
+
"Verify no user input reaches SQL queries without parameterised binding",
|
|
35699
|
+
"Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
|
|
35700
|
+
"Confirm no eval(), exec(), or shell_exec() with user-controlled input",
|
|
35701
|
+
"Validate proper error handling \u2014 no bare catch blocks that swallow errors"
|
|
35702
|
+
]
|
|
35703
|
+
}
|
|
35704
|
+
});
|
|
35571
35705
|
|
|
35572
35706
|
// src/lang/detector.ts
|
|
35573
35707
|
async function detectProjectLanguages(projectDir) {
|
|
@@ -35947,6 +36081,38 @@ var build_discovery = tool({
|
|
|
35947
36081
|
|
|
35948
36082
|
// src/tools/lint.ts
|
|
35949
36083
|
init_utils();
|
|
36084
|
+
|
|
36085
|
+
// src/utils/path-security.ts
|
|
36086
|
+
function containsPathTraversal(str) {
|
|
36087
|
+
if (/\.\.[/\\]/.test(str))
|
|
36088
|
+
return true;
|
|
36089
|
+
if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
|
|
36090
|
+
return true;
|
|
36091
|
+
if (/%2e%2e/i.test(str))
|
|
36092
|
+
return true;
|
|
36093
|
+
if (/%2e\./i.test(str))
|
|
36094
|
+
return true;
|
|
36095
|
+
if (/%2e/i.test(str) && /\.\./.test(str))
|
|
36096
|
+
return true;
|
|
36097
|
+
if (/%252e%252e/i.test(str))
|
|
36098
|
+
return true;
|
|
36099
|
+
if (/\uff0e/.test(str))
|
|
36100
|
+
return true;
|
|
36101
|
+
if (/\u3002/.test(str))
|
|
36102
|
+
return true;
|
|
36103
|
+
if (/\uff65/.test(str))
|
|
36104
|
+
return true;
|
|
36105
|
+
if (/%2f/i.test(str))
|
|
36106
|
+
return true;
|
|
36107
|
+
if (/%5c/i.test(str))
|
|
36108
|
+
return true;
|
|
36109
|
+
return false;
|
|
36110
|
+
}
|
|
36111
|
+
function containsControlChars(str) {
|
|
36112
|
+
return /[\0\t\r\n]/.test(str);
|
|
36113
|
+
}
|
|
36114
|
+
|
|
36115
|
+
// src/tools/lint.ts
|
|
35950
36116
|
var MAX_OUTPUT_BYTES = 512000;
|
|
35951
36117
|
var MAX_COMMAND_LENGTH = 500;
|
|
35952
36118
|
function validateArgs(args) {
|
|
@@ -36119,7 +36285,7 @@ async function detectAvailableLinter(directory) {
|
|
|
36119
36285
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
36120
36286
|
const DETECT_TIMEOUT = 2000;
|
|
36121
36287
|
try {
|
|
36122
|
-
const biomeProc = Bun.spawn([
|
|
36288
|
+
const biomeProc = Bun.spawn([biomeBin, "--version"], {
|
|
36123
36289
|
stdout: "pipe",
|
|
36124
36290
|
stderr: "pipe"
|
|
36125
36291
|
});
|
|
@@ -36133,7 +36299,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
36133
36299
|
}
|
|
36134
36300
|
} catch {}
|
|
36135
36301
|
try {
|
|
36136
|
-
const eslintProc = Bun.spawn([
|
|
36302
|
+
const eslintProc = Bun.spawn([eslintBin, "--version"], {
|
|
36137
36303
|
stdout: "pipe",
|
|
36138
36304
|
stderr: "pipe"
|
|
36139
36305
|
});
|
|
@@ -36516,19 +36682,6 @@ function isHighEntropyString(str) {
|
|
|
36516
36682
|
const entropy = calculateShannonEntropy(str);
|
|
36517
36683
|
return entropy > 4;
|
|
36518
36684
|
}
|
|
36519
|
-
function containsPathTraversal(str) {
|
|
36520
|
-
if (/\.\.[/\\]/.test(str))
|
|
36521
|
-
return true;
|
|
36522
|
-
if (/[/\\]\.\.$/.test(str) || str === "..")
|
|
36523
|
-
return true;
|
|
36524
|
-
if (/\.\.[/\\]/.test(path18.normalize(str.replace(/\*/g, "x"))))
|
|
36525
|
-
return true;
|
|
36526
|
-
if (str.includes("%2e%2e") || str.includes("%2E%2E"))
|
|
36527
|
-
return true;
|
|
36528
|
-
if (str.includes("..") && /%2e/i.test(str))
|
|
36529
|
-
return true;
|
|
36530
|
-
return false;
|
|
36531
|
-
}
|
|
36532
36685
|
function validateExcludePattern(exc) {
|
|
36533
36686
|
if (exc.length === 0)
|
|
36534
36687
|
return null;
|
|
@@ -36581,9 +36734,6 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
36581
36734
|
}
|
|
36582
36735
|
return false;
|
|
36583
36736
|
}
|
|
36584
|
-
function containsControlChars(str) {
|
|
36585
|
-
return /[\0\t\r\n]/.test(str);
|
|
36586
|
-
}
|
|
36587
36737
|
function validateDirectoryInput(dir) {
|
|
36588
36738
|
if (!dir || dir.length === 0) {
|
|
36589
36739
|
return "directory is required";
|
|
@@ -37024,31 +37174,6 @@ var MAX_COMMAND_LENGTH2 = 500;
|
|
|
37024
37174
|
var DEFAULT_TIMEOUT_MS = 60000;
|
|
37025
37175
|
var MAX_TIMEOUT_MS = 300000;
|
|
37026
37176
|
var MAX_SAFE_TEST_FILES = 50;
|
|
37027
|
-
function containsPathTraversal2(str) {
|
|
37028
|
-
if (/\.\.[/\\]/.test(str))
|
|
37029
|
-
return true;
|
|
37030
|
-
if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(str))
|
|
37031
|
-
return true;
|
|
37032
|
-
if (/%2e%2e/i.test(str))
|
|
37033
|
-
return true;
|
|
37034
|
-
if (/%2e\./i.test(str))
|
|
37035
|
-
return true;
|
|
37036
|
-
if (/%2e/i.test(str) && /\.\./.test(str))
|
|
37037
|
-
return true;
|
|
37038
|
-
if (/%252e%252e/i.test(str))
|
|
37039
|
-
return true;
|
|
37040
|
-
if (/\uff0e/.test(str))
|
|
37041
|
-
return true;
|
|
37042
|
-
if (/\u3002/.test(str))
|
|
37043
|
-
return true;
|
|
37044
|
-
if (/\uff65/.test(str))
|
|
37045
|
-
return true;
|
|
37046
|
-
if (/%2f/i.test(str))
|
|
37047
|
-
return true;
|
|
37048
|
-
if (/%5c/i.test(str))
|
|
37049
|
-
return true;
|
|
37050
|
-
return false;
|
|
37051
|
-
}
|
|
37052
37177
|
function isAbsolutePath(str) {
|
|
37053
37178
|
if (str.startsWith("/"))
|
|
37054
37179
|
return true;
|
|
@@ -37060,9 +37185,6 @@ function isAbsolutePath(str) {
|
|
|
37060
37185
|
return true;
|
|
37061
37186
|
return false;
|
|
37062
37187
|
}
|
|
37063
|
-
function containsControlChars2(str) {
|
|
37064
|
-
return /[\x00-\x08\x0a\x0b\x0c\x0d\x0e-\x1f\x7f\x80-\x9f]/.test(str);
|
|
37065
|
-
}
|
|
37066
37188
|
var POWERSHELL_METACHARACTERS = /[|;&`$(){}[\]<>"'#*?\x00-\x1f]/;
|
|
37067
37189
|
function containsPowerShellMetacharacters(str) {
|
|
37068
37190
|
return POWERSHELL_METACHARACTERS.test(str);
|
|
@@ -37084,9 +37206,9 @@ function validateArgs2(args) {
|
|
|
37084
37206
|
return false;
|
|
37085
37207
|
if (isAbsolutePath(f))
|
|
37086
37208
|
return false;
|
|
37087
|
-
if (
|
|
37209
|
+
if (containsPathTraversal(f))
|
|
37088
37210
|
return false;
|
|
37089
|
-
if (
|
|
37211
|
+
if (containsControlChars(f))
|
|
37090
37212
|
return false;
|
|
37091
37213
|
if (containsPowerShellMetacharacters(f))
|
|
37092
37214
|
return false;
|
|
@@ -37909,7 +38031,7 @@ var test_runner = createSwarmTool({
|
|
|
37909
38031
|
};
|
|
37910
38032
|
return JSON.stringify(errorResult, null, 2);
|
|
37911
38033
|
}
|
|
37912
|
-
if (
|
|
38034
|
+
if (containsControlChars(workingDir)) {
|
|
37913
38035
|
const errorResult = {
|
|
37914
38036
|
success: false,
|
|
37915
38037
|
framework: "none",
|
|
@@ -37918,7 +38040,7 @@ var test_runner = createSwarmTool({
|
|
|
37918
38040
|
};
|
|
37919
38041
|
return JSON.stringify(errorResult, null, 2);
|
|
37920
38042
|
}
|
|
37921
|
-
if (
|
|
38043
|
+
if (containsPathTraversal(workingDir)) {
|
|
37922
38044
|
const errorResult = {
|
|
37923
38045
|
success: false,
|
|
37924
38046
|
framework: "none",
|
|
@@ -38194,12 +38316,13 @@ async function runLintCheck(dir, linter, timeoutMs) {
|
|
|
38194
38316
|
const startTime = Date.now();
|
|
38195
38317
|
try {
|
|
38196
38318
|
const lintPromise = runLint(linter, "check", dir);
|
|
38319
|
+
let timeoutId;
|
|
38197
38320
|
const timeoutPromise = new Promise((_, reject) => {
|
|
38198
|
-
setTimeout(() => {
|
|
38321
|
+
timeoutId = setTimeout(() => {
|
|
38199
38322
|
reject(new Error(`Lint check timed out after ${timeoutMs}ms`));
|
|
38200
38323
|
}, timeoutMs);
|
|
38201
38324
|
});
|
|
38202
|
-
const result = await Promise.race([lintPromise, timeoutPromise]);
|
|
38325
|
+
const result = await Promise.race([lintPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
38203
38326
|
if (!result.success) {
|
|
38204
38327
|
return {
|
|
38205
38328
|
type: "lint",
|
|
@@ -39709,12 +39832,32 @@ function makeInitialState() {
|
|
|
39709
39832
|
lastSnapshotAt: null
|
|
39710
39833
|
};
|
|
39711
39834
|
}
|
|
39712
|
-
var
|
|
39713
|
-
function
|
|
39714
|
-
|
|
39715
|
-
|
|
39716
|
-
|
|
39717
|
-
|
|
39835
|
+
var sessionStates = new Map;
|
|
39836
|
+
function getSessionState(sessionId) {
|
|
39837
|
+
let state = sessionStates.get(sessionId);
|
|
39838
|
+
if (!state) {
|
|
39839
|
+
state = makeInitialState();
|
|
39840
|
+
sessionStates.set(sessionId, state);
|
|
39841
|
+
}
|
|
39842
|
+
return state;
|
|
39843
|
+
}
|
|
39844
|
+
function getCompactionMetrics(sessionId) {
|
|
39845
|
+
if (sessionId) {
|
|
39846
|
+
const state = getSessionState(sessionId);
|
|
39847
|
+
return {
|
|
39848
|
+
compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
|
|
39849
|
+
lastSnapshotAt: state.lastSnapshotAt
|
|
39850
|
+
};
|
|
39851
|
+
}
|
|
39852
|
+
let total = 0;
|
|
39853
|
+
let lastSnapshot = null;
|
|
39854
|
+
for (const state of sessionStates.values()) {
|
|
39855
|
+
total += state.observationCount + state.reflectionCount + state.emergencyCount;
|
|
39856
|
+
if (state.lastSnapshotAt && (!lastSnapshot || state.lastSnapshotAt > lastSnapshot)) {
|
|
39857
|
+
lastSnapshot = state.lastSnapshotAt;
|
|
39858
|
+
}
|
|
39859
|
+
}
|
|
39860
|
+
return { compactionCount: total, lastSnapshotAt: lastSnapshot };
|
|
39718
39861
|
}
|
|
39719
39862
|
|
|
39720
39863
|
// src/services/context-budget-service.ts
|
|
@@ -39877,6 +40020,7 @@ async function handleTurboCommand(_directory, args, sessionID) {
|
|
|
39877
40020
|
}
|
|
39878
40021
|
|
|
39879
40022
|
// src/tools/write-retro.ts
|
|
40023
|
+
init_evidence_schema();
|
|
39880
40024
|
init_manager();
|
|
39881
40025
|
async function executeWriteRetro(args, directory) {
|
|
39882
40026
|
const phase = args.phase;
|
|
@@ -40110,8 +40254,9 @@ async function executeWriteRetro(args, directory) {
|
|
|
40110
40254
|
};
|
|
40111
40255
|
const taxonomy = [];
|
|
40112
40256
|
try {
|
|
40113
|
-
|
|
40114
|
-
|
|
40257
|
+
const allTaskIds = await listEvidenceTaskIds(directory);
|
|
40258
|
+
const phaseTaskIds = allTaskIds.filter((id) => id.startsWith(`${phase}.`));
|
|
40259
|
+
for (const phaseTaskId of phaseTaskIds) {
|
|
40115
40260
|
const result = await loadEvidence(directory, phaseTaskId);
|
|
40116
40261
|
if (result.status !== "found")
|
|
40117
40262
|
continue;
|
|
@@ -40145,6 +40290,13 @@ async function executeWriteRetro(args, directory) {
|
|
|
40145
40290
|
}
|
|
40146
40291
|
} catch {}
|
|
40147
40292
|
retroEntry.error_taxonomy = [...new Set(taxonomy)];
|
|
40293
|
+
const validationResult = RetrospectiveEvidenceSchema.safeParse(retroEntry);
|
|
40294
|
+
if (!validationResult.success) {
|
|
40295
|
+
return JSON.stringify({
|
|
40296
|
+
success: false,
|
|
40297
|
+
error: `Retrospective entry failed validation: ${validationResult.error.message}`
|
|
40298
|
+
}, null, 2);
|
|
40299
|
+
}
|
|
40148
40300
|
try {
|
|
40149
40301
|
await saveEvidence(directory, taskId, retroEntry);
|
|
40150
40302
|
return JSON.stringify({
|
|
@@ -40209,7 +40361,7 @@ var write_retro = createSwarmTool({
|
|
|
40209
40361
|
}
|
|
40210
40362
|
});
|
|
40211
40363
|
|
|
40212
|
-
// src/commands/
|
|
40364
|
+
// src/commands/write-retro.ts
|
|
40213
40365
|
async function handleWriteRetroCommand(directory, args) {
|
|
40214
40366
|
if (args.length === 0 || !args[0] || args[0].trim() === "") {
|
|
40215
40367
|
return `## Usage: /swarm write-retro <json>
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export { handleSpecifyCommand } from './specify';
|
|
|
29
29
|
export { handleStatusCommand } from './status';
|
|
30
30
|
export { handleSyncPlanCommand } from './sync-plan';
|
|
31
31
|
export { handleTurboCommand } from './turbo';
|
|
32
|
-
export { handleWriteRetroCommand } from './
|
|
32
|
+
export { handleWriteRetroCommand } from './write-retro';
|
|
33
33
|
/**
|
|
34
34
|
* Creates a command.execute.before handler for /swarm commands.
|
|
35
35
|
* Uses factory pattern to close over directory and agents.
|