opencode-swarm 6.72.0 → 6.73.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/__tests__/critic_hallucination_verifier-whitelist.test.d.ts +1 -0
- package/dist/agents/architect.d.ts +11 -0
- package/dist/agents/critic.d.ts +2 -1
- package/dist/cli/index.js +243 -224
- package/dist/config/constants.d.ts +2 -2
- package/dist/db/qa-gate-profile.d.ts +15 -7
- package/dist/index.js +856 -375
- package/dist/state.d.ts +27 -1
- package/dist/tools/convene-council.d.ts +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/tools/write-hallucination-evidence.d.ts +30 -0
- 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);
|
|
@@ -18496,6 +18496,7 @@ var TOOL_NAMES = [
|
|
|
18496
18496
|
"lint_spec",
|
|
18497
18497
|
"write_retro",
|
|
18498
18498
|
"write_drift_evidence",
|
|
18499
|
+
"write_hallucination_evidence",
|
|
18499
18500
|
"declare_scope",
|
|
18500
18501
|
"knowledge_query",
|
|
18501
18502
|
"doc_scan",
|
|
@@ -18526,6 +18527,7 @@ var ALL_SUBAGENT_NAMES = [
|
|
|
18526
18527
|
"designer",
|
|
18527
18528
|
"critic_sounding_board",
|
|
18528
18529
|
"critic_drift_verifier",
|
|
18530
|
+
"critic_hallucination_verifier",
|
|
18529
18531
|
"curator_init",
|
|
18530
18532
|
"curator_phase",
|
|
18531
18533
|
...QA_AGENTS,
|
|
@@ -18570,6 +18572,7 @@ var AGENT_TOOL_MAP = {
|
|
|
18570
18572
|
"lint_spec",
|
|
18571
18573
|
"write_retro",
|
|
18572
18574
|
"write_drift_evidence",
|
|
18575
|
+
"write_hallucination_evidence",
|
|
18573
18576
|
"declare_scope",
|
|
18574
18577
|
"sast_scan",
|
|
18575
18578
|
"sbom_generate",
|
|
@@ -18697,6 +18700,19 @@ var AGENT_TOOL_MAP = {
|
|
|
18697
18700
|
"get_approved_plan",
|
|
18698
18701
|
"repo_map"
|
|
18699
18702
|
],
|
|
18703
|
+
critic_hallucination_verifier: [
|
|
18704
|
+
"complexity_hotspots",
|
|
18705
|
+
"detect_domains",
|
|
18706
|
+
"imports",
|
|
18707
|
+
"retrieve_summary",
|
|
18708
|
+
"symbols",
|
|
18709
|
+
"batch_symbols",
|
|
18710
|
+
"search",
|
|
18711
|
+
"pkg_audit",
|
|
18712
|
+
"knowledge_recall",
|
|
18713
|
+
"req_coverage",
|
|
18714
|
+
"repo_map"
|
|
18715
|
+
],
|
|
18700
18716
|
critic_oversight: [
|
|
18701
18717
|
"complexity_hotspots",
|
|
18702
18718
|
"detect_domains",
|
|
@@ -19471,6 +19487,199 @@ init_manager2();
|
|
|
19471
19487
|
// src/state.ts
|
|
19472
19488
|
init_plan_schema();
|
|
19473
19489
|
|
|
19490
|
+
// src/db/qa-gate-profile.ts
|
|
19491
|
+
import { createHash as createHash3 } from "crypto";
|
|
19492
|
+
|
|
19493
|
+
// src/db/project-db.ts
|
|
19494
|
+
import { Database } from "bun:sqlite";
|
|
19495
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
19496
|
+
import { join as join6, resolve as resolve4 } from "path";
|
|
19497
|
+
var MIGRATIONS = [
|
|
19498
|
+
{
|
|
19499
|
+
version: 1,
|
|
19500
|
+
name: "create_project_constraints",
|
|
19501
|
+
sql: `CREATE TABLE project_constraints (
|
|
19502
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19503
|
+
constraint_type TEXT NOT NULL,
|
|
19504
|
+
content TEXT NOT NULL,
|
|
19505
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
19506
|
+
)`
|
|
19507
|
+
},
|
|
19508
|
+
{
|
|
19509
|
+
version: 2,
|
|
19510
|
+
name: "create_qa_gate_profile",
|
|
19511
|
+
sql: `CREATE TABLE qa_gate_profile (
|
|
19512
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19513
|
+
plan_id TEXT NOT NULL UNIQUE,
|
|
19514
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
19515
|
+
project_type TEXT,
|
|
19516
|
+
gates TEXT NOT NULL DEFAULT '{}',
|
|
19517
|
+
locked_at TEXT,
|
|
19518
|
+
locked_by_snapshot_seq INTEGER
|
|
19519
|
+
)`
|
|
19520
|
+
},
|
|
19521
|
+
{
|
|
19522
|
+
version: 3,
|
|
19523
|
+
name: "create_qa_gate_profile_immutability_trigger",
|
|
19524
|
+
sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
|
|
19525
|
+
BEFORE UPDATE ON qa_gate_profile
|
|
19526
|
+
WHEN OLD.locked_at IS NOT NULL
|
|
19527
|
+
BEGIN
|
|
19528
|
+
SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
|
|
19529
|
+
END`
|
|
19530
|
+
}
|
|
19531
|
+
];
|
|
19532
|
+
var _projectDbs = new Map;
|
|
19533
|
+
function runProjectMigrations(db) {
|
|
19534
|
+
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
19535
|
+
version INTEGER PRIMARY KEY,
|
|
19536
|
+
name TEXT NOT NULL,
|
|
19537
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
19538
|
+
)`);
|
|
19539
|
+
const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
19540
|
+
const currentVersion = row?.version ?? 0;
|
|
19541
|
+
for (const migration of MIGRATIONS) {
|
|
19542
|
+
if (migration.version <= currentVersion)
|
|
19543
|
+
continue;
|
|
19544
|
+
const apply = db.transaction(() => {
|
|
19545
|
+
db.run(migration.sql);
|
|
19546
|
+
db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
|
|
19547
|
+
migration.version,
|
|
19548
|
+
migration.name
|
|
19549
|
+
]);
|
|
19550
|
+
});
|
|
19551
|
+
apply();
|
|
19552
|
+
}
|
|
19553
|
+
}
|
|
19554
|
+
function projectDbPath(directory) {
|
|
19555
|
+
return join6(resolve4(directory), ".swarm", "swarm.db");
|
|
19556
|
+
}
|
|
19557
|
+
function projectDbExists(directory) {
|
|
19558
|
+
return existsSync4(projectDbPath(directory));
|
|
19559
|
+
}
|
|
19560
|
+
function getProjectDb(directory) {
|
|
19561
|
+
const key = resolve4(directory);
|
|
19562
|
+
const existing = _projectDbs.get(key);
|
|
19563
|
+
if (existing)
|
|
19564
|
+
return existing;
|
|
19565
|
+
const swarmDir = join6(key, ".swarm");
|
|
19566
|
+
mkdirSync3(swarmDir, { recursive: true });
|
|
19567
|
+
const db = new Database(join6(swarmDir, "swarm.db"));
|
|
19568
|
+
db.run("PRAGMA journal_mode = WAL;");
|
|
19569
|
+
db.run("PRAGMA synchronous = NORMAL;");
|
|
19570
|
+
db.run("PRAGMA busy_timeout = 5000;");
|
|
19571
|
+
db.run("PRAGMA foreign_keys = ON;");
|
|
19572
|
+
runProjectMigrations(db);
|
|
19573
|
+
_projectDbs.set(key, db);
|
|
19574
|
+
return db;
|
|
19575
|
+
}
|
|
19576
|
+
|
|
19577
|
+
// src/db/qa-gate-profile.ts
|
|
19578
|
+
var DEFAULT_QA_GATES = {
|
|
19579
|
+
reviewer: true,
|
|
19580
|
+
test_engineer: true,
|
|
19581
|
+
council_mode: false,
|
|
19582
|
+
sme_enabled: true,
|
|
19583
|
+
critic_pre_plan: true,
|
|
19584
|
+
hallucination_guard: false,
|
|
19585
|
+
sast_enabled: true
|
|
19586
|
+
};
|
|
19587
|
+
function rowToProfile(row) {
|
|
19588
|
+
let parsed = {};
|
|
19589
|
+
try {
|
|
19590
|
+
parsed = JSON.parse(row.gates);
|
|
19591
|
+
} catch {
|
|
19592
|
+
parsed = {};
|
|
19593
|
+
}
|
|
19594
|
+
const gates = { ...DEFAULT_QA_GATES, ...parsed };
|
|
19595
|
+
return {
|
|
19596
|
+
id: row.id,
|
|
19597
|
+
plan_id: row.plan_id,
|
|
19598
|
+
created_at: row.created_at,
|
|
19599
|
+
project_type: row.project_type,
|
|
19600
|
+
gates,
|
|
19601
|
+
locked_at: row.locked_at,
|
|
19602
|
+
locked_by_snapshot_seq: row.locked_by_snapshot_seq
|
|
19603
|
+
};
|
|
19604
|
+
}
|
|
19605
|
+
function getProfile(directory, planId) {
|
|
19606
|
+
if (!projectDbExists(directory))
|
|
19607
|
+
return null;
|
|
19608
|
+
const db = getProjectDb(directory);
|
|
19609
|
+
const row = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
|
|
19610
|
+
return row ? rowToProfile(row) : null;
|
|
19611
|
+
}
|
|
19612
|
+
function getOrCreateProfile(directory, planId, projectType) {
|
|
19613
|
+
const existing = getProfile(directory, planId);
|
|
19614
|
+
if (existing)
|
|
19615
|
+
return existing;
|
|
19616
|
+
const db = getProjectDb(directory);
|
|
19617
|
+
const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
|
|
19618
|
+
const insert = db.transaction(() => {
|
|
19619
|
+
db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
|
|
19620
|
+
});
|
|
19621
|
+
try {
|
|
19622
|
+
insert();
|
|
19623
|
+
} catch (err) {
|
|
19624
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19625
|
+
if (!msg.toLowerCase().includes("unique")) {
|
|
19626
|
+
throw err;
|
|
19627
|
+
}
|
|
19628
|
+
}
|
|
19629
|
+
const after = getProfile(directory, planId);
|
|
19630
|
+
if (!after) {
|
|
19631
|
+
throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
|
|
19632
|
+
}
|
|
19633
|
+
return after;
|
|
19634
|
+
}
|
|
19635
|
+
function setGates(directory, planId, gates) {
|
|
19636
|
+
const current = getProfile(directory, planId);
|
|
19637
|
+
if (!current) {
|
|
19638
|
+
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
|
|
19639
|
+
}
|
|
19640
|
+
if (current.locked_at !== null) {
|
|
19641
|
+
throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
|
|
19642
|
+
}
|
|
19643
|
+
const merged = { ...current.gates };
|
|
19644
|
+
for (const key of Object.keys(gates)) {
|
|
19645
|
+
const incoming = gates[key];
|
|
19646
|
+
if (incoming === undefined)
|
|
19647
|
+
continue;
|
|
19648
|
+
if (incoming === false && current.gates[key] === true) {
|
|
19649
|
+
throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
|
|
19650
|
+
}
|
|
19651
|
+
if (incoming === true) {
|
|
19652
|
+
merged[key] = true;
|
|
19653
|
+
}
|
|
19654
|
+
}
|
|
19655
|
+
const db = getProjectDb(directory);
|
|
19656
|
+
db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
|
|
19657
|
+
JSON.stringify(merged),
|
|
19658
|
+
planId
|
|
19659
|
+
]);
|
|
19660
|
+
const updated = getProfile(directory, planId);
|
|
19661
|
+
if (!updated) {
|
|
19662
|
+
throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
|
|
19663
|
+
}
|
|
19664
|
+
return updated;
|
|
19665
|
+
}
|
|
19666
|
+
function computeProfileHash(profile) {
|
|
19667
|
+
const payload = JSON.stringify({
|
|
19668
|
+
plan_id: profile.plan_id,
|
|
19669
|
+
gates: profile.gates
|
|
19670
|
+
});
|
|
19671
|
+
return createHash3("sha256").update(payload).digest("hex");
|
|
19672
|
+
}
|
|
19673
|
+
function getEffectiveGates(profile, sessionOverrides) {
|
|
19674
|
+
const merged = { ...profile.gates };
|
|
19675
|
+
for (const key of Object.keys(sessionOverrides)) {
|
|
19676
|
+
if (sessionOverrides[key] === true) {
|
|
19677
|
+
merged[key] = true;
|
|
19678
|
+
}
|
|
19679
|
+
}
|
|
19680
|
+
return merged;
|
|
19681
|
+
}
|
|
19682
|
+
|
|
19474
19683
|
// src/hooks/delegation-gate.ts
|
|
19475
19684
|
init_telemetry();
|
|
19476
19685
|
|
|
@@ -19924,8 +20133,10 @@ function clearPendingCoderScope() {
|
|
|
19924
20133
|
}
|
|
19925
20134
|
|
|
19926
20135
|
// src/state.ts
|
|
20136
|
+
init_manager();
|
|
19927
20137
|
init_telemetry();
|
|
19928
20138
|
var _rehydrationCache = null;
|
|
20139
|
+
var _councilDisagreementWarned = new Set;
|
|
19929
20140
|
var swarmState = {
|
|
19930
20141
|
activeToolCalls: new Map,
|
|
19931
20142
|
toolAggregates: new Map,
|
|
@@ -19957,6 +20168,7 @@ function resetSwarmState() {
|
|
|
19957
20168
|
swarmState.fullAutoEnabledInConfig = false;
|
|
19958
20169
|
swarmState.environmentProfiles.clear();
|
|
19959
20170
|
clearPendingCoderScope();
|
|
20171
|
+
_councilDisagreementWarned.clear();
|
|
19960
20172
|
}
|
|
19961
20173
|
function getAgentSession(sessionId) {
|
|
19962
20174
|
return swarmState.agentSessions.get(sessionId);
|
|
@@ -33132,7 +33344,7 @@ function hasUncommittedChanges(cwd) {
|
|
|
33132
33344
|
|
|
33133
33345
|
// src/hooks/knowledge-store.ts
|
|
33134
33346
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
33135
|
-
import { existsSync as
|
|
33347
|
+
import { existsSync as existsSync6 } from "fs";
|
|
33136
33348
|
import { appendFile as appendFile2, mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
33137
33349
|
import * as os2 from "os";
|
|
33138
33350
|
import * as path8 from "path";
|
|
@@ -33160,7 +33372,7 @@ function resolveHiveRejectedPath() {
|
|
|
33160
33372
|
return path8.join(path8.dirname(hivePath), "shared-learnings-rejected.jsonl");
|
|
33161
33373
|
}
|
|
33162
33374
|
async function readKnowledge(filePath) {
|
|
33163
|
-
if (!
|
|
33375
|
+
if (!existsSync6(filePath))
|
|
33164
33376
|
return [];
|
|
33165
33377
|
const content = await readFile2(filePath, "utf-8");
|
|
33166
33378
|
const results = [];
|
|
@@ -33793,7 +34005,7 @@ async function writeCheckpoint(directory) {
|
|
|
33793
34005
|
|
|
33794
34006
|
// src/session/snapshot-writer.ts
|
|
33795
34007
|
init_utils2();
|
|
33796
|
-
import { mkdirSync as
|
|
34008
|
+
import { mkdirSync as mkdirSync6, renameSync as renameSync5 } from "fs";
|
|
33797
34009
|
import * as path11 from "path";
|
|
33798
34010
|
init_utils();
|
|
33799
34011
|
var _writeInFlight = Promise.resolve();
|
|
@@ -33886,7 +34098,7 @@ async function writeSnapshot(directory, state) {
|
|
|
33886
34098
|
const content = JSON.stringify(snapshot, null, 2);
|
|
33887
34099
|
const resolvedPath = validateSwarmPath(directory, "session/state.json");
|
|
33888
34100
|
const dir = path11.dirname(resolvedPath);
|
|
33889
|
-
|
|
34101
|
+
mkdirSync6(dir, { recursive: true });
|
|
33890
34102
|
const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
33891
34103
|
await Bun.write(tempPath, content);
|
|
33892
34104
|
renameSync5(tempPath, resolvedPath);
|
|
@@ -35384,7 +35596,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
35384
35596
|
|
|
35385
35597
|
// src/services/diagnose-service.ts
|
|
35386
35598
|
import * as child_process4 from "child_process";
|
|
35387
|
-
import { existsSync as
|
|
35599
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
35388
35600
|
import path16 from "path";
|
|
35389
35601
|
import { fileURLToPath } from "url";
|
|
35390
35602
|
init_manager2();
|
|
@@ -35621,7 +35833,7 @@ async function checkConfigBackups(directory) {
|
|
|
35621
35833
|
}
|
|
35622
35834
|
async function checkGitRepository(directory) {
|
|
35623
35835
|
try {
|
|
35624
|
-
if (!
|
|
35836
|
+
if (!existsSync7(directory) || !statSync4(directory).isDirectory()) {
|
|
35625
35837
|
return {
|
|
35626
35838
|
name: "Git Repository",
|
|
35627
35839
|
status: "\u274C",
|
|
@@ -35686,7 +35898,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
35686
35898
|
}
|
|
35687
35899
|
async function checkConfigParseability(directory) {
|
|
35688
35900
|
const configPath = path16.join(directory, ".opencode/opencode-swarm.json");
|
|
35689
|
-
if (!
|
|
35901
|
+
if (!existsSync7(configPath)) {
|
|
35690
35902
|
return {
|
|
35691
35903
|
name: "Config Parseability",
|
|
35692
35904
|
status: "\u2705",
|
|
@@ -35736,11 +35948,11 @@ async function checkGrammarWasmFiles() {
|
|
|
35736
35948
|
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
35737
35949
|
const grammarDir = isSource ? path16.join(thisDir, "..", "lang", "grammars") : path16.join(thisDir, "lang", "grammars");
|
|
35738
35950
|
const missing = [];
|
|
35739
|
-
if (!
|
|
35951
|
+
if (!existsSync7(path16.join(grammarDir, "tree-sitter.wasm"))) {
|
|
35740
35952
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
35741
35953
|
}
|
|
35742
35954
|
for (const file3 of grammarFiles) {
|
|
35743
|
-
if (!
|
|
35955
|
+
if (!existsSync7(path16.join(grammarDir, file3))) {
|
|
35744
35956
|
missing.push(file3);
|
|
35745
35957
|
}
|
|
35746
35958
|
}
|
|
@@ -35759,7 +35971,7 @@ async function checkGrammarWasmFiles() {
|
|
|
35759
35971
|
}
|
|
35760
35972
|
async function checkCheckpointManifest(directory) {
|
|
35761
35973
|
const manifestPath = path16.join(directory, ".swarm/checkpoints.json");
|
|
35762
|
-
if (!
|
|
35974
|
+
if (!existsSync7(manifestPath)) {
|
|
35763
35975
|
return {
|
|
35764
35976
|
name: "Checkpoint Manifest",
|
|
35765
35977
|
status: "\u2705",
|
|
@@ -35811,7 +36023,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
35811
36023
|
}
|
|
35812
36024
|
async function checkEventStreamIntegrity(directory) {
|
|
35813
36025
|
const eventsPath = path16.join(directory, ".swarm/events.jsonl");
|
|
35814
|
-
if (!
|
|
36026
|
+
if (!existsSync7(eventsPath)) {
|
|
35815
36027
|
return {
|
|
35816
36028
|
name: "Event Stream",
|
|
35817
36029
|
status: "\u2705",
|
|
@@ -35852,7 +36064,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
35852
36064
|
}
|
|
35853
36065
|
async function checkSteeringDirectives(directory) {
|
|
35854
36066
|
const eventsPath = path16.join(directory, ".swarm/events.jsonl");
|
|
35855
|
-
if (!
|
|
36067
|
+
if (!existsSync7(eventsPath)) {
|
|
35856
36068
|
return {
|
|
35857
36069
|
name: "Steering Directives",
|
|
35858
36070
|
status: "\u2705",
|
|
@@ -35908,7 +36120,7 @@ async function checkCurator(directory) {
|
|
|
35908
36120
|
};
|
|
35909
36121
|
}
|
|
35910
36122
|
const summaryPath = path16.join(directory, ".swarm/curator-summary.json");
|
|
35911
|
-
if (!
|
|
36123
|
+
if (!existsSync7(summaryPath)) {
|
|
35912
36124
|
return {
|
|
35913
36125
|
name: "Curator",
|
|
35914
36126
|
status: "\u2705",
|
|
@@ -36056,7 +36268,7 @@ async function getDiagnoseData(directory) {
|
|
|
36056
36268
|
checks5.push(await checkCurator(directory));
|
|
36057
36269
|
try {
|
|
36058
36270
|
const evidenceDir = path16.join(directory, ".swarm", "evidence");
|
|
36059
|
-
const snapshotFiles =
|
|
36271
|
+
const snapshotFiles = existsSync7(evidenceDir) ? readdirSync3(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
|
|
36060
36272
|
if (snapshotFiles.length > 0) {
|
|
36061
36273
|
const latest = snapshotFiles.sort().pop();
|
|
36062
36274
|
checks5.push({
|
|
@@ -36116,7 +36328,7 @@ import * as path18 from "path";
|
|
|
36116
36328
|
|
|
36117
36329
|
// src/lang/detector.ts
|
|
36118
36330
|
import { access as access2, readdir as readdir2 } from "fs/promises";
|
|
36119
|
-
import { extname as extname2, join as
|
|
36331
|
+
import { extname as extname2, join as join15 } from "path";
|
|
36120
36332
|
|
|
36121
36333
|
// src/lang/profiles.ts
|
|
36122
36334
|
class LanguageRegistry {
|
|
@@ -37096,7 +37308,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
37096
37308
|
if (detectFile.includes("*") || detectFile.includes("?"))
|
|
37097
37309
|
continue;
|
|
37098
37310
|
try {
|
|
37099
|
-
await access2(
|
|
37311
|
+
await access2(join15(dir, detectFile));
|
|
37100
37312
|
detected.add(profile.id);
|
|
37101
37313
|
break;
|
|
37102
37314
|
} catch {}
|
|
@@ -37117,7 +37329,7 @@ async function detectProjectLanguages(projectDir) {
|
|
|
37117
37329
|
const topEntries = await readdir2(projectDir, { withFileTypes: true });
|
|
37118
37330
|
for (const entry of topEntries) {
|
|
37119
37331
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
37120
|
-
await scanDir(
|
|
37332
|
+
await scanDir(join15(projectDir, entry.name));
|
|
37121
37333
|
}
|
|
37122
37334
|
}
|
|
37123
37335
|
} catch {}
|
|
@@ -38453,14 +38665,14 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
38453
38665
|
}
|
|
38454
38666
|
// src/hooks/knowledge-migrator.ts
|
|
38455
38667
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
38456
|
-
import { existsSync as
|
|
38668
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
38457
38669
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
38458
38670
|
import * as path20 from "path";
|
|
38459
38671
|
async function migrateContextToKnowledge(directory, config3) {
|
|
38460
38672
|
const sentinelPath = path20.join(directory, ".swarm", ".knowledge-migrated");
|
|
38461
38673
|
const contextPath = path20.join(directory, ".swarm", "context.md");
|
|
38462
38674
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
38463
|
-
if (
|
|
38675
|
+
if (existsSync11(sentinelPath)) {
|
|
38464
38676
|
return {
|
|
38465
38677
|
migrated: false,
|
|
38466
38678
|
entriesMigrated: 0,
|
|
@@ -38469,7 +38681,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
38469
38681
|
skippedReason: "sentinel-exists"
|
|
38470
38682
|
};
|
|
38471
38683
|
}
|
|
38472
|
-
if (!
|
|
38684
|
+
if (!existsSync11(contextPath)) {
|
|
38473
38685
|
return {
|
|
38474
38686
|
migrated: false,
|
|
38475
38687
|
entriesMigrated: 0,
|
|
@@ -38655,7 +38867,7 @@ function truncateLesson(text) {
|
|
|
38655
38867
|
}
|
|
38656
38868
|
function inferProjectName(directory) {
|
|
38657
38869
|
const packageJsonPath = path20.join(directory, "package.json");
|
|
38658
|
-
if (
|
|
38870
|
+
if (existsSync11(packageJsonPath)) {
|
|
38659
38871
|
try {
|
|
38660
38872
|
const pkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
|
|
38661
38873
|
if (pkg.name && typeof pkg.name === "string") {
|
|
@@ -39163,7 +39375,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
39163
39375
|
stderr: "pipe"
|
|
39164
39376
|
});
|
|
39165
39377
|
const biomeExit = biomeProc.exited;
|
|
39166
|
-
const timeout = new Promise((
|
|
39378
|
+
const timeout = new Promise((resolve8) => setTimeout(() => resolve8("timeout"), DETECT_TIMEOUT));
|
|
39167
39379
|
const result = await Promise.race([biomeExit, timeout]);
|
|
39168
39380
|
if (result === "timeout") {
|
|
39169
39381
|
biomeProc.kill();
|
|
@@ -39177,7 +39389,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
39177
39389
|
stderr: "pipe"
|
|
39178
39390
|
});
|
|
39179
39391
|
const eslintExit = eslintProc.exited;
|
|
39180
|
-
const timeout = new Promise((
|
|
39392
|
+
const timeout = new Promise((resolve8) => setTimeout(() => resolve8("timeout"), DETECT_TIMEOUT));
|
|
39181
39393
|
const result = await Promise.race([eslintExit, timeout]);
|
|
39182
39394
|
if (result === "timeout") {
|
|
39183
39395
|
eslintProc.kill();
|
|
@@ -40566,15 +40778,15 @@ function appendTestRun(record3, workingDir) {
|
|
|
40566
40778
|
prunedRecords.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
40567
40779
|
try {
|
|
40568
40780
|
const lines = prunedRecords.map((rec) => JSON.stringify(rec));
|
|
40569
|
-
const content = lines.join(`
|
|
40570
|
-
`)
|
|
40781
|
+
const content = `${lines.join(`
|
|
40782
|
+
`)}
|
|
40571
40783
|
`;
|
|
40572
|
-
const tempPath = historyPath
|
|
40784
|
+
const tempPath = `${historyPath}.tmp`;
|
|
40573
40785
|
fs14.writeFileSync(tempPath, content, "utf-8");
|
|
40574
40786
|
fs14.renameSync(tempPath, historyPath);
|
|
40575
40787
|
} catch (err) {
|
|
40576
40788
|
try {
|
|
40577
|
-
const tempPath = historyPath
|
|
40789
|
+
const tempPath = `${historyPath}.tmp`;
|
|
40578
40790
|
if (fs14.existsSync(tempPath)) {
|
|
40579
40791
|
fs14.unlinkSync(tempPath);
|
|
40580
40792
|
}
|
|
@@ -41396,9 +41608,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
41396
41608
|
stderr: "pipe",
|
|
41397
41609
|
cwd
|
|
41398
41610
|
});
|
|
41399
|
-
const timeoutPromise = new Promise((
|
|
41611
|
+
const timeoutPromise = new Promise((resolve11) => setTimeout(() => {
|
|
41400
41612
|
proc.kill();
|
|
41401
|
-
|
|
41613
|
+
resolve11(-1);
|
|
41402
41614
|
}, timeout_ms));
|
|
41403
41615
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
41404
41616
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -42627,199 +42839,6 @@ async function handlePromoteCommand(directory, args) {
|
|
|
42627
42839
|
}
|
|
42628
42840
|
}
|
|
42629
42841
|
|
|
42630
|
-
// src/db/qa-gate-profile.ts
|
|
42631
|
-
import { createHash as createHash4 } from "crypto";
|
|
42632
|
-
|
|
42633
|
-
// src/db/project-db.ts
|
|
42634
|
-
import { Database } from "bun:sqlite";
|
|
42635
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8 } from "fs";
|
|
42636
|
-
import { join as join23, resolve as resolve11 } from "path";
|
|
42637
|
-
var MIGRATIONS = [
|
|
42638
|
-
{
|
|
42639
|
-
version: 1,
|
|
42640
|
-
name: "create_project_constraints",
|
|
42641
|
-
sql: `CREATE TABLE project_constraints (
|
|
42642
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42643
|
-
constraint_type TEXT NOT NULL,
|
|
42644
|
-
content TEXT NOT NULL,
|
|
42645
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42646
|
-
)`
|
|
42647
|
-
},
|
|
42648
|
-
{
|
|
42649
|
-
version: 2,
|
|
42650
|
-
name: "create_qa_gate_profile",
|
|
42651
|
-
sql: `CREATE TABLE qa_gate_profile (
|
|
42652
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42653
|
-
plan_id TEXT NOT NULL UNIQUE,
|
|
42654
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42655
|
-
project_type TEXT,
|
|
42656
|
-
gates TEXT NOT NULL DEFAULT '{}',
|
|
42657
|
-
locked_at TEXT,
|
|
42658
|
-
locked_by_snapshot_seq INTEGER
|
|
42659
|
-
)`
|
|
42660
|
-
},
|
|
42661
|
-
{
|
|
42662
|
-
version: 3,
|
|
42663
|
-
name: "create_qa_gate_profile_immutability_trigger",
|
|
42664
|
-
sql: `CREATE TRIGGER IF NOT EXISTS trg_qa_gate_profile_no_update_after_lock
|
|
42665
|
-
BEFORE UPDATE ON qa_gate_profile
|
|
42666
|
-
WHEN OLD.locked_at IS NOT NULL
|
|
42667
|
-
BEGIN
|
|
42668
|
-
SELECT RAISE(ABORT, 'qa_gate_profile row is locked and cannot be modified after critic approval');
|
|
42669
|
-
END`
|
|
42670
|
-
}
|
|
42671
|
-
];
|
|
42672
|
-
var _projectDbs = new Map;
|
|
42673
|
-
function runProjectMigrations(db) {
|
|
42674
|
-
db.run(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
42675
|
-
version INTEGER PRIMARY KEY,
|
|
42676
|
-
name TEXT NOT NULL,
|
|
42677
|
-
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42678
|
-
)`);
|
|
42679
|
-
const row = db.query("SELECT MAX(version) as version FROM schema_migrations").get();
|
|
42680
|
-
const currentVersion = row?.version ?? 0;
|
|
42681
|
-
for (const migration of MIGRATIONS) {
|
|
42682
|
-
if (migration.version <= currentVersion)
|
|
42683
|
-
continue;
|
|
42684
|
-
const apply = db.transaction(() => {
|
|
42685
|
-
db.run(migration.sql);
|
|
42686
|
-
db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
|
|
42687
|
-
migration.version,
|
|
42688
|
-
migration.name
|
|
42689
|
-
]);
|
|
42690
|
-
});
|
|
42691
|
-
apply();
|
|
42692
|
-
}
|
|
42693
|
-
}
|
|
42694
|
-
function projectDbPath(directory) {
|
|
42695
|
-
return join23(resolve11(directory), ".swarm", "swarm.db");
|
|
42696
|
-
}
|
|
42697
|
-
function projectDbExists(directory) {
|
|
42698
|
-
return existsSync16(projectDbPath(directory));
|
|
42699
|
-
}
|
|
42700
|
-
function getProjectDb(directory) {
|
|
42701
|
-
const key = resolve11(directory);
|
|
42702
|
-
const existing = _projectDbs.get(key);
|
|
42703
|
-
if (existing)
|
|
42704
|
-
return existing;
|
|
42705
|
-
const swarmDir = join23(key, ".swarm");
|
|
42706
|
-
mkdirSync8(swarmDir, { recursive: true });
|
|
42707
|
-
const db = new Database(join23(swarmDir, "swarm.db"));
|
|
42708
|
-
db.run("PRAGMA journal_mode = WAL;");
|
|
42709
|
-
db.run("PRAGMA synchronous = NORMAL;");
|
|
42710
|
-
db.run("PRAGMA busy_timeout = 5000;");
|
|
42711
|
-
db.run("PRAGMA foreign_keys = ON;");
|
|
42712
|
-
runProjectMigrations(db);
|
|
42713
|
-
_projectDbs.set(key, db);
|
|
42714
|
-
return db;
|
|
42715
|
-
}
|
|
42716
|
-
|
|
42717
|
-
// src/db/qa-gate-profile.ts
|
|
42718
|
-
var DEFAULT_QA_GATES = {
|
|
42719
|
-
reviewer: true,
|
|
42720
|
-
test_engineer: true,
|
|
42721
|
-
council_mode: false,
|
|
42722
|
-
sme_enabled: true,
|
|
42723
|
-
critic_pre_plan: true,
|
|
42724
|
-
hallucination_guard: false,
|
|
42725
|
-
sast_enabled: true
|
|
42726
|
-
};
|
|
42727
|
-
function rowToProfile(row) {
|
|
42728
|
-
let parsed = {};
|
|
42729
|
-
try {
|
|
42730
|
-
parsed = JSON.parse(row.gates);
|
|
42731
|
-
} catch {
|
|
42732
|
-
parsed = {};
|
|
42733
|
-
}
|
|
42734
|
-
const gates = { ...DEFAULT_QA_GATES, ...parsed };
|
|
42735
|
-
return {
|
|
42736
|
-
id: row.id,
|
|
42737
|
-
plan_id: row.plan_id,
|
|
42738
|
-
created_at: row.created_at,
|
|
42739
|
-
project_type: row.project_type,
|
|
42740
|
-
gates,
|
|
42741
|
-
locked_at: row.locked_at,
|
|
42742
|
-
locked_by_snapshot_seq: row.locked_by_snapshot_seq
|
|
42743
|
-
};
|
|
42744
|
-
}
|
|
42745
|
-
function getProfile(directory, planId) {
|
|
42746
|
-
if (!projectDbExists(directory))
|
|
42747
|
-
return null;
|
|
42748
|
-
const db = getProjectDb(directory);
|
|
42749
|
-
const row = db.query("SELECT * FROM qa_gate_profile WHERE plan_id = ?").get(planId);
|
|
42750
|
-
return row ? rowToProfile(row) : null;
|
|
42751
|
-
}
|
|
42752
|
-
function getOrCreateProfile(directory, planId, projectType) {
|
|
42753
|
-
const existing = getProfile(directory, planId);
|
|
42754
|
-
if (existing)
|
|
42755
|
-
return existing;
|
|
42756
|
-
const db = getProjectDb(directory);
|
|
42757
|
-
const gatesJson = JSON.stringify(DEFAULT_QA_GATES);
|
|
42758
|
-
const insert = db.transaction(() => {
|
|
42759
|
-
db.run("INSERT INTO qa_gate_profile (plan_id, project_type, gates) VALUES (?, ?, ?)", [planId, projectType ?? null, gatesJson]);
|
|
42760
|
-
});
|
|
42761
|
-
try {
|
|
42762
|
-
insert();
|
|
42763
|
-
} catch (err) {
|
|
42764
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
42765
|
-
if (!msg.toLowerCase().includes("unique")) {
|
|
42766
|
-
throw err;
|
|
42767
|
-
}
|
|
42768
|
-
}
|
|
42769
|
-
const after = getProfile(directory, planId);
|
|
42770
|
-
if (!after) {
|
|
42771
|
-
throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
|
|
42772
|
-
}
|
|
42773
|
-
return after;
|
|
42774
|
-
}
|
|
42775
|
-
function setGates(directory, planId, gates) {
|
|
42776
|
-
const current = getProfile(directory, planId);
|
|
42777
|
-
if (!current) {
|
|
42778
|
-
throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
|
|
42779
|
-
}
|
|
42780
|
-
if (current.locked_at !== null) {
|
|
42781
|
-
throw new Error("Cannot modify gates: QA gate profile is locked after critic approval");
|
|
42782
|
-
}
|
|
42783
|
-
const merged = { ...current.gates };
|
|
42784
|
-
for (const key of Object.keys(gates)) {
|
|
42785
|
-
const incoming = gates[key];
|
|
42786
|
-
if (incoming === undefined)
|
|
42787
|
-
continue;
|
|
42788
|
-
if (incoming === false && current.gates[key] === true) {
|
|
42789
|
-
throw new Error(`Cannot disable gate '${key}': sessions can only ratchet tighter`);
|
|
42790
|
-
}
|
|
42791
|
-
if (incoming === true) {
|
|
42792
|
-
merged[key] = true;
|
|
42793
|
-
}
|
|
42794
|
-
}
|
|
42795
|
-
const db = getProjectDb(directory);
|
|
42796
|
-
db.run("UPDATE qa_gate_profile SET gates = ? WHERE plan_id = ?", [
|
|
42797
|
-
JSON.stringify(merged),
|
|
42798
|
-
planId
|
|
42799
|
-
]);
|
|
42800
|
-
const updated = getProfile(directory, planId);
|
|
42801
|
-
if (!updated) {
|
|
42802
|
-
throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
|
|
42803
|
-
}
|
|
42804
|
-
return updated;
|
|
42805
|
-
}
|
|
42806
|
-
function computeProfileHash(profile) {
|
|
42807
|
-
const payload = JSON.stringify({
|
|
42808
|
-
plan_id: profile.plan_id,
|
|
42809
|
-
gates: profile.gates
|
|
42810
|
-
});
|
|
42811
|
-
return createHash4("sha256").update(payload).digest("hex");
|
|
42812
|
-
}
|
|
42813
|
-
function getEffectiveGates(profile, sessionOverrides) {
|
|
42814
|
-
const merged = { ...profile.gates };
|
|
42815
|
-
for (const key of Object.keys(sessionOverrides)) {
|
|
42816
|
-
if (sessionOverrides[key] === true) {
|
|
42817
|
-
merged[key] = true;
|
|
42818
|
-
}
|
|
42819
|
-
}
|
|
42820
|
-
return merged;
|
|
42821
|
-
}
|
|
42822
|
-
|
|
42823
42842
|
// src/commands/qa-gates.ts
|
|
42824
42843
|
init_manager();
|
|
42825
42844
|
var ALL_GATE_NAMES = [
|