@wairon/cli 5.1.1-dev.5 → 5.1.1-dev.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.
package/dist/cli/index.js CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.1.1-dev.5";
68
+ WAIRON_VERSION = "5.1.1-dev.7";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  ARCHITECT_AGENT_ID = "agent-architect";
71
71
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -3976,9 +3976,9 @@ var init_surfaces = __esm({
3976
3976
  // src/core/rules/namespace.ts
3977
3977
  function isExternalNamespaceRef(ctx, ref) {
3978
3978
  if (ref.startsWith("::") || ref.startsWith("super::")) return true;
3979
- const sep6 = ref.indexOf("::");
3980
- if (sep6 === -1) return false;
3981
- return !ctx.subsystemIds.has(ref.slice(0, sep6));
3979
+ const sep9 = ref.indexOf("::");
3980
+ if (sep9 === -1) return false;
3981
+ return !ctx.subsystemIds.has(ref.slice(0, sep9));
3982
3982
  }
3983
3983
  function resolveSurfaceRef(ctx, ref) {
3984
3984
  const local = ref.split("::").filter((seg) => seg && seg !== "super").pop();
@@ -4315,10 +4315,10 @@ function stepGraph(steps) {
4315
4315
  const s = byNum.get(n);
4316
4316
  if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
4317
4317
  const entries = s.branches.map((b) => b.step).sort((a, b) => a - b);
4318
- const join49 = fallNext(s.endStep);
4318
+ const join50 = fallNext(s.endStep);
4319
4319
  for (let i = 0; i < entries.length; i++) {
4320
4320
  const armEnd = i + 1 < entries.length ? prevOf(entries[i + 1]) : s.endStep;
4321
- if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join49);
4321
+ if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join50);
4322
4322
  }
4323
4323
  }
4324
4324
  const successorsOf = (n) => {
@@ -4392,7 +4392,8 @@ var init_narrative_flow = __esm({
4392
4392
  { code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" },
4393
4393
  { code: "JUMP_INTO_REGION", defaultSeverity: "warning", summary: "Jump lands in the middle of a loop/try body from outside \u2014 regions are entered through their header" },
4394
4394
  { code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" },
4395
- { code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" }
4395
+ { code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" },
4396
+ { code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
4396
4397
  ],
4397
4398
  check(ctx) {
4398
4399
  for (const impl of ctx.implementations) {
@@ -4401,6 +4402,23 @@ var init_narrative_flow = __esm({
4401
4402
  const steps = implMethod.narrative;
4402
4403
  if (!steps.length) continue;
4403
4404
  const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
4405
+ const labelled = /* @__PURE__ */ new Map();
4406
+ for (const step of steps) {
4407
+ const label = step.label;
4408
+ if (!label) continue;
4409
+ const first = labelled.get(label);
4410
+ if (first !== void 0) {
4411
+ ctx.addIssue(
4412
+ "error",
4413
+ "DUPLICATE_STEP_LABEL",
4414
+ `${where}steps ${first} and ${step.stepNumber} share the label "${label}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`,
4415
+ impl.id,
4416
+ isDraftCtx
4417
+ );
4418
+ } else {
4419
+ labelled.set(label, step.stepNumber);
4420
+ }
4421
+ }
4404
4422
  let sound = true;
4405
4423
  const malformed = (msg) => {
4406
4424
  sound = false;
@@ -7626,11 +7644,11 @@ var init_narrative_antipatterns = __esm({
7626
7644
  const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
7627
7645
  if (memberEdges.length === 0) continue;
7628
7646
  const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
7629
- const path64 = [...keys].sort().join(" \u2192 ");
7647
+ const path67 = [...keys].sort().join(" \u2192 ");
7630
7648
  ctx.addIssue(
7631
7649
  "warning",
7632
7650
  "UNCONDITIONAL_CALL_CYCLE",
7633
- `Call cycle with no guard: ${path64} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
7651
+ `Call cycle with no guard: ${path67} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
7634
7652
  anchor.impl.id,
7635
7653
  memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
7636
7654
  );
@@ -14403,6 +14421,137 @@ var init_repository = __esm({
14403
14421
  }
14404
14422
  });
14405
14423
 
14424
+ // src/core/baseline.ts
14425
+ function diffSize(d) {
14426
+ return d.added.length + d.changed.length + d.removed.length;
14427
+ }
14428
+ function baselineDir() {
14429
+ return process.env["WAIRON_BASELINE_DIR"] || path17.join(os5.homedir(), ".wairon", "baselines");
14430
+ }
14431
+ function keyFor(root) {
14432
+ const normalized = path17.resolve(root).replace(/\\/g, "/").toLowerCase();
14433
+ return crypto3.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
14434
+ }
14435
+ function baselinePath(root) {
14436
+ return path17.join(baselineDir(), `${keyFor(root)}.json`);
14437
+ }
14438
+ function readBaseline(root = getProjectRoot()) {
14439
+ const p = baselinePath(root);
14440
+ if (!pathExists(p)) return null;
14441
+ try {
14442
+ return JSON.parse(fs13.readFileSync(p, "utf8"));
14443
+ } catch {
14444
+ return null;
14445
+ }
14446
+ }
14447
+ function writeBaseline(record2) {
14448
+ const p = baselinePath(record2.projectRoot);
14449
+ ensureDir(path17.dirname(p));
14450
+ fs13.writeFileSync(p, `${JSON.stringify(record2, null, 2)}
14451
+ `);
14452
+ return p;
14453
+ }
14454
+ function currentSpecs(root) {
14455
+ const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path17.resolve(root, s.projectPath).split(path17.sep).join("/")}/`);
14456
+ const out = {};
14457
+ for (const [abs, content] of snapshotSpecFiles()) {
14458
+ const normalized = path17.resolve(abs).split(path17.sep).join("/");
14459
+ if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
14460
+ out[path17.relative(root, abs).split(path17.sep).join("/")] = content;
14461
+ }
14462
+ return out;
14463
+ }
14464
+ function captureBaseline(approvedBy, children = {}, root = getProjectRoot(), scope) {
14465
+ const system = loadSystemSpec();
14466
+ const current2 = currentSpecs(root);
14467
+ let specs = current2;
14468
+ if (scope) {
14469
+ const previous = readBaseline(root)?.specs ?? {};
14470
+ specs = { ...previous };
14471
+ for (const rel2 of Object.keys(previous)) {
14472
+ if (scope.paths.has(rel2) && current2[rel2] === void 0) delete specs[rel2];
14473
+ }
14474
+ for (const rel2 of scope.paths) {
14475
+ if (current2[rel2] !== void 0) specs[rel2] = current2[rel2];
14476
+ }
14477
+ }
14478
+ return {
14479
+ schemaVersion: SCHEMA_VERSION,
14480
+ projectRoot: path17.resolve(root),
14481
+ systemName: system?.name ?? path17.basename(root),
14482
+ approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
14483
+ approvedBy,
14484
+ stateId: computeGateStateId(),
14485
+ specs,
14486
+ children
14487
+ };
14488
+ }
14489
+ function diffAgainstBaseline(root = getProjectRoot()) {
14490
+ const baseline = readBaseline(root);
14491
+ if (!baseline) return null;
14492
+ const current2 = currentSpecs(root);
14493
+ const added = [];
14494
+ const changed = [];
14495
+ const removed = [];
14496
+ const unchangedPaths = [];
14497
+ for (const [rel2, content] of Object.entries(current2)) {
14498
+ const before = baseline.specs[rel2];
14499
+ if (before === void 0) added.push(rel2);
14500
+ else if (before !== content) changed.push(rel2);
14501
+ else unchangedPaths.push(rel2);
14502
+ }
14503
+ for (const rel2 of Object.keys(baseline.specs)) {
14504
+ if (current2[rel2] === void 0) removed.push(rel2);
14505
+ }
14506
+ return {
14507
+ added: added.sort(),
14508
+ changed: changed.sort(),
14509
+ removed: removed.sort(),
14510
+ unchangedPaths: unchangedPaths.sort()
14511
+ };
14512
+ }
14513
+ function settledSpecPaths(root = getProjectRoot()) {
14514
+ const diff = diffAgainstBaseline(root);
14515
+ return diff ? new Set(diff.unchangedPaths) : null;
14516
+ }
14517
+ function pinOf(stateId) {
14518
+ return `${stateId.algorithm}:${stateId.digest}`;
14519
+ }
14520
+ function currentChildPins(mounts, root = getProjectRoot()) {
14521
+ const pins = {};
14522
+ for (const mount of mounts) {
14523
+ if (!mount.projectPath || mount.id.includes("::")) continue;
14524
+ const childRoot = path17.resolve(root, mount.projectPath);
14525
+ const child = readBaseline(childRoot);
14526
+ if (child) pins[mount.id] = pinOf(child.stateId);
14527
+ }
14528
+ return pins;
14529
+ }
14530
+ function movedChildren(mounts, root = getProjectRoot()) {
14531
+ const baseline = readBaseline(root);
14532
+ if (!baseline) return [];
14533
+ const now = currentChildPins(mounts, root);
14534
+ const moved = [];
14535
+ for (const [id, pinned] of Object.entries(baseline.children)) {
14536
+ const current2 = now[id] ?? null;
14537
+ if (current2 !== pinned) moved.push({ id, pinned, now: current2 });
14538
+ }
14539
+ return moved;
14540
+ }
14541
+ var fs13, os5, path17, crypto3, SCHEMA_VERSION;
14542
+ var init_baseline = __esm({
14543
+ "src/core/baseline.ts"() {
14544
+ "use strict";
14545
+ fs13 = __toESM(require("fs"));
14546
+ os5 = __toESM(require("os"));
14547
+ path17 = __toESM(require("path"));
14548
+ crypto3 = __toESM(require("crypto"));
14549
+ init_fs();
14550
+ init_specs2();
14551
+ SCHEMA_VERSION = "1.0.0";
14552
+ }
14553
+ });
14554
+
14406
14555
  // src/core/validation.ts
14407
14556
  var validation_exports = {};
14408
14557
  __export(validation_exports, {
@@ -14533,7 +14682,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
14533
14682
  const types = loadTypeSpecs();
14534
14683
  const surfaceSnapshots = loadSurfaceSnapshots();
14535
14684
  const codeModel = buildCodeModel(implementations, getProjectRoot());
14536
- const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : [];
14685
+ const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
14537
14686
  const statusSnapshot = statusBearing.map((s) => s.status);
14538
14687
  for (const s of statusBearing) s.status = "complete";
14539
14688
  try {
@@ -14655,10 +14804,34 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
14655
14804
  });
14656
14805
  }
14657
14806
  }
14807
+ function settledStatusBearing(loaded) {
14808
+ let settled;
14809
+ try {
14810
+ settled = settledSpecPaths();
14811
+ } catch {
14812
+ return [];
14813
+ }
14814
+ if (!settled || settled.size === 0) return [];
14815
+ const root = getProjectRoot();
14816
+ const index = scanAllSpecs();
14817
+ const rel2 = (abs) => path18.relative(root, abs).split(path18.sep).join("/");
14818
+ const out = [];
14819
+ const take = (specs, paths) => {
14820
+ for (const spec of specs) {
14821
+ const p = paths[spec.id];
14822
+ if (p && settled.has(rel2(p))) out.push(spec);
14823
+ }
14824
+ };
14825
+ take(loaded.subsystems, index.paths.subsystem);
14826
+ take(loaded.components, index.paths.component);
14827
+ take(loaded.interfaces, index.paths.interface);
14828
+ take(loaded.implementations, index.paths.implementation);
14829
+ return out;
14830
+ }
14658
14831
  function validateAsComplete(options) {
14659
14832
  return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
14660
14833
  }
14661
- var SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
14834
+ var path18, SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
14662
14835
  var init_validation = __esm({
14663
14836
  "src/core/validation.ts"() {
14664
14837
  "use strict";
@@ -14672,6 +14845,8 @@ var init_validation = __esm({
14672
14845
  init_source_analysis();
14673
14846
  init_specs2();
14674
14847
  init_fs();
14848
+ path18 = __toESM(require("path"));
14849
+ init_baseline();
14675
14850
  SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
14676
14851
  "UNDEFINED_TYPE_REFERENCE",
14677
14852
  "INVALID_DEPENDENCY_REFERENCE",
@@ -15001,8 +15176,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
15001
15176
  const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
15002
15177
  for (const step of steps) {
15003
15178
  for (const par of parallelArms) {
15004
- const sep6 = par.sepByStep.get(step.stepNumber);
15005
- if (sep6 !== void 0) lines.push(` and ${escapeLabel(sep6)}`);
15179
+ const sep9 = par.sepByStep.get(step.stepNumber);
15180
+ if (sep9 !== void 0) lines.push(` and ${escapeLabel(sep9)}`);
15006
15181
  }
15007
15182
  switch (step.type) {
15008
15183
  case "local":
@@ -15282,6 +15457,7 @@ __export(specs_exports, {
15282
15457
  saveTypeSpec: () => saveTypeSpec,
15283
15458
  scanAllSpecs: () => scanAllSpecs,
15284
15459
  snapshotSpecFiles: () => snapshotSpecFiles,
15460
+ specPathsInScope: () => specPathsInScope,
15285
15461
  splitNamespace: () => splitNamespace,
15286
15462
  updateSpec: () => updateSpec,
15287
15463
  workspaceFor: () => workspaceFor
@@ -15355,16 +15531,16 @@ function relativizeId(id, prefix) {
15355
15531
  return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
15356
15532
  }
15357
15533
  function isWithin(dir, file) {
15358
- const d = path17.resolve(dir);
15359
- const f = path17.resolve(file);
15360
- return f === d || f.startsWith(d + path17.sep);
15534
+ const d = path19.resolve(dir);
15535
+ const f = path19.resolve(file);
15536
+ return f === d || f.startsWith(d + path19.sep);
15361
15537
  }
15362
15538
  function projectPathEscapesRoot(projectRoot2, projectPath, resolvedChildDir) {
15363
- return path17.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir);
15539
+ return path19.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir);
15364
15540
  }
15365
15541
  function assertContainedProjectPath(projectRoot2, projectPath) {
15366
- const root = path17.resolve(projectRoot2);
15367
- const resolved = path17.resolve(root, projectPath);
15542
+ const root = path19.resolve(projectRoot2);
15543
+ const resolved = path19.resolve(root, projectPath);
15368
15544
  if (projectPathEscapesRoot(root, projectPath, resolved)) {
15369
15545
  throw new Error(
15370
15546
  `projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute paths and ../-escaping paths are rejected so a chained subproject is always contained by its parent.`
@@ -15375,11 +15551,11 @@ function assertContainedProjectPath(projectRoot2, projectPath) {
15375
15551
  function findChainingParent(childRoot) {
15376
15552
  let childResolved;
15377
15553
  try {
15378
- childResolved = path17.resolve(childRoot);
15554
+ childResolved = path19.resolve(childRoot);
15379
15555
  } catch {
15380
15556
  return null;
15381
15557
  }
15382
- let dir = path17.dirname(childResolved);
15558
+ let dir = path19.dirname(childResolved);
15383
15559
  for (let hops = 0; hops < 32; hops++) {
15384
15560
  const specsDir = aiPathsAt(dir).specsDir();
15385
15561
  if (pathExists(specsDir)) {
@@ -15394,7 +15570,7 @@ function findChainingParent(childRoot) {
15394
15570
  const projectPath = raw.projectPath;
15395
15571
  if (typeof projectPath === "string" && projectPath.trim() !== "") {
15396
15572
  try {
15397
- if (path17.resolve(dir, projectPath) === childResolved) {
15573
+ if (path19.resolve(dir, projectPath) === childResolved) {
15398
15574
  const id = raw.id;
15399
15575
  return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
15400
15576
  }
@@ -15404,7 +15580,7 @@ function findChainingParent(childRoot) {
15404
15580
  }
15405
15581
  }
15406
15582
  }
15407
- const up = path17.dirname(dir);
15583
+ const up = path19.dirname(dir);
15408
15584
  if (up === dir) break;
15409
15585
  dir = up;
15410
15586
  }
@@ -15509,18 +15685,18 @@ function findOwner(id, components) {
15509
15685
  }) ?? null;
15510
15686
  }
15511
15687
  function moveComponentFolder(fromDir, toDir) {
15512
- if (path17.normalize(fromDir) === path17.normalize(toDir)) return false;
15513
- if (!fs13.existsSync(fromDir) || fs13.existsSync(toDir)) return false;
15514
- ensureDir(path17.dirname(toDir));
15515
- fs13.renameSync(fromDir, toDir);
15688
+ if (path19.normalize(fromDir) === path19.normalize(toDir)) return false;
15689
+ if (!fs14.existsSync(fromDir) || fs14.existsSync(toDir)) return false;
15690
+ ensureDir(path19.dirname(toDir));
15691
+ fs14.renameSync(fromDir, toDir);
15516
15692
  return true;
15517
15693
  }
15518
15694
  function cleanEmptyDirs(filePath, specsRoot) {
15519
- let dir = path17.dirname(filePath);
15695
+ let dir = path19.dirname(filePath);
15520
15696
  while (dir !== specsRoot && dir.startsWith(specsRoot)) {
15521
- if (fs13.existsSync(dir) && fs13.readdirSync(dir).length === 0) {
15522
- fs13.rmdirSync(dir);
15523
- dir = path17.dirname(dir);
15697
+ if (fs14.existsSync(dir) && fs14.readdirSync(dir).length === 0) {
15698
+ fs14.rmdirSync(dir);
15699
+ dir = path19.dirname(dir);
15524
15700
  } else {
15525
15701
  break;
15526
15702
  }
@@ -15539,13 +15715,13 @@ function parseOrThrow(schema, value, kind, id) {
15539
15715
  function computeSpecTreeSignature(dirs) {
15540
15716
  const parts = [];
15541
15717
  for (const dir of dirs) {
15542
- if (!fs13.existsSync(dir)) {
15718
+ if (!fs14.existsSync(dir)) {
15543
15719
  parts.push(`${dir}:missing`);
15544
15720
  continue;
15545
15721
  }
15546
15722
  for (const f of listFilesRecursive(dir, ".yaml")) {
15547
15723
  try {
15548
- const st = fs13.statSync(f);
15724
+ const st = fs14.statSync(f);
15549
15725
  parts.push(`${f}:${st.mtimeMs}:${st.size}`);
15550
15726
  } catch {
15551
15727
  parts.push(`${f}:gone`);
@@ -15555,7 +15731,7 @@ function computeSpecTreeSignature(dirs) {
15555
15731
  return parts.join("|");
15556
15732
  }
15557
15733
  function workspaceFor(rootDir) {
15558
- const key = path17.resolve(rootDir);
15734
+ const key = path19.resolve(rootDir);
15559
15735
  let ws = workspaces.get(key);
15560
15736
  if (!ws) {
15561
15737
  ws = new SpecWorkspace(key);
@@ -15694,7 +15870,7 @@ function readLockState() {
15694
15870
  return { state: stateIdEquals(record2.stateId, current2) ? "locked" : "stale", record: record2, current: current2 };
15695
15871
  }
15696
15872
  function computeStateIdAt(root) {
15697
- const resolved = path17.resolve(root);
15873
+ const resolved = path19.resolve(root);
15698
15874
  return runWithProjectRoot(resolved, () => {
15699
15875
  workspaceFor(resolved).invalidate();
15700
15876
  const system = loadSystemSpec();
@@ -15724,12 +15900,15 @@ function collectPromotableSpecs(scopeSubsystem) {
15724
15900
  function applySpecStatus(kind, id, status2) {
15725
15901
  current().applySpecStatus(kind, id, status2);
15726
15902
  }
15903
+ function specPathsInScope(scopeSubsystem) {
15904
+ return current().specPathsInScope(scopeSubsystem);
15905
+ }
15727
15906
  function snapshotSpecFiles() {
15728
15907
  return current().snapshotSpecFiles();
15729
15908
  }
15730
15909
  function restoreSpecFiles(snapshot) {
15731
15910
  for (const [file, content] of snapshot) {
15732
- fs13.writeFileSync(file, content);
15911
+ fs14.writeFileSync(file, content);
15733
15912
  }
15734
15913
  }
15735
15914
  function findLegacySpecFiles() {
@@ -15738,12 +15917,12 @@ function findLegacySpecFiles() {
15738
15917
  function updateSpec(kind, id, delta, hooks) {
15739
15918
  return current().updateSpec(kind, id, delta, hooks);
15740
15919
  }
15741
- var fs13, path17, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
15920
+ var fs14, path19, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
15742
15921
  var init_specs2 = __esm({
15743
15922
  "src/core/specs.ts"() {
15744
15923
  "use strict";
15745
- fs13 = __toESM(require("fs"));
15746
- path17 = __toESM(require("path"));
15924
+ fs14 = __toESM(require("fs"));
15925
+ path19 = __toESM(require("path"));
15747
15926
  init_loader();
15748
15927
  init_fs();
15749
15928
  init_statehash();
@@ -15764,7 +15943,7 @@ var init_specs2 = __esm({
15764
15943
  this.rootSubsystems = /* @__PURE__ */ new Set();
15765
15944
  this.scanVisitedSpecDirs = [];
15766
15945
  this.loaderIssues = [];
15767
- this.rootDir = path17.resolve(rootDir);
15946
+ this.rootDir = path19.resolve(rootDir);
15768
15947
  this.paths = aiPathsAt(this.rootDir);
15769
15948
  }
15770
15949
  invalidate() {
@@ -15791,7 +15970,7 @@ var init_specs2 = __esm({
15791
15970
  this.rootSubsystems.clear();
15792
15971
  this.cachedRecursive = recursive;
15793
15972
  this.scanVisitedSpecDirs = [];
15794
- const visited = /* @__PURE__ */ new Set([path17.resolve(this.rootDir)]);
15973
+ const visited = /* @__PURE__ */ new Set([path19.resolve(this.rootDir)]);
15795
15974
  const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
15796
15975
  this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
15797
15976
  this.cachedSpecDirs = this.scanVisitedSpecDirs;
@@ -15806,10 +15985,10 @@ var init_specs2 = __esm({
15806
15985
  this.scanVisitedSpecDirs.push(specsDir);
15807
15986
  if (!pathExists(specsDir)) return index;
15808
15987
  const files = listFilesRecursive(specsDir, ".yaml");
15809
- const systemYaml = path17.normalize(projectPaths.specsSystem());
15988
+ const systemYaml = path19.normalize(projectPaths.specsSystem());
15810
15989
  const localSubprojects = [];
15811
15990
  for (const file of files) {
15812
- const normFile = path17.normalize(file);
15991
+ const normFile = path19.normalize(file);
15813
15992
  if (normFile === systemYaml) continue;
15814
15993
  let detectedType = "spec";
15815
15994
  try {
@@ -15819,7 +15998,7 @@ var init_specs2 = __esm({
15819
15998
  severity: "error",
15820
15999
  code: "INVALID_YAML",
15821
16000
  message: `Spec file "${file}" is not a valid YAML object or is empty.`,
15822
- specId: path17.basename(file, ".yaml")
16001
+ specId: path19.basename(file, ".yaml")
15823
16002
  });
15824
16003
  continue;
15825
16004
  }
@@ -15851,8 +16030,8 @@ var init_specs2 = __esm({
15851
16030
  detectedType = "implementation";
15852
16031
  const parsed = ImplementationSpecSchema.parse(raw);
15853
16032
  if (parsed.sourcePath) {
15854
- const absSourcePath = path17.resolve(projectDir, parsed.sourcePath);
15855
- parsed.sourcePath = path17.relative(projectDir, absSourcePath).replace(/\\/g, "/");
16033
+ const absSourcePath = path19.resolve(projectDir, parsed.sourcePath);
16034
+ parsed.sourcePath = path19.relative(projectDir, absSourcePath).replace(/\\/g, "/");
15856
16035
  }
15857
16036
  index.implementations.push(parsed);
15858
16037
  index.paths.implementation[parsed.id] = file;
@@ -15874,11 +16053,11 @@ var init_specs2 = __esm({
15874
16053
  severity: "error",
15875
16054
  code: "UNKNOWN_SPEC_TYPE",
15876
16055
  message: `Spec file "${file}" does not match any recognized L1-L4 schema structure.`,
15877
- specId: path17.basename(file, ".yaml")
16056
+ specId: path19.basename(file, ".yaml")
15878
16057
  });
15879
16058
  }
15880
16059
  } catch (e) {
15881
- const filename = path17.basename(file, ".yaml");
16060
+ const filename = path19.basename(file, ".yaml");
15882
16061
  this.loaderIssues.push({
15883
16062
  severity: "error",
15884
16063
  code: "SCHEMA_VALIDATION_ERROR",
@@ -15976,7 +16155,7 @@ var init_specs2 = __esm({
15976
16155
  }
15977
16156
  if (currentDepth < maxDepth) {
15978
16157
  for (const subproj of localSubprojects) {
15979
- const childDir = path17.resolve(projectDir, subproj.projectPath);
16158
+ const childDir = path19.resolve(projectDir, subproj.projectPath);
15980
16159
  if (projectPathEscapesRoot(this.rootDir, subproj.projectPath, childDir)) {
15981
16160
  this.loaderIssues.push({
15982
16161
  severity: "error",
@@ -15995,7 +16174,7 @@ var init_specs2 = __esm({
15995
16174
  });
15996
16175
  continue;
15997
16176
  }
15998
- if (!fs13.existsSync(childDir)) {
16177
+ if (!fs14.existsSync(childDir)) {
15999
16178
  this.loaderIssues.push({
16000
16179
  severity: "error",
16001
16180
  code: "SUBPROJECT_NOT_FOUND",
@@ -16038,7 +16217,7 @@ var init_specs2 = __esm({
16038
16217
  const index = this.scanAll();
16039
16218
  const sub = index.subsystems.find((s) => s.id === currentPrefix);
16040
16219
  if (sub && sub.projectPath) {
16041
- const nextDir = path17.resolve(currentDir, sub.projectPath);
16220
+ const nextDir = path19.resolve(currentDir, sub.projectPath);
16042
16221
  if (projectPathEscapesRoot(this.rootDir, sub.projectPath, nextDir)) {
16043
16222
  this.loaderIssues.push({
16044
16223
  severity: "error",
@@ -16090,9 +16269,9 @@ var init_specs2 = __esm({
16090
16269
  return index.paths.subsystem[matches2[0]];
16091
16270
  }
16092
16271
  if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
16093
- return path17.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
16272
+ return path19.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
16094
16273
  }
16095
- return path17.join(this.paths.specsDir(), id, ".index.yaml");
16274
+ return path19.join(this.paths.specsDir(), id, ".index.yaml");
16096
16275
  }
16097
16276
  getComponentPath(id, subsystemId) {
16098
16277
  const index = this.scanAll();
@@ -16112,21 +16291,21 @@ var init_specs2 = __esm({
16112
16291
  if (owner) {
16113
16292
  const ownerPath = index.paths.component[owner.id];
16114
16293
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
16115
- return path17.join(path17.dirname(ownerPath), id, ".index.yaml");
16294
+ return path19.join(path19.dirname(ownerPath), id, ".index.yaml");
16116
16295
  }
16117
16296
  }
16118
16297
  if (subsystemId) {
16119
16298
  const subPath = this.getSubsystemPath(subsystemId);
16120
- const subDir = path17.dirname(subPath);
16299
+ const subDir = path19.dirname(subPath);
16121
16300
  if (subPath.endsWith(".index.yaml")) {
16122
- return path17.join(subDir, id, ".index.yaml");
16301
+ return path19.join(subDir, id, ".index.yaml");
16123
16302
  }
16124
16303
  }
16125
16304
  if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
16126
- return path17.join(this.paths.specsComponentsDir(), `${id}.yaml`);
16305
+ return path19.join(this.paths.specsComponentsDir(), `${id}.yaml`);
16127
16306
  }
16128
16307
  const targetSubsystem = subsystemId || "default";
16129
- return path17.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
16308
+ return path19.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
16130
16309
  }
16131
16310
  getInterfacePath(id, componentId) {
16132
16311
  const index = this.scanAll();
@@ -16152,16 +16331,16 @@ var init_specs2 = __esm({
16152
16331
  }
16153
16332
  if (componentId) {
16154
16333
  const compPath = this.getComponentPath(componentId);
16155
- const compDir = path17.dirname(compPath);
16334
+ const compDir = path19.dirname(compPath);
16156
16335
  if (compPath.endsWith(".index.yaml")) {
16157
- return path17.join(compDir, ".interface.yaml");
16336
+ return path19.join(compDir, ".interface.yaml");
16158
16337
  }
16159
16338
  }
16160
16339
  if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
16161
- return path17.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
16340
+ return path19.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
16162
16341
  }
16163
16342
  const targetComponent = componentId || "default";
16164
- return path17.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
16343
+ return path19.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
16165
16344
  }
16166
16345
  getImplementationPath(id, contractId) {
16167
16346
  const index = this.scanAll();
@@ -16187,16 +16366,16 @@ var init_specs2 = __esm({
16187
16366
  }
16188
16367
  if (contractId) {
16189
16368
  const intfPath = this.getInterfacePath(contractId);
16190
- const intfDir = path17.dirname(intfPath);
16369
+ const intfDir = path19.dirname(intfPath);
16191
16370
  if (intfPath.endsWith(".interface.yaml")) {
16192
- return path17.join(intfDir, ".implementation.yaml");
16371
+ return path19.join(intfDir, ".implementation.yaml");
16193
16372
  }
16194
16373
  }
16195
16374
  if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
16196
- return path17.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
16375
+ return path19.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
16197
16376
  }
16198
16377
  const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
16199
- return path17.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
16378
+ return path19.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
16200
16379
  }
16201
16380
  getTypePath(id, subsystemId, group) {
16202
16381
  const index = this.scanAll();
@@ -16227,7 +16406,7 @@ var init_specs2 = __esm({
16227
16406
  const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
16228
16407
  if (groupPath) {
16229
16408
  const localId2 = id.split("::").pop();
16230
- return path17.join(path17.dirname(groupPath), `${localId2}.yaml`);
16409
+ return path19.join(path19.dirname(groupPath), `${localId2}.yaml`);
16231
16410
  }
16232
16411
  }
16233
16412
  let localId = id;
@@ -16240,12 +16419,12 @@ var init_specs2 = __esm({
16240
16419
  }
16241
16420
  if (subsystemId) {
16242
16421
  const subPath = this.getSubsystemPath(subsystemId);
16243
- const subDir = path17.dirname(subPath);
16422
+ const subDir = path19.dirname(subPath);
16244
16423
  if (subPath.endsWith(".index.yaml")) {
16245
- return path17.join(subDir, "types", `${localId}.yaml`);
16424
+ return path19.join(subDir, "types", `${localId}.yaml`);
16246
16425
  }
16247
16426
  }
16248
- return path17.join(this.paths.specsTypesDir(), `${localId}.yaml`);
16427
+ return path19.join(this.paths.specsTypesDir(), `${localId}.yaml`);
16249
16428
  }
16250
16429
  getGroupPath(id, subsystemId) {
16251
16430
  const index = this.scanAll();
@@ -16279,12 +16458,12 @@ var init_specs2 = __esm({
16279
16458
  }
16280
16459
  if (subsystemId) {
16281
16460
  const subPath = this.getSubsystemPath(subsystemId);
16282
- const subDir = path17.dirname(subPath);
16461
+ const subDir = path19.dirname(subPath);
16283
16462
  if (subPath.endsWith(".index.yaml")) {
16284
- return path17.join(subDir, "types", localId, ".index.yaml");
16463
+ return path19.join(subDir, "types", localId, ".index.yaml");
16285
16464
  }
16286
16465
  }
16287
- return path17.join(this.paths.specsTypesDir(), localId, ".index.yaml");
16466
+ return path19.join(this.paths.specsTypesDir(), localId, ".index.yaml");
16288
16467
  }
16289
16468
  // -------------------------------------------------------------------------
16290
16469
  // Level 0: System
@@ -16307,7 +16486,7 @@ var init_specs2 = __esm({
16307
16486
  }
16308
16487
  saveSystemSpec(spec) {
16309
16488
  const p = this.paths.specsSystem();
16310
- ensureDir(path17.dirname(p));
16489
+ ensureDir(path19.dirname(p));
16311
16490
  writeYamlFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
16312
16491
  invalidateSpecCache();
16313
16492
  }
@@ -16425,7 +16604,7 @@ var init_specs2 = __esm({
16425
16604
  }
16426
16605
  saveSubsystemSpec(spec) {
16427
16606
  const p = this.getSubsystemPath(spec.id);
16428
- ensureDir(path17.dirname(p));
16607
+ ensureDir(path19.dirname(p));
16429
16608
  const { prefix } = splitNamespace(spec.id);
16430
16609
  if (!prefix && spec.projectPath && spec.projectPath.trim() !== "") {
16431
16610
  assertContainedProjectPath(this.rootDir, spec.projectPath);
@@ -16456,9 +16635,9 @@ var init_specs2 = __esm({
16456
16635
  }
16457
16636
  deleteSubsystemSpec(id) {
16458
16637
  const p = this.getSubsystemPath(id);
16459
- if (!fs13.existsSync(p)) return false;
16460
- fs13.unlinkSync(p);
16461
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16638
+ if (!fs14.existsSync(p)) return false;
16639
+ fs14.unlinkSync(p);
16640
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16462
16641
  invalidateSpecCache();
16463
16642
  return true;
16464
16643
  }
@@ -16491,7 +16670,7 @@ var init_specs2 = __esm({
16491
16670
  saveComponentSpec(spec, opts) {
16492
16671
  const notices = [];
16493
16672
  const p = this.getComponentPath(spec.id, spec.subsystem);
16494
- ensureDir(path17.dirname(p));
16673
+ ensureDir(path19.dirname(p));
16495
16674
  const specToWrite = this.prepareComponentForWrite(spec);
16496
16675
  const existing = this.loadComponentSpec(spec.id);
16497
16676
  if (existing) {
@@ -16508,7 +16687,7 @@ var init_specs2 = __esm({
16508
16687
  const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
16509
16688
  if (subsystemChanged && !p.endsWith(".index.yaml")) {
16510
16689
  notices.push(
16511
- `component "${spec.id}" already exists at ${path17.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
16690
+ `component "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
16512
16691
  );
16513
16692
  }
16514
16693
  specToWrite.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -16519,9 +16698,9 @@ var init_specs2 = __esm({
16519
16698
  }
16520
16699
  deleteComponentSpec(id) {
16521
16700
  const p = this.getComponentPath(id);
16522
- if (!fs13.existsSync(p)) return false;
16523
- fs13.unlinkSync(p);
16524
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16701
+ if (!fs14.existsSync(p)) return false;
16702
+ fs14.unlinkSync(p);
16703
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16525
16704
  invalidateSpecCache();
16526
16705
  return true;
16527
16706
  }
@@ -16534,12 +16713,12 @@ var init_specs2 = __esm({
16534
16713
  if (owner) {
16535
16714
  const ownerPath = index.paths.component[owner.id];
16536
16715
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
16537
- const ownerSubDir = path17.dirname(this.getSubsystemPath(owner.subsystem));
16538
- return path17.join(ownerSubDir, owner.id, comp.id);
16716
+ const ownerSubDir = path19.dirname(this.getSubsystemPath(owner.subsystem));
16717
+ return path19.join(ownerSubDir, owner.id, comp.id);
16539
16718
  }
16540
16719
  }
16541
- const subDir = path17.dirname(this.getSubsystemPath(comp.subsystem));
16542
- return path17.join(subDir, comp.id);
16720
+ const subDir = path19.dirname(this.getSubsystemPath(comp.subsystem));
16721
+ return path19.join(subDir, comp.id);
16543
16722
  }
16544
16723
  /**
16545
16724
  * Move component folders so the physical tree mirrors ownership: each owned
@@ -16555,7 +16734,7 @@ var init_specs2 = __esm({
16555
16734
  if (!currentPath) continue;
16556
16735
  const desiredDir = this.desiredComponentDir(comp, index);
16557
16736
  if (!desiredDir) continue;
16558
- if (moveComponentFolder(path17.dirname(currentPath), desiredDir)) moved.push(comp.id);
16737
+ if (moveComponentFolder(path19.dirname(currentPath), desiredDir)) moved.push(comp.id);
16559
16738
  }
16560
16739
  if (moved.length) invalidateSpecCache();
16561
16740
  return moved;
@@ -16585,16 +16764,46 @@ var init_specs2 = __esm({
16585
16764
  return null;
16586
16765
  }
16587
16766
  }
16767
+ /**
16768
+ * Refuse a write that would land on a file already holding a DIFFERENT spec.
16769
+ *
16770
+ * In the nested layout a spec's path is derived from its parent — a component
16771
+ * for an interface, a contract for an implementation — and the spec's own id
16772
+ * is not part of it (see getInterfacePath / getImplementationPath, which take
16773
+ * the id but ignore it once the parent resolves). So a second id bound to the
16774
+ * same parent resolves to the SAME file.
16775
+ *
16776
+ * Left unguarded that is silent data loss, and doubly invisible: the caller's
16777
+ * `existing` lookup is by the NEW id, finds nothing, and every re-author
16778
+ * notice ("REMOVED …", the carry-forward seam) stays quiet. An agent renaming
16779
+ * an interface by defining a new one destroys the old contract, its
16780
+ * narratives and its lint.allows, and is told "Successfully defined".
16781
+ */
16782
+ assertPathHoldsNoOtherSpec(p, id, kind, parentLabel) {
16783
+ if (!pathExists(p)) return;
16784
+ let occupantId;
16785
+ try {
16786
+ occupantId = readYamlFile(p)?.id;
16787
+ } catch {
16788
+ return;
16789
+ }
16790
+ if (!occupantId) return;
16791
+ if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
16792
+ throw new Error(
16793
+ `Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path19.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
16794
+ );
16795
+ }
16588
16796
  /** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
16589
16797
  saveInterfaceSpec(spec, opts) {
16590
16798
  const notices = [];
16591
16799
  const p = this.getInterfacePath(spec.id, spec.component);
16592
- ensureDir(path17.dirname(p));
16800
+ this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
16801
+ ensureDir(path19.dirname(p));
16593
16802
  const specToWrite = this.prepareInterfaceForWrite(spec);
16594
16803
  const existing = this.loadInterfaceSpec(spec.id);
16595
16804
  if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
16596
16805
  notices.push(
16597
- `interface "${spec.id}" already exists at ${path17.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
16806
+ `interface "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
16598
16807
  );
16599
16808
  }
16600
16809
  if (existing) {
@@ -16617,9 +16826,9 @@ var init_specs2 = __esm({
16617
16826
  }
16618
16827
  deleteInterfaceSpec(id) {
16619
16828
  const p = this.getInterfacePath(id);
16620
- if (!fs13.existsSync(p)) return false;
16621
- fs13.unlinkSync(p);
16622
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16829
+ if (!fs14.existsSync(p)) return false;
16830
+ fs14.unlinkSync(p);
16831
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16623
16832
  invalidateSpecCache();
16624
16833
  return true;
16625
16834
  }
@@ -16652,12 +16861,13 @@ var init_specs2 = __esm({
16652
16861
  saveImplementationSpec(spec, opts) {
16653
16862
  const notices = [];
16654
16863
  const p = this.getImplementationPath(spec.id, spec.contract);
16655
- ensureDir(path17.dirname(p));
16864
+ this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
16865
+ ensureDir(path19.dirname(p));
16656
16866
  const specToWrite = this.prepareImplementationForWrite(spec);
16657
16867
  const existing = this.loadImplementationSpec(spec.id);
16658
16868
  if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
16659
16869
  notices.push(
16660
- `implementation "${spec.id}" already exists at ${path17.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
16870
+ `implementation "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
16661
16871
  );
16662
16872
  }
16663
16873
  if (existing) {
@@ -16673,9 +16883,9 @@ var init_specs2 = __esm({
16673
16883
  }
16674
16884
  deleteImplementationSpec(id) {
16675
16885
  const p = this.getImplementationPath(id);
16676
- if (!fs13.existsSync(p)) return false;
16677
- fs13.unlinkSync(p);
16678
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16886
+ if (!fs14.existsSync(p)) return false;
16887
+ fs14.unlinkSync(p);
16888
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16679
16889
  invalidateSpecCache();
16680
16890
  return true;
16681
16891
  }
@@ -16700,11 +16910,11 @@ var init_specs2 = __esm({
16700
16910
  const existing = this.loadTypeSpec(spec.id);
16701
16911
  const group = spec.group || (existing ? existing.group : void 0);
16702
16912
  const p = this.getTypePath(spec.id, spec.subsystem, group);
16703
- ensureDir(path17.dirname(p));
16913
+ ensureDir(path19.dirname(p));
16704
16914
  const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
16705
16915
  if (subsystemChanged) {
16706
16916
  notices.push(
16707
- `type "${spec.id}" already exists at ${path17.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
16917
+ `type "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
16708
16918
  );
16709
16919
  }
16710
16920
  if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
@@ -16727,9 +16937,9 @@ var init_specs2 = __esm({
16727
16937
  deleteTypeSpec(id) {
16728
16938
  const spec = this.loadTypeSpec(id);
16729
16939
  const p = this.getTypePath(id, spec?.subsystem, spec?.group);
16730
- if (!fs13.existsSync(p)) return false;
16731
- fs13.unlinkSync(p);
16732
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16940
+ if (!fs14.existsSync(p)) return false;
16941
+ fs14.unlinkSync(p);
16942
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16733
16943
  invalidateSpecCache();
16734
16944
  return true;
16735
16945
  }
@@ -16744,7 +16954,7 @@ var init_specs2 = __esm({
16744
16954
  }
16745
16955
  saveGroupSpec(spec) {
16746
16956
  const p = this.getGroupPath(spec.id);
16747
- ensureDir(path17.dirname(p));
16957
+ ensureDir(path19.dirname(p));
16748
16958
  const specToWrite = this.prepareGroupForWrite(spec);
16749
16959
  const existing = this.loadGroupSpec(spec.id);
16750
16960
  if (existing) {
@@ -16756,9 +16966,9 @@ var init_specs2 = __esm({
16756
16966
  }
16757
16967
  deleteGroupSpec(id) {
16758
16968
  const p = this.getGroupPath(id);
16759
- if (!fs13.existsSync(p)) return false;
16760
- fs13.unlinkSync(p);
16761
- cleanEmptyDirs(p, path17.resolve(this.paths.specsDir()));
16969
+ if (!fs14.existsSync(p)) return false;
16970
+ fs14.unlinkSync(p);
16971
+ cleanEmptyDirs(p, path19.resolve(this.paths.specsDir()));
16762
16972
  invalidateSpecCache();
16763
16973
  return true;
16764
16974
  }
@@ -16806,6 +17016,50 @@ var init_specs2 = __esm({
16806
17016
  }
16807
17017
  return out;
16808
17018
  }
17019
+ /**
17020
+ * Every spec FILE inside a subsystem scope (absolute paths), regardless of
17021
+ * status. `collectPromotableSpecs` answers a different question — which specs
17022
+ * are not yet complete — so it cannot stand in for this: a scoped approval
17023
+ * must cover the specs it approves whether or not they were already settled.
17024
+ */
17025
+ specPathsInScope(scopeSubsystem) {
17026
+ const index = this.scanAll();
17027
+ const components = this.loadComponentSpecs();
17028
+ const interfaces = this.loadInterfaceSpecs();
17029
+ const implementations = this.loadImplementationSpecs();
17030
+ const inScope = (specSubsystem) => {
17031
+ if (!scopeSubsystem) return true;
17032
+ if (!specSubsystem) return false;
17033
+ return specSubsystem === scopeSubsystem || specSubsystem.startsWith(`${scopeSubsystem}::`);
17034
+ };
17035
+ const out = /* @__PURE__ */ new Set();
17036
+ const add = (p) => {
17037
+ if (p) out.add(path19.resolve(p));
17038
+ };
17039
+ for (const s of this.loadSubsystemSpecs()) {
17040
+ if (!scopeSubsystem || s.id === scopeSubsystem || s.id.startsWith(`${scopeSubsystem}::`)) {
17041
+ add(index.paths.subsystem[s.id]);
17042
+ }
17043
+ }
17044
+ for (const c of components) {
17045
+ if (inScope(c.subsystem)) add(index.paths.component[c.id]);
17046
+ }
17047
+ for (const i of interfaces) {
17048
+ const comp = components.find((c) => c.id === i.component);
17049
+ if (comp && inScope(comp.subsystem)) add(index.paths.interface[i.id]);
17050
+ }
17051
+ for (const m of implementations) {
17052
+ const intf = interfaces.find((i) => i.id === m.contract);
17053
+ const comp = intf ? components.find((c) => c.id === intf.component) : null;
17054
+ if (comp && inScope(comp.subsystem)) add(index.paths.implementation[m.id]);
17055
+ }
17056
+ if (!scopeSubsystem) {
17057
+ add(this.paths.specsSystem());
17058
+ for (const p of Object.values(index.paths.type)) add(p);
17059
+ for (const p of Object.values(index.paths.group)) add(p);
17060
+ }
17061
+ return [...out];
17062
+ }
16809
17063
  /** Set a single spec's status (bumps updatedAt). Caller invalidates the cache. */
16810
17064
  applySpecStatus(kind, id, status2) {
16811
17065
  switch (kind) {
@@ -16843,16 +17097,16 @@ var init_specs2 = __esm({
16843
17097
  const files = /* @__PURE__ */ new Set();
16844
17098
  const sysPath = this.paths.specsSystem();
16845
17099
  if (pathExists(sysPath)) {
16846
- files.add(path17.resolve(sysPath));
17100
+ files.add(path19.resolve(sysPath));
16847
17101
  }
16848
17102
  for (const group of Object.values(index.paths)) {
16849
17103
  for (const file of Object.values(group)) {
16850
- files.add(path17.resolve(file));
17104
+ files.add(path19.resolve(file));
16851
17105
  }
16852
17106
  }
16853
17107
  for (const file of files) {
16854
- if (fs13.existsSync(file)) {
16855
- snapshot.set(file, fs13.readFileSync(file, "utf8"));
17108
+ if (fs14.existsSync(file)) {
17109
+ snapshot.set(file, fs14.readFileSync(file, "utf8"));
16856
17110
  }
16857
17111
  }
16858
17112
  return snapshot;
@@ -16866,20 +17120,20 @@ var init_specs2 = __esm({
16866
17120
  const files = listFilesRecursive(specsDir, ".yaml");
16867
17121
  const legacy = [];
16868
17122
  for (const f of files) {
16869
- const base = path17.basename(f);
16870
- const dir = path17.dirname(f);
17123
+ const base = path19.basename(f);
17124
+ const dir = path19.dirname(f);
16871
17125
  if (base === "system.yaml") {
16872
- legacy.push({ path: f, expected: path17.join(dir, ".index.yaml") });
17126
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
16873
17127
  } else if (base === "subsystem.yaml") {
16874
- legacy.push({ path: f, expected: path17.join(dir, ".index.yaml") });
17128
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
16875
17129
  } else if (base === "component.yaml") {
16876
- legacy.push({ path: f, expected: path17.join(dir, ".index.yaml") });
17130
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
16877
17131
  } else if (base === "group.yaml") {
16878
- legacy.push({ path: f, expected: path17.join(dir, ".index.yaml") });
17132
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
16879
17133
  } else if (base === "interface.yaml") {
16880
- legacy.push({ path: f, expected: path17.join(dir, ".interface.yaml") });
17134
+ legacy.push({ path: f, expected: path19.join(dir, ".interface.yaml") });
16881
17135
  } else if (base === "implementation.yaml") {
16882
- legacy.push({ path: f, expected: path17.join(dir, ".implementation.yaml") });
17136
+ legacy.push({ path: f, expected: path19.join(dir, ".implementation.yaml") });
16883
17137
  }
16884
17138
  }
16885
17139
  return legacy;
@@ -16946,6 +17200,35 @@ var init_specs2 = __esm({
16946
17200
  }
16947
17201
  return refs;
16948
17202
  };
17203
+ const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
17204
+ const assertDeltaMarkers = (item, label) => {
17205
+ if (!item || typeof item !== "object") return;
17206
+ if ("remove" in item && typeof item.remove !== "boolean") {
17207
+ throw new Error(
17208
+ `Refusing to update ${label}: "remove" must be the boolean true, got ${JSON.stringify(item.remove)}. A non-boolean is ignored, which would leave the element in place while reporting success.`
17209
+ );
17210
+ }
17211
+ if ("action" in item && item.action !== "add" && item.action !== "delete") {
17212
+ throw new Error(
17213
+ `Refusing to update ${label}: unknown action ${JSON.stringify(item.action)} \u2014 expected "add" or "delete". An unknown verb is ignored, which would leave the element in place while reporting success.`
17214
+ );
17215
+ }
17216
+ };
17217
+ const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label) => {
17218
+ if (deltaItem?.remove === true || deltaItem?.action === "delete") {
17219
+ throw new Error(
17220
+ `Refusing to delete ${label} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
17221
+ );
17222
+ }
17223
+ const near = existingKeys.find(
17224
+ (k) => String(k) !== String(key) && normalizeIdentity(k) === normalizeIdentity(key)
17225
+ );
17226
+ if (near !== void 0) {
17227
+ throw new Error(
17228
+ `Refusing to add ${label} "${String(key)}": "${String(near)}" already exists and differs only in case or separators. Use the existing identity to edit it, or pick a name that is not a near-duplicate.`
17229
+ );
17230
+ }
17231
+ };
16949
17232
  const mergeNarrative = (existingSteps, deltaSteps) => {
16950
17233
  let steps = [...existingSteps];
16951
17234
  const sortedDeltas = [...deltaSteps].sort((a, b) => a.stepNumber - b.stepNumber);
@@ -17014,6 +17297,7 @@ var init_specs2 = __esm({
17014
17297
  const mergeMethods = (existingMethods, deltaMethods) => {
17015
17298
  const merged = [...existingMethods];
17016
17299
  for (const deltaMethod of deltaMethods) {
17300
+ assertDeltaMarkers(deltaMethod, `method "${deltaMethod?.name}"`);
17017
17301
  const idx = merged.findIndex((m) => m.name === deltaMethod.name);
17018
17302
  if (idx !== -1) {
17019
17303
  if (deltaMethod.remove === true || deltaMethod.action === "delete") {
@@ -17033,9 +17317,8 @@ var init_specs2 = __esm({
17033
17317
  };
17034
17318
  }
17035
17319
  } else {
17036
- if (deltaMethod.remove !== true && deltaMethod.action !== "delete") {
17037
- merged.push(deltaMethod);
17038
- }
17320
+ assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
17321
+ merged.push(deltaMethod);
17039
17322
  }
17040
17323
  }
17041
17324
  return merged;
@@ -17043,6 +17326,7 @@ var init_specs2 = __esm({
17043
17326
  const mergeNamedArray = (existing, delta2) => {
17044
17327
  const merged = [...existing];
17045
17328
  for (const deltaItem of delta2) {
17329
+ assertDeltaMarkers(deltaItem, `entry "${deltaItem?.name}"`);
17046
17330
  const idx = merged.findIndex((item) => item.name === deltaItem.name);
17047
17331
  if (idx !== -1) {
17048
17332
  if (deltaItem.remove === true || deltaItem.action === "delete") {
@@ -17055,9 +17339,8 @@ var init_specs2 = __esm({
17055
17339
  };
17056
17340
  }
17057
17341
  } else {
17058
- if (deltaItem.remove !== true && deltaItem.action !== "delete") {
17059
- merged.push(deltaItem);
17060
- }
17342
+ assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
17343
+ merged.push(deltaItem);
17061
17344
  }
17062
17345
  }
17063
17346
  return merged;
@@ -17065,6 +17348,7 @@ var init_specs2 = __esm({
17065
17348
  const mergeKeyedArray = (existing, delta2, keyOf) => {
17066
17349
  const merged = [...existing];
17067
17350
  for (const deltaItem of delta2) {
17351
+ assertDeltaMarkers(deltaItem, `entry "${keyOf(deltaItem)}"`);
17068
17352
  const idx = merged.findIndex((item) => keyOf(item) === keyOf(deltaItem));
17069
17353
  if (idx !== -1) {
17070
17354
  if (deltaItem.remove === true || deltaItem.action === "delete") {
@@ -17072,7 +17356,8 @@ var init_specs2 = __esm({
17072
17356
  } else {
17073
17357
  merged[idx] = { ...merged[idx], ...deltaItem };
17074
17358
  }
17075
- } else if (deltaItem.remove !== true && deltaItem.action !== "delete") {
17359
+ } else {
17360
+ assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
17076
17361
  merged.push(deltaItem);
17077
17362
  }
17078
17363
  }
@@ -17081,6 +17366,8 @@ var init_specs2 = __esm({
17081
17366
  const mergePublicInterfaces = (existing, delta2) => {
17082
17367
  const merged = [...existing];
17083
17368
  for (const deltaItem of delta2) {
17369
+ const piKey = (i) => `${i?.component}.${i?.interface}`;
17370
+ assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
17084
17371
  const idx = merged.findIndex((item) => item.component === deltaItem.component && item.interface === deltaItem.interface);
17085
17372
  if (idx !== -1) {
17086
17373
  if (deltaItem.remove === true || deltaItem.action === "delete") {
@@ -17092,9 +17379,8 @@ var init_specs2 = __esm({
17092
17379
  };
17093
17380
  }
17094
17381
  } else {
17095
- if (deltaItem.remove !== true && deltaItem.action !== "delete") {
17096
- merged.push(deltaItem);
17097
- }
17382
+ assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
17383
+ merged.push(deltaItem);
17098
17384
  }
17099
17385
  }
17100
17386
  return merged;
@@ -17135,11 +17421,22 @@ var init_specs2 = __esm({
17135
17421
  for (const deltaItem of delta2) {
17136
17422
  const key = identityKeyOf(field, deltaItem);
17137
17423
  const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf(field, item) === key);
17424
+ assertDeltaMarkers(deltaItem, `${field} entry "${String(key)}"`);
17138
17425
  const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
17139
17426
  if (idx !== -1) {
17140
17427
  if (isDelete) merged.splice(idx, 1);
17141
17428
  else merged[idx] = { ...merged[idx], ...deltaItem };
17142
- } else if (!isDelete) {
17429
+ } else {
17430
+ if (key !== null) {
17431
+ assertUnmatchedIsAnAddition(
17432
+ deltaItem,
17433
+ key,
17434
+ merged.map((i) => identityKeyOf(field, i)).filter((k) => k !== null),
17435
+ field
17436
+ );
17437
+ } else if (isDelete) {
17438
+ throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
17439
+ }
17143
17440
  merged.push(deltaItem);
17144
17441
  }
17145
17442
  }
@@ -17331,7 +17628,7 @@ function provisionProject(name) {
17331
17628
  function ensureProjectInitialized(fallbackName) {
17332
17629
  const now = (/* @__PURE__ */ new Date()).toISOString();
17333
17630
  const paths = aiPathsAt(getProjectRoot());
17334
- const hasSystem = fs14.existsSync(paths.specsSystem());
17631
+ const hasSystem = fs15.existsSync(paths.specsSystem());
17335
17632
  let name = fallbackName;
17336
17633
  if (hasSystem) {
17337
17634
  const existing = loadSystemSpec();
@@ -17339,7 +17636,7 @@ function ensureProjectInitialized(fallbackName) {
17339
17636
  }
17340
17637
  let wroteConfig = false;
17341
17638
  let wroteSystem = false;
17342
- if (!fs14.existsSync(paths.projectConfig())) {
17639
+ if (!fs15.existsSync(paths.projectConfig())) {
17343
17640
  saveProjectConfig(defaultProjectConfig(name, now));
17344
17641
  wroteConfig = true;
17345
17642
  }
@@ -17368,11 +17665,11 @@ function promoteAllComplete() {
17368
17665
  function walkChainedSubprojects(projectRoot2, onChild) {
17369
17666
  const visited = /* @__PURE__ */ new Set();
17370
17667
  const walk = (dir) => {
17371
- const resolved = path18.resolve(dir);
17668
+ const resolved = path20.resolve(dir);
17372
17669
  if (visited.has(resolved)) return;
17373
17670
  visited.add(resolved);
17374
17671
  const specsDir = aiPathsAt(dir).specsDir();
17375
- if (!fs14.existsSync(specsDir)) return;
17672
+ if (!fs15.existsSync(specsDir)) return;
17376
17673
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
17377
17674
  let raw;
17378
17675
  try {
@@ -17390,7 +17687,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
17390
17687
  continue;
17391
17688
  }
17392
17689
  const id = raw.id;
17393
- onChild(childDir, typeof id === "string" ? id : path18.basename(childDir));
17690
+ onChild(childDir, typeof id === "string" ? id : path20.basename(childDir));
17394
17691
  walk(childDir);
17395
17692
  }
17396
17693
  };
@@ -17399,7 +17696,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
17399
17696
  function listDirectChainedSubprojects(projectRoot2) {
17400
17697
  const out = [];
17401
17698
  const specsDir = aiPathsAt(projectRoot2).specsDir();
17402
- if (!fs14.existsSync(specsDir)) return out;
17699
+ if (!fs15.existsSync(specsDir)) return out;
17403
17700
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
17404
17701
  let raw;
17405
17702
  try {
@@ -17417,12 +17714,12 @@ function listDirectChainedSubprojects(projectRoot2) {
17417
17714
  continue;
17418
17715
  }
17419
17716
  const id = raw.id;
17420
- out.push({ dir, subsystemId: typeof id === "string" ? id : path18.basename(dir) });
17717
+ out.push({ dir, subsystemId: typeof id === "string" ? id : path20.basename(dir) });
17421
17718
  }
17422
17719
  return out;
17423
17720
  }
17424
17721
  function childHasSpecsButNoConfig(childDir) {
17425
- return fs14.existsSync(aiPathsAt(childDir).specsDir()) && !fs14.existsSync(aiPathsAt(childDir).projectConfig());
17722
+ return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !fs15.existsSync(aiPathsAt(childDir).projectConfig());
17426
17723
  }
17427
17724
  function findChainingSubprojectsMissingConfig(projectRoot2) {
17428
17725
  const missing = [];
@@ -17471,14 +17768,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
17471
17768
  const oldDir = assertContainedProjectPath(root, sub.projectPath);
17472
17769
  const newDir = assertContainedProjectPath(root, nextPath);
17473
17770
  if (oldDir !== newDir) {
17474
- if (!fs14.existsSync(oldDir)) {
17771
+ if (!fs15.existsSync(oldDir)) {
17475
17772
  throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
17476
17773
  }
17477
- if (fs14.existsSync(newDir)) {
17774
+ if (fs15.existsSync(newDir)) {
17478
17775
  throw new WaironError(`Target directory already exists: ${newDir}`);
17479
17776
  }
17480
- ensureDir(path18.dirname(newDir));
17481
- fs14.renameSync(oldDir, newDir);
17777
+ ensureDir(path20.dirname(newDir));
17778
+ fs15.renameSync(oldDir, newDir);
17482
17779
  }
17483
17780
  saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
17484
17781
  invalidateSpecCache();
@@ -17487,9 +17784,9 @@ function toPosixPath(p) {
17487
17784
  return p.replace(/\\/g, "/");
17488
17785
  }
17489
17786
  function isWithinDir(dir, file) {
17490
- const d = path18.resolve(dir);
17491
- const f = path18.resolve(file);
17492
- return f === d || f.startsWith(d + path18.sep);
17787
+ const d = path20.resolve(dir);
17788
+ const f = path20.resolve(file);
17789
+ return f === d || f.startsWith(d + path20.sep);
17493
17790
  }
17494
17791
  function externalizeSubsystem(subsystemId, projectPath) {
17495
17792
  if (subsystemId.includes("::")) {
@@ -17501,14 +17798,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
17501
17798
  }
17502
17799
  const parentRoot = getProjectRoot();
17503
17800
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
17504
- const fooDir = path18.join(parentSpecsDir, subsystemId);
17505
- if (!fs14.existsSync(fooDir)) {
17801
+ const fooDir = path20.join(parentSpecsDir, subsystemId);
17802
+ if (!fs15.existsSync(fooDir)) {
17506
17803
  throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
17507
17804
  }
17508
17805
  const relPath = toPosixPath(projectPath);
17509
17806
  const childDir = assertContainedProjectPath(parentRoot, relPath);
17510
- const childFooDir = path18.join(childDir, ".wai", "specs", subsystemId);
17511
- if (fs14.existsSync(childFooDir)) {
17807
+ const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
17808
+ if (fs15.existsSync(childFooDir)) {
17512
17809
  throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
17513
17810
  }
17514
17811
  const renameMap = buildRenameMap(
@@ -17518,17 +17815,17 @@ function externalizeSubsystem(subsystemId, projectPath) {
17518
17815
  );
17519
17816
  const childSystemName = foo.name || subsystemId;
17520
17817
  runWithProjectRoot(childDir, () => {
17521
- ensureDir(path18.join(childDir, ".wai", "specs"));
17818
+ ensureDir(path20.join(childDir, ".wai", "specs"));
17522
17819
  provisionProject(childSystemName);
17523
17820
  });
17524
- ensureDir(path18.dirname(childFooDir));
17525
- fs14.renameSync(fooDir, childFooDir);
17526
- patchSubsystemIndex(path18.join(childFooDir, ".index.yaml"), (s) => {
17821
+ ensureDir(path20.dirname(childFooDir));
17822
+ fs15.renameSync(fooDir, childFooDir);
17823
+ patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
17527
17824
  s.parentSystem = childSystemName;
17528
17825
  delete s.projectPath;
17529
17826
  });
17530
17827
  ensureDir(fooDir);
17531
- writeYamlFile(path18.join(fooDir, ".index.yaml"), {
17828
+ writeYamlFile(path20.join(fooDir, ".index.yaml"), {
17532
17829
  id: subsystemId,
17533
17830
  name: foo.name,
17534
17831
  description: foo.description,
@@ -17554,9 +17851,9 @@ function internalizeSubsystem(subsystemId) {
17554
17851
  const parentRoot = getProjectRoot();
17555
17852
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
17556
17853
  const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
17557
- const childWai = path18.join(childDir, ".wai");
17558
- const childFooDir = path18.join(childDir, ".wai", "specs", subsystemId);
17559
- if (!fs14.existsSync(childFooDir)) {
17854
+ const childWai = path20.join(childDir, ".wai");
17855
+ const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
17856
+ if (!fs15.existsSync(childFooDir)) {
17560
17857
  throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
17561
17858
  }
17562
17859
  const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
@@ -17569,15 +17866,15 @@ function internalizeSubsystem(subsystemId) {
17569
17866
  false
17570
17867
  );
17571
17868
  const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
17572
- const fooDir = path18.join(parentSpecsDir, subsystemId);
17573
- fs14.rmSync(fooDir, { recursive: true, force: true });
17574
- ensureDir(path18.dirname(fooDir));
17575
- fs14.renameSync(childFooDir, fooDir);
17576
- patchSubsystemIndex(path18.join(fooDir, ".index.yaml"), (s) => {
17869
+ const fooDir = path20.join(parentSpecsDir, subsystemId);
17870
+ fs15.rmSync(fooDir, { recursive: true, force: true });
17871
+ ensureDir(path20.dirname(fooDir));
17872
+ fs15.renameSync(childFooDir, fooDir);
17873
+ patchSubsystemIndex(path20.join(fooDir, ".index.yaml"), (s) => {
17577
17874
  s.parentSystem = parentSystemName;
17578
17875
  delete s.projectPath;
17579
17876
  });
17580
- fs14.rmSync(childWai, { recursive: true, force: true });
17877
+ fs15.rmSync(childWai, { recursive: true, force: true });
17581
17878
  rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
17582
17879
  invalidateSpecCache();
17583
17880
  }
@@ -17674,18 +17971,18 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
17674
17971
  }
17675
17972
  }
17676
17973
  function patchSubsystemIndex(indexPath, mutate) {
17677
- if (!fs14.existsSync(indexPath)) return;
17974
+ if (!fs15.existsSync(indexPath)) return;
17678
17975
  const raw = readYamlFile(indexPath);
17679
17976
  mutate(raw);
17680
17977
  raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
17681
17978
  writeYamlFile(indexPath, raw);
17682
17979
  }
17683
- var fs14, path18;
17980
+ var fs15, path20;
17684
17981
  var init_provision = __esm({
17685
17982
  "src/core/provision.ts"() {
17686
17983
  "use strict";
17687
- fs14 = __toESM(require("fs"));
17688
- path18 = __toESM(require("path"));
17984
+ fs15 = __toESM(require("fs"));
17985
+ path20 = __toESM(require("path"));
17689
17986
  init_specs2();
17690
17987
  init_loader();
17691
17988
  init_fs();
@@ -17726,18 +18023,18 @@ __export(ai_guide_exports, {
17726
18023
  writeRootGuideDelegator: () => writeRootGuideDelegator
17727
18024
  });
17728
18025
  function globalGuideFilePath(targetType) {
17729
- if (targetType === "claude") return path19.join(process.env["CLAUDE_CONFIG_DIR"] || path19.join(os5.homedir(), ".claude"), "CLAUDE.md");
17730
- if (targetType === "gemini") return path19.join(process.env["GEMINI_CONFIG_DIR"] || path19.join(os5.homedir(), ".gemini"), "GEMINI.md");
18026
+ if (targetType === "claude") return path21.join(process.env["CLAUDE_CONFIG_DIR"] || path21.join(os6.homedir(), ".claude"), "CLAUDE.md");
18027
+ if (targetType === "gemini") return path21.join(process.env["GEMINI_CONFIG_DIR"] || path21.join(os6.homedir(), ".gemini"), "GEMINI.md");
17731
18028
  return null;
17732
18029
  }
17733
18030
  function localGuideFilePath(projectRoot2, targetType) {
17734
- if (targetType === "claude") return path19.join(projectRoot2, ".claude", "CLAUDE.md");
17735
- if (targetType === "gemini" || targetType === "agy") return path19.join(projectRoot2, ".gemini", "GEMINI.md");
18031
+ if (targetType === "claude") return path21.join(projectRoot2, ".claude", "CLAUDE.md");
18032
+ if (targetType === "gemini" || targetType === "agy") return path21.join(projectRoot2, ".gemini", "GEMINI.md");
17736
18033
  return null;
17737
18034
  }
17738
18035
  function hasWaironGuide(filePath) {
17739
- if (!fs15.existsSync(filePath)) return false;
17740
- return fs15.readFileSync(filePath, "utf-8").includes(GUIDE_MARKER_START);
18036
+ if (!fs16.existsSync(filePath)) return false;
18037
+ return fs16.readFileSync(filePath, "utf-8").includes(GUIDE_MARKER_START);
17741
18038
  }
17742
18039
  function injectGuide(filePath, scope) {
17743
18040
  const body = scope === "global" ? GLOBAL_GUIDE_BODY : LOCAL_GUIDE_BODY;
@@ -17748,11 +18045,11 @@ ${versionStamp()}
17748
18045
  ${body}
17749
18046
  ${GUIDE_MARKER_END}
17750
18047
  `;
17751
- const existing = fs15.existsSync(filePath) ? fs15.readFileSync(filePath, "utf-8") : "";
18048
+ const existing = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
17752
18049
  const stripped = stripGuideSection(existing);
17753
18050
  const newContent = stripped.trimEnd() + section;
17754
- fs15.mkdirSync(path19.dirname(filePath), { recursive: true });
17755
- fs15.writeFileSync(filePath, newContent, "utf-8");
18051
+ fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
18052
+ fs16.writeFileSync(filePath, newContent, "utf-8");
17756
18053
  }
17757
18054
  function stripGuideSection(content) {
17758
18055
  const start = content.indexOf(GUIDE_MARKER_START);
@@ -17762,7 +18059,7 @@ function stripGuideSection(content) {
17762
18059
  }
17763
18060
  function writeRootGuideDelegator(projectRoot2, targetType) {
17764
18061
  if (targetType === "claude") {
17765
- const filePath = path19.join(projectRoot2, "CLAUDE.md");
18062
+ const filePath = path21.join(projectRoot2, "CLAUDE.md");
17766
18063
  const content = `@.claude/CLAUDE.md
17767
18064
 
17768
18065
  # Wairon SDD Project
@@ -17775,42 +18072,42 @@ To design or modify the system, invoke the **\`sdd-architect\`** skill
17775
18072
  (in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
17776
18073
  the \`wairon\` CLI is the human developer's tool, not yours.
17777
18074
  `;
17778
- fs15.writeFileSync(filePath, content, "utf-8");
18075
+ fs16.writeFileSync(filePath, content, "utf-8");
17779
18076
  } else if (targetType === "gemini" || targetType === "agy") {
17780
- const filePath = path19.join(projectRoot2, "GEMINI.md");
18077
+ const filePath = path21.join(projectRoot2, "GEMINI.md");
17781
18078
  const content = `# Wairon SDD Project
17782
18079
  ${GUIDE_MARKER_START}
17783
18080
  ${versionStamp()}
17784
18081
  ${LOCAL_GUIDE_BODY}
17785
18082
  ${GUIDE_MARKER_END}
17786
18083
  `;
17787
- fs15.writeFileSync(filePath, content, "utf-8");
18084
+ fs16.writeFileSync(filePath, content, "utf-8");
17788
18085
  } else if (targetType === "cursor") {
17789
- const filePath = path19.join(projectRoot2, ".cursorrules");
18086
+ const filePath = path21.join(projectRoot2, ".cursorrules");
17790
18087
  const content = `# Wairon SDD Project
17791
18088
 
17792
18089
  This project uses the Wairon Spec-Driven Development (SDD) framework.
17793
18090
 
17794
18091
  Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
17795
18092
  `;
17796
- fs15.writeFileSync(filePath, content, "utf-8");
18093
+ fs16.writeFileSync(filePath, content, "utf-8");
17797
18094
  } else if (targetType === "copilot") {
17798
- const filePath = path19.join(projectRoot2, ".github", "copilot-instructions.md");
17799
- fs15.mkdirSync(path19.dirname(filePath), { recursive: true });
18095
+ const filePath = path21.join(projectRoot2, ".github", "copilot-instructions.md");
18096
+ fs16.mkdirSync(path21.dirname(filePath), { recursive: true });
17800
18097
  const content = `# Wairon SDD Project
17801
18098
 
17802
18099
  This project uses the Wairon Spec-Driven Development (SDD) framework.
17803
18100
 
17804
18101
  Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
17805
18102
  `;
17806
- fs15.writeFileSync(filePath, content, "utf-8");
18103
+ fs16.writeFileSync(filePath, content, "utf-8");
17807
18104
  } else if (targetType === "codex") {
17808
- const filePath = path19.join(projectRoot2, ".codexrules");
18105
+ const filePath = path21.join(projectRoot2, ".codexrules");
17809
18106
  const content = `# Wairon SDD Project
17810
18107
 
17811
18108
  Refer to [.codex/agents/](.codex/agents/) for full instructions.
17812
18109
  `;
17813
- fs15.writeFileSync(filePath, content, "utf-8");
18110
+ fs16.writeFileSync(filePath, content, "utf-8");
17814
18111
  }
17815
18112
  }
17816
18113
  function reinjectLocalGuides(projectRoot2, targetTypes) {
@@ -17826,13 +18123,13 @@ function reinjectLocalGuides(projectRoot2, targetTypes) {
17826
18123
  }
17827
18124
  return written;
17828
18125
  }
17829
- var fs15, os5, path19, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
18126
+ var fs16, os6, path21, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
17830
18127
  var init_ai_guide = __esm({
17831
18128
  "src/utils/ai-guide.ts"() {
17832
18129
  "use strict";
17833
- fs15 = __toESM(require("fs"));
17834
- os5 = __toESM(require("os"));
17835
- path19 = __toESM(require("path"));
18130
+ fs16 = __toESM(require("fs"));
18131
+ os6 = __toESM(require("os"));
18132
+ path21 = __toESM(require("path"));
17836
18133
  init_stamp();
17837
18134
  GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
17838
18135
  GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
@@ -17894,7 +18191,7 @@ __export(domains_exports, {
17894
18191
  resolveDomains: () => resolveDomains
17895
18192
  });
17896
18193
  function rel(p) {
17897
- return path24.relative(process.cwd(), p).replace(/\\/g, "/");
18194
+ return path26.relative(process.cwd(), p).replace(/\\/g, "/");
17898
18195
  }
17899
18196
  function deriveSubsystemDomains() {
17900
18197
  const subsystems = loadSubsystemSpecs();
@@ -17944,11 +18241,11 @@ function removeFreeStandingDomain(id) {
17944
18241
  config.domains.splice(idx, 1);
17945
18242
  saveTopologyConfig(config);
17946
18243
  }
17947
- var path24;
18244
+ var path26;
17948
18245
  var init_domains = __esm({
17949
18246
  "src/core/domains.ts"() {
17950
18247
  "use strict";
17951
- path24 = __toESM(require("path"));
18248
+ path26 = __toESM(require("path"));
17952
18249
  init_loader();
17953
18250
  init_specs2();
17954
18251
  init_errors();
@@ -18104,16 +18401,16 @@ function packNamespace(pack) {
18104
18401
  return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
18105
18402
  }
18106
18403
  function extensionsFor(builtin, packSkills) {
18107
- return packSkills.filter((s) => s.extends === builtin && fs17.existsSync(s.sourcePath));
18404
+ return packSkills.filter((s) => s.extends === builtin && fs18.existsSync(s.sourcePath));
18108
18405
  }
18109
18406
  function composeBuiltinSkill(name, packSkills) {
18110
18407
  const srcPath = skillTemplatePath(name);
18111
- const base = fs17.existsSync(srcPath) ? fs17.readFileSync(srcPath, "utf-8") : "";
18408
+ const base = fs18.existsSync(srcPath) ? fs18.readFileSync(srcPath, "utf-8") : "";
18112
18409
  const sections = extensionsFor(name, packSkills);
18113
18410
  if (sections.length === 0) return base;
18114
18411
  const parts = [base.trimEnd()];
18115
18412
  for (const section of sections) {
18116
- const body = fs17.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
18413
+ const body = fs18.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
18117
18414
  parts.push(`## Platform: ${section.pack}`, body.trim());
18118
18415
  }
18119
18416
  return `${parts.join("\n\n")}
@@ -18129,16 +18426,16 @@ function readFrontmatter(raw, fallbackName) {
18129
18426
  return { name: field("name") || fallbackName, description: field("description") };
18130
18427
  }
18131
18428
  function builtinSkillsDir() {
18132
- return path25.resolve(__dirname, "..", "templates", "skills");
18429
+ return path27.resolve(__dirname, "..", "templates", "skills");
18133
18430
  }
18134
18431
  function skillTemplatePath(name) {
18135
- return path25.join(builtinSkillsDir(), `${name}.md`);
18432
+ return path27.join(builtinSkillsDir(), `${name}.md`);
18136
18433
  }
18137
18434
  function skillDestPath(type, destDir, name) {
18138
18435
  if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
18139
- return path25.join(destDir, name, "SKILL.md");
18436
+ return path27.join(destDir, name, "SKILL.md");
18140
18437
  }
18141
- return path25.join(destDir, `${name}.md`);
18438
+ return path27.join(destDir, `${name}.md`);
18142
18439
  }
18143
18440
  function skillsDirForTarget(type) {
18144
18441
  switch (type) {
@@ -18173,22 +18470,22 @@ function exportSddSkills(targetTypes) {
18173
18470
  ensureDir(destDir);
18174
18471
  destinations.push(destDir);
18175
18472
  for (const name of SKILL_NAMES) {
18176
- if (!fs17.existsSync(skillTemplatePath(name))) continue;
18473
+ if (!fs18.existsSync(skillTemplatePath(name))) continue;
18177
18474
  const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
18178
18475
  const destPath = skillDestPath(type, destDir, name);
18179
- ensureDir(path25.dirname(destPath));
18180
- fs17.writeFileSync(destPath, content, "utf-8");
18476
+ ensureDir(path27.dirname(destPath));
18477
+ fs18.writeFileSync(destPath, content, "utf-8");
18181
18478
  fileCount++;
18182
18479
  }
18183
18480
  for (const skill of packSkills) {
18184
18481
  if (skill.extends !== void 0) continue;
18185
18482
  if (!skill.targets.includes(type)) continue;
18186
- if (!fs17.existsSync(skill.sourcePath)) continue;
18483
+ if (!fs18.existsSync(skill.sourcePath)) continue;
18187
18484
  const id = packSkillId(skill);
18188
- const content = fs17.readFileSync(skill.sourcePath, "utf-8");
18485
+ const content = fs18.readFileSync(skill.sourcePath, "utf-8");
18189
18486
  const destPath = skillDestPath(type, destDir, id);
18190
- ensureDir(path25.dirname(destPath));
18191
- fs17.writeFileSync(destPath, content, "utf-8");
18487
+ ensureDir(path27.dirname(destPath));
18488
+ fs18.writeFileSync(destPath, content, "utf-8");
18192
18489
  fileCount++;
18193
18490
  }
18194
18491
  }
@@ -18201,12 +18498,12 @@ function checkSkillFreshness(type) {
18201
18498
  const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
18202
18499
  for (const name of SKILL_NAMES) {
18203
18500
  const destPath = skillDestPath(type, dir, name);
18204
- if (!fs17.existsSync(destPath)) {
18501
+ if (!fs18.existsSync(destPath)) {
18205
18502
  result.missing.push(name);
18206
18503
  continue;
18207
18504
  }
18208
18505
  const want = composeBuiltinSkill(name, packSkills);
18209
- const have = fs17.readFileSync(destPath, "utf-8");
18506
+ const have = fs18.readFileSync(destPath, "utf-8");
18210
18507
  if (have === want) result.ok.push(name);
18211
18508
  else result.stale.push(name);
18212
18509
  }
@@ -18218,7 +18515,7 @@ function activeTargetTypes() {
18218
18515
  return config.targets.filter((t) => !("enabled" in t) || t.enabled).map((t) => typeof t === "string" ? t : t.type);
18219
18516
  }
18220
18517
  function readSkillFrontmatter(name) {
18221
- return readFrontmatter(fs17.readFileSync(skillTemplatePath(name), "utf-8"), name);
18518
+ return readFrontmatter(fs18.readFileSync(skillTemplatePath(name), "utf-8"), name);
18222
18519
  }
18223
18520
  function listSkillResources() {
18224
18521
  const builtin = RESOURCE_SKILL_IDS.map((id) => {
@@ -18232,9 +18529,9 @@ function listSkillResources() {
18232
18529
  defaultForHostedMcp: true
18233
18530
  };
18234
18531
  });
18235
- const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs17.existsSync(s.sourcePath)).map((skill) => {
18532
+ const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs18.existsSync(s.sourcePath)).map((skill) => {
18236
18533
  const id = packSkillId(skill);
18237
- const fm = readFrontmatter(fs17.readFileSync(skill.sourcePath, "utf-8"), id);
18534
+ const fm = readFrontmatter(fs18.readFileSync(skill.sourcePath, "utf-8"), id);
18238
18535
  return {
18239
18536
  id,
18240
18537
  name: fm.name,
@@ -18250,8 +18547,8 @@ function readSkillResource(resourceId) {
18250
18547
  const packSkills = loadProjectExtensions2().skills;
18251
18548
  if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
18252
18549
  const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
18253
- if (packSkill) return fs17.readFileSync(packSkill.sourcePath, "utf-8");
18254
- return fs17.readFileSync(skillTemplatePath(resourceId), "utf-8");
18550
+ if (packSkill) return fs18.readFileSync(packSkill.sourcePath, "utf-8");
18551
+ return fs18.readFileSync(skillTemplatePath(resourceId), "utf-8");
18255
18552
  }
18256
18553
  function listResources() {
18257
18554
  return listSkillResources();
@@ -18264,12 +18561,12 @@ function readResource(resourceId) {
18264
18561
  if (!known) throw new SkillResourceNotFoundError(resourceId);
18265
18562
  return readSkillResource(resourceId);
18266
18563
  }
18267
- var path25, fs17, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
18564
+ var path27, fs18, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
18268
18565
  var init_skills = __esm({
18269
18566
  "src/core/skills.ts"() {
18270
18567
  "use strict";
18271
- path25 = __toESM(require("path"));
18272
- fs17 = __toESM(require("fs"));
18568
+ path27 = __toESM(require("path"));
18569
+ fs18 = __toESM(require("fs"));
18273
18570
  init_fs();
18274
18571
  init_defaults();
18275
18572
  init_extensions();
@@ -18496,23 +18793,34 @@ async function runStatus(options = {}) {
18496
18793
  const lock = lockLine().trim();
18497
18794
  if (lock) {
18498
18795
  logger.blank();
18499
- if (lock.includes("STALE")) logger.warn(lock);
18796
+ if (lock.includes("changed since approval")) logger.warn(lock);
18500
18797
  else logger.info(lock);
18501
18798
  }
18502
18799
  logger.blank();
18503
18800
  }
18504
18801
  function lockLine() {
18505
18802
  try {
18506
- const { state, record: record2 } = readLockState();
18507
- if (state === "unlocked") return "";
18508
- if (state === "locked") {
18803
+ const baseline = readBaseline();
18804
+ if (!baseline) return "";
18805
+ const moved = movedChildren(loadSubsystemSpecs());
18806
+ const childNote = moved.length ? `
18807
+ ${moved.length} chained child project(s) moved since approval: ${moved.map((m) => m.id).join(", ")}.` : "";
18808
+ const diff = diffAgainstBaseline();
18809
+ if (!diff || diffSize(diff) === 0) {
18509
18810
  return `
18510
- Lock: FROZEN at ${record2.lockedAt} by ${record2.lockedBy} (wairon ${record2.validatorVersion}).
18811
+ Approved: ${baseline.approvedAt} by ${baseline.approvedBy} \u2014 no spec has changed since.${childNote}
18511
18812
  `;
18512
18813
  }
18814
+ const parts = [];
18815
+ if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
18816
+ if (diff.added.length) parts.push(`${diff.added.length} added`);
18817
+ if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
18818
+ const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
18819
+ const rest = diffSize(diff) - named.length;
18513
18820
  return `
18514
- Lock: STALE \u2014 the specs or the governing doctrine changed since ${record2.lockedAt}, so this lock no longer holds and promotion will refuse it. Re-run \`wairon lock\` to freeze the current state.
18515
- `;
18821
+ ${diffSize(diff)} spec(s) changed since approval (${parts.join(", ")}) \u2014 approved ${baseline.approvedAt} by ${baseline.approvedBy}:
18822
+ ` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
18823
+ \u2026 and ${rest} more` : "") + childNote + "\n";
18516
18824
  } catch {
18517
18825
  return "";
18518
18826
  }
@@ -18639,6 +18947,7 @@ var init_status = __esm({
18639
18947
  init_loader();
18640
18948
  init_fs();
18641
18949
  init_specs2();
18950
+ init_baseline();
18642
18951
  }
18643
18952
  });
18644
18953
 
@@ -18687,7 +18996,7 @@ function errText(message) {
18687
18996
  }
18688
18997
  function captureBuildStamp(entryPath) {
18689
18998
  try {
18690
- const s = fs18.statSync(entryPath);
18999
+ const s = fs19.statSync(entryPath);
18691
19000
  return { path: entryPath, mtimeMs: s.mtimeMs, size: s.size };
18692
19001
  } catch {
18693
19002
  return null;
@@ -18696,7 +19005,7 @@ function captureBuildStamp(entryPath) {
18696
19005
  function isBuildStale(stamp) {
18697
19006
  if (!stamp) return false;
18698
19007
  try {
18699
- const s = fs18.statSync(stamp.path);
19008
+ const s = fs19.statSync(stamp.path);
18700
19009
  return s.mtimeMs !== stamp.mtimeMs || s.size !== stamp.size;
18701
19010
  } catch {
18702
19011
  return false;
@@ -19914,10 +20223,6 @@ NOTICE:
19914
20223
  description: "Hosted project lifecycle (execute-primary): LOCK the bound project (validate-as-complete gate + commit-scoped lock record). Executes directly when your resolved permission is yes and returns the completed outcome; when it is approval, a pending approval request is created instead (await it with sdd_host_await_approval).",
19915
20224
  inputSchema: {}
19916
20225
  }, hostedStub);
19917
- reg(server, "sdd_host_promote_project", {
19918
- description: "Hosted project lifecycle (execute-primary): PROMOTE the bound, locked project after a StateId re-check. Executes directly when your resolved permission is yes; when it is approval, a pending approval request is created instead.",
19919
- inputSchema: {}
19920
- }, hostedStub);
19921
20226
  reg(server, "sdd_host_initialize_project", {
19922
20227
  description: "Hosted project lifecycle (execute-primary): initialize a new hosted project into its REQUIRED owner organization unit (with an optional profile selection). Executes directly when your resolved permission is yes; when it is approval, a pending approval request is created instead.",
19923
20228
  inputSchema: {
@@ -20003,7 +20308,7 @@ async function scopeToClientWorkspace(server) {
20003
20308
  } catch {
20004
20309
  dir = null;
20005
20310
  }
20006
- if (dir && (fs18.existsSync(path27.join(dir, ".wai")) || fs18.existsSync(path27.join(dir, ".wairon")))) {
20311
+ if (dir && (fs19.existsSync(path29.join(dir, ".wai")) || fs19.existsSync(path29.join(dir, ".wairon")))) {
20007
20312
  setProjectRoot(dir);
20008
20313
  process.stderr.write(`[wairon mcp] scoped to client workspace root: ${dir}
20009
20314
  `);
@@ -20027,7 +20332,7 @@ async function startMcpServer() {
20027
20332
  } catch {
20028
20333
  }
20029
20334
  }
20030
- var import_mcp, import_stdio, import_zod9, import_types3, fs18, path27, import_url, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, STORE_MANAGED_FIELDS, ALWAYS_CARRIED_FIELDS, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
20335
+ var import_mcp, import_stdio, import_zod9, import_types3, fs19, path29, import_url, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, STORE_MANAGED_FIELDS, ALWAYS_CARRIED_FIELDS, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
20031
20336
  var init_server = __esm({
20032
20337
  "src/mcp/server.ts"() {
20033
20338
  "use strict";
@@ -20035,8 +20340,8 @@ var init_server = __esm({
20035
20340
  import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
20036
20341
  import_zod9 = require("zod");
20037
20342
  import_types3 = require("@modelcontextprotocol/sdk/types.js");
20038
- fs18 = __toESM(require("fs"));
20039
- path27 = __toESM(require("path"));
20343
+ fs19 = __toESM(require("fs"));
20344
+ path29 = __toESM(require("path"));
20040
20345
  import_url = require("url");
20041
20346
  init_fs();
20042
20347
  init_defaults();
@@ -20087,34 +20392,34 @@ __export(mcp_exports, {
20087
20392
  validateConfigDir: () => validateConfigDir
20088
20393
  });
20089
20394
  function geminiGlobalDir(override) {
20090
- return override || process.env["GEMINI_CONFIG_DIR"] || path28.join(os6.homedir(), ".gemini");
20395
+ return override || process.env["GEMINI_CONFIG_DIR"] || path30.join(os7.homedir(), ".gemini");
20091
20396
  }
20092
20397
  function claudeMcpConfigPath(useGlobal, override) {
20093
- if (!useGlobal) return path28.join(process.cwd(), ".mcp.json");
20398
+ if (!useGlobal) return path30.join(process.cwd(), ".mcp.json");
20094
20399
  const dir = override || process.env["CLAUDE_CONFIG_DIR"];
20095
- return dir ? path28.join(dir, ".claude.json") : path28.join(os6.homedir(), ".claude.json");
20400
+ return dir ? path30.join(dir, ".claude.json") : path30.join(os7.homedir(), ".claude.json");
20096
20401
  }
20097
20402
  function validateConfigDir(dir, backend) {
20098
- const resolved = path28.resolve(dir);
20403
+ const resolved = path30.resolve(dir);
20099
20404
  const agent = backend === "claude" ? "Claude" : "Gemini/Antigravity";
20100
- if (!fs19.existsSync(resolved)) {
20101
- const parent = path28.dirname(resolved);
20102
- if (!fs19.existsSync(parent)) {
20405
+ if (!fs20.existsSync(resolved)) {
20406
+ const parent = path30.dirname(resolved);
20407
+ if (!fs20.existsSync(parent)) {
20103
20408
  throw new WaironError(`--config-dir "${dir}" does not exist and its parent is missing \u2014 check the path.`);
20104
20409
  }
20105
20410
  logger.warn(`Config dir "${resolved}" does not exist yet; it will be created.`);
20106
20411
  return;
20107
20412
  }
20108
- if (!fs19.statSync(resolved).isDirectory()) {
20413
+ if (!fs20.statSync(resolved).isDirectory()) {
20109
20414
  throw new WaironError(`--config-dir "${dir}" is not a directory.`);
20110
20415
  }
20111
20416
  const markers = backend === "claude" ? ["settings.json", "settings.local.json", ".credentials.json", "projects", "statsig", "todos", "shell-snapshots", "CLAUDE.md"] : ["settings.json", "GEMINI.md", "oauth_creds.json", "antigravity-cli", "tmp"];
20112
- const entries = fs19.readdirSync(resolved);
20417
+ const entries = fs20.readdirSync(resolved);
20113
20418
  if (entries.length === 0) {
20114
20419
  logger.warn(`Config dir "${resolved}" is empty; proceeding (treating it as a fresh ${agent} config dir).`);
20115
20420
  return;
20116
20421
  }
20117
- if (!markers.some((m) => fs19.existsSync(path28.join(resolved, m)))) {
20422
+ if (!markers.some((m) => fs20.existsSync(path30.join(resolved, m)))) {
20118
20423
  throw new WaironError(
20119
20424
  `"${dir}" does not look like a ${agent} config directory (none of ${markers.slice(0, 4).join(", ")} found). Point --config-dir at the agent's config directory.`
20120
20425
  );
@@ -20122,12 +20427,12 @@ function validateConfigDir(dir, backend) {
20122
20427
  }
20123
20428
  async function runMcpServe() {
20124
20429
  const { setProjectRoot: setProjectRoot2, getProjectRoot: getProjectRoot2, findProjectRoot: findProjectRoot2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
20125
- const cwd = path28.resolve(process.cwd());
20430
+ const cwd = path30.resolve(process.cwd());
20126
20431
  const envDir = process.env["WAIRON_PROJECT_DIR"];
20127
20432
  let resolved;
20128
20433
  let how;
20129
- if (envDir && fs19.existsSync(path28.join(envDir, ".wai"))) {
20130
- resolved = path28.resolve(envDir);
20434
+ if (envDir && fs20.existsSync(path30.join(envDir, ".wai"))) {
20435
+ resolved = path30.resolve(envDir);
20131
20436
  how = "WAIRON_PROJECT_DIR (pinned at install)";
20132
20437
  } else {
20133
20438
  const found = findProjectRoot2(cwd);
@@ -20190,20 +20495,20 @@ async function runMcpInstall(options = {}) {
20190
20495
  let settingsPath;
20191
20496
  if (backend === "gemini") {
20192
20497
  if (useGlobal) {
20193
- configBase = path28.join(geminiGlobalDir(options.configDir), "antigravity-cli");
20194
- settingsPath = path28.join(configBase, "mcp_config.json");
20498
+ configBase = path30.join(geminiGlobalDir(options.configDir), "antigravity-cli");
20499
+ settingsPath = path30.join(configBase, "mcp_config.json");
20195
20500
  } else {
20196
- configBase = path28.join(process.cwd(), ".gemini");
20197
- settingsPath = path28.join(configBase, "settings.json");
20501
+ configBase = path30.join(process.cwd(), ".gemini");
20502
+ settingsPath = path30.join(configBase, "settings.json");
20198
20503
  }
20199
20504
  } else {
20200
20505
  settingsPath = claudeMcpConfigPath(useGlobal, options.configDir);
20201
- configBase = path28.dirname(settingsPath);
20506
+ configBase = path30.dirname(settingsPath);
20202
20507
  }
20203
20508
  let settings = {};
20204
- if (fs19.existsSync(settingsPath)) {
20509
+ if (fs20.existsSync(settingsPath)) {
20205
20510
  try {
20206
- settings = JSON.parse(fs19.readFileSync(settingsPath, "utf8"));
20511
+ settings = JSON.parse(fs20.readFileSync(settingsPath, "utf8"));
20207
20512
  } catch {
20208
20513
  logger.warn(`Could not parse ${settingsPath} \u2014 starting fresh.`);
20209
20514
  }
@@ -20211,7 +20516,7 @@ async function runMcpInstall(options = {}) {
20211
20516
  const mcpServers = settings["mcpServers"] ?? {};
20212
20517
  const agentLabel = backend === "gemini" ? "Antigravity" : "Claude";
20213
20518
  const isPackaged = typeof process.pkg !== "undefined";
20214
- const scriptPath = process.argv[1] ? path28.resolve(process.argv[1]).replace(/\\/g, "/") : null;
20519
+ const scriptPath = process.argv[1] ? path30.resolve(process.argv[1]).replace(/\\/g, "/") : null;
20215
20520
  const useDirectNode = !isPackaged && scriptPath && (scriptPath.endsWith(".js") || scriptPath.endsWith(".ts"));
20216
20521
  const env = useGlobal ? {} : { WAIRON_PROJECT_DIR: process.cwd().replace(/\\/g, "/") };
20217
20522
  const desiredEntry = useDirectNode ? { command: "node", args: [scriptPath, "mcp", "serve"], env } : { command: "wairon", args: ["mcp", "serve"], env };
@@ -20223,8 +20528,8 @@ async function runMcpInstall(options = {}) {
20223
20528
  const wasStale = !!existingEntry;
20224
20529
  mcpServers["wairon"] = desiredEntry;
20225
20530
  settings["mcpServers"] = mcpServers;
20226
- if (!fs19.existsSync(configBase)) fs19.mkdirSync(configBase, { recursive: true });
20227
- fs19.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
20531
+ if (!fs20.existsSync(configBase)) fs20.mkdirSync(configBase, { recursive: true });
20532
+ fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
20228
20533
  logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk4.default.cyan(settingsPath)}.`);
20229
20534
  logger.blank();
20230
20535
  logger.info("AI tools using this config will have access to these wairon tools:");
@@ -20249,8 +20554,8 @@ async function runMcpStatus() {
20249
20554
  const projectConfig = loadProjectConfig();
20250
20555
  const claudeProject = claudeMcpConfigPath(false);
20251
20556
  const claudeGlobal = claudeMcpConfigPath(true);
20252
- const geminiProject = path28.join(process.cwd(), ".gemini", "settings.json");
20253
- const geminiGlobal = path28.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
20557
+ const geminiProject = path30.join(process.cwd(), ".gemini", "settings.json");
20558
+ const geminiGlobal = path30.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
20254
20559
  logger.blank();
20255
20560
  logger.info(`${import_chalk4.default.bold("wairon MCP Server")}`);
20256
20561
  logger.blank();
@@ -20261,12 +20566,12 @@ async function runMcpStatus() {
20261
20566
  { label: "Antigravity (global)", filePath: geminiGlobal, fallbackName: "mcp_config.json" }
20262
20567
  ];
20263
20568
  for (const { label, filePath, fallbackName } of checks) {
20264
- if (!fs19.existsSync(filePath)) {
20569
+ if (!fs20.existsSync(filePath)) {
20265
20570
  console.log(` ${label}: ${import_chalk4.default.gray(`${fallbackName} not found`)}`);
20266
20571
  continue;
20267
20572
  }
20268
20573
  try {
20269
- const s = JSON.parse(fs19.readFileSync(filePath, "utf8"));
20574
+ const s = JSON.parse(fs20.readFileSync(filePath, "utf8"));
20270
20575
  const registered = !!s["mcpServers"]?.["wairon"];
20271
20576
  const mark = registered ? import_chalk4.default.green("\u2713 registered") : import_chalk4.default.gray("not registered");
20272
20577
  console.log(` ${label}: ${mark} ${import_chalk4.default.gray(filePath)}`);
@@ -20282,16 +20587,16 @@ async function runMcpStatus() {
20282
20587
  logger.info(`To start manually: ${import_chalk4.default.bold("wairon mcp serve")}`);
20283
20588
  logger.blank();
20284
20589
  const mcpDir = aiDir("mcp");
20285
- if (fs19.existsSync(mcpDir)) {
20590
+ if (fs20.existsSync(mcpDir)) {
20286
20591
  logger.info(`MCP state dir: ${import_chalk4.default.gray(mcpDir)}`);
20287
20592
  }
20288
20593
  }
20289
20594
  function removeLegacyGlobalPlugin() {
20290
- const home = process.env["USERPROFILE"] ?? process.env["HOME"] ?? os6.homedir();
20291
- const pluginDir = path28.join(home, ".gemini", "config", "plugins", "wairon");
20595
+ const home = process.env["USERPROFILE"] ?? process.env["HOME"] ?? os7.homedir();
20596
+ const pluginDir = path30.join(home, ".gemini", "config", "plugins", "wairon");
20292
20597
  try {
20293
- if (fs19.existsSync(pluginDir)) {
20294
- fs19.rmSync(pluginDir, { recursive: true, force: true });
20598
+ if (fs20.existsSync(pluginDir)) {
20599
+ fs20.rmSync(pluginDir, { recursive: true, force: true });
20295
20600
  logger.info(`Removed legacy global Antigravity plugin at ${import_chalk4.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
20296
20601
  return true;
20297
20602
  }
@@ -20300,13 +20605,13 @@ function removeLegacyGlobalPlugin() {
20300
20605
  }
20301
20606
  return false;
20302
20607
  }
20303
- var fs19, os6, path28, import_chalk4;
20608
+ var fs20, os7, path30, import_chalk4;
20304
20609
  var init_mcp = __esm({
20305
20610
  "src/commands/mcp.ts"() {
20306
20611
  "use strict";
20307
- fs19 = __toESM(require("fs"));
20308
- os6 = __toESM(require("os"));
20309
- path28 = __toESM(require("path"));
20612
+ fs20 = __toESM(require("fs"));
20613
+ os7 = __toESM(require("os"));
20614
+ path30 = __toESM(require("path"));
20310
20615
  import_chalk4 = __toESM(require("chalk"));
20311
20616
  init_logger();
20312
20617
  init_loader();
@@ -23141,8 +23446,8 @@ var require_dist = __commonJS({
23141
23446
  function slug(name) {
23142
23447
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
23143
23448
  }
23144
- var fs53 = __toESM2(require("fs"));
23145
- var path64 = __toESM2(require("path"));
23449
+ var fs54 = __toESM2(require("fs"));
23450
+ var path67 = __toESM2(require("path"));
23146
23451
  var import_fflate = require_node();
23147
23452
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
23148
23453
  function listEntries(archive) {
@@ -23180,9 +23485,9 @@ var require_dist = __commonJS({
23180
23485
  }
23181
23486
  function writeTree(destDir, files) {
23182
23487
  for (const file of files) {
23183
- const absolute = path64.join(destDir, file.path);
23184
- fs53.mkdirSync(path64.dirname(absolute), { recursive: true });
23185
- fs53.writeFileSync(absolute, file.contents);
23488
+ const absolute = path67.join(destDir, file.path);
23489
+ fs54.mkdirSync(path67.dirname(absolute), { recursive: true });
23490
+ fs54.writeFileSync(absolute, file.contents);
23186
23491
  }
23187
23492
  }
23188
23493
  function classifyKind(name, symlinks) {
@@ -23191,14 +23496,14 @@ var require_dist = __commonJS({
23191
23496
  return "file";
23192
23497
  }
23193
23498
  function walkPackDir(root, current2, out) {
23194
- for (const entry of fs53.readdirSync(current2, { withFileTypes: true })) {
23499
+ for (const entry of fs54.readdirSync(current2, { withFileTypes: true })) {
23195
23500
  if (entry.isDirectory()) {
23196
23501
  if (SKIP_DIRS.has(entry.name)) continue;
23197
- walkPackDir(root, path64.join(current2, entry.name), out);
23502
+ walkPackDir(root, path67.join(current2, entry.name), out);
23198
23503
  } else if (entry.isFile()) {
23199
- const absolute = path64.join(current2, entry.name);
23200
- const relative22 = path64.relative(root, absolute).split(path64.sep).join("/");
23201
- out.push({ path: relative22, contents: fs53.readFileSync(absolute) });
23504
+ const absolute = path67.join(current2, entry.name);
23505
+ const relative22 = path67.relative(root, absolute).split(path67.sep).join("/");
23506
+ out.push({ path: relative22, contents: fs54.readFileSync(absolute) });
23202
23507
  }
23203
23508
  }
23204
23509
  }
@@ -23367,7 +23672,7 @@ var require_dist = __commonJS({
23367
23672
  });
23368
23673
 
23369
23674
  // src/cli/index.ts
23370
- var path63 = __toESM(require("path"));
23675
+ var path66 = __toESM(require("path"));
23371
23676
  var import_commander = require("commander");
23372
23677
  init_defaults();
23373
23678
  init_logger();
@@ -23594,7 +23899,7 @@ function isSupportedAlias(name) {
23594
23899
  }
23595
23900
 
23596
23901
  // src/commands/init.ts
23597
- var path29 = __toESM(require("path"));
23902
+ var path31 = __toESM(require("path"));
23598
23903
  var import_chalk5 = __toESM(require("chalk"));
23599
23904
  var import_inquirer = __toESM(require("inquirer"));
23600
23905
  init_logger();
@@ -23615,7 +23920,7 @@ var WAIRON_MANAGED_MARKER = "wairon:managed";
23615
23920
  var WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
23616
23921
 
23617
23922
  // src/exporters/claude.ts
23618
- var path20 = __toESM(require("path"));
23923
+ var path22 = __toESM(require("path"));
23619
23924
  init_fs();
23620
23925
  var ClaudeExporter = class {
23621
23926
  constructor() {
@@ -23624,7 +23929,7 @@ var ClaudeExporter = class {
23624
23929
  outputPath(ctx) {
23625
23930
  const { agent, target, projectRoot: projectRoot2 } = ctx;
23626
23931
  const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
23627
- return path20.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23932
+ return path22.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23628
23933
  }
23629
23934
  export(ctx) {
23630
23935
  const { agent, renderedInstructions } = ctx;
@@ -23645,7 +23950,7 @@ var ClaudeExporter = class {
23645
23950
  };
23646
23951
 
23647
23952
  // src/exporters/custom.ts
23648
- var path21 = __toESM(require("path"));
23953
+ var path23 = __toESM(require("path"));
23649
23954
  init_fs();
23650
23955
  var CustomExporter = class {
23651
23956
  constructor() {
@@ -23656,7 +23961,7 @@ var CustomExporter = class {
23656
23961
  if (!("outputDir" in target)) {
23657
23962
  throw new Error("CustomExporter requires target.outputDir");
23658
23963
  }
23659
- return path21.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23964
+ return path23.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23660
23965
  }
23661
23966
  export(ctx) {
23662
23967
  const { agent, target, renderedInstructions } = ctx;
@@ -23679,7 +23984,7 @@ var CustomExporter = class {
23679
23984
  };
23680
23985
 
23681
23986
  // src/exporters/gemini.ts
23682
- var path22 = __toESM(require("path"));
23987
+ var path24 = __toESM(require("path"));
23683
23988
  init_fs();
23684
23989
  var GeminiExporter = class {
23685
23990
  constructor() {
@@ -23688,7 +23993,7 @@ var GeminiExporter = class {
23688
23993
  outputPath(ctx) {
23689
23994
  const { agent, target, projectRoot: projectRoot2 } = ctx;
23690
23995
  const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
23691
- return path22.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
23996
+ return path24.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
23692
23997
  }
23693
23998
  export(ctx) {
23694
23999
  const { agent, renderedInstructions } = ctx;
@@ -23713,11 +24018,11 @@ function yamlString(value) {
23713
24018
  }
23714
24019
 
23715
24020
  // src/exporters/generate.ts
23716
- var path26 = __toESM(require("path"));
24021
+ var path28 = __toESM(require("path"));
23717
24022
 
23718
24023
  // src/core/detection.ts
23719
- var fs16 = __toESM(require("fs"));
23720
- var path23 = __toESM(require("path"));
24024
+ var fs17 = __toESM(require("fs"));
24025
+ var path25 = __toESM(require("path"));
23721
24026
  init_defaults();
23722
24027
  var PACKAGE_MARKERS = [
23723
24028
  "package.json",
@@ -23769,7 +24074,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
23769
24074
  });
23770
24075
  }
23771
24076
  function parseGitmodules(filePath) {
23772
- const content = fs16.readFileSync(filePath, "utf-8");
24077
+ const content = fs17.readFileSync(filePath, "utf-8");
23773
24078
  const entries = [];
23774
24079
  let current2 = {};
23775
24080
  for (const line2 of content.split("\n")) {
@@ -23791,8 +24096,8 @@ function parseGitmodules(filePath) {
23791
24096
  return entries;
23792
24097
  }
23793
24098
  function detectGitSubmodules(projectRoot2) {
23794
- const gitmodulesPath = path23.join(projectRoot2, ".gitmodules");
23795
- if (!fs16.existsSync(gitmodulesPath)) return [];
24099
+ const gitmodulesPath = path25.join(projectRoot2, ".gitmodules");
24100
+ if (!fs17.existsSync(gitmodulesPath)) return [];
23796
24101
  return parseGitmodules(gitmodulesPath).map((entry) => ({
23797
24102
  suggestedId: pathToId(entry.path),
23798
24103
  suggestedName: pathToName(entry.path),
@@ -23810,18 +24115,18 @@ function walkForGit(projectRoot2, currentDir, depth, results) {
23810
24115
  if (depth > MAX_SCAN_DEPTH) return;
23811
24116
  let entries;
23812
24117
  try {
23813
- entries = fs16.readdirSync(currentDir, { withFileTypes: true });
24118
+ entries = fs17.readdirSync(currentDir, { withFileTypes: true });
23814
24119
  } catch {
23815
24120
  return;
23816
24121
  }
23817
24122
  for (const entry of entries) {
23818
24123
  if (!entry.isDirectory()) continue;
23819
24124
  if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
23820
- const fullPath = path23.join(currentDir, entry.name);
23821
- const relPath = normalizePath3(path23.relative(projectRoot2, fullPath));
24125
+ const fullPath = path25.join(currentDir, entry.name);
24126
+ const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
23822
24127
  if (relPath === "" || relPath === ".") continue;
23823
- const gitPath = path23.join(fullPath, ".git");
23824
- if (fs16.existsSync(gitPath)) {
24128
+ const gitPath = path25.join(fullPath, ".git");
24129
+ if (fs17.existsSync(gitPath)) {
23825
24130
  results.push({
23826
24131
  suggestedId: pathToId(relPath),
23827
24132
  suggestedName: pathToName(relPath),
@@ -23843,17 +24148,17 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
23843
24148
  if (depth > MAX_SCAN_DEPTH) return;
23844
24149
  let entries;
23845
24150
  try {
23846
- entries = fs16.readdirSync(currentDir, { withFileTypes: true });
24151
+ entries = fs17.readdirSync(currentDir, { withFileTypes: true });
23847
24152
  } catch {
23848
24153
  return;
23849
24154
  }
23850
24155
  for (const entry of entries) {
23851
24156
  if (!entry.isDirectory()) continue;
23852
24157
  if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
23853
- const fullPath = path23.join(currentDir, entry.name);
23854
- const relPath = normalizePath3(path23.relative(projectRoot2, fullPath));
24158
+ const fullPath = path25.join(currentDir, entry.name);
24159
+ const relPath = normalizePath3(path25.relative(projectRoot2, fullPath));
23855
24160
  if (relPath === "" || relPath === ".") continue;
23856
- const hasMarker = PACKAGE_MARKERS.some((m) => fs16.existsSync(path23.join(fullPath, m)));
24161
+ const hasMarker = PACKAGE_MARKERS.some((m) => fs17.existsSync(path25.join(fullPath, m)));
23857
24162
  if (hasMarker) {
23858
24163
  results.push({
23859
24164
  suggestedId: pathToId(relPath),
@@ -23867,8 +24172,8 @@ function walkForPackages(projectRoot2, currentDir, depth, results) {
23867
24172
  }
23868
24173
  }
23869
24174
  function pathToId(relPath) {
23870
- const basename13 = path23.basename(relPath);
23871
- return basename13.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
24175
+ const basename14 = path25.basename(relPath);
24176
+ return basename14.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
23872
24177
  }
23873
24178
  function pathToName(relPath) {
23874
24179
  const id = pathToId(relPath);
@@ -24095,7 +24400,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getPro
24095
24400
  const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
24096
24401
  if (!targetConfig) continue;
24097
24402
  const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
24098
- expected.add(path26.resolve(getExporter(targetConfig).outputPath(ctx)));
24403
+ expected.add(path28.resolve(getExporter(targetConfig).outputPath(ctx)));
24099
24404
  }
24100
24405
  }
24101
24406
  return expected;
@@ -24113,7 +24418,7 @@ async function runInit(options = {}) {
24113
24418
  logger.header("wairon init");
24114
24419
  const cwd = process.cwd();
24115
24420
  const ancestorRoot = findSystemRoot(cwd);
24116
- if (ancestorRoot && path29.resolve(ancestorRoot) === path29.resolve(cwd)) {
24421
+ if (ancestorRoot && path31.resolve(ancestorRoot) === path31.resolve(cwd)) {
24117
24422
  logger.info("Project already initialized.");
24118
24423
  logger.info("Design your spec tree with the SDD architect skill, then run `wairon generate`.");
24119
24424
  return;
@@ -24129,8 +24434,8 @@ async function runInit(options = {}) {
24129
24434
  await runInitInteractive();
24130
24435
  }
24131
24436
  async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
24132
- const relPath = path29.relative(parentRoot, cwd) || ".";
24133
- const defaultId = path29.basename(cwd);
24437
+ const relPath = path31.relative(parentRoot, cwd) || ".";
24438
+ const defaultId = path31.basename(cwd);
24134
24439
  logger.info(`Detected a parent wairon project at ${parentRoot}`);
24135
24440
  logger.info(`This directory ("${relPath}") is not yet a wairon project.`);
24136
24441
  if (!options.yes) {
@@ -24190,7 +24495,7 @@ async function runInitAsExternalSubsystem(parentRoot, cwd, options) {
24190
24495
  }
24191
24496
  async function runInitNonInteractive() {
24192
24497
  const cwd = process.cwd();
24193
- const projectName = path29.basename(cwd);
24498
+ const projectName = path31.basename(cwd);
24194
24499
  const now = (/* @__PURE__ */ new Date()).toISOString();
24195
24500
  const targets = [defaultTargetConfig("claude"), defaultTargetConfig("agy")];
24196
24501
  const projectConfig = buildProjectConfig(projectName, targets, now, "backend");
@@ -24212,7 +24517,7 @@ async function runInitNonInteractive() {
24212
24517
  }
24213
24518
  async function runInitInteractive() {
24214
24519
  const cwd = process.cwd();
24215
- const defaultProjectName = path29.basename(cwd);
24520
+ const defaultProjectName = path31.basename(cwd);
24216
24521
  const { projectName } = await import_inquirer.default.prompt([
24217
24522
  {
24218
24523
  type: "input",
@@ -24372,7 +24677,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
24372
24677
  projectRoot: projectRoot2,
24373
24678
  target: targetConfig
24374
24679
  });
24375
- logger.success(`Generated: ${path29.relative(cwd, result.outputPath)}`);
24680
+ logger.success(`Generated: ${path31.relative(cwd, result.outputPath)}`);
24376
24681
  }
24377
24682
  }
24378
24683
  writeStarterDocs(projectName);
@@ -24387,7 +24692,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
24387
24692
  if (guidePlan.claudeLocal) {
24388
24693
  const p = localGuideFilePath(projectRoot2, "claude");
24389
24694
  injectGuide(p, "local");
24390
- logger.success(`Injected wairon guide into ${path29.relative(cwd, p)}`);
24695
+ logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
24391
24696
  writeRootGuideDelegator(projectRoot2, "claude");
24392
24697
  logger.success(`Created root CLAUDE.md delegator pointing to .claude/CLAUDE.md`);
24393
24698
  }
@@ -24399,7 +24704,7 @@ async function executeInit(projectName, targets, projectConfig, guidePlan, now)
24399
24704
  if (guidePlan.geminiLocal) {
24400
24705
  const p = localGuideFilePath(projectRoot2, "gemini");
24401
24706
  injectGuide(p, "local");
24402
- logger.success(`Injected wairon guide into ${path29.relative(cwd, p)}`);
24707
+ logger.success(`Injected wairon guide into ${path31.relative(cwd, p)}`);
24403
24708
  writeRootGuideDelegator(projectRoot2, "gemini");
24404
24709
  logger.success(`Created root GEMINI.md delegator pointing to .gemini/GEMINI.md`);
24405
24710
  }
@@ -24632,8 +24937,8 @@ A new project initialized with Wairon.
24632
24937
  }
24633
24938
 
24634
24939
  // src/commands/generate.ts
24635
- var fs20 = __toESM(require("fs"));
24636
- var path30 = __toESM(require("path"));
24940
+ var fs21 = __toESM(require("fs"));
24941
+ var path32 = __toESM(require("path"));
24637
24942
  init_logger();
24638
24943
  init_loader();
24639
24944
  init_fs();
@@ -24641,29 +24946,29 @@ init_provision();
24641
24946
  init_specs2();
24642
24947
  var WAIRON_AGENT_FILE = /-(owner|implementer|architect)\.md$/;
24643
24948
  function pruneStaleAgents(expectedPaths, scanDirs) {
24644
- const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) => path30.dirname(p)));
24949
+ const dirs = new Set(scanDirs ?? [...expectedPaths].map((p) => path32.dirname(p)));
24645
24950
  let pruned = 0;
24646
24951
  for (const dir of dirs) {
24647
24952
  let entries;
24648
24953
  try {
24649
- entries = fs20.readdirSync(dir);
24954
+ entries = fs21.readdirSync(dir);
24650
24955
  } catch {
24651
24956
  continue;
24652
24957
  }
24653
24958
  for (const name of entries) {
24654
24959
  if (!name.endsWith(".md")) continue;
24655
- const full = path30.resolve(dir, name);
24960
+ const full = path32.resolve(dir, name);
24656
24961
  if (expectedPaths.has(full)) continue;
24657
24962
  let owned = WAIRON_AGENT_FILE.test(name);
24658
24963
  if (!owned) {
24659
24964
  try {
24660
- owned = fs20.readFileSync(full, "utf8").includes(WAIRON_MANAGED_MARKER);
24965
+ owned = fs21.readFileSync(full, "utf8").includes(WAIRON_MANAGED_MARKER);
24661
24966
  } catch {
24662
24967
  owned = false;
24663
24968
  }
24664
24969
  }
24665
24970
  if (!owned) continue;
24666
- fs20.unlinkSync(full);
24971
+ fs21.unlinkSync(full);
24667
24972
  pruned++;
24668
24973
  logger.verbose(`Pruned stale: ${full}`);
24669
24974
  }
@@ -24677,7 +24982,7 @@ async function runGenerate(options = {}) {
24677
24982
  const children = listDirectChainedSubprojects(getProjectRoot());
24678
24983
  for (const child of children) {
24679
24984
  logger.blank();
24680
- logger.info(`\u21B3 Chained subproject "${child.subsystemId}" \u2014 generating its layer in ${path30.relative(getProjectRoot(), child.dir) || "."}/`);
24985
+ logger.info(`\u21B3 Chained subproject "${child.subsystemId}" \u2014 generating its layer in ${path32.relative(getProjectRoot(), child.dir) || "."}/`);
24681
24986
  await runWithProjectRoot(child.dir, async () => {
24682
24987
  ensureProjectInitialized(child.subsystemId);
24683
24988
  invalidateSpecCache();
@@ -24698,7 +25003,7 @@ async function generateLayer(options = {}) {
24698
25003
  } else if (options.dryRun) {
24699
25004
  logger.info("Dry run \u2014 rules.materializeAgentFiles is off: no agent files are written; leftover managed files would be removed.");
24700
25005
  } else if (options.prune !== false) {
24701
- const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) => path30.dirname(p));
25006
+ const candidateDirs = [...resolveExpectedOutputPaths(registry.agents, projectConfig)].map((p) => path32.dirname(p));
24702
25007
  const removed = pruneStaleAgents(/* @__PURE__ */ new Set(), candidateDirs);
24703
25008
  if (removed > 0) {
24704
25009
  logger.info(`Removed ${removed} previously materialized agent file(s) \u2014 agents are served as live briefs (sdd_get_agent_brief); opt back in with rules.materializeAgentFiles: true.`);
@@ -24785,22 +25090,34 @@ function materializeAgentLayer(agents, projectConfig, options) {
24785
25090
  }
24786
25091
 
24787
25092
  // src/commands/lock.ts
24788
- var os7 = __toESM(require("os"));
25093
+ var os8 = __toESM(require("os"));
24789
25094
  var import_inquirer2 = __toESM(require("inquirer"));
24790
25095
  init_logger();
25096
+ init_baseline();
24791
25097
  init_defaults();
25098
+ var path33 = __toESM(require("path"));
25099
+ init_fs();
24792
25100
  async function runLock(options = {}, gate) {
24793
- const promotable = collectPromotableSpecs(options.subsystem);
24794
- if (promotable.length === 0) {
24795
- logger.info("All specs are already complete \u2014 this will re-validate and regenerate the agent topology.");
25101
+ const diff = diffAgainstBaseline();
25102
+ if (!diff) {
25103
+ logger.info("First approval of this tree \u2014 the whole spec tree becomes the approved baseline.");
25104
+ } else if (diffSize(diff) === 0) {
25105
+ logger.info("Nothing has changed since the last approval \u2014 this will re-validate and regenerate the agent topology.");
24796
25106
  } else {
24797
- logger.info(`${promotable.length} spec(s) will be frozen as complete:`);
24798
- for (const p of promotable) {
24799
- logger.info(` \u2022 ${p.kind.padEnd(14)} ${p.id} (${p.status} \u2192 complete)`);
25107
+ logger.info(`${diffSize(diff)} spec(s) changed since the last approval:`);
25108
+ for (const p of diff.changed) logger.info(` ~ ${p}`);
25109
+ for (const p of diff.added) logger.info(` + ${p}`);
25110
+ for (const p of diff.removed) logger.info(` - ${p}`);
25111
+ }
25112
+ const moved = movedChildren(loadSubsystemSpecs());
25113
+ if (moved.length > 0) {
25114
+ logger.info(`${moved.length} chained child project(s) moved since the last approval:`);
25115
+ for (const m of moved) {
25116
+ logger.info(` ${m.id}: ${m.pinned.slice(0, 19)}\u2026 \u2192 ${m.now ? `${m.now.slice(0, 19)}\u2026` : "(no approval)"}`);
24800
25117
  }
24801
25118
  }
24802
25119
  logger.blank();
24803
- logger.warn("This freezes the design as the source of truth and (re)generates the agent topology.");
25120
+ logger.warn("This records the current design as approved and (re)generates the agent topology.");
24804
25121
  if (!options.yes) {
24805
25122
  if (!process.stdin.isTTY) {
24806
25123
  logger.error("Non-interactive shell \u2014 re-run with --yes to confirm the lock.");
@@ -24810,24 +25127,15 @@ async function runLock(options = {}, gate) {
24810
25127
  {
24811
25128
  type: "confirm",
24812
25129
  name: "confirmed",
24813
- message: "Lock these specs and generate the agent topology? This freezes the design.",
25130
+ message: "Approve this design and generate the agent topology?",
24814
25131
  default: false
24815
25132
  }
24816
25133
  ]);
24817
25134
  if (!confirmed) return null;
24818
25135
  }
24819
- if (options.subsystem) {
24820
- for (const p of promotable) applySpecStatus(p.kind, p.id, "complete");
24821
- invalidateSpecCache();
24822
- } else {
24823
- promoteAllComplete();
24824
- }
24825
- if (promotable.length > 0) {
24826
- logger.success(`Locked ${promotable.length} spec(s) as complete.`);
24827
- }
24828
25136
  let lockedBy = "local";
24829
25137
  try {
24830
- lockedBy = `local:${os7.userInfo().username}`;
25138
+ lockedBy = `local:${os8.userInfo().username}`;
24831
25139
  } catch {
24832
25140
  }
24833
25141
  const record2 = {
@@ -24843,6 +25151,13 @@ async function runLock(options = {}, gate) {
24843
25151
  status: "ready"
24844
25152
  };
24845
25153
  writeLockRecord(record2);
25154
+ const root = getProjectRoot();
25155
+ const scope = options.subsystem ? {
25156
+ paths: new Set(
25157
+ specPathsInScope(options.subsystem).map((p) => path33.relative(root, p).split(path33.sep).join("/"))
25158
+ )
25159
+ } : void 0;
25160
+ writeBaseline(captureBaseline(lockedBy, currentChildPins(loadSubsystemSpecs(), root), root, scope));
24846
25161
  return record2;
24847
25162
  }
24848
25163
 
@@ -25099,29 +25414,29 @@ init_mcp();
25099
25414
 
25100
25415
  // src/commands/update.ts
25101
25416
  var https2 = __toESM(require("https"));
25102
- var fs22 = __toESM(require("fs"));
25103
- var path31 = __toESM(require("path"));
25104
- var os8 = __toESM(require("os"));
25105
- var crypto3 = __toESM(require("crypto"));
25417
+ var fs23 = __toESM(require("fs"));
25418
+ var path34 = __toESM(require("path"));
25419
+ var os9 = __toESM(require("os"));
25420
+ var crypto4 = __toESM(require("crypto"));
25106
25421
  var import_child_process2 = require("child_process");
25107
25422
  init_logger();
25108
25423
  init_defaults();
25109
25424
  init_version();
25110
25425
 
25111
25426
  // src/utils/download.ts
25112
- var fs21 = __toESM(require("fs"));
25427
+ var fs22 = __toESM(require("fs"));
25113
25428
  var http = __toESM(require("http"));
25114
25429
  var https = __toESM(require("https"));
25115
25430
  init_defaults();
25116
25431
  function downloadFile(url, dest) {
25117
- return new Promise((resolve26, reject) => {
25118
- const file = fs21.createWriteStream(dest);
25432
+ return new Promise((resolve27, reject) => {
25433
+ const file = fs22.createWriteStream(dest);
25119
25434
  const get4 = url.startsWith("https://") ? https.get : http.get;
25120
25435
  get4(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
25121
25436
  if (res.statusCode === 301 || res.statusCode === 302) {
25122
25437
  file.close();
25123
25438
  res.destroy();
25124
- downloadFile(res.headers.location, dest).then(resolve26).catch(reject);
25439
+ downloadFile(res.headers.location, dest).then(resolve27).catch(reject);
25125
25440
  return;
25126
25441
  }
25127
25442
  if (res.statusCode !== 200) {
@@ -25133,16 +25448,16 @@ function downloadFile(url, dest) {
25133
25448
  res.pipe(file);
25134
25449
  file.on("finish", () => {
25135
25450
  res.destroy();
25136
- file.close(() => resolve26());
25451
+ file.close(() => resolve27());
25137
25452
  });
25138
25453
  file.on("error", (err) => {
25139
25454
  res.destroy();
25140
- fs21.unlink(dest, () => {
25455
+ fs22.unlink(dest, () => {
25141
25456
  });
25142
25457
  reject(err);
25143
25458
  });
25144
25459
  }).on("error", (err) => {
25145
- fs21.unlink(dest, () => {
25460
+ fs22.unlink(dest, () => {
25146
25461
  });
25147
25462
  reject(err);
25148
25463
  });
@@ -25208,8 +25523,8 @@ async function runUpdate(options = {}) {
25208
25523
  logger.info(`Download manually from: ${release.html_url}`);
25209
25524
  process.exit(1);
25210
25525
  }
25211
- const tmpDir = os8.tmpdir();
25212
- const tmpFile = path31.join(tmpDir, assetName);
25526
+ const tmpDir = os9.tmpdir();
25527
+ const tmpFile = path34.join(tmpDir, assetName);
25213
25528
  logger.info(`Downloading ${assetName}...`);
25214
25529
  try {
25215
25530
  await downloadFile(asset.browser_download_url, tmpFile);
@@ -25226,16 +25541,16 @@ async function runUpdate(options = {}) {
25226
25541
  const checksumAssetName = assetName + ".sha256";
25227
25542
  const checksumAsset = release.assets.find((a) => a.name === checksumAssetName);
25228
25543
  if (checksumAsset) {
25229
- const tmpChecksum = path31.join(tmpDir, checksumAssetName);
25544
+ const tmpChecksum = path34.join(tmpDir, checksumAssetName);
25230
25545
  logger.info(`Verifying checksum...`);
25231
25546
  try {
25232
25547
  await downloadFile(checksumAsset.browser_download_url, tmpChecksum);
25233
25548
  verifyChecksum(tmpFile, tmpChecksum, assetName);
25234
- fs22.unlinkSync(tmpChecksum);
25549
+ fs23.unlinkSync(tmpChecksum);
25235
25550
  } catch (err) {
25236
25551
  logger.error(`Checksum verification failed: ${err.message}`);
25237
25552
  try {
25238
- fs22.unlinkSync(tmpFile);
25553
+ fs23.unlinkSync(tmpFile);
25239
25554
  } catch {
25240
25555
  }
25241
25556
  process.exit(1);
@@ -25270,7 +25585,7 @@ function releaseChannelLabel(tag) {
25270
25585
  return "stable";
25271
25586
  }
25272
25587
  function fetchReleases(repo) {
25273
- return new Promise((resolve26, reject) => {
25588
+ return new Promise((resolve27, reject) => {
25274
25589
  const url = `https://api.github.com/repos/${repo}/releases?per_page=20`;
25275
25590
  const options = {
25276
25591
  headers: {
@@ -25290,7 +25605,7 @@ function fetchReleases(repo) {
25290
25605
  return;
25291
25606
  }
25292
25607
  try {
25293
- resolve26(JSON.parse(data));
25608
+ resolve27(JSON.parse(data));
25294
25609
  } catch {
25295
25610
  reject(new Error("Failed to parse GitHub API response"));
25296
25611
  }
@@ -25300,10 +25615,10 @@ function fetchReleases(repo) {
25300
25615
  });
25301
25616
  }
25302
25617
  function verifyChecksum(filePath, checksumFile, expectedFilename) {
25303
- const checksumContent = fs22.readFileSync(checksumFile, "utf-8").trim();
25618
+ const checksumContent = fs23.readFileSync(checksumFile, "utf-8").trim();
25304
25619
  const expectedHash = checksumContent.split(/\s+/)[0].toLowerCase();
25305
- const fileBuffer = fs22.readFileSync(filePath);
25306
- const actualHash = crypto3.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase();
25620
+ const fileBuffer = fs23.readFileSync(filePath);
25621
+ const actualHash = crypto4.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase();
25307
25622
  if (actualHash !== expectedHash) {
25308
25623
  throw new Error(
25309
25624
  `SHA-256 mismatch for ${expectedFilename}
@@ -25333,9 +25648,9 @@ function isPkgBinary2() {
25333
25648
  function installBinary(tmpFile, destPath) {
25334
25649
  const platform = process.platform;
25335
25650
  const isZip = tmpFile.endsWith(".zip");
25336
- const extractDir = path31.join(os8.tmpdir(), "wairon-extract");
25337
- if (fs22.existsSync(extractDir)) fs22.rmSync(extractDir, { recursive: true });
25338
- fs22.mkdirSync(extractDir, { recursive: true });
25651
+ const extractDir = path34.join(os9.tmpdir(), "wairon-extract");
25652
+ if (fs23.existsSync(extractDir)) fs23.rmSync(extractDir, { recursive: true });
25653
+ fs23.mkdirSync(extractDir, { recursive: true });
25339
25654
  if (isZip) {
25340
25655
  (0, import_child_process2.execSync)(
25341
25656
  `powershell -NoProfile -NonInteractive -Command "Expand-Archive -Path '${tmpFile}' -DestinationPath '${extractDir}' -Force"`,
@@ -25345,18 +25660,18 @@ function installBinary(tmpFile, destPath) {
25345
25660
  (0, import_child_process2.execSync)(`tar -xzf "${tmpFile}" -C "${extractDir}"`, { stdio: ["ignore", "pipe", "pipe"] });
25346
25661
  }
25347
25662
  const binaryName = platform === "win32" ? "wairon.exe" : "wairon";
25348
- const extractedBinary = path31.join(extractDir, binaryName);
25349
- if (!fs22.existsSync(extractedBinary)) {
25663
+ const extractedBinary = path34.join(extractDir, binaryName);
25664
+ if (!fs23.existsSync(extractedBinary)) {
25350
25665
  throw new Error(`Extracted binary not found at ${extractedBinary}`);
25351
25666
  }
25352
25667
  if (platform === "win32") {
25353
25668
  const oldPath = destPath + ".old";
25354
25669
  try {
25355
25670
  cleanStaleBinary(oldPath);
25356
- fs22.renameSync(destPath, oldPath);
25357
- fs22.copyFileSync(extractedBinary, destPath);
25671
+ fs23.renameSync(destPath, oldPath);
25672
+ fs23.copyFileSync(extractedBinary, destPath);
25358
25673
  try {
25359
- fs22.unlinkSync(oldPath);
25674
+ fs23.unlinkSync(oldPath);
25360
25675
  } catch {
25361
25676
  }
25362
25677
  } catch (err) {
@@ -25370,25 +25685,25 @@ function installBinary(tmpFile, destPath) {
25370
25685
  }
25371
25686
  } else {
25372
25687
  const tmpDest = destPath + ".new";
25373
- fs22.copyFileSync(extractedBinary, tmpDest);
25374
- fs22.chmodSync(tmpDest, 493);
25375
- fs22.renameSync(tmpDest, destPath);
25688
+ fs23.copyFileSync(extractedBinary, tmpDest);
25689
+ fs23.chmodSync(tmpDest, 493);
25690
+ fs23.renameSync(tmpDest, destPath);
25376
25691
  }
25377
25692
  try {
25378
- fs22.unlinkSync(tmpFile);
25693
+ fs23.unlinkSync(tmpFile);
25379
25694
  } catch {
25380
25695
  }
25381
25696
  try {
25382
- fs22.rmSync(extractDir, { recursive: true });
25697
+ fs23.rmSync(extractDir, { recursive: true });
25383
25698
  } catch {
25384
25699
  }
25385
25700
  }
25386
25701
  function cleanStaleBinary(oldPath) {
25387
25702
  const target = oldPath ?? (isPkgBinary2() ? process.execPath + ".old" : null);
25388
25703
  if (!target) return;
25389
- if (fs22.existsSync(target)) {
25704
+ if (fs23.existsSync(target)) {
25390
25705
  try {
25391
- fs22.unlinkSync(target);
25706
+ fs23.unlinkSync(target);
25392
25707
  } catch {
25393
25708
  }
25394
25709
  }
@@ -25465,7 +25780,7 @@ async function filteredCheckbox(config) {
25465
25780
  32,
25466
25781
  Math.max(...items.map((i) => i.label.length))
25467
25782
  );
25468
- return new Promise((resolve26) => {
25783
+ return new Promise((resolve27) => {
25469
25784
  const checked = /* @__PURE__ */ new Set();
25470
25785
  let cursor = 0;
25471
25786
  let filterIdx = 0;
@@ -25532,7 +25847,7 @@ async function filteredCheckbox(config) {
25532
25847
  );
25533
25848
  teardown();
25534
25849
  const result = items.filter((_, idx) => checked.has(idx)).map((i) => i.value);
25535
- resolve26(result);
25850
+ resolve27(result);
25536
25851
  }
25537
25852
  function abort() {
25538
25853
  process.stdout.write("\n");
@@ -25804,9 +26119,9 @@ async function runSkillsInstall() {
25804
26119
  }
25805
26120
 
25806
26121
  // src/commands/doctor.ts
25807
- var fs23 = __toESM(require("fs"));
25808
- var os9 = __toESM(require("os"));
25809
- var path32 = __toESM(require("path"));
26122
+ var fs24 = __toESM(require("fs"));
26123
+ var os10 = __toESM(require("os"));
26124
+ var path35 = __toESM(require("path"));
25810
26125
  var import_chalk12 = __toESM(require("chalk"));
25811
26126
  init_logger();
25812
26127
  init_defaults();
@@ -25837,10 +26152,10 @@ function stampVerdict(content) {
25837
26152
  return { mark: "warn", note: `v${v} \u2014 stale, installed is v${WAIRON_VERSION}` };
25838
26153
  }
25839
26154
  function mcpEntryHealth(settingsPath) {
25840
- if (!fs23.existsSync(settingsPath)) return { mark: "warn", note: "not registered" };
26155
+ if (!fs24.existsSync(settingsPath)) return { mark: "warn", note: "not registered" };
25841
26156
  let entry;
25842
26157
  try {
25843
- const s = JSON.parse(fs23.readFileSync(settingsPath, "utf8"));
26158
+ const s = JSON.parse(fs24.readFileSync(settingsPath, "utf8"));
25844
26159
  entry = s.mcpServers?.["wairon"];
25845
26160
  } catch {
25846
26161
  return { mark: "error", note: "parse error" };
@@ -25848,7 +26163,7 @@ function mcpEntryHealth(settingsPath) {
25848
26163
  if (!entry) return { mark: "warn", note: "not registered" };
25849
26164
  if (entry.command === "node" && Array.isArray(entry.args) && typeof entry.args[0] === "string") {
25850
26165
  const scriptPath = entry.args[0];
25851
- if (!fs23.existsSync(scriptPath)) {
26166
+ if (!fs24.existsSync(scriptPath)) {
25852
26167
  return { mark: "error", note: `registered but the server path is missing \u2014 ${scriptPath}` };
25853
26168
  }
25854
26169
  }
@@ -25900,7 +26215,7 @@ async function runDoctor(options = {}) {
25900
26215
  const { findChainingSubprojectsMissingConfig: findChainingSubprojectsMissingConfig2 } = (init_provision(), __toCommonJS(provision_exports));
25901
26216
  const missing = findChainingSubprojectsMissingConfig2(getProjectRoot());
25902
26217
  if (missing.length > 0) {
25903
- line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path32.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
26218
+ line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path35.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
25904
26219
  }
25905
26220
  } catch {
25906
26221
  }
@@ -25938,7 +26253,7 @@ async function runDoctor(options = {}) {
25938
26253
  const gp = localGuideFilePath(process.cwd(), t);
25939
26254
  if (!gp || seenGuides.has(gp)) continue;
25940
26255
  seenGuides.add(gp);
25941
- const rel2 = path32.relative(process.cwd(), gp).replace(/\\/g, "/");
26256
+ const rel2 = path35.relative(process.cwd(), gp).replace(/\\/g, "/");
25942
26257
  if (!pathExists(gp)) {
25943
26258
  line(tally, "warn", `${rel2} guide \u2014 not injected (run \`wairon generate\`)`);
25944
26259
  continue;
@@ -26020,17 +26335,17 @@ async function runDoctor(options = {}) {
26020
26335
  line(tally, h.mark, `Claude (project .mcp.json): ${h.note}${h.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend claude`"}`);
26021
26336
  }
26022
26337
  if (wantGemini) {
26023
- const globalCfg = path32.join(os9.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
26338
+ const globalCfg = path35.join(os10.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
26024
26339
  const hg = mcpEntryHealth(globalCfg);
26025
26340
  line(tally, hg.mark, `Antigravity (global mcp_config.json): ${hg.note}${hg.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend gemini --global`"}`);
26026
26341
  const projPath = fromProjectRoot(".gemini", "settings.json");
26027
- if (fs23.existsSync(projPath)) {
26342
+ if (fs24.existsSync(projPath)) {
26028
26343
  const hp = mcpEntryHealth(projPath);
26029
26344
  line(tally, hp.mark === "error" ? "error" : "ok", `Gemini CLI (project): ${hp.note} ${import_chalk12.default.gray("(Antigravity ignores this file)")}`);
26030
26345
  }
26031
26346
  }
26032
- const pluginDir = path32.join(os9.homedir(), ".gemini", "config", "plugins", "wairon");
26033
- if (fs23.existsSync(pluginDir)) {
26347
+ const pluginDir = path35.join(os10.homedir(), ".gemini", "config", "plugins", "wairon");
26348
+ if (fs24.existsSync(pluginDir)) {
26034
26349
  line(tally, "warn", `Legacy Antigravity plugin present (${pluginDir}) \u2014 it collides with the wairon MCP server. Remove it with \`wairon doctor --fix\`.`);
26035
26350
  }
26036
26351
  logger.blank();
@@ -26069,7 +26384,7 @@ async function applyFixes() {
26069
26384
  const legacySpecs = findLegacySpecFiles();
26070
26385
  if (legacySpecs.length > 0) {
26071
26386
  for (const { path: oldPath, expected: newPath } of legacySpecs) {
26072
- fs23.renameSync(oldPath, newPath);
26387
+ fs24.renameSync(oldPath, newPath);
26073
26388
  }
26074
26389
  console.log(` ${icon("ok")} Migrated ${legacySpecs.length} legacy spec file(s) to the new dot-prefixed unified schema.`);
26075
26390
  }
@@ -26123,8 +26438,8 @@ function printSummary(tally) {
26123
26438
  }
26124
26439
 
26125
26440
  // src/commands/diagram.ts
26126
- var fs24 = __toESM(require("fs"));
26127
- var path33 = __toESM(require("path"));
26441
+ var fs25 = __toESM(require("fs"));
26442
+ var path36 = __toESM(require("path"));
26128
26443
  init_logger();
26129
26444
  init_loader();
26130
26445
  init_fs();
@@ -26159,71 +26474,71 @@ function collectIssues() {
26159
26474
  }
26160
26475
  function writeCanvas(dest) {
26161
26476
  const model = buildCanvasModel(collectIssues());
26162
- ensureDir(path33.dirname(path33.resolve(dest)));
26163
- fs24.writeFileSync(dest, renderCanvasHtml(model), "utf-8");
26477
+ ensureDir(path36.dirname(path36.resolve(dest)));
26478
+ fs25.writeFileSync(dest, renderCanvasHtml(model), "utf-8");
26164
26479
  }
26165
26480
  function parseSequenceRef(ref) {
26166
- const sep6 = ref.includes(":") ? ref.lastIndexOf(":") : ref.lastIndexOf(".");
26167
- if (sep6 <= 0 || sep6 === ref.length - 1) {
26481
+ const sep9 = ref.includes(":") ? ref.lastIndexOf(":") : ref.lastIndexOf(".");
26482
+ if (sep9 <= 0 || sep9 === ref.length - 1) {
26168
26483
  throw new WaironError(
26169
26484
  `Invalid --sequence reference "${ref}". Use <componentId>:<methodName> (e.g. billing-portal:authorize).`
26170
26485
  );
26171
26486
  }
26172
- return { component: ref.slice(0, sep6), method: ref.slice(sep6 + 1) };
26487
+ return { component: ref.slice(0, sep9), method: ref.slice(sep9 + 1) };
26173
26488
  }
26174
26489
  async function runDiagram(rawOptions = {}) {
26175
26490
  assertProjectInitialized();
26176
26491
  const options = applyFormat(rawOptions);
26177
26492
  if (options.canvas && !options.all) {
26178
- const dest2 = options.out ?? path33.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
26493
+ const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
26179
26494
  writeCanvas(dest2);
26180
26495
  logger.success(`Interactive canvas written to ${dest2}`);
26181
26496
  logger.info("Open it in a browser \u2014 fully self-contained (works offline).");
26182
26497
  return;
26183
26498
  }
26184
26499
  if (options.drawio && !options.all) {
26185
- const dest2 = options.out ?? path33.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
26186
- ensureDir(path33.dirname(path33.resolve(dest2)));
26187
- fs24.writeFileSync(dest2, generateDrawioXml(buildCanvasModel()), "utf-8");
26500
+ const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
26501
+ ensureDir(path36.dirname(path36.resolve(dest2)));
26502
+ fs25.writeFileSync(dest2, generateDrawioXml(buildCanvasModel()), "utf-8");
26188
26503
  logger.success(`draw.io diagram written to ${dest2}`);
26189
26504
  logger.info("Open with draw.io / diagrams.net (or import into tools that accept the format).");
26190
26505
  return;
26191
26506
  }
26192
26507
  if (options.excalidraw && !options.all) {
26193
- const dest2 = options.out ?? path33.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
26194
- ensureDir(path33.dirname(path33.resolve(dest2)));
26195
- fs24.writeFileSync(dest2, generateExcalidrawScene(buildCanvasModel()), "utf-8");
26508
+ const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
26509
+ ensureDir(path36.dirname(path36.resolve(dest2)));
26510
+ fs25.writeFileSync(dest2, generateExcalidrawScene(buildCanvasModel()), "utf-8");
26196
26511
  logger.success(`Excalidraw scene written to ${dest2}`);
26197
26512
  logger.info("Open with excalidraw.com or the VS Code extension.");
26198
26513
  return;
26199
26514
  }
26200
26515
  const wantsMermaid = options.format?.toLowerCase().startsWith("mermaid") || !!options.subsystem || !!options.sequence;
26201
26516
  if (!options.all && !options.sequence && !wantsMermaid) {
26202
- const dest2 = options.out ?? path33.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
26517
+ const dest2 = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
26203
26518
  writeCanvas(dest2);
26204
26519
  logger.success(`Interactive canvas written to ${dest2}`);
26205
26520
  logger.info("Open it in a browser \u2014 fully self-contained (works offline). Other formats: --format mermaid|drawio|excalidraw.");
26206
26521
  return;
26207
26522
  }
26208
26523
  if (options.all) {
26209
- const outDir = options.out ?? path33.join(AI_PATHS.docsDir(), "diagrams");
26524
+ const outDir = options.out ?? path36.join(AI_PATHS.docsDir(), "diagrams");
26210
26525
  const files = generateDiagramSet();
26211
26526
  if (files.length === 0) {
26212
26527
  logger.warn("No diagrams to generate \u2014 the spec tree has no components yet.");
26213
26528
  return;
26214
26529
  }
26215
26530
  for (const file of files) {
26216
- const dest2 = path33.join(outDir, file.relPath);
26217
- ensureDir(path33.dirname(dest2));
26218
- fs24.writeFileSync(dest2, toMarkdown(file), "utf-8");
26531
+ const dest2 = path36.join(outDir, file.relPath);
26532
+ ensureDir(path36.dirname(dest2));
26533
+ fs25.writeFileSync(dest2, toMarkdown(file), "utf-8");
26219
26534
  }
26220
- writeCanvas(path33.join(outDir, "canvas.html"));
26535
+ writeCanvas(path36.join(outDir, "canvas.html"));
26221
26536
  const exportModel = buildCanvasModel();
26222
- fs24.writeFileSync(path33.join(outDir, "architecture.drawio"), generateDrawioXml(exportModel), "utf-8");
26223
- fs24.writeFileSync(path33.join(outDir, "architecture.excalidraw"), generateExcalidrawScene(exportModel), "utf-8");
26537
+ fs25.writeFileSync(path36.join(outDir, "architecture.drawio"), generateDrawioXml(exportModel), "utf-8");
26538
+ fs25.writeFileSync(path36.join(outDir, "architecture.excalidraw"), generateExcalidrawScene(exportModel), "utf-8");
26224
26539
  const graph = loadSpecGraph();
26225
- const indexPath = path33.join(outDir, "README.md");
26226
- fs24.writeFileSync(indexPath, diagramSetIndex(files, graph.systemName), "utf-8");
26540
+ const indexPath = path36.join(outDir, "README.md");
26541
+ fs25.writeFileSync(indexPath, diagramSetIndex(files, graph.systemName), "utf-8");
26227
26542
  logger.success(`Generated ${files.length} diagram(s) + interactive canvas.html + index into ${outDir}`);
26228
26543
  for (const file of files.slice(0, 12)) {
26229
26544
  logger.info(` ${file.relPath}`);
@@ -26234,26 +26549,26 @@ async function runDiagram(rawOptions = {}) {
26234
26549
  let mermaid;
26235
26550
  let title;
26236
26551
  let defaultDest;
26237
- const diagramsDir = path33.join(AI_PATHS.docsDir(), "diagrams");
26552
+ const diagramsDir = path36.join(AI_PATHS.docsDir(), "diagrams");
26238
26553
  if (options.sequence) {
26239
26554
  const { component, method: method2 } = parseSequenceRef(options.sequence);
26240
26555
  mermaid = generateSequenceDiagram(component, method2, { depth: options.depth });
26241
26556
  title = `${component}.${method2} \u2014 narrative sequence`;
26242
- defaultDest = path33.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
26557
+ defaultDest = path36.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
26243
26558
  } else if (options.subsystem) {
26244
26559
  mermaid = generateComponentDiagram({ subsystem: options.subsystem });
26245
26560
  title = `${options.subsystem} \u2014 components`;
26246
- defaultDest = path33.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
26561
+ defaultDest = path36.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
26247
26562
  } else {
26248
26563
  mermaid = generateComponentDiagram();
26249
26564
  title = "Component architecture";
26250
- defaultDest = path33.join(diagramsDir, "system.md");
26565
+ defaultDest = path36.join(diagramsDir, "system.md");
26251
26566
  }
26252
26567
  const dest = options.out ?? defaultDest;
26253
- ensureDir(path33.dirname(path33.resolve(dest)));
26568
+ ensureDir(path36.dirname(path36.resolve(dest)));
26254
26569
  const content = dest.endsWith(".mmd") ? `${mermaid}
26255
26570
  ` : toMarkdown({ relPath: dest, title, mermaid });
26256
- fs24.writeFileSync(dest, content, "utf-8");
26571
+ fs25.writeFileSync(dest, content, "utf-8");
26257
26572
  logger.success(`Mermaid diagram written to ${dest}`);
26258
26573
  logger.info("Renders on GitHub/IDE previews; use a .mmd --out path for raw Mermaid.");
26259
26574
  }
@@ -26362,9 +26677,9 @@ Component variants (${variants.length})
26362
26677
  }
26363
26678
 
26364
26679
  // src/commands/packs.ts
26365
- var fs25 = __toESM(require("fs"));
26366
- var os10 = __toESM(require("os"));
26367
- var path34 = __toESM(require("path"));
26680
+ var fs26 = __toESM(require("fs"));
26681
+ var os11 = __toESM(require("os"));
26682
+ var path37 = __toESM(require("path"));
26368
26683
  var import_chalk16 = __toESM(require("chalk"));
26369
26684
  var import_sdk = __toESM(require_dist());
26370
26685
  init_logger();
@@ -26390,11 +26705,11 @@ function describe(probe2) {
26390
26705
  return parts.join(", ");
26391
26706
  }
26392
26707
  function resolveSourceUnit(source) {
26393
- const abs = path34.resolve(source);
26394
- if (!fs25.existsSync(abs)) {
26708
+ const abs = path37.resolve(source);
26709
+ if (!fs26.existsSync(abs)) {
26395
26710
  throw new Error(`Pack source "${source}" does not exist.`);
26396
26711
  }
26397
- const isDir = fs25.statSync(abs).isDirectory();
26712
+ const isDir = fs26.statSync(abs).isDirectory();
26398
26713
  if (isDir && !packDirEntry(abs)) {
26399
26714
  throw new Error(`"${source}" is a directory without a pack entry file (pack.yaml | pack.cjs | index.cjs | ...).`);
26400
26715
  }
@@ -26410,7 +26725,7 @@ async function addPack(source, options = {}) {
26410
26725
  }
26411
26726
  const { abs } = resolveSourceUnit(source);
26412
26727
  const scope = options.global ? "global" : "project";
26413
- const probe2 = probePack(abs, path34.dirname(abs), scope);
26728
+ const probe2 = probePack(abs, path37.dirname(abs), scope);
26414
26729
  if (probe2.error) {
26415
26730
  logger.error(probe2.error);
26416
26731
  process.exitCode = 1;
@@ -26418,10 +26733,10 @@ async function addPack(source, options = {}) {
26418
26733
  }
26419
26734
  if (options.global) {
26420
26735
  const destDir = globalPacksDir();
26421
- const dest2 = path34.join(destDir, path34.basename(abs));
26422
- if (path34.resolve(dest2) !== abs) {
26423
- fs25.mkdirSync(destDir, { recursive: true });
26424
- fs25.cpSync(abs, dest2, { recursive: true, force: true });
26736
+ const dest2 = path37.join(destDir, path37.basename(abs));
26737
+ if (path37.resolve(dest2) !== abs) {
26738
+ fs26.mkdirSync(destDir, { recursive: true });
26739
+ fs26.cpSync(abs, dest2, { recursive: true, force: true });
26425
26740
  }
26426
26741
  logger.success(`Installed pack "${probe2.name}" globally: ${dest2}`);
26427
26742
  logger.info(`${describe(probe2)} \u2014 auto-loaded for every project on this machine (WAIRON_PACKS_DIR / ~/.wairon/packs).`);
@@ -26434,11 +26749,11 @@ async function addPack(source, options = {}) {
26434
26749
  return;
26435
26750
  }
26436
26751
  const root = getProjectRoot();
26437
- const relRef = `.wai/packs/${path34.basename(abs)}`;
26438
- const dest = path34.join(root, ".wai", "packs", path34.basename(abs));
26439
- if (path34.resolve(dest) !== abs) {
26440
- fs25.mkdirSync(path34.dirname(dest), { recursive: true });
26441
- fs25.cpSync(abs, dest, { recursive: true, force: true });
26752
+ const relRef = `.wai/packs/${path37.basename(abs)}`;
26753
+ const dest = path37.join(root, ".wai", "packs", path37.basename(abs));
26754
+ if (path37.resolve(dest) !== abs) {
26755
+ fs26.mkdirSync(path37.dirname(dest), { recursive: true });
26756
+ fs26.cpSync(abs, dest, { recursive: true, force: true });
26442
26757
  }
26443
26758
  const config = loadProjectConfig();
26444
26759
  const packs = config.extensions?.packs ?? [];
@@ -26447,14 +26762,14 @@ async function addPack(source, options = {}) {
26447
26762
  saveProjectConfig(config);
26448
26763
  logger.success(`Vendored pack "${probe2.name}" into ${relRef} and registered it in .wai/project.yaml.`);
26449
26764
  } else {
26450
- fs25.cpSync(abs, dest, { recursive: true, force: true });
26765
+ fs26.cpSync(abs, dest, { recursive: true, force: true });
26451
26766
  logger.success(`Pack "${probe2.name}" already registered \u2014 refreshed ${relRef} from the source.`);
26452
26767
  }
26453
26768
  logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
26454
26769
  }
26455
26770
  async function addPackFromArchive(source, options) {
26456
- const abs = path34.resolve(source);
26457
- if (!fs25.existsSync(abs) || !fs25.statSync(abs).isFile()) {
26771
+ const abs = path37.resolve(source);
26772
+ if (!fs26.existsSync(abs) || !fs26.statSync(abs).isFile()) {
26458
26773
  logger.error(`Pack archive "${source}" does not exist.`);
26459
26774
  process.exitCode = 1;
26460
26775
  return;
@@ -26469,27 +26784,27 @@ async function addPackFromArchive(source, options) {
26469
26784
  process.exitCode = 1;
26470
26785
  return;
26471
26786
  }
26472
- baseDir = path34.join(getProjectRoot(), ".wai", "packs");
26787
+ baseDir = path37.join(getProjectRoot(), ".wai", "packs");
26473
26788
  }
26474
- const bytes = fs25.readFileSync(abs);
26475
- fs25.mkdirSync(baseDir, { recursive: true });
26476
- const staging = fs25.mkdtempSync(path34.join(baseDir, ".wpack-staging-"));
26789
+ const bytes = fs26.readFileSync(abs);
26790
+ fs26.mkdirSync(baseDir, { recursive: true });
26791
+ const staging = fs26.mkdtempSync(path37.join(baseDir, ".wpack-staging-"));
26477
26792
  let result;
26478
26793
  try {
26479
26794
  result = (0, import_sdk.extractPack)(bytes, staging);
26480
26795
  } catch (err) {
26481
- fs25.rmSync(staging, { recursive: true, force: true });
26482
- logger.error(`Failed to extract pack archive "${path34.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
26796
+ fs26.rmSync(staging, { recursive: true, force: true });
26797
+ logger.error(`Failed to extract pack archive "${path37.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
26483
26798
  process.exitCode = 1;
26484
26799
  return;
26485
26800
  }
26486
26801
  const name = result.name;
26487
- const destDir = path34.join(baseDir, name);
26488
- if (fs25.existsSync(destDir)) fs25.rmSync(destDir, { recursive: true, force: true });
26489
- fs25.renameSync(staging, destDir);
26490
- const probe2 = probePack(destDir, path34.dirname(destDir), scope);
26802
+ const destDir = path37.join(baseDir, name);
26803
+ if (fs26.existsSync(destDir)) fs26.rmSync(destDir, { recursive: true, force: true });
26804
+ fs26.renameSync(staging, destDir);
26805
+ const probe2 = probePack(destDir, path37.dirname(destDir), scope);
26491
26806
  if (probe2.error) {
26492
- fs25.rmSync(destDir, { recursive: true, force: true });
26807
+ fs26.rmSync(destDir, { recursive: true, force: true });
26493
26808
  logger.error(probe2.error);
26494
26809
  process.exitCode = 1;
26495
26810
  return;
@@ -26507,7 +26822,7 @@ async function addPackFromArchive(source, options) {
26507
26822
  saveProjectConfig(config);
26508
26823
  logger.success(`Installed pack "${probe2.name ?? name}" into ${relRef} and registered it in .wai/project.yaml.`);
26509
26824
  } else {
26510
- logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path34.basename(abs)}.`);
26825
+ logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path37.basename(abs)}.`);
26511
26826
  }
26512
26827
  logger.info(`${describe(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
26513
26828
  }
@@ -26528,7 +26843,7 @@ async function buildPack(source, options = {}) {
26528
26843
  const sourceDir = source && source.length > 0 ? source : ".";
26529
26844
  const result = (0, import_sdk.buildPack)(sourceDir);
26530
26845
  const outPath = options.out ?? result.suggestedFileName;
26531
- fs25.writeFileSync(outPath, result.archive);
26846
+ fs26.writeFileSync(outPath, result.archive);
26532
26847
  logger.success(`Built pack "${result.info.name}" v${result.info.version} \u2192 ${outPath} (${result.archive.byteLength} bytes)`);
26533
26848
  logger.info(`Install it with \`wairon pack add ${outPath}\`, or upload it to a hosted instance.`);
26534
26849
  }
@@ -26539,8 +26854,8 @@ function expandSource(source, version) {
26539
26854
  return source.replace(/\{version\}/g, version ?? "").replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? "");
26540
26855
  }
26541
26856
  async function fetchArchive(url) {
26542
- const dir = fs25.mkdtempSync(path34.join(os10.tmpdir(), "wairon-packdl-"));
26543
- const dest = path34.join(dir, "pack.wpack");
26857
+ const dir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packdl-"));
26858
+ const dest = path37.join(dir, "pack.wpack");
26544
26859
  await downloadFile(url, dest);
26545
26860
  return dest;
26546
26861
  }
@@ -26559,7 +26874,7 @@ async function installPack(source) {
26559
26874
  }
26560
26875
  const extracted = extractArchiveToTemp(archive);
26561
26876
  try {
26562
- fs25.rmSync(path34.dirname(archive), { recursive: true, force: true });
26877
+ fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
26563
26878
  } catch {
26564
26879
  }
26565
26880
  if (!extracted) return;
@@ -26571,7 +26886,7 @@ async function installPack(source) {
26571
26886
  process.exitCode = 1;
26572
26887
  } finally {
26573
26888
  try {
26574
- fs25.rmSync(extracted.dir, { recursive: true, force: true });
26889
+ fs26.rmSync(extracted.dir, { recursive: true, force: true });
26575
26890
  } catch {
26576
26891
  }
26577
26892
  }
@@ -26582,7 +26897,7 @@ async function installPack(source) {
26582
26897
  if (!extracted) return;
26583
26898
  sourceDir = extracted.dir;
26584
26899
  cleanup = extracted.dir;
26585
- origin = path34.resolve(source);
26900
+ origin = path37.resolve(source);
26586
26901
  } else {
26587
26902
  let unit;
26588
26903
  try {
@@ -26608,7 +26923,7 @@ async function installPack(source) {
26608
26923
  } finally {
26609
26924
  if (cleanup) {
26610
26925
  try {
26611
- fs25.rmSync(cleanup, { recursive: true, force: true });
26926
+ fs26.rmSync(cleanup, { recursive: true, force: true });
26612
26927
  } catch {
26613
26928
  }
26614
26929
  }
@@ -26625,20 +26940,20 @@ function reportInstalled(installed, origin) {
26625
26940
  }
26626
26941
  }
26627
26942
  function extractArchiveToTemp(source) {
26628
- const abs = path34.resolve(source);
26629
- if (!fs25.existsSync(abs)) {
26943
+ const abs = path37.resolve(source);
26944
+ if (!fs26.existsSync(abs)) {
26630
26945
  logger.error(`Pack archive "${source}" does not exist.`);
26631
26946
  process.exitCode = 1;
26632
26947
  return null;
26633
26948
  }
26634
- const tempDir = fs25.mkdtempSync(path34.join(os10.tmpdir(), "wairon-packinstall-"));
26949
+ const tempDir = fs26.mkdtempSync(path37.join(os11.tmpdir(), "wairon-packinstall-"));
26635
26950
  try {
26636
- const result = (0, import_sdk.extractPack)(new Uint8Array(fs25.readFileSync(abs)), tempDir);
26951
+ const result = (0, import_sdk.extractPack)(new Uint8Array(fs26.readFileSync(abs)), tempDir);
26637
26952
  void result;
26638
26953
  return { dir: tempDir };
26639
26954
  } catch (e) {
26640
26955
  try {
26641
- fs25.rmSync(tempDir, { recursive: true, force: true });
26956
+ fs26.rmSync(tempDir, { recursive: true, force: true });
26642
26957
  } catch {
26643
26958
  }
26644
26959
  logger.error(`Could not extract "${source}": ${e instanceof Error ? e.message : String(e)}`);
@@ -26784,7 +27099,7 @@ async function fetchAndInstall(selection, url) {
26784
27099
  const archive = await fetchArchive(url);
26785
27100
  const extracted = extractArchiveToTemp(archive);
26786
27101
  try {
26787
- fs25.rmSync(path34.dirname(archive), { recursive: true, force: true });
27102
+ fs26.rmSync(path37.dirname(archive), { recursive: true, force: true });
26788
27103
  } catch {
26789
27104
  }
26790
27105
  if (!extracted) return null;
@@ -26796,7 +27111,7 @@ async function fetchAndInstall(selection, url) {
26796
27111
  return pack;
26797
27112
  } finally {
26798
27113
  try {
26799
- fs25.rmSync(extracted.dir, { recursive: true, force: true });
27114
+ fs26.rmSync(extracted.dir, { recursive: true, force: true });
26800
27115
  } catch {
26801
27116
  }
26802
27117
  }
@@ -26807,10 +27122,10 @@ function bundleTargets(selections, name, all) {
26807
27122
  return selections.filter((s) => s.bundle === true);
26808
27123
  }
26809
27124
  function writeBundle(root, resolved) {
26810
- const dest = path34.join(root, ".wai", "packs", resolved.name, resolved.version);
26811
- fs25.rmSync(dest, { recursive: true, force: true });
26812
- fs25.mkdirSync(path34.dirname(dest), { recursive: true });
26813
- fs25.cpSync(resolved.path, dest, { recursive: true });
27125
+ const dest = path37.join(root, ".wai", "packs", resolved.name, resolved.version);
27126
+ fs26.rmSync(dest, { recursive: true, force: true });
27127
+ fs26.mkdirSync(path37.dirname(dest), { recursive: true });
27128
+ fs26.cpSync(resolved.path, dest, { recursive: true });
26814
27129
  return dest;
26815
27130
  }
26816
27131
  async function bundlePack(name, options = {}) {
@@ -26841,7 +27156,7 @@ async function bundlePack(name, options = {}) {
26841
27156
  selection.version = resolved.version;
26842
27157
  selection.bundle = true;
26843
27158
  bundled.push(`${resolved.name}@${resolved.version}`);
26844
- console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(path34.relative(root, dest).replace(/\\/g, "/"))}`);
27159
+ console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(path37.relative(root, dest).replace(/\\/g, "/"))}`);
26845
27160
  }
26846
27161
  if (bundled.length === 0) return;
26847
27162
  config.extensions = { packs: entries, useGlobalPacks: globalPacksEnabled(config) };
@@ -26883,9 +27198,9 @@ async function listPacks() {
26883
27198
  console.log(import_chalk16.default.bold.cyan(`\u25A0 Global (${globalPacksDir()})${useGlobal ? "" : import_chalk16.default.yellow(" [disabled: extensions.useGlobalPacks: false]")}`));
26884
27199
  if (globalRefs.length === 0) console.log(import_chalk16.default.dim(" (none)"));
26885
27200
  for (const ref of globalRefs) {
26886
- const probe2 = probePack(ref, path34.dirname(ref), "global");
26887
- if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path34.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
26888
- else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path34.basename(ref))} ${import_chalk16.default.dim(describe(probe2))}`);
27201
+ const probe2 = probePack(ref, path37.dirname(ref), "global");
27202
+ if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path37.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
27203
+ else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path37.basename(ref))} ${import_chalk16.default.dim(describe(probe2))}`);
26889
27204
  }
26890
27205
  console.log("");
26891
27206
  if (!inProject) {
@@ -26913,9 +27228,9 @@ async function listPacks() {
26913
27228
  async function removePack(name, options = {}) {
26914
27229
  if (options.global) {
26915
27230
  for (const ref of discoverPacks(globalPacksDir())) {
26916
- const probe2 = probePack(ref, path34.dirname(ref), "global");
26917
- if (probe2.name === name || path34.basename(ref) === name) {
26918
- fs25.rmSync(ref, { recursive: true, force: true });
27231
+ const probe2 = probePack(ref, path37.dirname(ref), "global");
27232
+ if (probe2.name === name || path37.basename(ref) === name) {
27233
+ fs26.rmSync(ref, { recursive: true, force: true });
26919
27234
  logger.success(`Removed global pack "${probe2.name ?? name}" (${ref}).`);
26920
27235
  return;
26921
27236
  }
@@ -26936,16 +27251,16 @@ async function removePack(name, options = {}) {
26936
27251
  if (typeof entry !== "string") continue;
26937
27252
  const ref = entry;
26938
27253
  const probe2 = probePack(ref, root, "project");
26939
- if (probe2.name === name || ref === name || path34.basename(ref) === name) {
27254
+ if (probe2.name === name || ref === name || path37.basename(ref) === name) {
26940
27255
  config.extensions = {
26941
27256
  packs: packs.filter((p) => p !== ref),
26942
27257
  useGlobalPacks: globalPacksEnabled(config)
26943
27258
  };
26944
27259
  saveProjectConfig(config);
26945
- const resolved = path34.resolve(root, ref);
26946
- const vendorDir = path34.resolve(root, ".wai", "packs");
26947
- if (resolved.startsWith(vendorDir + path34.sep)) {
26948
- fs25.rmSync(resolved, { recursive: true, force: true });
27260
+ const resolved = path37.resolve(root, ref);
27261
+ const vendorDir = path37.resolve(root, ".wai", "packs");
27262
+ if (resolved.startsWith(vendorDir + path37.sep)) {
27263
+ fs26.rmSync(resolved, { recursive: true, force: true });
26949
27264
  logger.success(`Deregistered pack "${probe2.name ?? name}" and deleted ${ref}.`);
26950
27265
  } else {
26951
27266
  logger.success(`Deregistered pack "${probe2.name ?? name}" (files at ${ref} left in place).`);
@@ -26958,22 +27273,22 @@ async function removePack(name, options = {}) {
26958
27273
  }
26959
27274
 
26960
27275
  // src/commands/host.ts
26961
- var fs52 = __toESM(require("fs"));
26962
- var path61 = __toESM(require("path"));
26963
- var os11 = __toESM(require("os"));
26964
- var crypto21 = __toESM(require("crypto"));
27276
+ var fs53 = __toESM(require("fs"));
27277
+ var path64 = __toESM(require("path"));
27278
+ var os12 = __toESM(require("os"));
27279
+ var crypto22 = __toESM(require("crypto"));
26965
27280
  var import_child_process5 = require("child_process");
26966
27281
  var import_chalk17 = __toESM(require("chalk"));
26967
27282
  init_logger();
26968
27283
  init_errors();
26969
27284
 
26970
27285
  // src/server/admin.ts
26971
- var crypto8 = __toESM(require("crypto"));
27286
+ var crypto9 = __toESM(require("crypto"));
26972
27287
  init_fs();
26973
27288
  init_defaults();
26974
27289
 
26975
27290
  // src/server/auth.ts
26976
- var crypto6 = __toESM(require("crypto"));
27291
+ var crypto7 = __toESM(require("crypto"));
26977
27292
 
26978
27293
  // src/server/types.ts
26979
27294
  var SSO_ADMIN_ROLE_ID = "sso-admin";
@@ -26986,35 +27301,35 @@ var UNAUTHENTICATED = {
26986
27301
  var WEB_SESSION_PREFIX = "ws_";
26987
27302
 
26988
27303
  // src/server/credentials.ts
26989
- var fs26 = __toESM(require("fs"));
26990
- var path35 = __toESM(require("path"));
26991
- var crypto4 = __toESM(require("crypto"));
27304
+ var fs27 = __toESM(require("fs"));
27305
+ var path38 = __toESM(require("path"));
27306
+ var crypto5 = __toESM(require("crypto"));
26992
27307
  var HASH_NS = "wairon:token:v1";
26993
27308
  function hashToken(token) {
26994
- return crypto4.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
27309
+ return crypto5.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
26995
27310
  }
26996
27311
  function storePath(dataDir) {
26997
- return path35.join(dataDir, "auth", "credentials.json");
27312
+ return path38.join(dataDir, "auth", "credentials.json");
26998
27313
  }
26999
27314
  function load3(dataDir) {
27000
27315
  try {
27001
- return JSON.parse(fs26.readFileSync(storePath(dataDir), "utf8"));
27316
+ return JSON.parse(fs27.readFileSync(storePath(dataDir), "utf8"));
27002
27317
  } catch {
27003
27318
  return [];
27004
27319
  }
27005
27320
  }
27006
27321
  function save(dataDir, records) {
27007
27322
  const p = storePath(dataDir);
27008
- fs26.mkdirSync(path35.dirname(p), { recursive: true });
27323
+ fs27.mkdirSync(path38.dirname(p), { recursive: true });
27009
27324
  const tmp = `${p}.tmp`;
27010
- fs26.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
27011
- fs26.renameSync(tmp, p);
27325
+ fs27.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
27326
+ fs27.renameSync(tmp, p);
27012
27327
  }
27013
27328
  function digestEquals(a, b) {
27014
27329
  const ab = Buffer.from(a, "hex");
27015
27330
  const bb = Buffer.from(b, "hex");
27016
27331
  if (ab.length === 0 || ab.length !== bb.length) return false;
27017
- return crypto4.timingSafeEqual(ab, bb);
27332
+ return crypto5.timingSafeEqual(ab, bb);
27018
27333
  }
27019
27334
  function findByTokenHash(dataDir, tokenHash) {
27020
27335
  return load3(dataDir).find((r) => digestEquals(r.keyHash, tokenHash)) ?? null;
@@ -27053,17 +27368,17 @@ function listByOwner(dataDir, ownerUserId) {
27053
27368
  }
27054
27369
 
27055
27370
  // src/server/websessions.ts
27056
- var fs27 = __toESM(require("fs"));
27057
- var path36 = __toESM(require("path"));
27058
- var crypto5 = __toESM(require("crypto"));
27371
+ var fs28 = __toESM(require("fs"));
27372
+ var path39 = __toESM(require("path"));
27373
+ var crypto6 = __toESM(require("crypto"));
27059
27374
  function storePath2(dataDir) {
27060
- return path36.join(dataDir, "web-sessions.json");
27375
+ return path39.join(dataDir, "web-sessions.json");
27061
27376
  }
27062
27377
  function readSessions(dataDir) {
27063
27378
  const p = storePath2(dataDir);
27064
27379
  let raw;
27065
27380
  try {
27066
- raw = fs27.readFileSync(p, "utf8");
27381
+ raw = fs28.readFileSync(p, "utf8");
27067
27382
  } catch (e) {
27068
27383
  if (e.code === "ENOENT") return [];
27069
27384
  throw new Error(`Failed to read web session store at ${p}: ${e.message}`);
@@ -27078,13 +27393,13 @@ function readSessions(dataDir) {
27078
27393
  }
27079
27394
  function persistSessions(dataDir, sessions) {
27080
27395
  const p = storePath2(dataDir);
27081
- fs27.mkdirSync(path36.dirname(p), { recursive: true });
27396
+ fs28.mkdirSync(path39.dirname(p), { recursive: true });
27082
27397
  const tmp = `${p}.tmp`;
27083
- fs27.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
27084
- fs27.renameSync(tmp, p);
27398
+ fs28.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
27399
+ fs28.renameSync(tmp, p);
27085
27400
  }
27086
27401
  function mintSessionId() {
27087
- return `${WEB_SESSION_PREFIX}${crypto5.randomBytes(24).toString("hex")}`;
27402
+ return `${WEB_SESSION_PREFIX}${crypto6.randomBytes(24).toString("hex")}`;
27088
27403
  }
27089
27404
  var WebSessionStore = class {
27090
27405
  constructor(dataDir) {
@@ -27242,17 +27557,17 @@ function listWebSessionsBySubject(dataDir, userId) {
27242
27557
  }
27243
27558
 
27244
27559
  // src/server/users.ts
27245
- var fs28 = __toESM(require("fs"));
27246
- var path37 = __toESM(require("path"));
27560
+ var fs29 = __toESM(require("fs"));
27561
+ var path40 = __toESM(require("path"));
27247
27562
  var VALID_STATUSES = ["active", "inactive", "suspended", "deactivated", "disabled"];
27248
27563
  function storePath3(dataDir) {
27249
- return path37.join(dataDir, "users.json");
27564
+ return path40.join(dataDir, "users.json");
27250
27565
  }
27251
27566
  function loadStore(dataDir) {
27252
27567
  const p = storePath3(dataDir);
27253
27568
  let raw;
27254
27569
  try {
27255
- raw = fs28.readFileSync(p, "utf8");
27570
+ raw = fs29.readFileSync(p, "utf8");
27256
27571
  } catch (err) {
27257
27572
  if (err.code === "ENOENT") return [];
27258
27573
  throw new Error(`Cannot read hosted-user store at ${p}: ${err.message}`);
@@ -27270,10 +27585,10 @@ function loadStore(dataDir) {
27270
27585
  }
27271
27586
  function replaceAll(dataDir, records) {
27272
27587
  const p = storePath3(dataDir);
27273
- fs28.mkdirSync(path37.dirname(p), { recursive: true });
27588
+ fs29.mkdirSync(path40.dirname(p), { recursive: true });
27274
27589
  const tmp = `${p}.tmp`;
27275
- fs28.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
27276
- fs28.renameSync(tmp, p);
27590
+ fs29.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
27591
+ fs29.renameSync(tmp, p);
27277
27592
  }
27278
27593
  function registryUpsert(dataDir, record2) {
27279
27594
  const records = loadStore(dataDir);
@@ -27375,11 +27690,11 @@ function remapUnitReferences(dataDir, remap, removedScopeIds) {
27375
27690
  }
27376
27691
 
27377
27692
  // src/server/instance.ts
27378
- var fs29 = __toESM(require("fs"));
27379
- var path38 = __toESM(require("path"));
27693
+ var fs30 = __toESM(require("fs"));
27694
+ var path41 = __toESM(require("path"));
27380
27695
  var import_crypto = require("crypto");
27381
27696
  function storePath4(dataDir) {
27382
- return path38.join(dataDir, "instance.json");
27697
+ return path41.join(dataDir, "instance.json");
27383
27698
  }
27384
27699
  var InstanceIdentityStore = class {
27385
27700
  constructor(dataDir) {
@@ -27396,7 +27711,7 @@ var InstanceIdentityStore = class {
27396
27711
  const p = storePath4(this.dataDir);
27397
27712
  let raw;
27398
27713
  try {
27399
- raw = fs29.readFileSync(p, "utf8");
27714
+ raw = fs30.readFileSync(p, "utf8");
27400
27715
  } catch (err) {
27401
27716
  if (err.code === "ENOENT") return null;
27402
27717
  throw new Error(`Cannot read instance identity at ${p}: ${err.message}`);
@@ -27419,10 +27734,10 @@ var InstanceIdentityStore = class {
27419
27734
  * never truncates the file. Only called by the registry's create-once seed. */
27420
27735
  replace(identity) {
27421
27736
  const p = storePath4(this.dataDir);
27422
- fs29.mkdirSync(path38.dirname(p), { recursive: true });
27737
+ fs30.mkdirSync(path41.dirname(p), { recursive: true });
27423
27738
  const tmp = `${p}.tmp`;
27424
- fs29.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
27425
- fs29.renameSync(tmp, p);
27739
+ fs30.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
27740
+ fs30.renameSync(tmp, p);
27426
27741
  }
27427
27742
  };
27428
27743
  var InstanceIdentityRegistry = class {
@@ -27478,8 +27793,8 @@ function getInstanceIdentity(dataDir) {
27478
27793
  }
27479
27794
 
27480
27795
  // src/utils/secrets.ts
27481
- var fs30 = __toESM(require("fs"));
27482
- var path39 = __toESM(require("path"));
27796
+ var fs31 = __toESM(require("fs"));
27797
+ var path42 = __toESM(require("path"));
27483
27798
  var ENV_FALLBACK = {
27484
27799
  "git-token": ["WAIRON_GIT_TOKEN"],
27485
27800
  "notion-token": ["WAIRON_NOTION_TOKEN"],
@@ -27488,13 +27803,13 @@ var ENV_FALLBACK = {
27488
27803
  };
27489
27804
  function storePath5() {
27490
27805
  const dataDir = process.env["WAIRON_DATA_DIR"];
27491
- return dataDir ? path39.join(dataDir, "auth", "secrets.json") : null;
27806
+ return dataDir ? path42.join(dataDir, "auth", "secrets.json") : null;
27492
27807
  }
27493
27808
  function readStore() {
27494
27809
  const p = storePath5();
27495
27810
  if (!p) return {};
27496
27811
  try {
27497
- return JSON.parse(fs30.readFileSync(p, "utf8"));
27812
+ return JSON.parse(fs31.readFileSync(p, "utf8"));
27498
27813
  } catch {
27499
27814
  return {};
27500
27815
  }
@@ -27519,10 +27834,10 @@ function setSecret(key, value) {
27519
27834
  if (!p) throw new Error("WAIRON_DATA_DIR is not set \u2014 a running server needs it to store secrets.");
27520
27835
  const store = readStore();
27521
27836
  store[key] = value;
27522
- fs30.mkdirSync(path39.dirname(p), { recursive: true });
27837
+ fs31.mkdirSync(path42.dirname(p), { recursive: true });
27523
27838
  const tmp = `${p}.tmp`;
27524
- fs30.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
27525
- fs30.renameSync(tmp, p);
27839
+ fs31.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
27840
+ fs31.renameSync(tmp, p);
27526
27841
  }
27527
27842
  function listSecretKeys() {
27528
27843
  return Object.keys(readStore());
@@ -27600,7 +27915,7 @@ function masterMatches(credential) {
27600
27915
  if (!master || !credential) return false;
27601
27916
  const a = Buffer.from(hashToken(credential), "hex");
27602
27917
  const b = Buffer.from(hashToken(master), "hex");
27603
- return a.length === b.length && crypto6.timingSafeEqual(a, b);
27918
+ return a.length === b.length && crypto7.timingSafeEqual(a, b);
27604
27919
  }
27605
27920
  function bootstrapAdminPrincipal() {
27606
27921
  const subject = {
@@ -27648,7 +27963,7 @@ function authenticateMaster(token) {
27648
27963
  function hashedEquals(a, b) {
27649
27964
  const ha = Buffer.from(hashToken(a), "hex");
27650
27965
  const hb = Buffer.from(hashToken(b), "hex");
27651
- return ha.length === hb.length && crypto6.timingSafeEqual(ha, hb);
27966
+ return ha.length === hb.length && crypto7.timingSafeEqual(ha, hb);
27652
27967
  }
27653
27968
  function verifyBuiltinAdmin(cfg, user, password) {
27654
27969
  const configuredUser = cfg.builtinAdminUser ?? "";
@@ -27697,16 +28012,16 @@ function signingKey() {
27697
28012
  }
27698
28013
  function signViewToken(project2, format) {
27699
28014
  const body = Buffer.from(JSON.stringify({ project: project2, format, exp: Date.now() + VIEW_TTL_MS })).toString("base64url");
27700
- const sig = crypto6.createHmac("sha256", signingKey()).update(body).digest("base64url");
28015
+ const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
27701
28016
  return `${body}.${sig}`;
27702
28017
  }
27703
28018
  function verifyViewToken(token) {
27704
28019
  const [body, sig] = String(token).split(".");
27705
28020
  if (!body || !sig) throw new Error("invalid view token");
27706
- const expected = crypto6.createHmac("sha256", signingKey()).update(body).digest("base64url");
28021
+ const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
27707
28022
  const a = Buffer.from(sig);
27708
28023
  const b = Buffer.from(expected);
27709
- if (a.length !== b.length || !crypto6.timingSafeEqual(a, b)) throw new Error("invalid view token");
28024
+ if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid view token");
27710
28025
  const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
27711
28026
  if (Date.now() > payload.exp) throw new Error("expired view token");
27712
28027
  return { project: payload.project, format: payload.format, expiresAt: new Date(payload.exp).toISOString() };
@@ -27714,33 +28029,33 @@ function verifyViewToken(token) {
27714
28029
  var SSO_STATE_TTL_MS = 10 * 60 * 1e3;
27715
28030
  function signSsoState(payload) {
27716
28031
  const body = Buffer.from(JSON.stringify({ payload, exp: Date.now() + SSO_STATE_TTL_MS })).toString("base64url");
27717
- const sig = crypto6.createHmac("sha256", signingKey()).update(body).digest("base64url");
28032
+ const sig = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
27718
28033
  return `${body}.${sig}`;
27719
28034
  }
27720
28035
  function verifySsoState(state) {
27721
28036
  const [body, sig] = String(state).split(".");
27722
28037
  if (!body || !sig) throw new Error("invalid SSO state");
27723
- const expected = crypto6.createHmac("sha256", signingKey()).update(body).digest("base64url");
28038
+ const expected = crypto7.createHmac("sha256", signingKey()).update(body).digest("base64url");
27724
28039
  const a = Buffer.from(sig);
27725
28040
  const b = Buffer.from(expected);
27726
- if (a.length !== b.length || !crypto6.timingSafeEqual(a, b)) throw new Error("invalid SSO state");
28041
+ if (a.length !== b.length || !crypto7.timingSafeEqual(a, b)) throw new Error("invalid SSO state");
27727
28042
  const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
27728
28043
  if (Date.now() > parsed.exp) throw new Error("expired SSO state");
27729
28044
  return parsed.payload;
27730
28045
  }
27731
28046
 
27732
28047
  // src/server/organization.ts
27733
- var fs31 = __toESM(require("fs"));
27734
- var path40 = __toESM(require("path"));
27735
- var crypto7 = __toESM(require("crypto"));
28048
+ var fs32 = __toESM(require("fs"));
28049
+ var path43 = __toESM(require("path"));
28050
+ var crypto8 = __toESM(require("crypto"));
27736
28051
  function storePath6(dataDir) {
27737
- return path40.join(dataDir, "organization.json");
28052
+ return path43.join(dataDir, "organization.json");
27738
28053
  }
27739
28054
  function readState(dataDir) {
27740
28055
  const p = storePath6(dataDir);
27741
28056
  let raw;
27742
28057
  try {
27743
- raw = fs31.readFileSync(p, "utf8");
28058
+ raw = fs32.readFileSync(p, "utf8");
27744
28059
  } catch (e) {
27745
28060
  if (e.code === "ENOENT") return { units: [], placements: [] };
27746
28061
  throw new Error(`Failed to read organization store at ${p}: ${e.message}`);
@@ -27757,10 +28072,10 @@ function readState(dataDir) {
27757
28072
  }
27758
28073
  function persistState(dataDir, state) {
27759
28074
  const p = storePath6(dataDir);
27760
- fs31.mkdirSync(path40.dirname(p), { recursive: true });
28075
+ fs32.mkdirSync(path43.dirname(p), { recursive: true });
27761
28076
  const tmp = `${p}.tmp`;
27762
- fs31.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
27763
- fs31.renameSync(tmp, p);
28077
+ fs32.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
28078
+ fs32.renameSync(tmp, p);
27764
28079
  }
27765
28080
  var SLUG_PATTERN = /^[a-z0-9-]+$/;
27766
28081
  var UNIT_KINDS = ["business_entity", "department", "team", "group"];
@@ -28055,7 +28370,7 @@ var OrganizationRegistry = class {
28055
28370
  createdAt: placements[existingIdx].createdAt
28056
28371
  } : {
28057
28372
  ...placement,
28058
- id: placement.id || crypto7.randomUUID(),
28373
+ id: placement.id || crypto8.randomUUID(),
28059
28374
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
28060
28375
  };
28061
28376
  const next = isUpdate ? placements.map((p, i) => i === existingIdx ? stored : p) : [...placements, stored];
@@ -28136,11 +28451,11 @@ function getOrganizationUnit(dataDir, id) {
28136
28451
  }
28137
28452
 
28138
28453
  // src/server/permissions.ts
28139
- var fs32 = __toESM(require("fs"));
28140
- var path41 = __toESM(require("path"));
28454
+ var fs33 = __toESM(require("fs"));
28455
+ var path44 = __toESM(require("path"));
28141
28456
  var import_crypto2 = require("crypto");
28142
28457
  function storePath7(dataDir) {
28143
- return path41.join(dataDir, "permissions.json");
28458
+ return path44.join(dataDir, "permissions.json");
28144
28459
  }
28145
28460
  function assignmentKey(a) {
28146
28461
  return [a.subjectKind, a.subjectId ?? "", a.scopeKind, a.scopeId ?? "", a.capability].join("|");
@@ -28149,7 +28464,7 @@ function load4(dataDir) {
28149
28464
  const p = storePath7(dataDir);
28150
28465
  let raw;
28151
28466
  try {
28152
- raw = fs32.readFileSync(p, "utf8");
28467
+ raw = fs33.readFileSync(p, "utf8");
28153
28468
  } catch (err) {
28154
28469
  if (err.code === "ENOENT") return [];
28155
28470
  throw new Error(`Cannot read permission store at ${p}: ${err.message}`);
@@ -28167,10 +28482,10 @@ function load4(dataDir) {
28167
28482
  }
28168
28483
  function replaceAll2(dataDir, assignments) {
28169
28484
  const p = storePath7(dataDir);
28170
- fs32.mkdirSync(path41.dirname(p), { recursive: true });
28485
+ fs33.mkdirSync(path44.dirname(p), { recursive: true });
28171
28486
  const tmp = `${p}.tmp`;
28172
- fs32.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
28173
- fs32.renameSync(tmp, p);
28487
+ fs33.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
28488
+ fs33.renameSync(tmp, p);
28174
28489
  }
28175
28490
  function registrySet(dataDir, assignment) {
28176
28491
  const assignments = load4(dataDir);
@@ -28256,8 +28571,8 @@ function getAssignment(dataDir, assignmentId) {
28256
28571
  }
28257
28572
 
28258
28573
  // src/server/roles.ts
28259
- var fs33 = __toESM(require("fs"));
28260
- var path42 = __toESM(require("path"));
28574
+ var fs34 = __toESM(require("fs"));
28575
+ var path45 = __toESM(require("path"));
28261
28576
  var BUILTIN_ROLES = [
28262
28577
  {
28263
28578
  id: SSO_ADMIN_ROLE_ID,
@@ -28275,13 +28590,13 @@ function isBuiltinRoleId(roleId) {
28275
28590
  return BUILTIN_ROLE_IDS.has(roleId);
28276
28591
  }
28277
28592
  function storePath8(dataDir) {
28278
- return path42.join(dataDir, "roles.json");
28593
+ return path45.join(dataDir, "roles.json");
28279
28594
  }
28280
28595
  function load5(dataDir) {
28281
28596
  const p = storePath8(dataDir);
28282
28597
  let raw;
28283
28598
  try {
28284
- raw = fs33.readFileSync(p, "utf8");
28599
+ raw = fs34.readFileSync(p, "utf8");
28285
28600
  } catch (err) {
28286
28601
  if (err.code === "ENOENT") return [];
28287
28602
  throw new Error(`Cannot read role store at ${p}: ${err.message}`);
@@ -28299,10 +28614,10 @@ function load5(dataDir) {
28299
28614
  }
28300
28615
  function replaceAll3(dataDir, roles) {
28301
28616
  const p = storePath8(dataDir);
28302
- fs33.mkdirSync(path42.dirname(p), { recursive: true });
28617
+ fs34.mkdirSync(path45.dirname(p), { recursive: true });
28303
28618
  const tmp = `${p}.tmp`;
28304
- fs33.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
28305
- fs33.renameSync(tmp, p);
28619
+ fs34.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
28620
+ fs34.renameSync(tmp, p);
28306
28621
  }
28307
28622
  function registryCreate(dataDir, role) {
28308
28623
  if (isBuiltinRoleId(role.id)) {
@@ -28536,8 +28851,8 @@ function actionableUnitIds(scopes) {
28536
28851
  }
28537
28852
 
28538
28853
  // src/server/projects.ts
28539
- var fs37 = __toESM(require("fs"));
28540
- var path46 = __toESM(require("path"));
28854
+ var fs38 = __toESM(require("fs"));
28855
+ var path49 = __toESM(require("path"));
28541
28856
  init_loader();
28542
28857
  init_yaml();
28543
28858
  init_fs();
@@ -28556,37 +28871,37 @@ init_types();
28556
28871
  init_server();
28557
28872
 
28558
28873
  // src/git/config.ts
28559
- var fs34 = __toESM(require("fs"));
28560
- var path43 = __toESM(require("path"));
28874
+ var fs35 = __toESM(require("fs"));
28875
+ var path46 = __toESM(require("path"));
28561
28876
  init_fs();
28562
28877
  function configPath() {
28563
28878
  return aiDir("git.json");
28564
28879
  }
28565
28880
  function readGitConfig() {
28566
28881
  try {
28567
- return JSON.parse(fs34.readFileSync(configPath(), "utf8"));
28882
+ return JSON.parse(fs35.readFileSync(configPath(), "utf8"));
28568
28883
  } catch {
28569
28884
  return null;
28570
28885
  }
28571
28886
  }
28572
28887
  function writeGitConfig(config) {
28573
28888
  const p = configPath();
28574
- fs34.mkdirSync(path43.dirname(p), { recursive: true });
28889
+ fs35.mkdirSync(path46.dirname(p), { recursive: true });
28575
28890
  const tmp = `${p}.tmp`;
28576
- fs34.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
28577
- fs34.renameSync(tmp, p);
28891
+ fs35.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
28892
+ fs35.renameSync(tmp, p);
28578
28893
  }
28579
28894
  function clearGitConfig() {
28580
28895
  try {
28581
- fs34.rmSync(configPath(), { force: true });
28896
+ fs35.rmSync(configPath(), { force: true });
28582
28897
  } catch {
28583
28898
  }
28584
28899
  }
28585
28900
 
28586
28901
  // src/git/adapter.ts
28587
28902
  var import_child_process3 = require("child_process");
28588
- var fs35 = __toESM(require("fs"));
28589
- var path44 = __toESM(require("path"));
28903
+ var fs36 = __toESM(require("fs"));
28904
+ var path47 = __toESM(require("path"));
28590
28905
  init_fs();
28591
28906
  function git(args, cwd) {
28592
28907
  return (0, import_child_process3.execFileSync)("git", args, {
@@ -28638,10 +28953,10 @@ function compareUrl(remote, defaultBranch, workingBranch) {
28638
28953
  return `${web}/compare/${encodeURIComponent(defaultBranch)}...${encodeURIComponent(workingBranch)}`;
28639
28954
  }
28640
28955
  function excludeLocalFiles() {
28641
- const excludePath = path44.join(getProjectRoot(), ".git", "info", "exclude");
28956
+ const excludePath = path47.join(getProjectRoot(), ".git", "info", "exclude");
28642
28957
  try {
28643
- fs35.mkdirSync(path44.dirname(excludePath), { recursive: true });
28644
- fs35.appendFileSync(excludePath, "\n.wai/lock.json\n.wai/git.json\n");
28958
+ fs36.mkdirSync(path47.dirname(excludePath), { recursive: true });
28959
+ fs36.appendFileSync(excludePath, "\n.wai/lock.json\n.wai/git.json\n");
28645
28960
  } catch {
28646
28961
  }
28647
28962
  }
@@ -28706,25 +29021,25 @@ function configureSync(periodicSyncMinutes, skipIfClean) {
28706
29021
  }
28707
29022
 
28708
29023
  // src/producers/config.ts
28709
- var fs36 = __toESM(require("fs"));
28710
- var path45 = __toESM(require("path"));
29024
+ var fs37 = __toESM(require("fs"));
29025
+ var path48 = __toESM(require("path"));
28711
29026
  init_fs();
28712
29027
  function configPath2() {
28713
29028
  return aiDir("producers.json");
28714
29029
  }
28715
29030
  function load6() {
28716
29031
  try {
28717
- return JSON.parse(fs36.readFileSync(configPath2(), "utf8"));
29032
+ return JSON.parse(fs37.readFileSync(configPath2(), "utf8"));
28718
29033
  } catch {
28719
29034
  return [];
28720
29035
  }
28721
29036
  }
28722
29037
  function save2(configs) {
28723
29038
  const p = configPath2();
28724
- fs36.mkdirSync(path45.dirname(p), { recursive: true });
29039
+ fs37.mkdirSync(path48.dirname(p), { recursive: true });
28725
29040
  const tmp = `${p}.tmp`;
28726
- fs36.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
28727
- fs36.renameSync(tmp, p);
29041
+ fs37.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
29042
+ fs37.renameSync(tmp, p);
28728
29043
  }
28729
29044
  function readProducerConfig(target) {
28730
29045
  return load6().find((c) => c.target === target) ?? null;
@@ -29142,24 +29457,24 @@ function isValidProjectId(id) {
29142
29457
  return typeof id === "string" && ID_RE.test(id);
29143
29458
  }
29144
29459
  function registryPath(dataDir) {
29145
- return path46.join(dataDir, "projects.json");
29460
+ return path49.join(dataDir, "projects.json");
29146
29461
  }
29147
29462
  function load7(dataDir) {
29148
29463
  try {
29149
- return JSON.parse(fs37.readFileSync(registryPath(dataDir), "utf8"));
29464
+ return JSON.parse(fs38.readFileSync(registryPath(dataDir), "utf8"));
29150
29465
  } catch {
29151
29466
  return [];
29152
29467
  }
29153
29468
  }
29154
29469
  function save3(dataDir, records) {
29155
29470
  const p = registryPath(dataDir);
29156
- fs37.mkdirSync(path46.dirname(p), { recursive: true });
29471
+ fs38.mkdirSync(path49.dirname(p), { recursive: true });
29157
29472
  const tmp = `${p}.tmp`;
29158
- fs37.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
29159
- fs37.renameSync(tmp, p);
29473
+ fs38.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
29474
+ fs38.renameSync(tmp, p);
29160
29475
  }
29161
29476
  function projectRoot(dataDir, id) {
29162
- return path46.join(dataDir, "projects", id);
29477
+ return path49.join(dataDir, "projects", id);
29163
29478
  }
29164
29479
  function existingProjectRoot(dataDir, id) {
29165
29480
  if (!isValidProjectId(id)) return null;
@@ -29175,7 +29490,7 @@ function createProjectRecord(dataDir, id) {
29175
29490
  throw new Error(`Project "${id}" already exists.`);
29176
29491
  }
29177
29492
  const root = projectRoot(dataDir, id);
29178
- fs37.mkdirSync(root, { recursive: true });
29493
+ fs38.mkdirSync(root, { recursive: true });
29179
29494
  const record2 = {
29180
29495
  id,
29181
29496
  rootPath: root,
@@ -29210,7 +29525,7 @@ function removeProjectRecord(dataDir, id) {
29210
29525
  const rec = records.find((r) => r.id === id);
29211
29526
  if (rec) {
29212
29527
  try {
29213
- fs37.rmSync(rec.rootPath, { recursive: true, force: true });
29528
+ fs38.rmSync(rec.rootPath, { recursive: true, force: true });
29214
29529
  } catch {
29215
29530
  }
29216
29531
  }
@@ -29226,7 +29541,7 @@ function parseQualifiedSelector(value) {
29226
29541
  }
29227
29542
  function findSubsystemSpec(root, subsystemId) {
29228
29543
  const specsDir = aiPathsAt(root).specsDir();
29229
- if (!fs37.existsSync(specsDir)) return null;
29544
+ if (!fs38.existsSync(specsDir)) return null;
29230
29545
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
29231
29546
  let raw;
29232
29547
  try {
@@ -29404,9 +29719,9 @@ function mintKey(cfg, credential, project2, role) {
29404
29719
  "instance-wide super-admin (*:*) keys cannot be minted \u2014 the built-in admin account (WAIRON_ADMIN_USER) is the only super-admin"
29405
29720
  );
29406
29721
  }
29407
- const token = "wk_" + crypto8.randomBytes(24).toString("hex");
29722
+ const token = "wk_" + crypto9.randomBytes(24).toString("hex");
29408
29723
  const record2 = {
29409
- id: crypto8.randomBytes(6).toString("hex"),
29724
+ id: crypto9.randomBytes(6).toString("hex"),
29410
29725
  keyHash: hashToken(token),
29411
29726
  role,
29412
29727
  projects: project2 === "*" ? ["*"] : [project2],
@@ -29602,31 +29917,10 @@ function diagramViewLink(cfg, credential, project2) {
29602
29917
  if (!existingProjectRoot(cfg.dataDir, project2)) throw new Error(`Unknown project "${project2}".`);
29603
29918
  return `/view/diagram?token=${signViewToken(project2, "canvas")}`;
29604
29919
  }
29605
- function promoteProject(cfg, credential, project2) {
29606
- const principal = requirePrincipal(cfg, credential);
29607
- if (authorize(cfg.dataDir, principal, "project:write", "project", project2).value !== "yes") {
29608
- throw new AdminAuthError("Forbidden \u2014 promoting a project requires project:write over it");
29609
- }
29610
- return executeApprovedPromote(cfg, project2);
29611
- }
29612
- function executeApprovedPromote(cfg, projectId, subproject) {
29613
- const root = boundLifecycleRoot(cfg, projectId, subproject);
29614
- return runWithProjectRoot(root, () => {
29615
- const { state, record: lock, current: current2 } = hostCore.readLockState();
29616
- if (state === "unlocked" || !lock) {
29617
- return { status: "not-locked", message: "Project is not locked; run lock first." };
29618
- }
29619
- if (state === "stale") {
29620
- return { status: "stale", stateId: current2, message: "Spec tree or governing doctrine changed since lock; re-lock required." };
29621
- }
29622
- hostCore.writeLockRecord({ ...lock, status: "promoted" });
29623
- return { status: "ready", stateId: current2, message: "Locked state matches; change-set marked ready for promotion." };
29624
- });
29625
- }
29626
29920
 
29627
29921
  // src/server/packs.ts
29628
- var fs38 = __toESM(require("fs"));
29629
- var path47 = __toESM(require("path"));
29922
+ var fs39 = __toESM(require("fs"));
29923
+ var path50 = __toESM(require("path"));
29630
29924
  init_fs();
29631
29925
  init_yaml();
29632
29926
  init_loader();
@@ -29672,19 +29966,19 @@ function assertNotCodeArchive(info) {
29672
29966
  function extractDeclarativeArchive(archive, destDir) {
29673
29967
  const result = hostSdk.extractArchive(archive, destDir, HOSTED_STRICT_LIMITS);
29674
29968
  try {
29675
- assertDeclarative(fs38.readFileSync(path47.join(result.directory, result.entryPath), "utf8"));
29969
+ assertDeclarative(fs39.readFileSync(path50.join(result.directory, result.entryPath), "utf8"));
29676
29970
  } catch (e) {
29677
- fs38.rmSync(destDir, { recursive: true, force: true });
29971
+ fs39.rmSync(destDir, { recursive: true, force: true });
29678
29972
  throw e;
29679
29973
  }
29680
29974
  }
29681
29975
  function probe(loadRef, baseRoot, scope, displayRef) {
29682
29976
  const loaded = hostCore.loadExtensionPacks([{ ref: loadRef, scope }], baseRoot);
29683
29977
  if (loaded.errors.length) {
29684
- return { name: path47.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
29978
+ return { name: path50.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
29685
29979
  }
29686
29980
  return {
29687
- name: loaded.packNames[0] ?? path47.basename(displayRef),
29981
+ name: loaded.packNames[0] ?? path50.basename(displayRef),
29688
29982
  scope,
29689
29983
  ref: displayRef,
29690
29984
  profiles: Object.keys(loaded.profiles).length,
@@ -29698,20 +29992,20 @@ function probe(loadRef, baseRoot, scope, displayRef) {
29698
29992
  };
29699
29993
  }
29700
29994
  function writeFileAtomic(file, content) {
29701
- fs38.mkdirSync(path47.dirname(file), { recursive: true });
29995
+ fs39.mkdirSync(path50.dirname(file), { recursive: true });
29702
29996
  const tmp = `${file}.tmp`;
29703
- fs38.writeFileSync(tmp, content);
29704
- fs38.renameSync(tmp, file);
29997
+ fs39.writeFileSync(tmp, content);
29998
+ fs39.renameSync(tmp, file);
29705
29999
  }
29706
30000
  function stem(ref) {
29707
- return path47.basename(ref).replace(PACK_EXT_RE, "");
30001
+ return path50.basename(ref).replace(PACK_EXT_RE, "");
29708
30002
  }
29709
30003
  function imagePacksDir() {
29710
30004
  return process.env.WAIRON_IMAGE_PACKS_DIR ?? "/opt/wairon/packs";
29711
30005
  }
29712
30006
  function probeTier(dir, tier) {
29713
30007
  return hostCore.discoverPacks(dir).map((full) => {
29714
- const d = probe(full, path47.dirname(full), "global", path47.basename(full));
30008
+ const d = probe(full, path50.dirname(full), "global", path50.basename(full));
29715
30009
  d.tier = tier;
29716
30010
  return d;
29717
30011
  });
@@ -29729,9 +30023,9 @@ function scanGlobalPackProfiles() {
29729
30023
  for (const dir of [hostCore.globalPacksDir(), imagePacksDir()]) {
29730
30024
  for (const full of hostCore.discoverPacks(dir)) {
29731
30025
  try {
29732
- const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }], path47.dirname(full));
30026
+ const loaded = hostCore.loadExtensionPacks([{ ref: full, scope: "global" }], path50.dirname(full));
29733
30027
  if (loaded.errors.length) continue;
29734
- const source = loaded.packNames[0] ?? path47.basename(full);
30028
+ const source = loaded.packNames[0] ?? path50.basename(full);
29735
30029
  for (const [id, def] of Object.entries(loaded.profiles)) out.push({ id, source, family: def.family });
29736
30030
  } catch {
29737
30031
  }
@@ -29783,12 +30077,12 @@ function storeListProjectProfiles() {
29783
30077
  return out;
29784
30078
  }
29785
30079
  function readPackContent(full) {
29786
- const st = fs38.statSync(full);
29787
- if (st.isFile()) return fs38.readFileSync(full, "utf8");
30080
+ const st = fs39.statSync(full);
30081
+ if (st.isFile()) return fs39.readFileSync(full, "utf8");
29788
30082
  if (st.isDirectory()) {
29789
30083
  for (const entry of ["pack.yaml", "pack.yml"]) {
29790
- const p = path47.join(full, entry);
29791
- if (fs38.existsSync(p) && fs38.statSync(p).isFile()) return fs38.readFileSync(p, "utf8");
30084
+ const p = path50.join(full, entry);
30085
+ if (fs39.existsSync(p) && fs39.statSync(p).isFile()) return fs39.readFileSync(p, "utf8");
29792
30086
  }
29793
30087
  }
29794
30088
  return null;
@@ -29797,9 +30091,9 @@ function storeResolveGlobalPacks(names) {
29797
30091
  const index = /* @__PURE__ */ new Map();
29798
30092
  const indexTier = (dir, tier) => {
29799
30093
  for (const full of hostCore.discoverPacks(dir)) {
29800
- const manifestName = probe(full, path47.dirname(full), "global", path47.basename(full)).name;
30094
+ const manifestName = probe(full, path50.dirname(full), "global", path50.basename(full)).name;
29801
30095
  const candidate = { full, manifestName, tier };
29802
- for (const key of [manifestName, stem(full), path47.basename(full)]) {
30096
+ for (const key of [manifestName, stem(full), path50.basename(full)]) {
29803
30097
  if (!index.has(key)) index.set(key, candidate);
29804
30098
  }
29805
30099
  }
@@ -29826,7 +30120,7 @@ function storeInstallGlobalPack(name, content) {
29826
30120
  assertName(name);
29827
30121
  assertDeclarative(content);
29828
30122
  const dir = hostCore.globalPacksDir();
29829
- const file = path47.join(dir, `${name}.yaml`);
30123
+ const file = path50.join(dir, `${name}.yaml`);
29830
30124
  writeFileAtomic(file, content);
29831
30125
  const descriptor = probe(file, dir, "global", `${name}.yaml`);
29832
30126
  descriptor.tier = "instance";
@@ -29837,7 +30131,7 @@ function storeInstallGlobalPackArchive(archive, name) {
29837
30131
  assertNotCodeArchive(info);
29838
30132
  const packName = name ?? info.name;
29839
30133
  assertName(packName);
29840
- const dir = path47.join(hostCore.globalPacksDir(), packName);
30134
+ const dir = path50.join(hostCore.globalPacksDir(), packName);
29841
30135
  extractDeclarativeArchive(archive, dir);
29842
30136
  const descriptor = probe(dir, hostCore.globalPacksDir(), "global", packName);
29843
30137
  descriptor.tier = "instance";
@@ -29846,16 +30140,16 @@ function storeInstallGlobalPackArchive(archive, name) {
29846
30140
  function storeRemoveGlobalPack(name) {
29847
30141
  assertName(name);
29848
30142
  const dir = hostCore.globalPacksDir();
29849
- const match = hostCore.discoverPacks(dir).find((ref) => stem(ref) === name || path47.basename(ref) === name);
30143
+ const match = hostCore.discoverPacks(dir).find((ref) => stem(ref) === name || path50.basename(ref) === name);
29850
30144
  if (!match) {
29851
30145
  const inImage = hostCore.discoverPacks(imagePacksDir()).some(
29852
- (ref) => stem(ref) === name || path47.basename(ref) === name
30146
+ (ref) => stem(ref) === name || path50.basename(ref) === name
29853
30147
  );
29854
30148
  throw new Error(
29855
30149
  inImage ? `Pack "${name}" is an immutable image-layer pack (WAIRON_IMAGE_PACKS_DIR) and cannot be removed at runtime; rebuild the extension image without it.` : `No server-global instance pack named "${name}".`
29856
30150
  );
29857
30151
  }
29858
- fs38.rmSync(match, { recursive: true, force: true });
30152
+ fs39.rmSync(match, { recursive: true, force: true });
29859
30153
  }
29860
30154
  function storeListProjectPacks() {
29861
30155
  const root = getProjectRoot();
@@ -29871,7 +30165,7 @@ function storeInstallProjectPack(name, content) {
29871
30165
  assertDeclarative(content);
29872
30166
  const root = getProjectRoot();
29873
30167
  const relRef = `.wai/packs/${name}.yaml`;
29874
- writeFileAtomic(path47.join(root, ".wai", "packs", `${name}.yaml`), content);
30168
+ writeFileAtomic(path50.join(root, ".wai", "packs", `${name}.yaml`), content);
29875
30169
  const config = loadProjectConfig();
29876
30170
  const packs = config.extensions?.packs ?? [];
29877
30171
  if (!packs.includes(relRef)) {
@@ -29887,7 +30181,7 @@ function storeInstallProjectPackArchive(archive, name) {
29887
30181
  assertName(packName);
29888
30182
  const root = getProjectRoot();
29889
30183
  const relRef = `.wai/packs/${packName}`;
29890
- extractDeclarativeArchive(archive, path47.join(root, ".wai", "packs", packName));
30184
+ extractDeclarativeArchive(archive, path50.join(root, ".wai", "packs", packName));
29891
30185
  const config = loadProjectConfig();
29892
30186
  const packs = config.extensions?.packs ?? [];
29893
30187
  if (!packs.includes(relRef)) {
@@ -29902,18 +30196,18 @@ function storeRemoveProjectPack(name) {
29902
30196
  const config = loadProjectConfig();
29903
30197
  const packs = config.extensions?.packs ?? [];
29904
30198
  const relRef = `.wai/packs/${name}.yaml`;
29905
- const match = packs.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name || path47.basename(entry) === name : entry.name === name);
30199
+ const match = packs.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name || path50.basename(entry) === name : entry.name === name);
29906
30200
  if (!match) throw new Error(`Project has no registered pack named "${name}".`);
29907
30201
  config.extensions = { packs: packs.filter((entry) => entry !== match), useGlobalPacks: hostCore.globalPacksEnabled(config) };
29908
30202
  saveProjectConfig(config);
29909
- const resolved = path47.resolve(root, typeof match === "string" ? match : path47.join(".wai", "packs", match.name));
29910
- const vendorDir = path47.resolve(root, ".wai", "packs");
29911
- if (resolved.startsWith(vendorDir + path47.sep)) {
29912
- fs38.rmSync(resolved, { recursive: true, force: true });
30203
+ const resolved = path50.resolve(root, typeof match === "string" ? match : path50.join(".wai", "packs", match.name));
30204
+ const vendorDir = path50.resolve(root, ".wai", "packs");
30205
+ if (resolved.startsWith(vendorDir + path50.sep)) {
30206
+ fs39.rmSync(resolved, { recursive: true, force: true });
29913
30207
  }
29914
30208
  }
29915
30209
  function readProjectReferences(rootPath) {
29916
- const projectId = path47.basename(rootPath);
30210
+ const projectId = path50.basename(rootPath);
29917
30211
  const empty = { projectId, packNames: [] };
29918
30212
  try {
29919
30213
  return runWithProjectRoot(rootPath, () => {
@@ -30036,9 +30330,9 @@ function installProjectPackArchive(cfg, credential, project2, archive, name) {
30036
30330
  }
30037
30331
 
30038
30332
  // src/server/audit.ts
30039
- var fs39 = __toESM(require("fs"));
30040
- var path48 = __toESM(require("path"));
30041
- var crypto9 = __toESM(require("crypto"));
30333
+ var fs40 = __toESM(require("fs"));
30334
+ var path51 = __toESM(require("path"));
30335
+ var crypto10 = __toESM(require("crypto"));
30042
30336
  var LEVEL_ORDER = {
30043
30337
  debug: 0,
30044
30338
  info: 1,
@@ -30060,13 +30354,13 @@ var DEFAULT_AUDIT_POLICY = {
30060
30354
  metadataMode: "redacted"
30061
30355
  };
30062
30356
  function storePath9(dataDir) {
30063
- return path48.join(dataDir, "audit-events.json");
30357
+ return path51.join(dataDir, "audit-events.json");
30064
30358
  }
30065
30359
  function readEvents(dataDir) {
30066
30360
  const p = storePath9(dataDir);
30067
30361
  let raw;
30068
30362
  try {
30069
- raw = fs39.readFileSync(p, "utf8");
30363
+ raw = fs40.readFileSync(p, "utf8");
30070
30364
  } catch (e) {
30071
30365
  if (e.code === "ENOENT") return [];
30072
30366
  throw new Error(`Failed to read audit store at ${p}: ${e.message}`);
@@ -30081,10 +30375,10 @@ function readEvents(dataDir) {
30081
30375
  }
30082
30376
  function persistEvents(dataDir, events) {
30083
30377
  const p = storePath9(dataDir);
30084
- fs39.mkdirSync(path48.dirname(p), { recursive: true });
30378
+ fs40.mkdirSync(path51.dirname(p), { recursive: true });
30085
30379
  const tmp = `${p}.tmp`;
30086
- fs39.writeFileSync(tmp, JSON.stringify(events, null, 2) + "\n");
30087
- fs39.renameSync(tmp, p);
30380
+ fs40.writeFileSync(tmp, JSON.stringify(events, null, 2) + "\n");
30381
+ fs40.renameSync(tmp, p);
30088
30382
  }
30089
30383
  var SECRET_RE = /Bearer\s+\S/i;
30090
30384
  var LONG_HEX_RE = /[0-9a-fA-F]{32,}/;
@@ -30142,7 +30436,7 @@ var AuditRegistry = class {
30142
30436
  }
30143
30437
  const stamped = {
30144
30438
  ...event,
30145
- id: event.id && event.id.length > 0 ? event.id : crypto9.randomUUID(),
30439
+ id: event.id && event.id.length > 0 ? event.id : crypto10.randomUUID(),
30146
30440
  timestamp: event.timestamp && event.timestamp.length > 0 ? event.timestamp : (/* @__PURE__ */ new Date()).toISOString(),
30147
30441
  metadata
30148
30442
  };
@@ -30409,10 +30703,10 @@ function unbindRole(cfg, credential, userId, roleId, scopeKind, scopeId) {
30409
30703
  }
30410
30704
 
30411
30705
  // src/server/identity.ts
30412
- var crypto11 = __toESM(require("crypto"));
30706
+ var crypto12 = __toESM(require("crypto"));
30413
30707
 
30414
30708
  // src/server/idp.ts
30415
- var crypto10 = __toESM(require("crypto"));
30709
+ var crypto11 = __toESM(require("crypto"));
30416
30710
  var REQUESTED_SCOPE = "openid email profile";
30417
30711
  var discoveryCache = /* @__PURE__ */ new Map();
30418
30712
  var jwksCache = /* @__PURE__ */ new Map();
@@ -30639,7 +30933,7 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
30639
30933
  if (!digest) return false;
30640
30934
  let keyObject;
30641
30935
  try {
30642
- keyObject = crypto10.createPublicKey({ key: jwk, format: "jwk" });
30936
+ keyObject = crypto11.createPublicKey({ key: jwk, format: "jwk" });
30643
30937
  } catch {
30644
30938
  return false;
30645
30939
  }
@@ -30647,18 +30941,18 @@ function verifyJwtSignature(alg, signingInput, jwk, sigB64url) {
30647
30941
  const sig = Buffer.from(sigB64url, "base64url");
30648
30942
  try {
30649
30943
  if (alg.startsWith("RS")) {
30650
- return crypto10.verify(digest, data, keyObject, sig);
30944
+ return crypto11.verify(digest, data, keyObject, sig);
30651
30945
  }
30652
30946
  if (alg.startsWith("PS")) {
30653
- return crypto10.verify(
30947
+ return crypto11.verify(
30654
30948
  digest,
30655
30949
  data,
30656
- { key: keyObject, padding: crypto10.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto10.constants.RSA_PSS_SALTLEN_DIGEST },
30950
+ { key: keyObject, padding: crypto11.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto11.constants.RSA_PSS_SALTLEN_DIGEST },
30657
30951
  sig
30658
30952
  );
30659
30953
  }
30660
30954
  if (alg.startsWith("ES")) {
30661
- return crypto10.verify(digest, data, { key: keyObject, dsaEncoding: "ieee-p1363" }, sig);
30955
+ return crypto11.verify(digest, data, { key: keyObject, dsaEncoding: "ieee-p1363" }, sig);
30662
30956
  }
30663
30957
  } catch {
30664
30958
  return false;
@@ -30772,8 +31066,8 @@ function assertAllowedRedirectUri(provider, redirectUri) {
30772
31066
  }
30773
31067
 
30774
31068
  // src/server/policy.ts
30775
- var fs40 = __toESM(require("fs"));
30776
- var path49 = __toESM(require("path"));
31069
+ var fs41 = __toESM(require("fs"));
31070
+ var path52 = __toESM(require("path"));
30777
31071
  init_fs();
30778
31072
  init_yaml();
30779
31073
  init_loader();
@@ -30792,13 +31086,13 @@ function sendJson(res, status2, body) {
30792
31086
 
30793
31087
  // src/server/policy.ts
30794
31088
  function packPolicyPath(dataDir) {
30795
- return path49.join(dataDir, "pack-policy.json");
31089
+ return path52.join(dataDir, "pack-policy.json");
30796
31090
  }
30797
31091
  function getPackPolicyRecord(dataDir) {
30798
31092
  const p = packPolicyPath(dataDir);
30799
31093
  let raw;
30800
31094
  try {
30801
- raw = fs40.readFileSync(p, "utf8");
31095
+ raw = fs41.readFileSync(p, "utf8");
30802
31096
  } catch (e) {
30803
31097
  if (e.code === "ENOENT") return null;
30804
31098
  throw new Error(`Failed to read pack policy store at ${p}: ${e.message}`);
@@ -30812,14 +31106,14 @@ function getPackPolicyRecord(dataDir) {
30812
31106
  function setPackPolicyRecord(dataDir, policy) {
30813
31107
  const stored = { ...policy, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
30814
31108
  const p = packPolicyPath(dataDir);
30815
- fs40.mkdirSync(path49.dirname(p), { recursive: true });
31109
+ fs41.mkdirSync(path52.dirname(p), { recursive: true });
30816
31110
  const tmp = `${p}.tmp`;
30817
- fs40.writeFileSync(tmp, JSON.stringify(stored, null, 2) + "\n");
30818
- fs40.renameSync(tmp, p);
31111
+ fs41.writeFileSync(tmp, JSON.stringify(stored, null, 2) + "\n");
31112
+ fs41.renameSync(tmp, p);
30819
31113
  return stored;
30820
31114
  }
30821
31115
  function exposurePolicyPath(dataDir) {
30822
- return path49.join(dataDir, "exposure-policy.json");
31116
+ return path52.join(dataDir, "exposure-policy.json");
30823
31117
  }
30824
31118
  var COMPATIBLE_DEFAULT_EXPOSURE = {
30825
31119
  adminApiMode: "local_only",
@@ -30836,7 +31130,7 @@ function getExposurePolicyRecord(dataDir) {
30836
31130
  const p = exposurePolicyPath(dataDir);
30837
31131
  let raw;
30838
31132
  try {
30839
- raw = fs40.readFileSync(p, "utf8");
31133
+ raw = fs41.readFileSync(p, "utf8");
30840
31134
  } catch (e) {
30841
31135
  if (e.code === "ENOENT") return null;
30842
31136
  throw new Error(`Failed to read exposure policy store at ${p}: ${e.message}`);
@@ -30849,10 +31143,10 @@ function getExposurePolicyRecord(dataDir) {
30849
31143
  }
30850
31144
  function setExposurePolicyRecord(dataDir, policy) {
30851
31145
  const p = exposurePolicyPath(dataDir);
30852
- fs40.mkdirSync(path49.dirname(p), { recursive: true });
31146
+ fs41.mkdirSync(path52.dirname(p), { recursive: true });
30853
31147
  const tmp = `${p}.tmp`;
30854
- fs40.writeFileSync(tmp, JSON.stringify(policy, null, 2) + "\n");
30855
- fs40.renameSync(tmp, p);
31148
+ fs41.writeFileSync(tmp, JSON.stringify(policy, null, 2) + "\n");
31149
+ fs41.renameSync(tmp, p);
30856
31150
  return policy;
30857
31151
  }
30858
31152
  var PERMISSIVE_DEFAULT_POLICY = {
@@ -30870,7 +31164,7 @@ function effectivePolicy(dataDir) {
30870
31164
  return getPackPolicyRecord(dataDir) ?? PERMISSIVE_DEFAULT_POLICY;
30871
31165
  }
30872
31166
  function identityProvidersPath(dataDir) {
30873
- return path49.join(dataDir, "identity-providers.json");
31167
+ return path52.join(dataDir, "identity-providers.json");
30874
31168
  }
30875
31169
  function sanitizeIdentityProvider(config) {
30876
31170
  const clean = {
@@ -30894,16 +31188,16 @@ function sanitizeIdentityProvider(config) {
30894
31188
  }
30895
31189
  function writeIdentityProviderRecords(dataDir, records) {
30896
31190
  const p = identityProvidersPath(dataDir);
30897
- fs40.mkdirSync(path49.dirname(p), { recursive: true });
31191
+ fs41.mkdirSync(path52.dirname(p), { recursive: true });
30898
31192
  const tmp = `${p}.tmp`;
30899
- fs40.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
30900
- fs40.renameSync(tmp, p);
31193
+ fs41.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
31194
+ fs41.renameSync(tmp, p);
30901
31195
  }
30902
31196
  function listIdentityProviderRecords(dataDir) {
30903
31197
  const p = identityProvidersPath(dataDir);
30904
31198
  let raw;
30905
31199
  try {
30906
- raw = fs40.readFileSync(p, "utf8");
31200
+ raw = fs41.readFileSync(p, "utf8");
30907
31201
  } catch (e) {
30908
31202
  if (e.code === "ENOENT") return [];
30909
31203
  throw new Error(`Failed to read identity provider store at ${p}: ${e.message}`);
@@ -31530,9 +31824,9 @@ function mintToken(cfg, credential, request) {
31530
31824
  if (owner && owner.status !== "active") {
31531
31825
  throw new ForbiddenError("cannot mint a token for a deactivated user");
31532
31826
  }
31533
- const token = "wk_" + crypto11.randomBytes(24).toString("hex");
31827
+ const token = "wk_" + crypto12.randomBytes(24).toString("hex");
31534
31828
  const record2 = {
31535
- id: crypto11.randomBytes(6).toString("hex"),
31829
+ id: crypto12.randomBytes(6).toString("hex"),
31536
31830
  keyHash: hashToken(token),
31537
31831
  projects,
31538
31832
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -31576,10 +31870,10 @@ function mintSelfToken(cfg, credential, projectId, write) {
31576
31870
  if (parsed.mounts.length > 0) {
31577
31871
  assertMintableNarrowingEntry(cfg.dataDir, projectId);
31578
31872
  }
31579
- const token = "wk_" + crypto11.randomBytes(24).toString("hex");
31873
+ const token = "wk_" + crypto12.randomBytes(24).toString("hex");
31580
31874
  const owner = auditActor(principal);
31581
31875
  const record2 = {
31582
- id: crypto11.randomBytes(6).toString("hex"),
31876
+ id: crypto12.randomBytes(6).toString("hex"),
31583
31877
  keyHash: hashToken(token),
31584
31878
  projects: [projectId],
31585
31879
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -31694,7 +31988,7 @@ function resolveEnabledProvider(cfg, providerId) {
31694
31988
  }
31695
31989
  async function startSsoLogin(cfg, providerId, redirectUri) {
31696
31990
  const provider = resolveEnabledProvider(cfg, providerId);
31697
- const nonce = crypto11.randomBytes(16).toString("hex");
31991
+ const nonce = crypto12.randomBytes(16).toString("hex");
31698
31992
  const payload = { providerId, nonce, redirectUri };
31699
31993
  const state = signSsoState(JSON.stringify(payload));
31700
31994
  const endpoints = await resolveEndpoints(provider);
@@ -31745,9 +32039,9 @@ async function completeSsoLogin(cfg, state, code) {
31745
32039
  ...subject.email ? { email: subject.email } : {}
31746
32040
  });
31747
32041
  }
31748
- const token = "wk_" + crypto11.randomBytes(24).toString("hex");
32042
+ const token = "wk_" + crypto12.randomBytes(24).toString("hex");
31749
32043
  const record2 = {
31750
- id: crypto11.randomBytes(6).toString("hex"),
32044
+ id: crypto12.randomBytes(6).toString("hex"),
31751
32045
  keyHash: hashToken(token),
31752
32046
  projects: ["*"],
31753
32047
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -31879,17 +32173,17 @@ init_yaml();
31879
32173
  init_loader();
31880
32174
 
31881
32175
  // src/server/relations.ts
31882
- var fs41 = __toESM(require("fs"));
31883
- var path50 = __toESM(require("path"));
31884
- var crypto12 = __toESM(require("crypto"));
32176
+ var fs42 = __toESM(require("fs"));
32177
+ var path53 = __toESM(require("path"));
32178
+ var crypto13 = __toESM(require("crypto"));
31885
32179
  function storePath10(dataDir) {
31886
- return path50.join(dataDir, "relations.json");
32180
+ return path53.join(dataDir, "relations.json");
31887
32181
  }
31888
32182
  function readRelations(dataDir) {
31889
32183
  const p = storePath10(dataDir);
31890
32184
  let raw;
31891
32185
  try {
31892
- raw = fs41.readFileSync(p, "utf8");
32186
+ raw = fs42.readFileSync(p, "utf8");
31893
32187
  } catch (e) {
31894
32188
  if (e.code === "ENOENT") return [];
31895
32189
  throw new Error(`Failed to read relation store at ${p}: ${e.message}`);
@@ -31904,10 +32198,10 @@ function readRelations(dataDir) {
31904
32198
  }
31905
32199
  function persistRelations(dataDir, relations) {
31906
32200
  const p = storePath10(dataDir);
31907
- fs41.mkdirSync(path50.dirname(p), { recursive: true });
32201
+ fs42.mkdirSync(path53.dirname(p), { recursive: true });
31908
32202
  const tmp = `${p}.tmp`;
31909
- fs41.writeFileSync(tmp, JSON.stringify(relations, null, 2) + "\n");
31910
- fs41.renameSync(tmp, p);
32203
+ fs42.writeFileSync(tmp, JSON.stringify(relations, null, 2) + "\n");
32204
+ fs42.renameSync(tmp, p);
31911
32205
  }
31912
32206
  var ProjectRelationStore = class {
31913
32207
  constructor(dataDir) {
@@ -31950,7 +32244,7 @@ var ProjectRelationRegistry = class {
31950
32244
  const hasStatus = typeof record2.status === "string" && record2.status.trim().length > 0;
31951
32245
  const stored = {
31952
32246
  ...record2,
31953
- id: hasId ? record2.id : crypto12.randomUUID(),
32247
+ id: hasId ? record2.id : crypto13.randomUUID(),
31954
32248
  status: hasStatus ? record2.status : "active"
31955
32249
  };
31956
32250
  const relations = this.store.all();
@@ -32017,16 +32311,16 @@ function listProjectRelations(dataDir, sourceProjectId, targetProjectId, status2
32017
32311
  }
32018
32312
 
32019
32313
  // src/server/surfaces.ts
32020
- var fs42 = __toESM(require("fs"));
32021
- var path51 = __toESM(require("path"));
32314
+ var fs43 = __toESM(require("fs"));
32315
+ var path54 = __toESM(require("path"));
32022
32316
  function storePath11(dataDir) {
32023
- return path51.join(dataDir, "public-surfaces.json");
32317
+ return path54.join(dataDir, "public-surfaces.json");
32024
32318
  }
32025
32319
  function readSnapshots(dataDir) {
32026
32320
  const p = storePath11(dataDir);
32027
32321
  let raw;
32028
32322
  try {
32029
- raw = fs42.readFileSync(p, "utf8");
32323
+ raw = fs43.readFileSync(p, "utf8");
32030
32324
  } catch (e) {
32031
32325
  if (e.code === "ENOENT") return [];
32032
32326
  throw new Error(`Failed to read public surface store at ${p}: ${e.message}`);
@@ -32041,10 +32335,10 @@ function readSnapshots(dataDir) {
32041
32335
  }
32042
32336
  function persistSnapshots(dataDir, snapshots) {
32043
32337
  const p = storePath11(dataDir);
32044
- fs42.mkdirSync(path51.dirname(p), { recursive: true });
32338
+ fs43.mkdirSync(path54.dirname(p), { recursive: true });
32045
32339
  const tmp = `${p}.tmp`;
32046
- fs42.writeFileSync(tmp, JSON.stringify(snapshots, null, 2) + "\n");
32047
- fs42.renameSync(tmp, p);
32340
+ fs43.writeFileSync(tmp, JSON.stringify(snapshots, null, 2) + "\n");
32341
+ fs43.renameSync(tmp, p);
32048
32342
  }
32049
32343
  var PublicSurfaceStore = class {
32050
32344
  constructor(dataDir) {
@@ -32187,12 +32481,12 @@ function resolveVisibility(observerProjectId, units, placements) {
32187
32481
  const best = /* @__PURE__ */ new Map();
32188
32482
  for (const placement of placements) {
32189
32483
  if (placement.projectId === observerProjectId) continue;
32190
- const path64 = chainOf(placement.unitId, unitById);
32191
- if (!path64.length) continue;
32192
- const closedOk = path64.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
32484
+ const path67 = chainOf(placement.unitId, unitById);
32485
+ if (!path67.length) continue;
32486
+ const closedOk = path67.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
32193
32487
  if (!closedOk) continue;
32194
- const crossTenant = !tenantRoots.has(path64[path64.length - 1].id);
32195
- if (crossTenant && !path64.some(grantedTo)) continue;
32488
+ const crossTenant = !tenantRoots.has(path67[path67.length - 1].id);
32489
+ if (crossTenant && !path67.some(grantedTo)) continue;
32196
32490
  if (crossTenant && directUnits.length === 0) continue;
32197
32491
  const distance = crossTenant ? "partner" : sameBranch(placement.unitId) ? "department" : "instance";
32198
32492
  const existing = best.get(placement.projectId);
@@ -32838,8 +33132,8 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
32838
33132
  }
32839
33133
 
32840
33134
  // src/server/migration.ts
32841
- var fs43 = __toESM(require("fs"));
32842
- var path52 = __toESM(require("path"));
33135
+ var fs44 = __toESM(require("fs"));
33136
+ var path55 = __toESM(require("path"));
32843
33137
  var LEGACY_CAPABILITY_MAP = {
32844
33138
  "mcp:read": "project:read",
32845
33139
  "operations:read": "project:read",
@@ -32871,16 +33165,16 @@ function scopeOf(grant) {
32871
33165
  }
32872
33166
  function readRaw(file) {
32873
33167
  try {
32874
- return JSON.parse(fs43.readFileSync(file, "utf8"));
33168
+ return JSON.parse(fs44.readFileSync(file, "utf8"));
32875
33169
  } catch {
32876
33170
  return null;
32877
33171
  }
32878
33172
  }
32879
33173
  function writeRaw(file, value) {
32880
- fs43.mkdirSync(path52.dirname(file), { recursive: true });
33174
+ fs44.mkdirSync(path55.dirname(file), { recursive: true });
32881
33175
  const tmp = `${file}.tmp`;
32882
- fs43.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
32883
- fs43.renameSync(tmp, file);
33176
+ fs44.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
33177
+ fs44.renameSync(tmp, file);
32884
33178
  }
32885
33179
  function slugify(name) {
32886
33180
  return name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "unit";
@@ -32914,13 +33208,13 @@ function migratePermissionModel(dataDir, apply) {
32914
33208
  findings.push({ area, detail });
32915
33209
  };
32916
33210
  const grid = readRaw(
32917
- path52.join(dataDir, "permissions.json")
33211
+ path55.join(dataDir, "permissions.json")
32918
33212
  ) ?? [];
32919
33213
  if (!getInstanceIdentity(dataDir)) {
32920
33214
  found("instance", "instance identity is not seeded \u2014 the built-in admin/dev subjects have no persisted UUIDs");
32921
33215
  if (apply) ensureInstanceIdentity(dataDir);
32922
33216
  }
32923
- const orgPath = path52.join(dataDir, "organization.json");
33217
+ const orgPath = path55.join(dataDir, "organization.json");
32924
33218
  const org = readRaw(orgPath);
32925
33219
  if (org?.units?.length) {
32926
33220
  const units = org.units;
@@ -32974,7 +33268,7 @@ function migratePermissionModel(dataDir, apply) {
32974
33268
  found("units", `${remap.length} unit id(s) would be remapped across placements, exposeTo, assignments, and users`);
32975
33269
  }
32976
33270
  }
32977
- const usersPath = path52.join(dataDir, "users.json");
33271
+ const usersPath = path55.join(dataDir, "users.json");
32978
33272
  const users = readRaw(usersPath);
32979
33273
  if (users) {
32980
33274
  let changed = false;
@@ -33001,7 +33295,7 @@ function migratePermissionModel(dataDir, apply) {
33001
33295
  }
33002
33296
  if (apply && changed) writeRaw(usersPath, users);
33003
33297
  }
33004
- const keysPath = path52.join(dataDir, "auth", "credentials.json");
33298
+ const keysPath = path55.join(dataDir, "auth", "credentials.json");
33005
33299
  const keys = readRaw(keysPath);
33006
33300
  if (keys) {
33007
33301
  let changed = false;
@@ -33051,7 +33345,7 @@ function migratePermissionModel(dataDir, apply) {
33051
33345
  }
33052
33346
  if (apply && changed) writeRaw(keysPath, keys);
33053
33347
  }
33054
- const sessionsPath = path52.join(dataDir, "web-sessions.json");
33348
+ const sessionsPath = path55.join(dataDir, "web-sessions.json");
33055
33349
  const sessions = readRaw(sessionsPath);
33056
33350
  if (sessions) {
33057
33351
  let changed = false;
@@ -33130,25 +33424,25 @@ function migratePermissionModel(dataDir, apply) {
33130
33424
 
33131
33425
  // src/server/http.ts
33132
33426
  var http2 = __toESM(require("http"));
33133
- var fs51 = __toESM(require("fs"));
33134
- var path60 = __toESM(require("path"));
33427
+ var fs52 = __toESM(require("fs"));
33428
+ var path63 = __toESM(require("path"));
33135
33429
 
33136
33430
  // src/server/request.ts
33137
33431
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
33138
33432
  init_fs();
33139
33433
 
33140
33434
  // src/server/approvals.ts
33141
- var fs44 = __toESM(require("fs"));
33142
- var path53 = __toESM(require("path"));
33143
- var crypto13 = __toESM(require("crypto"));
33435
+ var fs45 = __toESM(require("fs"));
33436
+ var path56 = __toESM(require("path"));
33437
+ var crypto14 = __toESM(require("crypto"));
33144
33438
  function storePath12(dataDir) {
33145
- return path53.join(dataDir, "approvals.json");
33439
+ return path56.join(dataDir, "approvals.json");
33146
33440
  }
33147
33441
  function readRequests(dataDir) {
33148
33442
  const p = storePath12(dataDir);
33149
33443
  let raw;
33150
33444
  try {
33151
- raw = fs44.readFileSync(p, "utf8");
33445
+ raw = fs45.readFileSync(p, "utf8");
33152
33446
  } catch (e) {
33153
33447
  if (e.code === "ENOENT") return [];
33154
33448
  throw new Error(`Failed to read approval store at ${p}: ${e.message}`);
@@ -33163,10 +33457,10 @@ function readRequests(dataDir) {
33163
33457
  }
33164
33458
  function persistRequests(dataDir, requests) {
33165
33459
  const p = storePath12(dataDir);
33166
- fs44.mkdirSync(path53.dirname(p), { recursive: true });
33460
+ fs45.mkdirSync(path56.dirname(p), { recursive: true });
33167
33461
  const tmp = `${p}.tmp`;
33168
- fs44.writeFileSync(tmp, JSON.stringify(requests, null, 2) + "\n");
33169
- fs44.renameSync(tmp, p);
33462
+ fs45.writeFileSync(tmp, JSON.stringify(requests, null, 2) + "\n");
33463
+ fs45.renameSync(tmp, p);
33170
33464
  }
33171
33465
  var SECRET_RE2 = /Bearer\s+\S/i;
33172
33466
  var LONG_HEX_RE2 = /[0-9a-fA-F]{32,}/;
@@ -33214,7 +33508,7 @@ var ApprovalRegistry = class {
33214
33508
  }
33215
33509
  const stored = {
33216
33510
  ...request,
33217
- id: crypto13.randomUUID(),
33511
+ id: crypto14.randomUUID(),
33218
33512
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
33219
33513
  status: "pending"
33220
33514
  };
@@ -33523,23 +33817,6 @@ function lockProject2(cfg, credential, projectId, subproject) {
33523
33817
  }
33524
33818
  });
33525
33819
  }
33526
- function promoteProject2(cfg, credential, projectId, subproject) {
33527
- return lifecycleAction(cfg, credential, projectId, {
33528
- action: "project:promote",
33529
- verb: "Promote",
33530
- noun: "promotion",
33531
- subproject,
33532
- execute: () => {
33533
- const promo = executeApprovedPromote(cfg, projectId, subproject);
33534
- return {
33535
- status: "completed",
33536
- action: "project:promote",
33537
- summary: `Promotion of project "${projectId}"${subprojectSuffix(subproject)}: ${promo.status} \u2014 ${promo.message}`,
33538
- promote: promo
33539
- };
33540
- }
33541
- });
33542
- }
33543
33820
  function subprojectSuffix(subproject) {
33544
33821
  return subproject ? ` subproject "${subproject}"` : "";
33545
33822
  }
@@ -33663,11 +33940,6 @@ function executeApproved(cfg, req) {
33663
33940
  const lock = executeApprovedLock(cfg, req.projectId ?? "", scope);
33664
33941
  return `Locked project "${req.projectId}"${subprojectSuffix(scope)} (status: ${lock.status}).`;
33665
33942
  }
33666
- case "project:promote": {
33667
- const scope = readSubprojectScope(req);
33668
- const promo = executeApprovedPromote(cfg, req.projectId ?? "", scope);
33669
- return `Promotion of project "${req.projectId}"${subprojectSuffix(scope)}: ${promo.status} \u2014 ${promo.message}`;
33670
- }
33671
33943
  default:
33672
33944
  throw new Error(`Unsupported approval kind "${req.kind}".`);
33673
33945
  }
@@ -33709,7 +33981,7 @@ async function awaitApproval(cfg, credential, requestId, timeoutSeconds) {
33709
33981
  let current2 = req;
33710
33982
  while (current2.status === "pending" && Date.now() < deadline) {
33711
33983
  const remaining = deadline - Date.now();
33712
- await new Promise((resolve26) => setTimeout(resolve26, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
33984
+ await new Promise((resolve27) => setTimeout(resolve27, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
33713
33985
  expirePendingApprovals(cfg.dataDir, (/* @__PURE__ */ new Date()).toISOString());
33714
33986
  current2 = getApprovalRequestById(cfg.dataDir, requestId) ?? current2;
33715
33987
  }
@@ -33977,12 +34249,12 @@ function handleOperationsRequest(cfg, credential, req, res, url) {
33977
34249
  }
33978
34250
 
33979
34251
  // src/server/gitbacking.ts
33980
- var fs45 = __toESM(require("fs"));
33981
- var path54 = __toESM(require("path"));
33982
- var crypto14 = __toESM(require("crypto"));
34252
+ var fs46 = __toESM(require("fs"));
34253
+ var path57 = __toESM(require("path"));
34254
+ var crypto15 = __toESM(require("crypto"));
33983
34255
  var import_child_process4 = require("child_process");
33984
34256
  function storePath13(dataDir) {
33985
- return path54.join(dataDir, "git-backing.json");
34257
+ return path57.join(dataDir, "git-backing.json");
33986
34258
  }
33987
34259
  var GitBackingStore = class {
33988
34260
  constructor(dataDir) {
@@ -33994,7 +34266,7 @@ var GitBackingStore = class {
33994
34266
  const p = storePath13(this.dataDir);
33995
34267
  let raw;
33996
34268
  try {
33997
- raw = fs45.readFileSync(p, "utf8");
34269
+ raw = fs46.readFileSync(p, "utf8");
33998
34270
  } catch (err) {
33999
34271
  if (err.code === "ENOENT") return [];
34000
34272
  throw new Error(`Cannot read git-backing store at ${p}: ${err.message}`);
@@ -34013,10 +34285,10 @@ var GitBackingStore = class {
34013
34285
  /** Swap the persisted collection wholesale via write-temp-then-rename. */
34014
34286
  replaceAll(bindings) {
34015
34287
  const p = storePath13(this.dataDir);
34016
- fs45.mkdirSync(path54.dirname(p), { recursive: true });
34288
+ fs46.mkdirSync(path57.dirname(p), { recursive: true });
34017
34289
  const tmp = `${p}.tmp`;
34018
- fs45.writeFileSync(tmp, JSON.stringify(bindings, null, 2) + "\n");
34019
- fs45.renameSync(tmp, p);
34290
+ fs46.writeFileSync(tmp, JSON.stringify(bindings, null, 2) + "\n");
34291
+ fs46.renameSync(tmp, p);
34020
34292
  }
34021
34293
  };
34022
34294
  var GitBackingRegistry = class {
@@ -34035,7 +34307,7 @@ var GitBackingRegistry = class {
34035
34307
  const bindings = this.store.load();
34036
34308
  const stored = {
34037
34309
  ...binding,
34038
- id: binding.id || crypto14.randomUUID(),
34310
+ id: binding.id || crypto15.randomUUID(),
34039
34311
  createdAt: binding.createdAt || (/* @__PURE__ */ new Date()).toISOString()
34040
34312
  };
34041
34313
  const sameScope = (b) => b.scopeKind === stored.scopeKind && (b.scopeId ?? "") === (stored.scopeId ?? "");
@@ -34121,8 +34393,8 @@ function checkoutBranch(workdir, branch) {
34121
34393
  }
34122
34394
  }
34123
34395
  function cloneOrOpen(remote, branch, workdir, credentialRef) {
34124
- if (!fs45.existsSync(path54.join(workdir, ".git"))) {
34125
- fs45.mkdirSync(workdir, { recursive: true });
34396
+ if (!fs46.existsSync(path57.join(workdir, ".git"))) {
34397
+ fs46.mkdirSync(workdir, { recursive: true });
34126
34398
  git2(["clone", authRemote2(remote, credentialRef), "."], workdir);
34127
34399
  git2(["config", "user.name", process.env["WAIRON_GIT_NAME"] || "wairon-bot"], workdir);
34128
34400
  git2(["config", "user.email", process.env["WAIRON_GIT_EMAIL"] || "wairon-bot@localhost"], workdir);
@@ -34134,11 +34406,11 @@ function cloneOrOpen(remote, branch, workdir, credentialRef) {
34134
34406
  return workdir;
34135
34407
  }
34136
34408
  function mirrorTree(sourceDir, targetDir) {
34137
- fs45.rmSync(targetDir, { recursive: true, force: true });
34138
- const stat = fs45.statSync(sourceDir, { throwIfNoEntry: false });
34409
+ fs46.rmSync(targetDir, { recursive: true, force: true });
34410
+ const stat = fs46.statSync(sourceDir, { throwIfNoEntry: false });
34139
34411
  if (!stat) return;
34140
- fs45.mkdirSync(path54.dirname(targetDir), { recursive: true });
34141
- fs45.cpSync(sourceDir, targetDir, { recursive: true });
34412
+ fs46.mkdirSync(path57.dirname(targetDir), { recursive: true });
34413
+ fs46.cpSync(sourceDir, targetDir, { recursive: true });
34142
34414
  }
34143
34415
  function commitAndPush(workdir, message) {
34144
34416
  git2(["add", "-A"], workdir);
@@ -34260,7 +34532,7 @@ function unbindScope(cfg, credential, bindingId) {
34260
34532
  tryAppendAudit7(cfg, buildAuditEvent6(principal, "git.backing.unbind", "security", bindingId));
34261
34533
  }
34262
34534
  function runMirrorSync(cfg, binding) {
34263
- const workdir = path54.join(cfg.dataDir, "git-backing", binding.id);
34535
+ const workdir = path57.join(cfg.dataDir, "git-backing", binding.id);
34264
34536
  cloneOrOpen(binding.remote, binding.branch, workdir, binding.credentialRef);
34265
34537
  if (binding.scopeKind === "unit") {
34266
34538
  const units = listOrganizationUnits(cfg.dataDir);
@@ -34268,23 +34540,23 @@ function runMirrorSync(cfg, binding) {
34268
34540
  const placedIds = new Set(
34269
34541
  listProjectPlacements(cfg.dataDir).filter((p) => subtree.has(p.unitId)).map((p) => p.projectId)
34270
34542
  );
34271
- const projectsDir = path54.join(workdir, "projects");
34272
- fs45.rmSync(projectsDir, { recursive: true, force: true });
34543
+ const projectsDir = path57.join(workdir, "projects");
34544
+ fs46.rmSync(projectsDir, { recursive: true, force: true });
34273
34545
  for (const rec of listProjectRecords(cfg.dataDir)) {
34274
34546
  if (!placedIds.has(rec.id)) continue;
34275
- mirrorTree(path54.join(rec.rootPath, ".wai"), path54.join(projectsDir, rec.id, ".wai"));
34547
+ mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
34276
34548
  }
34277
34549
  } else {
34278
- const instanceDir = path54.join(workdir, "instance");
34279
- fs45.rmSync(instanceDir, { recursive: true, force: true });
34550
+ const instanceDir = path57.join(workdir, "instance");
34551
+ fs46.rmSync(instanceDir, { recursive: true, force: true });
34280
34552
  const files = binding.includeCredentials ? INSTANCE_STRUCTURE_FILES : INSTANCE_STRUCTURE_FILES.filter((rel2) => rel2 !== "auth/credentials.json");
34281
34553
  for (const rel2 of files) {
34282
- mirrorTree(path54.join(cfg.dataDir, rel2), path54.join(instanceDir, rel2));
34554
+ mirrorTree(path57.join(cfg.dataDir, rel2), path57.join(instanceDir, rel2));
34283
34555
  }
34284
- const projectsDir = path54.join(workdir, "projects");
34285
- fs45.rmSync(projectsDir, { recursive: true, force: true });
34556
+ const projectsDir = path57.join(workdir, "projects");
34557
+ fs46.rmSync(projectsDir, { recursive: true, force: true });
34286
34558
  for (const rec of listProjectRecords(cfg.dataDir)) {
34287
- mirrorTree(path54.join(rec.rootPath, ".wai"), path54.join(projectsDir, rec.id, ".wai"));
34559
+ mirrorTree(path57.join(rec.rootPath, ".wai"), path57.join(projectsDir, rec.id, ".wai"));
34288
34560
  }
34289
34561
  }
34290
34562
  const published = commitAndPush(
@@ -34430,7 +34702,7 @@ function setExposurePolicy2(cfg, credential, exposure) {
34430
34702
 
34431
34703
  // src/server/websocket.ts
34432
34704
  var import_events = require("events");
34433
- var crypto15 = __toESM(require("crypto"));
34705
+ var crypto16 = __toESM(require("crypto"));
34434
34706
  var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
34435
34707
  var MAX_PAYLOAD = 1 << 20;
34436
34708
  var OP_CONTINUATION = 0;
@@ -34444,7 +34716,7 @@ function isWebSocketUpgrade(req) {
34444
34716
  function acceptWebSocket(req, socket) {
34445
34717
  const key = req.headers["sec-websocket-key"];
34446
34718
  if (!isWebSocketUpgrade(req) || typeof key !== "string") return null;
34447
- const accept = crypto15.createHash("sha1").update(key + WS_GUID).digest("base64");
34719
+ const accept = crypto16.createHash("sha1").update(key + WS_GUID).digest("base64");
34448
34720
  socket.write(
34449
34721
  `HTTP/1.1 101 Switching Protocols\r
34450
34722
  Upgrade: websocket\r
@@ -34594,9 +34866,9 @@ function encodeFrame(opcode, payload) {
34594
34866
  }
34595
34867
 
34596
34868
  // src/server/web.ts
34597
- var crypto20 = __toESM(require("crypto"));
34598
- var fs50 = __toESM(require("fs"));
34599
- var path59 = __toESM(require("path"));
34869
+ var crypto21 = __toESM(require("crypto"));
34870
+ var fs51 = __toESM(require("fs"));
34871
+ var path62 = __toESM(require("path"));
34600
34872
 
34601
34873
  // src/server/webadmin.ts
34602
34874
  function listUsers3(cfg, sessionId, project2) {
@@ -34838,26 +35110,23 @@ function createProject2(cfg, sessionId, id, unitId, profileSelection) {
34838
35110
  function lockProject3(cfg, sessionId, projectId) {
34839
35111
  return lockProject(cfg, sessionId, projectId);
34840
35112
  }
34841
- function promoteProject3(cfg, sessionId, projectId) {
34842
- return promoteProject(cfg, sessionId, projectId);
34843
- }
34844
35113
  function destroyProject2(cfg, sessionId, id) {
34845
35114
  destroyProject(cfg, sessionId, id);
34846
35115
  }
34847
35116
 
34848
35117
  // src/server/shareadmin.ts
34849
- var crypto19 = __toESM(require("crypto"));
35118
+ var crypto20 = __toESM(require("crypto"));
34850
35119
 
34851
35120
  // src/server/sharesnapshots.ts
34852
- var fs46 = __toESM(require("fs"));
34853
- var path55 = __toESM(require("path"));
34854
- var crypto16 = __toESM(require("crypto"));
35121
+ var fs47 = __toESM(require("fs"));
35122
+ var path58 = __toESM(require("path"));
35123
+ var crypto17 = __toESM(require("crypto"));
34855
35124
  init_fs();
34856
35125
  function snapshotDir(dataDir) {
34857
- return path55.join(dataDir, "share-snapshots");
35126
+ return path58.join(dataDir, "share-snapshots");
34858
35127
  }
34859
35128
  function snapshotPath(dataDir, id) {
34860
- return path55.join(snapshotDir(dataDir), `${id}.json`);
35129
+ return path58.join(snapshotDir(dataDir), `${id}.json`);
34861
35130
  }
34862
35131
  var ShareSnapshotStore = class {
34863
35132
  constructor(dataDir) {
@@ -34865,17 +35134,17 @@ var ShareSnapshotStore = class {
34865
35134
  }
34866
35135
  write(snapshot) {
34867
35136
  const p = snapshotPath(this.dataDir, snapshot.id);
34868
- if (fs46.existsSync(p)) return;
34869
- fs46.mkdirSync(path55.dirname(p), { recursive: true });
35137
+ if (fs47.existsSync(p)) return;
35138
+ fs47.mkdirSync(path58.dirname(p), { recursive: true });
34870
35139
  const tmp = `${p}.tmp`;
34871
- fs46.writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n");
34872
- fs46.renameSync(tmp, p);
35140
+ fs47.writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n");
35141
+ fs47.renameSync(tmp, p);
34873
35142
  }
34874
35143
  read(snapshotId) {
34875
35144
  const p = snapshotPath(this.dataDir, snapshotId);
34876
35145
  let raw;
34877
35146
  try {
34878
- raw = fs46.readFileSync(p, "utf8");
35147
+ raw = fs47.readFileSync(p, "utf8");
34879
35148
  } catch (err) {
34880
35149
  if (err.code === "ENOENT") return null;
34881
35150
  throw new Error(`Cannot read share snapshot at ${p}: ${err.message}`);
@@ -34894,7 +35163,7 @@ var ShareSnapshotRegistry = class {
34894
35163
  put(snapshot) {
34895
35164
  const stored = {
34896
35165
  ...snapshot,
34897
- id: snapshot.id || crypto16.randomUUID(),
35166
+ id: snapshot.id || crypto17.randomUUID(),
34898
35167
  capturedAt: snapshot.capturedAt || (/* @__PURE__ */ new Date()).toISOString()
34899
35168
  };
34900
35169
  this.store.write(stored);
@@ -34967,11 +35236,11 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
34967
35236
  }
34968
35237
 
34969
35238
  // src/server/sharelinks.ts
34970
- var fs47 = __toESM(require("fs"));
34971
- var path56 = __toESM(require("path"));
34972
- var crypto17 = __toESM(require("crypto"));
35239
+ var fs48 = __toESM(require("fs"));
35240
+ var path59 = __toESM(require("path"));
35241
+ var crypto18 = __toESM(require("crypto"));
34973
35242
  function storePath14(dataDir) {
34974
- return path56.join(dataDir, "share-links.json");
35243
+ return path59.join(dataDir, "share-links.json");
34975
35244
  }
34976
35245
  var ShareLinkStore = class {
34977
35246
  constructor(dataDir) {
@@ -34981,7 +35250,7 @@ var ShareLinkStore = class {
34981
35250
  const p = storePath14(this.dataDir);
34982
35251
  let raw;
34983
35252
  try {
34984
- raw = fs47.readFileSync(p, "utf8");
35253
+ raw = fs48.readFileSync(p, "utf8");
34985
35254
  } catch (err) {
34986
35255
  if (err.code === "ENOENT") return [];
34987
35256
  throw new Error(`Cannot read share-link store at ${p}: ${err.message}`);
@@ -34999,10 +35268,10 @@ var ShareLinkStore = class {
34999
35268
  }
35000
35269
  replaceAll(links) {
35001
35270
  const p = storePath14(this.dataDir);
35002
- fs47.mkdirSync(path56.dirname(p), { recursive: true });
35271
+ fs48.mkdirSync(path59.dirname(p), { recursive: true });
35003
35272
  const tmp = `${p}.tmp`;
35004
- fs47.writeFileSync(tmp, JSON.stringify(links, null, 2) + "\n");
35005
- fs47.renameSync(tmp, p);
35273
+ fs48.writeFileSync(tmp, JSON.stringify(links, null, 2) + "\n");
35274
+ fs48.renameSync(tmp, p);
35006
35275
  }
35007
35276
  };
35008
35277
  var ShareLinkRegistry = class {
@@ -35013,7 +35282,7 @@ var ShareLinkRegistry = class {
35013
35282
  const links = this.store.load();
35014
35283
  const stored = {
35015
35284
  ...link,
35016
- id: link.id || crypto17.randomUUID(),
35285
+ id: link.id || crypto18.randomUUID(),
35017
35286
  createdAt: link.createdAt || (/* @__PURE__ */ new Date()).toISOString()
35018
35287
  };
35019
35288
  this.store.replaceAll([...links, stored]);
@@ -35070,11 +35339,11 @@ function listProjectLinks(dataDir, projectId) {
35070
35339
  }
35071
35340
 
35072
35341
  // src/server/shareaccesslog.ts
35073
- var fs48 = __toESM(require("fs"));
35074
- var path57 = __toESM(require("path"));
35075
- var crypto18 = __toESM(require("crypto"));
35342
+ var fs49 = __toESM(require("fs"));
35343
+ var path60 = __toESM(require("path"));
35344
+ var crypto19 = __toESM(require("crypto"));
35076
35345
  function storePath15(dataDir) {
35077
- return path57.join(dataDir, "share-access.json");
35346
+ return path60.join(dataDir, "share-access.json");
35078
35347
  }
35079
35348
  var ShareAccessStore = class {
35080
35349
  constructor(dataDir) {
@@ -35084,7 +35353,7 @@ var ShareAccessStore = class {
35084
35353
  const p = storePath15(this.dataDir);
35085
35354
  let raw;
35086
35355
  try {
35087
- raw = fs48.readFileSync(p, "utf8");
35356
+ raw = fs49.readFileSync(p, "utf8");
35088
35357
  } catch (err) {
35089
35358
  if (err.code === "ENOENT") return [];
35090
35359
  throw new Error(`Cannot read share-access store at ${p}: ${err.message}`);
@@ -35102,11 +35371,11 @@ var ShareAccessStore = class {
35102
35371
  }
35103
35372
  append(entry) {
35104
35373
  const p = storePath15(this.dataDir);
35105
- fs48.mkdirSync(path57.dirname(p), { recursive: true });
35374
+ fs49.mkdirSync(path60.dirname(p), { recursive: true });
35106
35375
  const next = [...this.load(), entry];
35107
35376
  const tmp = `${p}.tmp`;
35108
- fs48.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
35109
- fs48.renameSync(tmp, p);
35377
+ fs49.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
35378
+ fs49.renameSync(tmp, p);
35110
35379
  }
35111
35380
  };
35112
35381
  var ShareAccessRegistry = class {
@@ -35116,7 +35385,7 @@ var ShareAccessRegistry = class {
35116
35385
  append(entry) {
35117
35386
  const stored = {
35118
35387
  ...entry,
35119
- id: entry.id || crypto18.randomUUID(),
35388
+ id: entry.id || crypto19.randomUUID(),
35120
35389
  at: entry.at || (/* @__PURE__ */ new Date()).toISOString()
35121
35390
  };
35122
35391
  this.store.append(stored);
@@ -35180,7 +35449,7 @@ function createShareLink(cfg, sessionId, input) {
35180
35449
  const wantsOpenapi = !!input.allowDownloadOpenapi || (input.artifacts ?? []).includes("openapi");
35181
35450
  const artifacts = artifactsFor({ allowDownloadOpenapi: !!input.allowDownloadOpenapi }, wantsOpenapi);
35182
35451
  const snapshot = putSnapshot(cfg.dataDir, captureSnapshot(cfg.dataDir, principal, input.projectId, input.view, artifacts));
35183
- const token = crypto19.randomBytes(32).toString("base64url");
35452
+ const token = crypto20.randomBytes(32).toString("base64url");
35184
35453
  const link = createLink(cfg.dataDir, {
35185
35454
  id: "",
35186
35455
  tokenHash: hashToken(token),
@@ -35257,16 +35526,16 @@ function getShareAccessLog(cfg, sessionId, linkId, limit) {
35257
35526
  init_fs();
35258
35527
 
35259
35528
  // src/server/swagger.ts
35260
- var fs49 = __toESM(require("fs"));
35261
- var path58 = __toESM(require("path"));
35529
+ var fs50 = __toESM(require("fs"));
35530
+ var path61 = __toESM(require("path"));
35262
35531
  var cache;
35263
35532
  function loadAssets() {
35264
35533
  if (cache !== void 0) return cache;
35265
35534
  try {
35266
- const dir = path58.dirname(require.resolve("swagger-ui-dist/package.json"));
35535
+ const dir = path61.dirname(require.resolve("swagger-ui-dist/package.json"));
35267
35536
  cache = {
35268
- css: fs49.readFileSync(path58.join(dir, "swagger-ui.css"), "utf8"),
35269
- js: fs49.readFileSync(path58.join(dir, "swagger-ui-bundle.js"), "utf8")
35537
+ css: fs50.readFileSync(path61.join(dir, "swagger-ui.css"), "utf8"),
35538
+ js: fs50.readFileSync(path61.join(dir, "swagger-ui-bundle.js"), "utf8")
35270
35539
  };
35271
35540
  } catch {
35272
35541
  cache = null;
@@ -35306,7 +35575,7 @@ function getLoginOptions(cfg) {
35306
35575
  async function startSignIn(cfg, providerId, redirectUri) {
35307
35576
  const provider = resolveEnabledProvider(cfg, providerId);
35308
35577
  assertAllowedRedirectUri(provider, redirectUri);
35309
- const nonce = crypto20.randomBytes(16).toString("hex");
35578
+ const nonce = crypto21.randomBytes(16).toString("hex");
35310
35579
  const payload = { providerId, nonce, redirectUri };
35311
35580
  const state = signSsoState(JSON.stringify(payload));
35312
35581
  const endpoints = await resolveEndpoints(provider);
@@ -35658,20 +35927,20 @@ function clearNonceCookie(secure) {
35658
35927
  }
35659
35928
  function nonceMatches(a, b) {
35660
35929
  if (!a || !b || a.length !== b.length) return false;
35661
- return crypto20.timingSafeEqual(Buffer.from(a), Buffer.from(b));
35930
+ return crypto21.timingSafeEqual(Buffer.from(a), Buffer.from(b));
35662
35931
  }
35663
35932
  function loadReactBundle() {
35664
35933
  const candidates = [
35665
- path59.resolve(__dirname, "webapp.html"),
35934
+ path62.resolve(__dirname, "webapp.html"),
35666
35935
  // dist/index.js -> dist/webapp.html
35667
- path59.resolve(__dirname, "..", "webapp.html"),
35936
+ path62.resolve(__dirname, "..", "webapp.html"),
35668
35937
  // dist/cli/index.js -> dist/webapp.html
35669
- path59.resolve(__dirname, "..", "..", "web", "dist", "index.html")
35938
+ path62.resolve(__dirname, "..", "..", "web", "dist", "index.html")
35670
35939
  // tsx dev: src/server -> web/dist
35671
35940
  ];
35672
35941
  for (const candidate of candidates) {
35673
35942
  try {
35674
- if (fs50.existsSync(candidate)) return fs50.readFileSync(candidate, "utf8");
35943
+ if (fs51.existsSync(candidate)) return fs51.readFileSync(candidate, "utf8");
35675
35944
  } catch {
35676
35945
  }
35677
35946
  }
@@ -37358,9 +37627,6 @@ details.adv summary { cursor:pointer; color:var(--dim); font-size:12px; margin-b
37358
37627
  Array.prototype.forEach.call(host.querySelectorAll('[data-lock]'), function (b) {
37359
37628
  b.addEventListener('click', function () { projectAction('/web/projects/lock', { projectId: b.getAttribute('data-lock') }, b, 'Locking\u2026', 'Lock'); });
37360
37629
  });
37361
- Array.prototype.forEach.call(host.querySelectorAll('[data-promote]'), function (b) {
37362
- b.addEventListener('click', function () { projectAction('/web/projects/promote', { projectId: b.getAttribute('data-promote') }, b, 'Promoting\u2026', 'Promote'); });
37363
- });
37364
37630
  Array.prototype.forEach.call(host.querySelectorAll('[data-destroy]'), function (b) {
37365
37631
  b.addEventListener('click', function () {
37366
37632
  if (!confirm('Destroy project "' + b.getAttribute('data-destroy') + '"? This removes its entire spec tree.')) return;
@@ -37664,9 +37930,6 @@ function projectCreate(cfg, sessionId, body, res) {
37664
37930
  function projectLock(cfg, sessionId, body, res) {
37665
37931
  sendJson(res, 200, lockProject3(cfg, sessionId, String(body?.projectId ?? "")));
37666
37932
  }
37667
- function projectPromote(cfg, sessionId, body, res) {
37668
- sendJson(res, 200, promoteProject3(cfg, sessionId, String(body?.projectId ?? "")));
37669
- }
37670
37933
  function projectDestroy(cfg, sessionId, body, res) {
37671
37934
  destroyProject2(cfg, sessionId, String(body?.id ?? ""));
37672
37935
  sendJson(res, 200, { ok: true });
@@ -37946,9 +38209,6 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
37946
38209
  if (req.method === "POST" && parts.length === 3 && parts[2] === "lock") {
37947
38210
  return projectLock(cfg, sessionId, body, res);
37948
38211
  }
37949
- if (req.method === "POST" && parts.length === 3 && parts[2] === "promote") {
37950
- return projectPromote(cfg, sessionId, body, res);
37951
- }
37952
38212
  if (req.method === "POST" && parts.length === 3 && parts[2] === "destroy") {
37953
38213
  return projectDestroy(cfg, sessionId, body, res);
37954
38214
  }
@@ -38203,8 +38463,8 @@ var RealtimeHub = class {
38203
38463
  * complete the handshake, and register the connection. A bad path or session
38204
38464
  * destroys the socket. */
38205
38465
  handleUpgrade(cfg, req, socket) {
38206
- const path64 = (req.url ?? "/").split("?")[0];
38207
- if (path64 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
38466
+ const path67 = (req.url ?? "/").split("?")[0];
38467
+ if (path67 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
38208
38468
  socket.destroy();
38209
38469
  return;
38210
38470
  }
@@ -38381,7 +38641,6 @@ function auditToolCall(dataDir, principal, projectId, body, outcome, subproject)
38381
38641
  var PROJECT_LIFECYCLE_TOOLS = /* @__PURE__ */ new Set([
38382
38642
  "sdd_host_initialize_project",
38383
38643
  "sdd_host_lock_project",
38384
- "sdd_host_promote_project",
38385
38644
  "sdd_host_get_approval_status",
38386
38645
  "sdd_host_await_approval"
38387
38646
  ]);
@@ -38439,7 +38698,6 @@ function requiredDataPlaneCapability(toolName) {
38439
38698
  var MUTATING_HOST_TOOLS = /* @__PURE__ */ new Set([
38440
38699
  "sdd_host_initialize_project",
38441
38700
  "sdd_host_lock_project",
38442
- "sdd_host_promote_project",
38443
38701
  "sdd_host_await_approval",
38444
38702
  // a decided approval may have executed the action
38445
38703
  "sdd_host_policy_reconcile"
@@ -38509,9 +38767,6 @@ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body, su
38509
38767
  case "sdd_host_lock_project":
38510
38768
  value = lockProject2(cfg, credential, projectId, subproject);
38511
38769
  break;
38512
- case "sdd_host_promote_project":
38513
- value = promoteProject2(cfg, credential, projectId, subproject);
38514
- break;
38515
38770
  case "sdd_host_await_approval":
38516
38771
  value = await awaitApproval(
38517
38772
  cfg,
@@ -38977,7 +39232,6 @@ var WEB_MUTATION_PATHS = /* @__PURE__ */ new Set([
38977
39232
  "/web/tokens/revoke",
38978
39233
  "/web/projects",
38979
39234
  "/web/projects/lock",
38980
- "/web/projects/promote",
38981
39235
  "/web/projects/destroy"
38982
39236
  ]);
38983
39237
  function routeData(cfg, req, res) {
@@ -38989,7 +39243,7 @@ function routeData(cfg, req, res) {
38989
39243
  if (req.method === "GET" && url.pathname === "/readyz") {
38990
39244
  let ready = false;
38991
39245
  try {
38992
- fs51.accessSync(cfg.dataDir, fs51.constants.W_OK);
39246
+ fs52.accessSync(cfg.dataDir, fs52.constants.W_OK);
38993
39247
  ready = true;
38994
39248
  } catch {
38995
39249
  }
@@ -39089,7 +39343,7 @@ function routeData(cfg, req, res) {
39089
39343
  }
39090
39344
  function readExposurePolicyFile(dataDir) {
39091
39345
  try {
39092
- const raw = fs51.readFileSync(path60.join(dataDir, "exposure-policy.json"), "utf8");
39346
+ const raw = fs52.readFileSync(path63.join(dataDir, "exposure-policy.json"), "utf8");
39093
39347
  const parsed = JSON.parse(raw);
39094
39348
  return parsed && typeof parsed === "object" ? parsed : void 0;
39095
39349
  } catch {
@@ -39138,7 +39392,6 @@ async function routeAdmin(cfg, req, res) {
39138
39392
  return sendJson(res, 200, { ok: true });
39139
39393
  }
39140
39394
  if (req.method === "POST" && parts.length === 4 && parts[3] === "lock") return sendJson(res, 200, lockProject(cfg, cred, parts[2]));
39141
- if (req.method === "POST" && parts.length === 4 && parts[3] === "promote") return sendJson(res, 200, promoteProject(cfg, cred, parts[2]));
39142
39395
  if (req.method === "POST" && parts.length === 4 && parts[3] === "git") return sendJson(res, 201, enableGit(cfg, cred, parts[2], body.remote, body.branch ?? "main", body.pat));
39143
39396
  if (req.method === "DELETE" && parts.length === 4 && parts[3] === "git") {
39144
39397
  disableGit(cfg, cred, parts[2]);
@@ -39793,9 +40046,9 @@ function seedDemoTree() {
39793
40046
 
39794
40047
  // src/commands/host.ts
39795
40048
  function resolveHostConfig(options) {
39796
- const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path61.join(os11.homedir(), ".wairon", "data");
40049
+ const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path64.join(os12.homedir(), ".wairon", "data");
39797
40050
  if (!process.env["WAIRON_PACKS_DIR"]) {
39798
- process.env["WAIRON_PACKS_DIR"] = path61.join(dataDir, "packs");
40051
+ process.env["WAIRON_PACKS_DIR"] = path64.join(dataDir, "packs");
39799
40052
  }
39800
40053
  const cfg = {
39801
40054
  host: options.host || "0.0.0.0",
@@ -39896,11 +40149,11 @@ async function runServe(options = {}) {
39896
40149
  logger.info(` data dir: ${import_chalk17.default.gray(cfg.dataDir)}`);
39897
40150
  logger.blank();
39898
40151
  logger.info("Press Ctrl+C to stop.");
39899
- await new Promise((resolve26) => {
40152
+ await new Promise((resolve27) => {
39900
40153
  const shutdown = () => {
39901
40154
  logger.info("Shutting down\u2026");
39902
40155
  handle.close();
39903
- resolve26();
40156
+ resolve27();
39904
40157
  };
39905
40158
  process.on("SIGINT", shutdown);
39906
40159
  process.on("SIGTERM", shutdown);
@@ -39920,14 +40173,14 @@ function openBrowser(url) {
39920
40173
  }
39921
40174
  async function runDev(options = {}) {
39922
40175
  const cwd = process.cwd();
39923
- if (!fs52.existsSync(path61.join(cwd, ".wai"))) {
40176
+ if (!fs53.existsSync(path64.join(cwd, ".wai"))) {
39924
40177
  throw new WaironError(
39925
40178
  "No .wai/ found in the current directory. Run `wairon dev` from a wairon project root (or run `wairon init` first)."
39926
40179
  );
39927
40180
  }
39928
- const hash = crypto21.createHash("sha256").update(cwd).digest("hex").slice(0, 16);
39929
- const dataDir = path61.join(os11.tmpdir(), "wairon-dev", hash);
39930
- fs52.mkdirSync(dataDir, { recursive: true });
40181
+ const hash = crypto22.createHash("sha256").update(cwd).digest("hex").slice(0, 16);
40182
+ const dataDir = path64.join(os12.tmpdir(), "wairon-dev", hash);
40183
+ fs53.mkdirSync(dataDir, { recursive: true });
39931
40184
  registerLocalDevProject(dataDir, "local", cwd);
39932
40185
  const port = options.port ? Number(options.port) : 8080;
39933
40186
  const exposurePolicy = {
@@ -39965,11 +40218,11 @@ async function runDev(options = {}) {
39965
40218
  logger.blank();
39966
40219
  logger.info("An agent edits specs; refresh the page to see the live graph. Press Ctrl+C to stop.");
39967
40220
  if (options.open) openBrowser(url);
39968
- await new Promise((resolve26) => {
40221
+ await new Promise((resolve27) => {
39969
40222
  const shutdown = () => {
39970
40223
  logger.info("Shutting down\u2026");
39971
40224
  handle.close();
39972
- resolve26();
40225
+ resolve27();
39973
40226
  };
39974
40227
  process.on("SIGINT", shutdown);
39975
40228
  process.on("SIGTERM", shutdown);
@@ -40239,18 +40492,6 @@ async function runHostLock(options = {}) {
40239
40492
  throw mapAdminError(e);
40240
40493
  }
40241
40494
  }
40242
- async function runHostPromote(options = {}) {
40243
- const cfg = resolveHostConfig(options);
40244
- if (!options.project) throw new WaironError("`--project <id>` is required for `host promote`.");
40245
- try {
40246
- const result = promoteProject(cfg, masterCredential(), options.project);
40247
- const mark = result.status === "ready" ? import_chalk17.default.green("\u2713") : import_chalk17.default.yellow("\u2717");
40248
- logger.info(`${mark} ${result.message}`);
40249
- if (result.status !== "ready") process.exitCode = 1;
40250
- } catch (e) {
40251
- throw mapAdminError(e);
40252
- }
40253
- }
40254
40495
  async function runHostProducer(action, options = {}) {
40255
40496
  const cfg = resolveHostConfig(options);
40256
40497
  const cred = masterCredential();
@@ -40335,8 +40576,8 @@ async function runHostPacks(action, options = {}) {
40335
40576
  }
40336
40577
  case "install": {
40337
40578
  if (!options.file) throw new WaironError("`--file <path>` (a declarative pack YAML) is required for install.");
40338
- const name = options.name ?? path61.basename(options.file).replace(/\.(ya?ml)$/i, "");
40339
- const content = fs52.readFileSync(path61.resolve(options.file), "utf8");
40579
+ const name = options.name ?? path64.basename(options.file).replace(/\.(ya?ml)$/i, "");
40580
+ const content = fs53.readFileSync(path64.resolve(options.file), "utf8");
40340
40581
  const desc = project2 ? installProjectPack(cfg, cred, project2, name, content) : installGlobalPack(cfg, cred, name, content);
40341
40582
  logger.success(`Installed ${scope} pack "${desc.name}" (${desc.profiles} profile(s), ${desc.languages} language(s)).`);
40342
40583
  if (project2) logger.info("Committed with the project \u2014 every clone and CI will enforce it.");
@@ -40562,7 +40803,7 @@ async function runSurface(action, options = {}) {
40562
40803
  }
40563
40804
 
40564
40805
  // src/commands/subsystem.ts
40565
- var path62 = __toESM(require("path"));
40806
+ var path65 = __toESM(require("path"));
40566
40807
  init_logger();
40567
40808
  init_errors();
40568
40809
  init_fs();
@@ -40602,9 +40843,9 @@ async function runSubsystemAdd(id, options = {}) {
40602
40843
  updatedAt: now
40603
40844
  };
40604
40845
  createChainedSubsystem(subsystem, displayName);
40605
- const childDir = path62.resolve(getProjectRoot(), options.projectPath);
40846
+ const childDir = path65.resolve(getProjectRoot(), options.projectPath);
40606
40847
  logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
40607
- logger.info(`Scaffolded child project at ${path62.relative(process.cwd(), childDir) || "."}`);
40848
+ logger.info(`Scaffolded child project at ${path65.relative(process.cwd(), childDir) || "."}`);
40608
40849
  logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
40609
40850
  }
40610
40851
  async function runSubsystemMove(id, options = {}) {
@@ -40627,9 +40868,9 @@ async function runSubsystemExternalize(id, options = {}) {
40627
40868
  throw new WaironError("--project-path (the subproject destination) is required.");
40628
40869
  }
40629
40870
  externalizeSubsystem(id, options.projectPath);
40630
- const childDir = path62.resolve(getProjectRoot(), options.projectPath);
40871
+ const childDir = path65.resolve(getProjectRoot(), options.projectPath);
40631
40872
  logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
40632
- logger.info(`Moved its specs into ${path62.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
40873
+ logger.info(`Moved its specs into ${path65.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
40633
40874
  logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
40634
40875
  }
40635
40876
  async function runSubsystemInternalize(id) {
@@ -40873,7 +41114,7 @@ async function runAgent(action, id) {
40873
41114
  }
40874
41115
  case "customize": {
40875
41116
  const brief = composeAgentBrief3(id);
40876
- const guidancePath = path63.join(AI_PATHS.root(), "agents", `${id}.md`);
41117
+ const guidancePath = path66.join(AI_PATHS.root(), "agents", `${id}.md`);
40877
41118
  const relPath = `.wai/agents/${id}.md`;
40878
41119
  if (pathExists(guidancePath)) {
40879
41120
  throw new GuidanceFileExistsError(relPath);
@@ -40976,9 +41217,6 @@ hostCmd.command("key <action>").description("mint | list | revoke an API key").o
40976
41217
  hostCmd.command("lock").description("validate-as-complete and write the state-scoped lock record for a hosted project").requiredOption("--project <id>", "project id").option("--data-dir <path>", "data root").action(async (opts) => {
40977
41218
  await runHostLock({ project: opts.project, dataDir: opts.dataDir });
40978
41219
  });
40979
- hostCmd.command("promote").description("promote a locked project after re-checking its StateId (never merges to production)").requiredOption("--project <id>", "project id").option("--data-dir <path>", "data root").action(async (opts) => {
40980
- await runHostPromote({ project: opts.project, dataDir: opts.dataDir });
40981
- });
40982
41220
  hostCmd.command("git <action>").description("enable | disable | sync | commit | status | sync-config \u2014 bind a project to its REAL repo (wairon commits ONLY .wai/)").option("--project <id>", "project id").option("--remote <url>", "git remote URL (for enable)").option("--branch <name>", "default branch PRs target (for enable)", "main").option("--subsystem <id>", "narrow a commit to .wai/specs/<subsystem>/ (staging convenience \u2014 history stays per-repo)").option("-m, --message <message>", "commit message (for commit)").option("--interval <minutes>", "periodic-sync interval in minutes (for sync-config; omit to disable)").option("--no-skip-if-clean", "commit on every periodic tick even when the scoped path is clean (for sync-config)").option("--data-dir <path>", "data root").action(async (action, opts) => {
40983
41221
  await runHostGit(action, {
40984
41222
  project: opts.project,