opencode-swarm 6.60.0 → 6.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/agents/architect.commands-list.adversarial.test.d.ts +1 -0
- package/dist/agents/architect.commands-list.test.d.ts +1 -0
- package/dist/cli/index.js +85 -32
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.help-text.test.d.ts +1 -0
- package/dist/commands/registry-documentation.test.d.ts +1 -0
- package/dist/commands/registry-type.test.d.ts +1 -0
- package/dist/commands/registry.d.ts +59 -1
- package/dist/config/schema.d.ts +2 -0
- package/dist/hooks/guardrails.d.ts +5 -0
- package/dist/index.js +543 -184
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -297,7 +297,8 @@ var init_constants = __esm(() => {
|
|
|
297
297
|
"imports",
|
|
298
298
|
"retrieve_summary",
|
|
299
299
|
"symbols",
|
|
300
|
-
"knowledge_recall"
|
|
300
|
+
"knowledge_recall",
|
|
301
|
+
"req_coverage"
|
|
301
302
|
],
|
|
302
303
|
critic_drift_verifier: [
|
|
303
304
|
"complexity_hotspots",
|
|
@@ -14990,7 +14991,8 @@ var init_schema = __esm(() => {
|
|
|
14990
14991
|
]),
|
|
14991
14992
|
require_reviewer_test_engineer: exports_external.boolean().default(true)
|
|
14992
14993
|
}).optional(),
|
|
14993
|
-
profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
|
|
14994
|
+
profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional(),
|
|
14995
|
+
block_destructive_commands: exports_external.boolean().default(true)
|
|
14994
14996
|
});
|
|
14995
14997
|
WatchdogConfigSchema = exports_external.object({
|
|
14996
14998
|
scope_guard: exports_external.boolean().default(true),
|
|
@@ -30654,7 +30656,7 @@ var init_dist = __esm(() => {
|
|
|
30654
30656
|
|
|
30655
30657
|
// src/tools/create-tool.ts
|
|
30656
30658
|
function classifyToolError(error93) {
|
|
30657
|
-
const msg = (error93 instanceof Error ? error93.message : String(error93)).toLowerCase();
|
|
30659
|
+
const msg = (error93 instanceof Error ? error93.message ?? "" : String(error93)).toLowerCase();
|
|
30658
30660
|
if (msg.includes("not registered") || msg.includes("unknown tool"))
|
|
30659
30661
|
return "not_registered";
|
|
30660
30662
|
if (msg.includes("not whitelisted") || msg.includes("not allowed"))
|
|
@@ -34920,6 +34922,10 @@ async function discoverBuildCommands(workingDir, options) {
|
|
|
34920
34922
|
const skipped = [...profileSkipped];
|
|
34921
34923
|
for (const ecosystem of ECOSYSTEMS) {
|
|
34922
34924
|
if (coveredEcosystems.has(ecosystem.ecosystem)) {
|
|
34925
|
+
skipped.push({
|
|
34926
|
+
ecosystem: ecosystem.ecosystem,
|
|
34927
|
+
reason: `Covered by profile detection`
|
|
34928
|
+
});
|
|
34923
34929
|
continue;
|
|
34924
34930
|
}
|
|
34925
34931
|
if (!checkToolchain(ecosystem.toolchainCommands)) {
|
|
@@ -41322,7 +41328,7 @@ async function scanDocIndex(directory) {
|
|
|
41322
41328
|
try {
|
|
41323
41329
|
const fullPath = path46.join(directory, file3.path);
|
|
41324
41330
|
const stat2 = fs34.statSync(fullPath);
|
|
41325
|
-
if (stat2.mtimeMs >
|
|
41331
|
+
if (stat2.mtimeMs > file3.mtime) {
|
|
41326
41332
|
cacheValid = false;
|
|
41327
41333
|
break;
|
|
41328
41334
|
}
|
|
@@ -41619,6 +41625,98 @@ var init_doc_scan = __esm(() => {
|
|
|
41619
41625
|
});
|
|
41620
41626
|
});
|
|
41621
41627
|
|
|
41628
|
+
// src/tools/knowledge-recall.ts
|
|
41629
|
+
var exports_knowledge_recall = {};
|
|
41630
|
+
__export(exports_knowledge_recall, {
|
|
41631
|
+
knowledge_recall: () => knowledge_recall
|
|
41632
|
+
});
|
|
41633
|
+
var knowledge_recall;
|
|
41634
|
+
var init_knowledge_recall = __esm(() => {
|
|
41635
|
+
init_dist();
|
|
41636
|
+
init_knowledge_store();
|
|
41637
|
+
init_create_tool();
|
|
41638
|
+
knowledge_recall = createSwarmTool({
|
|
41639
|
+
description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
|
|
41640
|
+
args: {
|
|
41641
|
+
query: tool.schema.string().min(3).describe("Natural language search query"),
|
|
41642
|
+
top_n: tool.schema.number().int().min(1).max(20).optional().describe("Maximum results to return (default: 5)"),
|
|
41643
|
+
tier: tool.schema.enum(["all", "swarm", "hive"]).optional().describe("Knowledge tier to search (default: 'all')")
|
|
41644
|
+
},
|
|
41645
|
+
execute: async (args2, directory) => {
|
|
41646
|
+
let queryInput;
|
|
41647
|
+
let topNInput;
|
|
41648
|
+
let tierInput;
|
|
41649
|
+
try {
|
|
41650
|
+
if (args2 && typeof args2 === "object") {
|
|
41651
|
+
const obj = args2;
|
|
41652
|
+
queryInput = obj.query;
|
|
41653
|
+
topNInput = obj.top_n;
|
|
41654
|
+
tierInput = obj.tier;
|
|
41655
|
+
}
|
|
41656
|
+
} catch {}
|
|
41657
|
+
if (typeof queryInput !== "string" || queryInput.length < 3) {
|
|
41658
|
+
return JSON.stringify({
|
|
41659
|
+
results: [],
|
|
41660
|
+
total: 0,
|
|
41661
|
+
error: "query must be a string with at least 3 characters"
|
|
41662
|
+
});
|
|
41663
|
+
}
|
|
41664
|
+
let topN = 5;
|
|
41665
|
+
if (topNInput !== undefined) {
|
|
41666
|
+
if (typeof topNInput === "number" && Number.isInteger(topNInput)) {
|
|
41667
|
+
topN = Math.max(1, Math.min(20, topNInput));
|
|
41668
|
+
}
|
|
41669
|
+
}
|
|
41670
|
+
let tier = "all";
|
|
41671
|
+
if (tierInput !== undefined && typeof tierInput === "string") {
|
|
41672
|
+
if (tierInput === "swarm" || tierInput === "hive") {
|
|
41673
|
+
tier = tierInput;
|
|
41674
|
+
}
|
|
41675
|
+
}
|
|
41676
|
+
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
41677
|
+
const hivePath = resolveHiveKnowledgePath();
|
|
41678
|
+
const [swarmEntries, hiveEntries] = await Promise.all([
|
|
41679
|
+
readKnowledge(swarmPath),
|
|
41680
|
+
readKnowledge(hivePath)
|
|
41681
|
+
]);
|
|
41682
|
+
let entries = [];
|
|
41683
|
+
if (tier === "all" || tier === "swarm") {
|
|
41684
|
+
entries = entries.concat(swarmEntries);
|
|
41685
|
+
}
|
|
41686
|
+
if (tier === "all" || tier === "hive") {
|
|
41687
|
+
entries = entries.concat(hiveEntries);
|
|
41688
|
+
}
|
|
41689
|
+
if (entries.length === 0) {
|
|
41690
|
+
const result2 = { results: [], total: 0 };
|
|
41691
|
+
return JSON.stringify(result2);
|
|
41692
|
+
}
|
|
41693
|
+
const normalizedQuery = normalize2(queryInput);
|
|
41694
|
+
const queryBigrams = wordBigrams(normalizedQuery);
|
|
41695
|
+
const scoredEntries = entries.map((entry) => {
|
|
41696
|
+
const entryText = `${entry.lesson} ${entry.tags.join(" ")} ${entry.category}`;
|
|
41697
|
+
const entryBigrams = wordBigrams(entryText);
|
|
41698
|
+
const textScore = jaccardBigram(queryBigrams, entryBigrams);
|
|
41699
|
+
const boost = entry.status === "established" ? 0.1 : entry.status === "promoted" ? 0.05 : 0;
|
|
41700
|
+
const finalScore = textScore + boost;
|
|
41701
|
+
return {
|
|
41702
|
+
id: entry.id,
|
|
41703
|
+
confidence: entry.confidence,
|
|
41704
|
+
category: entry.category,
|
|
41705
|
+
lesson: entry.lesson,
|
|
41706
|
+
score: finalScore
|
|
41707
|
+
};
|
|
41708
|
+
});
|
|
41709
|
+
scoredEntries.sort((a, b) => b.score - a.score);
|
|
41710
|
+
const topResults = scoredEntries.slice(0, topN);
|
|
41711
|
+
const result = {
|
|
41712
|
+
results: topResults,
|
|
41713
|
+
total: topResults.length
|
|
41714
|
+
};
|
|
41715
|
+
return JSON.stringify(result);
|
|
41716
|
+
}
|
|
41717
|
+
});
|
|
41718
|
+
});
|
|
41719
|
+
|
|
41622
41720
|
// src/environment/prompt-renderer.ts
|
|
41623
41721
|
var exports_prompt_renderer = {};
|
|
41624
41722
|
__export(exports_prompt_renderer, {
|
|
@@ -45263,7 +45361,7 @@ ${warnings.map((w) => ` - ${w}`).join(`
|
|
|
45263
45361
|
`)}` : "";
|
|
45264
45362
|
const cautionMessage = `
|
|
45265
45363
|
|
|
45266
|
-
\u26A0\uFE0F
|
|
45364
|
+
\u26A0\uFE0F Warning: Spec drift was acknowledged \u2014 verify that the implementation still matches the spec before proceeding.`;
|
|
45267
45365
|
return baseMessage + warningMessage + cautionMessage;
|
|
45268
45366
|
}
|
|
45269
45367
|
|
|
@@ -48051,42 +48149,73 @@ init_utils2();
|
|
|
48051
48149
|
var DEFAULT_CURATOR_LLM_TIMEOUT_MS = 300000;
|
|
48052
48150
|
function parseKnowledgeRecommendations(llmOutput) {
|
|
48053
48151
|
const recommendations = [];
|
|
48054
|
-
const
|
|
48055
|
-
|
|
48056
|
-
|
|
48057
|
-
|
|
48152
|
+
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
48153
|
+
const obsSection = llmOutput.match(/OBSERVATIONS:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
|
|
48154
|
+
if (obsSection) {
|
|
48155
|
+
const lines = obsSection[1].split(`
|
|
48058
48156
|
`);
|
|
48059
|
-
|
|
48060
|
-
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48069
|
-
|
|
48070
|
-
|
|
48071
|
-
|
|
48072
|
-
|
|
48073
|
-
|
|
48074
|
-
|
|
48075
|
-
|
|
48076
|
-
|
|
48077
|
-
|
|
48078
|
-
|
|
48079
|
-
|
|
48080
|
-
|
|
48081
|
-
|
|
48082
|
-
|
|
48083
|
-
|
|
48084
|
-
|
|
48085
|
-
|
|
48086
|
-
|
|
48087
|
-
|
|
48088
|
-
|
|
48089
|
-
}
|
|
48157
|
+
for (const line of lines) {
|
|
48158
|
+
const trimmed = line.trim();
|
|
48159
|
+
if (!trimmed.startsWith("-"))
|
|
48160
|
+
continue;
|
|
48161
|
+
const match = trimmed.match(/^-\s+entry\s+(\S+)\s+\(([^)]+)\):\s+(.+)$/i);
|
|
48162
|
+
if (!match)
|
|
48163
|
+
continue;
|
|
48164
|
+
const uuid8 = match[1];
|
|
48165
|
+
const parenthetical = match[2];
|
|
48166
|
+
const text = match[3].trim().replace(/\s+\([^)]+\)$/, "");
|
|
48167
|
+
const entryId = uuid8 === "new" || !UUID_V4.test(uuid8) ? undefined : uuid8;
|
|
48168
|
+
let action = "rewrite";
|
|
48169
|
+
const lowerParenthetical = parenthetical.toLowerCase();
|
|
48170
|
+
if (lowerParenthetical.includes("suggests boost confidence") || lowerParenthetical.includes("mark hive_eligible") || lowerParenthetical.includes("appears high-confidence")) {
|
|
48171
|
+
action = "promote";
|
|
48172
|
+
} else if (lowerParenthetical.includes("suggests archive") || lowerParenthetical.includes("appears stale")) {
|
|
48173
|
+
action = "archive";
|
|
48174
|
+
} else if (lowerParenthetical.includes("contradicts project state")) {
|
|
48175
|
+
action = "flag_contradiction";
|
|
48176
|
+
} else if (lowerParenthetical.includes("suggests rewrite") || lowerParenthetical.includes("could be tighter")) {
|
|
48177
|
+
action = "rewrite";
|
|
48178
|
+
} else if (lowerParenthetical.includes("new candidate")) {
|
|
48179
|
+
action = "promote";
|
|
48180
|
+
}
|
|
48181
|
+
recommendations.push({
|
|
48182
|
+
action,
|
|
48183
|
+
entry_id: entryId,
|
|
48184
|
+
lesson: text,
|
|
48185
|
+
reason: text
|
|
48186
|
+
});
|
|
48187
|
+
}
|
|
48188
|
+
}
|
|
48189
|
+
const updatesSection = llmOutput.match(/KNOWLEDGE_UPDATES:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
|
|
48190
|
+
if (updatesSection) {
|
|
48191
|
+
const validActions = new Set([
|
|
48192
|
+
"promote",
|
|
48193
|
+
"archive",
|
|
48194
|
+
"rewrite",
|
|
48195
|
+
"flag_contradiction"
|
|
48196
|
+
]);
|
|
48197
|
+
const lines = updatesSection[1].split(`
|
|
48198
|
+
`);
|
|
48199
|
+
for (const line of lines) {
|
|
48200
|
+
const trimmed = line.trim();
|
|
48201
|
+
if (!trimmed.startsWith("-"))
|
|
48202
|
+
continue;
|
|
48203
|
+
const match = trimmed.match(/^-\s+(\S+)\s+(\S+):\s+(.+)$/);
|
|
48204
|
+
if (!match)
|
|
48205
|
+
continue;
|
|
48206
|
+
const action = match[1].toLowerCase();
|
|
48207
|
+
if (!validActions.has(action))
|
|
48208
|
+
continue;
|
|
48209
|
+
const id = match[2];
|
|
48210
|
+
const text = match[3].trim();
|
|
48211
|
+
const entryId = UUID_V4.test(id) ? id : undefined;
|
|
48212
|
+
recommendations.push({
|
|
48213
|
+
action,
|
|
48214
|
+
entry_id: entryId,
|
|
48215
|
+
lesson: text,
|
|
48216
|
+
reason: text
|
|
48217
|
+
});
|
|
48218
|
+
}
|
|
48090
48219
|
}
|
|
48091
48220
|
return recommendations;
|
|
48092
48221
|
}
|
|
@@ -52920,7 +53049,8 @@ Run \`/swarm evidence ${result.task_id ?? "unknown"}\` to view it, or \`/swarm s
|
|
|
52920
53049
|
var COMMAND_REGISTRY = {
|
|
52921
53050
|
"acknowledge-spec-drift": {
|
|
52922
53051
|
handler: (ctx) => handleAcknowledgeSpecDriftCommand(ctx.directory, ctx.args),
|
|
52923
|
-
description: "Acknowledge that the spec has drifted from the plan and suppress further warnings"
|
|
53052
|
+
description: "Acknowledge that the spec has drifted from the plan and suppress further warnings",
|
|
53053
|
+
args: ""
|
|
52924
53054
|
},
|
|
52925
53055
|
status: {
|
|
52926
53056
|
handler: (ctx) => handleStatusCommand(ctx.directory, ctx.agents),
|
|
@@ -52966,112 +53096,156 @@ var COMMAND_REGISTRY = {
|
|
|
52966
53096
|
},
|
|
52967
53097
|
"sync-plan": {
|
|
52968
53098
|
handler: (ctx) => handleSyncPlanCommand(ctx.directory, ctx.args),
|
|
52969
|
-
description: "Ensure plan.json and plan.md are synced"
|
|
53099
|
+
description: "Ensure plan.json and plan.md are synced",
|
|
53100
|
+
args: ""
|
|
52970
53101
|
},
|
|
52971
53102
|
benchmark: {
|
|
52972
53103
|
handler: (ctx) => handleBenchmarkCommand(ctx.directory, ctx.args),
|
|
52973
|
-
description: "Show performance metrics [--cumulative] [--ci-gate]"
|
|
53104
|
+
description: "Show performance metrics [--cumulative] [--ci-gate]",
|
|
53105
|
+
args: "--cumulative, --ci-gate"
|
|
52974
53106
|
},
|
|
52975
53107
|
export: {
|
|
52976
53108
|
handler: (ctx) => handleExportCommand(ctx.directory, ctx.args),
|
|
52977
|
-
description: "Export plan and context as JSON"
|
|
53109
|
+
description: "Export plan and context as JSON",
|
|
53110
|
+
args: "",
|
|
53111
|
+
details: "Exports the current plan and context as JSON to stdout. Useful for piping to external tools or debugging swarm state."
|
|
52978
53112
|
},
|
|
52979
53113
|
evidence: {
|
|
52980
53114
|
handler: (ctx) => handleEvidenceCommand(ctx.directory, ctx.args),
|
|
52981
|
-
description: "Show evidence bundles [taskId]"
|
|
53115
|
+
description: "Show evidence bundles [taskId]",
|
|
53116
|
+
args: "<taskId>",
|
|
53117
|
+
details: 'Displays review results, test verdicts, and other evidence bundles for the given task ID (e.g., "2.1").'
|
|
52982
53118
|
},
|
|
52983
53119
|
"evidence summary": {
|
|
52984
53120
|
handler: (ctx) => handleEvidenceSummaryCommand(ctx.directory),
|
|
52985
53121
|
description: "Generate evidence summary with completion ratio and blockers",
|
|
52986
|
-
subcommandOf: "evidence"
|
|
53122
|
+
subcommandOf: "evidence",
|
|
53123
|
+
args: "",
|
|
53124
|
+
details: "Generates a summary showing completion ratio across all tasks, lists blockers, and identifies missing evidence."
|
|
52987
53125
|
},
|
|
52988
53126
|
"evidence-summary": {
|
|
52989
53127
|
handler: (ctx) => handleEvidenceSummaryCommand(ctx.directory),
|
|
52990
53128
|
description: "Generate evidence summary with completion ratio and blockers",
|
|
52991
|
-
subcommandOf: "evidence"
|
|
53129
|
+
subcommandOf: "evidence",
|
|
53130
|
+
args: "",
|
|
53131
|
+
details: "Generates a summary showing completion ratio across all tasks, lists blockers, and identifies missing evidence."
|
|
52992
53132
|
},
|
|
52993
53133
|
archive: {
|
|
52994
53134
|
handler: (ctx) => handleArchiveCommand(ctx.directory, ctx.args),
|
|
52995
|
-
description: "Archive old evidence bundles [--dry-run]"
|
|
53135
|
+
description: "Archive old evidence bundles [--dry-run]",
|
|
53136
|
+
details: "Archives evidence bundles older than max_age_days (config, default 90) or beyond max_bundles cap (config, default 1000). --dry-run previews which bundles would be archived without deleting them. Applies two-tier retention: age-based first, then count-based on oldest remaining.",
|
|
53137
|
+
args: "--dry-run"
|
|
52996
53138
|
},
|
|
52997
53139
|
curate: {
|
|
52998
53140
|
handler: (ctx) => handleCurateCommand(ctx.directory, ctx.args),
|
|
52999
|
-
description: "Run knowledge curation and hive promotion review"
|
|
53141
|
+
description: "Run knowledge curation and hive promotion review",
|
|
53142
|
+
args: ""
|
|
53000
53143
|
},
|
|
53001
53144
|
"dark-matter": {
|
|
53002
53145
|
handler: (ctx) => handleDarkMatterCommand(ctx.directory, ctx.args),
|
|
53003
|
-
description: "Detect hidden file couplings via co-change NPMI analysis"
|
|
53146
|
+
description: "Detect hidden file couplings via co-change NPMI analysis",
|
|
53147
|
+
args: "--threshold <number>, --min-commits <number>"
|
|
53004
53148
|
},
|
|
53005
53149
|
close: {
|
|
53006
53150
|
handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args),
|
|
53007
|
-
description: "Use /swarm close to close the swarm project and archive evidence"
|
|
53151
|
+
description: "Use /swarm close to close the swarm project and archive evidence",
|
|
53152
|
+
details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs safe git ff-only to main. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation.",
|
|
53153
|
+
args: "--prune-branches"
|
|
53008
53154
|
},
|
|
53009
53155
|
simulate: {
|
|
53010
53156
|
handler: (ctx) => handleSimulateCommand(ctx.directory, ctx.args),
|
|
53011
|
-
description: "Dry-run
|
|
53157
|
+
description: "Dry-run hidden coupling analysis with configurable thresholds",
|
|
53158
|
+
args: "--threshold <number>, --min-commits <number>"
|
|
53012
53159
|
},
|
|
53013
53160
|
analyze: {
|
|
53014
53161
|
handler: (ctx) => handleAnalyzeCommand(ctx.directory, ctx.args),
|
|
53015
|
-
description: "Analyze spec.md vs plan.md for requirement coverage gaps"
|
|
53162
|
+
description: "Analyze spec.md vs plan.md for requirement coverage gaps",
|
|
53163
|
+
args: ""
|
|
53016
53164
|
},
|
|
53017
53165
|
clarify: {
|
|
53018
53166
|
handler: (ctx) => handleClarifyCommand(ctx.directory, ctx.args),
|
|
53019
|
-
description: "Clarify and refine an existing feature specification"
|
|
53167
|
+
description: "Clarify and refine an existing feature specification",
|
|
53168
|
+
args: "[description-text]"
|
|
53020
53169
|
},
|
|
53021
53170
|
specify: {
|
|
53022
53171
|
handler: (ctx) => handleSpecifyCommand(ctx.directory, ctx.args),
|
|
53023
|
-
description: "Generate or import a feature specification [description]"
|
|
53172
|
+
description: "Generate or import a feature specification [description]",
|
|
53173
|
+
args: "[description-text]"
|
|
53024
53174
|
},
|
|
53025
53175
|
promote: {
|
|
53026
53176
|
handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
|
|
53027
|
-
description: "Manually promote lesson to hive knowledge"
|
|
53177
|
+
description: "Manually promote lesson to hive knowledge",
|
|
53178
|
+
details: "Promotes a lesson directly to hive knowledge (--category flag sets category) or references an existing swarm lesson by ID (--from-swarm). Validates lesson text before promotion. Either direct text or --from-swarm ID is required.",
|
|
53179
|
+
args: "--category <category>, --from-swarm <lesson-id>, <lesson-text>"
|
|
53028
53180
|
},
|
|
53029
53181
|
reset: {
|
|
53030
53182
|
handler: (ctx) => handleResetCommand(ctx.directory, ctx.args),
|
|
53031
|
-
description: "Clear swarm state files [--confirm]"
|
|
53183
|
+
description: "Clear swarm state files [--confirm]",
|
|
53184
|
+
details: "DELETES plan.md, context.md, and summaries/ directory from .swarm/. Stops background automation and clears in-memory queues. SAFETY: requires --confirm flag \u2014 without it, displays a warning and tips to export first.",
|
|
53185
|
+
args: "--confirm (required)"
|
|
53032
53186
|
},
|
|
53033
53187
|
"reset-session": {
|
|
53034
53188
|
handler: (ctx) => handleResetSessionCommand(ctx.directory, ctx.args),
|
|
53035
|
-
description: "Clear session state while preserving plan, evidence, and knowledge"
|
|
53189
|
+
description: "Clear session state while preserving plan, evidence, and knowledge",
|
|
53190
|
+
details: "Deletes only .swarm/session/state.json and any other session files. Clears in-memory agent sessions and delegation chains. Preserves plan, evidence, and knowledge for cross-session continuity.",
|
|
53191
|
+
args: ""
|
|
53036
53192
|
},
|
|
53037
53193
|
rollback: {
|
|
53038
53194
|
handler: (ctx) => handleRollbackCommand(ctx.directory, ctx.args),
|
|
53039
|
-
description: "Restore swarm state to a checkpoint <phase>"
|
|
53195
|
+
description: "Restore swarm state to a checkpoint <phase>",
|
|
53196
|
+
details: "Restores .swarm/ state by directly overwriting files from a checkpoint directory (checkpoints/phase-<N>). Writes rollback event to events.jsonl. Without phase argument, lists available checkpoints. Partial failures are reported but processing continues.",
|
|
53197
|
+
args: "<phase-number>"
|
|
53040
53198
|
},
|
|
53041
53199
|
retrieve: {
|
|
53042
53200
|
handler: (ctx) => handleRetrieveCommand(ctx.directory, ctx.args),
|
|
53043
|
-
description: "Retrieve full output from a summary <id>"
|
|
53201
|
+
description: "Retrieve full output from a summary <id>",
|
|
53202
|
+
args: "<summary-id>",
|
|
53203
|
+
details: "Loads the full tool output that was previously summarized (referenced by IDs like S1, S2). Use when you need the complete output instead of the truncated summary."
|
|
53044
53204
|
},
|
|
53045
53205
|
handoff: {
|
|
53046
53206
|
handler: (ctx) => handleHandoffCommand(ctx.directory, ctx.args),
|
|
53047
|
-
description: "Prepare state for clean model switch (new session)"
|
|
53207
|
+
description: "Prepare state for clean model switch (new session)",
|
|
53208
|
+
args: "",
|
|
53209
|
+
details: "Generates handoff.md with full session state snapshot, including plan progress, recent decisions, and agent delegation history. Prepended to the next session prompt for seamless model switches."
|
|
53048
53210
|
},
|
|
53049
53211
|
turbo: {
|
|
53050
53212
|
handler: (ctx) => handleTurboCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
53051
|
-
description: "Toggle Turbo Mode for the active session [on|off]"
|
|
53213
|
+
description: "Toggle Turbo Mode for the active session [on|off]",
|
|
53214
|
+
args: "on, off",
|
|
53215
|
+
details: 'Toggles Turbo Mode which skips non-critical QA gates for faster iteration. When enabled, the architect can proceed without waiting for all automated checks. Session-scoped \u2014 resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.'
|
|
53052
53216
|
},
|
|
53053
53217
|
"full-auto": {
|
|
53054
53218
|
handler: (ctx) => handleFullAutoCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
53055
|
-
description: "Toggle Full-Auto Mode for the active session [on|off]"
|
|
53219
|
+
description: "Toggle Full-Auto Mode for the active session [on|off]",
|
|
53220
|
+
args: "on, off",
|
|
53221
|
+
details: 'Toggles Full-Auto Mode which enables autonomous execution without confirmation prompts. When enabled, the architect proceeds through implementation steps automatically. Session-scoped \u2014 resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.'
|
|
53056
53222
|
},
|
|
53057
53223
|
"write-retro": {
|
|
53058
53224
|
handler: (ctx) => handleWriteRetroCommand(ctx.directory, ctx.args),
|
|
53059
|
-
description: "Write a retrospective evidence bundle for a completed phase <json>"
|
|
53225
|
+
description: "Write a retrospective evidence bundle for a completed phase <json>",
|
|
53226
|
+
details: "Writes retrospective evidence bundle to .swarm/evidence/retro-{phase}/evidence.json. Required JSON: phase, summary, task_count, task_complexity, total_tool_calls, coder_revisions, reviewer_rejections, test_failures, security_findings, integration_issues. Optional: lessons_learned (max 5), top_rejection_reasons, task_id, metadata.",
|
|
53227
|
+
args: "<json: {phase, summary, task_count, task_complexity, ...}>"
|
|
53060
53228
|
},
|
|
53061
53229
|
"knowledge migrate": {
|
|
53062
53230
|
handler: (ctx) => handleKnowledgeMigrateCommand(ctx.directory, ctx.args),
|
|
53063
53231
|
description: "Migrate knowledge entries to the current format",
|
|
53064
|
-
subcommandOf: "knowledge"
|
|
53232
|
+
subcommandOf: "knowledge",
|
|
53233
|
+
details: "One-time migration from .swarm/context.md SME cache to .swarm/knowledge.jsonl. Skips if sentinel file .swarm/.knowledge-migrated exists, if context.md is absent, or if context.md is empty. Reports entries migrated, dropped (validation/dedup), and total processed.",
|
|
53234
|
+
args: "<directory>"
|
|
53065
53235
|
},
|
|
53066
53236
|
"knowledge quarantine": {
|
|
53067
53237
|
handler: (ctx) => handleKnowledgeQuarantineCommand(ctx.directory, ctx.args),
|
|
53068
53238
|
description: "Move a knowledge entry to quarantine <id> [reason]",
|
|
53069
|
-
subcommandOf: "knowledge"
|
|
53239
|
+
subcommandOf: "knowledge",
|
|
53240
|
+
details: 'Moves a knowledge entry to quarantine with optional reason string (defaults to "Quarantined via /swarm knowledge quarantine command"). Validates entry ID format (1-64 alphanumeric/hyphen/underscore). Quarantined entries are excluded from knowledge queries.',
|
|
53241
|
+
args: "<entry-id> [reason]"
|
|
53070
53242
|
},
|
|
53071
53243
|
"knowledge restore": {
|
|
53072
53244
|
handler: (ctx) => handleKnowledgeRestoreCommand(ctx.directory, ctx.args),
|
|
53073
53245
|
description: "Restore a quarantined knowledge entry <id>",
|
|
53074
|
-
subcommandOf: "knowledge"
|
|
53246
|
+
subcommandOf: "knowledge",
|
|
53247
|
+
details: "Restores a quarantined knowledge entry back to the active knowledge store by ID. Validates entry ID format (1-64 alphanumeric/hyphen/underscore). Entry must currently be in quarantine state.",
|
|
53248
|
+
args: "<entry-id>"
|
|
53075
53249
|
},
|
|
53076
53250
|
knowledge: {
|
|
53077
53251
|
handler: (ctx) => handleKnowledgeListCommand(ctx.directory, ctx.args),
|
|
@@ -53079,7 +53253,9 @@ var COMMAND_REGISTRY = {
|
|
|
53079
53253
|
},
|
|
53080
53254
|
checkpoint: {
|
|
53081
53255
|
handler: (ctx) => handleCheckpointCommand(ctx.directory, ctx.args),
|
|
53082
|
-
description: "Manage project checkpoints [save|restore|delete|list] <label>"
|
|
53256
|
+
description: "Manage project checkpoints [save|restore|delete|list] <label>",
|
|
53257
|
+
details: "save: creates named snapshot of current .swarm/ state. restore: soft-resets to checkpoint by overwriting current .swarm/ files. delete: removes named checkpoint. list: shows all checkpoints with timestamps. All subcommands require a label except list.",
|
|
53258
|
+
args: "<save|restore|delete|list> <label>"
|
|
53083
53259
|
}
|
|
53084
53260
|
};
|
|
53085
53261
|
var VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
|
|
@@ -53429,8 +53605,8 @@ SECURITY_KEYWORDS: password, secret, token, credential, auth, login, encryption,
|
|
|
53429
53605
|
{{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components \u2014 runs BEFORE coder on UI tasks)
|
|
53430
53606
|
|
|
53431
53607
|
## SLASH COMMANDS
|
|
53432
|
-
|
|
53433
|
-
|
|
53608
|
+
{{SLASH_COMMANDS}}
|
|
53609
|
+
Commands above are documented with args and behavioral details. Run commands via /swarm <command> [args].
|
|
53434
53610
|
Outside OpenCode, invoke any plugin command via: \`bunx opencode-swarm run <command> [args]\` (e.g. \`bunx opencode-swarm run knowledge migrate\`). Do not use \`bun -e\` or look for \`src/commands/\` \u2014 those paths are internal to the plugin source and do not exist in user project directories.
|
|
53435
53611
|
|
|
53436
53612
|
SMEs advise only. Reviewer and critic review only. None of them write code.
|
|
@@ -54158,7 +54334,131 @@ function buildAvailableToolsList() {
|
|
|
54158
54334
|
}).join(", ");
|
|
54159
54335
|
}
|
|
54160
54336
|
function buildSlashCommandsList() {
|
|
54161
|
-
|
|
54337
|
+
const SKIP_ALIASES = new Set(["config-doctor", "evidence-summary"]);
|
|
54338
|
+
const READ_ONLY_OBSERVATION = new Set([
|
|
54339
|
+
"status",
|
|
54340
|
+
"history",
|
|
54341
|
+
"agents",
|
|
54342
|
+
"config",
|
|
54343
|
+
"plan",
|
|
54344
|
+
"benchmark",
|
|
54345
|
+
"export",
|
|
54346
|
+
"retrieve"
|
|
54347
|
+
]);
|
|
54348
|
+
const CATEGORY_ORDER = [
|
|
54349
|
+
"Session Lifecycle",
|
|
54350
|
+
"Planning",
|
|
54351
|
+
"Execution Modes",
|
|
54352
|
+
"Observation",
|
|
54353
|
+
"Knowledge",
|
|
54354
|
+
"State Management",
|
|
54355
|
+
"Diagnostics"
|
|
54356
|
+
];
|
|
54357
|
+
const COMMANDS_BY_CATEGORY = {
|
|
54358
|
+
"Session Lifecycle": [
|
|
54359
|
+
"close",
|
|
54360
|
+
"reset",
|
|
54361
|
+
"reset-session",
|
|
54362
|
+
"handoff",
|
|
54363
|
+
"archive"
|
|
54364
|
+
],
|
|
54365
|
+
Planning: [
|
|
54366
|
+
"specify",
|
|
54367
|
+
"clarify",
|
|
54368
|
+
"analyze",
|
|
54369
|
+
"plan",
|
|
54370
|
+
"sync-plan",
|
|
54371
|
+
"acknowledge-spec-drift"
|
|
54372
|
+
],
|
|
54373
|
+
"Execution Modes": ["turbo", "full-auto"],
|
|
54374
|
+
Observation: [
|
|
54375
|
+
"status",
|
|
54376
|
+
"history",
|
|
54377
|
+
"agents",
|
|
54378
|
+
"config",
|
|
54379
|
+
"benchmark",
|
|
54380
|
+
"export",
|
|
54381
|
+
"evidence",
|
|
54382
|
+
"evidence summary",
|
|
54383
|
+
"retrieve"
|
|
54384
|
+
],
|
|
54385
|
+
Knowledge: [
|
|
54386
|
+
"knowledge",
|
|
54387
|
+
"knowledge migrate",
|
|
54388
|
+
"knowledge quarantine",
|
|
54389
|
+
"knowledge restore",
|
|
54390
|
+
"promote",
|
|
54391
|
+
"curate"
|
|
54392
|
+
],
|
|
54393
|
+
"State Management": ["checkpoint", "rollback", "write-retro"],
|
|
54394
|
+
Diagnostics: [
|
|
54395
|
+
"diagnose",
|
|
54396
|
+
"preflight",
|
|
54397
|
+
"doctor tools",
|
|
54398
|
+
"config doctor",
|
|
54399
|
+
"simulate",
|
|
54400
|
+
"dark-matter"
|
|
54401
|
+
]
|
|
54402
|
+
};
|
|
54403
|
+
const lines = [];
|
|
54404
|
+
const subcommandMap = {};
|
|
54405
|
+
for (const [cmdName, cmdEntry] of Object.entries(COMMAND_REGISTRY)) {
|
|
54406
|
+
const entry = cmdEntry;
|
|
54407
|
+
if (entry.subcommandOf) {
|
|
54408
|
+
if (!subcommandMap[entry.subcommandOf]) {
|
|
54409
|
+
subcommandMap[entry.subcommandOf] = [];
|
|
54410
|
+
}
|
|
54411
|
+
subcommandMap[entry.subcommandOf].push(cmdName);
|
|
54412
|
+
}
|
|
54413
|
+
}
|
|
54414
|
+
const compoundsInValidCommands = new Set;
|
|
54415
|
+
for (const category of CATEGORY_ORDER) {
|
|
54416
|
+
lines.push(`**${category}**`);
|
|
54417
|
+
const commandNames = COMMANDS_BY_CATEGORY[category];
|
|
54418
|
+
for (const name2 of commandNames) {
|
|
54419
|
+
const entry = COMMAND_REGISTRY[name2];
|
|
54420
|
+
if (!entry)
|
|
54421
|
+
continue;
|
|
54422
|
+
if (SKIP_ALIASES.has(name2))
|
|
54423
|
+
continue;
|
|
54424
|
+
if (entry.subcommandOf && !VALID_COMMANDS.includes(name2))
|
|
54425
|
+
continue;
|
|
54426
|
+
lines.push(`- \`/swarm ${name2}\` \u2014 ${entry.description}`);
|
|
54427
|
+
if (entry.subcommandOf && VALID_COMMANDS.includes(name2)) {
|
|
54428
|
+
compoundsInValidCommands.add(name2);
|
|
54429
|
+
}
|
|
54430
|
+
if (READ_ONLY_OBSERVATION.has(name2))
|
|
54431
|
+
continue;
|
|
54432
|
+
if (entry.details) {
|
|
54433
|
+
lines.push(` ${entry.details}`);
|
|
54434
|
+
}
|
|
54435
|
+
if (entry.args) {
|
|
54436
|
+
lines.push(` Args: ${entry.args}`);
|
|
54437
|
+
}
|
|
54438
|
+
}
|
|
54439
|
+
for (const parent of commandNames) {
|
|
54440
|
+
const subs = subcommandMap[parent];
|
|
54441
|
+
if (!subs)
|
|
54442
|
+
continue;
|
|
54443
|
+
for (const subName of subs) {
|
|
54444
|
+
const subEntry = COMMAND_REGISTRY[subName];
|
|
54445
|
+
if (!subEntry)
|
|
54446
|
+
continue;
|
|
54447
|
+
if (compoundsInValidCommands.has(subName) || subEntry.subcommandOf && VALID_COMMANDS.includes(subName) || SKIP_ALIASES.has(subName)) {
|
|
54448
|
+
continue;
|
|
54449
|
+
}
|
|
54450
|
+
lines.push(` - \`/swarm ${subName}\` \u2014 ${subEntry.description}`);
|
|
54451
|
+
if (subEntry.details) {
|
|
54452
|
+
lines.push(` ${subEntry.details}`);
|
|
54453
|
+
}
|
|
54454
|
+
if (subEntry.args) {
|
|
54455
|
+
lines.push(` Args: ${subEntry.args}`);
|
|
54456
|
+
}
|
|
54457
|
+
}
|
|
54458
|
+
}
|
|
54459
|
+
}
|
|
54460
|
+
return lines.join(`
|
|
54461
|
+
`);
|
|
54162
54462
|
}
|
|
54163
54463
|
function createArchitectAgent(model, customPrompt, customAppendPrompt, adversarialTesting) {
|
|
54164
54464
|
let prompt = ARCHITECT_PROMPT;
|
|
@@ -56198,7 +56498,7 @@ class PlanSyncWorker {
|
|
|
56198
56498
|
const planJsonPath = path36.join(swarmDir, "plan.json");
|
|
56199
56499
|
const markerPath = path36.join(swarmDir, ".plan-write-marker");
|
|
56200
56500
|
const planStats = fs24.statSync(planJsonPath);
|
|
56201
|
-
const planMtimeMs = planStats.mtimeMs;
|
|
56501
|
+
const planMtimeMs = Math.floor(planStats.mtimeMs);
|
|
56202
56502
|
const markerContent = fs24.readFileSync(markerPath, "utf8");
|
|
56203
56503
|
const marker = JSON.parse(markerContent);
|
|
56204
56504
|
const markerTimestampMs = new Date(marker.timestamp).getTime();
|
|
@@ -56229,12 +56529,54 @@ init_status_artifact();
|
|
|
56229
56529
|
init_trigger();
|
|
56230
56530
|
|
|
56231
56531
|
// src/commands/index.ts
|
|
56232
|
-
|
|
56233
|
-
"## Swarm Commands",
|
|
56234
|
-
|
|
56235
|
-
|
|
56236
|
-
|
|
56532
|
+
function buildHelpText() {
|
|
56533
|
+
const lines = ["## Swarm Commands", ""];
|
|
56534
|
+
const shownAsSubcommand = new Set;
|
|
56535
|
+
for (const cmd of VALID_COMMANDS) {
|
|
56536
|
+
if (cmd.includes(" ")) {
|
|
56537
|
+
const parent = cmd.split(" ")[0];
|
|
56538
|
+
if (VALID_COMMANDS.includes(parent)) {
|
|
56539
|
+
shownAsSubcommand.add(cmd);
|
|
56540
|
+
}
|
|
56541
|
+
continue;
|
|
56542
|
+
}
|
|
56543
|
+
const entry = COMMAND_REGISTRY[cmd];
|
|
56544
|
+
lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`);
|
|
56545
|
+
if (entry.args) {
|
|
56546
|
+
lines.push(` Args: \`${entry.args}\``);
|
|
56547
|
+
}
|
|
56548
|
+
if (entry.details) {
|
|
56549
|
+
lines.push(` ${entry.details}`);
|
|
56550
|
+
}
|
|
56551
|
+
const subcommands = VALID_COMMANDS.filter((sub) => sub.startsWith(`${cmd} `) && sub !== cmd);
|
|
56552
|
+
for (const sub of subcommands) {
|
|
56553
|
+
const subEntry = COMMAND_REGISTRY[sub];
|
|
56554
|
+
const subName = sub.slice(cmd.length + 1);
|
|
56555
|
+
lines.push(` - \`${subName}\` \u2014 ${subEntry.description}`);
|
|
56556
|
+
if (subEntry.args) {
|
|
56557
|
+
lines.push(` Args: \`${subEntry.args}\``);
|
|
56558
|
+
}
|
|
56559
|
+
if (subEntry.details) {
|
|
56560
|
+
lines.push(` ${subEntry.details}`);
|
|
56561
|
+
}
|
|
56562
|
+
}
|
|
56563
|
+
}
|
|
56564
|
+
for (const cmd of VALID_COMMANDS) {
|
|
56565
|
+
if (!cmd.includes(" ") || shownAsSubcommand.has(cmd))
|
|
56566
|
+
continue;
|
|
56567
|
+
const entry = COMMAND_REGISTRY[cmd];
|
|
56568
|
+
lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`);
|
|
56569
|
+
if (entry.args) {
|
|
56570
|
+
lines.push(` Args: \`${entry.args}\``);
|
|
56571
|
+
}
|
|
56572
|
+
if (entry.details) {
|
|
56573
|
+
lines.push(` ${entry.details}`);
|
|
56574
|
+
}
|
|
56575
|
+
}
|
|
56576
|
+
return lines.join(`
|
|
56237
56577
|
`);
|
|
56578
|
+
}
|
|
56579
|
+
var HELP_TEXT = buildHelpText();
|
|
56238
56580
|
function createSwarmCommandHandler(directory, agents) {
|
|
56239
56581
|
return async (input, output) => {
|
|
56240
56582
|
if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
|
|
@@ -57676,6 +58018,54 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
|
|
|
57676
58018
|
"pre_check_batch"
|
|
57677
58019
|
];
|
|
57678
58020
|
const requireReviewerAndTestEngineer = cfg.qa_gates?.require_reviewer_test_engineer ?? true;
|
|
58021
|
+
function checkDestructiveCommand(tool3, args2) {
|
|
58022
|
+
if (tool3 !== "bash" && tool3 !== "shell")
|
|
58023
|
+
return;
|
|
58024
|
+
if (cfg.block_destructive_commands === false)
|
|
58025
|
+
return;
|
|
58026
|
+
const toolArgs = args2;
|
|
58027
|
+
const command = typeof toolArgs?.command === "string" ? toolArgs.command.trim() : "";
|
|
58028
|
+
if (!command)
|
|
58029
|
+
return;
|
|
58030
|
+
if (/:\s*\(\s*\)\s*\{[^}]*\|[^}]*:/.test(command)) {
|
|
58031
|
+
throw new Error(`BLOCKED: Potentially destructive shell command detected: fork bomb pattern`);
|
|
58032
|
+
}
|
|
58033
|
+
const rmFlagPattern = /^rm\s+(-r\s+-f|-f\s+-r|-rf|-fr)\s+(.+)$/;
|
|
58034
|
+
const rmMatch = rmFlagPattern.exec(command);
|
|
58035
|
+
if (rmMatch) {
|
|
58036
|
+
const targetPart = rmMatch[2].trim();
|
|
58037
|
+
const targets = targetPart.split(/\s+/);
|
|
58038
|
+
const safeTargets = /^(node_modules|\.git)$/;
|
|
58039
|
+
const allSafe = targets.every((t) => safeTargets.test(t));
|
|
58040
|
+
if (!allSafe) {
|
|
58041
|
+
throw new Error(`BLOCKED: Potentially destructive shell command: rm -rf on unsafe path(s): ${targetPart}`);
|
|
58042
|
+
}
|
|
58043
|
+
}
|
|
58044
|
+
if (/^git\s+push\b.*?(--force|-f)\b/.test(command)) {
|
|
58045
|
+
throw new Error(`BLOCKED: Force push detected \u2014 git push --force is not allowed`);
|
|
58046
|
+
}
|
|
58047
|
+
if (/^git\s+reset\s+--hard/.test(command)) {
|
|
58048
|
+
throw new Error(`BLOCKED: "git reset --hard" detected \u2014 use --soft or --mixed with caution`);
|
|
58049
|
+
}
|
|
58050
|
+
if (/^git\s+reset\s+--mixed\s+\S+/.test(command)) {
|
|
58051
|
+
throw new Error(`BLOCKED: "git reset --mixed" with a target branch/commit is not allowed`);
|
|
58052
|
+
}
|
|
58053
|
+
if (/^kubectl\s+delete\b/.test(command)) {
|
|
58054
|
+
throw new Error(`BLOCKED: "kubectl delete" detected \u2014 destructive cluster operation`);
|
|
58055
|
+
}
|
|
58056
|
+
if (/^docker\s+system\s+prune\b/.test(command)) {
|
|
58057
|
+
throw new Error(`BLOCKED: "docker system prune" detected \u2014 destructive container operation`);
|
|
58058
|
+
}
|
|
58059
|
+
if (/^\s*DROP\s+(TABLE|DATABASE|SCHEMA)\b/i.test(command)) {
|
|
58060
|
+
throw new Error(`BLOCKED: SQL DROP command detected \u2014 destructive database operation`);
|
|
58061
|
+
}
|
|
58062
|
+
if (/^\s*TRUNCATE\s+TABLE\b/i.test(command)) {
|
|
58063
|
+
throw new Error(`BLOCKED: SQL TRUNCATE command detected \u2014 destructive database operation`);
|
|
58064
|
+
}
|
|
58065
|
+
if (/^mkfs[./]/.test(command)) {
|
|
58066
|
+
throw new Error(`BLOCKED: Disk format command (mkfs) detected \u2014 disk formatting operation`);
|
|
58067
|
+
}
|
|
58068
|
+
}
|
|
57679
58069
|
async function checkGateLimits(params) {
|
|
57680
58070
|
const { sessionID, window: window2, agentConfig, elapsedMinutes, repetitionCount } = params;
|
|
57681
58071
|
if (agentConfig.max_tool_calls > 0 && window2.toolCalls >= agentConfig.max_tool_calls) {
|
|
@@ -58025,6 +58415,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
|
|
|
58025
58415
|
handleDelegatedWriteTracking(input.sessionID, input.tool, output.args);
|
|
58026
58416
|
handleLoopDetection(input.sessionID, input.tool, output.args);
|
|
58027
58417
|
handleTestSuiteBlocking(input.tool, output.args);
|
|
58418
|
+
checkDestructiveCommand(input.tool, output.args);
|
|
58028
58419
|
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
58029
58420
|
handlePlanAndScopeProtection(input.sessionID, input.tool, output.args);
|
|
58030
58421
|
const toolArgs = output.args;
|
|
@@ -58205,18 +58596,18 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
|
|
|
58205
58596
|
const fallbackModels = swarmAgents?.[baseAgentName]?.fallback_models;
|
|
58206
58597
|
session.modelFallbackExhausted = !fallbackModels || session.model_fallback_index > fallbackModels.length;
|
|
58207
58598
|
const fallbackModel = resolveFallbackModel(baseAgentName, session.model_fallback_index, swarmAgents);
|
|
58599
|
+
const primaryModel = swarmAgents?.[baseAgentName]?.model ?? "default";
|
|
58208
58600
|
if (fallbackModel) {
|
|
58209
|
-
const primaryModel = swarmAgents?.[baseAgentName]?.model ?? "default";
|
|
58210
58601
|
if (swarmAgents?.[baseAgentName]) {
|
|
58211
58602
|
swarmAgents[baseAgentName].model = fallbackModel;
|
|
58212
58603
|
}
|
|
58213
|
-
telemetry.modelFallback(input.sessionID, session.agentName, primaryModel, fallbackModel, "transient_model_error");
|
|
58214
58604
|
session.pendingAdvisoryMessages ??= [];
|
|
58215
58605
|
session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Applied fallback model "${fallbackModel}" (attempt ${session.model_fallback_index}). ` + `Using /swarm handoff to reset to primary model.`);
|
|
58216
58606
|
} else {
|
|
58217
58607
|
session.pendingAdvisoryMessages ??= [];
|
|
58218
58608
|
session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Transient model error detected (attempt ${session.model_fallback_index}). ` + `No fallback models configured for this agent. Add "fallback_models": ["model-a", "model-b"] ` + `to the agent's config in opencode-swarm.json.`);
|
|
58219
58609
|
}
|
|
58610
|
+
telemetry.modelFallback(input.sessionID, session.agentName, primaryModel, fallbackModel ?? "none", "transient_model_error");
|
|
58220
58611
|
swarmState.pendingEvents++;
|
|
58221
58612
|
}
|
|
58222
58613
|
}
|
|
@@ -58589,17 +58980,22 @@ function normalizePathWithCache(filePath, cwd) {
|
|
|
58589
58980
|
return fallback;
|
|
58590
58981
|
}
|
|
58591
58982
|
}
|
|
58592
|
-
function getGlobMatcher(pattern) {
|
|
58983
|
+
function getGlobMatcher(pattern, caseInsensitive = process.platform === "win32" || process.platform === "darwin") {
|
|
58593
58984
|
const cached3 = globMatcherCache.get(pattern);
|
|
58594
58985
|
if (cached3 !== undefined) {
|
|
58595
58986
|
return cached3;
|
|
58596
58987
|
}
|
|
58597
|
-
|
|
58598
|
-
|
|
58599
|
-
|
|
58600
|
-
|
|
58601
|
-
|
|
58602
|
-
|
|
58988
|
+
try {
|
|
58989
|
+
const matcher = import_picomatch.default(pattern, {
|
|
58990
|
+
dot: true,
|
|
58991
|
+
nocase: caseInsensitive
|
|
58992
|
+
});
|
|
58993
|
+
globMatcherCache.set(pattern, matcher);
|
|
58994
|
+
return matcher;
|
|
58995
|
+
} catch (err2) {
|
|
58996
|
+
warn(`picomatch error for pattern "${pattern}": ${err2}`);
|
|
58997
|
+
return () => false;
|
|
58998
|
+
}
|
|
58603
58999
|
}
|
|
58604
59000
|
var DEFAULT_AGENT_AUTHORITY_RULES = {
|
|
58605
59001
|
architect: {
|
|
@@ -58677,7 +59073,7 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
|
|
|
58677
59073
|
const dir = cwd || process.cwd();
|
|
58678
59074
|
let normalizedPath;
|
|
58679
59075
|
try {
|
|
58680
|
-
const normalizedWithSymlinks = normalizePathWithCache(filePath,
|
|
59076
|
+
const normalizedWithSymlinks = normalizePathWithCache(filePath, dir);
|
|
58681
59077
|
const resolved = path38.resolve(dir, normalizedWithSymlinks);
|
|
58682
59078
|
normalizedPath = path38.relative(dir, resolved).replace(/\\/g, "/");
|
|
58683
59079
|
} catch {
|
|
@@ -58730,6 +59126,16 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
|
|
|
58730
59126
|
return { allowed: true };
|
|
58731
59127
|
}
|
|
58732
59128
|
}
|
|
59129
|
+
if (rules.blockedPrefix && rules.blockedPrefix.length > 0) {
|
|
59130
|
+
for (const prefix of rules.blockedPrefix) {
|
|
59131
|
+
if (normalizedPath.startsWith(prefix)) {
|
|
59132
|
+
return {
|
|
59133
|
+
allowed: false,
|
|
59134
|
+
reason: `Path blocked: ${normalizedPath} is under ${prefix}`
|
|
59135
|
+
};
|
|
59136
|
+
}
|
|
59137
|
+
}
|
|
59138
|
+
}
|
|
58733
59139
|
if (rules.allowedPrefix != null && rules.allowedPrefix.length > 0) {
|
|
58734
59140
|
const isAllowed = rules.allowedPrefix.some((prefix) => normalizedPath.startsWith(prefix));
|
|
58735
59141
|
if (!isAllowed) {
|
|
@@ -58744,16 +59150,6 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
|
|
|
58744
59150
|
reason: `Path ${normalizedPath} not in allowed list for ${normalizedAgent}`
|
|
58745
59151
|
};
|
|
58746
59152
|
}
|
|
58747
|
-
if (rules.blockedPrefix && rules.blockedPrefix.length > 0) {
|
|
58748
|
-
for (const prefix of rules.blockedPrefix) {
|
|
58749
|
-
if (normalizedPath.startsWith(prefix)) {
|
|
58750
|
-
return {
|
|
58751
|
-
allowed: false,
|
|
58752
|
-
reason: `Path blocked: ${normalizedPath} is under ${prefix}`
|
|
58753
|
-
};
|
|
58754
|
-
}
|
|
58755
|
-
}
|
|
58756
|
-
}
|
|
58757
59153
|
if (rules.blockedZones && rules.blockedZones.length > 0) {
|
|
58758
59154
|
const { zone } = classifyFile(normalizedPath);
|
|
58759
59155
|
if (rules.blockedZones.includes(zone)) {
|
|
@@ -59526,7 +59922,9 @@ var END_OF_SENTENCE_QUESTION_PATTERN = /\?\s*$/;
|
|
|
59526
59922
|
var PHASE_COMPLETION_PATTERNS = [
|
|
59527
59923
|
/Ready for Phase (?:\d+|\[?N\+1\]?)\??/i,
|
|
59528
59924
|
/phase.{0,20}(?:complete|finish|done|wrap)/i,
|
|
59529
|
-
/move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i
|
|
59925
|
+
/move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i,
|
|
59926
|
+
/(?:proceed|move)\s+to\s+the\s+next\s+phase/i,
|
|
59927
|
+
/what would you like.{0,20}(?:next|do next)/i
|
|
59530
59928
|
];
|
|
59531
59929
|
var QUESTION_ESCALATION_PATTERNS = [
|
|
59532
59930
|
/escalat/i,
|
|
@@ -61431,6 +61829,47 @@ ${handoffBlock}`);
|
|
|
61431
61829
|
}
|
|
61432
61830
|
} catch {}
|
|
61433
61831
|
}
|
|
61832
|
+
if (baseRole === "coder") {
|
|
61833
|
+
const sessionId_ccp = _input.sessionID ?? "";
|
|
61834
|
+
const ccpSession = swarmState.agentSessions.get(sessionId_ccp);
|
|
61835
|
+
try {
|
|
61836
|
+
const coderScope = ccpSession?.declaredCoderScope;
|
|
61837
|
+
const primaryFile = coderScope?.[0] ?? "";
|
|
61838
|
+
if (primaryFile.length > 0) {
|
|
61839
|
+
const { knowledge_recall: knowledge_recall2 } = await Promise.resolve().then(() => (init_knowledge_recall(), exports_knowledge_recall));
|
|
61840
|
+
const rawResult = await knowledge_recall2.execute({ query: primaryFile }, { directory });
|
|
61841
|
+
if (rawResult && typeof rawResult === "string") {
|
|
61842
|
+
const parsed = JSON.parse(rawResult);
|
|
61843
|
+
if (parsed.results.length > 0) {
|
|
61844
|
+
const lines = parsed.results.map((r) => {
|
|
61845
|
+
const lesson = r.lesson.length > 200 ? `${r.lesson.slice(0, 200)}...` : r.lesson;
|
|
61846
|
+
return `- [${r.category}] ${lesson}`;
|
|
61847
|
+
});
|
|
61848
|
+
tryInject(`## CONTEXT FROM KNOWLEDGE BASE
|
|
61849
|
+
${lines.join(`
|
|
61850
|
+
`)}`);
|
|
61851
|
+
}
|
|
61852
|
+
}
|
|
61853
|
+
}
|
|
61854
|
+
} catch {}
|
|
61855
|
+
try {
|
|
61856
|
+
const taskId_ccp = ccpSession?.currentTaskId;
|
|
61857
|
+
if (taskId_ccp && !taskId_ccp.includes("..") && !taskId_ccp.includes("/") && !taskId_ccp.includes("\\") && !taskId_ccp.includes("\x00")) {
|
|
61858
|
+
const evidencePath = path47.join(directory, ".swarm", "evidence", `${taskId_ccp}.json`);
|
|
61859
|
+
if (fs35.existsSync(evidencePath)) {
|
|
61860
|
+
const evidenceContent = fs35.readFileSync(evidencePath, "utf-8");
|
|
61861
|
+
const evidenceData = JSON.parse(evidenceContent);
|
|
61862
|
+
const rejections = (evidenceData.bundle?.entries ?? []).filter((e) => e.type === "gate" && e.gate_type === "reviewer" && e.verdict === "reject");
|
|
61863
|
+
if (rejections.length > 0) {
|
|
61864
|
+
const lines = rejections.map((r) => `- ${r.reason ?? "No reason provided"}`);
|
|
61865
|
+
tryInject(`## PRIOR REJECTIONS
|
|
61866
|
+
${lines.join(`
|
|
61867
|
+
`)}`);
|
|
61868
|
+
}
|
|
61869
|
+
}
|
|
61870
|
+
}
|
|
61871
|
+
} catch {}
|
|
61872
|
+
}
|
|
61434
61873
|
if (baseRole === "coder") {
|
|
61435
61874
|
const taskText_lang_a = plan2 && plan2.migration_status !== "migration_failed" ? extractCurrentTaskFromPlan(plan2) : null;
|
|
61436
61875
|
const langConstraints_a = buildLanguageCoderConstraints(taskText_lang_a);
|
|
@@ -67760,90 +68199,10 @@ var knowledge_query = createSwarmTool({
|
|
|
67760
68199
|
`);
|
|
67761
68200
|
}
|
|
67762
68201
|
});
|
|
67763
|
-
|
|
67764
|
-
|
|
67765
|
-
|
|
67766
|
-
|
|
67767
|
-
var knowledge_recall = createSwarmTool({
|
|
67768
|
-
description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
|
|
67769
|
-
args: {
|
|
67770
|
-
query: tool.schema.string().min(3).describe("Natural language search query"),
|
|
67771
|
-
top_n: tool.schema.number().int().min(1).max(20).optional().describe("Maximum results to return (default: 5)"),
|
|
67772
|
-
tier: tool.schema.enum(["all", "swarm", "hive"]).optional().describe("Knowledge tier to search (default: 'all')")
|
|
67773
|
-
},
|
|
67774
|
-
execute: async (args2, directory) => {
|
|
67775
|
-
let queryInput;
|
|
67776
|
-
let topNInput;
|
|
67777
|
-
let tierInput;
|
|
67778
|
-
try {
|
|
67779
|
-
if (args2 && typeof args2 === "object") {
|
|
67780
|
-
const obj = args2;
|
|
67781
|
-
queryInput = obj.query;
|
|
67782
|
-
topNInput = obj.top_n;
|
|
67783
|
-
tierInput = obj.tier;
|
|
67784
|
-
}
|
|
67785
|
-
} catch {}
|
|
67786
|
-
if (typeof queryInput !== "string" || queryInput.length < 3) {
|
|
67787
|
-
return JSON.stringify({
|
|
67788
|
-
results: [],
|
|
67789
|
-
total: 0,
|
|
67790
|
-
error: "query must be a string with at least 3 characters"
|
|
67791
|
-
});
|
|
67792
|
-
}
|
|
67793
|
-
let topN = 5;
|
|
67794
|
-
if (topNInput !== undefined) {
|
|
67795
|
-
if (typeof topNInput === "number" && Number.isInteger(topNInput)) {
|
|
67796
|
-
topN = Math.max(1, Math.min(20, topNInput));
|
|
67797
|
-
}
|
|
67798
|
-
}
|
|
67799
|
-
let tier = "all";
|
|
67800
|
-
if (tierInput !== undefined && typeof tierInput === "string") {
|
|
67801
|
-
if (tierInput === "swarm" || tierInput === "hive") {
|
|
67802
|
-
tier = tierInput;
|
|
67803
|
-
}
|
|
67804
|
-
}
|
|
67805
|
-
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
67806
|
-
const hivePath = resolveHiveKnowledgePath();
|
|
67807
|
-
const [swarmEntries, hiveEntries] = await Promise.all([
|
|
67808
|
-
readKnowledge(swarmPath),
|
|
67809
|
-
readKnowledge(hivePath)
|
|
67810
|
-
]);
|
|
67811
|
-
let entries = [];
|
|
67812
|
-
if (tier === "all" || tier === "swarm") {
|
|
67813
|
-
entries = entries.concat(swarmEntries);
|
|
67814
|
-
}
|
|
67815
|
-
if (tier === "all" || tier === "hive") {
|
|
67816
|
-
entries = entries.concat(hiveEntries);
|
|
67817
|
-
}
|
|
67818
|
-
if (entries.length === 0) {
|
|
67819
|
-
const result2 = { results: [], total: 0 };
|
|
67820
|
-
return JSON.stringify(result2);
|
|
67821
|
-
}
|
|
67822
|
-
const normalizedQuery = normalize2(queryInput);
|
|
67823
|
-
const queryBigrams = wordBigrams(normalizedQuery);
|
|
67824
|
-
const scoredEntries = entries.map((entry) => {
|
|
67825
|
-
const entryText = `${entry.lesson} ${entry.tags.join(" ")} ${entry.category}`;
|
|
67826
|
-
const entryBigrams = wordBigrams(entryText);
|
|
67827
|
-
const textScore = jaccardBigram(queryBigrams, entryBigrams);
|
|
67828
|
-
const boost = entry.status === "established" ? 0.1 : entry.status === "promoted" ? 0.05 : 0;
|
|
67829
|
-
const finalScore = textScore + boost;
|
|
67830
|
-
return {
|
|
67831
|
-
id: entry.id,
|
|
67832
|
-
confidence: entry.confidence,
|
|
67833
|
-
category: entry.category,
|
|
67834
|
-
lesson: entry.lesson,
|
|
67835
|
-
score: finalScore
|
|
67836
|
-
};
|
|
67837
|
-
});
|
|
67838
|
-
scoredEntries.sort((a, b) => b.score - a.score);
|
|
67839
|
-
const topResults = scoredEntries.slice(0, topN);
|
|
67840
|
-
const result = {
|
|
67841
|
-
results: topResults,
|
|
67842
|
-
total: topResults.length
|
|
67843
|
-
};
|
|
67844
|
-
return JSON.stringify(result);
|
|
67845
|
-
}
|
|
67846
|
-
});
|
|
68202
|
+
|
|
68203
|
+
// src/tools/index.ts
|
|
68204
|
+
init_knowledge_recall();
|
|
68205
|
+
|
|
67847
68206
|
// src/tools/knowledge-remove.ts
|
|
67848
68207
|
init_dist();
|
|
67849
68208
|
init_knowledge_store();
|
|
@@ -76173,7 +76532,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
76173
76532
|
message: "Invalid working_directory: null bytes are not allowed"
|
|
76174
76533
|
};
|
|
76175
76534
|
}
|
|
76176
|
-
|
|
76535
|
+
{
|
|
76177
76536
|
const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
|
|
76178
76537
|
if (devicePathPattern.test(args2.working_directory)) {
|
|
76179
76538
|
return {
|
|
@@ -76329,7 +76688,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
76329
76688
|
errors: [error93 instanceof Error ? error93.message : String(error93)]
|
|
76330
76689
|
};
|
|
76331
76690
|
} finally {
|
|
76332
|
-
if (lockResult
|
|
76691
|
+
if (lockResult?.acquired && lockResult.lock._release) {
|
|
76333
76692
|
try {
|
|
76334
76693
|
await lockResult.lock._release();
|
|
76335
76694
|
} catch (releaseError) {
|