@oxagen/cli 0.6.2 → 0.7.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.
Files changed (2) hide show
  1. package/oxagen.mjs +1709 -293
  2. package/package.json +2 -2
package/oxagen.mjs CHANGED
@@ -3039,7 +3039,7 @@ var init_package = __esm({
3039
3039
  "package.json"() {
3040
3040
  package_default = {
3041
3041
  name: "@oxagen/cli",
3042
- version: "0.6.2",
3042
+ version: "0.7.0",
3043
3043
  description: "Oxagen CLI \u2014 agentic coding powered by Oxagen's knowledge-graph context engine",
3044
3044
  type: "module",
3045
3045
  main: "./dist/index.js",
@@ -21479,7 +21479,7 @@ var init_dist3 = __esm({
21479
21479
  {}
21480
21480
  ) : void 0;
21481
21481
  const name24 = typeof options === "string" ? options : (options == null ? void 0 : options.nameStrategy) === "title" ? void 0 : options == null ? void 0 : options.name;
21482
- const main2 = (_a24 = parseDef(
21482
+ const main3 = (_a24 = parseDef(
21483
21483
  schema._def,
21484
21484
  name24 === void 0 ? refs : {
21485
21485
  ...refs,
@@ -21489,12 +21489,12 @@ var init_dist3 = __esm({
21489
21489
  )) != null ? _a24 : parseAnyDef();
21490
21490
  const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0;
21491
21491
  if (title !== void 0) {
21492
- main2.title = title;
21492
+ main3.title = title;
21493
21493
  }
21494
21494
  const combined = name24 === void 0 ? definitions ? {
21495
- ...main2,
21495
+ ...main3,
21496
21496
  [refs.definitionPath]: definitions
21497
- } : main2 : {
21497
+ } : main3 : {
21498
21498
  $ref: [
21499
21499
  ...refs.$refStrategy === "relative" ? [] : refs.basePath,
21500
21500
  refs.definitionPath,
@@ -21502,7 +21502,7 @@ var init_dist3 = __esm({
21502
21502
  ].join("/"),
21503
21503
  [refs.definitionPath]: {
21504
21504
  ...definitions,
21505
- [name24]: main2
21505
+ [name24]: main3
21506
21506
  }
21507
21507
  };
21508
21508
  combined.$schema = "http://json-schema.org/draft-07/schema#";
@@ -32595,51 +32595,382 @@ var init_dist5 = __esm({
32595
32595
  }
32596
32596
  });
32597
32597
 
32598
+ // src/daemon/code-graph/store.ts
32599
+ var store_exports = {};
32600
+ __export(store_exports, {
32601
+ CodeGraphStore: () => CodeGraphStore,
32602
+ computeEdgeId: () => computeEdgeId,
32603
+ createCodeGraphStore: () => createCodeGraphStore,
32604
+ defaultCodeGraphDbPath: () => defaultCodeGraphDbPath,
32605
+ hashContent: () => hashContent
32606
+ });
32607
+ import { createHash } from "node:crypto";
32608
+ import { homedir } from "node:os";
32609
+ import { join } from "node:path";
32610
+ function defaultCodeGraphDbPath() {
32611
+ return join(homedir(), ".config", "oxagen", "code-graph.duckdb");
32612
+ }
32613
+ function computeEdgeId(source, target2, type) {
32614
+ return createHash("sha256").update(`${source}:${target2}:${type}`).digest("hex").slice(0, 32);
32615
+ }
32616
+ function hashContent(content) {
32617
+ return createHash("sha256").update(content).digest("hex");
32618
+ }
32619
+ function rowToNode(row) {
32620
+ return {
32621
+ id: row["id"],
32622
+ kind: row["kind"],
32623
+ name: row["name"],
32624
+ path: row["path"],
32625
+ range: { start: Number(row["range_start"]), end: Number(row["range_end"]) },
32626
+ language: row["language"],
32627
+ signature: row["signature"] ?? void 0,
32628
+ docstring: row["docstring"] ?? void 0
32629
+ };
32630
+ }
32631
+ function createCodeGraphStore(opts) {
32632
+ return new CodeGraphStore(opts);
32633
+ }
32634
+ var CREATE_NODES_SQL, CREATE_EDGES_SQL, CREATE_FILES_SQL, CodeGraphStore;
32635
+ var init_store = __esm({
32636
+ "src/daemon/code-graph/store.ts"() {
32637
+ CREATE_NODES_SQL = `
32638
+ CREATE TABLE IF NOT EXISTS code_nodes (
32639
+ root VARCHAR NOT NULL,
32640
+ id VARCHAR NOT NULL,
32641
+ kind VARCHAR NOT NULL,
32642
+ name VARCHAR NOT NULL,
32643
+ path VARCHAR NOT NULL,
32644
+ range_start INTEGER NOT NULL,
32645
+ range_end INTEGER NOT NULL,
32646
+ language VARCHAR NOT NULL,
32647
+ signature VARCHAR,
32648
+ docstring VARCHAR,
32649
+ PRIMARY KEY (root, id)
32650
+ )
32651
+ `;
32652
+ CREATE_EDGES_SQL = `
32653
+ CREATE TABLE IF NOT EXISTS code_edges (
32654
+ root VARCHAR NOT NULL,
32655
+ id VARCHAR NOT NULL,
32656
+ file_path VARCHAR NOT NULL,
32657
+ source VARCHAR NOT NULL,
32658
+ target VARCHAR NOT NULL,
32659
+ type VARCHAR NOT NULL,
32660
+ PRIMARY KEY (root, id)
32661
+ )
32662
+ `;
32663
+ CREATE_FILES_SQL = `
32664
+ CREATE TABLE IF NOT EXISTS code_files (
32665
+ root VARCHAR NOT NULL,
32666
+ path VARCHAR NOT NULL,
32667
+ content_hash VARCHAR NOT NULL,
32668
+ indexed_at BIGINT NOT NULL,
32669
+ PRIMARY KEY (root, path)
32670
+ )
32671
+ `;
32672
+ CodeGraphStore = class {
32673
+ db;
32674
+ conn;
32675
+ ready;
32676
+ constructor(opts) {
32677
+ const duckdb = __require("duckdb");
32678
+ this.db = new duckdb.Database(opts.duckdbPath);
32679
+ this.conn = this.db.connect();
32680
+ this.ready = this.initialize();
32681
+ void this.ready.catch(() => {
32682
+ });
32683
+ }
32684
+ initialize() {
32685
+ return new Promise((resolve6, reject) => {
32686
+ this.conn.run(CREATE_NODES_SQL, (e1) => {
32687
+ if (e1) return reject(e1);
32688
+ this.conn.run(CREATE_EDGES_SQL, (e2) => {
32689
+ if (e2) return reject(e2);
32690
+ this.conn.run(CREATE_FILES_SQL, (e3) => {
32691
+ if (e3) return reject(e3);
32692
+ resolve6();
32693
+ });
32694
+ });
32695
+ });
32696
+ });
32697
+ }
32698
+ /** Resolve once the schema is ready (or reject if the DB could not open). */
32699
+ whenReady() {
32700
+ return this.ready;
32701
+ }
32702
+ runSql(sql, params = []) {
32703
+ return new Promise((resolve6, reject) => {
32704
+ this.conn.run(sql, ...params, (err) => {
32705
+ if (err) reject(err);
32706
+ else resolve6();
32707
+ });
32708
+ });
32709
+ }
32710
+ querySql(sql, params = []) {
32711
+ return new Promise((resolve6, reject) => {
32712
+ this.conn.all(sql, ...params, (err, rows) => {
32713
+ if (err) reject(err);
32714
+ else resolve6(rows ?? []);
32715
+ });
32716
+ });
32717
+ }
32718
+ // -------------------------------------------------------------------------
32719
+ // Incremental write
32720
+ // -------------------------------------------------------------------------
32721
+ /** Stored content hash for a file, or null if never indexed. */
32722
+ async fileHash(root, relPath) {
32723
+ await this.ready;
32724
+ const rows = await this.querySql(
32725
+ "SELECT content_hash FROM code_files WHERE root = ? AND path = ?",
32726
+ [root, relPath]
32727
+ );
32728
+ return rows.length ? rows[0]["content_hash"] : null;
32729
+ }
32730
+ /** All file paths currently indexed for a root (used to detect deletions). */
32731
+ async indexedFiles(root) {
32732
+ await this.ready;
32733
+ const rows = await this.querySql("SELECT path FROM code_files WHERE root = ?", [root]);
32734
+ return rows.map((r) => r["path"]);
32735
+ }
32736
+ /**
32737
+ * Replace one file's nodes/edges atomically. Deletes the file's prior nodes
32738
+ * (matched by `path`) and the edges it declared (matched by `file_path`),
32739
+ * inserts the new subgraph, and records the content hash. Idempotent: calling
32740
+ * twice with the same hash yields the same rows.
32741
+ */
32742
+ async replaceFile(root, relPath, fileGraph) {
32743
+ await this.ready;
32744
+ await this.runSql("BEGIN TRANSACTION");
32745
+ try {
32746
+ await this.runSql("DELETE FROM code_nodes WHERE root = ? AND path = ?", [root, relPath]);
32747
+ await this.runSql("DELETE FROM code_edges WHERE root = ? AND file_path = ?", [root, relPath]);
32748
+ for (const n of fileGraph.nodes) {
32749
+ await this.runSql(
32750
+ `INSERT INTO code_nodes
32751
+ (root, id, kind, name, path, range_start, range_end, language, signature, docstring)
32752
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
32753
+ ON CONFLICT (root, id) DO UPDATE SET
32754
+ kind = excluded.kind, name = excluded.name, path = excluded.path,
32755
+ range_start = excluded.range_start, range_end = excluded.range_end,
32756
+ language = excluded.language, signature = excluded.signature,
32757
+ docstring = excluded.docstring`,
32758
+ [
32759
+ root,
32760
+ n.id,
32761
+ n.kind,
32762
+ n.name,
32763
+ n.path,
32764
+ n.range.start,
32765
+ n.range.end,
32766
+ n.language,
32767
+ n.signature ?? null,
32768
+ n.docstring ?? null
32769
+ ]
32770
+ );
32771
+ }
32772
+ for (const e of fileGraph.edges) {
32773
+ const id = computeEdgeId(e.source, e.target, e.type);
32774
+ await this.runSql(
32775
+ `INSERT INTO code_edges (root, id, file_path, source, target, type)
32776
+ VALUES (?, ?, ?, ?, ?, ?)
32777
+ ON CONFLICT (root, id) DO UPDATE SET
32778
+ file_path = excluded.file_path, source = excluded.source,
32779
+ target = excluded.target, type = excluded.type`,
32780
+ [root, id, relPath, e.source, e.target, e.type]
32781
+ );
32782
+ }
32783
+ await this.runSql(
32784
+ `INSERT INTO code_files (root, path, content_hash, indexed_at)
32785
+ VALUES (?, ?, ?, ?)
32786
+ ON CONFLICT (root, path) DO UPDATE SET
32787
+ content_hash = excluded.content_hash, indexed_at = excluded.indexed_at`,
32788
+ [root, relPath, fileGraph.contentHash, Date.now()]
32789
+ );
32790
+ await this.runSql("COMMIT");
32791
+ } catch (err) {
32792
+ await this.runSql("ROLLBACK");
32793
+ throw err;
32794
+ }
32795
+ }
32796
+ /** Drop a file that no longer exists on disk, with its nodes and edges. */
32797
+ async removeFile(root, relPath) {
32798
+ await this.ready;
32799
+ await this.runSql("BEGIN TRANSACTION");
32800
+ try {
32801
+ await this.runSql("DELETE FROM code_nodes WHERE root = ? AND path = ?", [root, relPath]);
32802
+ await this.runSql("DELETE FROM code_edges WHERE root = ? AND file_path = ?", [root, relPath]);
32803
+ await this.runSql("DELETE FROM code_files WHERE root = ? AND path = ?", [root, relPath]);
32804
+ await this.runSql("COMMIT");
32805
+ } catch (err) {
32806
+ await this.runSql("ROLLBACK");
32807
+ throw err;
32808
+ }
32809
+ }
32810
+ // -------------------------------------------------------------------------
32811
+ // Read
32812
+ // -------------------------------------------------------------------------
32813
+ /** Reconstruct the full in-memory CodeGraph for a root. */
32814
+ async loadGraph(root) {
32815
+ await this.ready;
32816
+ const nodeRows = await this.querySql(
32817
+ `SELECT id, kind, name, path, range_start, range_end, language, signature, docstring
32818
+ FROM code_nodes WHERE root = ?`,
32819
+ [root]
32820
+ );
32821
+ const edgeRows = await this.querySql(
32822
+ "SELECT source, target, type FROM code_edges WHERE root = ?",
32823
+ [root]
32824
+ );
32825
+ const nodes = /* @__PURE__ */ new Map();
32826
+ for (const r of nodeRows) nodes.set(r["id"], rowToNode(r));
32827
+ const edges = edgeRows.map((r) => ({
32828
+ source: r["source"],
32829
+ target: r["target"],
32830
+ type: r["type"]
32831
+ }));
32832
+ return { nodes, edges };
32833
+ }
32834
+ async stats(root) {
32835
+ await this.ready;
32836
+ const fileRows = await this.querySql(
32837
+ "SELECT COUNT(*) AS cnt FROM code_nodes WHERE root = ? AND kind = 'file'",
32838
+ [root]
32839
+ );
32840
+ const symRows = await this.querySql(
32841
+ "SELECT COUNT(*) AS cnt FROM code_nodes WHERE root = ? AND kind <> 'file'",
32842
+ [root]
32843
+ );
32844
+ const edgeRows = await this.querySql(
32845
+ "SELECT COUNT(*) AS cnt FROM code_edges WHERE root = ?",
32846
+ [root]
32847
+ );
32848
+ return {
32849
+ files: Number(fileRows[0]["cnt"] ?? 0),
32850
+ symbols: Number(symRows[0]["cnt"] ?? 0),
32851
+ edges: Number(edgeRows[0]["cnt"] ?? 0)
32852
+ };
32853
+ }
32854
+ // -------------------------------------------------------------------------
32855
+ // Lifecycle
32856
+ // -------------------------------------------------------------------------
32857
+ async close() {
32858
+ await this.ready.catch(() => {
32859
+ });
32860
+ return new Promise((resolve6, reject) => {
32861
+ this.conn.close((err) => {
32862
+ if (err) return reject(err);
32863
+ this.db.close((err2) => {
32864
+ if (err2) return reject(err2);
32865
+ resolve6();
32866
+ });
32867
+ });
32868
+ });
32869
+ }
32870
+ };
32871
+ }
32872
+ });
32873
+
32598
32874
  // src/daemon/code-graph/builder.ts
32875
+ var builder_exports = {};
32876
+ __export(builder_exports, {
32877
+ buildAndPersistCodeGraph: () => buildAndPersistCodeGraph,
32878
+ buildCodeGraph: () => buildCodeGraph,
32879
+ extractFileGraph: () => extractFileGraph,
32880
+ fileGraphFromContent: () => fileGraphFromContent,
32881
+ listSourceFiles: () => listSourceFiles
32882
+ });
32599
32883
  import * as fs from "node:fs";
32600
32884
  import * as path from "node:path";
32601
- import { createHash } from "node:crypto";
32885
+ import { createHash as createHash2 } from "node:crypto";
32602
32886
  async function buildCodeGraph(workspaceRoot) {
32603
32887
  const graph2 = { nodes: /* @__PURE__ */ new Map(), edges: [] };
32604
- await walkDirectory(workspaceRoot, workspaceRoot, graph2);
32888
+ for (const rel of await listSourceFiles(workspaceRoot)) {
32889
+ const fg = await extractFileGraph(path.join(workspaceRoot, rel), workspaceRoot);
32890
+ if (!fg) continue;
32891
+ for (const node of fg.nodes) graph2.nodes.set(node.id, node);
32892
+ graph2.edges.push(...fg.edges);
32893
+ }
32605
32894
  return graph2;
32606
32895
  }
32607
- async function walkDirectory(dir, root, graph2) {
32608
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
32896
+ async function listSourceFiles(workspaceRoot) {
32897
+ const out2 = [];
32898
+ await walkDirectory(workspaceRoot, workspaceRoot, out2);
32899
+ return out2;
32900
+ }
32901
+ async function walkDirectory(dir, root, out2) {
32902
+ let entries;
32903
+ try {
32904
+ entries = await fs.promises.readdir(dir, { withFileTypes: true });
32905
+ } catch {
32906
+ return;
32907
+ }
32609
32908
  for (const entry of entries) {
32610
32909
  if (IGNORE_DIRS.has(entry.name)) continue;
32611
32910
  const fullPath = path.join(dir, entry.name);
32612
32911
  if (entry.isDirectory()) {
32613
- await walkDirectory(fullPath, root, graph2);
32912
+ await walkDirectory(fullPath, root, out2);
32614
32913
  } else if (entry.isFile() && SUPPORTED_EXTENSIONS.has(path.extname(entry.name))) {
32615
- await processFile(fullPath, root, graph2);
32914
+ out2.push(path.relative(root, fullPath));
32616
32915
  }
32617
32916
  }
32618
32917
  }
32619
- async function processFile(filePath, root, graph2) {
32620
- const relativePath = path.relative(root, filePath);
32621
- const ext = path.extname(filePath);
32622
- const language = extensionToLanguage(ext);
32918
+ function fileGraphFromContent(relativePath, content, root) {
32919
+ const language = extensionToLanguage(path.extname(relativePath));
32920
+ const nodes = [];
32921
+ const edges = [];
32623
32922
  const fileNode = {
32624
- id: computeNodeId(relativePath, path.basename(filePath), "file"),
32923
+ id: computeNodeId(relativePath, path.basename(relativePath), "file"),
32625
32924
  kind: "file",
32626
- name: path.basename(filePath),
32925
+ name: path.basename(relativePath),
32627
32926
  path: relativePath,
32628
32927
  range: { start: 0, end: 0 },
32629
32928
  language
32630
32929
  };
32631
- graph2.nodes.set(fileNode.id, fileNode);
32930
+ nodes.push(fileNode);
32931
+ for (const symbol21 of extractSymbols(content, relativePath, language)) {
32932
+ nodes.push(symbol21);
32933
+ edges.push({ source: fileNode.id, target: symbol21.id, type: "contains" });
32934
+ }
32935
+ edges.push(...extractImports(content, relativePath, root));
32936
+ return { contentHash: hashContent(content), nodes, edges };
32937
+ }
32938
+ async function extractFileGraph(filePath, root) {
32939
+ let content;
32632
32940
  try {
32633
- const content = await fs.promises.readFile(filePath, "utf-8");
32634
- const symbols = extractSymbols(content, relativePath, language);
32635
- for (const symbol21 of symbols) {
32636
- graph2.nodes.set(symbol21.id, symbol21);
32637
- graph2.edges.push({ source: fileNode.id, target: symbol21.id, type: "contains" });
32638
- }
32639
- const imports2 = extractImports(content, relativePath, root);
32640
- graph2.edges.push(...imports2);
32941
+ content = await fs.promises.readFile(filePath, "utf-8");
32641
32942
  } catch {
32943
+ return null;
32642
32944
  }
32945
+ return fileGraphFromContent(path.relative(root, filePath), content, root);
32946
+ }
32947
+ async function buildAndPersistCodeGraph(root, store) {
32948
+ const onDisk = await listSourceFiles(root);
32949
+ const onDiskSet = new Set(onDisk);
32950
+ let indexed = 0;
32951
+ let skipped = 0;
32952
+ for (const rel of onDisk) {
32953
+ let content;
32954
+ try {
32955
+ content = await fs.promises.readFile(path.join(root, rel), "utf-8");
32956
+ } catch {
32957
+ continue;
32958
+ }
32959
+ if (await store.fileHash(root, rel) === hashContent(content)) {
32960
+ skipped++;
32961
+ continue;
32962
+ }
32963
+ await store.replaceFile(root, rel, fileGraphFromContent(rel, content, root));
32964
+ indexed++;
32965
+ }
32966
+ let removed = 0;
32967
+ for (const rel of await store.indexedFiles(root)) {
32968
+ if (!onDiskSet.has(rel)) {
32969
+ await store.removeFile(root, rel);
32970
+ removed++;
32971
+ }
32972
+ }
32973
+ return { indexed, skipped, removed };
32643
32974
  }
32644
32975
  function extractSymbols(content, filePath, language) {
32645
32976
  const symbols = [];
@@ -32719,7 +33050,7 @@ function extractImports(content, filePath, root) {
32719
33050
  return edges;
32720
33051
  }
32721
33052
  function computeNodeId(path2, name21, kind) {
32722
- return createHash("sha256").update(`${path2}:${name21}:${kind}`).digest("hex").slice(0, 32);
33053
+ return createHash2("sha256").update(`${path2}:${name21}:${kind}`).digest("hex").slice(0, 32);
32723
33054
  }
32724
33055
  function findEndLine(lines, startLine) {
32725
33056
  let braceCount = 0;
@@ -32757,6 +33088,7 @@ function extensionToLanguage(ext) {
32757
33088
  var SUPPORTED_EXTENSIONS, IGNORE_DIRS, RESOLVE_EXTENSIONS;
32758
33089
  var init_builder = __esm({
32759
33090
  "src/daemon/code-graph/builder.ts"() {
33091
+ init_store();
32760
33092
  SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
32761
33093
  ".ts",
32762
33094
  ".tsx",
@@ -32785,6 +33117,49 @@ var init_builder = __esm({
32785
33117
  });
32786
33118
 
32787
33119
  // src/daemon/code-graph/query.ts
33120
+ var query_exports = {};
33121
+ __export(query_exports, {
33122
+ callees: () => callees,
33123
+ callers: () => callers,
33124
+ dependents: () => dependents,
33125
+ imports: () => imports,
33126
+ neighbors: () => neighbors,
33127
+ searchSymbols: () => searchSymbols
33128
+ });
33129
+ function neighbors(graph2, nodeId, hops = 1, edgeTypes) {
33130
+ const visitedNodes = /* @__PURE__ */ new Set();
33131
+ const resultEdges = [];
33132
+ let frontier = /* @__PURE__ */ new Set([nodeId]);
33133
+ for (let hop = 0; hop < hops; hop++) {
33134
+ const nextFrontier = /* @__PURE__ */ new Set();
33135
+ for (const id of frontier) {
33136
+ visitedNodes.add(id);
33137
+ for (const edge of graph2.edges) {
33138
+ if (edgeTypes && !edgeTypes.includes(edge.type)) continue;
33139
+ if (edge.source === id && !visitedNodes.has(edge.target)) {
33140
+ nextFrontier.add(edge.target);
33141
+ resultEdges.push(edge);
33142
+ }
33143
+ if (edge.target === id && !visitedNodes.has(edge.source)) {
33144
+ nextFrontier.add(edge.source);
33145
+ resultEdges.push(edge);
33146
+ }
33147
+ }
33148
+ }
33149
+ frontier = nextFrontier;
33150
+ }
33151
+ for (const id of frontier) visitedNodes.add(id);
33152
+ const nodes = [...visitedNodes].map((id) => graph2.nodes.get(id)).filter((n) => n !== void 0);
33153
+ return { nodes, edges: resultEdges };
33154
+ }
33155
+ function callers(graph2, functionId) {
33156
+ const callerIds = graph2.edges.filter((e) => e.target === functionId && e.type === "calls").map((e) => e.source);
33157
+ return callerIds.map((id) => graph2.nodes.get(id)).filter((n) => n !== void 0);
33158
+ }
33159
+ function callees(graph2, functionId) {
33160
+ const calleeIds = graph2.edges.filter((e) => e.source === functionId && e.type === "calls").map((e) => e.target);
33161
+ return calleeIds.map((id) => graph2.nodes.get(id)).filter((n) => n !== void 0);
33162
+ }
32788
33163
  function imports(graph2, fileId) {
32789
33164
  const importedIds = graph2.edges.filter((e) => e.source === fileId && e.type === "imports").map((e) => e.target);
32790
33165
  return importedIds.map((id) => graph2.nodes.get(id)).filter((n) => n !== void 0);
@@ -32813,14 +33188,36 @@ var init_query = __esm({
32813
33188
  });
32814
33189
 
32815
33190
  // src/agent/code-graph.ts
33191
+ import { mkdirSync } from "node:fs";
33192
+ import { dirname as dirname2 } from "node:path";
32816
33193
  function getCodeGraph(cwd2) {
32817
33194
  let graph2 = cache.get(cwd2);
32818
33195
  if (!graph2) {
32819
- graph2 = buildCodeGraph(cwd2);
33196
+ graph2 = loadOrBuildCodeGraph(cwd2);
32820
33197
  cache.set(cwd2, graph2);
32821
33198
  }
32822
33199
  return graph2;
32823
33200
  }
33201
+ async function loadOrBuildCodeGraph(cwd2) {
33202
+ let store = null;
33203
+ try {
33204
+ const dbPath2 = defaultCodeGraphDbPath();
33205
+ mkdirSync(dirname2(dbPath2), { recursive: true });
33206
+ store = createCodeGraphStore({ duckdbPath: dbPath2 });
33207
+ await store.whenReady();
33208
+ await buildAndPersistCodeGraph(cwd2, store);
33209
+ return await store.loadGraph(cwd2);
33210
+ } catch {
33211
+ return buildCodeGraph(cwd2);
33212
+ } finally {
33213
+ if (store) {
33214
+ try {
33215
+ await store.close();
33216
+ } catch {
33217
+ }
33218
+ }
33219
+ }
33220
+ }
32824
33221
  function fileNodeFor(graph2, query) {
32825
33222
  const q = query.replace(/^\.\//, "");
32826
33223
  let suffixMatch = null;
@@ -32883,6 +33280,7 @@ var init_code_graph = __esm({
32883
33280
  "src/agent/code-graph.ts"() {
32884
33281
  init_builder();
32885
33282
  init_query();
33283
+ init_store();
32886
33284
  cache = /* @__PURE__ */ new Map();
32887
33285
  }
32888
33286
  });
@@ -32891,7 +33289,7 @@ var init_code_graph = __esm({
32891
33289
  import { promises as fs2 } from "node:fs";
32892
33290
  import { exec } from "node:child_process";
32893
33291
  import { promisify } from "node:util";
32894
- import { resolve as resolve4, relative as relative3, join as join2, isAbsolute as isAbsolute2 } from "node:path";
33292
+ import { resolve as resolve4, relative as relative3, join as join3, isAbsolute as isAbsolute2 } from "node:path";
32895
33293
  function clip(text2) {
32896
33294
  if (text2.length <= MAX_OUTPUT) return text2;
32897
33295
  return text2.slice(0, MAX_OUTPUT) + `
@@ -32932,7 +33330,7 @@ async function* walk(dir, cwd2) {
32932
33330
  }
32933
33331
  for (const entry of entries) {
32934
33332
  if (entry.isDir && IGNORE_DIRS2.has(entry.name)) continue;
32935
- const abs = join2(dir, entry.name);
33333
+ const abs = join3(dir, entry.name);
32936
33334
  const rel = relative3(cwd2, abs);
32937
33335
  if (entry.isDir) {
32938
33336
  yield* walk(abs, cwd2);
@@ -33219,9 +33617,9 @@ var init_system_prompt = __esm({
33219
33617
  });
33220
33618
 
33221
33619
  // src/lib/config.ts
33222
- import { homedir } from "os";
33223
- import { join as join3 } from "path";
33224
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
33620
+ import { homedir as homedir2 } from "os";
33621
+ import { join as join4 } from "path";
33622
+ import { readFileSync, writeFileSync, mkdirSync as mkdirSync2, existsSync } from "fs";
33225
33623
  function readConfig() {
33226
33624
  if (!existsSync(CONFIG_FILE)) return {};
33227
33625
  try {
@@ -33239,7 +33637,7 @@ Run \`oxagen auth login\` to re-authenticate.
33239
33637
  function writeConfig(patch) {
33240
33638
  const current = readConfig();
33241
33639
  const next = { ...current, ...patch };
33242
- if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
33640
+ if (!existsSync(CONFIG_DIR)) mkdirSync2(CONFIG_DIR, { recursive: true });
33243
33641
  writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2), "utf8");
33244
33642
  }
33245
33643
  function getToken() {
@@ -33257,8 +33655,8 @@ function getApiUrl() {
33257
33655
  var CONFIG_DIR, CONFIG_FILE;
33258
33656
  var init_config = __esm({
33259
33657
  "src/lib/config.ts"() {
33260
- CONFIG_DIR = join3(homedir(), ".config", "oxagen");
33261
- CONFIG_FILE = join3(CONFIG_DIR, "config.json");
33658
+ CONFIG_DIR = join4(homedir2(), ".config", "oxagen");
33659
+ CONFIG_FILE = join4(CONFIG_DIR, "config.json");
33262
33660
  }
33263
33661
  });
33264
33662
 
@@ -33276,12 +33674,12 @@ var init_model = __esm({
33276
33674
 
33277
33675
  // src/agent/env.ts
33278
33676
  import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
33279
- import { dirname as dirname2, join as join4, parse as parsePath } from "node:path";
33677
+ import { dirname as dirname3, join as join5, parse as parsePath } from "node:path";
33280
33678
  function findInEnvLocal(startDir) {
33281
33679
  let dir = startDir;
33282
33680
  const { root } = parsePath(startDir);
33283
33681
  for (; ; ) {
33284
- const candidate = join4(dir, ".env.local");
33682
+ const candidate = join5(dir, ".env.local");
33285
33683
  if (existsSync2(candidate)) {
33286
33684
  try {
33287
33685
  const text2 = readFileSync2(candidate, "utf8");
@@ -33297,7 +33695,7 @@ function findInEnvLocal(startDir) {
33297
33695
  }
33298
33696
  }
33299
33697
  if (dir === root) return void 0;
33300
- dir = dirname2(dir);
33698
+ dir = dirname3(dir);
33301
33699
  }
33302
33700
  }
33303
33701
  function ensureGatewayKey(cwd2 = process.cwd()) {
@@ -33445,8 +33843,8 @@ var init_schema = __esm({
33445
33843
  });
33446
33844
 
33447
33845
  // ../../packages/mcp-config/src/resolve.ts
33448
- import { join as join5 } from "node:path";
33449
- import { homedir as homedir2 } from "node:os";
33846
+ import { join as join6 } from "node:path";
33847
+ import { homedir as homedir3 } from "node:os";
33450
33848
  function expandEnvVars(value, env4 = process.env, warnings = /* @__PURE__ */ new Set()) {
33451
33849
  if (typeof value === "string") {
33452
33850
  return value.replace(ENV_VAR_PATTERN, (_, varName) => {
@@ -33475,8 +33873,8 @@ var init_resolve = __esm({
33475
33873
  "../../packages/mcp-config/src/resolve.ts"() {
33476
33874
  "use strict";
33477
33875
  init_schema();
33478
- USER_CONFIG_DIR = join5(homedir2(), ".config", "oxagen");
33479
- USER_SETTINGS_FILE = join5(USER_CONFIG_DIR, "settings.json");
33876
+ USER_CONFIG_DIR = join6(homedir3(), ".config", "oxagen");
33877
+ USER_SETTINGS_FILE = join6(USER_CONFIG_DIR, "settings.json");
33480
33878
  ENV_VAR_PATTERN = /\$\{([^}]+)\}/g;
33481
33879
  }
33482
33880
  });
@@ -33556,18 +33954,18 @@ var init_schema2 = __esm({
33556
33954
 
33557
33955
  // src/settings/resolve.ts
33558
33956
  import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
33559
- import { join as join6 } from "node:path";
33560
- import { homedir as homedir3 } from "node:os";
33957
+ import { join as join7 } from "node:path";
33958
+ import { homedir as homedir4 } from "node:os";
33561
33959
  function userSettingsFile() {
33562
- return join6(homedir3(), ".config", "oxagen", "settings.json");
33960
+ return join7(homedir4(), ".config", "oxagen", "settings.json");
33563
33961
  }
33564
33962
  function getScopePaths(opts = {}) {
33565
33963
  const root = opts.cwd ?? process.cwd();
33566
33964
  const dir = opts.projectDirName ?? DEFAULT_PROJECT_DIR;
33567
33965
  return {
33568
33966
  user: opts.userSettingsPath ?? userSettingsFile(),
33569
- project: join6(root, dir, "settings.json"),
33570
- local: join6(root, dir, "settings.local.json")
33967
+ project: join7(root, dir, "settings.json"),
33968
+ local: join7(root, dir, "settings.local.json")
33571
33969
  };
33572
33970
  }
33573
33971
  function loadScope(scope, path2) {
@@ -36539,7 +36937,7 @@ var init_protocol = __esm({
36539
36937
  return;
36540
36938
  }
36541
36939
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
36542
- await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
36940
+ await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
36543
36941
  options?.signal?.throwIfAborted();
36544
36942
  }
36545
36943
  } catch (error40) {
@@ -36556,7 +36954,7 @@ var init_protocol = __esm({
36556
36954
  */
36557
36955
  request(request, resultSchema, options) {
36558
36956
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
36559
- return new Promise((resolve5, reject) => {
36957
+ return new Promise((resolve6, reject) => {
36560
36958
  const earlyReject = (error40) => {
36561
36959
  reject(error40);
36562
36960
  };
@@ -36634,7 +37032,7 @@ var init_protocol = __esm({
36634
37032
  if (!parseResult.success) {
36635
37033
  reject(parseResult.error);
36636
37034
  } else {
36637
- resolve5(parseResult.data);
37035
+ resolve6(parseResult.data);
36638
37036
  }
36639
37037
  } catch (error40) {
36640
37038
  reject(error40);
@@ -36895,12 +37293,12 @@ var init_protocol = __esm({
36895
37293
  }
36896
37294
  } catch {
36897
37295
  }
36898
- return new Promise((resolve5, reject) => {
37296
+ return new Promise((resolve6, reject) => {
36899
37297
  if (signal.aborted) {
36900
37298
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
36901
37299
  return;
36902
37300
  }
36903
- const timeoutId = setTimeout(resolve5, interval);
37301
+ const timeoutId = setTimeout(resolve6, interval);
36904
37302
  signal.addEventListener("abort", () => {
36905
37303
  clearTimeout(timeoutId);
36906
37304
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -39927,7 +40325,7 @@ var require_compile = __commonJS({
39927
40325
  const schOrFunc = root.refs[ref];
39928
40326
  if (schOrFunc)
39929
40327
  return schOrFunc;
39930
- let _sch = resolve5.call(this, root, ref);
40328
+ let _sch = resolve6.call(this, root, ref);
39931
40329
  if (_sch === void 0) {
39932
40330
  const schema = (_a21 = root.localRefs) === null || _a21 === void 0 ? void 0 : _a21[ref];
39933
40331
  const { schemaId } = this.opts;
@@ -39954,7 +40352,7 @@ var require_compile = __commonJS({
39954
40352
  function sameSchemaEnv(s1, s2) {
39955
40353
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
39956
40354
  }
39957
- function resolve5(root, ref) {
40355
+ function resolve6(root, ref) {
39958
40356
  let sch;
39959
40357
  while (typeof (sch = this.refs[ref]) == "string")
39960
40358
  ref = sch;
@@ -40585,7 +40983,7 @@ var require_fast_uri = __commonJS({
40585
40983
  }
40586
40984
  return uri;
40587
40985
  }
40588
- function resolve5(baseURI, relativeURI, options) {
40986
+ function resolve6(baseURI, relativeURI, options) {
40589
40987
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
40590
40988
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
40591
40989
  schemelessOptions.skipEscape = true;
@@ -40843,7 +41241,7 @@ var require_fast_uri = __commonJS({
40843
41241
  var fastUri = {
40844
41242
  SCHEMES,
40845
41243
  normalize,
40846
- resolve: resolve5,
41244
+ resolve: resolve6,
40847
41245
  resolveComponent,
40848
41246
  equal,
40849
41247
  serialize,
@@ -44729,12 +45127,12 @@ var require_isexe = __commonJS({
44729
45127
  if (typeof Promise !== "function") {
44730
45128
  throw new TypeError("callback not provided");
44731
45129
  }
44732
- return new Promise(function(resolve5, reject) {
45130
+ return new Promise(function(resolve6, reject) {
44733
45131
  isexe(path2, options || {}, function(er, is) {
44734
45132
  if (er) {
44735
45133
  reject(er);
44736
45134
  } else {
44737
- resolve5(is);
45135
+ resolve6(is);
44738
45136
  }
44739
45137
  });
44740
45138
  });
@@ -44800,27 +45198,27 @@ var require_which = __commonJS({
44800
45198
  opt = {};
44801
45199
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
44802
45200
  const found = [];
44803
- const step = (i) => new Promise((resolve5, reject) => {
45201
+ const step = (i) => new Promise((resolve6, reject) => {
44804
45202
  if (i === pathEnv.length)
44805
- return opt.all && found.length ? resolve5(found) : reject(getNotFoundError(cmd));
45203
+ return opt.all && found.length ? resolve6(found) : reject(getNotFoundError(cmd));
44806
45204
  const ppRaw = pathEnv[i];
44807
45205
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
44808
45206
  const pCmd = path2.join(pathPart, cmd);
44809
45207
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
44810
- resolve5(subStep(p, i, 0));
45208
+ resolve6(subStep(p, i, 0));
44811
45209
  });
44812
- const subStep = (p, i, ii) => new Promise((resolve5, reject) => {
45210
+ const subStep = (p, i, ii) => new Promise((resolve6, reject) => {
44813
45211
  if (ii === pathExt.length)
44814
- return resolve5(step(i + 1));
45212
+ return resolve6(step(i + 1));
44815
45213
  const ext = pathExt[ii];
44816
45214
  isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
44817
45215
  if (!er && is) {
44818
45216
  if (opt.all)
44819
45217
  found.push(p + ext);
44820
45218
  else
44821
- return resolve5(p + ext);
45219
+ return resolve6(p + ext);
44822
45220
  }
44823
- return resolve5(subStep(p, i, ii + 1));
45221
+ return resolve6(subStep(p, i, ii + 1));
44824
45222
  });
44825
45223
  });
44826
45224
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -45221,7 +45619,7 @@ var init_stdio2 = __esm({
45221
45619
  if (this._process) {
45222
45620
  throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
45223
45621
  }
45224
- return new Promise((resolve5, reject) => {
45622
+ return new Promise((resolve6, reject) => {
45225
45623
  this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], {
45226
45624
  // merge default env with server env because mcp server needs some env vars
45227
45625
  env: {
@@ -45238,7 +45636,7 @@ var init_stdio2 = __esm({
45238
45636
  this.onerror?.(error40);
45239
45637
  });
45240
45638
  this._process.on("spawn", () => {
45241
- resolve5();
45639
+ resolve6();
45242
45640
  });
45243
45641
  this._process.on("close", (_code) => {
45244
45642
  this._process = void 0;
@@ -45297,22 +45695,22 @@ var init_stdio2 = __esm({
45297
45695
  if (this._process) {
45298
45696
  const processToClose = this._process;
45299
45697
  this._process = void 0;
45300
- const closePromise = new Promise((resolve5) => {
45698
+ const closePromise = new Promise((resolve6) => {
45301
45699
  processToClose.once("close", () => {
45302
- resolve5();
45700
+ resolve6();
45303
45701
  });
45304
45702
  });
45305
45703
  try {
45306
45704
  processToClose.stdin?.end();
45307
45705
  } catch {
45308
45706
  }
45309
- await Promise.race([closePromise, new Promise((resolve5) => setTimeout(resolve5, 2e3).unref())]);
45707
+ await Promise.race([closePromise, new Promise((resolve6) => setTimeout(resolve6, 2e3).unref())]);
45310
45708
  if (processToClose.exitCode === null) {
45311
45709
  try {
45312
45710
  processToClose.kill("SIGTERM");
45313
45711
  } catch {
45314
45712
  }
45315
- await Promise.race([closePromise, new Promise((resolve5) => setTimeout(resolve5, 2e3).unref())]);
45713
+ await Promise.race([closePromise, new Promise((resolve6) => setTimeout(resolve6, 2e3).unref())]);
45316
45714
  }
45317
45715
  if (processToClose.exitCode === null) {
45318
45716
  try {
@@ -45324,15 +45722,15 @@ var init_stdio2 = __esm({
45324
45722
  this._readBuffer.clear();
45325
45723
  }
45326
45724
  send(message) {
45327
- return new Promise((resolve5) => {
45725
+ return new Promise((resolve6) => {
45328
45726
  if (!this._process?.stdin) {
45329
45727
  throw new Error("Not connected");
45330
45728
  }
45331
45729
  const json3 = serializeMessage(message);
45332
45730
  if (this._process.stdin.write(json3)) {
45333
- resolve5();
45731
+ resolve6();
45334
45732
  } else {
45335
- this._process.stdin.once("drain", resolve5);
45733
+ this._process.stdin.once("drain", resolve6);
45336
45734
  }
45337
45735
  });
45338
45736
  }
@@ -46995,7 +47393,7 @@ var init_sse = __esm({
46995
47393
  }
46996
47394
  _startOrAuth() {
46997
47395
  const fetchImpl = this?._eventSourceInit?.fetch ?? this._fetch ?? fetch;
46998
- return new Promise((resolve5, reject) => {
47396
+ return new Promise((resolve6, reject) => {
46999
47397
  this._eventSource = new EventSource(this._url.href, {
47000
47398
  ...this._eventSourceInit,
47001
47399
  fetch: async (url2, init) => {
@@ -47016,7 +47414,7 @@ var init_sse = __esm({
47016
47414
  this._abortController = new AbortController();
47017
47415
  this._eventSource.onerror = (event) => {
47018
47416
  if (event.code === 401 && this._authProvider) {
47019
- this._authThenStart().then(resolve5, reject);
47417
+ this._authThenStart().then(resolve6, reject);
47020
47418
  return;
47021
47419
  }
47022
47420
  const error40 = new SseError(event.code, event.message, event);
@@ -47038,7 +47436,7 @@ var init_sse = __esm({
47038
47436
  void this.close();
47039
47437
  return;
47040
47438
  }
47041
- resolve5();
47439
+ resolve6();
47042
47440
  });
47043
47441
  this._eventSource.onmessage = (event) => {
47044
47442
  const messageEvent = event;
@@ -47143,7 +47541,7 @@ var init_websocket = __esm({
47143
47541
  if (this._socket) {
47144
47542
  throw new Error("WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.");
47145
47543
  }
47146
- return new Promise((resolve5, reject) => {
47544
+ return new Promise((resolve6, reject) => {
47147
47545
  this._socket = new WebSocket(this._url, SUBPROTOCOL);
47148
47546
  this._socket.onerror = (event) => {
47149
47547
  const error40 = "error" in event ? event.error : new Error(`WebSocket error: ${JSON.stringify(event)}`);
@@ -47151,7 +47549,7 @@ var init_websocket = __esm({
47151
47549
  this.onerror?.(error40);
47152
47550
  };
47153
47551
  this._socket.onopen = () => {
47154
- resolve5();
47552
+ resolve6();
47155
47553
  };
47156
47554
  this._socket.onclose = () => {
47157
47555
  this.onclose?.();
@@ -47172,13 +47570,13 @@ var init_websocket = __esm({
47172
47570
  this._socket?.close();
47173
47571
  }
47174
47572
  send(message) {
47175
- return new Promise((resolve5, reject) => {
47573
+ return new Promise((resolve6, reject) => {
47176
47574
  if (!this._socket) {
47177
47575
  reject(new Error("Not connected"));
47178
47576
  return;
47179
47577
  }
47180
47578
  this._socket?.send(JSON.stringify(message));
47181
- resolve5();
47579
+ resolve6();
47182
47580
  });
47183
47581
  }
47184
47582
  };
@@ -47190,13 +47588,13 @@ import {
47190
47588
  readFileSync as readFileSync4,
47191
47589
  writeFileSync as writeFileSync2,
47192
47590
  existsSync as existsSync4,
47193
- mkdirSync as mkdirSync2,
47591
+ mkdirSync as mkdirSync3,
47194
47592
  unlinkSync
47195
47593
  } from "node:fs";
47196
- import { join as join7 } from "node:path";
47197
- import { homedir as homedir4 } from "node:os";
47594
+ import { join as join8 } from "node:path";
47595
+ import { homedir as homedir5 } from "node:os";
47198
47596
  function getCredentialFilePath(serverName) {
47199
- return join7(CREDENTIALS_DIR, `${serverName}.json`);
47597
+ return join8(CREDENTIALS_DIR, `${serverName}.json`);
47200
47598
  }
47201
47599
  function readCredential(serverName) {
47202
47600
  const filePath = getCredentialFilePath(serverName);
@@ -47210,7 +47608,7 @@ function readCredential(serverName) {
47210
47608
  }
47211
47609
  function writeCredential(serverName, credential) {
47212
47610
  if (!existsSync4(CREDENTIALS_DIR)) {
47213
- mkdirSync2(CREDENTIALS_DIR, { recursive: true, mode: 448 });
47611
+ mkdirSync3(CREDENTIALS_DIR, { recursive: true, mode: 448 });
47214
47612
  }
47215
47613
  const filePath = getCredentialFilePath(serverName);
47216
47614
  const data = {
@@ -47298,7 +47696,7 @@ var CREDENTIALS_DIR;
47298
47696
  var init_credentials = __esm({
47299
47697
  "../../packages/mcp-config/src/credentials.ts"() {
47300
47698
  "use strict";
47301
- CREDENTIALS_DIR = join7(homedir4(), ".config", "oxagen", "credentials");
47699
+ CREDENTIALS_DIR = join8(homedir5(), ".config", "oxagen", "credentials");
47302
47700
  }
47303
47701
  });
47304
47702
 
@@ -47488,8 +47886,8 @@ var init_tools2 = __esm({
47488
47886
 
47489
47887
  // src/agents/loader.ts
47490
47888
  import { readFileSync as readFileSync5, readdirSync, existsSync as existsSync5 } from "node:fs";
47491
- import { join as join8, basename as basename2 } from "node:path";
47492
- import { homedir as homedir5 } from "node:os";
47889
+ import { join as join9, basename as basename2 } from "node:path";
47890
+ import { homedir as homedir6 } from "node:os";
47493
47891
  function parseFrontmatter(raw) {
47494
47892
  const text2 = raw.replace(/^/, "");
47495
47893
  if (!text2.startsWith("---")) return { data: {}, body: text2.trim() };
@@ -47547,17 +47945,17 @@ function loadDir(dir, into) {
47547
47945
  return;
47548
47946
  }
47549
47947
  for (const file2 of files.sort()) {
47550
- const def = defFromMarkdown(join8(dir, file2));
47948
+ const def = defFromMarkdown(join9(dir, file2));
47551
47949
  if (def) into.set(def.name, def);
47552
47950
  }
47553
47951
  }
47554
47952
  function loadAgents(opts = {}) {
47555
47953
  const cwd2 = opts.cwd ?? process.cwd();
47556
- const userDir = opts.userAgentsDir ?? join8(homedir5(), ".config", "oxagen", "agents");
47954
+ const userDir = opts.userAgentsDir ?? join9(homedir6(), ".config", "oxagen", "agents");
47557
47955
  const registry2 = /* @__PURE__ */ new Map();
47558
47956
  loadDir(userDir, registry2);
47559
- loadDir(join8(cwd2, ".claude", "agents"), registry2);
47560
- loadDir(join8(cwd2, ".oxagen", "agents"), registry2);
47957
+ loadDir(join9(cwd2, ".claude", "agents"), registry2);
47958
+ loadDir(join9(cwd2, ".oxagen", "agents"), registry2);
47561
47959
  const settings2 = loadSettings({ cwd: cwd2, ...opts.settingsOptions, noCache: true }).settings;
47562
47960
  const inline = settings2.agents;
47563
47961
  if (inline && typeof inline === "object") {
@@ -47588,8 +47986,8 @@ var init_loader = __esm({
47588
47986
 
47589
47987
  // src/rules/loader.ts
47590
47988
  import { readdirSync as readdirSync2, existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
47591
- import { join as join9, basename as basename3 } from "node:path";
47592
- import { homedir as homedir6 } from "node:os";
47989
+ import { join as join10, basename as basename3 } from "node:path";
47990
+ import { homedir as homedir7 } from "node:os";
47593
47991
  function guardFrom(data) {
47594
47992
  const tool2 = data["guard-tool"];
47595
47993
  const denyPathGlob = data["guard-deny-path"];
@@ -47628,17 +48026,17 @@ function loadDir2(dir, into) {
47628
48026
  return;
47629
48027
  }
47630
48028
  for (const file2 of files.sort()) {
47631
- const rule = ruleFromFile(join9(dir, file2));
48029
+ const rule = ruleFromFile(join10(dir, file2));
47632
48030
  if (rule) into.set(rule.id, rule);
47633
48031
  }
47634
48032
  }
47635
48033
  function loadRules(opts = {}) {
47636
48034
  const cwd2 = opts.cwd ?? process.cwd();
47637
- const userDir = opts.userRulesDir ?? join9(homedir6(), ".config", "oxagen", "rules");
48035
+ const userDir = opts.userRulesDir ?? join10(homedir7(), ".config", "oxagen", "rules");
47638
48036
  const registry2 = /* @__PURE__ */ new Map();
47639
48037
  loadDir2(userDir, registry2);
47640
- loadDir2(join9(cwd2, ".claude", "rules"), registry2);
47641
- loadDir2(join9(cwd2, ".oxagen", "rules"), registry2);
48038
+ loadDir2(join10(cwd2, ".claude", "rules"), registry2);
48039
+ loadDir2(join10(cwd2, ".oxagen", "rules"), registry2);
47642
48040
  return [...registry2.values()];
47643
48041
  }
47644
48042
  var init_loader2 = __esm({
@@ -47683,13 +48081,13 @@ var init_enforce = __esm({
47683
48081
  });
47684
48082
 
47685
48083
  // src/rules/write.ts
47686
- import { writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "node:fs";
47687
- import { join as join10, dirname as dirname3 } from "node:path";
48084
+ import { writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "node:fs";
48085
+ import { join as join11, dirname as dirname4 } from "node:path";
47688
48086
  function scaffoldRule(opts) {
47689
- const dir = opts.dir ?? join10(opts.cwd ?? process.cwd(), ".oxagen", "rules");
47690
- const path2 = join10(dir, `${opts.name}.md`);
48087
+ const dir = opts.dir ?? join11(opts.cwd ?? process.cwd(), ".oxagen", "rules");
48088
+ const path2 = join11(dir, `${opts.name}.md`);
47691
48089
  if (existsSync7(path2)) return { path: path2, created: false };
47692
- mkdirSync3(dirname3(path2), { recursive: true });
48090
+ mkdirSync4(dirname4(path2), { recursive: true });
47693
48091
  writeFileSync3(path2, TEMPLATE(opts.name), "utf8");
47694
48092
  return { path: path2, created: true };
47695
48093
  }
@@ -48301,14 +48699,14 @@ var init_judge = __esm({
48301
48699
  });
48302
48700
 
48303
48701
  // src/agent/fleet/memory.ts
48304
- import { mkdirSync as mkdirSync4, appendFileSync, readFileSync as readFileSync7, existsSync as existsSync8 } from "node:fs";
48305
- import { homedir as homedir7 } from "node:os";
48306
- import { basename as basename4, dirname as dirname4, join as join11 } from "node:path";
48702
+ import { mkdirSync as mkdirSync5, appendFileSync, readFileSync as readFileSync7, existsSync as existsSync8 } from "node:fs";
48703
+ import { homedir as homedir8 } from "node:os";
48704
+ import { basename as basename4, dirname as dirname5, join as join12 } from "node:path";
48307
48705
  function projectKey(cwd2) {
48308
48706
  return basename4(cwd2) || "default";
48309
48707
  }
48310
48708
  function memoryPath(cwd2) {
48311
- return join11(homedir7(), ".config", "oxagen", "memories", `${projectKey(cwd2)}.jsonl`);
48709
+ return join12(homedir8(), ".config", "oxagen", "memories", `${projectKey(cwd2)}.jsonl`);
48312
48710
  }
48313
48711
  function terms(text2) {
48314
48712
  return (text2.toLowerCase().match(/[a-z0-9_]+/g) ?? []).filter(
@@ -48346,7 +48744,7 @@ function openFleetMemory(cwd2) {
48346
48744
  record(rec) {
48347
48745
  const full = { ...rec, id: newId(), createdAt: Date.now() };
48348
48746
  try {
48349
- mkdirSync4(dirname4(path2), { recursive: true });
48747
+ mkdirSync5(dirname5(path2), { recursive: true });
48350
48748
  appendFileSync(path2, JSON.stringify(full) + "\n", "utf8");
48351
48749
  if (cache6) cache6.push(full);
48352
48750
  else cache6 = [full];
@@ -48857,7 +49255,7 @@ var init_pipeline2 = __esm({
48857
49255
 
48858
49256
  // src/agent/project-context.ts
48859
49257
  import { readFileSync as readFileSync8, existsSync as existsSync9 } from "node:fs";
48860
- import { dirname as dirname5, join as join12, parse as parsePath2, relative as relative4 } from "node:path";
49258
+ import { dirname as dirname6, join as join13, parse as parsePath2, relative as relative4 } from "node:path";
48861
49259
  function loadProjectContext(cwd2) {
48862
49260
  const chunks = [];
48863
49261
  const sources = [];
@@ -48866,7 +49264,7 @@ function loadProjectContext(cwd2) {
48866
49264
  const { root } = parsePath2(cwd2);
48867
49265
  for (let depth = 0; depth < MAX_DEPTH; depth++) {
48868
49266
  for (const rel of RULE_FILES) {
48869
- const full = join12(dir, rel);
49267
+ const full = join13(dir, rel);
48870
49268
  if (!existsSync9(full)) continue;
48871
49269
  let text2;
48872
49270
  try {
@@ -48886,7 +49284,7 @@ ${clipped}`);
48886
49284
  }
48887
49285
  if (total >= MAX_TOTAL) break;
48888
49286
  if (dir === root) break;
48889
- dir = dirname5(dir);
49287
+ dir = dirname6(dir);
48890
49288
  }
48891
49289
  return { text: chunks.join("\n\n"), sources };
48892
49290
  }
@@ -49284,32 +49682,32 @@ var init_duckdb_adapter = __esm({
49284
49682
  });
49285
49683
  }
49286
49684
  initialize() {
49287
- return new Promise((resolve5, reject) => {
49685
+ return new Promise((resolve6, reject) => {
49288
49686
  this.conn.run(CREATE_TABLE_SQL, (err) => {
49289
49687
  if (err) return reject(err);
49290
49688
  this.conn.run(INDEX_SQL, (err2) => {
49291
49689
  if (err2) return reject(err2);
49292
- resolve5();
49690
+ resolve6();
49293
49691
  });
49294
49692
  });
49295
49693
  });
49296
49694
  }
49297
49695
  runSql(sql, params = []) {
49298
- return new Promise((resolve5, reject) => {
49696
+ return new Promise((resolve6, reject) => {
49299
49697
  this.conn.run(sql, ...params, (err) => {
49300
49698
  if (err) reject(err);
49301
- else resolve5();
49699
+ else resolve6();
49302
49700
  });
49303
49701
  });
49304
49702
  }
49305
49703
  querySql(sql, params = []) {
49306
- return new Promise((resolve5, reject) => {
49704
+ return new Promise((resolve6, reject) => {
49307
49705
  this.conn.all(
49308
49706
  sql,
49309
49707
  ...params,
49310
49708
  (err, rows) => {
49311
49709
  if (err) reject(err);
49312
- else resolve5(rows ?? []);
49710
+ else resolve6(rows ?? []);
49313
49711
  }
49314
49712
  );
49315
49713
  });
@@ -49412,12 +49810,12 @@ var init_duckdb_adapter = __esm({
49412
49810
  async close() {
49413
49811
  await this.ready.catch(() => {
49414
49812
  });
49415
- return new Promise((resolve5, reject) => {
49813
+ return new Promise((resolve6, reject) => {
49416
49814
  this.conn.close((err) => {
49417
49815
  if (err) return reject(err);
49418
49816
  this.db.close((err2) => {
49419
49817
  if (err2) return reject(err2);
49420
- resolve5();
49818
+ resolve6();
49421
49819
  });
49422
49820
  });
49423
49821
  });
@@ -51022,8 +51420,8 @@ var require_sleep = __commonJS({
51022
51420
  Object.defineProperty(exports, "__esModule", { value: true });
51023
51421
  exports.sleep = sleep;
51024
51422
  async function sleep(ms) {
51025
- await new Promise((resolve5) => setTimeout(() => {
51026
- resolve5(void 0);
51423
+ await new Promise((resolve6) => setTimeout(() => {
51424
+ resolve6(void 0);
51027
51425
  }, ms));
51028
51426
  }
51029
51427
  }
@@ -51542,24 +51940,24 @@ var require_stream3 = __commonJS({
51542
51940
  exports.drainStreamInternal = drainStreamInternal;
51543
51941
  var client_common_1 = require_dist3();
51544
51942
  async function drainStream(stream) {
51545
- return new Promise((resolve5, reject) => {
51943
+ return new Promise((resolve6, reject) => {
51546
51944
  if (stream.errored) {
51547
51945
  reject(stream.errored);
51548
51946
  return;
51549
51947
  }
51550
51948
  if (stream.readableEnded) {
51551
- resolve5();
51949
+ resolve6();
51552
51950
  return;
51553
51951
  }
51554
51952
  if (stream.closed) {
51555
- resolve5();
51953
+ resolve6();
51556
51954
  return;
51557
51955
  }
51558
51956
  function dropData() {
51559
51957
  }
51560
51958
  function onEnd() {
51561
51959
  removeListeners();
51562
- resolve5();
51960
+ resolve6();
51563
51961
  }
51564
51962
  function onError(err) {
51565
51963
  removeListeners();
@@ -51567,7 +51965,7 @@ var require_stream3 = __commonJS({
51567
51965
  }
51568
51966
  function onClose() {
51569
51967
  removeListeners();
51570
- resolve5();
51968
+ resolve6();
51571
51969
  }
51572
51970
  function removeListeners() {
51573
51971
  stream.removeListener("data", dropData);
@@ -51582,7 +51980,7 @@ var require_stream3 = __commonJS({
51582
51980
  });
51583
51981
  }
51584
51982
  async function drainStreamInternal(ctx, stream) {
51585
- return new Promise((resolve5, reject) => {
51983
+ return new Promise((resolve6, reject) => {
51586
51984
  const startTime = Date.now();
51587
51985
  let bytesReceived = 0;
51588
51986
  let chunkCount = 0;
@@ -51624,7 +52022,7 @@ var require_stream3 = __commonJS({
51624
52022
  }
51625
52023
  });
51626
52024
  }
51627
- resolve5();
52025
+ resolve6();
51628
52026
  return;
51629
52027
  }
51630
52028
  if (stream.closed) {
@@ -51638,7 +52036,7 @@ var require_stream3 = __commonJS({
51638
52036
  }
51639
52037
  });
51640
52038
  }
51641
- resolve5();
52039
+ resolve6();
51642
52040
  return;
51643
52041
  }
51644
52042
  function dropData(chunk) {
@@ -51671,7 +52069,7 @@ var require_stream3 = __commonJS({
51671
52069
  }
51672
52070
  });
51673
52071
  }
51674
- resolve5();
52072
+ resolve6();
51675
52073
  }
51676
52074
  function onError(err) {
51677
52075
  removeListeners();
@@ -51704,7 +52102,7 @@ var require_stream3 = __commonJS({
51704
52102
  }
51705
52103
  });
51706
52104
  }
51707
- resolve5();
52105
+ resolve6();
51708
52106
  }
51709
52107
  function removeListeners() {
51710
52108
  stream.removeListener("data", dropData);
@@ -51852,7 +52250,7 @@ var require_socket_pool = __commonJS({
51852
52250
  const start = Date.now();
51853
52251
  const request = this.createClientRequest(params);
51854
52252
  const request_id = this.getNewRequestId();
51855
- return new Promise((resolve5, reject) => {
52253
+ return new Promise((resolve6, reject) => {
51856
52254
  const onError = (e) => {
51857
52255
  removeRequestListeners();
51858
52256
  if (e instanceof Error) {
@@ -51993,7 +52391,7 @@ var require_socket_pool = __commonJS({
51993
52391
  reject(err);
51994
52392
  }
51995
52393
  } else {
51996
- return resolve5({
52394
+ return resolve6({
51997
52395
  stream: responseStream,
51998
52396
  summary: params.parse_summary ? this.parseSummary(op, _response) : void 0,
51999
52397
  response_headers: { ..._response.headers },
@@ -53577,7 +53975,7 @@ function detectAdapter() {
53577
53975
  if (process.env["CLICKHOUSE_URL"]) return "clickhouse";
53578
53976
  return "duckdb";
53579
53977
  }
53580
- var init_store = __esm({
53978
+ var init_store2 = __esm({
53581
53979
  "../../packages/engram/src/store/index.ts"() {
53582
53980
  "use strict";
53583
53981
  init_duckdb_adapter();
@@ -53588,7 +53986,7 @@ var init_store = __esm({
53588
53986
  });
53589
53987
 
53590
53988
  // ../../packages/engram/src/store/graph-store.ts
53591
- function rowToNode(row) {
53989
+ function rowToNode2(row) {
53592
53990
  return {
53593
53991
  id: row["id"],
53594
53992
  labels: JSON.parse(row["labels"]),
@@ -53604,11 +54002,11 @@ function rowToNode(row) {
53604
54002
  function createGraphStore(opts) {
53605
54003
  return new GraphStore({ duckdbPath: opts.duckdbPath });
53606
54004
  }
53607
- var CREATE_NODES_SQL, CREATE_EDGES_SQL, CREATE_CURSOR_SQL, GraphStore;
54005
+ var CREATE_NODES_SQL2, CREATE_EDGES_SQL2, CREATE_CURSOR_SQL, CREATE_CODE_CURSOR_SQL, CREATE_LINEAGE_CURSOR_SQL, GraphStore;
53608
54006
  var init_graph_store = __esm({
53609
54007
  "../../packages/engram/src/store/graph-store.ts"() {
53610
54008
  "use strict";
53611
- CREATE_NODES_SQL = `
54009
+ CREATE_NODES_SQL2 = `
53612
54010
  CREATE TABLE IF NOT EXISTS graph_nodes (
53613
54011
  id VARCHAR PRIMARY KEY,
53614
54012
  labels JSON NOT NULL,
@@ -53621,7 +54019,7 @@ var init_graph_store = __esm({
53621
54019
  workspace VARCHAR NOT NULL
53622
54020
  )
53623
54021
  `;
53624
- CREATE_EDGES_SQL = `
54022
+ CREATE_EDGES_SQL2 = `
53625
54023
  CREATE TABLE IF NOT EXISTS graph_edges (
53626
54024
  id VARCHAR PRIMARY KEY,
53627
54025
  source_id VARCHAR NOT NULL,
@@ -53643,6 +54041,25 @@ var init_graph_store = __esm({
53643
54041
  synced_at BIGINT NOT NULL,
53644
54042
  PRIMARY KEY (org, workspace)
53645
54043
  )
54044
+ `;
54045
+ CREATE_CODE_CURSOR_SQL = `
54046
+ CREATE TABLE IF NOT EXISTS code_push_cursor (
54047
+ org VARCHAR NOT NULL,
54048
+ workspace VARCHAR NOT NULL,
54049
+ repo VARCHAR NOT NULL,
54050
+ sha VARCHAR NOT NULL,
54051
+ pushed_at BIGINT NOT NULL,
54052
+ PRIMARY KEY (org, workspace, repo)
54053
+ )
54054
+ `;
54055
+ CREATE_LINEAGE_CURSOR_SQL = `
54056
+ CREATE TABLE IF NOT EXISTS lineage_sync_cursor (
54057
+ org VARCHAR NOT NULL,
54058
+ workspace VARCHAR NOT NULL,
54059
+ last_synced_at BIGINT NOT NULL,
54060
+ synced_at BIGINT NOT NULL,
54061
+ PRIMARY KEY (org, workspace)
54062
+ )
53646
54063
  `;
53647
54064
  GraphStore = class {
53648
54065
  db;
@@ -53657,35 +54074,41 @@ var init_graph_store = __esm({
53657
54074
  });
53658
54075
  }
53659
54076
  initialize() {
53660
- return new Promise((resolve5, reject) => {
53661
- this.conn.run(CREATE_NODES_SQL, (err) => {
54077
+ return new Promise((resolve6, reject) => {
54078
+ this.conn.run(CREATE_NODES_SQL2, (err) => {
53662
54079
  if (err) return reject(err);
53663
- this.conn.run(CREATE_EDGES_SQL, (err2) => {
54080
+ this.conn.run(CREATE_EDGES_SQL2, (err2) => {
53664
54081
  if (err2) return reject(err2);
53665
54082
  this.conn.run(CREATE_CURSOR_SQL, (err3) => {
53666
54083
  if (err3) return reject(err3);
53667
- resolve5();
54084
+ this.conn.run(CREATE_CODE_CURSOR_SQL, (err4) => {
54085
+ if (err4) return reject(err4);
54086
+ this.conn.run(CREATE_LINEAGE_CURSOR_SQL, (err5) => {
54087
+ if (err5) return reject(err5);
54088
+ resolve6();
54089
+ });
54090
+ });
53668
54091
  });
53669
54092
  });
53670
54093
  });
53671
54094
  });
53672
54095
  }
53673
54096
  runSql(sql, params = []) {
53674
- return new Promise((resolve5, reject) => {
54097
+ return new Promise((resolve6, reject) => {
53675
54098
  this.conn.run(sql, ...params, (err) => {
53676
54099
  if (err) reject(err);
53677
- else resolve5();
54100
+ else resolve6();
53678
54101
  });
53679
54102
  });
53680
54103
  }
53681
54104
  querySql(sql, params = []) {
53682
- return new Promise((resolve5, reject) => {
54105
+ return new Promise((resolve6, reject) => {
53683
54106
  this.conn.all(
53684
54107
  sql,
53685
54108
  ...params,
53686
54109
  (err, rows) => {
53687
54110
  if (err) reject(err);
53688
- else resolve5(rows ?? []);
54111
+ else resolve6(rows ?? []);
53689
54112
  }
53690
54113
  );
53691
54114
  });
@@ -53786,6 +54209,64 @@ var init_graph_store = __esm({
53786
54209
  );
53787
54210
  }
53788
54211
  // ---------------------------------------------------------------------------
54212
+ // Code push cursor — git SHA tracking for 'oxagen graph push'
54213
+ // ---------------------------------------------------------------------------
54214
+ async getCodeCursor(org, workspace, repo) {
54215
+ await this.ready;
54216
+ const rows = await this.querySql(
54217
+ "SELECT sha FROM code_push_cursor WHERE org = ? AND workspace = ? AND repo = ?",
54218
+ [org, workspace, repo]
54219
+ );
54220
+ if (rows.length === 0) return null;
54221
+ return rows[0]["sha"];
54222
+ }
54223
+ async setCodeCursor(org, workspace, repo, sha) {
54224
+ await this.ready;
54225
+ await this.runSql(
54226
+ "DELETE FROM code_push_cursor WHERE org = ? AND workspace = ? AND repo = ?",
54227
+ [org, workspace, repo]
54228
+ );
54229
+ await this.runSql(
54230
+ `INSERT INTO code_push_cursor (org, workspace, repo, sha, pushed_at)
54231
+ VALUES (?, ?, ?, ?, ?)`,
54232
+ [org, workspace, repo, sha, Date.now()]
54233
+ );
54234
+ }
54235
+ // ---------------------------------------------------------------------------
54236
+ // Lineage push cursor (TurnTrace sync tracking for `oxagen graph lineage`)
54237
+ // ---------------------------------------------------------------------------
54238
+ /**
54239
+ * Return the `createdAt` epoch-ms of the most recently synced TurnTrace, or
54240
+ * `null` if no lineage sync has been run yet.
54241
+ *
54242
+ * Traces with `createdAt > lastSyncedAt` are candidates for the next push.
54243
+ */
54244
+ async getLineageCursor(org, workspace) {
54245
+ await this.ready;
54246
+ const rows = await this.querySql(
54247
+ "SELECT last_synced_at FROM lineage_sync_cursor WHERE org = ? AND workspace = ?",
54248
+ [org, workspace]
54249
+ );
54250
+ if (rows.length === 0) return null;
54251
+ return Number(rows[0]["last_synced_at"]);
54252
+ }
54253
+ /**
54254
+ * Persist the `createdAt` epoch-ms of the latest TurnTrace that was
54255
+ * successfully pushed to the workspace graph.
54256
+ */
54257
+ async setLineageCursor(org, workspace, lastSyncedAt) {
54258
+ await this.ready;
54259
+ await this.runSql(
54260
+ "DELETE FROM lineage_sync_cursor WHERE org = ? AND workspace = ?",
54261
+ [org, workspace]
54262
+ );
54263
+ await this.runSql(
54264
+ `INSERT INTO lineage_sync_cursor (org, workspace, last_synced_at, synced_at)
54265
+ VALUES (?, ?, ?, ?)`,
54266
+ [org, workspace, lastSyncedAt, Date.now()]
54267
+ );
54268
+ }
54269
+ // ---------------------------------------------------------------------------
53789
54270
  // Read API
53790
54271
  // ---------------------------------------------------------------------------
53791
54272
  async stats(org, workspace) {
@@ -53816,7 +54297,7 @@ var init_graph_store = __esm({
53816
54297
  LIMIT ?`,
53817
54298
  [org, workspace, q, q, limit]
53818
54299
  );
53819
- return rows.map(rowToNode);
54300
+ return rows.map(rowToNode2);
53820
54301
  }
53821
54302
  async getNode(org, workspace, id) {
53822
54303
  await this.ready;
@@ -53825,7 +54306,7 @@ var init_graph_store = __esm({
53825
54306
  [org, workspace, id]
53826
54307
  );
53827
54308
  if (rows.length === 0) return null;
53828
- return rowToNode(rows[0]);
54309
+ return rowToNode2(rows[0]);
53829
54310
  }
53830
54311
  // ---------------------------------------------------------------------------
53831
54312
  // Lifecycle
@@ -53833,12 +54314,12 @@ var init_graph_store = __esm({
53833
54314
  async close() {
53834
54315
  await this.ready.catch(() => {
53835
54316
  });
53836
- return new Promise((resolve5, reject) => {
54317
+ return new Promise((resolve6, reject) => {
53837
54318
  this.conn.close((err) => {
53838
54319
  if (err) return reject(err);
53839
54320
  this.db.close((err2) => {
53840
54321
  if (err2) return reject(err2);
53841
- resolve5();
54322
+ resolve6();
53842
54323
  });
53843
54324
  });
53844
54325
  });
@@ -56662,6 +57143,335 @@ var init_report = __esm({
56662
57143
  }
56663
57144
  });
56664
57145
 
57146
+ // ../../packages/engram/src/eval/golden-fixtures.ts
57147
+ function hex64(seed) {
57148
+ let out2 = "";
57149
+ for (let lane = 0; lane < 8; lane++) {
57150
+ let h = 2166136261 ^ lane * 16777619;
57151
+ const s = `${seed}#${lane}`;
57152
+ for (let i = 0; i < s.length; i++) {
57153
+ h ^= s.charCodeAt(i);
57154
+ h = Math.imul(h, 16777619) >>> 0;
57155
+ }
57156
+ out2 += h.toString(16).padStart(8, "0");
57157
+ }
57158
+ return out2.slice(0, 64);
57159
+ }
57160
+ function semantic(seed, fact, domain2, salience = 0.7) {
57161
+ return {
57162
+ id: hex64(seed),
57163
+ kind: "semantic",
57164
+ namespace: { org: "oxagen", workspace: "eval" },
57165
+ body: { fact, domain: domain2 },
57166
+ salience,
57167
+ confidence: 0.95,
57168
+ provenance: {
57169
+ author: "golden-fixtures",
57170
+ derivedFrom: [],
57171
+ timestamp: 17e11
57172
+ },
57173
+ causality: [],
57174
+ createdAt: 17e11
57175
+ };
57176
+ }
57177
+ function frame(taskDescription, workingSet = []) {
57178
+ return {
57179
+ namespace: { org: "oxagen", workspace: "eval" },
57180
+ taskDescription,
57181
+ workingSet,
57182
+ recentEventIds: [],
57183
+ modelId: "claude-sonnet-4.5"
57184
+ };
57185
+ }
57186
+ var GOLDEN_CORPUS, SIGNAL_BUDGET, BASELINE, GOLDEN_TRACES, GOLDEN_GROUND_TRUTH;
57187
+ var init_golden_fixtures = __esm({
57188
+ "../../packages/engram/src/eval/golden-fixtures.ts"() {
57189
+ "use strict";
57190
+ GOLDEN_CORPUS = [
57191
+ semantic(
57192
+ "cli-default-model",
57193
+ "The DEFAULT_MODEL constant in apps/cli/src/agent/model.ts is set to anthropic/claude-sonnet-4.5.",
57194
+ "cli-config",
57195
+ 0.8
57196
+ ),
57197
+ semantic(
57198
+ "cli-oneshot-runturn",
57199
+ "apps/cli/src/repl/one-shot.ts imports runTurn from ../agent/pipeline.js to run a single agent turn.",
57200
+ "cli-oneshot"
57201
+ ),
57202
+ semantic(
57203
+ "cli-mutating-tools",
57204
+ "The MUTATING_TOOLS constant in apps/cli/src/agent/permissions.ts lists write_file, edit_file, and bash.",
57205
+ "cli-permissions"
57206
+ ),
57207
+ semantic(
57208
+ "cli-codegraph-builder",
57209
+ "apps/cli/src/agent/code-graph.ts imports buildCodeGraph from daemon/code-graph/builder.",
57210
+ "cli-codegraph"
57211
+ ),
57212
+ semantic(
57213
+ "app-proxy-file",
57214
+ "apps/app intercepts requests via proxy.ts, which replaces the old middleware.ts convention.",
57215
+ "app-routing"
57216
+ ),
57217
+ // --- noise: correct facts about other domains, but irrelevant to CLI questions ---
57218
+ semantic(
57219
+ "noise-billing",
57220
+ "Billing credits are the customer-facing currency, bought in US dollars and priced at a cent each.",
57221
+ "billing"
57222
+ ),
57223
+ semantic(
57224
+ "noise-rls",
57225
+ "Tenant isolation is enforced by withTenantDb / withSystemDb; raw db() is banned.",
57226
+ "database"
57227
+ ),
57228
+ semantic(
57229
+ "noise-brand",
57230
+ "The Oxagen Graphite skin pairs graphite grays with an ember accent (#F87854).",
57231
+ "brand"
57232
+ )
57233
+ ];
57234
+ SIGNAL_BUDGET = {
57235
+ total: 4e3,
57236
+ reserved: { system: 200, procedural: 200, volatile: 3400, working: 200 }
57237
+ };
57238
+ BASELINE = {
57239
+ contextPrecision: 0.9,
57240
+ contextRecall: 0.9,
57241
+ retrievalHitRate: 0.9,
57242
+ cacheHitRate: 0,
57243
+ tokensToSuccess: 0,
57244
+ costPerTask: 0,
57245
+ turnLatency: { p50: 0, p95: 0, p99: 0 }
57246
+ };
57247
+ GOLDEN_TRACES = [
57248
+ {
57249
+ id: "repo-structural-qa",
57250
+ name: "Repo-structural Q&A (CLI context engine)",
57251
+ description: "Five repo-grounded lookups that a code-aware context engine should answer by retrieving exactly the relevant fact and excluding unrelated tenant/domain noise.",
57252
+ expectedRecords: GOLDEN_CORPUS.slice(0, 5).map((r) => r.id),
57253
+ excludedRecords: GOLDEN_CORPUS.slice(5).map((r) => r.id),
57254
+ baseline: BASELINE,
57255
+ lastValidated: "2026-06-28",
57256
+ turns: [
57257
+ {
57258
+ taskFrame: frame(
57259
+ "What is the value of the DEFAULT_MODEL constant in the CLI agent model module?"
57260
+ ),
57261
+ budget: SIGNAL_BUDGET,
57262
+ requiredInContext: [hex64("cli-default-model")],
57263
+ forbiddenInContext: [hex64("noise-billing")],
57264
+ expectedToolCalls: [],
57265
+ expectedOutcome: "success"
57266
+ },
57267
+ {
57268
+ taskFrame: frame(
57269
+ "Which function does one-shot.ts import from pipeline to run a single turn? Mention runTurn."
57270
+ ),
57271
+ budget: SIGNAL_BUDGET,
57272
+ requiredInContext: [hex64("cli-oneshot-runturn")],
57273
+ forbiddenInContext: [hex64("noise-brand")],
57274
+ expectedToolCalls: [],
57275
+ expectedOutcome: "success"
57276
+ },
57277
+ {
57278
+ taskFrame: frame(
57279
+ "Name the three tools in the MUTATING_TOOLS constant in the CLI permissions module."
57280
+ ),
57281
+ budget: SIGNAL_BUDGET,
57282
+ requiredInContext: [hex64("cli-mutating-tools")],
57283
+ forbiddenInContext: [hex64("noise-rls")],
57284
+ expectedToolCalls: [],
57285
+ expectedOutcome: "success"
57286
+ },
57287
+ {
57288
+ taskFrame: frame(
57289
+ "Where does the CLI code-graph module import buildCodeGraph from?"
57290
+ ),
57291
+ budget: SIGNAL_BUDGET,
57292
+ requiredInContext: [hex64("cli-codegraph-builder")],
57293
+ forbiddenInContext: [hex64("noise-billing")],
57294
+ expectedToolCalls: [],
57295
+ expectedOutcome: "success"
57296
+ },
57297
+ {
57298
+ taskFrame: frame(
57299
+ "Which file does apps/app use for request interception that replaces middleware.ts? It is proxy.ts."
57300
+ ),
57301
+ budget: SIGNAL_BUDGET,
57302
+ requiredInContext: [hex64("app-proxy-file")],
57303
+ forbiddenInContext: [hex64("noise-brand")],
57304
+ expectedToolCalls: [],
57305
+ expectedOutcome: "success"
57306
+ }
57307
+ ]
57308
+ }
57309
+ ];
57310
+ GOLDEN_GROUND_TRUTH = {
57311
+ [hex64("cli-default-model")]: "anthropic/claude-sonnet-4.5",
57312
+ [hex64("cli-oneshot-runturn")]: "runTurn",
57313
+ [hex64("cli-mutating-tools")]: "write_file, edit_file, bash",
57314
+ [hex64("cli-codegraph-builder")]: "daemon/code-graph/builder",
57315
+ [hex64("app-proxy-file")]: "proxy.ts"
57316
+ };
57317
+ }
57318
+ });
57319
+
57320
+ // ../../packages/engram/src/eval/golden-compile.ts
57321
+ function terms2(text2) {
57322
+ const out2 = /* @__PURE__ */ new Set();
57323
+ for (const raw of text2.toLowerCase().split(/[^a-z0-9]+/)) {
57324
+ if (raw.length >= 3 && !STOPWORDS2.has(raw)) out2.add(raw);
57325
+ }
57326
+ return out2;
57327
+ }
57328
+ function factText(record2) {
57329
+ const body = record2.body;
57330
+ return `${body.fact ?? ""} ${body.domain ?? ""}`.trim();
57331
+ }
57332
+ function rankCorpus(corpus, taskFrame) {
57333
+ const query = terms2(`${taskFrame.taskDescription} ${taskFrame.workingSet.join(" ")}`);
57334
+ return corpus.map((record2) => {
57335
+ const text2 = factText(record2);
57336
+ let score = 0;
57337
+ for (const term of terms2(text2)) if (query.has(term)) score++;
57338
+ return { id: record2.id, score, text: text2, tokens: countTokens(text2, taskFrame.modelId) };
57339
+ }).filter((c) => c.score > 0).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
57340
+ }
57341
+ function makeGoldenCompileFn(corpus) {
57342
+ return async function compileGolden(taskFrame, budget) {
57343
+ const ranked = rankCorpus(corpus, taskFrame);
57344
+ const cap = budget.reserved.volatile;
57345
+ const packed = [];
57346
+ let used = 0;
57347
+ for (const candidate of ranked) {
57348
+ if (used + candidate.tokens > cap) continue;
57349
+ packed.push(candidate.id);
57350
+ used += candidate.tokens;
57351
+ }
57352
+ return packed;
57353
+ };
57354
+ }
57355
+ var STOPWORDS2;
57356
+ var init_golden_compile = __esm({
57357
+ "../../packages/engram/src/eval/golden-compile.ts"() {
57358
+ "use strict";
57359
+ init_tokenizer();
57360
+ STOPWORDS2 = /* @__PURE__ */ new Set([
57361
+ "the",
57362
+ "and",
57363
+ "for",
57364
+ "from",
57365
+ "with",
57366
+ "that",
57367
+ "this",
57368
+ "what",
57369
+ "which",
57370
+ "does",
57371
+ "where",
57372
+ "who",
57373
+ "whom",
57374
+ "into",
57375
+ "onto",
57376
+ "are",
57377
+ "was",
57378
+ "were",
57379
+ "has",
57380
+ "have",
57381
+ "its",
57382
+ "value",
57383
+ "constant",
57384
+ "module",
57385
+ "file",
57386
+ "mention",
57387
+ "answer",
57388
+ "just",
57389
+ "name",
57390
+ "give",
57391
+ "list",
57392
+ "used",
57393
+ "use",
57394
+ "uses",
57395
+ "set",
57396
+ "their",
57397
+ "you"
57398
+ ]);
57399
+ }
57400
+ });
57401
+
57402
+ // ../../packages/engram/src/eval/run-golden.ts
57403
+ import { fileURLToPath } from "node:url";
57404
+ import { dirname as dirname7, resolve as resolve5 } from "node:path";
57405
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "node:fs";
57406
+ function runGoldenSuite() {
57407
+ return runEvalSuite(GOLDEN_TRACES, makeGoldenCompileFn(GOLDEN_CORPUS));
57408
+ }
57409
+ async function buildRagDataset() {
57410
+ const compile2 = makeGoldenCompileFn(GOLDEN_CORPUS);
57411
+ const byId = new Map(GOLDEN_CORPUS.map((r) => [r.id, r]));
57412
+ const records = [];
57413
+ for (const trace2 of GOLDEN_TRACES) {
57414
+ for (const [i, turn] of trace2.turns.entries()) {
57415
+ const packedIds = await compile2(turn.taskFrame, turn.budget);
57416
+ const contexts = packedIds.map((id) => {
57417
+ const body = byId.get(id)?.body;
57418
+ return body?.fact ?? "";
57419
+ });
57420
+ const requiredId = turn.requiredInContext[0] ?? `${trace2.id}-${i}`;
57421
+ const groundTruth = GOLDEN_GROUND_TRUTH[requiredId] ?? "";
57422
+ records.push({
57423
+ id: `${trace2.id}-${i}`,
57424
+ question: turn.taskFrame.taskDescription,
57425
+ contexts,
57426
+ answer: groundTruth,
57427
+ ground_truth: groundTruth
57428
+ });
57429
+ }
57430
+ }
57431
+ return records;
57432
+ }
57433
+ function repoRoot() {
57434
+ return resolve5(dirname7(fileURLToPath(import.meta.url)), "../../../..");
57435
+ }
57436
+ async function writeRagDataset(outPath) {
57437
+ const dest = outPath ?? resolve5(repoRoot(), "bench/rag-eval/dataset.engram.jsonl");
57438
+ mkdirSync6(dirname7(dest), { recursive: true });
57439
+ const rows = await buildRagDataset();
57440
+ writeFileSync4(dest, rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
57441
+ return dest;
57442
+ }
57443
+ async function main() {
57444
+ const result = await runGoldenSuite();
57445
+ process.stdout.write(generateTextReport(result) + "\n");
57446
+ if (process.argv.includes("--export")) {
57447
+ const dest = await writeRagDataset();
57448
+ process.stdout.write(`
57449
+ Exported RAGAS/DeepEval dataset \u2192 ${dest}
57450
+ `);
57451
+ }
57452
+ if (!result.overallPassed) process.exitCode = 1;
57453
+ }
57454
+ function isDirectCliEntry() {
57455
+ try {
57456
+ const entry = process.argv[1];
57457
+ return entry !== void 0 && fileURLToPath(import.meta.url) === resolve5(entry);
57458
+ } catch {
57459
+ return false;
57460
+ }
57461
+ }
57462
+ var init_run_golden = __esm({
57463
+ "../../packages/engram/src/eval/run-golden.ts"() {
57464
+ "use strict";
57465
+ init_harness();
57466
+ init_report();
57467
+ init_golden_fixtures();
57468
+ init_golden_compile();
57469
+ if (isDirectCliEntry()) {
57470
+ void main();
57471
+ }
57472
+ }
57473
+ });
57474
+
56665
57475
  // ../../packages/engram/src/index.ts
56666
57476
  var src_exports = {};
56667
57477
  __export(src_exports, {
@@ -56675,6 +57485,9 @@ __export(src_exports, {
56675
57485
  EdgeBodySchema: () => EdgeBodySchema,
56676
57486
  EntityBodySchema: () => EntityBodySchema,
56677
57487
  EpisodicBodySchema: () => EpisodicBodySchema,
57488
+ GOLDEN_CORPUS: () => GOLDEN_CORPUS,
57489
+ GOLDEN_GROUND_TRUTH: () => GOLDEN_GROUND_TRUTH,
57490
+ GOLDEN_TRACES: () => GOLDEN_TRACES,
56678
57491
  GraphRetrievalEngine: () => GraphRetrievalEngine,
56679
57492
  GraphStore: () => GraphStore,
56680
57493
  IntentLedger: () => IntentLedger,
@@ -56702,6 +57515,7 @@ __export(src_exports, {
56702
57515
  batchByPriority: () => batchByPriority,
56703
57516
  buildLayout: () => buildLayout,
56704
57517
  buildMerkleTree: () => buildMerkleTree,
57518
+ buildRagDataset: () => buildRagDataset,
56705
57519
  clearCompileTelemetrySinkForTests: () => clearCompileTelemetrySinkForTests,
56706
57520
  clearGraphSyncClientForTests: () => clearGraphSyncClientForTests,
56707
57521
  clusterEvents: () => clusterEvents,
@@ -56756,6 +57570,7 @@ __export(src_exports, {
56756
57570
  identifyEvictionCandidates: () => identifyEvictionCandidates,
56757
57571
  initHash: () => initHash,
56758
57572
  isWithinNamespace: () => isWithinNamespace,
57573
+ makeGoldenCompileFn: () => makeGoldenCompileFn,
56759
57574
  mapBody: () => mapBody,
56760
57575
  mapKind: () => mapKind,
56761
57576
  mapNamespace: () => mapNamespace,
@@ -56770,10 +57585,12 @@ __export(src_exports, {
56770
57585
  prioritizeForSync: () => prioritizeForSync,
56771
57586
  promotePatterns: () => promotePatterns,
56772
57587
  quantizeToInt8: () => quantizeToInt8,
57588
+ rankCorpus: () => rankCorpus,
56773
57589
  relate: () => relate,
56774
57590
  remember: () => remember,
56775
57591
  resolveConflict: () => resolveConflict,
56776
57592
  runEvalSuite: () => runEvalSuite,
57593
+ runGoldenSuite: () => runGoldenSuite,
56777
57594
  sessionNamespace: () => sessionNamespace,
56778
57595
  setCompileTelemetrySink: () => setCompileTelemetrySink,
56779
57596
  setGraphSyncClient: () => setGraphSyncClient,
@@ -56784,7 +57601,8 @@ __export(src_exports, {
56784
57601
  validateNamespace: () => validateNamespace,
56785
57602
  validateTurn: () => validateTurn,
56786
57603
  verifyRecordIntegrity: () => verifyRecordIntegrity,
56787
- workspaceNamespace: () => workspaceNamespace
57604
+ workspaceNamespace: () => workspaceNamespace,
57605
+ writeRagDataset: () => writeRagDataset
56788
57606
  });
56789
57607
  function createEngram(store) {
56790
57608
  return {
@@ -56804,7 +57622,7 @@ var init_src = __esm({
56804
57622
  init_record2();
56805
57623
  init_namespace();
56806
57624
  init_salience();
56807
- init_store();
57625
+ init_store2();
56808
57626
  init_graph_store();
56809
57627
  init_remember();
56810
57628
  init_assert();
@@ -56858,15 +57676,18 @@ var init_src = __esm({
56858
57676
  init_metrics();
56859
57677
  init_golden_traces();
56860
57678
  init_report();
57679
+ init_run_golden();
57680
+ init_golden_fixtures();
57681
+ init_golden_compile();
56861
57682
  }
56862
57683
  });
56863
57684
 
56864
57685
  // src/agent/memory.ts
56865
- import { mkdirSync as mkdirSync5 } from "node:fs";
56866
- import { homedir as homedir8 } from "node:os";
56867
- import { basename as basename5, dirname as dirname6, join as join13 } from "node:path";
57686
+ import { mkdirSync as mkdirSync7 } from "node:fs";
57687
+ import { homedir as homedir9 } from "node:os";
57688
+ import { basename as basename5, dirname as dirname8, join as join14 } from "node:path";
56868
57689
  function dbPath() {
56869
- return join13(homedir8(), ".config", "oxagen", "context.duckdb");
57690
+ return join14(homedir9(), ".config", "oxagen", "context.duckdb");
56870
57691
  }
56871
57692
  function projectKey2(cwd2) {
56872
57693
  return basename5(cwd2) || "default";
@@ -56875,7 +57696,7 @@ async function openSessionMemory(cwd2, sessionId) {
56875
57696
  let store;
56876
57697
  try {
56877
57698
  const path2 = dbPath();
56878
- mkdirSync5(dirname6(path2), { recursive: true });
57699
+ mkdirSync7(dirname8(path2), { recursive: true });
56879
57700
  store = createStore({ adapter: "duckdb", duckdbPath: path2 });
56880
57701
  } catch (err) {
56881
57702
  if (process.env["OXAGEN_DEBUG"])
@@ -56953,14 +57774,14 @@ var init_memory2 = __esm({
56953
57774
  });
56954
57775
 
56955
57776
  // src/agent/trace-store.ts
56956
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync10 } from "node:fs";
56957
- import { homedir as homedir9 } from "node:os";
56958
- import { basename as basename6, dirname as dirname7, join as join14 } from "node:path";
57777
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "node:fs";
57778
+ import { homedir as homedir10 } from "node:os";
57779
+ import { basename as basename6, dirname as dirname9, join as join15 } from "node:path";
56959
57780
  function projectKey3(cwd2) {
56960
57781
  return basename6(cwd2) || "default";
56961
57782
  }
56962
57783
  function storePath(cwd2) {
56963
- return join14(homedir9(), ".config", "oxagen", "traces", `${projectKey3(cwd2)}.json`);
57784
+ return join15(homedir10(), ".config", "oxagen", "traces", `${projectKey3(cwd2)}.json`);
56964
57785
  }
56965
57786
  function read(cwd2) {
56966
57787
  const path2 = storePath(cwd2);
@@ -56975,8 +57796,8 @@ function read(cwd2) {
56975
57796
  function write(cwd2, data) {
56976
57797
  const path2 = storePath(cwd2);
56977
57798
  try {
56978
- mkdirSync6(dirname7(path2), { recursive: true });
56979
- writeFileSync4(path2, JSON.stringify(data, null, 2), "utf8");
57799
+ mkdirSync8(dirname9(path2), { recursive: true });
57800
+ writeFileSync5(path2, JSON.stringify(data, null, 2), "utf8");
56980
57801
  } catch {
56981
57802
  }
56982
57803
  }
@@ -57019,19 +57840,19 @@ var init_trace_store = __esm({
57019
57840
  });
57020
57841
 
57021
57842
  // src/agent/verbose-log.ts
57022
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7, existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
57023
- import { homedir as homedir10 } from "node:os";
57024
- import { basename as basename7, dirname as dirname8, join as join15 } from "node:path";
57843
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9, existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
57844
+ import { homedir as homedir11 } from "node:os";
57845
+ import { basename as basename7, dirname as dirname10, join as join16 } from "node:path";
57025
57846
  function projectKey4(cwd2) {
57026
57847
  return basename7(cwd2) || "default";
57027
57848
  }
57028
57849
  function verboseLogPath(cwd2) {
57029
- return join15(homedir10(), ".config", "oxagen", "verbose", `${projectKey4(cwd2)}.jsonl`);
57850
+ return join16(homedir11(), ".config", "oxagen", "verbose", `${projectKey4(cwd2)}.jsonl`);
57030
57851
  }
57031
57852
  function appendVerboseLog(cwd2, trace2) {
57032
57853
  const path2 = verboseLogPath(cwd2);
57033
57854
  try {
57034
- mkdirSync7(dirname8(path2), { recursive: true });
57855
+ mkdirSync9(dirname10(path2), { recursive: true });
57035
57856
  appendFileSync2(path2, JSON.stringify(trace2) + "\n", "utf8");
57036
57857
  } catch {
57037
57858
  }
@@ -58209,21 +59030,21 @@ var require_react_development = __commonJS({
58209
59030
  );
58210
59031
  actScopeDepth = prevActScopeDepth;
58211
59032
  }
58212
- function recursivelyFlushAsyncActWork(returnValue, resolve5, reject) {
59033
+ function recursivelyFlushAsyncActWork(returnValue, resolve6, reject) {
58213
59034
  var queue = ReactSharedInternals.actQueue;
58214
59035
  if (null !== queue)
58215
59036
  if (0 !== queue.length)
58216
59037
  try {
58217
59038
  flushActQueue(queue);
58218
59039
  enqueueTask(function() {
58219
- return recursivelyFlushAsyncActWork(returnValue, resolve5, reject);
59040
+ return recursivelyFlushAsyncActWork(returnValue, resolve6, reject);
58220
59041
  });
58221
59042
  return;
58222
59043
  } catch (error40) {
58223
59044
  ReactSharedInternals.thrownErrors.push(error40);
58224
59045
  }
58225
59046
  else ReactSharedInternals.actQueue = null;
58226
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve5(returnValue);
59047
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve6(returnValue);
58227
59048
  }
58228
59049
  function flushActQueue(queue) {
58229
59050
  if (!isFlushing) {
@@ -58410,7 +59231,7 @@ var require_react_development = __commonJS({
58410
59231
  ));
58411
59232
  });
58412
59233
  return {
58413
- then: function(resolve5, reject) {
59234
+ then: function(resolve6, reject) {
58414
59235
  didAwaitActCall = true;
58415
59236
  thenable.then(
58416
59237
  function(returnValue) {
@@ -58420,7 +59241,7 @@ var require_react_development = __commonJS({
58420
59241
  flushActQueue(queue), enqueueTask(function() {
58421
59242
  return recursivelyFlushAsyncActWork(
58422
59243
  returnValue,
58423
- resolve5,
59244
+ resolve6,
58424
59245
  reject
58425
59246
  );
58426
59247
  });
@@ -58434,7 +59255,7 @@ var require_react_development = __commonJS({
58434
59255
  ReactSharedInternals.thrownErrors.length = 0;
58435
59256
  reject(_thrownError);
58436
59257
  }
58437
- } else resolve5(returnValue);
59258
+ } else resolve6(returnValue);
58438
59259
  },
58439
59260
  function(error40) {
58440
59261
  popActScope(prevActQueue, prevActScopeDepth);
@@ -58456,15 +59277,15 @@ var require_react_development = __commonJS({
58456
59277
  if (0 < ReactSharedInternals.thrownErrors.length)
58457
59278
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
58458
59279
  return {
58459
- then: function(resolve5, reject) {
59280
+ then: function(resolve6, reject) {
58460
59281
  didAwaitActCall = true;
58461
59282
  0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
58462
59283
  return recursivelyFlushAsyncActWork(
58463
59284
  returnValue$jscomp$0,
58464
- resolve5,
59285
+ resolve6,
58465
59286
  reject
58466
59287
  );
58467
- })) : resolve5(returnValue$jscomp$0);
59288
+ })) : resolve6(returnValue$jscomp$0);
58468
59289
  }
58469
59290
  };
58470
59291
  };
@@ -62986,9 +63807,9 @@ var require_react_reconciler_production = __commonJS({
62986
63807
  if (1 !== RunInRootFrame || 1 !== namePropDescriptor) {
62987
63808
  do
62988
63809
  if (RunInRootFrame--, namePropDescriptor--, 0 > namePropDescriptor || sampleLines[RunInRootFrame] !== controlLines[namePropDescriptor]) {
62989
- var frame = "\n" + sampleLines[RunInRootFrame].replace(" at new ", " at ");
62990
- fn.displayName && frame.includes("<anonymous>") && (frame = frame.replace("<anonymous>", fn.displayName));
62991
- return frame;
63810
+ var frame2 = "\n" + sampleLines[RunInRootFrame].replace(" at new ", " at ");
63811
+ fn.displayName && frame2.includes("<anonymous>") && (frame2 = frame2.replace("<anonymous>", fn.displayName));
63812
+ return frame2;
62992
63813
  }
62993
63814
  while (1 <= RunInRootFrame && 0 <= namePropDescriptor);
62994
63815
  }
@@ -63493,8 +64314,8 @@ var require_react_reconciler_production = __commonJS({
63493
64314
  currentEntangledActionThenable = {
63494
64315
  status: "pending",
63495
64316
  value: void 0,
63496
- then: function(resolve5) {
63497
- entangledListeners.push(resolve5);
64317
+ then: function(resolve6) {
64318
+ entangledListeners.push(resolve6);
63498
64319
  }
63499
64320
  };
63500
64321
  }
@@ -63517,8 +64338,8 @@ var require_react_reconciler_production = __commonJS({
63517
64338
  status: "pending",
63518
64339
  value: null,
63519
64340
  reason: null,
63520
- then: function(resolve5) {
63521
- listeners.push(resolve5);
64341
+ then: function(resolve6) {
64342
+ listeners.push(resolve6);
63522
64343
  }
63523
64344
  };
63524
64345
  thenable.then(
@@ -71890,10 +72711,10 @@ var require_react_reconciler_development = __commonJS({
71890
72711
  }
71891
72712
  function describeNativeComponentFrame(fn, construct) {
71892
72713
  if (!fn || reentry) return "";
71893
- var frame = componentFrameCache.get(fn);
71894
- if (void 0 !== frame) return frame;
72714
+ var frame2 = componentFrameCache.get(fn);
72715
+ if (void 0 !== frame2) return frame2;
71895
72716
  reentry = true;
71896
- frame = Error.prepareStackTrace;
72717
+ frame2 = Error.prepareStackTrace;
71897
72718
  Error.prepareStackTrace = void 0;
71898
72719
  var previousDispatcher = null;
71899
72720
  previousDispatcher = ReactSharedInternals.H;
@@ -71986,7 +72807,7 @@ var require_react_reconciler_development = __commonJS({
71986
72807
  }
71987
72808
  }
71988
72809
  } finally {
71989
- reentry = false, ReactSharedInternals.H = previousDispatcher, reenableLogs(), Error.prepareStackTrace = frame;
72810
+ reentry = false, ReactSharedInternals.H = previousDispatcher, reenableLogs(), Error.prepareStackTrace = frame2;
71990
72811
  }
71991
72812
  sampleLines = (sampleLines = fn ? fn.displayName || fn.name : "") ? describeBuiltInComponentFrame(sampleLines) : "";
71992
72813
  "function" === typeof fn && componentFrameCache.set(fn, sampleLines);
@@ -73117,8 +73938,8 @@ var require_react_reconciler_development = __commonJS({
73117
73938
  currentEntangledActionThenable = {
73118
73939
  status: "pending",
73119
73940
  value: void 0,
73120
- then: function(resolve5) {
73121
- entangledListeners.push(resolve5);
73941
+ then: function(resolve6) {
73942
+ entangledListeners.push(resolve6);
73122
73943
  }
73123
73944
  };
73124
73945
  }
@@ -73141,8 +73962,8 @@ var require_react_reconciler_development = __commonJS({
73141
73962
  status: "pending",
73142
73963
  value: null,
73143
73964
  reason: null,
73144
- then: function(resolve5) {
73145
- listeners.push(resolve5);
73965
+ then: function(resolve6) {
73966
+ listeners.push(resolve6);
73146
73967
  }
73147
73968
  };
73148
73969
  thenable.then(
@@ -88591,7 +89412,7 @@ var require_websocket = __commonJS({
88591
89412
  var http = __require("http");
88592
89413
  var net3 = __require("net");
88593
89414
  var tls = __require("tls");
88594
- var { randomBytes, createHash: createHash2 } = __require("crypto");
89415
+ var { randomBytes, createHash: createHash5 } = __require("crypto");
88595
89416
  var { Duplex, Readable } = __require("stream");
88596
89417
  var { URL: URL2 } = __require("url");
88597
89418
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -89259,7 +90080,7 @@ var require_websocket = __commonJS({
89259
90080
  abortHandshake(websocket, socket, "Invalid Upgrade header");
89260
90081
  return;
89261
90082
  }
89262
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
90083
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
89263
90084
  if (res.headers["sec-websocket-accept"] !== digest) {
89264
90085
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
89265
90086
  return;
@@ -89628,7 +90449,7 @@ var require_websocket_server = __commonJS({
89628
90449
  var EventEmitter4 = __require("events");
89629
90450
  var http = __require("http");
89630
90451
  var { Duplex } = __require("stream");
89631
- var { createHash: createHash2 } = __require("crypto");
90452
+ var { createHash: createHash5 } = __require("crypto");
89632
90453
  var extension2 = require_extension();
89633
90454
  var PerMessageDeflate2 = require_permessage_deflate();
89634
90455
  var subprotocol2 = require_subprotocol();
@@ -89935,7 +90756,7 @@ var require_websocket_server = __commonJS({
89935
90756
  );
89936
90757
  }
89937
90758
  if (this._state > RUNNING) return abortHandshake(socket, 503);
89938
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
90759
+ const digest = createHash5("sha1").update(key + GUID).digest("base64");
89939
90760
  const headers = [
89940
90761
  "HTTP/1.1 101 Switching Protocols",
89941
90762
  "Upgrade: websocket",
@@ -90118,22 +90939,22 @@ var init_devtools = __esm({
90118
90939
  init_devtools_window_polyfill();
90119
90940
  init_wrapper();
90120
90941
  init_react_devtools_core();
90121
- isDevToolsReachable = async () => new Promise((resolve5) => {
90942
+ isDevToolsReachable = async () => new Promise((resolve6) => {
90122
90943
  const socket = new wrapper_default("ws://localhost:8097");
90123
90944
  const timeout = setTimeout(() => {
90124
90945
  socket.terminate();
90125
- resolve5(false);
90946
+ resolve6(false);
90126
90947
  }, 2e3);
90127
90948
  timeout.unref();
90128
90949
  socket.on("open", () => {
90129
90950
  clearTimeout(timeout);
90130
90951
  socket.terminate();
90131
- resolve5(true);
90952
+ resolve6(true);
90132
90953
  });
90133
90954
  socket.on("error", () => {
90134
90955
  clearTimeout(timeout);
90135
90956
  socket.terminate();
90136
- resolve5(false);
90957
+ resolve6(false);
90137
90958
  });
90138
90959
  });
90139
90960
  if (await isDevToolsReachable()) {
@@ -93901,8 +94722,8 @@ var init_ink = __esm({
93901
94722
  noop2 = () => {
93902
94723
  };
93903
94724
  textEncoder = new TextEncoder();
93904
- yieldImmediate = async () => new Promise((resolve5) => {
93905
- setImmediate(resolve5);
94725
+ yieldImmediate = async () => new Promise((resolve6) => {
94726
+ setImmediate(resolve6);
93906
94727
  });
93907
94728
  kittyQueryEscapeByte = 27;
93908
94729
  kittyQueryOpenBracketByte = 91;
@@ -94119,8 +94940,8 @@ var init_ink = __esm({
94119
94940
  };
94120
94941
  }
94121
94942
  this.initKittyKeyboard();
94122
- this.exitPromise = new Promise((resolve5, reject) => {
94123
- this.resolveExitPromise = resolve5;
94943
+ this.exitPromise = new Promise((resolve6, reject) => {
94944
+ this.resolveExitPromise = resolve6;
94124
94945
  this.rejectExitPromise = reject;
94125
94946
  });
94126
94947
  void this.exitPromise.catch(noop2);
@@ -94431,9 +95252,9 @@ var init_ink = __esm({
94431
95252
  settleThrottle(this.throttledOnRender, canWriteToStdout);
94432
95253
  settleThrottle(this.throttledLog, canWriteToStdout);
94433
95254
  if (canWriteToStdout && hasWritableState) {
94434
- await new Promise((resolve5) => {
95255
+ await new Promise((resolve6) => {
94435
95256
  this.options.stdout.write("", () => {
94436
- resolve5();
95257
+ resolve6();
94437
95258
  });
94438
95259
  });
94439
95260
  return;
@@ -94519,8 +95340,8 @@ var init_ink = __esm({
94519
95340
  async awaitNextRender() {
94520
95341
  if (!this.nextRenderCommit) {
94521
95342
  let resolveRender;
94522
- const promise2 = new Promise((resolve5) => {
94523
- resolveRender = resolve5;
95343
+ const promise2 = new Promise((resolve6) => {
95344
+ resolveRender = resolve6;
94524
95345
  });
94525
95346
  this.nextRenderCommit = { promise: promise2, resolve: resolveRender };
94526
95347
  }
@@ -95826,8 +96647,8 @@ var init_theme = __esm({
95826
96647
 
95827
96648
  // src/slash/loader.ts
95828
96649
  import { readdirSync as readdirSync3, existsSync as existsSync13 } from "node:fs";
95829
- import { join as join16, basename as basename8 } from "node:path";
95830
- import { homedir as homedir11 } from "node:os";
96650
+ import { join as join17, basename as basename8 } from "node:path";
96651
+ import { homedir as homedir12 } from "node:os";
95831
96652
  import { readFileSync as readFileSync12 } from "node:fs";
95832
96653
  function defFromFile(path2) {
95833
96654
  let raw;
@@ -95857,17 +96678,17 @@ function loadDir3(dir, into) {
95857
96678
  return;
95858
96679
  }
95859
96680
  for (const file2 of files.sort()) {
95860
- const def = defFromFile(join16(dir, file2));
96681
+ const def = defFromFile(join17(dir, file2));
95861
96682
  if (def) into.set(def.name, def);
95862
96683
  }
95863
96684
  }
95864
96685
  function loadCommands(opts = {}) {
95865
96686
  const cwd2 = opts.cwd ?? process.cwd();
95866
- const userDir = opts.userCommandsDir ?? join16(homedir11(), ".config", "oxagen", "commands");
96687
+ const userDir = opts.userCommandsDir ?? join17(homedir12(), ".config", "oxagen", "commands");
95867
96688
  const registry2 = /* @__PURE__ */ new Map();
95868
96689
  loadDir3(userDir, registry2);
95869
- loadDir3(join16(cwd2, ".claude", "commands"), registry2);
95870
- loadDir3(join16(cwd2, ".oxagen", "commands"), registry2);
96690
+ loadDir3(join17(cwd2, ".claude", "commands"), registry2);
96691
+ loadDir3(join17(cwd2, ".oxagen", "commands"), registry2);
95871
96692
  return registry2;
95872
96693
  }
95873
96694
  var init_loader3 = __esm({
@@ -95979,14 +96800,14 @@ function ThinkingIndicator({
95979
96800
  startedAt,
95980
96801
  getTokens
95981
96802
  }) {
95982
- const [frame, setFrame] = (0, import_react34.useState)(0);
96803
+ const [frame2, setFrame] = (0, import_react34.useState)(0);
95983
96804
  (0, import_react34.useEffect)(() => {
95984
96805
  const id = setInterval(() => setFrame((f) => f + 1), 100);
95985
96806
  return () => clearInterval(id);
95986
96807
  }, []);
95987
96808
  const elapsed2 = Math.max(0, Math.round((Date.now() - startedAt) / 1e3));
95988
96809
  const tokens = getTokens();
95989
- return /* @__PURE__ */ import_react34.default.createElement(Box_default, { paddingX: 1 }, /* @__PURE__ */ import_react34.default.createElement(Text, { color: "#FBBF24", bold: true }, SPINNER[frame % SPINNER.length], " "), /* @__PURE__ */ import_react34.default.createElement(Text, { color: "#FBBF24" }, "Thinking\u2026 "), /* @__PURE__ */ import_react34.default.createElement(Text, { dimColor: true }, elapsed2, "s", tokens > 0 ? ` \xB7 ~${humanizeTokens(tokens)} tok` : "", " \xB7 esc to cancel"));
96810
+ return /* @__PURE__ */ import_react34.default.createElement(Box_default, { paddingX: 1 }, /* @__PURE__ */ import_react34.default.createElement(Text, { color: "#FBBF24", bold: true }, SPINNER[frame2 % SPINNER.length], " "), /* @__PURE__ */ import_react34.default.createElement(Text, { color: "#FBBF24" }, "Thinking\u2026 "), /* @__PURE__ */ import_react34.default.createElement(Text, { dimColor: true }, elapsed2, "s", tokens > 0 ? ` \xB7 ~${humanizeTokens(tokens)} tok` : "", " \xB7 esc to cancel"));
95990
96811
  }
95991
96812
  function StatusLine({
95992
96813
  model,
@@ -96156,7 +96977,7 @@ function ReplApp({
96156
96977
  brokerRef.current = new PermissionBroker({
96157
96978
  mode: modeRef.current,
96158
96979
  cwd: cwd2,
96159
- approver: (req) => new Promise((resolve5) => setApproval({ req, resolve: resolve5 }))
96980
+ approver: (req) => new Promise((resolve6) => setApproval({ req, resolve: resolve6 }))
96160
96981
  });
96161
96982
  }
96162
96983
  (0, import_react35.useEffect)(() => {
@@ -96915,8 +97736,8 @@ var init_orchestrator = __esm({
96915
97736
  /** Begin scheduling. Resolves when every task has reached a terminal state. */
96916
97737
  start() {
96917
97738
  if (!this.donePromise) {
96918
- this.donePromise = new Promise((resolve5) => {
96919
- this.settle = resolve5;
97739
+ this.donePromise = new Promise((resolve6) => {
97740
+ this.settle = resolve6;
96920
97741
  });
96921
97742
  }
96922
97743
  this.pump();
@@ -97263,14 +98084,14 @@ var init_planner = __esm({
97263
98084
  });
97264
98085
 
97265
98086
  // src/agent/fleet/store.ts
97266
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync14 } from "node:fs";
97267
- import { homedir as homedir12 } from "node:os";
97268
- import { basename as basename9, dirname as dirname9, join as join17 } from "node:path";
98087
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync6, existsSync as existsSync14 } from "node:fs";
98088
+ import { homedir as homedir13 } from "node:os";
98089
+ import { basename as basename9, dirname as dirname11, join as join18 } from "node:path";
97269
98090
  function projectKey5(cwd2) {
97270
98091
  return basename9(cwd2) || "default";
97271
98092
  }
97272
98093
  function storePath2(cwd2) {
97273
- return join17(homedir12(), ".config", "oxagen", "fleet", `${projectKey5(cwd2)}.json`);
98094
+ return join18(homedir13(), ".config", "oxagen", "fleet", `${projectKey5(cwd2)}.json`);
97274
98095
  }
97275
98096
  function read2(cwd2) {
97276
98097
  const path2 = storePath2(cwd2);
@@ -97284,8 +98105,8 @@ function read2(cwd2) {
97284
98105
  function write2(cwd2, data) {
97285
98106
  const path2 = storePath2(cwd2);
97286
98107
  try {
97287
- mkdirSync8(dirname9(path2), { recursive: true });
97288
- writeFileSync5(path2, JSON.stringify(data, null, 2), "utf8");
98108
+ mkdirSync10(dirname11(path2), { recursive: true });
98109
+ writeFileSync6(path2, JSON.stringify(data, null, 2), "utf8");
97289
98110
  } catch {
97290
98111
  }
97291
98112
  }
@@ -97322,21 +98143,21 @@ function openPlanStore(cwd2) {
97322
98143
  }
97323
98144
  };
97324
98145
  }
97325
- var init_store2 = __esm({
98146
+ var init_store3 = __esm({
97326
98147
  "src/agent/fleet/store.ts"() {
97327
98148
  }
97328
98149
  });
97329
98150
 
97330
98151
  // src/agent/fleet/git-isolation.ts
97331
98152
  import { execFile } from "node:child_process";
97332
- import { mkdirSync as mkdirSync9, existsSync as existsSync15, readFileSync as readFileSync14, appendFileSync as appendFileSync3 } from "node:fs";
97333
- import { join as join18 } from "node:path";
98153
+ import { mkdirSync as mkdirSync11, existsSync as existsSync15, readFileSync as readFileSync14, appendFileSync as appendFileSync3 } from "node:fs";
98154
+ import { join as join19 } from "node:path";
97334
98155
  import { promisify as promisify2 } from "node:util";
97335
- function defaultGitRunner(repoRoot) {
98156
+ function defaultGitRunner(repoRoot2) {
97336
98157
  return async (args, cwd2) => {
97337
98158
  try {
97338
98159
  const { stdout, stderr } = await pexec("git", args, {
97339
- cwd: cwd2 ?? repoRoot,
98160
+ cwd: cwd2 ?? repoRoot2,
97340
98161
  maxBuffer: 64 * 1024 * 1024
97341
98162
  });
97342
98163
  return { stdout, stderr, code: 0 };
@@ -97380,7 +98201,7 @@ var init_git_isolation = __esm({
97380
98201
  this.git = opts.git ?? defaultGitRunner(opts.repoRoot);
97381
98202
  this.ns = safeSegment(opts.namespace ?? "fleet");
97382
98203
  this.repoRoot = opts.repoRoot;
97383
- this.root = opts.root ?? join18(opts.repoRoot, ".oxagen", "worktrees");
98204
+ this.root = opts.root ?? join19(opts.repoRoot, ".oxagen", "worktrees");
97384
98205
  }
97385
98206
  /**
97386
98207
  * Resolve the repo toplevel, freeze the base ref, enable `rerere`, and keep
@@ -97393,13 +98214,13 @@ var init_git_isolation = __esm({
97393
98214
  throw new Error(`git isolation: not a git repository (${top.stderr.trim()})`);
97394
98215
  }
97395
98216
  this.repoRoot = top.stdout.trim();
97396
- if (!this.opts.root) this.root = join18(this.repoRoot, ".oxagen", "worktrees");
98217
+ if (!this.opts.root) this.root = join19(this.repoRoot, ".oxagen", "worktrees");
97397
98218
  const head = await this.git(["rev-parse", "HEAD"]);
97398
98219
  if (head.code !== 0) throw new Error(`git isolation: cannot resolve HEAD (${head.stderr.trim()})`);
97399
98220
  this.baseRef = head.stdout.trim();
97400
98221
  await this.git(["config", "rerere.enabled", "true"]);
97401
98222
  this.excludeWorktreeRoot();
97402
- mkdirSync9(this.root, { recursive: true });
98223
+ mkdirSync11(this.root, { recursive: true });
97403
98224
  this.initialized = true;
97404
98225
  }
97405
98226
  /** The frozen base every task fans out from. */
@@ -97414,7 +98235,7 @@ var init_git_isolation = __esm({
97414
98235
  await this.init();
97415
98236
  const seg = safeSegment(taskId);
97416
98237
  const branch = `fleet/${this.ns}/t/${seg}`;
97417
- const path2 = join18(this.root, this.ns, `t-${seg}`);
98238
+ const path2 = join19(this.root, this.ns, `t-${seg}`);
97418
98239
  await this.removeWorktree(path2);
97419
98240
  await this.git(["branch", "-D", branch]);
97420
98241
  const add = await this.git(["worktree", "add", "-b", branch, path2, this.baseRef]);
@@ -97517,7 +98338,7 @@ var init_git_isolation = __esm({
97517
98338
  async ensureIntegration() {
97518
98339
  if (this.integration) return this.integration;
97519
98340
  const branch = `fleet/${this.ns}/integration`;
97520
- const path2 = join18(this.root, this.ns, "integration");
98341
+ const path2 = join19(this.root, this.ns, "integration");
97521
98342
  await this.removeWorktree(path2);
97522
98343
  await this.git(["branch", "-D", branch]);
97523
98344
  const add = await this.git(["worktree", "add", "-b", branch, path2, this.baseRef]);
@@ -97542,7 +98363,7 @@ var init_git_isolation = __esm({
97542
98363
  /** Keep `.oxagen/` out of git's untracked list without touching tracked .gitignore. */
97543
98364
  excludeWorktreeRoot() {
97544
98365
  try {
97545
- const exclude = join18(this.repoRoot, ".git", "info", "exclude");
98366
+ const exclude = join19(this.repoRoot, ".git", "info", "exclude");
97546
98367
  if (!existsSync15(exclude)) return;
97547
98368
  const body = readFileSync14(exclude, "utf8");
97548
98369
  if (!body.split("\n").some((l) => l.trim() === ".oxagen/")) {
@@ -97578,10 +98399,10 @@ var init_fleet_summary = __esm({
97578
98399
  function tierColor(tier) {
97579
98400
  return tier === "fast" ? "#34D399" : tier === "balanced" ? theme.cyan : theme.violet;
97580
98401
  }
97581
- function glyphFor(status, frame) {
98402
+ function glyphFor(status, frame2) {
97582
98403
  switch (status) {
97583
98404
  case "running":
97584
- return { ch: SPINNER2[frame % SPINNER2.length] ?? "\u280B", color: theme.cyan };
98405
+ return { ch: SPINNER2[frame2 % SPINNER2.length] ?? "\u280B", color: theme.cyan };
97585
98406
  case "done":
97586
98407
  return { ch: "\u2713", color: "#34D399" };
97587
98408
  case "failed":
@@ -97605,12 +98426,12 @@ function RosterHeader() {
97605
98426
  }
97606
98427
  function AgentRow({
97607
98428
  agent: agent2,
97608
- frame,
98429
+ frame: frame2,
97609
98430
  showDetail
97610
98431
  }) {
97611
98432
  const running = agent2.status === "running";
97612
98433
  const dim = agent2.status === "queued" || agent2.status === "cancelled";
97613
- const { ch, color } = glyphFor(agent2.status, frame);
98434
+ const { ch, color } = glyphFor(agent2.status, frame2);
97614
98435
  const tokens = agent2.usage.inputTokens + agent2.usage.outputTokens;
97615
98436
  const tool2 = agent2.lastTool ?? (running ? "thinking\u2026" : agent2.status === "queued" ? "waiting" : "\u2014");
97616
98437
  const detail = agent2.status === "failed" && agent2.error ? agent2.error : agent2.logTail.replace(/\s+/g, " ").trim();
@@ -97668,7 +98489,7 @@ function DispatchInput({
97668
98489
  setValue((v) => v + input);
97669
98490
  }
97670
98491
  });
97671
- return /* @__PURE__ */ import_react45.default.createElement(Box_default, { borderStyle: "round", borderColor: theme.cyan, paddingX: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: theme.cyan, bold: true }, theme.pointer, " "), /* @__PURE__ */ import_react45.default.createElement(Text, null, value, /* @__PURE__ */ import_react45.default.createElement(Text, { color: theme.cyan }, "\u2588")), value ? null : /* @__PURE__ */ import_react45.default.createElement(Text, { dimColor: true }, " describe a task and press enter to dispatch an agent"));
98492
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { borderStyle: "round", borderColor: theme.cyan, paddingX: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: theme.cyan, bold: true }, theme.pointer, " "), /* @__PURE__ */ import_react45.default.createElement(Text, null, value, /* @__PURE__ */ import_react45.default.createElement(Text, { color: theme.cyan }, "\u2588")), value ? null : /* @__PURE__ */ import_react45.default.createElement(Text, { dimColor: true }, " submit a prompt using enter to dispatch an agent"));
97672
98493
  }
97673
98494
  var import_react45;
97674
98495
  var init_dispatch_input = __esm({
@@ -97687,7 +98508,7 @@ function FleetApp({
97687
98508
  }) {
97688
98509
  const { exit } = use_app_default();
97689
98510
  const [snap, setSnap] = (0, import_react46.useState)(() => fleet.snapshot());
97690
- const [frame, setFrame] = (0, import_react46.useState)(0);
98511
+ const [frame2, setFrame] = (0, import_react46.useState)(0);
97691
98512
  const [phase, setPhase] = (0, import_react46.useState)(goal && plan ? "planning" : "running");
97692
98513
  const [error40, setError] = (0, import_react46.useState)("");
97693
98514
  const [showDetail, setShowDetail] = (0, import_react46.useState)(false);
@@ -97744,12 +98565,12 @@ function FleetApp({
97744
98565
  return /* @__PURE__ */ import_react46.default.createElement(Box_default, { paddingX: 1, marginY: 1 }, /* @__PURE__ */ import_react46.default.createElement(Text, { color: "#F87171", bold: true }, "\u2717", " "), /* @__PURE__ */ import_react46.default.createElement(Text, { wrap: "wrap" }, error40));
97745
98566
  }
97746
98567
  const agents = snap.agents;
97747
- return /* @__PURE__ */ import_react46.default.createElement(Box_default, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ import_react46.default.createElement(FleetSummary, { snap }), phase === "planning" ? /* @__PURE__ */ import_react46.default.createElement(Box_default, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ import_react46.default.createElement(Text, { color: "#FBBF24", bold: true }, SPINNER3[frame % SPINNER3.length] ?? "\u280B", " "), /* @__PURE__ */ import_react46.default.createElement(Text, { color: "#FBBF24" }, "Planning "), /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true, wrap: "truncate" }, goal), /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true }, " \u2026")) : null, agents.length > 0 ? /* @__PURE__ */ import_react46.default.createElement(RosterHeader, null) : null, agents.length === 0 && phase !== "planning" ? /* @__PURE__ */ import_react46.default.createElement(Box_default, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true }, "No agents yet \u2014 type a task below and press Enter.")) : null, agents.map((a) => /* @__PURE__ */ import_react46.default.createElement(
98568
+ return /* @__PURE__ */ import_react46.default.createElement(Box_default, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ import_react46.default.createElement(FleetSummary, { snap }), phase === "planning" ? /* @__PURE__ */ import_react46.default.createElement(Box_default, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ import_react46.default.createElement(Text, { color: "#FBBF24", bold: true }, SPINNER3[frame2 % SPINNER3.length] ?? "\u280B", " "), /* @__PURE__ */ import_react46.default.createElement(Text, { color: "#FBBF24" }, "Planning "), /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true, wrap: "truncate" }, goal), /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true }, " \u2026")) : null, agents.length > 0 ? /* @__PURE__ */ import_react46.default.createElement(RosterHeader, null) : null, agents.length === 0 && phase !== "planning" ? /* @__PURE__ */ import_react46.default.createElement(Box_default, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ import_react46.default.createElement(Text, { dimColor: true }, "No agents yet \u2014 type a task below and press Enter.")) : null, agents.map((a) => /* @__PURE__ */ import_react46.default.createElement(
97748
98569
  AgentRow,
97749
98570
  {
97750
98571
  key: a.taskId,
97751
98572
  agent: a,
97752
- frame,
98573
+ frame: frame2,
97753
98574
  showDetail
97754
98575
  }
97755
98576
  )), /* @__PURE__ */ import_react46.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react46.default.createElement(
@@ -97817,7 +98638,7 @@ var init_fleet_view = __esm({
97817
98638
  init_orchestrator();
97818
98639
  init_planner();
97819
98640
  init_memory();
97820
- init_store2();
98641
+ init_store3();
97821
98642
  init_git_isolation();
97822
98643
  init_project_context();
97823
98644
  init_loader();
@@ -97853,7 +98674,7 @@ var init_client4 = __esm({
97853
98674
  * Throws if the daemon is unreachable.
97854
98675
  */
97855
98676
  async send(request) {
97856
- return new Promise((resolve5, reject) => {
98677
+ return new Promise((resolve6, reject) => {
97857
98678
  const socket = net.createConnection(this.socketPath);
97858
98679
  let buffer = "";
97859
98680
  socket.on("connect", () => {
@@ -97867,7 +98688,7 @@ var init_client4 = __esm({
97867
98688
  try {
97868
98689
  const response = JSON.parse(line);
97869
98690
  socket.destroy();
97870
- resolve5(response);
98691
+ resolve6(response);
97871
98692
  return;
97872
98693
  } catch {
97873
98694
  }
@@ -97906,6 +98727,39 @@ var init_client4 = __esm({
97906
98727
  if (response.error) throw new Error(response.error.message);
97907
98728
  return response.result;
97908
98729
  }
98730
+ /**
98731
+ * Incrementally (re)build + persist the code graph for `root` (defaults to the
98732
+ * daemon's workspace root). Returns the index delta + totals.
98733
+ */
98734
+ async buildCodeGraph(root) {
98735
+ const response = await this.send({
98736
+ id: crypto.randomUUID(),
98737
+ method: "graph.build",
98738
+ params: root ? { root } : {}
98739
+ });
98740
+ if (response.error) throw new Error(response.error.message);
98741
+ return response.result;
98742
+ }
98743
+ /** Fuzzy symbol search across the persisted code graph. */
98744
+ async searchCodeGraph(pattern, limit = 20, root) {
98745
+ const response = await this.send({
98746
+ id: crypto.randomUUID(),
98747
+ method: "graph.search",
98748
+ params: { pattern, limit, ...root ? { root } : {} }
98749
+ });
98750
+ if (response.error) throw new Error(response.error.message);
98751
+ return response.result;
98752
+ }
98753
+ /** k-hop neighborhood of a code node from the persisted code graph. */
98754
+ async queryCodeGraph(nodeId, opts = {}) {
98755
+ const response = await this.send({
98756
+ id: crypto.randomUUID(),
98757
+ method: "graph.query",
98758
+ params: { nodeId, ...opts }
98759
+ });
98760
+ if (response.error) throw new Error(response.error.message);
98761
+ return response.result;
98762
+ }
97909
98763
  /**
97910
98764
  * Ask the daemon to shut down gracefully.
97911
98765
  */
@@ -97965,8 +98819,8 @@ var init_server = __esm({
97965
98819
  this.server = net2.createServer((socket) => {
97966
98820
  this.handleConnection(socket);
97967
98821
  });
97968
- await new Promise((resolve5, reject) => {
97969
- this.server.listen(this.config.socketPath, () => resolve5());
98822
+ await new Promise((resolve6, reject) => {
98823
+ this.server.listen(this.config.socketPath, () => resolve6());
97970
98824
  this.server.on("error", reject);
97971
98825
  });
97972
98826
  fs6.writeFileSync(this.config.pidFile, String(process.pid));
@@ -98047,6 +98901,8 @@ var init_server = __esm({
98047
98901
  return this.handleQuery(request.params);
98048
98902
  case "recall":
98049
98903
  return this.handleRecall(request.params);
98904
+ case "graph.build":
98905
+ return this.handleGraphBuild(request.params);
98050
98906
  case "graph.query":
98051
98907
  return this.handleGraphQuery(request.params);
98052
98908
  case "graph.search":
@@ -98079,11 +98935,54 @@ var init_server = __esm({
98079
98935
  await store.close();
98080
98936
  return { record: record2 };
98081
98937
  }
98082
- async handleGraphQuery(_params) {
98083
- return { nodes: [], edges: [] };
98938
+ /**
98939
+ * Open the persistent code-graph store, ensure `root` is indexed (building it
98940
+ * on first use so even the very first query after a cold daemon start
98941
+ * answers), load the graph, run `fn`, and always close the store. Opening
98942
+ * per-request — like the engram handlers above — keeps the DuckDB write lock
98943
+ * short so a co-located `oxagen` agent process can use the same store between
98944
+ * requests instead of being starved.
98945
+ */
98946
+ async withCodeGraph(root, fn) {
98947
+ const { createCodeGraphStore: createCodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
98948
+ const { buildAndPersistCodeGraph: buildAndPersistCodeGraph2 } = await Promise.resolve().then(() => (init_builder(), builder_exports));
98949
+ const store = createCodeGraphStore2({ duckdbPath: this.config.codeGraphDbPath });
98950
+ try {
98951
+ if ((await store.stats(root)).files === 0) {
98952
+ await buildAndPersistCodeGraph2(root, store);
98953
+ }
98954
+ return fn(await store.loadGraph(root));
98955
+ } finally {
98956
+ await store.close();
98957
+ }
98084
98958
  }
98085
- async handleGraphSearch(_params) {
98086
- return { results: [] };
98959
+ /** Incrementally (re)build + persist the code graph; report the delta + totals. */
98960
+ async handleGraphBuild(params) {
98961
+ const root = params.root ?? this.config.workspaceRoot;
98962
+ const { createCodeGraphStore: createCodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
98963
+ const { buildAndPersistCodeGraph: buildAndPersistCodeGraph2 } = await Promise.resolve().then(() => (init_builder(), builder_exports));
98964
+ const store = createCodeGraphStore2({ duckdbPath: this.config.codeGraphDbPath });
98965
+ try {
98966
+ const delta = await buildAndPersistCodeGraph2(root, store);
98967
+ return { root, ...delta, ...await store.stats(root) };
98968
+ } finally {
98969
+ await store.close();
98970
+ }
98971
+ }
98972
+ async handleGraphQuery(params) {
98973
+ const root = params.root ?? this.config.workspaceRoot;
98974
+ const { neighbors: neighbors2 } = await Promise.resolve().then(() => (init_query(), query_exports));
98975
+ return this.withCodeGraph(
98976
+ root,
98977
+ (graph2) => neighbors2(graph2, params.nodeId, params.hops ?? 1, params.edgeTypes)
98978
+ );
98979
+ }
98980
+ async handleGraphSearch(params) {
98981
+ const root = params.root ?? this.config.workspaceRoot;
98982
+ const { searchSymbols: searchSymbols2 } = await Promise.resolve().then(() => (init_query(), query_exports));
98983
+ return this.withCodeGraph(root, (graph2) => ({
98984
+ results: searchSymbols2(graph2, params.pattern, params.limit ?? 20)
98985
+ }));
98087
98986
  }
98088
98987
  sendResponse(socket, response) {
98089
98988
  try {
@@ -98102,8 +99001,8 @@ __export(lifecycle_exports, {
98102
99001
  startDaemon: () => startDaemon,
98103
99002
  stopDaemon: () => stopDaemon
98104
99003
  });
98105
- import { join as join19 } from "node:path";
98106
- import { homedir as homedir13 } from "node:os";
99004
+ import { join as join20 } from "node:path";
99005
+ import { homedir as homedir14 } from "node:os";
98107
99006
  import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
98108
99007
  import { spawn as spawn3 } from "node:child_process";
98109
99008
  function getClient() {
@@ -98124,19 +99023,20 @@ async function startDaemon(opts) {
98124
99023
  logFile: LOG_FILE,
98125
99024
  idleTimeoutMs: 30 * 60 * 1e3,
98126
99025
  workspaceRoot: process.cwd(),
98127
- dbPath: DB_PATH
99026
+ dbPath: DB_PATH,
99027
+ codeGraphDbPath: CODE_GRAPH_DB_PATH
98128
99028
  });
98129
99029
  console.log("Starting context daemon (foreground)\u2026");
98130
99030
  console.log(` Socket: ${SOCKET_PATH}`);
98131
99031
  console.log(` DB: ${DB_PATH}`);
98132
99032
  await daemon2.start();
98133
99033
  console.log("\u2713 Daemon running. Press Ctrl+C to stop.");
98134
- await new Promise((resolve5) => {
99034
+ await new Promise((resolve6) => {
98135
99035
  process.on("SIGINT", () => {
98136
- void daemon2.shutdown().then(resolve5);
99036
+ void daemon2.shutdown().then(resolve6);
98137
99037
  });
98138
99038
  process.on("SIGTERM", () => {
98139
- void daemon2.shutdown().then(resolve5);
99039
+ void daemon2.shutdown().then(resolve6);
98140
99040
  });
98141
99041
  });
98142
99042
  } else {
@@ -98157,6 +99057,7 @@ async function startDaemon(opts) {
98157
99057
  OXAGEN_DAEMON_PID: PID_FILE,
98158
99058
  OXAGEN_DAEMON_LOG: LOG_FILE,
98159
99059
  OXAGEN_DAEMON_DB: DB_PATH,
99060
+ OXAGEN_DAEMON_CODE_GRAPH_DB: CODE_GRAPH_DB_PATH,
98160
99061
  OXAGEN_DAEMON_CWD: process.cwd()
98161
99062
  }
98162
99063
  }
@@ -98219,15 +99120,16 @@ function formatUptime2(seconds) {
98219
99120
  if (m > 0) return `${m}m ${s}s`;
98220
99121
  return `${s}s`;
98221
99122
  }
98222
- var DAEMON_DIR, SOCKET_PATH, PID_FILE, LOG_FILE, DB_PATH;
99123
+ var DAEMON_DIR, SOCKET_PATH, PID_FILE, LOG_FILE, DB_PATH, CODE_GRAPH_DB_PATH;
98223
99124
  var init_lifecycle = __esm({
98224
99125
  "src/daemon/lifecycle.ts"() {
98225
99126
  init_client4();
98226
- DAEMON_DIR = join19(homedir13(), ".config", "oxagen");
98227
- SOCKET_PATH = join19(DAEMON_DIR, "daemon.sock");
98228
- PID_FILE = join19(DAEMON_DIR, "daemon.pid");
98229
- LOG_FILE = join19(DAEMON_DIR, "daemon.log");
98230
- DB_PATH = join19(DAEMON_DIR, "context.duckdb");
99127
+ DAEMON_DIR = join20(homedir14(), ".config", "oxagen");
99128
+ SOCKET_PATH = join20(DAEMON_DIR, "daemon.sock");
99129
+ PID_FILE = join20(DAEMON_DIR, "daemon.pid");
99130
+ LOG_FILE = join20(DAEMON_DIR, "daemon.log");
99131
+ DB_PATH = join20(DAEMON_DIR, "context.duckdb");
99132
+ CODE_GRAPH_DB_PATH = join20(DAEMON_DIR, "code-graph.duckdb");
98231
99133
  }
98232
99134
  });
98233
99135
 
@@ -98471,11 +99373,11 @@ __export(graph_pull_exports, {
98471
99373
  graphStorePath: () => graphStorePath,
98472
99374
  handleGraphPull: () => handleGraphPull
98473
99375
  });
98474
- import { mkdirSync as mkdirSync10 } from "node:fs";
98475
- import { homedir as homedir14 } from "node:os";
98476
- import { dirname as dirname10, join as join20 } from "node:path";
99376
+ import { mkdirSync as mkdirSync12 } from "node:fs";
99377
+ import { homedir as homedir15 } from "node:os";
99378
+ import { dirname as dirname12, join as join21 } from "node:path";
98477
99379
  function graphStorePath(workspaceSlug) {
98478
- return join20(homedir14(), ".config", "oxagen", "graph", `${workspaceSlug}.duckdb`);
99380
+ return join21(homedir15(), ".config", "oxagen", "graph", `${workspaceSlug}.duckdb`);
98479
99381
  }
98480
99382
  function splitCsv2(value) {
98481
99383
  if (!value) return void 0;
@@ -98494,7 +99396,7 @@ async function handleGraphPull(opts) {
98494
99396
  }
98495
99397
  const duckdbPath = opts._duckdbPath ?? graphStorePath(workspace);
98496
99398
  if (duckdbPath !== ":memory:") {
98497
- mkdirSync10(dirname10(duckdbPath), { recursive: true });
99399
+ mkdirSync12(dirname12(duckdbPath), { recursive: true });
98498
99400
  }
98499
99401
  const store = createGraphStore({ duckdbPath });
98500
99402
  try {
@@ -98669,6 +99571,505 @@ var init_graph_status = __esm({
98669
99571
  }
98670
99572
  });
98671
99573
 
99574
+ // src/commands/graph.push.ts
99575
+ var graph_push_exports = {};
99576
+ __export(graph_push_exports, {
99577
+ getCodePushCursor: () => getCodePushCursor,
99578
+ handleGraphPush: () => handleGraphPush,
99579
+ homedir: () => homedir16,
99580
+ join: () => join22
99581
+ });
99582
+ import { execFileSync as execFileSync2 } from "node:child_process";
99583
+ import { mkdirSync as mkdirSync13 } from "node:fs";
99584
+ import { dirname as dirname13, join as join22 } from "node:path";
99585
+ import { homedir as homedir16 } from "node:os";
99586
+ import { createHash as createHash3 } from "node:crypto";
99587
+ function git(args, cwd2) {
99588
+ return execFileSync2("git", args, { cwd: cwd2, encoding: "utf-8" }).trim();
99589
+ }
99590
+ function headSha(cwd2) {
99591
+ return git(["rev-parse", "HEAD"], cwd2);
99592
+ }
99593
+ function gitRoot(cwd2) {
99594
+ return git(["rev-parse", "--show-toplevel"], cwd2);
99595
+ }
99596
+ function repoId(root) {
99597
+ try {
99598
+ const url2 = git(["remote", "get-url", "origin"], root);
99599
+ if (url2) return url2;
99600
+ } catch {
99601
+ }
99602
+ return createHash3("sha256").update(root).digest("hex").slice(0, 16);
99603
+ }
99604
+ function changedFiles(fromSha, toSha, root) {
99605
+ const raw = git(["diff", "--name-status", `${fromSha}..${toSha}`], root);
99606
+ const added = [];
99607
+ const deleted = [];
99608
+ for (const line of raw.split("\n")) {
99609
+ if (!line.trim()) continue;
99610
+ const [status, ...rest] = line.split(" ");
99611
+ const file2 = rest.join(" ").trim();
99612
+ if (!file2 || !SUPPORTED_EXTENSIONS_RE.test(file2)) continue;
99613
+ if (status === "D") {
99614
+ deleted.push(file2);
99615
+ } else {
99616
+ added.push(file2);
99617
+ }
99618
+ }
99619
+ return { added, deleted };
99620
+ }
99621
+ function allTrackedFiles(root) {
99622
+ const raw = git(["ls-files"], root);
99623
+ return raw.split("\n").map((f) => f.trim()).filter((f) => f && SUPPORTED_EXTENSIONS_RE.test(f));
99624
+ }
99625
+ async function buildEnvelopeForFiles(files, root, repo) {
99626
+ if (files.length === 0) return { nodes: [], edges: [] };
99627
+ const graph2 = await buildCodeGraph(root);
99628
+ const nodeMap = /* @__PURE__ */ new Map();
99629
+ const edgeList = [];
99630
+ const nodesByPath = /* @__PURE__ */ new Map();
99631
+ for (const [, n] of graph2.nodes) {
99632
+ if (n.kind === "file") {
99633
+ const fileKey = `code:${repo}:${n.path}`;
99634
+ const pushNode = {
99635
+ key: fileKey,
99636
+ labels: ["SourceFile"],
99637
+ displayName: n.name,
99638
+ properties: { path: n.path, language: n.language },
99639
+ isSystem: true
99640
+ };
99641
+ nodeMap.set(fileKey, pushNode);
99642
+ const existing = nodesByPath.get(n.path) ?? [];
99643
+ existing.push(fileKey);
99644
+ nodesByPath.set(n.path, existing);
99645
+ } else {
99646
+ const symbolKey = `code:${repo}:${n.path}#${n.name}:${n.kind}`;
99647
+ const props = {
99648
+ path: n.path,
99649
+ language: n.language,
99650
+ kind: n.kind,
99651
+ lineStart: n.range.start,
99652
+ lineEnd: n.range.end
99653
+ };
99654
+ if (n.signature) props["signature"] = n.signature;
99655
+ if (n.docstring) props["docstring"] = n.docstring;
99656
+ const pushNode = {
99657
+ key: symbolKey,
99658
+ labels: ["Symbol"],
99659
+ displayName: n.name,
99660
+ properties: props,
99661
+ isSystem: true
99662
+ };
99663
+ nodeMap.set(symbolKey, pushNode);
99664
+ const existing = nodesByPath.get(n.path) ?? [];
99665
+ existing.push(symbolKey);
99666
+ nodesByPath.set(n.path, existing);
99667
+ nodeMap.set(n.id, pushNode);
99668
+ }
99669
+ }
99670
+ const idToKey = /* @__PURE__ */ new Map();
99671
+ for (const [, n] of graph2.nodes) {
99672
+ if (n.kind === "file") {
99673
+ idToKey.set(n.id, `code:${repo}:${n.path}`);
99674
+ } else {
99675
+ idToKey.set(n.id, `code:${repo}:${n.path}#${n.name}:${n.kind}`);
99676
+ }
99677
+ }
99678
+ const fileSet = new Set(files);
99679
+ const filteredNodeKeys = /* @__PURE__ */ new Set();
99680
+ for (const [, n] of graph2.nodes) {
99681
+ if (fileSet.has(n.path)) {
99682
+ const key = idToKey.get(n.id);
99683
+ if (key) filteredNodeKeys.add(key);
99684
+ }
99685
+ }
99686
+ for (const e of graph2.edges) {
99687
+ const srcKey = idToKey.get(e.source);
99688
+ const tgtKey = idToKey.get(e.target);
99689
+ if (!srcKey || !tgtKey) continue;
99690
+ if (!filteredNodeKeys.has(srcKey) && !filteredNodeKeys.has(tgtKey)) continue;
99691
+ const relType = EDGE_TYPE_MAP[e.type] ?? e.type.toUpperCase();
99692
+ edgeList.push({ sourceKey: srcKey, targetKey: tgtKey, type: relType });
99693
+ }
99694
+ const nodesInEdges = /* @__PURE__ */ new Set();
99695
+ for (const e of edgeList) {
99696
+ nodesInEdges.add(e.sourceKey);
99697
+ nodesInEdges.add(e.targetKey);
99698
+ }
99699
+ const finalNodeKeys = /* @__PURE__ */ new Set([...filteredNodeKeys, ...nodesInEdges]);
99700
+ const nodes = [];
99701
+ for (const key of finalNodeKeys) {
99702
+ const n = nodeMap.get(key);
99703
+ if (n) nodes.push(n);
99704
+ }
99705
+ return { nodes, edges: edgeList };
99706
+ }
99707
+ async function handleGraphPush(opts) {
99708
+ const org = getOrgId();
99709
+ const workspace = getWorkspaceId();
99710
+ if (!org || !workspace) {
99711
+ process.stderr.write(
99712
+ "Missing org or workspace. Run `oxagen config` or set OXAGEN_ORG_ID / OXAGEN_WORKSPACE_ID.\n"
99713
+ );
99714
+ process.exitCode = 1;
99715
+ return;
99716
+ }
99717
+ let root;
99718
+ try {
99719
+ root = opts._gitRoot ?? gitRoot(process.cwd());
99720
+ } catch {
99721
+ process.stderr.write(
99722
+ "Not inside a git repository. `oxagen graph push` requires a git repo.\n"
99723
+ );
99724
+ process.exitCode = 1;
99725
+ return;
99726
+ }
99727
+ const repo = opts.repo ?? repoId(root);
99728
+ const duckdbPath = opts._duckdbPath ?? graphStorePath(workspace);
99729
+ if (duckdbPath !== ":memory:") {
99730
+ mkdirSync13(dirname13(duckdbPath), { recursive: true });
99731
+ }
99732
+ const store = createGraphStore({ duckdbPath });
99733
+ try {
99734
+ const currentSha = headSha(root);
99735
+ const lastSha = opts.full ? null : await store.getCodeCursor(org, workspace, repo);
99736
+ let addedFiles;
99737
+ let deletedFiles;
99738
+ if (!lastSha) {
99739
+ addedFiles = allTrackedFiles(root);
99740
+ deletedFiles = [];
99741
+ } else if (lastSha === currentSha) {
99742
+ if (opts.json) {
99743
+ process.stdout.write(
99744
+ JSON.stringify({ upToDate: true, sha: currentSha, repo }) + "\n"
99745
+ );
99746
+ } else {
99747
+ process.stdout.write(
99748
+ `Already up to date (HEAD: ${currentSha.slice(0, 8)}).
99749
+ `
99750
+ );
99751
+ }
99752
+ return;
99753
+ } else {
99754
+ const changed = changedFiles(lastSha, currentSha, root);
99755
+ addedFiles = changed.added;
99756
+ deletedFiles = changed.deleted;
99757
+ }
99758
+ const { nodes, edges } = await buildEnvelopeForFiles(addedFiles, root, repo);
99759
+ const tombstones = deletedFiles.map((f) => ({ key: `code:${repo}:${f}` }));
99760
+ const idempotencyKey = lastSha ? `code:${repo}:${lastSha}:${currentSha}` : `code:${repo}:full:${currentSha}`;
99761
+ const envelope = {
99762
+ source: "code",
99763
+ idempotencyKey,
99764
+ nodes,
99765
+ edges,
99766
+ ...tombstones.length > 0 ? { tombstones } : {}
99767
+ };
99768
+ if (nodes.length === 0 && edges.length === 0 && tombstones.length === 0) {
99769
+ if (opts.json) {
99770
+ process.stdout.write(
99771
+ JSON.stringify({ pushed: false, reason: "no changes", sha: currentSha, repo }) + "\n"
99772
+ );
99773
+ } else {
99774
+ process.stdout.write(`No source file changes since ${lastSha?.slice(0, 8) ?? "start"}.
99775
+ `);
99776
+ }
99777
+ await store.setCodeCursor(org, workspace, repo, currentSha);
99778
+ return;
99779
+ }
99780
+ process.stderr.write(
99781
+ `Pushing ${nodes.length} node(s), ${edges.length} edge(s), ${tombstones.length} tombstone(s)...
99782
+ `
99783
+ );
99784
+ const result = await apiPost("graph/sync/push", envelope);
99785
+ await store.setCodeCursor(org, workspace, repo, currentSha);
99786
+ const summary = {
99787
+ nodesUpserted: result.nodesUpserted,
99788
+ edgesUpserted: result.edgesUpserted,
99789
+ tombstoned: result.tombstoned,
99790
+ sha: currentSha,
99791
+ repo,
99792
+ fromSha: lastSha ?? "(full)"
99793
+ };
99794
+ if (opts.json) {
99795
+ process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
99796
+ } else {
99797
+ process.stdout.write(
99798
+ `Pushed to workspace graph:
99799
+ Nodes upserted: ${result.nodesUpserted}
99800
+ Edges upserted: ${result.edgesUpserted}
99801
+ Tombstoned: ${result.tombstoned}
99802
+ From SHA: ${(lastSha ?? "(full)").toString().slice(0, 8)}
99803
+ To SHA (HEAD): ${currentSha.slice(0, 8)}
99804
+ Repo: ${repo}
99805
+ `
99806
+ );
99807
+ }
99808
+ } finally {
99809
+ await store.close();
99810
+ }
99811
+ }
99812
+ async function getCodePushCursor(org, workspace, repo, duckdbPath) {
99813
+ const store = createGraphStore({ duckdbPath });
99814
+ try {
99815
+ return await store.getCodeCursor(org, workspace, repo);
99816
+ } finally {
99817
+ await store.close();
99818
+ }
99819
+ }
99820
+ var EDGE_TYPE_MAP, SUPPORTED_EXTENSIONS_RE;
99821
+ var init_graph_push = __esm({
99822
+ "src/commands/graph.push.ts"() {
99823
+ init_builder();
99824
+ init_api2();
99825
+ init_config();
99826
+ init_src();
99827
+ init_graph_pull();
99828
+ EDGE_TYPE_MAP = {
99829
+ calls: "CALLS",
99830
+ imports: "IMPORTS",
99831
+ exports: "EXPORTS",
99832
+ extends: "EXTENDS",
99833
+ implements: "IMPLEMENTS",
99834
+ contains: "CONTAINS",
99835
+ references: "REFERENCES"
99836
+ };
99837
+ SUPPORTED_EXTENSIONS_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt)$/;
99838
+ }
99839
+ });
99840
+
99841
+ // src/agent/lineage-projection.ts
99842
+ import { basename as basename10 } from "node:path";
99843
+ function sessionNodeKey(sessionKey) {
99844
+ return `lineage:session:${sessionKey}`;
99845
+ }
99846
+ function turnNodeKey(turnId) {
99847
+ return `lineage:turn:${turnId}`;
99848
+ }
99849
+ function toolNodeKey(turnId, idx) {
99850
+ return `lineage:tool:${turnId}:${idx}`;
99851
+ }
99852
+ function modelNodeKey(slug) {
99853
+ return `lineage:model:${slug}`;
99854
+ }
99855
+ function fileNodeKey(repo, relativePath) {
99856
+ return `code:${repo}:${relativePath}`;
99857
+ }
99858
+ function projectTrace(trace2, opts = {}) {
99859
+ const sessionKey = opts.sessionKey ?? (trace2.cwd ? basename10(trace2.cwd) || "default" : "default");
99860
+ const nodes = [];
99861
+ const edges = [];
99862
+ const emittedKeys = /* @__PURE__ */ new Set();
99863
+ function pushNode(n) {
99864
+ if (!emittedKeys.has(n.key)) {
99865
+ emittedKeys.add(n.key);
99866
+ nodes.push(n);
99867
+ }
99868
+ }
99869
+ const sKey = sessionNodeKey(sessionKey);
99870
+ pushNode({
99871
+ key: sKey,
99872
+ labels: ["AgentSession"],
99873
+ displayName: sessionKey,
99874
+ properties: { cwd: trace2.cwd ?? "" },
99875
+ isSystem: true
99876
+ });
99877
+ const tKey = turnNodeKey(trace2.id);
99878
+ pushNode({
99879
+ key: tKey,
99880
+ labels: ["AgentTurn"],
99881
+ displayName: `Turn ${trace2.id.slice(0, 8)}`,
99882
+ properties: {
99883
+ turnId: trace2.id,
99884
+ createdAt: trace2.createdAt,
99885
+ originalPrompt: trace2.originalPrompt.slice(0, 500),
99886
+ selectedModel: trace2.selectedModel,
99887
+ selectedTier: trace2.selectedTier,
99888
+ inputTokens: trace2.usage.inputTokens ?? 0,
99889
+ outputTokens: trace2.usage.outputTokens ?? 0,
99890
+ totalTokens: trace2.usage.inputTokens + trace2.usage.outputTokens,
99891
+ costUsd: trace2.usage.costUsd,
99892
+ durationMs: trace2.durationMs,
99893
+ finalComplete: trace2.finalComplete,
99894
+ steps: trace2.steps
99895
+ },
99896
+ isSystem: true
99897
+ });
99898
+ edges.push({ sourceKey: sKey, targetKey: tKey, type: "HAS_TURN" });
99899
+ if (trace2.selectedModel) {
99900
+ const mKey = modelNodeKey(trace2.selectedModel);
99901
+ pushNode({
99902
+ key: mKey,
99903
+ labels: ["AIModel"],
99904
+ displayName: trace2.selectedModel,
99905
+ properties: { slug: trace2.selectedModel, tier: trace2.selectedTier },
99906
+ isSystem: true
99907
+ });
99908
+ edges.push({ sourceKey: tKey, targetKey: mKey, type: "USED_MODEL" });
99909
+ }
99910
+ if (opts.repo && trace2.filesTouched.length > 0) {
99911
+ for (const relativePath of trace2.filesTouched) {
99912
+ if (!relativePath) continue;
99913
+ const fKey = fileNodeKey(opts.repo, relativePath);
99914
+ pushNode({
99915
+ key: fKey,
99916
+ labels: ["SourceFile"],
99917
+ displayName: basename10(relativePath),
99918
+ properties: { path: relativePath },
99919
+ isSystem: true
99920
+ });
99921
+ edges.push({ sourceKey: tKey, targetKey: fKey, type: "TOUCHED_FILE" });
99922
+ }
99923
+ }
99924
+ const toolEvents = trace2.toolEvents ?? [];
99925
+ for (let idx = 0; idx < toolEvents.length; idx++) {
99926
+ const te = toolEvents[idx];
99927
+ const teKey = toolNodeKey(trace2.id, idx);
99928
+ pushNode({
99929
+ key: teKey,
99930
+ labels: ["AgentToolCall"],
99931
+ displayName: te.name,
99932
+ properties: {
99933
+ name: te.name,
99934
+ durationMs: te.durationMs,
99935
+ ok: te.ok,
99936
+ // input is already truncated by the loop — safe to store
99937
+ input: te.input
99938
+ },
99939
+ isSystem: true
99940
+ });
99941
+ edges.push({ sourceKey: tKey, targetKey: teKey, type: "USED_TOOL" });
99942
+ }
99943
+ return { nodes, edges };
99944
+ }
99945
+ function mergeEnvelopes(envelopes) {
99946
+ const nodeMap = /* @__PURE__ */ new Map();
99947
+ const edges = [];
99948
+ for (const env4 of envelopes) {
99949
+ for (const n of env4.nodes) {
99950
+ if (!nodeMap.has(n.key)) nodeMap.set(n.key, n);
99951
+ }
99952
+ edges.push(...env4.edges);
99953
+ }
99954
+ return { nodes: Array.from(nodeMap.values()), edges };
99955
+ }
99956
+ var init_lineage_projection = __esm({
99957
+ "src/agent/lineage-projection.ts"() {
99958
+ }
99959
+ });
99960
+
99961
+ // src/commands/graph.lineage.ts
99962
+ var graph_lineage_exports = {};
99963
+ __export(graph_lineage_exports, {
99964
+ handleGraphLineage: () => handleGraphLineage
99965
+ });
99966
+ import { mkdirSync as mkdirSync14 } from "node:fs";
99967
+ import { dirname as dirname14 } from "node:path";
99968
+ import { execFileSync as execFileSync3 } from "node:child_process";
99969
+ import { createHash as createHash4 } from "node:crypto";
99970
+ function runGit(args, cwd2) {
99971
+ return execFileSync3("git", args, { cwd: cwd2, encoding: "utf-8" }).trim();
99972
+ }
99973
+ function resolveGitRoot(cwd2) {
99974
+ try {
99975
+ return runGit(["rev-parse", "--show-toplevel"], cwd2);
99976
+ } catch {
99977
+ return null;
99978
+ }
99979
+ }
99980
+ function resolveRepoId(root) {
99981
+ try {
99982
+ const url2 = runGit(["remote", "get-url", "origin"], root);
99983
+ if (url2) return url2;
99984
+ } catch {
99985
+ }
99986
+ return createHash4("sha256").update(root).digest("hex").slice(0, 16);
99987
+ }
99988
+ async function handleGraphLineage(opts) {
99989
+ const org = getOrgId();
99990
+ const workspace = getWorkspaceId();
99991
+ if (!org || !workspace) {
99992
+ process.stderr.write(
99993
+ "Missing org or workspace. Run `oxagen config` or set OXAGEN_ORG_ID / OXAGEN_WORKSPACE_ID.\n"
99994
+ );
99995
+ process.exitCode = 1;
99996
+ return;
99997
+ }
99998
+ const cwd2 = opts._gitRoot ?? process.cwd();
99999
+ const gitRoot2 = opts._gitRoot ?? resolveGitRoot(cwd2);
100000
+ const repo = opts.repo ?? (gitRoot2 ? resolveRepoId(gitRoot2) : void 0);
100001
+ const duckdbPath = opts._duckdbPath ?? graphStorePath(workspace);
100002
+ if (duckdbPath !== ":memory:") {
100003
+ mkdirSync14(dirname14(duckdbPath), { recursive: true });
100004
+ }
100005
+ const store = createGraphStore({ duckdbPath });
100006
+ try {
100007
+ const lastSyncedAt = await store.getLineageCursor(org, workspace);
100008
+ const traceStore = opts._traceStore ?? openTraceStore(cwd2);
100009
+ const allTraces = traceStore.list();
100010
+ const pendingTraces = lastSyncedAt ? allTraces.filter((t) => t.createdAt > lastSyncedAt) : allTraces;
100011
+ if (pendingTraces.length === 0) {
100012
+ const msg = lastSyncedAt ? `Lineage up to date \u2014 no new turns since ${new Date(lastSyncedAt).toISOString()}.
100013
+ ` : "No turns found in the local trace store. Run at least one CLI query first.\n";
100014
+ if (opts.json) {
100015
+ process.stdout.write(JSON.stringify({ upToDate: true }) + "\n");
100016
+ } else {
100017
+ process.stdout.write(msg);
100018
+ }
100019
+ return;
100020
+ }
100021
+ const envelopes = pendingTraces.map(
100022
+ (trace2) => projectTrace(trace2, { repo })
100023
+ );
100024
+ const merged = mergeEnvelopes(envelopes);
100025
+ const minCreatedAt = Math.min(...pendingTraces.map((t) => t.createdAt));
100026
+ const maxCreatedAt = Math.max(...pendingTraces.map((t) => t.createdAt));
100027
+ const idempotencyKey = `lineage:${org}:${workspace}:${minCreatedAt}:${maxCreatedAt}`;
100028
+ process.stderr.write(
100029
+ `Syncing ${pendingTraces.length} turn(s): ${merged.nodes.length} node(s), ${merged.edges.length} edge(s)...
100030
+ `
100031
+ );
100032
+ const result = await apiPost("graph/sync/push", {
100033
+ source: "lineage",
100034
+ idempotencyKey,
100035
+ nodes: merged.nodes,
100036
+ edges: merged.edges
100037
+ });
100038
+ await store.setLineageCursor(org, workspace, maxCreatedAt);
100039
+ const summary = {
100040
+ nodesUpserted: result.nodesUpserted,
100041
+ edgesUpserted: result.edgesUpserted,
100042
+ tombstoned: result.tombstoned,
100043
+ turnsSynced: pendingTraces.length,
100044
+ lastSyncedAt: maxCreatedAt
100045
+ };
100046
+ if (opts.json) {
100047
+ process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
100048
+ } else {
100049
+ process.stdout.write(
100050
+ `Lineage synced to workspace graph:
100051
+ Turns synced: ${pendingTraces.length}
100052
+ Nodes upserted: ${result.nodesUpserted}
100053
+ Edges upserted: ${result.edgesUpserted}
100054
+ Last turn at: ${new Date(maxCreatedAt).toISOString()}
100055
+ `
100056
+ );
100057
+ }
100058
+ } finally {
100059
+ await store.close();
100060
+ }
100061
+ }
100062
+ var init_graph_lineage = __esm({
100063
+ "src/commands/graph.lineage.ts"() {
100064
+ init_api2();
100065
+ init_config();
100066
+ init_trace_store();
100067
+ init_lineage_projection();
100068
+ init_src();
100069
+ init_graph_pull();
100070
+ }
100071
+ });
100072
+
98672
100073
  // src/commands/config.ts
98673
100074
  var config_exports = {};
98674
100075
  __export(config_exports, {
@@ -98771,8 +100172,8 @@ var init_runtime = __esm({
98771
100172
  });
98772
100173
 
98773
100174
  // src/settings/write.ts
98774
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync7, existsSync as existsSync20, mkdirSync as mkdirSync11 } from "node:fs";
98775
- import { dirname as dirname11 } from "node:path";
100175
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, existsSync as existsSync20, mkdirSync as mkdirSync15 } from "node:fs";
100176
+ import { dirname as dirname15 } from "node:path";
98776
100177
  function readScopeDoc(path2) {
98777
100178
  if (!existsSync20(path2)) return {};
98778
100179
  const parsed = JSON.parse(readFileSync16(path2, "utf8"));
@@ -98783,8 +100184,8 @@ function readScopeDoc(path2) {
98783
100184
  }
98784
100185
  function writeScopeDoc(path2, doc) {
98785
100186
  const validated = oxagenSettingsSchema.parse(doc);
98786
- mkdirSync11(dirname11(path2), { recursive: true });
98787
- writeFileSync7(path2, JSON.stringify(validated, null, 2) + "\n", "utf8");
100187
+ mkdirSync15(dirname15(path2), { recursive: true });
100188
+ writeFileSync8(path2, JSON.stringify(validated, null, 2) + "\n", "utf8");
98788
100189
  clearSettingsCache();
98789
100190
  }
98790
100191
  function writeSettingsValue(opts) {
@@ -99015,14 +100416,14 @@ var init_settings2 = __esm({
99015
100416
  });
99016
100417
 
99017
100418
  // src/agents/write.ts
99018
- import { writeFileSync as writeFileSync8, existsSync as existsSync22, mkdirSync as mkdirSync12 } from "node:fs";
99019
- import { join as join21, dirname as dirname12 } from "node:path";
100419
+ import { writeFileSync as writeFileSync9, existsSync as existsSync22, mkdirSync as mkdirSync16 } from "node:fs";
100420
+ import { join as join23, dirname as dirname16 } from "node:path";
99020
100421
  function scaffoldAgent(opts) {
99021
- const dir = opts.dir ?? join21(opts.cwd ?? process.cwd(), ".oxagen", "agents");
99022
- const path2 = join21(dir, `${opts.name}.md`);
100422
+ const dir = opts.dir ?? join23(opts.cwd ?? process.cwd(), ".oxagen", "agents");
100423
+ const path2 = join23(dir, `${opts.name}.md`);
99023
100424
  if (existsSync22(path2)) return { path: path2, created: false };
99024
- mkdirSync12(dirname12(path2), { recursive: true });
99025
- writeFileSync8(path2, TEMPLATE2(opts.name), "utf8");
100425
+ mkdirSync16(dirname16(path2), { recursive: true });
100426
+ writeFileSync9(path2, TEMPLATE2(opts.name), "utf8");
99026
100427
  return { path: path2, created: true };
99027
100428
  }
99028
100429
  var TEMPLATE2;
@@ -99110,14 +100511,14 @@ var init_agent = __esm({
99110
100511
  });
99111
100512
 
99112
100513
  // src/slash/write.ts
99113
- import { writeFileSync as writeFileSync9, existsSync as existsSync23, mkdirSync as mkdirSync13 } from "node:fs";
99114
- import { join as join22, dirname as dirname13 } from "node:path";
100514
+ import { writeFileSync as writeFileSync10, existsSync as existsSync23, mkdirSync as mkdirSync17 } from "node:fs";
100515
+ import { join as join24, dirname as dirname17 } from "node:path";
99115
100516
  function scaffoldCommand(opts) {
99116
- const dir = opts.dir ?? join22(opts.cwd ?? process.cwd(), ".oxagen", "commands");
99117
- const path2 = join22(dir, `${opts.name}.md`);
100517
+ const dir = opts.dir ?? join24(opts.cwd ?? process.cwd(), ".oxagen", "commands");
100518
+ const path2 = join24(dir, `${opts.name}.md`);
99118
100519
  if (existsSync23(path2)) return { path: path2, created: false };
99119
- mkdirSync13(dirname13(path2), { recursive: true });
99120
- writeFileSync9(path2, TEMPLATE3(opts.name), "utf8");
100520
+ mkdirSync17(dirname17(path2), { recursive: true });
100521
+ writeFileSync10(path2, TEMPLATE3(opts.name), "utf8");
99121
100522
  return { path: path2, created: true };
99122
100523
  }
99123
100524
  var TEMPLATE3;
@@ -99698,8 +101099,8 @@ async function handleSecretExport(opts) {
99698
101099
  );
99699
101100
  process.stderr.write("\u26A0 access recorded (actor, time)\n");
99700
101101
  if (opts.out) {
99701
- const { writeFileSync: writeFileSync10 } = await import("fs");
99702
- writeFileSync10(opts.out, dotenv, "utf8");
101102
+ const { writeFileSync: writeFileSync11 } = await import("fs");
101103
+ writeFileSync11(opts.out, dotenv, "utf8");
99703
101104
  process.stdout.write(`\u2713 wrote ${env4.length} key(s) to ${opts.out}
99704
101105
  `);
99705
101106
  } else {
@@ -99876,6 +101277,21 @@ graph.command("status").description("Show the state of the local workspace-graph
99876
101277
  const { handleGraphStatus: handleGraphStatus2 } = await Promise.resolve().then(() => (init_graph_status(), graph_status_exports));
99877
101278
  await handleGraphStatus2(opts);
99878
101279
  });
101280
+ graph.command("push").description(
101281
+ "Compute a git code delta and push it to the workspace knowledge graph (ADR-018 up-sync)"
101282
+ ).option("--full", "Ignore the saved cursor and push all tracked source files", false).option("--repo <id>", "Override the repo identifier").option("--json", "Output summary as JSON").action(async (opts) => {
101283
+ const { handleGraphPush: handleGraphPush2 } = await Promise.resolve().then(() => (init_graph_push(), graph_push_exports));
101284
+ await handleGraphPush2({ full: opts.full, repo: opts.repo, json: opts.json });
101285
+ });
101286
+ graph.command("lineage").description(
101287
+ "Push CLI session execution lineage into the workspace knowledge graph (ADR-018 slice 3)"
101288
+ ).option(
101289
+ "--repo <id>",
101290
+ "Override the repo identifier used to link touched files to the code subgraph"
101291
+ ).option("--json", "Output summary as JSON").action(async (opts) => {
101292
+ const { handleGraphLineage: handleGraphLineage2 } = await Promise.resolve().then(() => (init_graph_lineage(), graph_lineage_exports));
101293
+ await handleGraphLineage2({ repo: opts.repo, json: opts.json });
101294
+ });
99879
101295
  program2.command("config").description("View or set configuration (api key, model, etc.)").argument("[key]", "Config key to get/set").argument("[value]", "Value to set (omit to read)").action(async (key, value) => {
99880
101296
  const { handleConfig: handleConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
99881
101297
  await handleConfig2(key, value);
@@ -100048,9 +101464,9 @@ secret.command("export").description("Export resolved secrets as .env (recorded
100048
101464
  const { handleSecretExport: handleSecretExport2 } = await Promise.resolve().then(() => (init_secret(), secret_exports));
100049
101465
  await handleSecretExport2(opts);
100050
101466
  });
100051
- async function main() {
101467
+ async function main2() {
100052
101468
  const { applySettingsToEnv: applySettingsToEnv2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
100053
101469
  applySettingsToEnv2();
100054
101470
  await program2.parseAsync(process.argv);
100055
101471
  }
100056
- void main();
101472
+ void main2();