opencode-swarm 6.67.0 → 6.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +363 -6
- package/dist/commands/brainstorm.d.ts +13 -0
- package/dist/commands/brainstorm.test.d.ts +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/qa-gates.d.ts +15 -0
- package/dist/commands/qa-gates.test.d.ts +1 -0
- package/dist/commands/registry.d.ts +12 -0
- package/dist/db/global-db.d.ts +22 -0
- package/dist/db/global-db.test.d.ts +7 -0
- package/dist/db/index.d.ts +13 -0
- package/dist/db/project-db.d.ts +40 -0
- package/dist/db/project-db.test.d.ts +4 -0
- package/dist/db/qa-gate-profile.d.ts +89 -0
- package/dist/db/qa-gate-profile.test.d.ts +4 -0
- package/dist/index.js +709 -97
- package/dist/state.d.ts +7 -0
- package/dist/tools/get-approved-plan.d.ts +4 -0
- package/dist/tools/get-qa-gate-profile.d.ts +27 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/set-qa-gates.d.ts +37 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,7 +79,9 @@ var init_tool_names = __esm(() => {
|
|
|
79
79
|
"suggest_patch",
|
|
80
80
|
"req_coverage",
|
|
81
81
|
"get_approved_plan",
|
|
82
|
-
"repo_map"
|
|
82
|
+
"repo_map",
|
|
83
|
+
"get_qa_gate_profile",
|
|
84
|
+
"set_qa_gates"
|
|
83
85
|
];
|
|
84
86
|
TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
85
87
|
});
|
|
@@ -217,7 +219,9 @@ var init_constants = __esm(() => {
|
|
|
217
219
|
"knowledge_remove",
|
|
218
220
|
"co_change_analyzer",
|
|
219
221
|
"suggest_patch",
|
|
220
|
-
"repo_map"
|
|
222
|
+
"repo_map",
|
|
223
|
+
"get_qa_gate_profile",
|
|
224
|
+
"set_qa_gates"
|
|
221
225
|
],
|
|
222
226
|
explorer: [
|
|
223
227
|
"complexity_hotspots",
|
|
@@ -409,7 +413,9 @@ var init_constants = __esm(() => {
|
|
|
409
413
|
suggest_patch: "Reviewer-safe structured patch suggestion tool. Produces context-anchored patch artifacts without file modification. Returns structured diagnostics on context mismatch.",
|
|
410
414
|
lint_spec: "validate .swarm/spec.md format and required fields",
|
|
411
415
|
get_approved_plan: "retrieve the last critic-approved immutable plan snapshot for baseline drift comparison",
|
|
412
|
-
repo_map: "query the repo code graph: importers, dependencies, blast radius, and localization context for structural awareness before refactoring"
|
|
416
|
+
repo_map: "query the repo code graph: importers, dependencies, blast radius, and localization context for structural awareness before refactoring",
|
|
417
|
+
get_qa_gate_profile: "retrieve the QA gate profile for the current plan: gates, lock state, and profile hash. Read-only.",
|
|
418
|
+
set_qa_gates: "configure the QA gate profile for the current plan. Architect-only. Ratchet-tighter only \u2014 rejected once the profile is locked after critic approval."
|
|
413
419
|
};
|
|
414
420
|
for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
415
421
|
const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
|
|
@@ -17692,6 +17698,7 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
17692
17698
|
scopeViolationDetected: false,
|
|
17693
17699
|
modifiedFilesThisCoderTask: [],
|
|
17694
17700
|
turboMode: false,
|
|
17701
|
+
qaGateSessionOverrides: {},
|
|
17695
17702
|
fullAutoMode: false,
|
|
17696
17703
|
fullAutoInteractionCount: 0,
|
|
17697
17704
|
fullAutoDeadlockCount: 0,
|
|
@@ -17817,6 +17824,9 @@ function ensureAgentSession(sessionId, agentName, directory) {
|
|
|
17817
17824
|
if (session.turboMode === undefined) {
|
|
17818
17825
|
session.turboMode = false;
|
|
17819
17826
|
}
|
|
17827
|
+
if (session.qaGateSessionOverrides === undefined) {
|
|
17828
|
+
session.qaGateSessionOverrides = {};
|
|
17829
|
+
}
|
|
17820
17830
|
if (session.model_fallback_index === undefined) {
|
|
17821
17831
|
session.model_fallback_index = 0;
|
|
17822
17832
|
}
|
|
@@ -35284,7 +35294,7 @@ function checkAgentToolMapAlignment(registeredKeys) {
|
|
|
35284
35294
|
id: `agent-tool-map-mismatch-${agentName}-${toolName}`,
|
|
35285
35295
|
title: "AGENT_TOOL_MAP alignment gap",
|
|
35286
35296
|
description: `Tool "${toolName}" is assigned to agent "${agentName}" in AGENT_TOOL_MAP but is not registered in the plugin's tool: {} block. The agent will not be able to use this tool.`,
|
|
35287
|
-
severity: "
|
|
35297
|
+
severity: "error",
|
|
35288
35298
|
path: `AGENT_TOOL_MAP.${agentName}`,
|
|
35289
35299
|
currentValue: toolName,
|
|
35290
35300
|
autoFixable: false
|
|
@@ -38750,12 +38760,12 @@ __export(exports_evidence_summary_integration, {
|
|
|
38750
38760
|
createEvidenceSummaryIntegration: () => createEvidenceSummaryIntegration,
|
|
38751
38761
|
EvidenceSummaryIntegration: () => EvidenceSummaryIntegration
|
|
38752
38762
|
});
|
|
38753
|
-
import { existsSync as
|
|
38763
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
38754
38764
|
import * as path36 from "path";
|
|
38755
38765
|
function persistSummary(swarmDir, artifact, filename) {
|
|
38756
38766
|
const swarmPath = path36.join(swarmDir, ".swarm");
|
|
38757
|
-
if (!
|
|
38758
|
-
|
|
38767
|
+
if (!existsSync22(swarmPath)) {
|
|
38768
|
+
mkdirSync11(swarmPath, { recursive: true });
|
|
38759
38769
|
}
|
|
38760
38770
|
const artifactPath = path36.join(swarmPath, filename);
|
|
38761
38771
|
const content = JSON.stringify(artifact, null, 2);
|
|
@@ -41023,7 +41033,7 @@ __export(exports_gate_evidence, {
|
|
|
41023
41033
|
deriveRequiredGates: () => deriveRequiredGates,
|
|
41024
41034
|
DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
|
|
41025
41035
|
});
|
|
41026
|
-
import { mkdirSync as
|
|
41036
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync18, renameSync as renameSync10, unlinkSync as unlinkSync5 } from "fs";
|
|
41027
41037
|
import * as path40 from "path";
|
|
41028
41038
|
function isValidTaskId2(taskId) {
|
|
41029
41039
|
return isStrictTaskId(taskId);
|
|
@@ -41088,7 +41098,7 @@ async function recordGateEvidence(directory, taskId, gate, sessionId, turbo) {
|
|
|
41088
41098
|
assertValidTaskId(taskId);
|
|
41089
41099
|
const evidenceDir = getEvidenceDir(directory);
|
|
41090
41100
|
const evidencePath = getEvidencePath(directory, taskId);
|
|
41091
|
-
|
|
41101
|
+
mkdirSync13(evidenceDir, { recursive: true });
|
|
41092
41102
|
const existing = readExisting(evidencePath);
|
|
41093
41103
|
const requiredGates = existing ? expandRequiredGates(existing.required_gates, gate) : deriveRequiredGates(gate);
|
|
41094
41104
|
const updated = {
|
|
@@ -41111,7 +41121,7 @@ async function recordAgentDispatch(directory, taskId, agentType, turbo) {
|
|
|
41111
41121
|
assertValidTaskId(taskId);
|
|
41112
41122
|
const evidenceDir = getEvidenceDir(directory);
|
|
41113
41123
|
const evidencePath = getEvidencePath(directory, taskId);
|
|
41114
|
-
|
|
41124
|
+
mkdirSync13(evidenceDir, { recursive: true });
|
|
41115
41125
|
const existing = readExisting(evidencePath);
|
|
41116
41126
|
const requiredGates = existing ? expandRequiredGates(existing.required_gates, agentType) : deriveRequiredGates(agentType);
|
|
41117
41127
|
const updated = {
|
|
@@ -43534,8 +43544,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
43534
43544
|
var moduleRtn;
|
|
43535
43545
|
var Module = moduleArg;
|
|
43536
43546
|
var readyPromiseResolve, readyPromiseReject;
|
|
43537
|
-
var readyPromise = new Promise((
|
|
43538
|
-
readyPromiseResolve =
|
|
43547
|
+
var readyPromise = new Promise((resolve27, reject) => {
|
|
43548
|
+
readyPromiseResolve = resolve27;
|
|
43539
43549
|
readyPromiseReject = reject;
|
|
43540
43550
|
});
|
|
43541
43551
|
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
|
@@ -43615,13 +43625,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
43615
43625
|
}
|
|
43616
43626
|
readAsync = /* @__PURE__ */ __name(async (url3) => {
|
|
43617
43627
|
if (isFileURI(url3)) {
|
|
43618
|
-
return new Promise((
|
|
43628
|
+
return new Promise((resolve27, reject) => {
|
|
43619
43629
|
var xhr = new XMLHttpRequest;
|
|
43620
43630
|
xhr.open("GET", url3, true);
|
|
43621
43631
|
xhr.responseType = "arraybuffer";
|
|
43622
43632
|
xhr.onload = () => {
|
|
43623
43633
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
43624
|
-
|
|
43634
|
+
resolve27(xhr.response);
|
|
43625
43635
|
return;
|
|
43626
43636
|
}
|
|
43627
43637
|
reject(xhr.status);
|
|
@@ -43841,10 +43851,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
43841
43851
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
43842
43852
|
var info2 = getWasmImports();
|
|
43843
43853
|
if (Module["instantiateWasm"]) {
|
|
43844
|
-
return new Promise((
|
|
43854
|
+
return new Promise((resolve27, reject) => {
|
|
43845
43855
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
43846
43856
|
receiveInstance(mod, inst);
|
|
43847
|
-
|
|
43857
|
+
resolve27(mod.exports);
|
|
43848
43858
|
});
|
|
43849
43859
|
});
|
|
43850
43860
|
}
|
|
@@ -45364,8 +45374,8 @@ async function loadGrammar(languageId) {
|
|
|
45364
45374
|
const parser = new Parser;
|
|
45365
45375
|
const wasmFileName = getWasmFileName(normalizedId);
|
|
45366
45376
|
const wasmPath = path66.join(getGrammarsDirAbsolute(), wasmFileName);
|
|
45367
|
-
const { existsSync:
|
|
45368
|
-
if (!
|
|
45377
|
+
const { existsSync: existsSync40 } = await import("fs");
|
|
45378
|
+
if (!existsSync40(wasmPath)) {
|
|
45369
45379
|
throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
|
|
45370
45380
|
Make sure to run 'bun run build' to copy grammar files to dist/lang/grammars/`);
|
|
45371
45381
|
}
|
|
@@ -45981,6 +45991,24 @@ async function handleBenchmarkCommand(directory, args2) {
|
|
|
45981
45991
|
`);
|
|
45982
45992
|
}
|
|
45983
45993
|
|
|
45994
|
+
// src/commands/brainstorm.ts
|
|
45995
|
+
function sanitizeTopic(raw) {
|
|
45996
|
+
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
45997
|
+
const stripped = collapsed.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
45998
|
+
const normalized = stripped.replace(/\s+/g, " ").trim();
|
|
45999
|
+
const MAX_TOPIC_LEN = 2000;
|
|
46000
|
+
if (normalized.length <= MAX_TOPIC_LEN)
|
|
46001
|
+
return normalized;
|
|
46002
|
+
return `${normalized.slice(0, MAX_TOPIC_LEN)}\u2026`;
|
|
46003
|
+
}
|
|
46004
|
+
async function handleBrainstormCommand(_directory, args2) {
|
|
46005
|
+
const description = sanitizeTopic(args2.join(" "));
|
|
46006
|
+
if (description) {
|
|
46007
|
+
return `[MODE: BRAINSTORM] ${description}`;
|
|
46008
|
+
}
|
|
46009
|
+
return "[MODE: BRAINSTORM] Please enter MODE: BRAINSTORM and begin the structured brainstorm workflow (CONTEXT SCAN \u2192 DIALOGUE \u2192 APPROACHES \u2192 DESIGN SECTIONS \u2192 SPEC WRITE + SELF-REVIEW \u2192 QA GATE SELECTION \u2192 TRANSITION).";
|
|
46010
|
+
}
|
|
46011
|
+
|
|
45984
46012
|
// src/commands/checkpoint.ts
|
|
45985
46013
|
init_zod();
|
|
45986
46014
|
init_checkpoint();
|
|
@@ -50015,6 +50043,11 @@ function formatToolDoctorMarkdown(result) {
|
|
|
50015
50043
|
}
|
|
50016
50044
|
lines.push("");
|
|
50017
50045
|
}
|
|
50046
|
+
if (result.summary.error > 0) {
|
|
50047
|
+
lines.push("---", "");
|
|
50048
|
+
lines.push(`**BLOCKING**: ${result.summary.error} error-severity finding(s) must be resolved before release. ` + `AGENT_TOOL_MAP alignment errors mean an agent's system prompt instructs the model to call a tool that opencode has not registered \u2014 the agent's workflow will silently fail at runtime.`);
|
|
50049
|
+
lines.push("");
|
|
50050
|
+
}
|
|
50018
50051
|
}
|
|
50019
50052
|
return lines.join(`
|
|
50020
50053
|
`);
|
|
@@ -51492,6 +51525,341 @@ async function handlePromoteCommand(directory, args2) {
|
|
|
51492
51525
|
}
|
|
51493
51526
|
}
|
|
51494
51527
|
|
|
51528
|
+
// src/db/qa-gate-profile.ts
|
|
51529
|
+
import { createHash as createHash4 } from "crypto";
|
|
51530
|
+
|
|
51531
|
+
// src/db/project-db.ts
|
|
51532
|
+
import { Database } from "bun:sqlite";
|
|
51533
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9 } from "fs";
|
|
51534
|
+
import { join as join26, resolve as resolve11 } from "path";
|
|
51535
|
+
var MIGRATIONS = [
|
|
51536
|
+
{
|
|
51537
|
+
version: 1,
|
|
51538
|
+
name: "create_project_constraints",
|
|
51539
|
+
sql: `CREATE TABLE project_constraints (
|
|
51540
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51541
|
+
constraint_type TEXT NOT NULL,
|
|
51542
|
+
content TEXT NOT NULL,
|
|
51543
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
51544
|
+
)`
|
|
51545
|
+
},
|
|
51546
|
+
{
|
|
51547
|
+
version: 2,
|
|
51548
|
+
name: "create_qa_gate_profile",
|
|
51549
|
+
sql: `CREATE TABLE qa_gate_profile (
|
|
51550
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51551
|
+
plan_id TEXT NOT NULL UNIQUE,
|
|
51552
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
51553
|
+
project_type TEXT,
|
|
51554
|
+
gates TEXT NOT NULL DEFAULT '{}',
|
|
51555
|
+
locked_at TEXT,
|
|
51556
|
+
locked_by_snapshot_seq INTEGER
|
|
51557
|
+
)`
|
|
51558
|
+
},
|
|
51559
|
+
{
|
|
51560
|
+
version: 3,
|
|
51561
|
+
name: "create_qa_gate_profile_immutability_trigger",
|
|
51562
|
+
sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
|
|
51563
|
+
BEFORE UPDATE ON qa_gate_profile
|
|
51564
|
+
WHEN OLD.locked_at IS NOT NULL
|
|
51565
|
+
BEGIN
|
|
51566
|
+
SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
|
|
51567
|
+
END`
|
|
51568
|
+
}
|
|
51569
|
+
];
|
|
51570
|
+
var _projectDbs = new Map;
|
|
51571
|
+
function runProjectMigrations(db) {
|
|
51572
|
+
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
51573
|
+
version INTEGER PRIMARY KEY,
|
|
51574
|
+
name TEXT NOT NULL,
|
|
51575
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
51576
|
+
)`);
|
|
51577
|
+
const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
51578
|
+
const currentVersion = row?.version ?? 0;
|
|
51579
|
+
for (const migration of MIGRATIONS) {
|
|
51580
|
+
if (migration.version <= currentVersion)
|
|
51581
|
+
continue;
|
|
51582
|
+
const apply = db.transaction(() => {
|
|
51583
|
+
db.run(migration.sql);
|
|
51584
|
+
db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
|
|
51585
|
+
migration.version,
|
|
51586
|
+
migration.name
|
|
51587
|
+
]);
|
|
51588
|
+
});
|
|
51589
|
+
apply();
|
|
51590
|
+
}
|
|
51591
|
+
}
|
|
51592
|
+
function projectDbPath(directory) {
|
|
51593
|
+
return join26(resolve11(directory), ".swarm", "swarm.db");
|
|
51594
|
+
}
|
|
51595
|
+
function projectDbExists(directory) {
|
|
51596
|
+
return existsSync18(projectDbPath(directory));
|
|
51597
|
+
}
|
|
51598
|
+
function getProjectDb(directory) {
|
|
51599
|
+
const key = resolve11(directory);
|
|
51600
|
+
const existing = _projectDbs.get(key);
|
|
51601
|
+
if (existing)
|
|
51602
|
+
return existing;
|
|
51603
|
+
const swarmDir = join26(key, ".swarm");
|
|
51604
|
+
mkdirSync9(swarmDir, { recursive: true });
|
|
51605
|
+
const db = new Database(join26(swarmDir, "swarm.db"));
|
|
51606
|
+
db.run("PRAGMA journal_mode = WAL;");
|
|
51607
|
+
db.run("PRAGMA synchronous = NORMAL;");
|
|
51608
|
+
db.run("PRAGMA busy_timeout = 5000;");
|
|
51609
|
+
db.run("PRAGMA foreign_keys = ON;");
|
|
51610
|
+
runProjectMigrations(db);
|
|
51611
|
+
_projectDbs.set(key, db);
|
|
51612
|
+
return db;
|
|
51613
|
+
}
|
|
51614
|
+
|
|
51615
|
+
// src/db/qa-gate-profile.ts
|
|
51616
|
+
var DEFAULT_QA_GATES = {
|
|
51617
|
+
reviewer: true,
|
|
51618
|
+
test_engineer: true,
|
|
51619
|
+
council_mode: false,
|
|
51620
|
+
sme_enabled: true,
|
|
51621
|
+
critic_pre_plan: true,
|
|
51622
|
+
hallucination_guard: false,
|
|
51623
|
+
sast_enabled: true
|
|
51624
|
+
};
|
|
51625
|
+
function rowToProfile(row) {
|
|
51626
|
+
let parsed = {};
|
|
51627
|
+
try {
|
|
51628
|
+
parsed = JSON.parse(row.gates);
|
|
51629
|
+
} catch {
|
|
51630
|
+
parsed = {};
|
|
51631
|
+
}
|
|
51632
|
+
const gates = { ...DEFAULT_QA_GATES, ...parsed };
|
|
51633
|
+
return {
|
|
51634
|
+
id: row.id,
|
|
51635
|
+
plan_id: row.plan_id,
|
|
51636
|
+
created_at: row.created_at,
|
|
51637
|
+
project_type: row.project_type,
|
|
51638
|
+
gates,
|
|
51639
|
+
locked_at: row.locked_at,
|
|
51640
|
+
locked_by_snapshot_seq: row.locked_by_snapshot_seq
|
|
51641
|
+
};
|
|
51642
|
+
}
|
|
51643
|
+
function getProfile(directory, planId) {
|
|
51644
|
+
if (!projectDbExists(directory))
|
|
51645
|
+
return null;
|
|
51646
|
+
const db = getProjectDb(directory);
|
|
51647
|
+
const row = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
|
|
51648
|
+
return row ? rowToProfile(row) : null;
|
|
51649
|
+
}
|
|
51650
|
+
function getOrCreateProfile(directory, planId, projectType) {
|
|
51651
|
+
const existing = getProfile(directory, planId);
|
|
51652
|
+
if (existing)
|
|
51653
|
+
return existing;
|
|
51654
|
+
const db = getProjectDb(directory);
|
|
51655
|
+
const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
|
|
51656
|
+
const insert = db.transaction(() => {
|
|
51657
|
+
db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
|
|
51658
|
+
});
|
|
51659
|
+
try {
|
|
51660
|
+
insert();
|
|
51661
|
+
} catch (err2) {
|
|
51662
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
51663
|
+
if (!msg.toLowerCase().includes("unique")) {
|
|
51664
|
+
throw err2;
|
|
51665
|
+
}
|
|
51666
|
+
}
|
|
51667
|
+
const after = getProfile(directory, planId);
|
|
51668
|
+
if (!after) {
|
|
51669
|
+
throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
|
|
51670
|
+
}
|
|
51671
|
+
return after;
|
|
51672
|
+
}
|
|
51673
|
+
function setGates(directory, planId, gates) {
|
|
51674
|
+
const current = getProfile(directory, planId);
|
|
51675
|
+
if (!current) {
|
|
51676
|
+
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
|
|
51677
|
+
}
|
|
51678
|
+
if (current.locked_at !== null) {
|
|
51679
|
+
throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
|
|
51680
|
+
}
|
|
51681
|
+
const merged = { ...current.gates };
|
|
51682
|
+
for (const key of Object.keys(gates)) {
|
|
51683
|
+
const incoming = gates[key];
|
|
51684
|
+
if (incoming === undefined)
|
|
51685
|
+
continue;
|
|
51686
|
+
if (incoming === false && current.gates[key] === true) {
|
|
51687
|
+
throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
|
|
51688
|
+
}
|
|
51689
|
+
if (incoming === true) {
|
|
51690
|
+
merged[key] = true;
|
|
51691
|
+
}
|
|
51692
|
+
}
|
|
51693
|
+
const db = getProjectDb(directory);
|
|
51694
|
+
db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
|
|
51695
|
+
JSON.stringify(merged),
|
|
51696
|
+
planId
|
|
51697
|
+
]);
|
|
51698
|
+
const updated = getProfile(directory, planId);
|
|
51699
|
+
if (!updated) {
|
|
51700
|
+
throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
|
|
51701
|
+
}
|
|
51702
|
+
return updated;
|
|
51703
|
+
}
|
|
51704
|
+
function lockProfile(directory, planId, snapshotSeq) {
|
|
51705
|
+
const current = getProfile(directory, planId);
|
|
51706
|
+
if (!current) {
|
|
51707
|
+
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 cannot lock`);
|
|
51708
|
+
}
|
|
51709
|
+
if (current.locked_at !== null) {
|
|
51710
|
+
return current;
|
|
51711
|
+
}
|
|
51712
|
+
const db = getProjectDb(directory);
|
|
51713
|
+
db.run("UPDATE qa_gate_profile SET locked_at = datetime('now'), locked_by_snapshot_seq = ? WHERE plan_id = ?", [snapshotSeq, planId]);
|
|
51714
|
+
const locked = getProfile(directory, planId);
|
|
51715
|
+
if (!locked) {
|
|
51716
|
+
throw new Error(`Failed to re-read locked QA gate profile for plan_id=${planId}`);
|
|
51717
|
+
}
|
|
51718
|
+
return locked;
|
|
51719
|
+
}
|
|
51720
|
+
function computeProfileHash(profile) {
|
|
51721
|
+
const payload = JSON.stringify({
|
|
51722
|
+
plan_id: profile.plan_id,
|
|
51723
|
+
gates: profile.gates
|
|
51724
|
+
});
|
|
51725
|
+
return createHash4("sha256").update(payload).digest("hex");
|
|
51726
|
+
}
|
|
51727
|
+
function getEffectiveGates(profile, sessionOverrides) {
|
|
51728
|
+
const merged = { ...profile.gates };
|
|
51729
|
+
for (const key of Object.keys(sessionOverrides)) {
|
|
51730
|
+
if (sessionOverrides[key] === true) {
|
|
51731
|
+
merged[key] = true;
|
|
51732
|
+
}
|
|
51733
|
+
}
|
|
51734
|
+
return merged;
|
|
51735
|
+
}
|
|
51736
|
+
|
|
51737
|
+
// src/commands/qa-gates.ts
|
|
51738
|
+
init_manager();
|
|
51739
|
+
init_state();
|
|
51740
|
+
var ALL_GATE_NAMES = [
|
|
51741
|
+
"reviewer",
|
|
51742
|
+
"test_engineer",
|
|
51743
|
+
"council_mode",
|
|
51744
|
+
"sme_enabled",
|
|
51745
|
+
"critic_pre_plan",
|
|
51746
|
+
"hallucination_guard",
|
|
51747
|
+
"sast_enabled"
|
|
51748
|
+
];
|
|
51749
|
+
function derivePlanId(plan) {
|
|
51750
|
+
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
51751
|
+
}
|
|
51752
|
+
function isGateName(name2) {
|
|
51753
|
+
return ALL_GATE_NAMES.includes(name2);
|
|
51754
|
+
}
|
|
51755
|
+
function formatGates(gates) {
|
|
51756
|
+
return ALL_GATE_NAMES.map((g) => ` - ${g}: ${gates[g] ? "on" : "off"}`).join(`
|
|
51757
|
+
`);
|
|
51758
|
+
}
|
|
51759
|
+
async function handleQaGatesCommand(directory, args2, sessionID) {
|
|
51760
|
+
const plan = await loadPlanJsonOnly(directory);
|
|
51761
|
+
if (!plan) {
|
|
51762
|
+
return "Error: plan.json not found or invalid. Create a plan first (e.g. /swarm specify or save_plan).";
|
|
51763
|
+
}
|
|
51764
|
+
const planId = derivePlanId(plan);
|
|
51765
|
+
const subcommand = args2[0]?.toLowerCase();
|
|
51766
|
+
const gateArgs = args2.slice(1);
|
|
51767
|
+
if (!subcommand || subcommand === "show" || subcommand === "status") {
|
|
51768
|
+
const profile = getProfile(directory, planId);
|
|
51769
|
+
const spec = profile ? profile.gates : DEFAULT_QA_GATES;
|
|
51770
|
+
const session = sessionID ? getAgentSession(sessionID) : null;
|
|
51771
|
+
const overrides = session?.qaGateSessionOverrides ?? {};
|
|
51772
|
+
const effective = profile ? getEffectiveGates(profile, overrides) : { ...DEFAULT_QA_GATES, ...overrides };
|
|
51773
|
+
const lines = [];
|
|
51774
|
+
lines.push(`QA Gate Profile for plan_id=${planId}`);
|
|
51775
|
+
if (!profile) {
|
|
51776
|
+
lines.push(" (no profile persisted yet \u2014 showing defaults)");
|
|
51777
|
+
} else {
|
|
51778
|
+
lines.push(` locked: ${profile.locked_at ? `yes @ ${profile.locked_at} (seq ${profile.locked_by_snapshot_seq ?? "?"})` : "no"}`);
|
|
51779
|
+
lines.push(` profile_hash: ${computeProfileHash(profile)}`);
|
|
51780
|
+
}
|
|
51781
|
+
lines.push("Spec-level gates:");
|
|
51782
|
+
lines.push(formatGates(spec));
|
|
51783
|
+
lines.push("Session overrides (ratchet-tighter only):");
|
|
51784
|
+
if (Object.keys(overrides).length === 0) {
|
|
51785
|
+
lines.push(" (none)");
|
|
51786
|
+
} else {
|
|
51787
|
+
for (const k of ALL_GATE_NAMES) {
|
|
51788
|
+
if (overrides[k] === true)
|
|
51789
|
+
lines.push(` - ${k}: on (override)`);
|
|
51790
|
+
}
|
|
51791
|
+
}
|
|
51792
|
+
lines.push("Effective gates:");
|
|
51793
|
+
lines.push(formatGates(effective));
|
|
51794
|
+
return lines.join(`
|
|
51795
|
+
`);
|
|
51796
|
+
}
|
|
51797
|
+
if (subcommand === "enable") {
|
|
51798
|
+
if (gateArgs.length === 0) {
|
|
51799
|
+
return "Usage: /swarm qa-gates enable <gate> [<gate> ...]";
|
|
51800
|
+
}
|
|
51801
|
+
const invalid = gateArgs.filter((g) => !isGateName(g));
|
|
51802
|
+
if (invalid.length > 0) {
|
|
51803
|
+
return `Error: unknown gate(s): ${invalid.join(", ")}. Valid gates: ${ALL_GATE_NAMES.join(", ")}`;
|
|
51804
|
+
}
|
|
51805
|
+
getOrCreateProfile(directory, planId);
|
|
51806
|
+
const patch = {};
|
|
51807
|
+
for (const g of gateArgs) {
|
|
51808
|
+
if (isGateName(g))
|
|
51809
|
+
patch[g] = true;
|
|
51810
|
+
}
|
|
51811
|
+
try {
|
|
51812
|
+
const updated = setGates(directory, planId, patch);
|
|
51813
|
+
return [
|
|
51814
|
+
`Enabled gates persisted for plan_id=${planId}:`,
|
|
51815
|
+
formatGates(updated.gates),
|
|
51816
|
+
`profile_hash: ${computeProfileHash(updated)}`
|
|
51817
|
+
].join(`
|
|
51818
|
+
`);
|
|
51819
|
+
} catch (err2) {
|
|
51820
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
51821
|
+
return `Error: ${msg}`;
|
|
51822
|
+
}
|
|
51823
|
+
}
|
|
51824
|
+
if (subcommand === "override") {
|
|
51825
|
+
if (!sessionID) {
|
|
51826
|
+
return "Error: session overrides require an active session context.";
|
|
51827
|
+
}
|
|
51828
|
+
if (gateArgs.length === 0) {
|
|
51829
|
+
return "Usage: /swarm qa-gates override <gate> [<gate> ...]";
|
|
51830
|
+
}
|
|
51831
|
+
const invalid = gateArgs.filter((g) => !isGateName(g));
|
|
51832
|
+
if (invalid.length > 0) {
|
|
51833
|
+
return `Error: unknown gate(s): ${invalid.join(", ")}. Valid gates: ${ALL_GATE_NAMES.join(", ")}`;
|
|
51834
|
+
}
|
|
51835
|
+
const session = getAgentSession(sessionID);
|
|
51836
|
+
if (!session) {
|
|
51837
|
+
return "Error: no active session found for override.";
|
|
51838
|
+
}
|
|
51839
|
+
const current = session.qaGateSessionOverrides ?? {};
|
|
51840
|
+
const next = { ...current };
|
|
51841
|
+
for (const g of gateArgs) {
|
|
51842
|
+
if (isGateName(g))
|
|
51843
|
+
next[g] = true;
|
|
51844
|
+
}
|
|
51845
|
+
session.qaGateSessionOverrides = next;
|
|
51846
|
+
return [
|
|
51847
|
+
`Session overrides updated for plan_id=${planId}:`,
|
|
51848
|
+
Object.keys(next).filter((k) => next[k] === true).map((k) => ` - ${k}: on`).join(`
|
|
51849
|
+
`) || " (none)"
|
|
51850
|
+
].join(`
|
|
51851
|
+
`);
|
|
51852
|
+
}
|
|
51853
|
+
return [
|
|
51854
|
+
"Usage:",
|
|
51855
|
+
" /swarm qa-gates show current profile + effective gates",
|
|
51856
|
+
" /swarm qa-gates enable <gate>... persist-enable gate(s) (rejected if locked)",
|
|
51857
|
+
" /swarm qa-gates override <gate>... session-only enable (ratchet-tighter)",
|
|
51858
|
+
`Valid gates: ${ALL_GATE_NAMES.join(", ")}`
|
|
51859
|
+
].join(`
|
|
51860
|
+
`);
|
|
51861
|
+
}
|
|
51862
|
+
|
|
51495
51863
|
// src/commands/reset.ts
|
|
51496
51864
|
import * as fs21 from "fs";
|
|
51497
51865
|
|
|
@@ -51548,13 +51916,13 @@ class CircuitBreaker {
|
|
|
51548
51916
|
if (this.config.callTimeoutMs <= 0) {
|
|
51549
51917
|
return fn();
|
|
51550
51918
|
}
|
|
51551
|
-
return new Promise((
|
|
51919
|
+
return new Promise((resolve12, reject) => {
|
|
51552
51920
|
const timeout = setTimeout(() => {
|
|
51553
51921
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
51554
51922
|
}, this.config.callTimeoutMs);
|
|
51555
51923
|
fn().then((result) => {
|
|
51556
51924
|
clearTimeout(timeout);
|
|
51557
|
-
|
|
51925
|
+
resolve12(result);
|
|
51558
51926
|
}).catch((error93) => {
|
|
51559
51927
|
clearTimeout(timeout);
|
|
51560
51928
|
reject(error93);
|
|
@@ -51700,7 +52068,7 @@ init_queue();
|
|
|
51700
52068
|
// src/background/worker.ts
|
|
51701
52069
|
init_event_bus();
|
|
51702
52070
|
function sleep(ms) {
|
|
51703
|
-
return new Promise((
|
|
52071
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
51704
52072
|
}
|
|
51705
52073
|
|
|
51706
52074
|
class WorkerManager {
|
|
@@ -52169,7 +52537,7 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
52169
52537
|
// src/summaries/manager.ts
|
|
52170
52538
|
init_utils2();
|
|
52171
52539
|
init_utils();
|
|
52172
|
-
import { mkdirSync as
|
|
52540
|
+
import { mkdirSync as mkdirSync10, readdirSync as readdirSync9, renameSync as renameSync8, rmSync as rmSync3, statSync as statSync7 } from "fs";
|
|
52173
52541
|
import * as path32 from "path";
|
|
52174
52542
|
var SUMMARY_ID_REGEX = /^S\d+$/;
|
|
52175
52543
|
function sanitizeSummaryId(id) {
|
|
@@ -52216,7 +52584,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
|
|
|
52216
52584
|
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
52217
52585
|
};
|
|
52218
52586
|
const entryJson = JSON.stringify(entry);
|
|
52219
|
-
|
|
52587
|
+
mkdirSync10(summaryDir, { recursive: true });
|
|
52220
52588
|
const tempPath = path32.join(summaryDir, `${sanitizedId}.json.tmp.${Date.now()}.${process.pid}`);
|
|
52221
52589
|
try {
|
|
52222
52590
|
await Bun.write(tempPath, entryJson);
|
|
@@ -53370,6 +53738,18 @@ var COMMAND_REGISTRY = {
|
|
|
53370
53738
|
description: "Generate or import a feature specification [description]",
|
|
53371
53739
|
args: "[description-text]"
|
|
53372
53740
|
},
|
|
53741
|
+
brainstorm: {
|
|
53742
|
+
handler: (ctx) => handleBrainstormCommand(ctx.directory, ctx.args),
|
|
53743
|
+
description: "Enter architect MODE: BRAINSTORM \u2014 structured seven-phase planning workflow [topic]",
|
|
53744
|
+
args: "[topic-text]",
|
|
53745
|
+
details: "Triggers the architect to run the brainstorm workflow: CONTEXT SCAN, single-question DIALOGUE, APPROACHES, DESIGN SECTIONS, SPEC WRITE + SELF-REVIEW, QA GATE SELECTION, TRANSITION. Use for new plans where requirements need to be drawn out before writing spec.md / plan.md."
|
|
53746
|
+
},
|
|
53747
|
+
"qa-gates": {
|
|
53748
|
+
handler: (ctx) => handleQaGatesCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
53749
|
+
description: "View or modify QA gate profile for the current plan [enable|override <gate>...]",
|
|
53750
|
+
args: "[show|enable|override] <gate>...",
|
|
53751
|
+
details: "show: display spec-level, session-override, and effective QA gates for the current plan. enable: persist gate(s) into the locked-once profile (architect; rejected after critic approval lock). override: session-only ratchet-tighter enable. Valid gates: reviewer, test_engineer, council_mode, sme_enabled, critic_pre_plan, hallucination_guard, sast_enabled."
|
|
53752
|
+
},
|
|
53373
53753
|
promote: {
|
|
53374
53754
|
handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
|
|
53375
53755
|
description: "Manually promote lesson to hive knowledge",
|
|
@@ -53913,7 +54293,7 @@ OUTPUT: Code scaffold for src/pages/Settings.tsx with component tree, typed prop
|
|
|
53913
54293
|
### MODE DETECTION (Priority Order)
|
|
53914
54294
|
Evaluate the user's request and context in this exact order \u2014 the FIRST matching rule wins:
|
|
53915
54295
|
|
|
53916
|
-
0. **EXPLICIT COMMAND OVERRIDE** \u2014 User explicitly invokes \`/swarm specify\`, \`/swarm clarify\`, or uses the phrases "specify [something about spec/requirements]", "write a spec", "create a spec", "define requirements", "list requirements", "define a feature", "I have requirements" \u2192 Enter MODE: SPECIFY
|
|
54296
|
+
0. **EXPLICIT COMMAND OVERRIDE** \u2014 User explicitly invokes \`/swarm specify\`, \`/swarm clarify\`, \`/swarm brainstorm\`, or uses the phrases "specify [something about spec/requirements]", "write a spec", "create a spec", "define requirements", "list requirements", "define a feature", "I have requirements", "brainstorm", "let's think through", "think this through with me", "workshop this idea" \u2192 Enter MODE: SPECIFY, MODE: CLARIFY-SPEC, or MODE: BRAINSTORM as appropriate. This override fires BEFORE RESUME \u2014 an explicit spec command always wins, even if plan.md has incomplete tasks. \`/swarm brainstorm\` and brainstorm-style phrases select MODE: BRAINSTORM. Note: bare "specify" in an ambiguous context (e.g., "specify what this does") should resolve via CLARIFY (priority 4) rather than this override \u2014 use context to determine intent.
|
|
53917
54297
|
1. **RESUME** \u2014 \`.swarm/plan.md\` exists and contains incomplete (unchecked) tasks AND the user has NOT issued an explicit spec command (see priority 0) \u2192 Resume at current task.
|
|
53918
54298
|
2. **SPECIFY** \u2014 No \`.swarm/spec.md\` exists AND no \`.swarm/plan.md\` exists \u2192 Enter MODE: SPECIFY.
|
|
53919
54299
|
3. **CLARIFY-SPEC** \u2014 \`.swarm/spec.md\` exists AND contains \`[NEEDS CLARIFICATION]\` markers; OR user explicitly asks to clarify or refine the spec; OR \`/swarm clarify\` is invoked \u2192 Enter MODE: CLARIFY-SPEC.
|
|
@@ -53922,12 +54302,72 @@ Evaluate the user's request and context in this exact order \u2014 the FIRST mat
|
|
|
53922
54302
|
6. All other modes (CONSULT, PLAN, CRITIC-GATE, EXECUTE, PHASE-WRAP) \u2014 Follow their respective sections below.
|
|
53923
54303
|
|
|
53924
54304
|
PRIORITY RULES:
|
|
53925
|
-
- EXPLICIT COMMAND OVERRIDE (priority 0) wins over everything \u2014 an explicit \`/swarm specify
|
|
54305
|
+
- EXPLICIT COMMAND OVERRIDE (priority 0) wins over everything \u2014 an explicit \`/swarm specify\`, \`/swarm clarify\`, or \`/swarm brainstorm\` command, or explicit spec-creation / brainstorming language ("specify", "write a spec", "create a spec", "define requirements", "define a feature", "brainstorm", "think through with me") always overrides RESUME.
|
|
54306
|
+
- BRAINSTORM is selected via the EXPLICIT COMMAND OVERRIDE when \`/swarm brainstorm\` is invoked or the user asks to "brainstorm" / "think through" / "workshop" a problem before committing to a spec. Use BRAINSTORM when the problem is still fuzzy \u2014 it produces both spec.md and a QA gate profile. Use SPECIFY when requirements are clear enough to write directly.
|
|
53926
54307
|
- RESUME wins over SPECIFY (priority 2) and all other modes when no explicit spec command is present \u2014 a user continuing existing work is never accidentally routed to SPECIFY.
|
|
53927
54308
|
- SPECIFY (priority 2) fires only for new projects with no spec and no plan.
|
|
53928
54309
|
- CLARIFY-SPEC fires between SPECIFY and CLARIFY; it only activates when no explicit spec command is present and no incomplete (unchecked) tasks exist in plan.md \u2014 RESUME takes priority if they do.
|
|
53929
54310
|
- CLARIFY fires only when user input is genuinely needed (not as a substitute for informed defaults).
|
|
53930
54311
|
|
|
54312
|
+
### MODE: BRAINSTORM
|
|
54313
|
+
Activates when: user invokes \`/swarm brainstorm\`; OR uses phrases like "brainstorm", "let's think through", "think this through with me", "workshop this idea"; OR the problem is fuzzy/exploratory and the user has not yet written (or does not want to directly dictate) a spec.
|
|
54314
|
+
|
|
54315
|
+
Use BRAINSTORM when requirements need to be drawn out through structured dialogue before committing to a spec. Use SPECIFY when the user has already articulated clear requirements.
|
|
54316
|
+
|
|
54317
|
+
MODE: BRAINSTORM runs seven phases in strict order. Do not skip phases. Do not collapse phases. Each phase has a clear entry signal and a clear exit signal.
|
|
54318
|
+
|
|
54319
|
+
**Phase 1: CONTEXT SCAN (architect + explorer, parallel).**
|
|
54320
|
+
- Delegate to \`{{AGENT_PREFIX}}explorer\` to map the relevant portion of the codebase. Scope the explorer to the area most likely affected by the topic.
|
|
54321
|
+
- In parallel, read any existing \`.swarm/spec.md\`, \`.swarm/plan.md\`, and \`.swarm/knowledge.jsonl\` entries that are relevant.
|
|
54322
|
+
- Run CODEBASE REALITY CHECK on any claims the user made in their topic statement. Surface discrepancies before moving forward.
|
|
54323
|
+
- Exit when you have a confident map of: (a) existing code and patterns, (b) relevant prior decisions, (c) what is actually unknown.
|
|
54324
|
+
|
|
54325
|
+
**Phase 2: DIALOGUE (architect \u2194 user).**
|
|
54326
|
+
- Ask EXACTLY ONE focused question per message. Wait for the user's answer before asking the next.
|
|
54327
|
+
- Prioritize questions that materially change scope, risk, or architecture. Skip questions whose answers can be responsibly defaulted \u2014 use informed defaults and say so.
|
|
54328
|
+
- Hard cap: no more than SIX questions total in this phase. Stop sooner if uncertainty has collapsed.
|
|
54329
|
+
- Each question must include: (a) why it matters, (b) the default you will use if the user doesn't answer, (c) the concrete options you're weighing.
|
|
54330
|
+
- Exit when: remaining ambiguity can be defaulted safely, or the user explicitly says "good, move on" or equivalent.
|
|
54331
|
+
|
|
54332
|
+
**Phase 3: APPROACHES (architect, optionally with SME).**
|
|
54333
|
+
- Produce 2-4 distinct candidate approaches. Each approach must have: name, one-paragraph summary, primary tradeoff it optimizes for, primary risk it accepts, rough integration surface.
|
|
54334
|
+
- For high-risk domains (auth, payments, data mutation, public API, schema, concurrency, security-sensitive parsing), delegate to \`{{AGENT_PREFIX}}sme\` for domain research first.
|
|
54335
|
+
- Present the approaches to the user and recommend one with explicit reasoning. The user can pick, modify, or reject.
|
|
54336
|
+
- Exit when the user has chosen (or agreed to your recommended) approach.
|
|
54337
|
+
|
|
54338
|
+
**Phase 4: DESIGN SECTIONS (architect).**
|
|
54339
|
+
- Draft the structural design of the chosen approach. Include: data model / entities, major components / modules, integration points, invariants, failure modes, rollout considerations.
|
|
54340
|
+
- Keep design technology-aware (this is NOT the spec \u2014 BRAINSTORM design notes can reference frameworks and patterns).
|
|
54341
|
+
- Name the design sections explicitly so you can reference them in the spec without duplicating.
|
|
54342
|
+
- Exit with a design outline the user can skim in under two minutes.
|
|
54343
|
+
|
|
54344
|
+
**Phase 5: SPEC WRITE + SELF-REVIEW (architect + reviewer).**
|
|
54345
|
+
- Generate \`.swarm/spec.md\` following the same SPEC CONTENT RULES that MODE: SPECIFY uses: WHAT/WHY only, no tech stack, no implementation details, FR-### / SC-### numbering, Given/When/Then scenarios, \`[NEEDS CLARIFICATION]\` markers (max 3).
|
|
54346
|
+
- Cross-reference design sections by name where relevant context helps (but keep HOW out of the spec).
|
|
54347
|
+
- Delegate to \`{{AGENT_PREFIX}}reviewer\` for an independent review of the draft spec. Reviewer must flag: requirements that encode HOW, untestable requirements, missing edge cases, silent assumptions.
|
|
54348
|
+
- Apply reviewer feedback. If reviewer rejects, iterate once and re-review. After two rounds, surface remaining disagreements to the user.
|
|
54349
|
+
- Write the final spec to \`.swarm/spec.md\`.
|
|
54350
|
+
- Exit when reviewer signs off (or user explicitly accepts remaining disagreements).
|
|
54351
|
+
|
|
54352
|
+
**Phase 6: QA GATE SELECTION (architect).**
|
|
54353
|
+
- Read the current QA gate profile for this plan via \`get_qa_gate_profile\`. If none exists, the tool returns \`success: false, reason: 'no_profile'\` \u2014 this is expected for a new plan.
|
|
54354
|
+
- Based on risk tier of the work (see "High-risk work" list in the quality policy), choose which gates to enable. Default profile enables reviewer, test_engineer, sme_enabled, critic_pre_plan, and sast_enabled. Consider enabling council_mode for high-impact architecture and hallucination_guard for claim-heavy work.
|
|
54355
|
+
- Apply the chosen gates via \`set_qa_gates\`. The tool ratchets tighter only \u2014 it cannot disable gates that are already on. It rejects writes once the profile is locked by critic approval.
|
|
54356
|
+
- Briefly explain to the user which gates you selected and why.
|
|
54357
|
+
- Exit with a QA gate profile persisted for this plan.
|
|
54358
|
+
|
|
54359
|
+
**Phase 7: TRANSITION.**
|
|
54360
|
+
- Summarize: (a) chosen approach, (b) design sections produced, (c) spec written, (d) QA gates selected, (e) remaining \`[NEEDS CLARIFICATION]\` markers.
|
|
54361
|
+
- Offer the user two next steps: \`PLAN\` (go to MODE: PLAN and write plan.md) or \`CLARIFY-SPEC\` (resolve remaining markers first).
|
|
54362
|
+
- Do NOT proceed to PLAN or CLARIFY-SPEC automatically \u2014 wait for user direction.
|
|
54363
|
+
|
|
54364
|
+
BRAINSTORM RULES:
|
|
54365
|
+
- No skipping phases. Each phase's exit condition must be met before moving on.
|
|
54366
|
+
- One question per message in DIALOGUE \u2014 never batch.
|
|
54367
|
+
- Always offer an informed default for every question.
|
|
54368
|
+
- The spec produced in Phase 5 must still satisfy the SPEC CONTENT RULES (no tech stack, no implementation details).
|
|
54369
|
+
- QA gates set in Phase 6 are ratchet-tighter \u2014 you cannot undo them later in the session.
|
|
54370
|
+
|
|
53931
54371
|
### MODE: SPECIFY
|
|
53932
54372
|
Activates when: user asks to "specify", "define requirements", "write a spec", or "define a feature"; OR \`/swarm specify\` is invoked; OR no \`.swarm/spec.md\` exists and no \`.swarm/plan.md\` exists.
|
|
53933
54373
|
|
|
@@ -54733,16 +55173,17 @@ ${customAppendPrompt}`;
|
|
|
54733
55173
|
}
|
|
54734
55174
|
prompt = prompt?.replace("{{YOUR_TOOLS}}", buildYourToolsList())?.replace("{{AVAILABLE_TOOLS}}", buildAvailableToolsList())?.replace("{{SLASH_COMMANDS}}", buildSlashCommandsList());
|
|
54735
55175
|
const councilBlock = buildCouncilWorkflow(council);
|
|
55176
|
+
const hasPlaceholder = prompt?.includes("{{COUNCIL_WORKFLOW}}") === true;
|
|
54736
55177
|
if (councilBlock === "") {
|
|
54737
|
-
prompt = prompt?.replace(`
|
|
54738
|
-
|
|
54739
|
-
{{COUNCIL_WORKFLOW}}
|
|
54740
|
-
|
|
54741
|
-
`, `
|
|
55178
|
+
prompt = prompt?.replace(/\n\n\{\{COUNCIL_WORKFLOW\}\}\n\n/g, `
|
|
54742
55179
|
|
|
54743
55180
|
`);
|
|
55181
|
+
} else if (hasPlaceholder) {
|
|
55182
|
+
prompt = prompt?.replace(/\{\{COUNCIL_WORKFLOW\}\}/g, councilBlock);
|
|
54744
55183
|
} else {
|
|
54745
|
-
prompt = prompt
|
|
55184
|
+
prompt = `${prompt ?? ""}
|
|
55185
|
+
|
|
55186
|
+
${councilBlock}`;
|
|
54746
55187
|
}
|
|
54747
55188
|
const advEnabled = adversarialTesting?.enabled ?? true;
|
|
54748
55189
|
const advScope = adversarialTesting?.scope ?? "all";
|
|
@@ -56494,6 +56935,13 @@ function getAgentConfigs(config3, directory, sessionId) {
|
|
|
56494
56935
|
} else {
|
|
56495
56936
|
allowedTools = AGENT_TOOL_MAP[baseAgentName];
|
|
56496
56937
|
}
|
|
56938
|
+
if (baseAgentName === "architect" && config3?.council?.enabled === true && override !== undefined) {
|
|
56939
|
+
const required3 = ["declare_council_criteria", "convene_council"];
|
|
56940
|
+
const missing = required3.filter((t) => !override.includes(t));
|
|
56941
|
+
if (missing.length > 0) {
|
|
56942
|
+
throw new Error(`[opencode-swarm] Conflicting config: council.enabled=true but tool_filter.overrides.architect omits ${missing.join(", ")}. ` + `Either set council.enabled=false, remove the architect override entirely to fall back on AGENT_TOOL_MAP, or add the missing council tools to the override. ` + `Refusing to silently override your explicit tool_filter.overrides.architect.`);
|
|
56943
|
+
}
|
|
56944
|
+
}
|
|
56497
56945
|
if (!allowedTools && !Object.hasOwn(toolFilterOverrides, baseAgentName)) {
|
|
56498
56946
|
if (!warnedMissingWhitelist.has(baseAgentName)) {
|
|
56499
56947
|
console.warn(`[getAgentConfigs] Unknown agent '${baseAgentName}', defaulting to minimal toolset.`);
|
|
@@ -56801,13 +57249,13 @@ class PlanSyncWorker {
|
|
|
56801
57249
|
} catch {}
|
|
56802
57250
|
}
|
|
56803
57251
|
withTimeout(promise3, ms, timeoutMessage) {
|
|
56804
|
-
return new Promise((
|
|
57252
|
+
return new Promise((resolve13, reject) => {
|
|
56805
57253
|
const timer = setTimeout(() => {
|
|
56806
57254
|
reject(new Error(`${timeoutMessage} (${ms}ms)`));
|
|
56807
57255
|
}, ms);
|
|
56808
57256
|
promise3.then((result) => {
|
|
56809
57257
|
clearTimeout(timer);
|
|
56810
|
-
|
|
57258
|
+
resolve13(result);
|
|
56811
57259
|
}).catch((error93) => {
|
|
56812
57260
|
clearTimeout(timer);
|
|
56813
57261
|
reject(error93);
|
|
@@ -57034,7 +57482,7 @@ ${content.substring(endIndex + 1)}`;
|
|
|
57034
57482
|
// src/hooks/compaction-customizer.ts
|
|
57035
57483
|
init_manager();
|
|
57036
57484
|
import * as fs27 from "fs";
|
|
57037
|
-
import { join as
|
|
57485
|
+
import { join as join36 } from "path";
|
|
57038
57486
|
init_utils2();
|
|
57039
57487
|
function createCompactionCustomizerHook(config3, directory) {
|
|
57040
57488
|
const enabled = config3.hooks?.compaction !== false;
|
|
@@ -57080,7 +57528,7 @@ function createCompactionCustomizerHook(config3, directory) {
|
|
|
57080
57528
|
}
|
|
57081
57529
|
}
|
|
57082
57530
|
try {
|
|
57083
|
-
const summariesDir =
|
|
57531
|
+
const summariesDir = join36(directory, ".swarm", "summaries");
|
|
57084
57532
|
const files = await fs27.promises.readdir(summariesDir);
|
|
57085
57533
|
if (files.length > 0) {
|
|
57086
57534
|
const count = files.length;
|
|
@@ -60993,7 +61441,7 @@ import * as path47 from "path";
|
|
|
60993
61441
|
init_utils2();
|
|
60994
61442
|
init_path_security();
|
|
60995
61443
|
import * as fsSync2 from "fs";
|
|
60996
|
-
import { constants as constants2, existsSync as
|
|
61444
|
+
import { constants as constants2, existsSync as existsSync28, realpathSync as realpathSync6 } from "fs";
|
|
60997
61445
|
import * as fsPromises3 from "fs/promises";
|
|
60998
61446
|
import * as path46 from "path";
|
|
60999
61447
|
|
|
@@ -61455,7 +61903,7 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
|
|
|
61455
61903
|
} catch {
|
|
61456
61904
|
realRoot = path46.normalize(workspaceRoot);
|
|
61457
61905
|
}
|
|
61458
|
-
if (!
|
|
61906
|
+
if (!existsSync28(resolved)) {
|
|
61459
61907
|
const EXTENSIONS = [
|
|
61460
61908
|
".ts",
|
|
61461
61909
|
".tsx",
|
|
@@ -61469,7 +61917,7 @@ function resolveModuleSpecifier(workspaceRoot, sourceFile, specifier) {
|
|
|
61469
61917
|
let found = null;
|
|
61470
61918
|
for (const ext of EXTENSIONS) {
|
|
61471
61919
|
const candidate = resolved + ext;
|
|
61472
|
-
if (
|
|
61920
|
+
if (existsSync28(candidate)) {
|
|
61473
61921
|
found = candidate;
|
|
61474
61922
|
break;
|
|
61475
61923
|
}
|
|
@@ -61566,7 +62014,7 @@ async function loadGraph(workspace) {
|
|
|
61566
62014
|
if (cached3 && !isDirty(normalized)) {
|
|
61567
62015
|
try {
|
|
61568
62016
|
const graphPath = getGraphPath(workspace);
|
|
61569
|
-
if (
|
|
62017
|
+
if (existsSync28(graphPath)) {
|
|
61570
62018
|
const stats = await fsPromises3.stat(graphPath);
|
|
61571
62019
|
const cachedMtime = mtimeCache.get(normalized);
|
|
61572
62020
|
if (cachedMtime !== undefined && stats.mtimeMs !== cachedMtime) {
|
|
@@ -61583,7 +62031,7 @@ async function loadGraph(workspace) {
|
|
|
61583
62031
|
}
|
|
61584
62032
|
try {
|
|
61585
62033
|
const graphPath = getGraphPath(workspace);
|
|
61586
|
-
if (!
|
|
62034
|
+
if (!existsSync28(graphPath)) {
|
|
61587
62035
|
return null;
|
|
61588
62036
|
}
|
|
61589
62037
|
const stats = await fsPromises3.stat(graphPath);
|
|
@@ -61709,7 +62157,7 @@ async function saveGraph(workspace, graph, options) {
|
|
|
61709
62157
|
lastError = error93 instanceof Error ? error93 : new Error(String(error93));
|
|
61710
62158
|
if (lastError instanceof Error && "code" in lastError && lastError.code === "EEXIST" && retries < WINDOWS_RENAME_MAX_RETRIES - 1) {
|
|
61711
62159
|
retries++;
|
|
61712
|
-
await new Promise((
|
|
62160
|
+
await new Promise((resolve18) => setTimeout(resolve18, WINDOWS_RENAME_RETRY_DELAY_MS));
|
|
61713
62161
|
continue;
|
|
61714
62162
|
}
|
|
61715
62163
|
throw lastError;
|
|
@@ -61842,7 +62290,7 @@ function buildWorkspaceGraph(workspaceRoot, options) {
|
|
|
61842
62290
|
const maxFileSize = options?.maxFileSizeBytes ?? 1024 * 1024;
|
|
61843
62291
|
const maxFiles = options?.maxFiles ?? 1e4;
|
|
61844
62292
|
const absoluteRoot = path46.resolve(workspaceRoot);
|
|
61845
|
-
if (!
|
|
62293
|
+
if (!existsSync28(absoluteRoot)) {
|
|
61846
62294
|
throw new Error(`Workspace directory does not exist: ${workspaceRoot}`);
|
|
61847
62295
|
}
|
|
61848
62296
|
const graph = createEmptyGraph(workspaceRoot);
|
|
@@ -61996,7 +62444,7 @@ async function updateGraphForFiles(workspaceRoot, filePaths, options) {
|
|
|
61996
62444
|
const updatedPaths = new Set;
|
|
61997
62445
|
for (const rawFilePath of filePaths) {
|
|
61998
62446
|
const normalizedPath = normalizeGraphPath(rawFilePath);
|
|
61999
|
-
const fileExists =
|
|
62447
|
+
const fileExists = existsSync28(rawFilePath);
|
|
62000
62448
|
if (fileExists) {
|
|
62001
62449
|
graph.edges = graph.edges.filter((e) => normalizeGraphPath(e.source) !== normalizedPath);
|
|
62002
62450
|
const result = scanFile(rawFilePath, absoluteRoot, maxFileSize);
|
|
@@ -62849,26 +63297,26 @@ function pLimit(concurrency) {
|
|
|
62849
63297
|
activeCount--;
|
|
62850
63298
|
resumeNext();
|
|
62851
63299
|
};
|
|
62852
|
-
const run2 = async (function_,
|
|
63300
|
+
const run2 = async (function_, resolve19, arguments_2) => {
|
|
62853
63301
|
const result = (async () => function_(...arguments_2))();
|
|
62854
|
-
|
|
63302
|
+
resolve19(result);
|
|
62855
63303
|
try {
|
|
62856
63304
|
await result;
|
|
62857
63305
|
} catch {}
|
|
62858
63306
|
next();
|
|
62859
63307
|
};
|
|
62860
|
-
const enqueue = (function_,
|
|
63308
|
+
const enqueue = (function_, resolve19, reject, arguments_2) => {
|
|
62861
63309
|
const queueItem = { reject };
|
|
62862
63310
|
new Promise((internalResolve) => {
|
|
62863
63311
|
queueItem.run = internalResolve;
|
|
62864
63312
|
queue.enqueue(queueItem);
|
|
62865
|
-
}).then(run2.bind(undefined, function_,
|
|
63313
|
+
}).then(run2.bind(undefined, function_, resolve19, arguments_2));
|
|
62866
63314
|
if (activeCount < concurrency) {
|
|
62867
63315
|
resumeNext();
|
|
62868
63316
|
}
|
|
62869
63317
|
};
|
|
62870
|
-
const generator = (function_, ...arguments_2) => new Promise((
|
|
62871
|
-
enqueue(function_,
|
|
63318
|
+
const generator = (function_, ...arguments_2) => new Promise((resolve19, reject) => {
|
|
63319
|
+
enqueue(function_, resolve19, reject, arguments_2);
|
|
62872
63320
|
});
|
|
62873
63321
|
Object.defineProperties(generator, {
|
|
62874
63322
|
activeCount: {
|
|
@@ -65597,7 +66045,7 @@ import * as path56 from "path";
|
|
|
65597
66045
|
import * as child_process5 from "child_process";
|
|
65598
66046
|
var WIN32_CMD_BINARIES = new Set(["npm", "npx", "pnpm", "yarn"]);
|
|
65599
66047
|
function spawnAsync(command, cwd, timeoutMs) {
|
|
65600
|
-
return new Promise((
|
|
66048
|
+
return new Promise((resolve22) => {
|
|
65601
66049
|
try {
|
|
65602
66050
|
const [rawCmd, ...args2] = command;
|
|
65603
66051
|
const cmd = process.platform === "win32" && WIN32_CMD_BINARIES.has(rawCmd) && !rawCmd.includes(".") ? `${rawCmd}.cmd` : rawCmd;
|
|
@@ -65644,24 +66092,24 @@ function spawnAsync(command, cwd, timeoutMs) {
|
|
|
65644
66092
|
try {
|
|
65645
66093
|
proc.kill();
|
|
65646
66094
|
} catch {}
|
|
65647
|
-
|
|
66095
|
+
resolve22(null);
|
|
65648
66096
|
}, timeoutMs);
|
|
65649
66097
|
proc.on("close", (code) => {
|
|
65650
66098
|
if (done)
|
|
65651
66099
|
return;
|
|
65652
66100
|
done = true;
|
|
65653
66101
|
clearTimeout(timer);
|
|
65654
|
-
|
|
66102
|
+
resolve22({ exitCode: code ?? 1, stdout, stderr });
|
|
65655
66103
|
});
|
|
65656
66104
|
proc.on("error", () => {
|
|
65657
66105
|
if (done)
|
|
65658
66106
|
return;
|
|
65659
66107
|
done = true;
|
|
65660
66108
|
clearTimeout(timer);
|
|
65661
|
-
|
|
66109
|
+
resolve22(null);
|
|
65662
66110
|
});
|
|
65663
66111
|
} catch {
|
|
65664
|
-
|
|
66112
|
+
resolve22(null);
|
|
65665
66113
|
}
|
|
65666
66114
|
});
|
|
65667
66115
|
}
|
|
@@ -68373,12 +68821,12 @@ ${body2}`);
|
|
|
68373
68821
|
// src/council/council-evidence-writer.ts
|
|
68374
68822
|
import {
|
|
68375
68823
|
appendFileSync as appendFileSync7,
|
|
68376
|
-
existsSync as
|
|
68377
|
-
mkdirSync as
|
|
68824
|
+
existsSync as existsSync37,
|
|
68825
|
+
mkdirSync as mkdirSync17,
|
|
68378
68826
|
readFileSync as readFileSync35,
|
|
68379
68827
|
writeFileSync as writeFileSync11
|
|
68380
68828
|
} from "fs";
|
|
68381
|
-
import { join as
|
|
68829
|
+
import { join as join60 } from "path";
|
|
68382
68830
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
68383
68831
|
var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
|
|
68384
68832
|
var COUNCIL_GATE_NAME = "council";
|
|
@@ -68412,11 +68860,11 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
68412
68860
|
if (!VALID_TASK_ID.test(synthesis.taskId)) {
|
|
68413
68861
|
throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" \u2014 must match N.M or N.M.P format`);
|
|
68414
68862
|
}
|
|
68415
|
-
const dir =
|
|
68416
|
-
|
|
68417
|
-
const filePath =
|
|
68863
|
+
const dir = join60(workingDir, EVIDENCE_DIR2);
|
|
68864
|
+
mkdirSync17(dir, { recursive: true });
|
|
68865
|
+
const filePath = join60(dir, `${synthesis.taskId}.json`);
|
|
68418
68866
|
const existingRoot = Object.create(null);
|
|
68419
|
-
if (
|
|
68867
|
+
if (existsSync37(filePath)) {
|
|
68420
68868
|
try {
|
|
68421
68869
|
const parsed = JSON.parse(readFileSync35(filePath, "utf-8"));
|
|
68422
68870
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -68443,15 +68891,15 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
68443
68891
|
updated.gates = mergedGates;
|
|
68444
68892
|
writeFileSync11(filePath, JSON.stringify(updated, null, 2));
|
|
68445
68893
|
try {
|
|
68446
|
-
const councilDir =
|
|
68447
|
-
|
|
68894
|
+
const councilDir = join60(workingDir, ".swarm", "council");
|
|
68895
|
+
mkdirSync17(councilDir, { recursive: true });
|
|
68448
68896
|
const auditLine = JSON.stringify({
|
|
68449
68897
|
round: synthesis.roundNumber,
|
|
68450
68898
|
verdict: synthesis.overallVerdict,
|
|
68451
68899
|
timestamp: synthesis.timestamp,
|
|
68452
68900
|
vetoedBy: synthesis.vetoedBy
|
|
68453
68901
|
});
|
|
68454
|
-
appendFileSync7(
|
|
68902
|
+
appendFileSync7(join60(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
|
|
68455
68903
|
`);
|
|
68456
68904
|
} catch (auditError) {
|
|
68457
68905
|
console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
|
|
@@ -68580,22 +69028,22 @@ function buildUnifiedFeedback(taskId, verdict, vetoedBy, requiredFixes, advisory
|
|
|
68580
69028
|
}
|
|
68581
69029
|
|
|
68582
69030
|
// src/council/criteria-store.ts
|
|
68583
|
-
import { existsSync as
|
|
68584
|
-
import { join as
|
|
69031
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as readFileSync36, writeFileSync as writeFileSync12 } from "fs";
|
|
69032
|
+
import { join as join61 } from "path";
|
|
68585
69033
|
var COUNCIL_DIR = ".swarm/council";
|
|
68586
69034
|
function writeCriteria(workingDir, taskId, criteria) {
|
|
68587
|
-
const dir =
|
|
68588
|
-
|
|
69035
|
+
const dir = join61(workingDir, COUNCIL_DIR);
|
|
69036
|
+
mkdirSync18(dir, { recursive: true });
|
|
68589
69037
|
const payload = {
|
|
68590
69038
|
taskId,
|
|
68591
69039
|
criteria,
|
|
68592
69040
|
declaredAt: new Date().toISOString()
|
|
68593
69041
|
};
|
|
68594
|
-
writeFileSync12(
|
|
69042
|
+
writeFileSync12(join61(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
|
|
68595
69043
|
}
|
|
68596
69044
|
function readCriteria(workingDir, taskId) {
|
|
68597
|
-
const filePath =
|
|
68598
|
-
if (!
|
|
69045
|
+
const filePath = join61(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
|
|
69046
|
+
if (!existsSync38(filePath))
|
|
68599
69047
|
return null;
|
|
68600
69048
|
try {
|
|
68601
69049
|
const parsed = JSON.parse(readFileSync36(filePath, "utf-8"));
|
|
@@ -70163,7 +70611,7 @@ function summarizePlan(plan) {
|
|
|
70163
70611
|
}))
|
|
70164
70612
|
};
|
|
70165
70613
|
}
|
|
70166
|
-
function
|
|
70614
|
+
function derivePlanId2(plan) {
|
|
70167
70615
|
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
70168
70616
|
}
|
|
70169
70617
|
async function executeGetApprovedPlan(args2, directory) {
|
|
@@ -70184,7 +70632,9 @@ async function executeGetApprovedPlan(args2, directory) {
|
|
|
70184
70632
|
reason: "no_approved_snapshot"
|
|
70185
70633
|
};
|
|
70186
70634
|
}
|
|
70187
|
-
const expectedPlanId =
|
|
70635
|
+
const expectedPlanId = derivePlanId2(currentPlan);
|
|
70636
|
+
const profile = getProfile(directory, expectedPlanId);
|
|
70637
|
+
const qaProfileHash = profile ? computeProfileHash(profile) : null;
|
|
70188
70638
|
const approved = await loadLastApprovedPlan(directory, expectedPlanId);
|
|
70189
70639
|
if (!approved) {
|
|
70190
70640
|
const unscopedSnapshot = await loadLastApprovedPlan(directory);
|
|
@@ -70194,12 +70644,14 @@ async function executeGetApprovedPlan(args2, directory) {
|
|
|
70194
70644
|
approved_plan: undefined,
|
|
70195
70645
|
current_plan: null,
|
|
70196
70646
|
drift_detected: true,
|
|
70197
|
-
current_plan_error: "Plan identity (swarm/title) was mutated after approval \u2014 " + `expected plan_id '${expectedPlanId}' but approved snapshot has a different identity. ` + "This is a form of plan tampering."
|
|
70647
|
+
current_plan_error: "Plan identity (swarm/title) was mutated after approval \u2014 " + `expected plan_id '${expectedPlanId}' but approved snapshot has a different identity. ` + "This is a form of plan tampering.",
|
|
70648
|
+
qa_profile_hash: qaProfileHash
|
|
70198
70649
|
};
|
|
70199
70650
|
}
|
|
70200
70651
|
return {
|
|
70201
70652
|
success: false,
|
|
70202
|
-
reason: "no_approved_snapshot"
|
|
70653
|
+
reason: "no_approved_snapshot",
|
|
70654
|
+
qa_profile_hash: qaProfileHash
|
|
70203
70655
|
};
|
|
70204
70656
|
}
|
|
70205
70657
|
const summaryOnly = args2.summary_only === true;
|
|
@@ -70220,7 +70672,8 @@ async function executeGetApprovedPlan(args2, directory) {
|
|
|
70220
70672
|
success: true,
|
|
70221
70673
|
approved_plan: approvedPayload,
|
|
70222
70674
|
current_plan: currentPayload,
|
|
70223
|
-
drift_detected: driftDetected
|
|
70675
|
+
drift_detected: driftDetected,
|
|
70676
|
+
qa_profile_hash: qaProfileHash
|
|
70224
70677
|
};
|
|
70225
70678
|
}
|
|
70226
70679
|
var get_approved_plan = createSwarmTool({
|
|
@@ -70233,13 +70686,58 @@ var get_approved_plan = createSwarmTool({
|
|
|
70233
70686
|
return JSON.stringify(await executeGetApprovedPlan(typedArgs, directory), null, 2);
|
|
70234
70687
|
}
|
|
70235
70688
|
});
|
|
70689
|
+
// src/tools/get-qa-gate-profile.ts
|
|
70690
|
+
init_manager();
|
|
70691
|
+
init_create_tool();
|
|
70692
|
+
function derivePlanId3(plan) {
|
|
70693
|
+
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
70694
|
+
}
|
|
70695
|
+
async function executeGetQaGateProfile(_args, directory) {
|
|
70696
|
+
const plan = await loadPlanJsonOnly(directory);
|
|
70697
|
+
if (!plan) {
|
|
70698
|
+
return {
|
|
70699
|
+
success: false,
|
|
70700
|
+
reason: "plan_json_unavailable"
|
|
70701
|
+
};
|
|
70702
|
+
}
|
|
70703
|
+
const planId = derivePlanId3(plan);
|
|
70704
|
+
const profile = getProfile(directory, planId);
|
|
70705
|
+
if (!profile) {
|
|
70706
|
+
return {
|
|
70707
|
+
success: false,
|
|
70708
|
+
reason: "no_profile",
|
|
70709
|
+
plan_id: planId
|
|
70710
|
+
};
|
|
70711
|
+
}
|
|
70712
|
+
return {
|
|
70713
|
+
success: true,
|
|
70714
|
+
plan_id: planId,
|
|
70715
|
+
profile: {
|
|
70716
|
+
plan_id: profile.plan_id,
|
|
70717
|
+
project_type: profile.project_type,
|
|
70718
|
+
gates: { ...profile.gates },
|
|
70719
|
+
locked_at: profile.locked_at,
|
|
70720
|
+
locked_by_snapshot_seq: profile.locked_by_snapshot_seq,
|
|
70721
|
+
created_at: profile.created_at,
|
|
70722
|
+
profile_hash: computeProfileHash(profile)
|
|
70723
|
+
}
|
|
70724
|
+
};
|
|
70725
|
+
}
|
|
70726
|
+
var get_qa_gate_profile = createSwarmTool({
|
|
70727
|
+
description: "Retrieve the QA gate profile for the current plan. Returns the spec-level " + "gates, lock state, and a SHA-256 profile hash. Read-only \u2014 does not " + "create a profile if none exists. plan_id is derived automatically from " + "plan.json (swarm + title).",
|
|
70728
|
+
args: {},
|
|
70729
|
+
execute: async (args2, directory) => {
|
|
70730
|
+
const typedArgs = args2 ?? {};
|
|
70731
|
+
return JSON.stringify(await executeGetQaGateProfile(typedArgs, directory), null, 2);
|
|
70732
|
+
}
|
|
70733
|
+
});
|
|
70236
70734
|
// src/tools/gitingest.ts
|
|
70237
70735
|
init_dist();
|
|
70238
70736
|
init_create_tool();
|
|
70239
70737
|
var GITINGEST_TIMEOUT_MS = 1e4;
|
|
70240
70738
|
var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
|
|
70241
70739
|
var GITINGEST_MAX_RETRIES = 2;
|
|
70242
|
-
var delay = (ms) => new Promise((
|
|
70740
|
+
var delay = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
|
|
70243
70741
|
async function fetchGitingest(args2) {
|
|
70244
70742
|
for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
|
|
70245
70743
|
try {
|
|
@@ -70816,7 +71314,7 @@ init_dist();
|
|
|
70816
71314
|
init_config();
|
|
70817
71315
|
init_knowledge_store();
|
|
70818
71316
|
init_create_tool();
|
|
70819
|
-
import { existsSync as
|
|
71317
|
+
import { existsSync as existsSync43 } from "fs";
|
|
70820
71318
|
var DEFAULT_LIMIT = 10;
|
|
70821
71319
|
var MAX_LESSON_LENGTH = 200;
|
|
70822
71320
|
var VALID_CATEGORIES3 = [
|
|
@@ -70885,14 +71383,14 @@ function validateLimit(limit) {
|
|
|
70885
71383
|
}
|
|
70886
71384
|
async function readSwarmKnowledge(directory) {
|
|
70887
71385
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
70888
|
-
if (!
|
|
71386
|
+
if (!existsSync43(swarmPath)) {
|
|
70889
71387
|
return [];
|
|
70890
71388
|
}
|
|
70891
71389
|
return readKnowledge(swarmPath);
|
|
70892
71390
|
}
|
|
70893
71391
|
async function readHiveKnowledge() {
|
|
70894
71392
|
const hivePath = resolveHiveKnowledgePath();
|
|
70895
|
-
if (!
|
|
71393
|
+
if (!existsSync43(hivePath)) {
|
|
70896
71394
|
return [];
|
|
70897
71395
|
}
|
|
70898
71396
|
return readKnowledge(hivePath);
|
|
@@ -71849,7 +72347,7 @@ async function runNpmAudit(directory) {
|
|
|
71849
72347
|
stderr: "pipe",
|
|
71850
72348
|
cwd: directory
|
|
71851
72349
|
});
|
|
71852
|
-
const timeoutPromise = new Promise((
|
|
72350
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
71853
72351
|
const result = await Promise.race([
|
|
71854
72352
|
Promise.all([
|
|
71855
72353
|
new Response(proc.stdout).text(),
|
|
@@ -71972,7 +72470,7 @@ async function runPipAudit(directory) {
|
|
|
71972
72470
|
stderr: "pipe",
|
|
71973
72471
|
cwd: directory
|
|
71974
72472
|
});
|
|
71975
|
-
const timeoutPromise = new Promise((
|
|
72473
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
71976
72474
|
const result = await Promise.race([
|
|
71977
72475
|
Promise.all([
|
|
71978
72476
|
new Response(proc.stdout).text(),
|
|
@@ -72103,7 +72601,7 @@ async function runCargoAudit(directory) {
|
|
|
72103
72601
|
stderr: "pipe",
|
|
72104
72602
|
cwd: directory
|
|
72105
72603
|
});
|
|
72106
|
-
const timeoutPromise = new Promise((
|
|
72604
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72107
72605
|
const result = await Promise.race([
|
|
72108
72606
|
Promise.all([
|
|
72109
72607
|
new Response(proc.stdout).text(),
|
|
@@ -72230,7 +72728,7 @@ async function runGoAudit(directory) {
|
|
|
72230
72728
|
stderr: "pipe",
|
|
72231
72729
|
cwd: directory
|
|
72232
72730
|
});
|
|
72233
|
-
const timeoutPromise = new Promise((
|
|
72731
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72234
72732
|
const result = await Promise.race([
|
|
72235
72733
|
Promise.all([
|
|
72236
72734
|
new Response(proc.stdout).text(),
|
|
@@ -72366,7 +72864,7 @@ async function runDotnetAudit(directory) {
|
|
|
72366
72864
|
stderr: "pipe",
|
|
72367
72865
|
cwd: directory
|
|
72368
72866
|
});
|
|
72369
|
-
const timeoutPromise = new Promise((
|
|
72867
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72370
72868
|
const result = await Promise.race([
|
|
72371
72869
|
Promise.all([
|
|
72372
72870
|
new Response(proc.stdout).text(),
|
|
@@ -72485,7 +72983,7 @@ async function runBundleAudit(directory) {
|
|
|
72485
72983
|
stderr: "pipe",
|
|
72486
72984
|
cwd: directory
|
|
72487
72985
|
});
|
|
72488
|
-
const timeoutPromise = new Promise((
|
|
72986
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72489
72987
|
const result = await Promise.race([
|
|
72490
72988
|
Promise.all([
|
|
72491
72989
|
new Response(proc.stdout).text(),
|
|
@@ -72633,7 +73131,7 @@ async function runDartAudit(directory) {
|
|
|
72633
73131
|
stderr: "pipe",
|
|
72634
73132
|
cwd: directory
|
|
72635
73133
|
});
|
|
72636
|
-
const timeoutPromise = new Promise((
|
|
73134
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72637
73135
|
const result = await Promise.race([
|
|
72638
73136
|
Promise.all([
|
|
72639
73137
|
new Response(proc.stdout).text(),
|
|
@@ -72751,7 +73249,7 @@ async function runComposerAudit(directory) {
|
|
|
72751
73249
|
stderr: "pipe",
|
|
72752
73250
|
cwd: directory
|
|
72753
73251
|
});
|
|
72754
|
-
const timeoutPromise = new Promise((
|
|
73252
|
+
const timeoutPromise = new Promise((resolve29) => setTimeout(() => resolve29("timeout"), AUDIT_TIMEOUT_MS));
|
|
72755
73253
|
const result = await Promise.race([
|
|
72756
73254
|
Promise.all([
|
|
72757
73255
|
new Response(proc.stdout).text(),
|
|
@@ -74393,7 +74891,7 @@ function mapSemgrepSeverity(severity) {
|
|
|
74393
74891
|
}
|
|
74394
74892
|
}
|
|
74395
74893
|
async function executeWithTimeout(command, args2, options) {
|
|
74396
|
-
return new Promise((
|
|
74894
|
+
return new Promise((resolve30) => {
|
|
74397
74895
|
const child = child_process7.spawn(command, args2, {
|
|
74398
74896
|
shell: false,
|
|
74399
74897
|
cwd: options.cwd
|
|
@@ -74402,7 +74900,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
74402
74900
|
let stderr = "";
|
|
74403
74901
|
const timeout = setTimeout(() => {
|
|
74404
74902
|
child.kill("SIGTERM");
|
|
74405
|
-
|
|
74903
|
+
resolve30({
|
|
74406
74904
|
stdout,
|
|
74407
74905
|
stderr: "Process timed out",
|
|
74408
74906
|
exitCode: 124
|
|
@@ -74416,7 +74914,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
74416
74914
|
});
|
|
74417
74915
|
child.on("close", (code) => {
|
|
74418
74916
|
clearTimeout(timeout);
|
|
74419
|
-
|
|
74917
|
+
resolve30({
|
|
74420
74918
|
stdout,
|
|
74421
74919
|
stderr,
|
|
74422
74920
|
exitCode: code ?? 0
|
|
@@ -74424,7 +74922,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
74424
74922
|
});
|
|
74425
74923
|
child.on("error", (err2) => {
|
|
74426
74924
|
clearTimeout(timeout);
|
|
74427
|
-
|
|
74925
|
+
resolve30({
|
|
74428
74926
|
stdout,
|
|
74429
74927
|
stderr: err2.message,
|
|
74430
74928
|
exitCode: 1
|
|
@@ -77868,7 +78366,7 @@ async function ripgrepSearch(opts) {
|
|
|
77868
78366
|
stderr: "pipe",
|
|
77869
78367
|
cwd: opts.workspace
|
|
77870
78368
|
});
|
|
77871
|
-
const timeout = new Promise((
|
|
78369
|
+
const timeout = new Promise((resolve35) => setTimeout(() => resolve35("timeout"), REGEX_TIMEOUT_MS));
|
|
77872
78370
|
const exitPromise = proc.exited;
|
|
77873
78371
|
const result = await Promise.race([exitPromise, timeout]);
|
|
77874
78372
|
if (result === "timeout") {
|
|
@@ -78152,6 +78650,84 @@ var search = createSwarmTool({
|
|
|
78152
78650
|
// src/tools/index.ts
|
|
78153
78651
|
init_secretscan();
|
|
78154
78652
|
|
|
78653
|
+
// src/tools/set-qa-gates.ts
|
|
78654
|
+
init_dist();
|
|
78655
|
+
init_manager();
|
|
78656
|
+
init_create_tool();
|
|
78657
|
+
function derivePlanId4(plan) {
|
|
78658
|
+
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
78659
|
+
}
|
|
78660
|
+
async function executeSetQaGates(args2, directory) {
|
|
78661
|
+
const plan = await loadPlanJsonOnly(directory);
|
|
78662
|
+
if (!plan) {
|
|
78663
|
+
return {
|
|
78664
|
+
success: false,
|
|
78665
|
+
reason: "plan_json_unavailable",
|
|
78666
|
+
message: "Cannot configure QA gates: plan.json is missing or invalid. " + "Create a plan first (e.g. via /swarm specify or save_plan)."
|
|
78667
|
+
};
|
|
78668
|
+
}
|
|
78669
|
+
const planId = derivePlanId4(plan);
|
|
78670
|
+
getOrCreateProfile(directory, planId, args2.project_type);
|
|
78671
|
+
const partial3 = {};
|
|
78672
|
+
for (const key of [
|
|
78673
|
+
"reviewer",
|
|
78674
|
+
"test_engineer",
|
|
78675
|
+
"council_mode",
|
|
78676
|
+
"sme_enabled",
|
|
78677
|
+
"critic_pre_plan",
|
|
78678
|
+
"hallucination_guard",
|
|
78679
|
+
"sast_enabled"
|
|
78680
|
+
]) {
|
|
78681
|
+
if (args2[key] !== undefined)
|
|
78682
|
+
partial3[key] = args2[key];
|
|
78683
|
+
}
|
|
78684
|
+
try {
|
|
78685
|
+
const updated = setGates(directory, planId, partial3);
|
|
78686
|
+
return {
|
|
78687
|
+
success: true,
|
|
78688
|
+
plan_id: planId,
|
|
78689
|
+
message: `QA gates updated for plan_id=${planId}`,
|
|
78690
|
+
profile: {
|
|
78691
|
+
plan_id: updated.plan_id,
|
|
78692
|
+
gates: { ...updated.gates },
|
|
78693
|
+
locked_at: updated.locked_at,
|
|
78694
|
+
locked_by_snapshot_seq: updated.locked_by_snapshot_seq,
|
|
78695
|
+
profile_hash: computeProfileHash(updated)
|
|
78696
|
+
}
|
|
78697
|
+
};
|
|
78698
|
+
} catch (err3) {
|
|
78699
|
+
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
78700
|
+
const lower = msg.toLowerCase();
|
|
78701
|
+
let reason = "set_gates_failed";
|
|
78702
|
+
if (lower.includes("locked"))
|
|
78703
|
+
reason = "profile_locked";
|
|
78704
|
+
else if (lower.includes("ratchet"))
|
|
78705
|
+
reason = "ratchet_violation";
|
|
78706
|
+
return {
|
|
78707
|
+
success: false,
|
|
78708
|
+
reason,
|
|
78709
|
+
message: msg,
|
|
78710
|
+
plan_id: planId
|
|
78711
|
+
};
|
|
78712
|
+
}
|
|
78713
|
+
}
|
|
78714
|
+
var set_qa_gates = createSwarmTool({
|
|
78715
|
+
description: "Configure the QA gate profile for the current plan. Architect-only. " + "Ratchet-tighter: can enable additional gates but cannot disable gates " + "that are already enabled. Rejects all writes once the profile is " + "locked (after critic approval). Creates the profile with defaults if " + "none exists. plan_id is derived automatically from plan.json.",
|
|
78716
|
+
args: {
|
|
78717
|
+
reviewer: tool.schema.boolean().optional().describe("Enable the reviewer gate (true) \u2014 cannot be disabled."),
|
|
78718
|
+
test_engineer: tool.schema.boolean().optional().describe("Enable the test_engineer gate (true) \u2014 cannot be disabled once on."),
|
|
78719
|
+
council_mode: tool.schema.boolean().optional().describe("Enable council mode (multi-SME consensus on high-risk phases)."),
|
|
78720
|
+
sme_enabled: tool.schema.boolean().optional().describe("Enable SME consultation."),
|
|
78721
|
+
critic_pre_plan: tool.schema.boolean().optional().describe("Enable critic_pre_plan review before plan approval."),
|
|
78722
|
+
hallucination_guard: tool.schema.boolean().optional().describe("Enable hallucination_guard checks on plan and implementation claims."),
|
|
78723
|
+
sast_enabled: tool.schema.boolean().optional().describe("Enable SAST scanning as a required QA gate."),
|
|
78724
|
+
project_type: tool.schema.string().optional().describe('Project type label (e.g. "ts", "python"). Only applied when the profile is being created for the first time.')
|
|
78725
|
+
},
|
|
78726
|
+
execute: async (args2, directory) => {
|
|
78727
|
+
const typedArgs = args2 ?? {};
|
|
78728
|
+
return JSON.stringify(await executeSetQaGates(typedArgs, directory), null, 2);
|
|
78729
|
+
}
|
|
78730
|
+
});
|
|
78155
78731
|
// src/tools/suggest-patch.ts
|
|
78156
78732
|
init_tool();
|
|
78157
78733
|
init_path_security();
|
|
@@ -79659,12 +80235,15 @@ var update_task_status = createSwarmTool({
|
|
|
79659
80235
|
});
|
|
79660
80236
|
// src/tools/write-drift-evidence.ts
|
|
79661
80237
|
init_tool();
|
|
80238
|
+
import fs71 from "fs";
|
|
80239
|
+
import path87 from "path";
|
|
79662
80240
|
init_utils2();
|
|
79663
80241
|
init_ledger();
|
|
79664
80242
|
init_manager();
|
|
79665
80243
|
init_create_tool();
|
|
79666
|
-
|
|
79667
|
-
|
|
80244
|
+
function derivePlanId5(plan) {
|
|
80245
|
+
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
80246
|
+
}
|
|
79668
80247
|
function normalizeVerdict(verdict) {
|
|
79669
80248
|
switch (verdict) {
|
|
79670
80249
|
case "APPROVED":
|
|
@@ -79731,6 +80310,8 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
79731
80310
|
await fs71.promises.rename(tempPath, validatedPath);
|
|
79732
80311
|
let snapshotInfo;
|
|
79733
80312
|
let snapshotError;
|
|
80313
|
+
let qaProfileLocked;
|
|
80314
|
+
let qaProfileLockError;
|
|
79734
80315
|
if (normalizedVerdict === "approved") {
|
|
79735
80316
|
try {
|
|
79736
80317
|
const currentPlan = await loadPlanJsonOnly(directory);
|
|
@@ -79748,6 +80329,21 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
79748
80329
|
seq: snapshotEvent.seq,
|
|
79749
80330
|
timestamp: snapshotEvent.timestamp
|
|
79750
80331
|
};
|
|
80332
|
+
try {
|
|
80333
|
+
const planId = derivePlanId5(currentPlan);
|
|
80334
|
+
const locked = lockProfile(directory, planId, snapshotEvent.seq);
|
|
80335
|
+
qaProfileLocked = {
|
|
80336
|
+
plan_id: planId,
|
|
80337
|
+
locked_at: locked.locked_at ?? "",
|
|
80338
|
+
locked_by_snapshot_seq: locked.locked_by_snapshot_seq ?? -1
|
|
80339
|
+
};
|
|
80340
|
+
} catch (lockErr) {
|
|
80341
|
+
const msg = lockErr instanceof Error ? lockErr.message : String(lockErr);
|
|
80342
|
+
if (!/No QA gate profile/i.test(msg)) {
|
|
80343
|
+
qaProfileLockError = msg;
|
|
80344
|
+
console.warn("[write_drift_evidence] QA gate profile lock failed:", msg);
|
|
80345
|
+
}
|
|
80346
|
+
}
|
|
79751
80347
|
} else {
|
|
79752
80348
|
snapshotError = "plan.json not available for snapshot";
|
|
79753
80349
|
}
|
|
@@ -79762,7 +80358,9 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
79762
80358
|
verdict: normalizedVerdict,
|
|
79763
80359
|
message: `Drift evidence written to .swarm/evidence/${phase}/drift-verifier.json`,
|
|
79764
80360
|
approvedSnapshot: snapshotInfo,
|
|
79765
|
-
snapshotError
|
|
80361
|
+
snapshotError,
|
|
80362
|
+
qaProfileLocked,
|
|
80363
|
+
qaProfileLockError
|
|
79766
80364
|
}, null, 2);
|
|
79767
80365
|
} catch (error93) {
|
|
79768
80366
|
return JSON.stringify({
|
|
@@ -80067,7 +80665,9 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
80067
80665
|
checkpoint,
|
|
80068
80666
|
completion_verify,
|
|
80069
80667
|
complexity_hotspots,
|
|
80668
|
+
convene_council,
|
|
80070
80669
|
curator_analyze,
|
|
80670
|
+
declare_council_criteria,
|
|
80071
80671
|
knowledge_add,
|
|
80072
80672
|
knowledge_recall,
|
|
80073
80673
|
knowledge_remove,
|
|
@@ -80078,10 +80678,13 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
80078
80678
|
evidence_check,
|
|
80079
80679
|
extract_code_blocks,
|
|
80080
80680
|
get_approved_plan,
|
|
80681
|
+
get_qa_gate_profile,
|
|
80682
|
+
set_qa_gates,
|
|
80081
80683
|
gitingest,
|
|
80082
80684
|
imports,
|
|
80083
80685
|
knowledge_query,
|
|
80084
80686
|
lint,
|
|
80687
|
+
lint_spec,
|
|
80085
80688
|
diff,
|
|
80086
80689
|
pkg_audit,
|
|
80087
80690
|
placeholder_scan,
|
|
@@ -80089,6 +80692,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
80089
80692
|
pre_check_batch,
|
|
80090
80693
|
quality_budget,
|
|
80091
80694
|
repo_map,
|
|
80695
|
+
req_coverage,
|
|
80092
80696
|
retrieve_summary,
|
|
80093
80697
|
save_plan,
|
|
80094
80698
|
sast_scan,
|
|
@@ -80118,7 +80722,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
80118
80722
|
...opencodeConfig.command || {},
|
|
80119
80723
|
swarm: {
|
|
80120
80724
|
template: "/swarm $ARGUMENTS",
|
|
80121
|
-
description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|close]"
|
|
80725
|
+
description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|brainstorm|qa-gates|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|close]"
|
|
80122
80726
|
},
|
|
80123
80727
|
"swarm-status": {
|
|
80124
80728
|
template: "/swarm status",
|
|
@@ -80196,6 +80800,14 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
80196
80800
|
template: "/swarm specify $ARGUMENTS",
|
|
80197
80801
|
description: "Use /swarm specify to generate or import a feature specification"
|
|
80198
80802
|
},
|
|
80803
|
+
"swarm-brainstorm": {
|
|
80804
|
+
template: "/swarm brainstorm $ARGUMENTS",
|
|
80805
|
+
description: "Use /swarm brainstorm to enter the architect MODE: BRAINSTORM planning workflow"
|
|
80806
|
+
},
|
|
80807
|
+
"swarm-qa-gates": {
|
|
80808
|
+
template: "/swarm qa-gates $ARGUMENTS",
|
|
80809
|
+
description: "Use /swarm qa-gates to view or modify QA gate profile for the current plan"
|
|
80810
|
+
},
|
|
80199
80811
|
"swarm-dark-matter": {
|
|
80200
80812
|
template: "/swarm dark-matter",
|
|
80201
80813
|
description: "Use /swarm dark-matter to detect hidden file couplings"
|