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/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((resolve4, reject) => {
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
- resolve4(result);
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 existsSync5 } from "fs";
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 (!existsSync5(filePath))
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 mkdirSync5, renameSync as renameSync5 } from "fs";
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
- mkdirSync5(dir, { recursive: true });
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 existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
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 (!existsSync6(directory) || !statSync4(directory).isDirectory()) {
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 (!existsSync6(configPath)) {
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 (!existsSync6(path16.join(grammarDir, "tree-sitter.wasm"))) {
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 (!existsSync6(path16.join(grammarDir, file3))) {
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 (!existsSync6(manifestPath)) {
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 (!existsSync6(eventsPath)) {
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 (!existsSync6(eventsPath)) {
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 (!existsSync6(summaryPath)) {
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 = existsSync6(evidenceDir) ? readdirSync3(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
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 join14 } from "path";
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(join14(dir, detectFile));
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(join14(projectDir, entry.name));
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 existsSync10, readFileSync as readFileSync10 } from "fs";
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 (existsSync10(sentinelPath)) {
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 (!existsSync10(contextPath)) {
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 (existsSync10(packageJsonPath)) {
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((resolve7) => setTimeout(() => resolve7("timeout"), DETECT_TIMEOUT));
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((resolve7) => setTimeout(() => resolve7("timeout"), DETECT_TIMEOUT));
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 + ".tmp";
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 + ".tmp";
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((resolve10) => setTimeout(() => {
41611
+ const timeoutPromise = new Promise((resolve11) => setTimeout(() => {
41400
41612
  proc.kill();
41401
- resolve10(-1);
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 = [