@inixiative/config 0.4.1 → 0.5.0

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
@@ -60,9 +60,9 @@ bunx @inixiative/config train [root]
60
60
 
61
61
  `sync` applies every fix, re-locks via `bun install`, and runs `bun update` on stale ecosystem entries. 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.
62
62
 
63
- `scan` runs `check` across every ecosystem checkout under a root directory. Targets are derived from the BOM's `ecosystem` keys and matched by package name, so directory names and local layout never need declaring.
63
+ `scan` runs `check` across every ecosystem checkout under a root directory. Targets are derived from the BOM's `ecosystem` keys and matched by package name, so directory names and local layout never need declaring. 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, and surfaces unmerged `claude/*` session branches.
64
64
 
65
- `train` is the release cascade, run from this repo's checkout. It walks the ecosystem in dependency order; per repo it: skips if dirty or behind origin → bumps ecosystem ranges to the BOM → re-locks → runs the repo's `check` → commits only its own changes (`package.json` + `bun.lock`) → publishes via `npm publish` when the local version is ahead of npm (OTP prompts pass through) → records the published version in the BOM. It never pushes; review its commits, push, then commit and publish this package so the BOM names the new state.
65
+ `train` is the release cascade, run from this repo's checkout. Origin is the truth: it refuses to run from a config checkout behind origin or with any BOM repo missing a checkout, and it fast-forwards clean checkouts to origin before working (dirty or diverged repos still skip). It walks the ecosystem in dependency order; per repo it: bumps ecosystem ranges to the BOM → re-locks → runs the repo's `check` → commits only its own changes (`package.json` + `bun.lock`) → publishes via `npm publish` when the local version is ahead of npm (OTP prompts pass through) → records the published version in the BOM. It never pushes; review its commits, push, then commit and publish this package so the BOM names the new state.
66
66
 
67
67
  Division of labor: this CLI owns the toolchain set, ecosystem coherence, stubs, and lockfile format. Renovate owns everything else plus bumping `@inixiative/config` itself, and must be fenced off the toolchain packages.
68
68
 
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { spawnSync as spawnSync2 } from "child_process";
5
- import { existsSync as existsSync2 } from "fs";
5
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
6
6
  import { join as join2, resolve } from "path";
7
7
 
8
8
  // src/lib.ts
@@ -160,6 +160,12 @@ var discoverRepos = (root, manifest2) => {
160
160
  }
161
161
  return repos.sort((a, b) => a.name.localeCompare(b.name));
162
162
  };
163
+ var missingRepos = (repos, manifest2) => {
164
+ const found = new Set(repos.map((repo) => repo.name));
165
+ return Object.keys(manifest2.ecosystem).filter(
166
+ (name) => name !== "@inixiative/config" && !found.has(name)
167
+ );
168
+ };
163
169
  var detectPreset = (pkg, override) => {
164
170
  if (override) return override;
165
171
  const hasReact = DEP_FIELDS.some((field) => pkg[field]?.react !== void 0);
@@ -510,6 +516,34 @@ var report = (findings) => {
510
516
  if (process.versions.bun && process.versions.bun !== manifest.bun) {
511
517
  console.log(`\u26A0 running bun ${process.versions.bun}, blessed is ${manifest.bun} (bun upgrade)`);
512
518
  }
519
+ var git = (cwd, ...gitArgs) => spawnSync2("git", gitArgs, { cwd, encoding: "utf8" });
520
+ var behindOrigin = (cwd) => {
521
+ const behind = git(cwd, "rev-list", "--count", "HEAD..@{upstream}");
522
+ return behind.status === 0 ? Number(behind.stdout.trim()) : 0;
523
+ };
524
+ var staleCheckoutFindings = (cwd) => {
525
+ git(cwd, "fetch", "--quiet");
526
+ const behind = behindOrigin(cwd);
527
+ return behind > 0 ? [
528
+ {
529
+ level: "error",
530
+ message: `checkout is ${behind} commit${behind === 1 ? "" : "s"} behind origin \u2014 local state is stale, pull before trusting findings`
531
+ }
532
+ ] : [];
533
+ };
534
+ var unmergedSessionBranches = (cwd) => {
535
+ const branches = git(
536
+ cwd,
537
+ "branch",
538
+ "-r",
539
+ "--no-merged",
540
+ "@{upstream}",
541
+ "--list",
542
+ "origin/claude/*"
543
+ );
544
+ if (branches.status !== 0) return [];
545
+ return branches.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
546
+ };
513
547
  if (command === "check") {
514
548
  const { findings } = inspect(dir, manifest, presetFlag);
515
549
  process.exit(report(findings) > 0 ? 1 : 0);
@@ -520,17 +554,24 @@ if (command === "scan") {
520
554
  console.error(`\u2717 no ecosystem repos found under ${dir}`);
521
555
  process.exit(1);
522
556
  }
557
+ const missing = missingRepos(repos, manifest);
523
558
  const drifted = [];
524
559
  for (const repo of repos) {
525
560
  console.log(`
526
561
  ${repo.name} \u2014 ${repo.dir}`);
527
- if (report(inspect(repo.dir, manifest).findings) > 0) drifted.push(repo.name);
562
+ const findings = [...staleCheckoutFindings(repo.dir), ...inspect(repo.dir, manifest).findings];
563
+ if (report(findings) > 0) drifted.push(repo.name);
564
+ for (const branch of unmergedSessionBranches(repo.dir)) {
565
+ console.log(`\u26A0 unmerged session branch: ${branch}`);
566
+ }
528
567
  }
568
+ if (missing.length > 0) console.error(`
569
+ \u2717 no checkout found for: ${missing.join(", ")}`);
529
570
  console.log(
530
571
  `
531
- scanned ${repos.length} repos: ${drifted.length === 0 ? "all in sync" : `${drifted.length} drifted (${drifted.join(", ")})`}`
572
+ scanned ${repos.length} repos: ${drifted.length === 0 ? "all in sync" : `${drifted.length} drifted (${drifted.join(", ")})`}${missing.length > 0 ? `, ${missing.length} missing` : ""}`
532
573
  );
533
- process.exit(drifted.length > 0 ? 1 : 0);
574
+ process.exit(drifted.length > 0 || missing.length > 0 ? 1 : 0);
534
575
  }
535
576
  if (command === "train") {
536
577
  const order = topoOrder(dir, manifest);
@@ -538,6 +579,18 @@ if (command === "train") {
538
579
  console.error(`\u2717 no ecosystem repos found under ${dir}`);
539
580
  process.exit(1);
540
581
  }
582
+ const missing = missingRepos(order, manifest);
583
+ if (missing.length > 0) {
584
+ console.error(
585
+ `\u2717 no checkout under ${dir} for: ${missing.join(", ")} \u2014 a partial train ships an incoherent set`
586
+ );
587
+ process.exit(1);
588
+ }
589
+ git(packageRoot, "fetch", "--quiet");
590
+ if (behindOrigin(packageRoot) > 0) {
591
+ console.error("\u2717 this config checkout is behind origin \u2014 its blessed set is stale; pull first");
592
+ process.exit(1);
593
+ }
541
594
  console.log(`train order: ${order.map((repo) => repo.name).join(" \u2192 ")}`);
542
595
  const published = [];
543
596
  const skipped = [];
@@ -559,15 +612,26 @@ if (command === "train") {
559
612
  skipped.push(repo.name);
560
613
  continue;
561
614
  }
562
- spawnSync2("git", ["fetch", "--quiet"], { cwd: repo.dir });
563
- const behind = spawnSync2("git", ["rev-list", "--count", "HEAD..@{upstream}"], {
564
- cwd: repo.dir,
565
- encoding: "utf8"
566
- });
567
- if (behind.status === 0 && Number(behind.stdout.trim()) > 0) {
568
- console.log("\u26A0 behind origin \u2014 pull first, skipping");
569
- skipped.push(repo.name);
570
- continue;
615
+ git(repo.dir, "fetch", "--quiet");
616
+ for (const branch of unmergedSessionBranches(repo.dir)) {
617
+ console.log(`\u26A0 unmerged session branch: ${branch} \u2014 a bump may be hiding there`);
618
+ }
619
+ const behind = behindOrigin(repo.dir);
620
+ if (behind > 0) {
621
+ const localOnly = git(repo.dir, "rev-list", "--count", "@{upstream}..HEAD");
622
+ if (localOnly.status === 0 && Number(localOnly.stdout.trim()) > 0) {
623
+ console.log("\u26A0 diverged from origin \u2014 reconcile first, skipping");
624
+ skipped.push(repo.name);
625
+ continue;
626
+ }
627
+ const ff = git(repo.dir, "merge", "--ff-only", "@{upstream}");
628
+ if (ff.status !== 0) {
629
+ console.log("\u26A0 fast-forward to origin failed \u2014 reconcile first, skipping");
630
+ skipped.push(repo.name);
631
+ continue;
632
+ }
633
+ console.log(`\u2193 fast-forwarded to origin (+${behind})`);
634
+ repo.pkg = JSON.parse(readFileSync2(join2(repo.dir, "package.json"), "utf8"));
571
635
  }
572
636
  const inspection = inspect(repo.dir, manifest);
573
637
  const bumps = inspection.findings.filter((finding) => finding.kind === "ecosystem-range");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inixiative/config",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
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,11 +13,11 @@
13
13
  "@types/bun"
14
14
  ],
15
15
  "ecosystem": {
16
- "@inixiative/config": "0.4.1",
16
+ "@inixiative/config": "0.5.0",
17
17
  "@inixiative/json-rules": "2.14.1",
18
18
  "@inixiative/permissions": "0.3.2",
19
19
  "@inixiative/transitions": "0.1.0",
20
- "@inixiative/rules-builder": "0.14.1",
20
+ "@inixiative/rules-builder": "0.15.0",
21
21
  "@inixiative/prisma-map": "0.1.0",
22
22
  "@inixiative/atlas": "0.1.1"
23
23
  }