archctx 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "archcontext.practice-catalog-manifest/v1",
3
3
  "catalogVersion": "2026.06.0",
4
- "productVersion": "0.9.0",
4
+ "productVersion": "0.9.1",
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:c26e4ebe824315b857d673a5eba8538e2330208333d543d3f23583888d595e9a"
392
+ "catalogDigest": "sha256:d23b231c9623d6594abcceb22ce08bb74e81c157ce8dca59e3efaef5f961c264"
393
393
  }
package/bin/archctx.mjs CHANGED
@@ -53,6 +53,9 @@ function stableYaml(value, indent = 0) {
53
53
  return `[]
54
54
  `;
55
55
  return value.map((item) => {
56
+ if (Array.isArray(item))
57
+ return `${pad}- ${canonicalize(item)}
58
+ `;
56
59
  if (item !== null && typeof item === "object") {
57
60
  return `${pad}- ${stableYaml(item, indent + 2).trimStart()}`;
58
61
  }
@@ -3873,7 +3876,7 @@ function compareVersionTriples(left, right) {
3873
3876
  }
3874
3877
  return 0;
3875
3878
  }
3876
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.0", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
3879
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.1", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
3877
3880
 
3878
3881
  // packages/contracts/src/refactor.ts
3879
3882
  function refactorNamespaceIssues(value, label = "product.id") {
@@ -27311,7 +27314,7 @@ function listModelFiles(root) {
27311
27314
  return {
27312
27315
  path,
27313
27316
  body,
27314
- schemaVersion: extractSchemaVersion(body),
27317
+ schemaVersion: extractSchemaVersion(body, path),
27315
27318
  digest: digestJson({ path, body })
27316
27319
  };
27317
27320
  });
@@ -27357,7 +27360,15 @@ function readdirOrFile(path) {
27357
27360
  return "file";
27358
27361
  }
27359
27362
  }
27360
- function extractSchemaVersion(body) {
27363
+ function extractSchemaVersion(body, path) {
27364
+ if (path.startsWith(".archcontext/model/") && /\.ya?ml$/.test(path)) {
27365
+ try {
27366
+ const parsed = parseJsonOrStableYaml(body, path);
27367
+ return typeof parsed?.schemaVersion === "string" ? parsed.schemaVersion : "";
27368
+ } catch {
27369
+ return "";
27370
+ }
27371
+ }
27361
27372
  const match = body.match(/schemaVersion:\s*"?([^"\n]+)"?/);
27362
27373
  if (match)
27363
27374
  return match[1].trim();
@@ -39648,6 +39659,7 @@ class AgentJobService {
39648
39659
  supersededByJobId: input.supersededByJobId,
39649
39660
  now: input.now ?? this.context.clock()
39650
39661
  });
39662
+ this.context.abortOwnedAudit(job.job.jobId);
39651
39663
  return okEnvelope("jobs.cancel", { job });
39652
39664
  }
39653
39665
  async runtimeAgentJobInScope(scope, jobId) {
@@ -54391,7 +54403,7 @@ class ProjectionWriterFailure extends Error {
54391
54403
  function projectionWriterPaths(repositoryRoot, remote, base, env = process.env) {
54392
54404
  const key = createHash13("sha256").update(`${remote}\x00${base}`, "utf8").digest("hex").slice(0, 16);
54393
54405
  const dir = join23(runtimeStatePaths(repositoryRoot, env).repositoryStateDir, "projection-writer", key);
54394
- return { dir, worktree: join23(dir, "worktree"), state: join23(dir, "state.json"), lock: join23(dir, "lock") };
54406
+ return { dir, baseRef: `refs/archctx/projection-writer/${key}/base`, worktree: join23(dir, "worktree"), state: join23(dir, "state.json"), lock: join23(dir, "lock") };
54395
54407
  }
54396
54408
  async function runProjectionPublishCommand(options) {
54397
54409
  const env = options.env ?? process.env;
@@ -54483,10 +54495,21 @@ async function runLockedProjectionPublish(input) {
54483
54495
  const failed = (failure, fields = {}, extra = {}) => finish("failed", { ...fields, error: writerError(failure) }, extra);
54484
54496
  try {
54485
54497
  const pushRemote = projectionWriterPushUrl(repositoryRoot, remote);
54498
+ const fetchUrls = git(repositoryRoot, ["remote", "get-url", "--all", "--", remote]).trim().split(`
54499
+ `).filter(Boolean);
54500
+ const tracksPublication = fetchUrls.length === 1 && fetchUrls[0] === pushRemote;
54486
54501
  while (iterations < PROJECTION_WRITER_MAX_ITERATIONS) {
54487
54502
  iterations += 1;
54488
- remoteGit(repositoryRoot, ["fetch", "--no-tags", remote, `+refs/heads/${base}:refs/remotes/${remote}/${base}`], env);
54489
- baseSha = git(repositoryRoot, ["rev-parse", "--verify", `refs/remotes/${remote}/${base}^{commit}`]).trim();
54503
+ remoteGit(repositoryRoot, [
54504
+ "fetch",
54505
+ "--no-tags",
54506
+ "--no-write-fetch-head",
54507
+ "--",
54508
+ pushRemote,
54509
+ `+refs/heads/${base}:${paths.baseRef}`,
54510
+ ...tracksPublication ? [`+refs/heads/${base}:refs/remotes/${remote}/${base}`] : []
54511
+ ], env);
54512
+ baseSha = git(repositoryRoot, ["rev-parse", "--verify", `${paths.baseRef}^{commit}`]).trim();
54490
54513
  const successMatches = state.lastSuccessfulBranch === branch && state.lastSuccessfulSha === baseSha;
54491
54514
  const pending = state.publisherPending?.branch === branch && state.publisherPending.baseSha === baseSha ? state.publisherPending : successMatches && state.lastSuccessfulCommit && state.lastSuccessfulPublisher !== publisherIdentity ? { branch, baseSha, commitSha: state.lastSuccessfulCommit, files: state.lastSuccessfulFiles } : null;
54492
54515
  if (!pending && successMatches && state.lastSuccessfulPublisher === publisherIdentity && (state.lastSuccessfulCommit === null || lsRemoteBranch(repositoryRoot, pushRemote, branch, env) === state.lastSuccessfulCommit)) {
@@ -54535,8 +54558,15 @@ async function runLockedProjectionPublish(input) {
54535
54558
  throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-apply-status-unsupported", `projection apply ended with status ${projectionStatus}; the writer publishes only noop, applied and applied-partial results`);
54536
54559
  }
54537
54560
  const changed = worktreeChangedPaths(paths.worktree);
54538
- if (changed.length === 0)
54561
+ if (changed.length === 0) {
54562
+ const tip = lsRemoteBranch(repositoryRoot, pushRemote, base, env);
54563
+ if (tip === null) {
54564
+ return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-missing", `${remote} has no branch ${base}`), { projectionStatus });
54565
+ }
54566
+ if (tip !== baseSha)
54567
+ continue;
54539
54568
  return finish("up-to-date", { projectionStatus });
54569
+ }
54540
54570
  const written = new Set(result.files.filter((file) => file.action !== "unchanged").map((file) => file.path));
54541
54571
  const offending = changed.filter((path) => !written.has(path) || !isProjectionOwnedPath(path, ownedPatterns));
54542
54572
  if (offending.length > 0) {
@@ -54551,7 +54581,7 @@ async function runLockedProjectionPublish(input) {
54551
54581
  return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-commit-failed", `git commit failed in the writer worktree; configure a git identity (user.name, user.email) for the repository or set GIT_AUTHOR_*/GIT_COMMITTER_*: ${errorMessage2(error)}`), { projectionStatus, files: changed });
54552
54582
  }
54553
54583
  const commitSha = git(paths.worktree, ["rev-parse", "HEAD"]).trim();
54554
- const tip = lsRemoteBranch(repositoryRoot, remote, base, env);
54584
+ const tip = lsRemoteBranch(repositoryRoot, pushRemote, base, env);
54555
54585
  if (tip === null) {
54556
54586
  return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-missing", `${remote} has no branch ${base}`), { projectionStatus, files: changed });
54557
54587
  }
@@ -54900,8 +54930,8 @@ function createGhProjectionPublisher(options = {}) {
54900
54930
  const env = { ...stringEnv(process.env), GH_PROMPT_DISABLED: "1" };
54901
54931
  delete env.GH_REPO;
54902
54932
  const gh = (args) => withLocalEgress2("projection-publishing", () => exec("gh", [...args, "--repo", repository], { cwd: input.worktree, env, timeoutMs }), admissionEnv);
54903
- const listed = await gh(["pr", "list", "--head", input.branch, "--base", input.base, "--state", "open", "--json", "number,url", "--limit", "1"]);
54904
- const open = parseGhPullRequestList(listed.stdout);
54933
+ const listed = await gh(["pr", "list", "--head", input.branch, "--base", input.base, "--state", "open", "--json", "number,url,isCrossRepository,headRefName,baseRefName", "--limit", "100"]);
54934
+ const open = parseGhPullRequestList(listed.stdout, input.branch, input.base);
54905
54935
  if (open) {
54906
54936
  await gh(["pr", "edit", String(open.number), "--title", input.title, "--body-file", input.bodyFile]);
54907
54937
  return { number: open.number, ...open.url ? { url: open.url } : {} };
@@ -55049,16 +55079,24 @@ function readCommandFirstLine(command) {
55049
55079
  closeSync9(fd);
55050
55080
  }
55051
55081
  }
55052
- function parseGhPullRequestList(stdout) {
55053
- const parsed = JSON.parse(stdout.trim() === "" ? "[]" : stdout);
55082
+ function parseGhPullRequestList(stdout, branch, base) {
55083
+ const parsed = JSON.parse(stdout);
55054
55084
  if (!Array.isArray(parsed))
55055
55085
  throw new Error("gh pr list returned an unexpected shape");
55056
- const first = parsed[0];
55057
- if (!first)
55058
- return;
55059
- if (typeof first.number !== "number" || !Number.isInteger(first.number) || first.number <= 0)
55060
- throw new Error("gh pr list returned a pull request without a number");
55061
- return { number: first.number, ...typeof first.url === "string" && first.url !== "" ? { url: first.url } : {} };
55086
+ if (parsed.length >= 100)
55087
+ throw new Error("gh pr list reached the 100-result limit; resolve rolling PR ambiguity manually");
55088
+ const matches = [];
55089
+ for (const row of parsed) {
55090
+ if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.isCrossRepository !== "boolean" || row.headRefName !== branch || row.baseRefName !== base || typeof row.number !== "number" || !Number.isInteger(row.number) || row.number <= 0) {
55091
+ throw new Error("gh pr list returned a pull request without verified identity");
55092
+ }
55093
+ if (!row.isCrossRepository) {
55094
+ matches.push({ number: row.number, ...typeof row.url === "string" && row.url !== "" ? { url: row.url } : {} });
55095
+ }
55096
+ }
55097
+ if (matches.length > 1)
55098
+ throw new Error("gh pr list returned multiple same-repository rolling pull requests");
55099
+ return matches[0];
55062
55100
  }
55063
55101
  function parsePublisherCommandOutput(stdout) {
55064
55102
  let parsed;
@@ -57954,23 +57992,31 @@ class ArchctxRuntimeRpcServer {
57954
57992
  this.server = undefined;
57955
57993
  const connection = this.connection;
57956
57994
  this.connection = undefined;
57995
+ const errors = [];
57996
+ const cleanup = async (action) => {
57997
+ try {
57998
+ await action();
57999
+ } catch (error) {
58000
+ errors.push(error);
58001
+ }
58002
+ };
57957
58003
  if (server) {
57958
- await new Promise((resolveClose, rejectClose) => {
58004
+ await cleanup(() => new Promise((resolveClose, rejectClose) => {
57959
58005
  server.close((error) => error ? rejectClose(error) : resolveClose());
57960
- });
57961
- }
57962
- await this.daemon.stop();
57963
- try {
57964
- if (connection)
57965
- await removePrivateControlFile(connection.connectionPath);
57966
- } finally {
57967
- if (this.lockFd !== undefined)
57968
- closeSync11(this.lockFd);
57969
- this.lockFd = undefined;
58006
+ }));
57970
58007
  }
58008
+ await cleanup(() => this.daemon.stop());
58009
+ if (connection)
58010
+ await cleanup(() => removePrivateControlFile(connection.connectionPath));
58011
+ const lockFd = this.lockFd;
58012
+ this.lockFd = undefined;
58013
+ if (lockFd !== undefined)
58014
+ await cleanup(() => closeSync11(lockFd));
57971
58015
  if (connection)
57972
- await removePrivateControlFile(connection.lockPath);
57973
- this.options.onStop?.();
58016
+ await cleanup(() => removePrivateControlFile(connection.lockPath));
58017
+ await cleanup(() => this.options.onStop?.());
58018
+ if (errors.length > 0)
58019
+ throw errors[0];
57974
58020
  }
57975
58021
  armIdleTimer() {
57976
58022
  if (this.idleTimeoutMs <= 0 || !this.server)
@@ -58486,6 +58532,7 @@ class ArchctxDaemon {
58486
58532
  localStore: this.localStore
58487
58533
  });
58488
58534
  this.agentJobs = new AgentJobService({
58535
+ abortOwnedAudit: (jobId) => this.auditRunAbortControllers.get(jobId)?.abort(),
58489
58536
  assertRunning: () => this.assertRunning(),
58490
58537
  openSession: (root) => this.openSession(root),
58491
58538
  architectureLedgerScope: (root) => this.architectureLedgerScope(root),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",