archctx 0.4.5 → 0.4.7

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "archcontext.practice-catalog-manifest/v1",
3
3
  "catalogVersion": "2026.06.0",
4
- "productVersion": "0.4.5",
4
+ "productVersion": "0.4.7",
5
5
  "generatedAt": "1970-01-01T00:00:00.000Z",
6
6
  "entries": [
7
7
  {
@@ -389,5 +389,5 @@
389
389
  "structurizr.dsl",
390
390
  "twelve-factor"
391
391
  ],
392
- "catalogDigest": "sha256:9687f9aabf24904bb08a174a9c0a83664d437bd0862bf0c7725faf2c11bbb913"
392
+ "catalogDigest": "sha256:557e42cbb594f7f2244b78847ab8d442f02977d5a08742a5c9576c051b1405b4"
393
393
  }
package/bin/archctx.mjs CHANGED
@@ -1088,7 +1088,7 @@ function productVersionManifest() {
1088
1088
  }
1089
1089
  };
1090
1090
  }
1091
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.4.5", 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-07-11.explorer-view-v2";
1091
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.4.7", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
1092
1092
 
1093
1093
  // packages/contracts/src/validator.ts
1094
1094
  function validateJsonSchema(schema, value) {
@@ -10397,13 +10397,21 @@ function diagnostics() {
10397
10397
  const egress = localEgressStatus();
10398
10398
  return {
10399
10399
  node: process.version,
10400
- supportedNode: /^v(24|25)\./.test(process.version),
10400
+ supportedNode: isSupportedNodeVersion(process.version),
10401
10401
  codeGraphVersion: REQUIRED_CODEGRAPH_VERSION,
10402
10402
  privacyRouteDigest: controlPlaneRouteDigest(),
10403
10403
  secureDefaults: secureDefaults(),
10404
10404
  egress
10405
10405
  };
10406
10406
  }
10407
+ function isSupportedNodeVersion(version) {
10408
+ const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(version);
10409
+ if (!match)
10410
+ return false;
10411
+ const major = Number(match[1]);
10412
+ const minor = Number(match[2]);
10413
+ return major < 26 && (major > 22 || major === 22 && minor >= 22);
10414
+ }
10407
10415
  function secureDefaults() {
10408
10416
  return {
10409
10417
  tunnelEnabledByDefault: false,
@@ -10449,8 +10457,9 @@ function uninstallMarker(content, host) {
10449
10457
  function dependencyAudit(root) {
10450
10458
  const packageJson = JSON.parse(readFileSync6(resolve6(root, "package.json"), "utf8"));
10451
10459
  const issues = [];
10452
- if (!packageJson.engines?.node?.includes(">=24"))
10453
- issues.push("node engine must include >=24");
10460
+ if (packageJson.engines?.node !== ARCHCONTEXT_NODE_RANGE) {
10461
+ issues.push(`node engine must equal ${ARCHCONTEXT_NODE_RANGE}`);
10462
+ }
10454
10463
  return { ok: issues.length === 0, issues };
10455
10464
  }
10456
10465
  function secretScan(root) {
@@ -28492,6 +28501,71 @@ function auditGithubIssuesEnabledInManifestText(manifestText) {
28492
28501
  }
28493
28502
  var RUNTIME_RPC_VERSION = LOCAL_RUNTIME_RPC_SCHEMA_VERSION;
28494
28503
 
28504
+ class RuntimeUpdateInputError extends Error {
28505
+ }
28506
+ function decodeRuntimeWorktreeDigestProfile(value, field) {
28507
+ switch (value) {
28508
+ case "repository":
28509
+ case "architecture-documentation-projection":
28510
+ return value;
28511
+ default:
28512
+ throw new RuntimeUpdateInputError(`${field} must be repository or architecture-documentation-projection`);
28513
+ }
28514
+ }
28515
+ function decodeRuntimePlanUpdateInput(value) {
28516
+ const input = runtimeUpdateInputRecord(value, "plan_update input");
28517
+ if (typeof input.id !== "string" || input.id.length === 0) {
28518
+ throw new RuntimeUpdateInputError("plan_update id must be a non-empty string");
28519
+ }
28520
+ if (!Array.isArray(input.operations)) {
28521
+ throw new RuntimeUpdateInputError("plan_update operations must be an array");
28522
+ }
28523
+ let worktreeDigestPrecondition;
28524
+ if (input.worktreeDigestPrecondition !== undefined) {
28525
+ const precondition = runtimeUpdateInputRecord(input.worktreeDigestPrecondition, "plan_update worktreeDigestPrecondition");
28526
+ const profile = decodeRuntimeWorktreeDigestProfile(precondition.profile, "plan_update worktreeDigestPrecondition.profile");
28527
+ if (profile !== "architecture-documentation-projection") {
28528
+ throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.profile must be architecture-documentation-projection");
28529
+ }
28530
+ if (typeof precondition.expectedDigest !== "string" || precondition.expectedDigest.length === 0) {
28531
+ throw new RuntimeUpdateInputError("plan_update worktreeDigestPrecondition.expectedDigest must be a non-empty string");
28532
+ }
28533
+ worktreeDigestPrecondition = { profile, expectedDigest: precondition.expectedDigest };
28534
+ }
28535
+ return {
28536
+ id: input.id,
28537
+ operations: input.operations,
28538
+ ...input.reason === undefined ? {} : { reason: input.reason },
28539
+ ...worktreeDigestPrecondition === undefined ? {} : { worktreeDigestPrecondition }
28540
+ };
28541
+ }
28542
+ function decodeRuntimeApplyUpdateInput(value) {
28543
+ const input = runtimeUpdateInputRecord(value, "apply_update input");
28544
+ if (typeof input.id !== "string" || input.id.length === 0) {
28545
+ throw new RuntimeUpdateInputError("apply_update id must be a non-empty string");
28546
+ }
28547
+ if (typeof input.approved !== "boolean") {
28548
+ throw new RuntimeUpdateInputError("apply_update approved must be a boolean");
28549
+ }
28550
+ if (typeof input.expectedWorktreeDigest !== "string" || input.expectedWorktreeDigest.length === 0) {
28551
+ throw new RuntimeUpdateInputError("apply_update expectedWorktreeDigest must be a non-empty string");
28552
+ }
28553
+ const worktreeDigestProfile = input.worktreeDigestProfile === undefined ? undefined : decodeRuntimeWorktreeDigestProfile(input.worktreeDigestProfile, "apply_update worktreeDigestProfile");
28554
+ return {
28555
+ id: input.id,
28556
+ approved: input.approved,
28557
+ expectedWorktreeDigest: input.expectedWorktreeDigest,
28558
+ ...worktreeDigestProfile === undefined ? {} : { worktreeDigestProfile },
28559
+ ...input.projectionApplyReceipt === undefined ? {} : { projectionApplyReceipt: input.projectionApplyReceipt }
28560
+ };
28561
+ }
28562
+ function runtimeUpdateInputRecord(value, field) {
28563
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
28564
+ throw new RuntimeUpdateInputError(`${field} must be an object`);
28565
+ }
28566
+ return value;
28567
+ }
28568
+
28495
28569
  class ArchitectureLedgerReadModelStore {
28496
28570
  fallback;
28497
28571
  localStore;
@@ -28602,6 +28676,7 @@ class ArchctxDaemon {
28602
28676
  checkpointBaselines = new Map;
28603
28677
  checkpointCoalesced = new Map;
28604
28678
  changesets = new Map;
28679
+ changeSetWorktreeDigestProfiles = new Map;
28605
28680
  deferredArchitectureChangeFeedFailures = new Map;
28606
28681
  auditRunAbortControllers = new Map;
28607
28682
  landscape;
@@ -29591,6 +29666,7 @@ class ArchctxDaemon {
29591
29666
  operations: [{ op: "write_waiver", path, expectedHash, body }]
29592
29667
  });
29593
29668
  this.changesets.set(draft.id, draft);
29669
+ this.changeSetWorktreeDigestProfiles.set(draft.id, "repository");
29594
29670
  return okEnvelope("practices.waive", {
29595
29671
  schemaVersion: "archcontext.practice-waiver-plan/v1",
29596
29672
  waiver,
@@ -29762,21 +29838,36 @@ class ArchctxDaemon {
29762
29838
  };
29763
29839
  return okEnvelope("resource.read", result);
29764
29840
  }
29765
- async planUpdate(root, input) {
29841
+ async planUpdate(root, rawInput) {
29766
29842
  this.assertRunning();
29843
+ let input;
29844
+ try {
29845
+ input = decodeRuntimePlanUpdateInput(rawInput);
29846
+ } catch (error) {
29847
+ if (error instanceof RuntimeUpdateInputError) {
29848
+ return errorEnvelope("plan_update", "AC_SCHEMA_INVALID", error.message);
29849
+ }
29850
+ throw error;
29851
+ }
29767
29852
  const session = await this.openSession(root);
29768
29853
  const model = await this.readModelStore.validateModel(session.workspace);
29854
+ const worktreeDigestProfile = input.worktreeDigestPrecondition?.profile ?? "repository";
29855
+ const worktreeDigest = runtimeWorktreeDigest(root, worktreeDigestProfile);
29856
+ if (input.worktreeDigestPrecondition && input.worktreeDigestPrecondition.expectedDigest !== worktreeDigest) {
29857
+ throw new Error("Worktree digest changed before plan");
29858
+ }
29769
29859
  const draft = this.changeSetEngine.plan({
29770
29860
  id: input.id,
29771
29861
  base: {
29772
29862
  headSha: session.workspace.headSha,
29773
- worktreeDigest: session.snapshot.worktreeDigest,
29863
+ worktreeDigest,
29774
29864
  modelDigest: model.modelDigest
29775
29865
  },
29776
29866
  reason: input.reason ?? { taskSessionId: "task_runtime" },
29777
29867
  operations: input.operations
29778
29868
  });
29779
29869
  this.changesets.set(draft.id, draft);
29870
+ this.changeSetWorktreeDigestProfiles.set(draft.id, worktreeDigestProfile);
29780
29871
  return okEnvelope("plan_update", {
29781
29872
  draft,
29782
29873
  preview: this.changeSetEngine.preview(root, draft)
@@ -29905,18 +29996,31 @@ class ArchctxDaemon {
29905
29996
  return cached ? { ...cached.resource, queryDigest, cacheStatus: "stale" } : undefined;
29906
29997
  }
29907
29998
  }
29908
- async applyUpdate(root, input) {
29999
+ async applyUpdate(root, rawInput) {
29909
30000
  this.assertRunning();
30001
+ let input;
30002
+ try {
30003
+ input = decodeRuntimeApplyUpdateInput(rawInput);
30004
+ } catch (error) {
30005
+ if (error instanceof RuntimeUpdateInputError) {
30006
+ return errorEnvelope("apply_update", "AC_SCHEMA_INVALID", error.message);
30007
+ }
30008
+ throw error;
30009
+ }
29910
30010
  return this.withWriter(async () => {
29911
- if (!input.expectedWorktreeDigest)
29912
- throw new Error("apply_update requires expectedWorktreeDigest");
29913
- const current = computeWorktreeDigest2(root);
29914
- if (current !== input.expectedWorktreeDigest)
29915
- throw new Error("Worktree digest changed before apply");
29916
- const session = await this.openSession(root);
29917
30011
  const draft = this.changesets.get(input.id);
29918
30012
  if (!draft)
29919
30013
  throw new Error(`Unknown ChangeSet: ${input.id}`);
30014
+ const plannedProfile = this.changeSetWorktreeDigestProfiles.get(input.id);
30015
+ if (!plannedProfile)
30016
+ throw new Error("ChangeSet worktree digest profile missing before apply");
30017
+ const requestedProfile = input.worktreeDigestProfile ?? "repository";
30018
+ if (requestedProfile !== plannedProfile)
30019
+ throw new Error("ChangeSet worktree digest profile changed before apply");
30020
+ const current = runtimeWorktreeDigest(root, plannedProfile);
30021
+ if (current !== input.expectedWorktreeDigest)
30022
+ throw new Error("Worktree digest changed before apply");
30023
+ const session = await this.openSession(root);
29920
30024
  if (draft.base.headSha !== session.workspace.headSha)
29921
30025
  throw new Error("ChangeSet HEAD changed before apply");
29922
30026
  if (draft.base.worktreeDigest !== current)
@@ -32404,7 +32508,7 @@ class ArchctxRuntimeRpcServer {
32404
32508
  pid: process.pid,
32405
32509
  protocol: "http-loopback",
32406
32510
  version: 1,
32407
- product: productVersionManifest(),
32511
+ product: this.options.productManifest?.() ?? productVersionManifest(),
32408
32512
  composition: this.daemon.compositionReport()
32409
32513
  });
32410
32514
  return;
@@ -32719,6 +32823,16 @@ function shouldSkipGeneratedProjectionJob(metadata, input) {
32719
32823
  function isRuntimeAgentJobCursorStale(job, scope) {
32720
32824
  return job.worktree.headSha !== scope.worktree.headSha || job.worktree.worktreeDigest !== scope.worktree.worktreeDigest;
32721
32825
  }
32826
+ function runtimeWorktreeDigest(root, profile) {
32827
+ switch (profile) {
32828
+ case "repository":
32829
+ return computeWorktreeDigest2(root);
32830
+ case "architecture-documentation-projection":
32831
+ return architectureDocumentationProjectionWorktreeDigest(root, loadNativeModelFromArchContext(root));
32832
+ default:
32833
+ throw new RuntimeUpdateInputError(`unsupported worktree digest profile: ${String(profile)}`);
32834
+ }
32835
+ }
32722
32836
  function isArchContextGeneratedProjectionPath(path) {
32723
32837
  return path.replace(/\\/g, "/").startsWith(".archcontext/generated/");
32724
32838
  }
@@ -35773,14 +35887,19 @@ async function runProjectionProtocolCommand(args2, cwd, daemon) {
35773
35887
  const planned = await daemon.planUpdate(root, {
35774
35888
  id: changeSetId,
35775
35889
  reason: { taskSessionId: request.requestId },
35776
- operations: [architectureDocsRenderProjectionOperation(root, projection3.files)]
35890
+ operations: [architectureDocsRenderProjectionOperation(root, projection3.files)],
35891
+ worktreeDigestPrecondition: {
35892
+ profile: "architecture-documentation-projection",
35893
+ expectedDigest: request.expected.worktreeDigest
35894
+ }
35777
35895
  });
35778
35896
  if (!planned.ok)
35779
35897
  return planned;
35780
35898
  const applied = await daemon.applyUpdate(root, {
35781
35899
  id: changeSetId,
35782
35900
  approved: true,
35783
- expectedWorktreeDigest: computeWorktreeDigest(root),
35901
+ expectedWorktreeDigest: request.expected.worktreeDigest,
35902
+ worktreeDigestProfile: "architecture-documentation-projection",
35784
35903
  ...applyReceipt ? { projectionApplyReceipt: applyReceipt } : {}
35785
35904
  });
35786
35905
  if (!applied.ok)
@@ -37571,6 +37690,9 @@ async function doctorDaemon(cwd) {
37571
37690
  const health = await client.health().catch(() => {
37572
37691
  return;
37573
37692
  });
37693
+ const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health);
37694
+ if (healthIssue)
37695
+ return incompatibleDaemonStatus(healthIssue);
37574
37696
  if (health?.ok === true) {
37575
37697
  const stalenessIssue = cliEntryStalenessIssue(cwd);
37576
37698
  if (stalenessIssue?.pidAlive)
@@ -37580,6 +37702,7 @@ async function doctorDaemon(cwd) {
37580
37702
  running: health?.ok === true,
37581
37703
  staleConnection: health?.ok !== true,
37582
37704
  rpcVersionCompatible: health?.schemaVersion === productVersionManifest().runtime.localRpc.schemaVersion,
37705
+ productVersionCompatible: health?.product?.product?.version === productVersionManifest().product.version,
37583
37706
  connection: client.connectionInfo(),
37584
37707
  health: health?.ok === true ? {
37585
37708
  composition: health.composition,
@@ -37723,6 +37846,9 @@ async function createOrStartRuntimeRpcClient(cwd) {
37723
37846
  const health = await startedClient.health().catch(() => {
37724
37847
  return;
37725
37848
  });
37849
+ const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, startedClient, health);
37850
+ if (healthIssue)
37851
+ throw new RuntimeVersionUnsupportedError(healthIssue);
37726
37852
  if (health?.ok === true)
37727
37853
  return startedClient;
37728
37854
  }
@@ -37812,6 +37938,7 @@ async function runDaemonCommand(args2, cwd) {
37812
37938
  running: true,
37813
37939
  product: health.product,
37814
37940
  rpcVersionCompatible: health.schemaVersion === RUNTIME_RPC_VERSION,
37941
+ productVersionCompatible: true,
37815
37942
  ...client.connectionInfo(),
37816
37943
  token: "stored-in-connection-file"
37817
37944
  });
@@ -37852,6 +37979,9 @@ async function startBackgroundDaemon(args2, cwd) {
37852
37979
  return errorEnvelope("daemon.start", "AC_RUNTIME_VERSION_UNSUPPORTED", runtimeVersionUnsupportedMessage(compatibilityIssue));
37853
37980
  }
37854
37981
  const discovered = await discoverRunningDaemonInfo(cwd, { recoverStale: true });
37982
+ if (discovered.compatibilityIssue?.pidAlive) {
37983
+ return errorEnvelope("daemon.start", "AC_RUNTIME_VERSION_UNSUPPORTED", runtimeVersionUnsupportedMessage(discovered.compatibilityIssue));
37984
+ }
37855
37985
  if (discovered.info) {
37856
37986
  return okEnvelope("daemon.start", {
37857
37987
  running: true,
@@ -37909,7 +38039,8 @@ async function startBackgroundDaemon(args2, cwd) {
37909
38039
  }
37910
38040
  }
37911
38041
  async function upgradeDaemon(args2, cwd) {
37912
- const issue = runtimeRpcCompatibilityIssue(cwd) ?? cliEntryStalenessIssue(cwd);
38042
+ const persistedIssue = runtimeRpcCompatibilityIssue(cwd) ?? cliEntryStalenessIssue(cwd);
38043
+ const issue = persistedIssue ?? (await discoverRunningDaemonInfo(cwd)).compatibilityIssue;
37913
38044
  if (!issue) {
37914
38045
  const started2 = await startBackgroundDaemon(args2, cwd);
37915
38046
  return started2.ok ? { ...started2, requestId: "daemon.upgrade", data: { ...started2.data, upgraded: false, reason: "runtime-compatible" } } : started2;
@@ -37930,6 +38061,10 @@ async function upgradeDaemon(args2, cwd) {
37930
38061
  previousStartedAt: issue.received,
37931
38062
  entrypointMtime: issue.expected,
37932
38063
  previousPid: issue.pid
38064
+ } : issue.reason === "product-version-mismatch" ? {
38065
+ previousProductVersion: issue.received,
38066
+ expectedProductVersion: issue.expected,
38067
+ previousPid: issue.pid
37933
38068
  } : {
37934
38069
  previousRpcSchemaVersion: issue.received,
37935
38070
  expectedRpcSchemaVersion: issue.expected,
@@ -37954,17 +38089,27 @@ async function discoverRunningDaemonInfo(cwd, options = {}) {
37954
38089
  if (!client) {
37955
38090
  return {
37956
38091
  info: undefined,
38092
+ compatibilityIssue: undefined,
37957
38093
  recovery: options.recoverStale ? recoverStaleDaemonControlFiles(cwd) : emptyRecovery(cwd)
37958
38094
  };
37959
38095
  }
37960
38096
  const health = await client.health().catch(() => {
37961
38097
  return;
37962
38098
  });
38099
+ const compatibilityIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health) ?? cliEntryStalenessIssue(cwd);
38100
+ if (compatibilityIssue) {
38101
+ return {
38102
+ info: undefined,
38103
+ compatibilityIssue,
38104
+ recovery: emptyRecovery(cwd)
38105
+ };
38106
+ }
37963
38107
  if (health?.ok === true) {
37964
- return { info: client.connectionInfo(), recovery: emptyRecovery(cwd) };
38108
+ return { info: client.connectionInfo(), compatibilityIssue: undefined, recovery: emptyRecovery(cwd) };
37965
38109
  }
37966
38110
  return {
37967
38111
  info: undefined,
38112
+ compatibilityIssue: undefined,
37968
38113
  recovery: options.recoverStale ? recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true }) : emptyRecovery(cwd)
37969
38114
  };
37970
38115
  }
@@ -37972,26 +38117,40 @@ function runtimeRpcCompatibilityIssueFromHealth(cwd, client, health) {
37972
38117
  if (!(health && typeof health === "object"))
37973
38118
  return;
37974
38119
  const body = health;
37975
- if (body.ok !== false || body.error !== "runtime RPC version mismatch")
37976
- return;
37977
38120
  const connection = client.connectionInfo();
37978
38121
  const pid = typeof connection.pid === "number" ? connection.pid : undefined;
37979
- return {
37980
- reason: "rpc-version-mismatch",
37981
- expected: RUNTIME_RPC_VERSION,
37982
- received: typeof body.expected === "string" ? body.expected : "unknown",
38122
+ const location = {
37983
38123
  connectionPath: connection.connectionPath ?? defaultDaemonConnectionPath(cwd),
37984
38124
  lockPath: connection.lockPath ?? defaultDaemonLockPath(cwd),
37985
38125
  pid,
37986
38126
  pidAlive: pid !== undefined ? isPidAlive(pid) : false,
37987
38127
  upgradeCommand: "archctx daemon upgrade"
37988
38128
  };
38129
+ if (body.ok === true) {
38130
+ const expected = productVersionManifest().product.version;
38131
+ const received = typeof body.product?.product?.version === "string" ? body.product.product.version : "unknown";
38132
+ return received === expected ? undefined : {
38133
+ reason: "product-version-mismatch",
38134
+ expected,
38135
+ received,
38136
+ ...location
38137
+ };
38138
+ }
38139
+ if (body.ok !== false || body.error !== "runtime RPC version mismatch")
38140
+ return;
38141
+ return {
38142
+ reason: "rpc-version-mismatch",
38143
+ expected: RUNTIME_RPC_VERSION,
38144
+ received: typeof body.expected === "string" ? body.expected : "unknown",
38145
+ ...location
38146
+ };
37989
38147
  }
37990
38148
  function incompatibleDaemonStatus(issue) {
37991
38149
  return {
37992
38150
  running: issue.pidAlive,
37993
38151
  protocol: "http-loopback",
37994
- rpcVersionCompatible: false,
38152
+ rpcVersionCompatible: issue.reason === "product-version-mismatch",
38153
+ productVersionCompatible: issue.reason !== "product-version-mismatch",
37995
38154
  connectionPath: issue.connectionPath,
37996
38155
  lockPath: issue.lockPath,
37997
38156
  pid: issue.pid,
@@ -38008,6 +38167,9 @@ function runtimeVersionUnsupportedMessage(issue) {
38008
38167
  if (issue.reason === "stale-daemon-entry") {
38009
38168
  return `archctxd (pid ${issue.pid ?? "unknown"}, started ${issue.received}) was spawned from an older copy of the archctx entrypoint than the one running this command (modified ${issue.expected}); run ${issue.upgradeCommand} to replace the local daemon.`;
38010
38169
  }
38170
+ if (issue.reason === "product-version-mismatch") {
38171
+ return `archctxd product version ${issue.received} does not exactly match this CLI (${issue.expected}); run ${issue.upgradeCommand} to replace the local daemon.`;
38172
+ }
38011
38173
  return `archctxd RPC version ${issue.received} is incompatible with this CLI (${issue.expected}); run ${issue.upgradeCommand} to replace the local daemon.`;
38012
38174
  }
38013
38175
  async function waitForPidExit(pid, timeoutMs) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "registry": "https://registry.npmjs.org/"
21
21
  },
22
22
  "engines": {
23
- "node": ">=24 <26"
23
+ "node": ">=22.22 <26"
24
24
  },
25
25
  "dependencies": {
26
26
  "@colbymchenry/codegraph": "1.5.0",
@@ -22,7 +22,7 @@
22
22
  "additionalProperties": false,
23
23
  "required": ["node"],
24
24
  "properties": {
25
- "node": { "const": ">=24 <26" }
25
+ "node": { "const": ">=22.22 <26" }
26
26
  }
27
27
  },
28
28
  "surfaces": {