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