opencode-swarm 6.71.1 → 6.72.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/architect.d.ts +11 -0
- package/dist/cli/index.js +238 -227
- package/dist/config/schema.d.ts +6 -0
- package/dist/hooks/knowledge-store.d.ts +10 -1
- package/dist/hooks/knowledge-types.d.ts +9 -1
- package/dist/index.js +671 -373
- package/dist/state.d.ts +27 -1
- package/dist/tools/convene-council.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17320,12 +17320,12 @@ var require_adapter = __commonJS((exports, module) => {
|
|
|
17320
17320
|
return newFs;
|
|
17321
17321
|
}
|
|
17322
17322
|
function toPromise(method) {
|
|
17323
|
-
return (...args) => new Promise((
|
|
17323
|
+
return (...args) => new Promise((resolve5, reject) => {
|
|
17324
17324
|
args.push((err, result) => {
|
|
17325
17325
|
if (err) {
|
|
17326
17326
|
reject(err);
|
|
17327
17327
|
} else {
|
|
17328
|
-
|
|
17328
|
+
resolve5(result);
|
|
17329
17329
|
}
|
|
17330
17330
|
});
|
|
17331
17331
|
method(...args);
|
|
@@ -19162,7 +19162,10 @@ var KnowledgeConfigSchema = exports_external.object({
|
|
|
19162
19162
|
min_encounter_score: exports_external.number().min(0).max(1).default(0.1),
|
|
19163
19163
|
initial_encounter_score: exports_external.number().min(0).max(5).default(1),
|
|
19164
19164
|
encounter_increment: exports_external.number().min(0).max(1).default(0.1),
|
|
19165
|
-
max_encounter_score: exports_external.number().min(1).max(20).default(10)
|
|
19165
|
+
max_encounter_score: exports_external.number().min(1).max(20).default(10),
|
|
19166
|
+
default_max_phases: exports_external.number().int().positive().default(10),
|
|
19167
|
+
todo_max_phases: exports_external.number().int().positive().default(3),
|
|
19168
|
+
sweep_enabled: exports_external.boolean().default(true)
|
|
19166
19169
|
});
|
|
19167
19170
|
var CuratorConfigSchema = exports_external.object({
|
|
19168
19171
|
enabled: exports_external.boolean().default(true),
|
|
@@ -19468,6 +19471,199 @@ init_manager2();
|
|
|
19468
19471
|
// src/state.ts
|
|
19469
19472
|
init_plan_schema();
|
|
19470
19473
|
|
|
19474
|
+
// src/db/qa-gate-profile.ts
|
|
19475
|
+
import { createHash as createHash3 } from "crypto";
|
|
19476
|
+
|
|
19477
|
+
// src/db/project-db.ts
|
|
19478
|
+
import { Database } from "bun:sqlite";
|
|
19479
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
19480
|
+
import { join as join6, resolve as resolve4 } from "path";
|
|
19481
|
+
var MIGRATIONS = [
|
|
19482
|
+
{
|
|
19483
|
+
version: 1,
|
|
19484
|
+
name: "create_project_constraints",
|
|
19485
|
+
sql: `CREATE TABLE project_constraints (
|
|
19486
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19487
|
+
constraint_type TEXT NOT NULL,
|
|
19488
|
+
content TEXT NOT NULL,
|
|
19489
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
19490
|
+
)`
|
|
19491
|
+
},
|
|
19492
|
+
{
|
|
19493
|
+
version: 2,
|
|
19494
|
+
name: "create_qa_gate_profile",
|
|
19495
|
+
sql: `CREATE TABLE qa_gate_profile (
|
|
19496
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19497
|
+
plan_id TEXT NOT NULL UNIQUE,
|
|
19498
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
19499
|
+
project_type TEXT,
|
|
19500
|
+
gates TEXT NOT NULL DEFAULT '{}',
|
|
19501
|
+
locked_at TEXT,
|
|
19502
|
+
locked_by_snapshot_seq INTEGER
|
|
19503
|
+
)`
|
|
19504
|
+
},
|
|
19505
|
+
{
|
|
19506
|
+
version: 3,
|
|
19507
|
+
name: "create_qa_gate_profile_immutability_trigger",
|
|
19508
|
+
sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
|
|
19509
|
+
BEFORE UPDATE ON qa_gate_profile
|
|
19510
|
+
WHEN OLD.locked_at IS NOT NULL
|
|
19511
|
+
BEGIN
|
|
19512
|
+
SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
|
|
19513
|
+
END`
|
|
19514
|
+
}
|
|
19515
|
+
];
|
|
19516
|
+
var _projectDbs = new Map;
|
|
19517
|
+
function runProjectMigrations(db) {
|
|
19518
|
+
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
19519
|
+
version INTEGER PRIMARY KEY,
|
|
19520
|
+
name TEXT NOT NULL,
|
|
19521
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
19522
|
+
)`);
|
|
19523
|
+
const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
19524
|
+
const currentVersion = row?.version ?? 0;
|
|
19525
|
+
for (const migration of MIGRATIONS) {
|
|
19526
|
+
if (migration.version <= currentVersion)
|
|
19527
|
+
continue;
|
|
19528
|
+
const apply = db.transaction(() => {
|
|
19529
|
+
db.run(migration.sql);
|
|
19530
|
+
db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
|
|
19531
|
+
migration.version,
|
|
19532
|
+
migration.name
|
|
19533
|
+
]);
|
|
19534
|
+
});
|
|
19535
|
+
apply();
|
|
19536
|
+
}
|
|
19537
|
+
}
|
|
19538
|
+
function projectDbPath(directory) {
|
|
19539
|
+
return join6(resolve4(directory), ".swarm", "swarm.db");
|
|
19540
|
+
}
|
|
19541
|
+
function projectDbExists(directory) {
|
|
19542
|
+
return existsSync4(projectDbPath(directory));
|
|
19543
|
+
}
|
|
19544
|
+
function getProjectDb(directory) {
|
|
19545
|
+
const key = resolve4(directory);
|
|
19546
|
+
const existing = _projectDbs.get(key);
|
|
19547
|
+
if (existing)
|
|
19548
|
+
return existing;
|
|
19549
|
+
const swarmDir = join6(key, ".swarm");
|
|
19550
|
+
mkdirSync3(swarmDir, { recursive: true });
|
|
19551
|
+
const db = new Database(join6(swarmDir, "swarm.db"));
|
|
19552
|
+
db.run("PRAGMA journal_mode = WAL;");
|
|
19553
|
+
db.run("PRAGMA synchronous = NORMAL;");
|
|
19554
|
+
db.run("PRAGMA busy_timeout = 5000;");
|
|
19555
|
+
db.run("PRAGMA foreign_keys = ON;");
|
|
19556
|
+
runProjectMigrations(db);
|
|
19557
|
+
_projectDbs.set(key, db);
|
|
19558
|
+
return db;
|
|
19559
|
+
}
|
|
19560
|
+
|
|
19561
|
+
// src/db/qa-gate-profile.ts
|
|
19562
|
+
var DEFAULT_QA_GATES = {
|
|
19563
|
+
reviewer: true,
|
|
19564
|
+
test_engineer: true,
|
|
19565
|
+
council_mode: false,
|
|
19566
|
+
sme_enabled: true,
|
|
19567
|
+
critic_pre_plan: true,
|
|
19568
|
+
hallucination_guard: false,
|
|
19569
|
+
sast_enabled: true
|
|
19570
|
+
};
|
|
19571
|
+
function rowToProfile(row) {
|
|
19572
|
+
let parsed = {};
|
|
19573
|
+
try {
|
|
19574
|
+
parsed = JSON.parse(row.gates);
|
|
19575
|
+
} catch {
|
|
19576
|
+
parsed = {};
|
|
19577
|
+
}
|
|
19578
|
+
const gates = { ...DEFAULT_QA_GATES, ...parsed };
|
|
19579
|
+
return {
|
|
19580
|
+
id: row.id,
|
|
19581
|
+
plan_id: row.plan_id,
|
|
19582
|
+
created_at: row.created_at,
|
|
19583
|
+
project_type: row.project_type,
|
|
19584
|
+
gates,
|
|
19585
|
+
locked_at: row.locked_at,
|
|
19586
|
+
locked_by_snapshot_seq: row.locked_by_snapshot_seq
|
|
19587
|
+
};
|
|
19588
|
+
}
|
|
19589
|
+
function getProfile(directory, planId) {
|
|
19590
|
+
if (!projectDbExists(directory))
|
|
19591
|
+
return null;
|
|
19592
|
+
const db = getProjectDb(directory);
|
|
19593
|
+
const row = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
|
|
19594
|
+
return row ? rowToProfile(row) : null;
|
|
19595
|
+
}
|
|
19596
|
+
function getOrCreateProfile(directory, planId, projectType) {
|
|
19597
|
+
const existing = getProfile(directory, planId);
|
|
19598
|
+
if (existing)
|
|
19599
|
+
return existing;
|
|
19600
|
+
const db = getProjectDb(directory);
|
|
19601
|
+
const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
|
|
19602
|
+
const insert = db.transaction(() => {
|
|
19603
|
+
db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
|
|
19604
|
+
});
|
|
19605
|
+
try {
|
|
19606
|
+
insert();
|
|
19607
|
+
} catch (err) {
|
|
19608
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19609
|
+
if (!msg.toLowerCase().includes("unique")) {
|
|
19610
|
+
throw err;
|
|
19611
|
+
}
|
|
19612
|
+
}
|
|
19613
|
+
const after = getProfile(directory, planId);
|
|
19614
|
+
if (!after) {
|
|
19615
|
+
throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
|
|
19616
|
+
}
|
|
19617
|
+
return after;
|
|
19618
|
+
}
|
|
19619
|
+
function setGates(directory, planId, gates) {
|
|
19620
|
+
const current = getProfile(directory, planId);
|
|
19621
|
+
if (!current) {
|
|
19622
|
+
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
|
|
19623
|
+
}
|
|
19624
|
+
if (current.locked_at !== null) {
|
|
19625
|
+
throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
|
|
19626
|
+
}
|
|
19627
|
+
const merged = { ...current.gates };
|
|
19628
|
+
for (const key of Object.keys(gates)) {
|
|
19629
|
+
const incoming = gates[key];
|
|
19630
|
+
if (incoming === undefined)
|
|
19631
|
+
continue;
|
|
19632
|
+
if (incoming === false && current.gates[key] === true) {
|
|
19633
|
+
throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
|
|
19634
|
+
}
|
|
19635
|
+
if (incoming === true) {
|
|
19636
|
+
merged[key] = true;
|
|
19637
|
+
}
|
|
19638
|
+
}
|
|
19639
|
+
const db = getProjectDb(directory);
|
|
19640
|
+
db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
|
|
19641
|
+
JSON.stringify(merged),
|
|
19642
|
+
planId
|
|
19643
|
+
]);
|
|
19644
|
+
const updated = getProfile(directory, planId);
|
|
19645
|
+
if (!updated) {
|
|
19646
|
+
throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
|
|
19647
|
+
}
|
|
19648
|
+
return updated;
|
|
19649
|
+
}
|
|
19650
|
+
function computeProfileHash(profile) {
|
|
19651
|
+
const payload = JSON.stringify({
|
|
19652
|
+
plan_id: profile.plan_id,
|
|
19653
|
+
gates: profile.gates
|
|
19654
|
+
});
|
|
19655
|
+
return createHash3("sha256").update(payload).digest("hex");
|
|
19656
|
+
}
|
|
19657
|
+
function getEffectiveGates(profile, sessionOverrides) {
|
|
19658
|
+
const merged = { ...profile.gates };
|
|
19659
|
+
for (const key of Object.keys(sessionOverrides)) {
|
|
19660
|
+
if (sessionOverrides[key] === true) {
|
|
19661
|
+
merged[key] = true;
|
|
19662
|
+
}
|
|
19663
|
+
}
|
|
19664
|
+
return merged;
|
|
19665
|
+
}
|
|
19666
|
+
|
|
19471
19667
|
// src/hooks/delegation-gate.ts
|
|
19472
19668
|
init_telemetry();
|
|
19473
19669
|
|
|
@@ -19921,8 +20117,10 @@ function clearPendingCoderScope() {
|
|
|
19921
20117
|
}
|
|
19922
20118
|
|
|
19923
20119
|
// src/state.ts
|
|
20120
|
+
init_manager();
|
|
19924
20121
|
init_telemetry();
|
|
19925
20122
|
var _rehydrationCache = null;
|
|
20123
|
+
var _councilDisagreementWarned = new Set;
|
|
19926
20124
|
var swarmState = {
|
|
19927
20125
|
activeToolCalls: new Map,
|
|
19928
20126
|
toolAggregates: new Map,
|
|
@@ -19954,6 +20152,7 @@ function resetSwarmState() {
|
|
|
19954
20152
|
swarmState.fullAutoEnabledInConfig = false;
|
|
19955
20153
|
swarmState.environmentProfiles.clear();
|
|
19956
20154
|
clearPendingCoderScope();
|
|
20155
|
+
_councilDisagreementWarned.clear();
|
|
19957
20156
|
}
|
|
19958
20157
|
function getAgentSession(sessionId) {
|
|
19959
20158
|
return swarmState.agentSessions.get(sessionId);
|
|
@@ -33129,7 +33328,7 @@ function hasUncommittedChanges(cwd) {
|
|
|
33129
33328
|
|
|
33130
33329
|
// src/hooks/knowledge-store.ts
|
|
33131
33330
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
33132
|
-
import { existsSync as
|
|
33331
|
+
import { existsSync as existsSync6 } from "fs";
|
|
33133
33332
|
import { appendFile as appendFile2, mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
33134
33333
|
import * as os2 from "os";
|
|
33135
33334
|
import * as path8 from "path";
|
|
@@ -33157,7 +33356,7 @@ function resolveHiveRejectedPath() {
|
|
|
33157
33356
|
return path8.join(path8.dirname(hivePath), "shared-learnings-rejected.jsonl");
|
|
33158
33357
|
}
|
|
33159
33358
|
async function readKnowledge(filePath) {
|
|
33160
|
-
if (!
|
|
33359
|
+
if (!existsSync6(filePath))
|
|
33161
33360
|
return [];
|
|
33162
33361
|
const content = await readFile2(filePath, "utf-8");
|
|
33163
33362
|
const results = [];
|
|
@@ -33188,7 +33387,8 @@ async function rewriteKnowledge(filePath, entries) {
|
|
|
33188
33387
|
let release = null;
|
|
33189
33388
|
try {
|
|
33190
33389
|
release = await import_proper_lockfile2.default.lock(dir, {
|
|
33191
|
-
retries: { retries:
|
|
33390
|
+
retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
|
|
33391
|
+
stale: 5000
|
|
33192
33392
|
});
|
|
33193
33393
|
const content = entries.map((e) => JSON.stringify(e)).join(`
|
|
33194
33394
|
`) + (entries.length > 0 ? `
|
|
@@ -33257,6 +33457,8 @@ function computeConfidence(confirmedByCount, autoGenerated) {
|
|
|
33257
33457
|
function inferTags(lesson) {
|
|
33258
33458
|
const lower = lesson.toLowerCase();
|
|
33259
33459
|
const tags = [];
|
|
33460
|
+
if (/(^|\s)(?:todo|remember|don't?(?:\s+)?forget)(?:\s|:|,|$)/i.test(lesson))
|
|
33461
|
+
tags.push("todo");
|
|
33260
33462
|
if (/\b(?:typescript|ts)\b/.test(lower))
|
|
33261
33463
|
tags.push("typescript");
|
|
33262
33464
|
if (/\b(?:javascript|js)\b/.test(lower))
|
|
@@ -33341,6 +33543,7 @@ var VALID_CATEGORIES = new Set([
|
|
|
33341
33543
|
"debugging",
|
|
33342
33544
|
"performance",
|
|
33343
33545
|
"integration",
|
|
33546
|
+
"todo",
|
|
33344
33547
|
"other"
|
|
33345
33548
|
]);
|
|
33346
33549
|
var TECH_REFERENCE_WORDS = new Set([
|
|
@@ -33659,7 +33862,8 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
33659
33862
|
["debugging", "debugging"],
|
|
33660
33863
|
["performance", "performance"],
|
|
33661
33864
|
["integration", "integration"],
|
|
33662
|
-
["other", "other"]
|
|
33865
|
+
["other", "other"],
|
|
33866
|
+
["todo", "todo"]
|
|
33663
33867
|
]);
|
|
33664
33868
|
for (const lesson of lessons) {
|
|
33665
33869
|
const tags = inferTags(lesson);
|
|
@@ -33785,7 +33989,7 @@ async function writeCheckpoint(directory) {
|
|
|
33785
33989
|
|
|
33786
33990
|
// src/session/snapshot-writer.ts
|
|
33787
33991
|
init_utils2();
|
|
33788
|
-
import { mkdirSync as
|
|
33992
|
+
import { mkdirSync as mkdirSync6, renameSync as renameSync5 } from "fs";
|
|
33789
33993
|
import * as path11 from "path";
|
|
33790
33994
|
init_utils();
|
|
33791
33995
|
var _writeInFlight = Promise.resolve();
|
|
@@ -33878,7 +34082,7 @@ async function writeSnapshot(directory, state) {
|
|
|
33878
34082
|
const content = JSON.stringify(snapshot, null, 2);
|
|
33879
34083
|
const resolvedPath = validateSwarmPath(directory, "session/state.json");
|
|
33880
34084
|
const dir = path11.dirname(resolvedPath);
|
|
33881
|
-
|
|
34085
|
+
mkdirSync6(dir, { recursive: true });
|
|
33882
34086
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
33883
34087
|
await Bun.write(tempPath, content);
|
|
33884
34088
|
renameSync5(tempPath, resolvedPath);
|
|
@@ -35376,7 +35580,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
35376
35580
|
|
|
35377
35581
|
// src/services/diagnose-service.ts
|
|
35378
35582
|
import * as child_process4 from "child_process";
|
|
35379
|
-
import { existsSync as
|
|
35583
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
35380
35584
|
import path16 from "path";
|
|
35381
35585
|
import { fileURLToPath } from "url";
|
|
35382
35586
|
init_manager2();
|
|
@@ -35613,7 +35817,7 @@ async function checkConfigBackups(directory) {
|
|
|
35613
35817
|
}
|
|
35614
35818
|
async function checkGitRepository(directory) {
|
|
35615
35819
|
try {
|
|
35616
|
-
if (!
|
|
35820
|
+
if (!existsSync7(directory) || !statSync4(directory).isDirectory()) {
|
|
35617
35821
|
return {
|
|
35618
35822
|
name: "Git Repository",
|
|
35619
35823
|
status: "\u274C",
|
|
@@ -35678,7 +35882,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
35678
35882
|
}
|
|
35679
35883
|
async function checkConfigParseability(directory) {
|
|
35680
35884
|
const configPath = path16.join(directory, ".opencode/opencode-swarm.json");
|
|
35681
|
-
if (!
|
|
35885
|
+
if (!existsSync7(configPath)) {
|
|
35682
35886
|
return {
|
|
35683
35887
|
name: "Config Parseability",
|
|
35684
35888
|
status: "\u2705",
|
|
@@ -35728,11 +35932,11 @@ async function checkGrammarWasmFiles() {
|
|
|
35728
35932
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
35729
35933
|
const grammarDir = isSource ? path16.join(thisDir, "..", "lang", "grammars") : path16.join(thisDir, "lang", "grammars");
|
|
35730
35934
|
const missing = [];
|
|
35731
|
-
if (!
|
|
35935
|
+
if (!existsSync7(path16.join(grammarDir, "tree-sitter.wasm"))) {
|
|
35732
35936
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
35733
35937
|
}
|
|
35734
35938
|
for (const file3 of grammarFiles) {
|
|
35735
|
-
if (!
|
|
35939
|
+
if (!existsSync7(path16.join(grammarDir, file3))) {
|
|
35736
35940
|
missing.push(file3);
|
|
35737
35941
|
}
|
|
35738
35942
|
}
|
|
@@ -35751,7 +35955,7 @@ async function checkGrammarWasmFiles() {
|
|
|
35751
35955
|
}
|
|
35752
35956
|
async function checkCheckpointManifest(directory) {
|
|
35753
35957
|
const manifestPath = path16.join(directory, ".swarm/checkpoints.json");
|
|
35754
|
-
if (!
|
|
35958
|
+
if (!existsSync7(manifestPath)) {
|
|
35755
35959
|
return {
|
|
35756
35960
|
name: "Checkpoint Manifest",
|
|
35757
35961
|
status: "\u2705",
|
|
@@ -35803,7 +36007,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
35803
36007
|
}
|
|
35804
36008
|
async function checkEventStreamIntegrity(directory) {
|
|
35805
36009
|
const eventsPath = path16.join(directory, ".swarm/events.jsonl");
|
|
35806
|
-
if (!
|
|
36010
|
+
if (!existsSync7(eventsPath)) {
|
|
35807
36011
|
return {
|
|
35808
36012
|
name: "Event Stream",
|
|
35809
36013
|
status: "\u2705",
|
|
@@ -35844,7 +36048,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
35844
36048
|
}
|
|
35845
36049
|
async function checkSteeringDirectives(directory) {
|
|
35846
36050
|
const eventsPath = path16.join(directory, ".swarm/events.jsonl");
|
|
35847
|
-
if (!
|
|
36051
|
+
if (!existsSync7(eventsPath)) {
|
|
35848
36052
|
return {
|
|
35849
36053
|
name: "Steering Directives",
|
|
35850
36054
|
status: "\u2705",
|
|
@@ -35900,7 +36104,7 @@ async function checkCurator(directory) {
|
|
|
35900
36104
|
};
|
|
35901
36105
|
}
|
|
35902
36106
|
const summaryPath = path16.join(directory, ".swarm/curator-summary.json");
|
|
35903
|
-
if (!
|
|
36107
|
+
if (!existsSync7(summaryPath)) {
|
|
35904
36108
|
return {
|
|
35905
36109
|
name: "Curator",
|
|
35906
36110
|
status: "\u2705",
|
|
@@ -36048,7 +36252,7 @@ async function getDiagnoseData(directory) {
|
|
|
36048
36252
|
checks5.push(await checkCurator(directory));
|
|
36049
36253
|
try {
|
|
36050
36254
|
const evidenceDir = path16.join(directory, ".swarm", "evidence");
|
|
36051
|
-
const snapshotFiles =
|
|
36255
|
+
const snapshotFiles = existsSync7(evidenceDir) ? readdirSync3(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
|
|
36052
36256
|
if (snapshotFiles.length > 0) {
|
|
36053
36257
|
const latest = snapshotFiles.sort().pop();
|
|
36054
36258
|
checks5.push({
|
|
@@ -36108,7 +36312,7 @@ import * as path18 from "path";
|
|
|
36108
36312
|
|
|
36109
36313
|
// src/lang/detector.ts
|
|
36110
36314
|
import { access as access2, readdir as readdir2 } from "fs/promises";
|
|
36111
|
-
import { extname as extname2, join as
|
|
36315
|
+
import { extname as extname2, join as join15 } from "path";
|
|
36112
36316
|
|
|
36113
36317
|
// src/lang/profiles.ts
|
|
36114
36318
|
class LanguageRegistry {
|
|
@@ -37088,7 +37292,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
37088
37292
|
if (detectFile.includes("*") || detectFile.includes("?"))
|
|
37089
37293
|
continue;
|
|
37090
37294
|
try {
|
|
37091
|
-
await access2(
|
|
37295
|
+
await access2(join15(dir, detectFile));
|
|
37092
37296
|
detected.add(profile.id);
|
|
37093
37297
|
break;
|
|
37094
37298
|
} catch {}
|
|
@@ -37109,7 +37313,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
37109
37313
|
const topEntries = await readdir2(projectDir, { withFileTypes: true });
|
|
37110
37314
|
for (const entry of topEntries) {
|
|
37111
37315
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
37112
|
-
await scanDir(
|
|
37316
|
+
await scanDir(join15(projectDir, entry.name));
|
|
37113
37317
|
}
|
|
37114
37318
|
}
|
|
37115
37319
|
} catch {}
|
|
@@ -38445,14 +38649,14 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
38445
38649
|
}
|
|
38446
38650
|
// src/hooks/knowledge-migrator.ts
|
|
38447
38651
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
38448
|
-
import { existsSync as
|
|
38652
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
38449
38653
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
38450
38654
|
import * as path20 from "path";
|
|
38451
38655
|
async function migrateContextToKnowledge(directory, config3) {
|
|
38452
38656
|
const sentinelPath = path20.join(directory, ".swarm", ".knowledge-migrated");
|
|
38453
38657
|
const contextPath = path20.join(directory, ".swarm", "context.md");
|
|
38454
38658
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
38455
|
-
if (
|
|
38659
|
+
if (existsSync11(sentinelPath)) {
|
|
38456
38660
|
return {
|
|
38457
38661
|
migrated: false,
|
|
38458
38662
|
entriesMigrated: 0,
|
|
@@ -38461,7 +38665,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
38461
38665
|
skippedReason: "sentinel-exists"
|
|
38462
38666
|
};
|
|
38463
38667
|
}
|
|
38464
|
-
if (!
|
|
38668
|
+
if (!existsSync11(contextPath)) {
|
|
38465
38669
|
return {
|
|
38466
38670
|
migrated: false,
|
|
38467
38671
|
entriesMigrated: 0,
|
|
@@ -38647,7 +38851,7 @@ function truncateLesson(text) {
|
|
|
38647
38851
|
}
|
|
38648
38852
|
function inferProjectName(directory) {
|
|
38649
38853
|
const packageJsonPath = path20.join(directory, "package.json");
|
|
38650
|
-
if (
|
|
38854
|
+
if (existsSync11(packageJsonPath)) {
|
|
38651
38855
|
try {
|
|
38652
38856
|
const pkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
|
|
38653
38857
|
if (pkg.name && typeof pkg.name === "string") {
|
|
@@ -39155,7 +39359,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
39155
39359
|
stderr: "pipe"
|
|
39156
39360
|
});
|
|
39157
39361
|
const biomeExit = biomeProc.exited;
|
|
39158
|
-
const timeout = new Promise((
|
|
39362
|
+
const timeout = new Promise((resolve8) => setTimeout(() => resolve8("timeout"), DETECT_TIMEOUT));
|
|
39159
39363
|
const result = await Promise.race([biomeExit, timeout]);
|
|
39160
39364
|
if (result === "timeout") {
|
|
39161
39365
|
biomeProc.kill();
|
|
@@ -39169,7 +39373,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
39169
39373
|
stderr: "pipe"
|
|
39170
39374
|
});
|
|
39171
39375
|
const eslintExit = eslintProc.exited;
|
|
39172
|
-
const timeout = new Promise((
|
|
39376
|
+
const timeout = new Promise((resolve8) => setTimeout(() => resolve8("timeout"), DETECT_TIMEOUT));
|
|
39173
39377
|
const result = await Promise.race([eslintExit, timeout]);
|
|
39174
39378
|
if (result === "timeout") {
|
|
39175
39379
|
eslintProc.kill();
|
|
@@ -40558,15 +40762,15 @@ function appendTestRun(record3, workingDir) {
|
|
|
40558
40762
|
prunedRecords.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
40559
40763
|
try {
|
|
40560
40764
|
const lines = prunedRecords.map((rec) => JSON.stringify(rec));
|
|
40561
|
-
const content = lines.join(`
|
|
40562
|
-
`)
|
|
40765
|
+
const content = `${lines.join(`
|
|
40766
|
+
`)}
|
|
40563
40767
|
`;
|
|
40564
|
-
const tempPath = historyPath
|
|
40768
|
+
const tempPath = `${historyPath}.tmp`;
|
|
40565
40769
|
fs14.writeFileSync(tempPath, content, "utf-8");
|
|
40566
40770
|
fs14.renameSync(tempPath, historyPath);
|
|
40567
40771
|
} catch (err) {
|
|
40568
40772
|
try {
|
|
40569
|
-
const tempPath = historyPath
|
|
40773
|
+
const tempPath = `${historyPath}.tmp`;
|
|
40570
40774
|
if (fs14.existsSync(tempPath)) {
|
|
40571
40775
|
fs14.unlinkSync(tempPath);
|
|
40572
40776
|
}
|
|
@@ -41388,9 +41592,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
41388
41592
|
stderr: "pipe",
|
|
41389
41593
|
cwd
|
|
41390
41594
|
});
|
|
41391
|
-
const timeoutPromise = new Promise((
|
|
41595
|
+
const timeoutPromise = new Promise((resolve11) => setTimeout(() => {
|
|
41392
41596
|
proc.kill();
|
|
41393
|
-
|
|
41597
|
+
resolve11(-1);
|
|
41394
41598
|
}, timeout_ms));
|
|
41395
41599
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
41396
41600
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -42619,199 +42823,6 @@ async function handlePromoteCommand(directory, args) {
|
|
|
42619
42823
|
}
|
|
42620
42824
|
}
|
|
42621
42825
|
|
|
42622
|
-
// src/db/qa-gate-profile.ts
|
|
42623
|
-
import { createHash as createHash4 } from "crypto";
|
|
42624
|
-
|
|
42625
|
-
// src/db/project-db.ts
|
|
42626
|
-
import { Database } from "bun:sqlite";
|
|
42627
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8 } from "fs";
|
|
42628
|
-
import { join as join23, resolve as resolve11 } from "path";
|
|
42629
|
-
var MIGRATIONS = [
|
|
42630
|
-
{
|
|
42631
|
-
version: 1,
|
|
42632
|
-
name: "create_project_constraints",
|
|
42633
|
-
sql: `CREATE TABLE project_constraints (
|
|
42634
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42635
|
-
constraint_type TEXT NOT NULL,
|
|
42636
|
-
content TEXT NOT NULL,
|
|
42637
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42638
|
-
)`
|
|
42639
|
-
},
|
|
42640
|
-
{
|
|
42641
|
-
version: 2,
|
|
42642
|
-
name: "create_qa_gate_profile",
|
|
42643
|
-
sql: `CREATE TABLE qa_gate_profile (
|
|
42644
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42645
|
-
plan_id TEXT NOT NULL UNIQUE,
|
|
42646
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42647
|
-
project_type TEXT,
|
|
42648
|
-
gates TEXT NOT NULL DEFAULT '{}',
|
|
42649
|
-
locked_at TEXT,
|
|
42650
|
-
locked_by_snapshot_seq INTEGER
|
|
42651
|
-
)`
|
|
42652
|
-
},
|
|
42653
|
-
{
|
|
42654
|
-
version: 3,
|
|
42655
|
-
name: "create_qa_gate_profile_immutability_trigger",
|
|
42656
|
-
sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
|
|
42657
|
-
BEFORE UPDATE ON qa_gate_profile
|
|
42658
|
-
WHEN OLD.locked_at IS NOT NULL
|
|
42659
|
-
BEGIN
|
|
42660
|
-
SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
|
|
42661
|
-
END`
|
|
42662
|
-
}
|
|
42663
|
-
];
|
|
42664
|
-
var _projectDbs = new Map;
|
|
42665
|
-
function runProjectMigrations(db) {
|
|
42666
|
-
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
42667
|
-
version INTEGER PRIMARY KEY,
|
|
42668
|
-
name TEXT NOT NULL,
|
|
42669
|
-
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42670
|
-
)`);
|
|
42671
|
-
const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
42672
|
-
const currentVersion = row?.version ?? 0;
|
|
42673
|
-
for (const migration of MIGRATIONS) {
|
|
42674
|
-
if (migration.version <= currentVersion)
|
|
42675
|
-
continue;
|
|
42676
|
-
const apply = db.transaction(() => {
|
|
42677
|
-
db.run(migration.sql);
|
|
42678
|
-
db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
|
|
42679
|
-
migration.version,
|
|
42680
|
-
migration.name
|
|
42681
|
-
]);
|
|
42682
|
-
});
|
|
42683
|
-
apply();
|
|
42684
|
-
}
|
|
42685
|
-
}
|
|
42686
|
-
function projectDbPath(directory) {
|
|
42687
|
-
return join23(resolve11(directory), ".swarm", "swarm.db");
|
|
42688
|
-
}
|
|
42689
|
-
function projectDbExists(directory) {
|
|
42690
|
-
return existsSync16(projectDbPath(directory));
|
|
42691
|
-
}
|
|
42692
|
-
function getProjectDb(directory) {
|
|
42693
|
-
const key = resolve11(directory);
|
|
42694
|
-
const existing = _projectDbs.get(key);
|
|
42695
|
-
if (existing)
|
|
42696
|
-
return existing;
|
|
42697
|
-
const swarmDir = join23(key, ".swarm");
|
|
42698
|
-
mkdirSync8(swarmDir, { recursive: true });
|
|
42699
|
-
const db = new Database(join23(swarmDir, "swarm.db"));
|
|
42700
|
-
db.run("PRAGMA journal_mode = WAL;");
|
|
42701
|
-
db.run("PRAGMA synchronous = NORMAL;");
|
|
42702
|
-
db.run("PRAGMA busy_timeout = 5000;");
|
|
42703
|
-
db.run("PRAGMA foreign_keys = ON;");
|
|
42704
|
-
runProjectMigrations(db);
|
|
42705
|
-
_projectDbs.set(key, db);
|
|
42706
|
-
return db;
|
|
42707
|
-
}
|
|
42708
|
-
|
|
42709
|
-
// src/db/qa-gate-profile.ts
|
|
42710
|
-
var DEFAULT_QA_GATES = {
|
|
42711
|
-
reviewer: true,
|
|
42712
|
-
test_engineer: true,
|
|
42713
|
-
council_mode: false,
|
|
42714
|
-
sme_enabled: true,
|
|
42715
|
-
critic_pre_plan: true,
|
|
42716
|
-
hallucination_guard: false,
|
|
42717
|
-
sast_enabled: true
|
|
42718
|
-
};
|
|
42719
|
-
function rowToProfile(row) {
|
|
42720
|
-
let parsed = {};
|
|
42721
|
-
try {
|
|
42722
|
-
parsed = JSON.parse(row.gates);
|
|
42723
|
-
} catch {
|
|
42724
|
-
parsed = {};
|
|
42725
|
-
}
|
|
42726
|
-
const gates = { ...DEFAULT_QA_GATES, ...parsed };
|
|
42727
|
-
return {
|
|
42728
|
-
id: row.id,
|
|
42729
|
-
plan_id: row.plan_id,
|
|
42730
|
-
created_at: row.created_at,
|
|
42731
|
-
project_type: row.project_type,
|
|
42732
|
-
gates,
|
|
42733
|
-
locked_at: row.locked_at,
|
|
42734
|
-
locked_by_snapshot_seq: row.locked_by_snapshot_seq
|
|
42735
|
-
};
|
|
42736
|
-
}
|
|
42737
|
-
function getProfile(directory, planId) {
|
|
42738
|
-
if (!projectDbExists(directory))
|
|
42739
|
-
return null;
|
|
42740
|
-
const db = getProjectDb(directory);
|
|
42741
|
-
const row = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
|
|
42742
|
-
return row ? rowToProfile(row) : null;
|
|
42743
|
-
}
|
|
42744
|
-
function getOrCreateProfile(directory, planId, projectType) {
|
|
42745
|
-
const existing = getProfile(directory, planId);
|
|
42746
|
-
if (existing)
|
|
42747
|
-
return existing;
|
|
42748
|
-
const db = getProjectDb(directory);
|
|
42749
|
-
const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
|
|
42750
|
-
const insert = db.transaction(() => {
|
|
42751
|
-
db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
|
|
42752
|
-
});
|
|
42753
|
-
try {
|
|
42754
|
-
insert();
|
|
42755
|
-
} catch (err) {
|
|
42756
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
42757
|
-
if (!msg.toLowerCase().includes("unique")) {
|
|
42758
|
-
throw err;
|
|
42759
|
-
}
|
|
42760
|
-
}
|
|
42761
|
-
const after = getProfile(directory, planId);
|
|
42762
|
-
if (!after) {
|
|
42763
|
-
throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
|
|
42764
|
-
}
|
|
42765
|
-
return after;
|
|
42766
|
-
}
|
|
42767
|
-
function setGates(directory, planId, gates) {
|
|
42768
|
-
const current = getProfile(directory, planId);
|
|
42769
|
-
if (!current) {
|
|
42770
|
-
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
|
|
42771
|
-
}
|
|
42772
|
-
if (current.locked_at !== null) {
|
|
42773
|
-
throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
|
|
42774
|
-
}
|
|
42775
|
-
const merged = { ...current.gates };
|
|
42776
|
-
for (const key of Object.keys(gates)) {
|
|
42777
|
-
const incoming = gates[key];
|
|
42778
|
-
if (incoming === undefined)
|
|
42779
|
-
continue;
|
|
42780
|
-
if (incoming === false && current.gates[key] === true) {
|
|
42781
|
-
throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
|
|
42782
|
-
}
|
|
42783
|
-
if (incoming === true) {
|
|
42784
|
-
merged[key] = true;
|
|
42785
|
-
}
|
|
42786
|
-
}
|
|
42787
|
-
const db = getProjectDb(directory);
|
|
42788
|
-
db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
|
|
42789
|
-
JSON.stringify(merged),
|
|
42790
|
-
planId
|
|
42791
|
-
]);
|
|
42792
|
-
const updated = getProfile(directory, planId);
|
|
42793
|
-
if (!updated) {
|
|
42794
|
-
throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
|
|
42795
|
-
}
|
|
42796
|
-
return updated;
|
|
42797
|
-
}
|
|
42798
|
-
function computeProfileHash(profile) {
|
|
42799
|
-
const payload = JSON.stringify({
|
|
42800
|
-
plan_id: profile.plan_id,
|
|
42801
|
-
gates: profile.gates
|
|
42802
|
-
});
|
|
42803
|
-
return createHash4("sha256").update(payload).digest("hex");
|
|
42804
|
-
}
|
|
42805
|
-
function getEffectiveGates(profile, sessionOverrides) {
|
|
42806
|
-
const merged = { ...profile.gates };
|
|
42807
|
-
for (const key of Object.keys(sessionOverrides)) {
|
|
42808
|
-
if (sessionOverrides[key] === true) {
|
|
42809
|
-
merged[key] = true;
|
|
42810
|
-
}
|
|
42811
|
-
}
|
|
42812
|
-
return merged;
|
|
42813
|
-
}
|
|
42814
|
-
|
|
42815
42826
|
// src/commands/qa-gates.ts
|
|
42816
42827
|
init_manager();
|
|
42817
42828
|
var ALL_GATE_NAMES = [
|