@inixiative/config 0.5.40 → 0.5.42
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 +2 -2
- package/dist/cli.js +122 -13
- package/package.json +3 -3
- package/versions.json +2 -2
package/README.md
CHANGED
|
@@ -68,9 +68,9 @@ bunx @inixiative/config scan [root] [--lane=primitives|agentic]
|
|
|
68
68
|
bunx @inixiative/config train [root] [--lane=primitives|agentic] [--push]
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
`check` is read-only and exits non-zero on drift — run it in CI after a frozen-lockfile install. It verifies toolchain pins, `"latest"` ranges, `packageManager`/`.bun-version`, legacy `bun.lockb`, a committed (tracked, un-ignored) `bun.lock`, 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). 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.
|
|
71
|
+
`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`, 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). 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.
|
|
72
72
|
|
|
73
|
-
`sync` applies every fix, re-locks via `bun install
|
|
73
|
+
`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.
|
|
74
74
|
|
|
75
75
|
`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 `kingdom-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, 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.
|
|
76
76
|
|
package/dist/cli.js
CHANGED
|
@@ -157,6 +157,27 @@ var lockedVersion = (lockText, key, target = key) => {
|
|
|
157
157
|
const version = spec.slice(target.length + 1);
|
|
158
158
|
return /^\d/.test(version) ? version : null;
|
|
159
159
|
};
|
|
160
|
+
var lockedCopies = (lockText, name) => {
|
|
161
|
+
const packages = tryParseJsonc(lockText)?.packages;
|
|
162
|
+
if (!packages || typeof packages !== "object") return [];
|
|
163
|
+
return Object.entries(packages).flatMap(([key, entry]) => {
|
|
164
|
+
const spec = Array.isArray(entry) ? entry[0] : null;
|
|
165
|
+
if (typeof spec !== "string" || !spec.startsWith(`${name}@`)) return [];
|
|
166
|
+
const version = spec.slice(name.length + 1);
|
|
167
|
+
return /^\d/.test(version) ? [{ key, version }] : [];
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
var peerFloor = (pin) => {
|
|
171
|
+
const [major, minor] = pin.split(".");
|
|
172
|
+
return `>=${major}.${minor}.0`;
|
|
173
|
+
};
|
|
174
|
+
var admitsOlderMajor = (range, pin) => {
|
|
175
|
+
const pinMajor = Number(pin.split(".")[0]);
|
|
176
|
+
return range.split("||").some((part) => {
|
|
177
|
+
const parsed = parseRange(part.trim());
|
|
178
|
+
return !!parsed && Number(parsed.version.split(".")[0]) < pinMajor;
|
|
179
|
+
});
|
|
180
|
+
};
|
|
160
181
|
var npmAlias = (spec) => {
|
|
161
182
|
const match = spec.match(/^npm:((?:@[^/@]+\/)?[^@]+)@(.+)$/);
|
|
162
183
|
return match ? { name: match[1], range: match[2] } : null;
|
|
@@ -337,6 +358,15 @@ var workspacePackagePaths = (dir2, pkg) => {
|
|
|
337
358
|
}
|
|
338
359
|
return [...paths].sort();
|
|
339
360
|
};
|
|
361
|
+
var staleUpdates = (dir2, findings) => {
|
|
362
|
+
const groups = /* @__PURE__ */ new Map();
|
|
363
|
+
for (const finding of findings) {
|
|
364
|
+
if (finding.kind !== "stale-lock" || !finding.name) continue;
|
|
365
|
+
const cwd = join(dir2, dirname(finding.path ?? "package.json"));
|
|
366
|
+
groups.set(cwd, (groups.get(cwd) ?? /* @__PURE__ */ new Set()).add(finding.name));
|
|
367
|
+
}
|
|
368
|
+
return [...groups].map(([cwd, names]) => ({ cwd, names: [...names] }));
|
|
369
|
+
};
|
|
340
370
|
var detectPreset = (pkg, override) => {
|
|
341
371
|
if (override) return override;
|
|
342
372
|
const hasReact = DEP_FIELDS.some((field) => pkg[field]?.react !== void 0);
|
|
@@ -557,15 +587,77 @@ function inspect(dir2, manifest2, presetOverride) {
|
|
|
557
587
|
}
|
|
558
588
|
}
|
|
559
589
|
const rangeTargets = [
|
|
560
|
-
{ label: "", pkg, mark: touch },
|
|
590
|
+
{ label: "", path: "package.json", pkg, mark: touch },
|
|
561
591
|
...members.map((member) => ({
|
|
562
592
|
label: `${member.rel}: `,
|
|
593
|
+
path: member.rel,
|
|
563
594
|
pkg: member.pkg,
|
|
564
595
|
mark: () => {
|
|
565
596
|
member.dirty = true;
|
|
566
597
|
}
|
|
567
598
|
}))
|
|
568
599
|
];
|
|
600
|
+
for (const [index, member] of members.entries()) {
|
|
601
|
+
const target = rangeTargets[index + 1];
|
|
602
|
+
for (const name of Object.keys(manifest2.toolchain)) {
|
|
603
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
604
|
+
const deps = member.pkg[field];
|
|
605
|
+
if (!deps?.[name]) continue;
|
|
606
|
+
findings.push({
|
|
607
|
+
level: "error",
|
|
608
|
+
kind: "member-toolchain",
|
|
609
|
+
name,
|
|
610
|
+
path: member.rel,
|
|
611
|
+
message: `${member.rel}: ${name} declared in a workspace member \u2192 remove; the root pin applies`,
|
|
612
|
+
fix: () => {
|
|
613
|
+
delete deps[name];
|
|
614
|
+
target.mark();
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
for (const target of rangeTargets) {
|
|
621
|
+
const peers = target.pkg.peerDependencies;
|
|
622
|
+
for (const [name, pin] of Object.entries(manifest2.toolchain)) {
|
|
623
|
+
const range = peers?.[name];
|
|
624
|
+
if (!range || !peers) continue;
|
|
625
|
+
const floor = peerFloor(pin);
|
|
626
|
+
const ok = admits(range, pin);
|
|
627
|
+
if (ok === null) {
|
|
628
|
+
findings.push({
|
|
629
|
+
level: "warn",
|
|
630
|
+
message: `${target.label}${name} peer range ${range} not understood; verify it admits pinned ${pin}`
|
|
631
|
+
});
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const reason = !ok ? `does not admit pinned ${pin}` : admitsOlderMajor(range, pin) ? `admits majors below pinned ${pin}` : null;
|
|
635
|
+
if (!reason) continue;
|
|
636
|
+
findings.push({
|
|
637
|
+
level: "error",
|
|
638
|
+
message: `${target.label}${name} peerDependencies range ${range} ${reason} \u2192 ${floor}`,
|
|
639
|
+
fix: () => {
|
|
640
|
+
peers[name] = floor;
|
|
641
|
+
target.mark();
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (lockText) {
|
|
647
|
+
for (const [name, pin] of Object.entries(manifest2.toolchain)) {
|
|
648
|
+
const seen = /* @__PURE__ */ new Set();
|
|
649
|
+
for (const { key, version } of lockedCopies(lockText, name)) {
|
|
650
|
+
if (version === pin || seen.has(version)) continue;
|
|
651
|
+
seen.add(version);
|
|
652
|
+
findings.push({
|
|
653
|
+
level: "error",
|
|
654
|
+
kind: "toolchain-lock",
|
|
655
|
+
name,
|
|
656
|
+
message: `bun.lock resolves ${name}@${version} (${key}) beside the pinned ${pin} \u2192 re-lock`
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
569
661
|
const blessedSet = blessedVersions(manifest2);
|
|
570
662
|
const staleKeys = /* @__PURE__ */ new Set();
|
|
571
663
|
for (const target of rangeTargets) {
|
|
@@ -606,6 +698,7 @@ function inspect(dir2, manifest2, presetOverride) {
|
|
|
606
698
|
level: "error",
|
|
607
699
|
kind: "stale-lock",
|
|
608
700
|
name: key,
|
|
701
|
+
path: target.path,
|
|
609
702
|
message: `${target.label}${key} locked at ${locked}, violating declared ${spec} (stale lockfile) \u2192 re-lock`
|
|
610
703
|
});
|
|
611
704
|
} else if (locked && locked !== blessed) {
|
|
@@ -614,6 +707,7 @@ function inspect(dir2, manifest2, presetOverride) {
|
|
|
614
707
|
level: "error",
|
|
615
708
|
kind: "stale-lock",
|
|
616
709
|
name: key,
|
|
710
|
+
path: target.path,
|
|
617
711
|
message: `${target.label}${key} locked at ${locked}, blessed is ${blessed} \u2192 re-lock`
|
|
618
712
|
});
|
|
619
713
|
}
|
|
@@ -884,9 +978,11 @@ if (command === "train") {
|
|
|
884
978
|
if (spawnSync2("bun", ["install"], { cwd: checkout.dir, stdio: "inherit" }).status !== 0) {
|
|
885
979
|
throw new Abort(`bun install failed in ${checkout.name}`);
|
|
886
980
|
}
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
981
|
+
for (const { cwd, names } of staleUpdates(
|
|
982
|
+
checkout.dir,
|
|
983
|
+
inspect(checkout.dir, manifest).findings
|
|
984
|
+
)) {
|
|
985
|
+
spawnSync2("bun", ["update", ...names], { cwd, stdio: "inherit" });
|
|
890
986
|
}
|
|
891
987
|
if (spawnSync2("bun", ["run", "check"], { cwd: checkout.dir, stdio: "inherit" }).status !== 0) {
|
|
892
988
|
throw new Abort(`check failed in ${checkout.name}`);
|
|
@@ -1179,16 +1275,29 @@ first.flush();
|
|
|
1179
1275
|
var fixed = first.findings.filter((finding) => finding.fix).length;
|
|
1180
1276
|
if (fixed > 0) console.log(`applied ${fixed} fix${fixed === 1 ? "" : "es"}`);
|
|
1181
1277
|
if (!flags.has("--no-install")) {
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1278
|
+
const relinked = new Set(
|
|
1279
|
+
first.findings.flatMap(
|
|
1280
|
+
(finding) => finding.kind === "member-toolchain" && finding.path ? [dirname2(finding.path)] : []
|
|
1281
|
+
)
|
|
1282
|
+
);
|
|
1283
|
+
for (const member of relinked) {
|
|
1284
|
+
rmSync2(join2(dir, member, "node_modules"), { recursive: true, force: true });
|
|
1285
|
+
}
|
|
1286
|
+
const install = (...extra) => {
|
|
1287
|
+
if (spawnSync2("bun", ["install", ...extra], { cwd: dir, stdio: "inherit" }).status !== 0) {
|
|
1288
|
+
console.error("\u2717 bun install failed");
|
|
1289
|
+
process.exit(1);
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
install();
|
|
1293
|
+
let installed = inspect(dir, manifest, presetFlag);
|
|
1294
|
+
if (installed.findings.some((finding) => finding.kind === "toolchain-lock")) {
|
|
1295
|
+
install("--force");
|
|
1296
|
+
installed = inspect(dir, manifest, presetFlag);
|
|
1186
1297
|
}
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if (update.status !== 0) {
|
|
1191
|
-
console.error("\u2717 bun update failed");
|
|
1298
|
+
for (const { cwd, names } of staleUpdates(dir, installed.findings)) {
|
|
1299
|
+
if (spawnSync2("bun", ["update", ...names], { cwd, stdio: "inherit" }).status !== 0) {
|
|
1300
|
+
console.error(`\u2717 bun update failed in ${cwd}`);
|
|
1192
1301
|
process.exit(1);
|
|
1193
1302
|
}
|
|
1194
1303
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inixiative/config",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.42",
|
|
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",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@biomejs/biome": ">=2.5.0",
|
|
59
|
-
"tsup": ">=8.
|
|
60
|
-
"typescript": ">=
|
|
59
|
+
"tsup": ">=8.5.0",
|
|
60
|
+
"typescript": ">=6.0.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"@biomejs/biome": {
|
package/versions.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@types/bun"
|
|
14
14
|
],
|
|
15
15
|
"ecosystem": {
|
|
16
|
-
"@inixiative/config": "0.5.
|
|
16
|
+
"@inixiative/config": "0.5.42",
|
|
17
17
|
"@inixiative/json-rules": "2.25.0",
|
|
18
18
|
"@inixiative/permissions": "0.3.2",
|
|
19
19
|
"@inixiative/transitions": "0.1.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@inixiative/archive": "0.5.1"
|
|
25
25
|
},
|
|
26
26
|
"agentic": {
|
|
27
|
-
"@inixiative/agent-session": "0.
|
|
27
|
+
"@inixiative/agent-session": "0.3.1",
|
|
28
28
|
"@inixiative/foundry-core": "0.2.0",
|
|
29
29
|
"@inixiative/foundry": "0.2.0"
|
|
30
30
|
},
|