@inixiative/config 0.5.184 → 0.5.185

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/README.md CHANGED
@@ -71,12 +71,32 @@ bunx @inixiative/config sync [dir] [--preset=...] [--force] [--no-install]
71
71
  bunx @inixiative/config scan [root] [--lane=primitives|agentic]
72
72
  bunx @inixiative/config train [root] [--lane=primitives|agentic] [--push] [--dry-run]
73
73
  bunx @inixiative/config ports [project]
74
+ bunx @inixiative/config terms [dir] [--write-baseline]
74
75
  ```
75
76
 
76
77
  `ports` prints each project's local ports from `ports.json` (also exported as `@inixiative/config/ports.json`). A project's port for a service is the service's usual default plus 100 times the project's block, so no two projects collide. Zealot alone sits at block 0, the defaults (Aron, 2026-10-20: it already runs there); every other project is off the defaults: Archive (block 7) serves HTTP on 4700 with Postgres on 6132; Kingdom (block 2) has web 3200, API 8200, Postgres 5632, Redis 6579. Reserved ports (macOS AirPlay on 5000 and 7000) are never assigned. Give a new project the next free block (never 0), and a new kind of service its usual default; the tests fail on any collision.
77
78
 
78
79
  `check` is read-only and exits non-zero on drift — run it in CI after a frozen-lockfile install. It verifies toolchain pins — declared once, at the root, pinned exactly: a workspace member declaring one is an error, and `bun.lock` must resolve each only to its pin (no second copy) — toolchain peer ranges (they must admit the pin and no older major; `sync` writes `>=<pin major>.<pin minor>.0`), `"latest"` ranges, `packageManager`/`.bun-version`, legacy `bun.lockb`, a committed (tracked, un-ignored) `bun.lock`, a gitignored `tmp/` (scratch space; `sync` adds `/tmp/*`, which keeps re-included files tracked), required scripts (`check`/`typecheck`/`lint`/`test`), lefthook (dep + `lefthook.yml` extending the shared hooks + `prepare` script; git repos only), stub `extends`, presence of this package, and for every ecosystem dependency — in the root or any workspace member, of either lane — that the declared range admits the blessed version and the lockfile actually resolves to it (the stale-lockfile class). A package with a candidate (one no hold covers) also accepts the candidate and anything between: a range admitting either passes, and so does a lock from the blessed version up to the candidate. A range that does not admit the blessed version is rewritten to `^<blessed>`, except an exact pin, which stays exact and moves to the blessed version. An npm alias (`"@inixiative/session-archive": "npm:@inixiative/archive@^0.2.1"`) is held to its target's blessed version and rewritten as an alias; `workspace:`, `file:` and `link:` ranges are a repo linking itself and are left alone.
79
80
 
81
+ `check` also runs the **retired-terms** rule (alone: `terms`), which keeps public copy from rotting back to names the project retired. It reads text people read, never code identifiers: every Markdown, MDX, HTML and Astro file a repo tracks, and each `package.json` `description`. Fixtures, changelogs (history records the rename), `node_modules`, generated Warden regions (`.foundry/regions/`; fix their source docs) and root `dist`, `build`, `coverage` and `tmp` are skipped. The rule runs in `check` and `terms` only; `scan`, `sync` and the train are about package drift and don't run it. The list lives in `src/retiredTerms.ts`, each term with the word to use instead, and comes from the workspace CLAUDE.md "Dead concepts" and the copy brief's "Retired words":
82
+
83
+ | Term | Checked in | Use instead |
84
+ | --- | --- | --- |
85
+ | Kastle, Castle (capitalised) | every doc | Kingdom, owner, integration or Signet |
86
+ | `RuntimeInstallation`, `kingdom_runtime_` | every doc | a Signet enrollment |
87
+ | `signetDemo`, `medicalQuestionStep` | every doc | nothing; demos live in template-workbench |
88
+ | `kingdom-workspace`, `foundry-lab`, `native-candidate` | every doc | the canonical repos |
89
+ | foundry-oracle | every doc | Foundry and Oracle, two products |
90
+ | "your own LLM/API keys" | every doc | your own subscriptions |
91
+ | Cartographer, Librarian, Artificer, Steward (capitalised) | READMEs, package descriptions, site pages only | say what it does; Foundry's code still has a Librarian and a Cartographer |
92
+
93
+ Left out on purpose, because a pattern would flag mostly legitimate text: "medical" (the doctor's-visit story is allowed in marketing), "Connection" and `connectionId` (WebSocket and database connections; Foundry's viewer has a live per-socket `connectionId`), "principal" (the permissions README's glossary word for any holder, job titles, the principal–agent problem), and "Cursor as a runtime" or "token savings" (a phrase can't tell a claim from a comparison).
94
+
95
+ Per repo, in `scripts/ci/retired-terms/` (the same shape as template's doc-freshness rule):
96
+
97
+ - `allow.txt` for legitimate uses: `<term>` allows it everywhere, `<path>\t<term>` in one file, or under a directory when the path ends in `/` (a retired-names table, a history doc). Stale copy never goes here.
98
+ - `baseline.txt` for uses that predate the rule, `<path>\t<term>\t<count>`: reported as one warning, not failed. A use beyond the count fails, and so does a count that dropped (lower or delete the line), so the baseline only shrinks. `terms --write-baseline` writes it when there is none, and later only to shrink it: it refuses to add a use.
99
+
80
100
  `sync` applies every fix (deleting member toolchain entries and their stale member links), re-locks via `bun install` (`--force` when a second toolchain copy survives), and runs `bun update` on stale ecosystem entries in the directory that declares them. It refuses to run on a dirty working tree without `--force`. The react preset is inferred from a `react` dependency; override with `--preset`. Missing scripts are filled with defaults; existing script bodies are never touched.
81
101
 
82
102
  `scan` runs `check` across every ecosystem checkout under a root directory, for every lane or just the one `--lane` names. Targets are derived from the BOM: lane packages match by package name — the root package or any workspace member, so a monorepo such as foundry is found by the packages it publishes — and consumers match by their `origin` GitHub repo. Directory names and local layout never need declaring: scan looks at each child of the root and one level into a child without a `package.json` (a workspace folder such as `team-workspace/`). Linked git worktrees are other sessions' branches and never targets; when several clones claim one package or repo, only those with an `origin` remote count, a clone directly under the root wins over one nested in a workspace folder, and if that still leaves more than one the target is reported ambiguous rather than guessed. Origin is the truth, not the checkout: scan fetches per repo and fails on checkouts behind origin (their findings describe a stale tree), fails when a BOM entry has no checkout at all, warns when a checkout sits on a non-default branch (its findings describe the branch, not what ships), and surfaces unmerged `claude/*` session branches.
@@ -142,7 +162,7 @@ jobs:
142
162
  uses: inixiative/config/.github/workflows/lib-ci.yml@main
143
163
  ```
144
164
 
145
- `lib-ci.yml` pins bun from `.bun-version`, installs with `--frozen-lockfile` (kills the stale-lockfile class), runs `inixiative-config check .`, then the repo's own `bun run check`.
165
+ `lib-ci.yml` pins bun from `.bun-version`, installs with `--frozen-lockfile` (kills the stale-lockfile class), runs `inixiative-config check .` (retired terms included), then the repo's own `bun run check`. A repo that doesn't use `lib-ci.yml` (template, Kingdom, Oracle, agent-session, gloss, Tribe) adopts the retired-terms rule by running `bunx inixiative-config terms .` from its own CI rules (template: a `scripts/ci/rules/retired-terms.sh` beside `doc-freshness.sh`).
146
166
 
147
167
  ## Release flow
148
168
 
package/dist/cli.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { spawnSync as spawnSync2 } from "child_process";
4
+ import { spawnSync as spawnSync3 } from "child_process";
5
5
  import {
6
- existsSync as existsSync2,
7
- mkdirSync as mkdirSync2,
6
+ existsSync as existsSync3,
7
+ mkdirSync as mkdirSync3,
8
8
  mkdtempSync,
9
- readdirSync as readdirSync2,
10
- readFileSync as readFileSync2,
11
- rmSync as rmSync2,
12
- writeFileSync as writeFileSync2
9
+ readdirSync as readdirSync3,
10
+ readFileSync as readFileSync3,
11
+ rmSync as rmSync3,
12
+ writeFileSync as writeFileSync3
13
13
  } from "fs";
14
14
  import { hostname as hostname2, tmpdir } from "os";
15
- import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
15
+ import { basename, dirname as dirname3, join as join3, resolve as resolve2 } from "path";
16
16
 
17
17
  // src/args.ts
18
18
  import { parseArgs } from "util";
@@ -999,7 +999,7 @@ var isConfigCheckout = (dir2) => {
999
999
  };
1000
1000
 
1001
1001
  // src/args.ts
1002
- var COMMANDS = ["check", "sync", "scan", "train", "ports"];
1002
+ var COMMANDS = ["check", "sync", "scan", "train", "ports", "terms"];
1003
1003
  var PRESETS = ["base", "node", "react"];
1004
1004
  var preset = { type: "string" };
1005
1005
  var lane = { type: "string" };
@@ -1009,7 +1009,8 @@ var OPTIONS = {
1009
1009
  sync: { preset, force: flag, "no-install": flag },
1010
1010
  scan: { lane },
1011
1011
  train: { lane, push: flag, "dry-run": flag },
1012
- ports: {}
1012
+ ports: {},
1013
+ terms: { "write-baseline": flag }
1013
1014
  };
1014
1015
  var UsageError = class extends Error {
1015
1016
  };
@@ -1057,7 +1058,8 @@ var parseCommandLine = (argv) => {
1057
1058
  force: values.force === true,
1058
1059
  noInstall: values["no-install"] === true,
1059
1060
  push: values.push === true,
1060
- dryRun: values["dry-run"] === true
1061
+ dryRun: values["dry-run"] === true,
1062
+ writeBaseline: values["write-baseline"] === true
1061
1063
  };
1062
1064
  };
1063
1065
 
@@ -1103,6 +1105,238 @@ function portsFor(project) {
1103
1105
  );
1104
1106
  }
1105
1107
 
1108
+ // src/retiredTerms.ts
1109
+ import { spawnSync as spawnSync2 } from "child_process";
1110
+ import {
1111
+ existsSync as existsSync2,
1112
+ lstatSync,
1113
+ mkdirSync as mkdirSync2,
1114
+ readdirSync as readdirSync2,
1115
+ readFileSync as readFileSync2,
1116
+ rmSync as rmSync2,
1117
+ writeFileSync as writeFileSync2
1118
+ } from "fs";
1119
+ import { dirname as dirname2, join as join2 } from "path";
1120
+ var RETIRED_TERMS = [
1121
+ {
1122
+ id: "kastle",
1123
+ pattern: /\bkastles?\b/gi,
1124
+ scope: "docs",
1125
+ instead: "Kingdom, owner, integration or Signet"
1126
+ },
1127
+ {
1128
+ id: "castle",
1129
+ pattern: /\bCastles?\b/g,
1130
+ scope: "docs",
1131
+ instead: "Kingdom, owner, integration or Signet"
1132
+ },
1133
+ {
1134
+ id: "runtime-keys",
1135
+ pattern: /\bRuntimeInstallations?\b|\bkingdom_runtime_/g,
1136
+ scope: "docs",
1137
+ instead: "a Signet enrollment on the Foundry integration"
1138
+ },
1139
+ {
1140
+ id: "medical-demo",
1141
+ pattern: /\bsignetDemo\b|\bmedicalQuestionStep\b/g,
1142
+ scope: "docs",
1143
+ instead: "nothing: the demo is gone (demos live in template-workbench)"
1144
+ },
1145
+ {
1146
+ id: "retired-workspaces",
1147
+ pattern: /\bkingdom-workspace\b|\bfoundry-lab\b|\bnative-candidate\b/g,
1148
+ scope: "docs",
1149
+ instead: "the canonical repos (kingdom, foundry)"
1150
+ },
1151
+ {
1152
+ id: "foundry-oracle",
1153
+ pattern: /\bfoundry-oracle\b/gi,
1154
+ scope: "docs",
1155
+ instead: "Foundry and Oracle, two products"
1156
+ },
1157
+ {
1158
+ id: "own-llm-keys",
1159
+ pattern: /\byour own (?:LLM|model|API) keys?\b/gi,
1160
+ scope: "docs",
1161
+ instead: "your own subscriptions (Foundry runs on subscription logins)"
1162
+ },
1163
+ {
1164
+ id: "foundry-roles",
1165
+ pattern: /\b(?:Cartographer|Librarian|Artificer|Steward)s?\b/g,
1166
+ scope: "public",
1167
+ instead: "say what it does: Wardens point at what exists; your agent decides"
1168
+ }
1169
+ ];
1170
+ var RETIRED_TERMS_DIR = "scripts/ci/retired-terms";
1171
+ var ALLOW_FILE = `${RETIRED_TERMS_DIR}/allow.txt`;
1172
+ var BASELINE_FILE = `${RETIRED_TERMS_DIR}/baseline.txt`;
1173
+ var TEXT_FILE = /\.(md|mdx|html|astro)$/i;
1174
+ var PUBLIC_FILE = /(^|\/)README[^/]*\.md$|\.(html|astro|mdx)$/i;
1175
+ var SKIPPED = /(^|\/)(node_modules|\.git|\.worktrees|fixtures|rule-violations)\/|^(dist|build|coverage|tmp)\/|^\.foundry\/regions\/|(^|\/)CHANGELOG[^/]*\.md$/i;
1176
+ var walk = (root, dir2 = "", out = []) => {
1177
+ for (const entry of readdirSync2(join2(root, dir2), { withFileTypes: true })) {
1178
+ const rel = dir2 ? `${dir2}/${entry.name}` : entry.name;
1179
+ if (entry.isDirectory() && !SKIPPED.test(`${rel}/`)) walk(root, rel, out);
1180
+ else if (entry.isFile()) out.push(rel);
1181
+ }
1182
+ return out;
1183
+ };
1184
+ var readText = (abs) => {
1185
+ try {
1186
+ return lstatSync(abs).isFile() ? readFileSync2(abs, "utf8") : null;
1187
+ } catch {
1188
+ return null;
1189
+ }
1190
+ };
1191
+ var listFiles = (root) => {
1192
+ if (existsSync2(join2(root, ".git"))) {
1193
+ const listed = spawnSync2(
1194
+ "git",
1195
+ ["ls-files", "-z", "--cached", "--others", "--exclude-standard"],
1196
+ {
1197
+ cwd: root,
1198
+ encoding: "utf8"
1199
+ }
1200
+ );
1201
+ if (listed.status === 0) return listed.stdout.split("\0").filter(Boolean);
1202
+ }
1203
+ return walk(root);
1204
+ };
1205
+ var lineOf = (text, index) => text.slice(0, index).split("\n").length;
1206
+ var scanRetiredTerms = (root) => {
1207
+ const hits = [];
1208
+ for (const path of listFiles(root).sort()) {
1209
+ if (SKIPPED.test(path) || path.startsWith(`${RETIRED_TERMS_DIR}/`)) continue;
1210
+ const isManifest = /(^|\/)package\.json$/.test(path);
1211
+ if (!isManifest && !TEXT_FILE.test(path)) continue;
1212
+ const raw = readText(join2(root, path));
1213
+ if (raw === null) continue;
1214
+ let text = raw;
1215
+ if (isManifest) {
1216
+ try {
1217
+ const description = JSON.parse(raw).description;
1218
+ if (typeof description !== "string") continue;
1219
+ text = description;
1220
+ } catch {
1221
+ continue;
1222
+ }
1223
+ }
1224
+ const isPublic = isManifest || PUBLIC_FILE.test(path);
1225
+ for (const term of RETIRED_TERMS) {
1226
+ if (term.scope === "public" && !isPublic) continue;
1227
+ for (const match of text.matchAll(term.pattern)) {
1228
+ hits.push({
1229
+ path,
1230
+ line: isManifest ? lineOf(raw, raw.indexOf('"description"')) : lineOf(text, match.index),
1231
+ term,
1232
+ text: match[0]
1233
+ });
1234
+ }
1235
+ }
1236
+ }
1237
+ return hits;
1238
+ };
1239
+ var readList = (root, file) => {
1240
+ const abs = join2(root, file);
1241
+ if (!existsSync2(abs)) return [];
1242
+ return readFileSync2(abs, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => line.trim() !== "" && !line.trimStart().startsWith("#"));
1243
+ };
1244
+ var allowedBy = (lines) => {
1245
+ const rules = lines.map((line) => {
1246
+ const [first2, second] = line.split(" ");
1247
+ return second === void 0 ? { path: "", term: first2.trim() } : { path: first2, term: second.trim() };
1248
+ });
1249
+ return (hit) => rules.some(
1250
+ (rule) => rule.term === hit.term.id && (rule.path === "" || rule.path === hit.path || rule.path.endsWith("/") && hit.path.startsWith(rule.path))
1251
+ );
1252
+ };
1253
+ var countByKey = (hits) => {
1254
+ const counts = /* @__PURE__ */ new Map();
1255
+ for (const hit of hits) {
1256
+ const key = `${hit.path} ${hit.term.id}`;
1257
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1258
+ }
1259
+ return counts;
1260
+ };
1261
+ var BASELINE_LINE = /^([^\t]+)\t([^\t]+)\t(\d+)$/;
1262
+ var readBaseline = (root) => {
1263
+ const counts = /* @__PURE__ */ new Map();
1264
+ const malformed = [];
1265
+ for (const line of readList(root, BASELINE_FILE)) {
1266
+ const match = BASELINE_LINE.exec(line);
1267
+ if (match) counts.set(`${match[1]} ${match[2]}`, Number(match[3]));
1268
+ else malformed.push(line);
1269
+ }
1270
+ return { counts, malformed };
1271
+ };
1272
+ var checkRetiredTerms = (root) => {
1273
+ const allowed = allowedBy(readList(root, ALLOW_FILE));
1274
+ const hits = scanRetiredTerms(root).filter((hit) => !allowed(hit));
1275
+ const counts = countByKey(hits);
1276
+ const { counts: baseline, malformed } = readBaseline(root);
1277
+ const findings = malformed.map((line) => ({
1278
+ level: "error",
1279
+ message: `${BASELINE_FILE}: "${line}" is not <path>\\t<term>\\t<count> \u2192 rewrite it with terms --write-baseline`
1280
+ }));
1281
+ for (const [key, count] of counts) {
1282
+ const known = baseline.get(key) ?? 0;
1283
+ if (count <= known) continue;
1284
+ const fresh = hits.filter((hit) => `${hit.path} ${hit.term.id}` === key).slice(known);
1285
+ for (const hit of fresh) {
1286
+ findings.push({
1287
+ level: "error",
1288
+ message: `retired term in ${hit.path}:${hit.line}: "${hit.text}" (${hit.term.id}) \u2192 ${hit.term.instead}`
1289
+ });
1290
+ }
1291
+ }
1292
+ for (const [key, known] of baseline) {
1293
+ const count = counts.get(key) ?? 0;
1294
+ if (count >= known) continue;
1295
+ const [path, term] = key.split(" ");
1296
+ findings.push({
1297
+ level: "error",
1298
+ message: count === 0 ? `${BASELINE_FILE}: ${path} no longer uses ${term} \u2192 delete its line` : `${BASELINE_FILE}: ${path} uses ${term} ${count}\xD7 now, not ${known} \u2192 lower its count`
1299
+ });
1300
+ }
1301
+ const baselined = [...counts].reduce(
1302
+ (sum, [key, count]) => sum + Math.min(count, baseline.get(key) ?? 0),
1303
+ 0
1304
+ );
1305
+ if (baselined > 0) {
1306
+ findings.push({
1307
+ level: "warn",
1308
+ message: `${baselined} retired-term use${baselined === 1 ? "" : "s"} in ${BASELINE_FILE} (not blocking; fix the copy and shrink the baseline)`
1309
+ });
1310
+ }
1311
+ return { findings, hits };
1312
+ };
1313
+ var writeRetiredTermsBaseline = (root) => {
1314
+ const allowed = allowedBy(readList(root, ALLOW_FILE));
1315
+ const counts = countByKey(scanRetiredTerms(root).filter((hit) => !allowed(hit)));
1316
+ const path = join2(root, BASELINE_FILE);
1317
+ if (existsSync2(path)) {
1318
+ const { counts: baseline } = readBaseline(root);
1319
+ const refused = [...counts].filter(([key, count]) => count > (baseline.get(key) ?? 0)).map(
1320
+ ([key, count]) => `${key.replace(" ", " ")} ${count}\xD7 (baseline ${baseline.get(key) ?? 0})`
1321
+ );
1322
+ if (refused.length > 0) return { written: 0, refused };
1323
+ }
1324
+ const header = [
1325
+ "# Retired-term uses that predate the rule: reported, not failed (@inixiative/config check).",
1326
+ "# One per line: <path>\\t<term>\\t<count>. Fix the copy and lower or delete its line.",
1327
+ "# Rewrite after fixing: inixiative-config terms --write-baseline (it refuses to grow)."
1328
+ ];
1329
+ const lines = [...counts].sort(([a], [b]) => a.localeCompare(b)).map(([key, n]) => `${key} ${n}`);
1330
+ if (lines.length === 0) {
1331
+ rmSync2(path, { force: true });
1332
+ return { written: 0, refused: [] };
1333
+ }
1334
+ mkdirSync2(dirname2(path), { recursive: true });
1335
+ writeFileSync2(path, `${[...header, ...lines].join("\n")}
1336
+ `);
1337
+ return { written: lines.length, refused: [] };
1338
+ };
1339
+
1106
1340
  // src/cli.ts
1107
1341
  var commandLine;
1108
1342
  try {
@@ -1111,7 +1345,7 @@ try {
1111
1345
  if (!(error instanceof UsageError)) throw error;
1112
1346
  console.error(`\u2717 ${error.message}`);
1113
1347
  console.error(
1114
- "Usage: inixiative-config check [dir] [--preset=base|node|react]\n inixiative-config sync [dir] [--preset=base|node|react] [--force] [--no-install]\n inixiative-config scan [root] [--lane=primitives|agentic]\n inixiative-config train [root] [--lane=primitives|agentic] [--push] [--dry-run]\n inixiative-config ports [project]"
1348
+ "Usage: inixiative-config check [dir] [--preset=base|node|react]\n inixiative-config sync [dir] [--preset=base|node|react] [--force] [--no-install]\n inixiative-config scan [root] [--lane=primitives|agentic]\n inixiative-config train [root] [--lane=primitives|agentic] [--push] [--dry-run]\n inixiative-config ports [project]\n inixiative-config terms [dir] [--write-baseline]"
1115
1349
  );
1116
1350
  process.exit(2);
1117
1351
  }
@@ -1145,7 +1379,7 @@ var report = (findings) => {
1145
1379
  if (process.versions.bun && process.versions.bun !== manifest.bun) {
1146
1380
  console.log(`\u26A0 running bun ${process.versions.bun}, blessed is ${manifest.bun} (bun upgrade)`);
1147
1381
  }
1148
- var git = (cwd, ...gitArgs) => spawnSync2("git", gitArgs, { cwd, encoding: "utf8" });
1382
+ var git = (cwd, ...gitArgs) => spawnSync3("git", gitArgs, { cwd, encoding: "utf8" });
1149
1383
  var behindOrigin = (cwd) => {
1150
1384
  const behind = git(cwd, "rev-list", "--count", "HEAD..@{upstream}");
1151
1385
  return behind.status === 0 ? Number(behind.stdout.trim()) : 0;
@@ -1183,7 +1417,20 @@ var offDefaultBranch = (cwd) => {
1183
1417
  };
1184
1418
  if (command === "check") {
1185
1419
  const { findings } = inspect(dir, manifest, presetFlag);
1186
- process.exit(report(findings) > 0 ? 1 : 0);
1420
+ process.exit(report([...findings, ...checkRetiredTerms(dir).findings]) > 0 ? 1 : 0);
1421
+ }
1422
+ if (command === "terms") {
1423
+ if (commandLine.writeBaseline) {
1424
+ const { written, refused } = writeRetiredTermsBaseline(dir);
1425
+ if (refused.length > 0) {
1426
+ console.error("\u2717 the baseline only shrinks; fix these uses (or allow a legitimate one):");
1427
+ for (const entry of refused) console.error(` ${entry}`);
1428
+ process.exit(1);
1429
+ }
1430
+ console.log(`wrote ${written} baseline entr${written === 1 ? "y" : "ies"}`);
1431
+ process.exit(0);
1432
+ }
1433
+ process.exit(report(checkRetiredTerms(dir).findings) > 0 ? 1 : 0);
1187
1434
  }
1188
1435
  var laneLabel = (checkout) => checkout.consumer ? `consumer of ${checkout.consumer.lanes.join(" + ")}` : `${checkout.lane} lane`;
1189
1436
  if (command === "scan") {
@@ -1306,7 +1553,7 @@ if (command === "train") {
1306
1553
  const worktrees = [];
1307
1554
  const removeWorktree = ({ repo, path, tooling }) => {
1308
1555
  if (tooling) {
1309
- spawnSync2("bun", ["run", "worktree:destroy", basename(path), "--force"], {
1556
+ spawnSync3("bun", ["run", "worktree:destroy", basename(path), "--force"], {
1310
1557
  cwd: repo,
1311
1558
  stdio: "inherit"
1312
1559
  });
@@ -1355,7 +1602,7 @@ if (command === "train") {
1355
1602
  }
1356
1603
  if (!dryRun) {
1357
1604
  console.log(`\u2193 fast-forwarded to origin (+${behind})`);
1358
- checkout.pkg = JSON.parse(readFileSync2(join2(checkout.dir, "package.json"), "utf8"));
1605
+ checkout.pkg = JSON.parse(readFileSync3(join3(checkout.dir, "package.json"), "utf8"));
1359
1606
  }
1360
1607
  }
1361
1608
  return true;
@@ -1381,7 +1628,7 @@ if (command === "train") {
1381
1628
  };
1382
1629
  };
1383
1630
  const relockAndCheck = (checkout, check = true) => effect(`re-lock ${checkout.name}${check ? " and run its check" : ""}`, void 0, () => {
1384
- const install = (...flags) => spawnSync2("bun", ["install", ...flags], { cwd: checkout.dir, stdio: "inherit" }).status === 0;
1631
+ const install = (...flags) => spawnSync3("bun", ["install", ...flags], { cwd: checkout.dir, stdio: "inherit" }).status === 0;
1385
1632
  if (!install() && !install("--no-cache")) {
1386
1633
  throw new Abort(`bun install failed in ${checkout.name}`);
1387
1634
  }
@@ -1389,9 +1636,9 @@ if (command === "train") {
1389
1636
  checkout.dir,
1390
1637
  inspect(checkout.dir, target()).findings
1391
1638
  )) {
1392
- spawnSync2("bun", ["update", ...names], { cwd, stdio: "inherit" });
1639
+ spawnSync3("bun", ["update", ...names], { cwd, stdio: "inherit" });
1393
1640
  }
1394
- if (check && spawnSync2("bun", ["run", "check"], { cwd: checkout.dir, stdio: "inherit" }).status !== 0) {
1641
+ if (check && spawnSync3("bun", ["run", "check"], { cwd: checkout.dir, stdio: "inherit" }).status !== 0) {
1395
1642
  throw new CheckFailed(`check failed in ${checkout.name}`);
1396
1643
  }
1397
1644
  });
@@ -1400,7 +1647,7 @@ if (command === "train") {
1400
1647
  if (git(checkout.dir, "status", "--porcelain").stdout.trim() === "") return;
1401
1648
  git(checkout.dir, "reset", "--quiet", "--hard", "HEAD");
1402
1649
  git(checkout.dir, "clean", "--quiet", "-fd");
1403
- spawnSync2("bun", ["install", "--frozen-lockfile"], { cwd: checkout.dir, stdio: "inherit" });
1650
+ spawnSync3("bun", ["install", "--frozen-lockfile"], { cwd: checkout.dir, stdio: "inherit" });
1404
1651
  });
1405
1652
  const movesOf = (checkout) => {
1406
1653
  const candidates2 = activeCandidates(manifest);
@@ -1430,11 +1677,11 @@ if (command === "train") {
1430
1677
  };
1431
1678
  const worktree = (checkout, branch) => {
1432
1679
  const tooling = !!checkout.pkg.scripts?.["worktree:create"];
1433
- const path = tooling ? join2(checkout.dir, ".worktrees", branch.replaceAll("/", "-")) : join2(mkdtempSync(join2(tmpdir(), "inixiative-train-")), basename(checkout.dir));
1434
- const created = tooling ? spawnSync2("bun", ["run", "worktree:create", defaultBranch(checkout.dir), branch], {
1680
+ const path = tooling ? join3(checkout.dir, ".worktrees", branch.replaceAll("/", "-")) : join3(mkdtempSync(join3(tmpdir(), "inixiative-train-")), basename(checkout.dir));
1681
+ const created = tooling ? spawnSync3("bun", ["run", "worktree:create", defaultBranch(checkout.dir), branch], {
1435
1682
  cwd: checkout.dir,
1436
1683
  stdio: "inherit"
1437
- }).status === 0 && existsSync2(path) : git(checkout.dir, "worktree", "add", "--quiet", "-b", branch, path, "HEAD").status === 0;
1684
+ }).status === 0 && existsSync3(path) : git(checkout.dir, "worktree", "add", "--quiet", "-b", branch, path, "HEAD").status === 0;
1438
1685
  if (!created) throw new Abort(`could not create ${branch} in ${checkout.name}`);
1439
1686
  worktrees.push({ repo: checkout.dir, path, tooling });
1440
1687
  const envFiles = git(
@@ -1449,9 +1696,9 @@ if (command === "train") {
1449
1696
  (file) => /(^|\/)\.env[^/]*$/.test(file) && !/(^|\/)(node_modules|\.worktrees)\//.test(file)
1450
1697
  );
1451
1698
  for (const file of envFiles) {
1452
- if (existsSync2(join2(path, file))) continue;
1453
- mkdirSync2(dirname2(join2(path, file)), { recursive: true });
1454
- writeFileSync2(join2(path, file), readFileSync2(join2(checkout.dir, file)));
1699
+ if (existsSync3(join3(path, file))) continue;
1700
+ mkdirSync3(dirname3(join3(path, file)), { recursive: true });
1701
+ writeFileSync3(join3(path, file), readFileSync3(join3(checkout.dir, file)));
1455
1702
  }
1456
1703
  return { ...checkout, dir: path };
1457
1704
  };
@@ -1475,8 +1722,8 @@ if (command === "train") {
1475
1722
  () => {
1476
1723
  const branchCheckout = worktree(checkout, branch);
1477
1724
  for (const path of [...packagePaths, "bun.lock"]) {
1478
- if (existsSync2(join2(checkout.dir, path))) {
1479
- writeFileSync2(join2(branchCheckout.dir, path), readFileSync2(join2(checkout.dir, path)));
1725
+ if (existsSync3(join3(checkout.dir, path))) {
1726
+ writeFileSync3(join3(branchCheckout.dir, path), readFileSync3(join3(checkout.dir, path)));
1480
1727
  }
1481
1728
  }
1482
1729
  restore(checkout);
@@ -1517,7 +1764,7 @@ if (command === "train") {
1517
1764
  try {
1518
1765
  bumpRanges(branchCheckout);
1519
1766
  relockAndCheck(branchCheckout, false);
1520
- if (script && spawnSync2("bun", ["run", "train:adopt"], {
1767
+ if (script && spawnSync3("bun", ["run", "train:adopt"], {
1521
1768
  cwd: branchCheckout.dir,
1522
1769
  stdio: "inherit"
1523
1770
  }).status !== 0) {
@@ -1526,7 +1773,7 @@ if (command === "train") {
1526
1773
  );
1527
1774
  }
1528
1775
  git(branchCheckout.dir, "add", "-A");
1529
- const commit = spawnSync2("git", ["commit", "-m", `chore: adopt ${label}`], {
1776
+ const commit = spawnSync3("git", ["commit", "-m", `chore: adopt ${label}`], {
1530
1777
  cwd: branchCheckout.dir,
1531
1778
  stdio: "inherit"
1532
1779
  });
@@ -1555,14 +1802,14 @@ if (command === "train") {
1555
1802
  if (dryRun && !changes) return false;
1556
1803
  return effect(`commit its package.json files + bun.lock in ${checkout.name}`, true, () => {
1557
1804
  const tracked = [...packagePaths, "bun.lock"].filter(
1558
- (path) => existsSync2(join2(checkout.dir, path))
1805
+ (path) => existsSync3(join3(checkout.dir, path))
1559
1806
  );
1560
1807
  const mutated = git(checkout.dir, "status", "--porcelain", "--", ...tracked);
1561
1808
  if (mutated.status !== 0 || mutated.stdout.trim().length === 0) return false;
1562
1809
  if (git(checkout.dir, "add", "--", ...tracked).status !== 0) {
1563
1810
  throw new Abort(`git add failed in ${checkout.name} \u2014 is bun.lock ignored?`);
1564
1811
  }
1565
- const commit = spawnSync2("git", ["commit", "-m", message], {
1812
+ const commit = spawnSync3("git", ["commit", "-m", message], {
1566
1813
  cwd: checkout.dir,
1567
1814
  stdio: "inherit"
1568
1815
  });
@@ -1580,24 +1827,24 @@ if (command === "train") {
1580
1827
  });
1581
1828
  const publishToNpm = (checkout, entry) => {
1582
1829
  if (entry.path === "package.json") {
1583
- return spawnSync2("npm", ["publish"], { cwd: checkout.dir, stdio: "inherit" }).status === 0;
1830
+ return spawnSync3("npm", ["publish"], { cwd: checkout.dir, stdio: "inherit" }).status === 0;
1584
1831
  }
1585
- const memberDir = join2(checkout.dir, dirname2(entry.path));
1586
- const out = mkdtempSync(join2(tmpdir(), "inixiative-train-"));
1832
+ const memberDir = join3(checkout.dir, dirname3(entry.path));
1833
+ const out = mkdtempSync(join3(tmpdir(), "inixiative-train-"));
1587
1834
  try {
1588
- const pack = spawnSync2("bun", ["pm", "pack", "--destination", out], {
1835
+ const pack = spawnSync3("bun", ["pm", "pack", "--destination", out], {
1589
1836
  cwd: memberDir,
1590
1837
  stdio: "inherit"
1591
1838
  });
1592
- const tarball = readdirSync2(out).find((name) => name.endsWith(".tgz"));
1839
+ const tarball = readdirSync3(out).find((name) => name.endsWith(".tgz"));
1593
1840
  if (pack.status !== 0 || !tarball) return false;
1594
- return spawnSync2("npm", ["publish", join2(out, tarball)], { cwd: memberDir, stdio: "inherit" }).status === 0;
1841
+ return spawnSync3("npm", ["publish", join3(out, tarball)], { cwd: memberDir, stdio: "inherit" }).status === 0;
1595
1842
  } finally {
1596
- rmSync2(out, { recursive: true, force: true });
1843
+ rmSync3(out, { recursive: true, force: true });
1597
1844
  }
1598
1845
  };
1599
1846
  const npmLatest = (name) => {
1600
- const remote = spawnSync2("npm", ["view", `${name}@latest`, "version"], { encoding: "utf8" });
1847
+ const remote = spawnSync3("npm", ["view", `${name}@latest`, "version"], { encoding: "utf8" });
1601
1848
  return remote.status === 0 ? remote.stdout.trim() : null;
1602
1849
  };
1603
1850
  for (const { lane: lane2, checkouts, missing, ambiguous } of plan.lanes) {
@@ -1626,7 +1873,7 @@ if (command === "train") {
1626
1873
  continue;
1627
1874
  }
1628
1875
  const releases = checkout.packages.map((entry) => {
1629
- const local = JSON.parse(readFileSync2(join2(checkout.dir, entry.path), "utf8")).version ?? "0.0.0";
1876
+ const local = JSON.parse(readFileSync3(join3(checkout.dir, entry.path), "utf8")).version ?? "0.0.0";
1630
1877
  const remote = npmLatest(entry.name);
1631
1878
  return { entry, local, remote, ahead: remote === null || compare(local, remote) > 0 };
1632
1879
  });
@@ -1816,10 +2063,10 @@ blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
1816
2063
  version = bumpPatch(remoteVersion);
1817
2064
  const bumped = version;
1818
2065
  effect(`bump @inixiative/config to ${bumped} in package.json`, void 0, () => {
1819
- const pkgPath = join2(packageRoot, "package.json");
1820
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
2066
+ const pkgPath = join3(packageRoot, "package.json");
2067
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
1821
2068
  pkg.version = bumped;
1822
- writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
2069
+ writeFileSync3(pkgPath, `${JSON.stringify(pkg, null, 2)}
1823
2070
  `);
1824
2071
  });
1825
2072
  }
@@ -1838,7 +2085,7 @@ blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
1838
2085
  }
1839
2086
  );
1840
2087
  effect("run bun run check in @inixiative/config", void 0, () => {
1841
- const check = spawnSync2("bun", ["run", "check"], { cwd: packageRoot, stdio: "inherit" });
2088
+ const check = spawnSync3("bun", ["run", "check"], { cwd: packageRoot, stdio: "inherit" });
1842
2089
  if (check.status !== 0) {
1843
2090
  console.error("\u2717 check failed in @inixiative/config \u2014 aborting before publish");
1844
2091
  process.exit(1);
@@ -1849,12 +2096,12 @@ blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
1849
2096
  recorded.length > 0 ? `candidate ${recorded.join(", ")}` : ""
1850
2097
  ].filter(Boolean).join("; ") || "release"} \u2014 ${version}`;
1851
2098
  effect(`commit "${message}" in @inixiative/config`, void 0, () => {
1852
- spawnSync2("git", ["add", "--", "package.json", "versions.json", "test/fixtures"], {
2099
+ spawnSync3("git", ["add", "--", "package.json", "versions.json", "test/fixtures"], {
1853
2100
  cwd: packageRoot
1854
2101
  });
1855
- if (spawnSync2("git", ["diff", "--cached", "--quiet"], { cwd: packageRoot }).status === 0)
2102
+ if (spawnSync3("git", ["diff", "--cached", "--quiet"], { cwd: packageRoot }).status === 0)
1856
2103
  return;
1857
- const commit = spawnSync2("git", ["commit", "-m", message], {
2104
+ const commit = spawnSync3("git", ["commit", "-m", message], {
1858
2105
  cwd: packageRoot,
1859
2106
  stdio: "inherit"
1860
2107
  });
@@ -1864,7 +2111,7 @@ blessed: ${promoted.length === 0 ? "nothing" : promoted.join(", ")}`);
1864
2111
  }
1865
2112
  });
1866
2113
  effect(`publish @inixiative/config@${version} to npm`, void 0, () => {
1867
- const publish2 = spawnSync2("npm", ["publish"], { cwd: packageRoot, stdio: "inherit" });
2114
+ const publish2 = spawnSync3("npm", ["publish"], { cwd: packageRoot, stdio: "inherit" });
1868
2115
  if (publish2.status !== 0 || !servedByRegistry("@inixiative/config", version)) {
1869
2116
  console.error("\u2717 publish failed for @inixiative/config");
1870
2117
  process.exit(1);
@@ -1898,17 +2145,17 @@ dry run complete \u2014 a real train would commit in ${committed.size} repo(s) a
1898
2145
  for (const { dir: repoDir, branch } of reviewBranches) console.log(` ${repoDir} (${branch})`);
1899
2146
  } else if (commandLine.push) {
1900
2147
  for (const repoDir of committed) {
1901
- if (spawnSync2("git", ["push"], { cwd: repoDir, stdio: "inherit" }).status !== 0) {
2148
+ if (spawnSync3("git", ["push"], { cwd: repoDir, stdio: "inherit" }).status !== 0) {
1902
2149
  console.error(`\u2717 push failed in ${repoDir}`);
1903
2150
  process.exit(1);
1904
2151
  }
1905
2152
  }
1906
2153
  for (const { dir: repoDir, branch, base } of reviewBranches) {
1907
- const push = spawnSync2("git", ["push", "-u", "origin", branch], {
2154
+ const push = spawnSync3("git", ["push", "-u", "origin", branch], {
1908
2155
  cwd: repoDir,
1909
2156
  stdio: "inherit"
1910
2157
  });
1911
- const pr = push.status === 0 && spawnSync2("gh", ["pr", "create", "--fill", "--base", base, "--head", branch], {
2158
+ const pr = push.status === 0 && spawnSync3("gh", ["pr", "create", "--fill", "--base", base, "--head", branch], {
1912
2159
  cwd: repoDir,
1913
2160
  stdio: "inherit"
1914
2161
  }).status === 0;
@@ -1927,7 +2174,7 @@ dry run complete \u2014 a real train would commit in ${committed.size} repo(s) a
1927
2174
  process.exit(failed.length > 0 ? 1 : 0);
1928
2175
  }
1929
2176
  function prUrl(cwd, branch) {
1930
- const view = spawnSync2("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
2177
+ const view = spawnSync3("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
1931
2178
  cwd,
1932
2179
  encoding: "utf8"
1933
2180
  });
@@ -1945,7 +2192,7 @@ function freeBranch(cwd, name) {
1945
2192
  function servedByRegistry(name, version) {
1946
2193
  const deadline = Date.now() + 15 * 6e4;
1947
2194
  while (true) {
1948
- const view = spawnSync2("npm", ["view", `${name}@${version}`, "version", "--prefer-online"], {
2195
+ const view = spawnSync3("npm", ["view", `${name}@${version}`, "version", "--prefer-online"], {
1949
2196
  encoding: "utf8"
1950
2197
  });
1951
2198
  if (view.status === 0 && view.stdout.trim() === version) return true;
@@ -1958,12 +2205,12 @@ function bumpPatch(version) {
1958
2205
  return `${major}.${minor}.${(patch ?? 0) + 1}`;
1959
2206
  }
1960
2207
  function fixtureDirs() {
1961
- const root = join2(packageRoot, "test", "fixtures");
1962
- if (!existsSync2(root)) return [];
1963
- return readdirSync2(root).map((name) => join2(root, name)).filter((dir2) => existsSync2(join2(dir2, "package.json")));
2208
+ const root = join3(packageRoot, "test", "fixtures");
2209
+ if (!existsSync3(root)) return [];
2210
+ return readdirSync3(root).map((name) => join3(root, name)).filter((dir2) => existsSync3(join3(dir2, "package.json")));
1964
2211
  }
1965
- if (existsSync2(join2(dir, ".git")) && !commandLine.force) {
1966
- const status = spawnSync2("git", ["status", "--porcelain"], { cwd: dir, encoding: "utf8" });
2212
+ if (existsSync3(join3(dir, ".git")) && !commandLine.force) {
2213
+ const status = spawnSync3("git", ["status", "--porcelain"], { cwd: dir, encoding: "utf8" });
1967
2214
  if (status.status === 0 && status.stdout.trim().length > 0) {
1968
2215
  console.error("\u2717 working tree dirty \u2014 commit first or pass --force");
1969
2216
  process.exit(1);
@@ -1977,14 +2224,14 @@ if (fixed > 0) console.log(`applied ${fixed} fix${fixed === 1 ? "" : "es"}`);
1977
2224
  if (!commandLine.noInstall) {
1978
2225
  const relinked = new Set(
1979
2226
  first.findings.flatMap(
1980
- (finding) => finding.kind === "member-toolchain" && finding.path ? [dirname2(finding.path)] : []
2227
+ (finding) => finding.kind === "member-toolchain" && finding.path ? [dirname3(finding.path)] : []
1981
2228
  )
1982
2229
  );
1983
2230
  for (const member of relinked) {
1984
- rmSync2(join2(dir, member, "node_modules"), { recursive: true, force: true });
2231
+ rmSync3(join3(dir, member, "node_modules"), { recursive: true, force: true });
1985
2232
  }
1986
2233
  const install = (...extra) => {
1987
- if (spawnSync2("bun", ["install", ...extra], { cwd: dir, stdio: "inherit" }).status !== 0) {
2234
+ if (spawnSync3("bun", ["install", ...extra], { cwd: dir, stdio: "inherit" }).status !== 0) {
1988
2235
  console.error("\u2717 bun install failed");
1989
2236
  process.exit(1);
1990
2237
  }
@@ -1996,7 +2243,7 @@ if (!commandLine.noInstall) {
1996
2243
  installed = inspect(dir, manifest, presetFlag);
1997
2244
  }
1998
2245
  for (const { cwd, names } of staleUpdates(dir, installed.findings)) {
1999
- if (spawnSync2("bun", ["update", ...names], { cwd, stdio: "inherit" }).status !== 0) {
2246
+ if (spawnSync3("bun", ["update", ...names], { cwd, stdio: "inherit" }).status !== 0) {
2000
2247
  console.error(`\u2717 bun update failed in ${cwd}`);
2001
2248
  process.exit(1);
2002
2249
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inixiative/config",
3
- "version": "0.5.184",
3
+ "version": "0.5.185",
4
4
  "description": "Shared toolchain for the inixiative ecosystem: tsconfig/biome/tsup presets, a version manifest (BOM), and a sync/check CLI",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/versions.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "@types/bun"
14
14
  ],
15
15
  "ecosystem": {
16
- "@inixiative/config": "0.5.184",
16
+ "@inixiative/config": "0.5.185",
17
17
  "@inixiative/json-rules": "3.16.0",
18
18
  "@inixiative/permissions": "0.18.0",
19
19
  "@inixiative/transitions": "0.6.0",
@@ -26,7 +26,7 @@
26
26
  "@inixiative/signet": "0.12.2",
27
27
  "@inixiative/kcp": "0.1.0",
28
28
  "@inixiative/archive": "0.33.0",
29
- "@inixiative/agent-session": "0.9.0",
29
+ "@inixiative/agent-session": "0.10.0",
30
30
  "@inixiative/foundry-core": "0.5.10",
31
31
  "@inixiative/foundry": "0.5.7",
32
32
  "@inixiative/foundry-acp": "0.1.2"
@@ -51,7 +51,6 @@
51
51
  }
52
52
  },
53
53
  "candidates": {
54
- "@inixiative/atlas": "0.5.0",
55
- "@inixiative/agent-session": "0.10.0"
54
+ "@inixiative/atlas": "0.5.0"
56
55
  }
57
56
  }