archctx 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/archctx.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  // @bun
3
3
  import { createRequire } from "node:module";
4
4
  var __defProp = Object.defineProperty;
@@ -667,7 +667,7 @@ function productVersionManifest() {
667
667
  }
668
668
  };
669
669
  }
670
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.1", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-06-20.fg1";
670
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.1.3", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=24 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-06-20.fg1";
671
671
  // packages/contracts/src/index.ts
672
672
  var init_src = __esm(() => {
673
673
  init_control_plane_routes();
@@ -6538,14 +6538,25 @@ function listFiles(root) {
6538
6538
  // packages/local-runtime/local-store-sqlite/src/index.ts
6539
6539
  import { execFileSync as execFileSync2 } from "node:child_process";
6540
6540
  import { createHash as createHash5, randomUUID } from "node:crypto";
6541
- import { chmodSync, closeSync as closeSync3, existsSync as existsSync3, fsyncSync as fsyncSync2, mkdirSync as mkdirSync2, openSync as openSync3, readFileSync as readFileSync5, realpathSync as realpathSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
6541
+ import { chmodSync, closeSync as closeSync3, existsSync as existsSync3, fsyncSync as fsyncSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync2, openSync as openSync3, readFileSync as readFileSync5, realpathSync as realpathSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
6542
6542
  import { createRequire as createRequire2 } from "node:module";
6543
6543
  import { homedir } from "node:os";
6544
- import { dirname as dirname2, isAbsolute as isAbsolute3, join as join2, resolve as resolve6 } from "node:path";
6544
+ import { dirname as dirname2, isAbsolute as isAbsolute3, join as join2, relative as relative4, resolve as resolve6 } from "node:path";
6545
6545
  var runtimeRequire = createRequire2(import.meta.url);
6546
6546
  var SQLITE_SIDECAR_SUFFIXES = ["", "-wal", "-shm"];
6547
6547
  var LEGACY_MIGRATION_MARKER_FILE = "runtime.sqlite.migration.json";
6548
6548
  var LEGACY_MIGRATION_LOCK_FILE = "runtime.sqlite.migration.lock";
6549
+ var REQUIRED_LOCAL_STORE_TABLES = [
6550
+ "schema_migrations",
6551
+ "repository_sessions",
6552
+ "snapshots",
6553
+ "task_states",
6554
+ "observed_evidence",
6555
+ "review_results",
6556
+ "landscapes",
6557
+ "cross_repo_edges",
6558
+ "changeset_journal"
6559
+ ];
6549
6560
  var SQLITE_PRAGMAS = [
6550
6561
  "PRAGMA journal_mode = WAL",
6551
6562
  "PRAGMA foreign_keys = ON",
@@ -6708,7 +6719,7 @@ function inspectLegacyLocalStoreMigration(root = process.cwd(), env = process.en
6708
6719
  const legacyExists = existsSync3(paths.legacyLocalStorePath);
6709
6720
  const targetExists = existsSync3(paths.localStorePath);
6710
6721
  if (targetExists) {
6711
- const target = safeSqliteIntegrityCheck(paths.localStorePath);
6722
+ const target = safeCurrentLocalStoreCheck(paths.localStorePath);
6712
6723
  return legacyMigrationResult(false, "target-exists", paths, [], {
6713
6724
  status: target.ok ? "target-current" : "target-incomplete",
6714
6725
  integrityCheck: target.ok ? { target: target.result } : { target: "failed", error: target.error }
@@ -6719,6 +6730,13 @@ function inspectLegacyLocalStoreMigration(root = process.cwd(), env = process.en
6719
6730
  status: "legacy-missing"
6720
6731
  });
6721
6732
  }
6733
+ const source = safeTrustedLegacyLocalStoreSourceCheck(paths);
6734
+ if (!source.ok) {
6735
+ return legacyMigrationResult(false, undefined, paths, [], {
6736
+ status: "legacy-invalid",
6737
+ integrityCheck: { legacy: "failed", error: source.error }
6738
+ });
6739
+ }
6722
6740
  const legacy = safeSqliteIntegrityCheck(paths.legacyLocalStorePath);
6723
6741
  return legacyMigrationResult(false, undefined, paths, [], {
6724
6742
  status: legacy.ok ? "pending" : "legacy-invalid",
@@ -6736,9 +6754,10 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
6736
6754
  const legacyExists = existsSync3(paths.legacyLocalStorePath);
6737
6755
  const integrityCheck = {};
6738
6756
  const quarantinedFiles = [];
6739
- if (existsSync3(paths.localStorePath)) {
6757
+ const targetExists = existsSync3(paths.localStorePath);
6758
+ if (targetExists) {
6740
6759
  try {
6741
- integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
6760
+ integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
6742
6761
  return legacyMigrationResult(false, "target-exists", paths, [], {
6743
6762
  status: "target-current",
6744
6763
  integrityCheck
@@ -6749,7 +6768,6 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
6749
6768
  if (!legacyExists) {
6750
6769
  throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
6751
6770
  }
6752
- quarantinedFiles.push(...quarantineExistingLocalStore(paths));
6753
6771
  }
6754
6772
  }
6755
6773
  if (!legacyExists) {
@@ -6758,18 +6776,21 @@ function migrateLegacyLocalStoreIfNeeded(root = process.cwd(), env = process.env
6758
6776
  integrityCheck
6759
6777
  });
6760
6778
  }
6779
+ assertTrustedLegacyLocalStoreSource(paths);
6761
6780
  const lock = acquireLegacyMigrationLock(paths);
6762
6781
  const stagingDir = join2(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID()}`);
6763
6782
  const stagingPath = join2(stagingDir, "runtime.sqlite");
6764
6783
  try {
6784
+ if (targetExists)
6785
+ quarantinedFiles.push(...quarantineExistingLocalStore(paths));
6765
6786
  ensurePrivateDir(stagingDir);
6766
6787
  integrityCheck.legacy = vacuumLegacySqliteInto(paths.legacyLocalStorePath, stagingPath);
6767
6788
  makePrivateFile(stagingPath);
6768
6789
  migrateSqliteDatabaseSync(stagingPath);
6769
6790
  compactSqliteDatabase(stagingPath);
6770
- integrityCheck.staging = assertSqliteIntegrity(stagingPath);
6791
+ integrityCheck.staging = assertCurrentLocalStore(stagingPath);
6771
6792
  publishStagedLocalStore(stagingPath, paths.localStorePath);
6772
- integrityCheck.target = assertSqliteIntegrity(paths.localStorePath);
6793
+ integrityCheck.target = assertCurrentLocalStore(paths.localStorePath);
6773
6794
  const markerPath = writeLegacyMigrationMarker(paths, integrityCheck, quarantinedFiles);
6774
6795
  return legacyMigrationResult(true, undefined, paths, [paths.localStorePath], {
6775
6796
  status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
@@ -6830,6 +6851,21 @@ function safeSqliteIntegrityCheck(path) {
6830
6851
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
6831
6852
  }
6832
6853
  }
6854
+ function safeCurrentLocalStoreCheck(path) {
6855
+ try {
6856
+ return { ok: true, result: assertCurrentLocalStore(path) };
6857
+ } catch (error) {
6858
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
6859
+ }
6860
+ }
6861
+ function safeTrustedLegacyLocalStoreSourceCheck(paths) {
6862
+ try {
6863
+ assertTrustedLegacyLocalStoreSource(paths);
6864
+ return { ok: true };
6865
+ } catch (error) {
6866
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
6867
+ }
6868
+ }
6833
6869
  function assertSqliteIntegrity(path) {
6834
6870
  const db = openSqliteDatabaseSync(path);
6835
6871
  try {
@@ -6843,6 +6879,42 @@ function assertSqliteIntegrity(path) {
6843
6879
  db.close();
6844
6880
  }
6845
6881
  }
6882
+ function assertCurrentLocalStore(path) {
6883
+ const db = openSqliteDatabaseSync(path);
6884
+ try {
6885
+ db.exec("PRAGMA busy_timeout = 5000");
6886
+ const integrity = sqliteIntegrityCheckOpenDatabase(db, path);
6887
+ assertCurrentLocalStoreSchema(db, path);
6888
+ return integrity;
6889
+ } finally {
6890
+ db.close();
6891
+ }
6892
+ }
6893
+ function assertCurrentLocalStoreSchema(db, path) {
6894
+ const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
6895
+ const missingTables = REQUIRED_LOCAL_STORE_TABLES.filter((table) => !tables.has(table));
6896
+ if (missingTables.length > 0) {
6897
+ throw new Error(`SQLite local store schema incomplete for ${path}: missing tables ${missingTables.join(", ")}`);
6898
+ }
6899
+ const migrations = new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
6900
+ const missingMigrations = LOCAL_SQLITE_MIGRATIONS.map((migration) => migration.id).filter((id) => !migrations.has(id));
6901
+ if (missingMigrations.length > 0) {
6902
+ throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
6903
+ }
6904
+ }
6905
+ function assertTrustedLegacyLocalStoreSource(paths) {
6906
+ const stat = lstatSync2(paths.legacyLocalStorePath);
6907
+ if (stat.isSymbolicLink()) {
6908
+ throw new Error(`Legacy SQLite source must not be a symbolic link: ${paths.legacyLocalStorePath}`);
6909
+ }
6910
+ if (!stat.isFile()) {
6911
+ throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
6912
+ }
6913
+ const sourceRealPath = realpathSync4.native(paths.legacyLocalStorePath);
6914
+ if (!isPathInsideOrSame(sourceRealPath, paths.repositoryRoot)) {
6915
+ throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
6916
+ }
6917
+ }
6846
6918
  function vacuumLegacySqliteInto(sourcePath, targetPath) {
6847
6919
  const db = openSqliteDatabaseSync(sourcePath);
6848
6920
  try {
@@ -7042,6 +7114,12 @@ function canonicalPath(path) {
7042
7114
  return resolved;
7043
7115
  }
7044
7116
  }
7117
+ function isPathInsideOrSame(path, parent) {
7118
+ const child = resolve6(path);
7119
+ const base = resolve6(parent);
7120
+ const fromBase = relative4(base, child);
7121
+ return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute3(fromBase);
7122
+ }
7045
7123
  function stableStorageId(prefix, value) {
7046
7124
  return `${prefix}.${createHash5("sha256").update(value).digest("hex").slice(0, 16)}`;
7047
7125
  }
@@ -7326,15 +7404,26 @@ function isGitWorktreeError(error) {
7326
7404
  // packages/local-runtime/local-store-sqlite/src/index.ts
7327
7405
  import { execFileSync as execFileSync5 } from "node:child_process";
7328
7406
  import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
7329
- import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync6, fsyncSync as fsyncSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync6, realpathSync as realpathSync5, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync3 } from "node:fs";
7407
+ import { chmodSync as chmodSync2, closeSync as closeSync4, existsSync as existsSync6, fsyncSync as fsyncSync3, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync6, realpathSync as realpathSync5, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync3 } from "node:fs";
7330
7408
  import { readdir, readFile } from "node:fs/promises";
7331
7409
  import { createRequire as createRequire3 } from "node:module";
7332
7410
  import { homedir as homedir2 } from "node:os";
7333
- import { dirname as dirname5, isAbsolute as isAbsolute4, join as join5, resolve as resolve9 } from "node:path";
7411
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join5, relative as relative5, resolve as resolve9 } from "node:path";
7334
7412
  var runtimeRequire2 = createRequire3(import.meta.url);
7335
7413
  var SQLITE_SIDECAR_SUFFIXES2 = ["", "-wal", "-shm"];
7336
7414
  var LEGACY_MIGRATION_MARKER_FILE2 = "runtime.sqlite.migration.json";
7337
7415
  var LEGACY_MIGRATION_LOCK_FILE2 = "runtime.sqlite.migration.lock";
7416
+ var REQUIRED_LOCAL_STORE_TABLES2 = [
7417
+ "schema_migrations",
7418
+ "repository_sessions",
7419
+ "snapshots",
7420
+ "task_states",
7421
+ "observed_evidence",
7422
+ "review_results",
7423
+ "landscapes",
7424
+ "cross_repo_edges",
7425
+ "changeset_journal"
7426
+ ];
7338
7427
  var SQLITE_PRAGMAS2 = [
7339
7428
  "PRAGMA journal_mode = WAL",
7340
7429
  "PRAGMA foreign_keys = ON",
@@ -7498,9 +7587,10 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
7498
7587
  const legacyExists = existsSync6(paths.legacyLocalStorePath);
7499
7588
  const integrityCheck = {};
7500
7589
  const quarantinedFiles = [];
7501
- if (existsSync6(paths.localStorePath)) {
7590
+ const targetExists = existsSync6(paths.localStorePath);
7591
+ if (targetExists) {
7502
7592
  try {
7503
- integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
7593
+ integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
7504
7594
  return legacyMigrationResult2(false, "target-exists", paths, [], {
7505
7595
  status: "target-current",
7506
7596
  integrityCheck
@@ -7511,7 +7601,6 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
7511
7601
  if (!legacyExists) {
7512
7602
  throw new Error(`ArchContext runtime state target is not a valid SQLite database and no legacy store is available: ${paths.localStorePath}`);
7513
7603
  }
7514
- quarantinedFiles.push(...quarantineExistingLocalStore2(paths));
7515
7604
  }
7516
7605
  }
7517
7606
  if (!legacyExists) {
@@ -7520,18 +7609,21 @@ function migrateLegacyLocalStoreIfNeeded2(root = process.cwd(), env = process.en
7520
7609
  integrityCheck
7521
7610
  });
7522
7611
  }
7612
+ assertTrustedLegacyLocalStoreSource2(paths);
7523
7613
  const lock = acquireLegacyMigrationLock2(paths);
7524
7614
  const stagingDir = join5(paths.workspaceStateDir, `.runtime.sqlite.migration-${process.pid}-${randomUUID2()}`);
7525
7615
  const stagingPath = join5(stagingDir, "runtime.sqlite");
7526
7616
  try {
7617
+ if (targetExists)
7618
+ quarantinedFiles.push(...quarantineExistingLocalStore2(paths));
7527
7619
  ensurePrivateDir2(stagingDir);
7528
7620
  integrityCheck.legacy = vacuumLegacySqliteInto2(paths.legacyLocalStorePath, stagingPath);
7529
7621
  makePrivateFile2(stagingPath);
7530
7622
  migrateSqliteDatabaseSync2(stagingPath);
7531
7623
  compactSqliteDatabase2(stagingPath);
7532
- integrityCheck.staging = assertSqliteIntegrity2(stagingPath);
7624
+ integrityCheck.staging = assertCurrentLocalStore2(stagingPath);
7533
7625
  publishStagedLocalStore2(stagingPath, paths.localStorePath);
7534
- integrityCheck.target = assertSqliteIntegrity2(paths.localStorePath);
7626
+ integrityCheck.target = assertCurrentLocalStore2(paths.localStorePath);
7535
7627
  const markerPath = writeLegacyMigrationMarker2(paths, integrityCheck, quarantinedFiles);
7536
7628
  return legacyMigrationResult2(true, undefined, paths, [paths.localStorePath], {
7537
7629
  status: quarantinedFiles.length > 0 ? "target-quarantined-and-migrated" : "migrated",
@@ -7798,19 +7890,42 @@ function legacyMigrationResult2(migrated, skippedReason, paths, copiedFiles, det
7798
7890
  quarantinedFiles: details.quarantinedFiles ?? []
7799
7891
  };
7800
7892
  }
7801
- function assertSqliteIntegrity2(path) {
7893
+ function assertCurrentLocalStore2(path) {
7802
7894
  const db = openSqliteDatabaseSync2(path);
7803
7895
  try {
7804
7896
  db.exec("PRAGMA busy_timeout = 5000");
7805
- const row = db.prepare("PRAGMA integrity_check").get();
7806
- const result = firstSqliteColumn2(row);
7807
- if (result !== "ok")
7808
- throw new Error(`SQLite integrity_check failed for ${path}: ${result}`);
7809
- return result;
7897
+ const integrity = sqliteIntegrityCheckOpenDatabase2(db, path);
7898
+ assertCurrentLocalStoreSchema2(db, path);
7899
+ return integrity;
7810
7900
  } finally {
7811
7901
  db.close();
7812
7902
  }
7813
7903
  }
7904
+ function assertCurrentLocalStoreSchema2(db, path) {
7905
+ const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => String(row.name)));
7906
+ const missingTables = REQUIRED_LOCAL_STORE_TABLES2.filter((table) => !tables.has(table));
7907
+ if (missingTables.length > 0) {
7908
+ throw new Error(`SQLite local store schema incomplete for ${path}: missing tables ${missingTables.join(", ")}`);
7909
+ }
7910
+ const migrations = new Set(db.prepare("SELECT id FROM schema_migrations").all().map((row) => String(row.id)));
7911
+ const missingMigrations = LOCAL_SQLITE_MIGRATIONS2.map((migration) => migration.id).filter((id) => !migrations.has(id));
7912
+ if (missingMigrations.length > 0) {
7913
+ throw new Error(`SQLite local store schema incomplete for ${path}: missing migrations ${missingMigrations.join(", ")}`);
7914
+ }
7915
+ }
7916
+ function assertTrustedLegacyLocalStoreSource2(paths) {
7917
+ const stat = lstatSync3(paths.legacyLocalStorePath);
7918
+ if (stat.isSymbolicLink()) {
7919
+ throw new Error(`Legacy SQLite source must not be a symbolic link: ${paths.legacyLocalStorePath}`);
7920
+ }
7921
+ if (!stat.isFile()) {
7922
+ throw new Error(`Legacy SQLite source must be a regular file: ${paths.legacyLocalStorePath}`);
7923
+ }
7924
+ const sourceRealPath = realpathSync5.native(paths.legacyLocalStorePath);
7925
+ if (!isPathInsideOrSame2(sourceRealPath, paths.repositoryRoot)) {
7926
+ throw new Error(`Legacy SQLite source must stay inside the repository root: ${paths.legacyLocalStorePath}`);
7927
+ }
7928
+ }
7814
7929
  function vacuumLegacySqliteInto2(sourcePath, targetPath) {
7815
7930
  const db = openSqliteDatabaseSync2(sourcePath);
7816
7931
  try {
@@ -8010,6 +8125,12 @@ function canonicalPath2(path) {
8010
8125
  return resolved;
8011
8126
  }
8012
8127
  }
8128
+ function isPathInsideOrSame2(path, parent) {
8129
+ const child = resolve9(path);
8130
+ const base = resolve9(parent);
8131
+ const fromBase = relative5(base, child);
8132
+ return fromBase === "" || !!fromBase && !fromBase.startsWith("..") && !isAbsolute4(fromBase);
8133
+ }
8013
8134
  function stableStorageId2(prefix, value) {
8014
8135
  return `${prefix}.${createHash6("sha256").update(value).digest("hex").slice(0, 16)}`;
8015
8136
  }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ const require = createRequire(import.meta.url);
5
+ const packageJson = require.resolve("@colbymchenry/codegraph/package.json");
6
+ require(join(dirname(packageJson), "npm-shim.js"));
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "bin": {
8
- "archctx": "./bin/archctx.mjs"
8
+ "archctx": "./bin/archctx.mjs",
9
+ "codegraph": "./bin/codegraph.mjs"
9
10
  },
10
- "packageManager": "bun@1.3.10",
11
11
  "files": [
12
12
  "bin",
13
13
  "README.md"
@@ -18,8 +18,7 @@
18
18
  "registry": "https://registry.npmjs.org/"
19
19
  },
20
20
  "engines": {
21
- "node": ">=24 <26",
22
- "bun": ">=1.3.10"
21
+ "node": ">=24 <26"
23
22
  },
24
23
  "dependencies": {
25
24
  "@colbymchenry/codegraph": "1.0.1"