@wairon/cli 5.1.1-dev.61 → 5.1.1-dev.62

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.61";
68
+ WAIRON_VERSION = "5.1.1-dev.62";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -1464,10 +1464,6 @@ function normalizeApprover(value) {
1464
1464
  }
1465
1465
  return { id: typeof value === "string" && value ? value : "unknown", source: "legacy" };
1466
1466
  }
1467
- function describeApprover(who) {
1468
- const label = who.name ? `${who.name} (${who.id})` : who.id;
1469
- return who.source === "hosted" ? `${label} [authenticated]` : label;
1470
- }
1471
1467
  function readLockRecord() {
1472
1468
  try {
1473
1469
  return normalizeRecord(JSON.parse(fs6.readFileSync(lockPath(), "utf8")));
@@ -8642,6 +8638,7 @@ var init_extensions = __esm({
8642
8638
  init_canonical_json();
8643
8639
  init_project();
8644
8640
  init_packstore();
8641
+ init_packstore();
8645
8642
  ProfileDefSchema = import_zod9.z.object({
8646
8643
  family: import_zod9.z.enum(["backend-like", "frontend-like", "neutral"]).default("neutral"),
8647
8644
  forbiddenStereotypes: import_zod9.z.array(import_zod9.z.object({ types: import_zod9.z.array(import_zod9.z.string()).min(1), reason: import_zod9.z.string().min(1) })).default([]),
@@ -22143,223 +22140,6 @@ var init_domain_curator = __esm({
22143
22140
  }
22144
22141
  });
22145
22142
 
22146
- // src/core/detection.ts
22147
- function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
22148
- const candidates = /* @__PURE__ */ new Map();
22149
- for (const c of detectGitSubmodules(projectRoot2)) {
22150
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22151
- }
22152
- for (const c of detectNestedGitRepos(projectRoot2)) {
22153
- if (!candidates.has(c.path)) {
22154
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22155
- }
22156
- }
22157
- const gitPaths = new Set(
22158
- Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
22159
- );
22160
- for (const c of detectPackageRoots(projectRoot2)) {
22161
- if (candidates.has(c.path)) continue;
22162
- const insideGit = Array.from(gitPaths).some(
22163
- (gp) => c.path === gp || c.path.startsWith(gp + "/")
22164
- );
22165
- if (insideGit) continue;
22166
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22167
- }
22168
- const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
22169
- return deduplicateIds(sorted, alreadyTrackedIds);
22170
- }
22171
- function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
22172
- const idCount = /* @__PURE__ */ new Map();
22173
- for (const id of existingIds) {
22174
- idCount.set(id, (idCount.get(id) ?? 0) + 1);
22175
- }
22176
- for (const c of candidates) {
22177
- idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
22178
- }
22179
- return candidates.map((c) => {
22180
- if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
22181
- const parts = c.path.split("/");
22182
- const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
22183
- return { ...c, suggestedId: qualifiedId2 };
22184
- });
22185
- }
22186
- function parseGitmodules(filePath) {
22187
- const content = fs15.readFileSync(filePath, "utf-8");
22188
- const entries = [];
22189
- let current2 = {};
22190
- for (const line2 of content.split("\n")) {
22191
- const trimmed = line2.trim();
22192
- const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
22193
- if (headerMatch) {
22194
- if (current2.path) entries.push(current2);
22195
- current2 = { name: headerMatch[1] };
22196
- continue;
22197
- }
22198
- const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
22199
- if (keyVal) {
22200
- const [, key, value] = keyVal;
22201
- if (key === "path") current2.path = value.trim();
22202
- if (key === "url") current2.url = value.trim();
22203
- }
22204
- }
22205
- if (current2.path) entries.push(current2);
22206
- return entries;
22207
- }
22208
- function detectGitSubmodules(projectRoot2) {
22209
- const gitmodulesPath = path19.join(projectRoot2, ".gitmodules");
22210
- if (!fs15.existsSync(gitmodulesPath)) return [];
22211
- return parseGitmodules(gitmodulesPath).map((entry) => ({
22212
- suggestedId: pathToId(entry.path),
22213
- suggestedName: pathToName(entry.path),
22214
- path: normalizePath(entry.path),
22215
- type: "git-submodule",
22216
- alreadyTracked: false
22217
- }));
22218
- }
22219
- function detectNestedGitRepos(projectRoot2) {
22220
- const results = [];
22221
- walkForGit(projectRoot2, projectRoot2, 0, results);
22222
- return results;
22223
- }
22224
- function walkForGit(projectRoot2, currentDir, depth, results) {
22225
- if (depth > MAX_SCAN_DEPTH) return;
22226
- let entries;
22227
- try {
22228
- entries = fs15.readdirSync(currentDir, { withFileTypes: true });
22229
- } catch {
22230
- return;
22231
- }
22232
- for (const entry of entries) {
22233
- if (!entry.isDirectory()) continue;
22234
- if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22235
- const fullPath = path19.join(currentDir, entry.name);
22236
- const relPath = normalizePath(path19.relative(projectRoot2, fullPath));
22237
- if (relPath === "" || relPath === ".") continue;
22238
- const gitPath = path19.join(fullPath, ".git");
22239
- if (fs15.existsSync(gitPath)) {
22240
- results.push({
22241
- suggestedId: pathToId(relPath),
22242
- suggestedName: pathToName(relPath),
22243
- path: relPath,
22244
- type: "git-repo",
22245
- alreadyTracked: false
22246
- });
22247
- continue;
22248
- }
22249
- walkForGit(projectRoot2, fullPath, depth + 1, results);
22250
- }
22251
- }
22252
- function detectPackageRoots(projectRoot2) {
22253
- const results = [];
22254
- walkForPackages(projectRoot2, projectRoot2, 0, results);
22255
- return results;
22256
- }
22257
- function walkForPackages(projectRoot2, currentDir, depth, results) {
22258
- if (depth > MAX_SCAN_DEPTH) return;
22259
- let entries;
22260
- try {
22261
- entries = fs15.readdirSync(currentDir, { withFileTypes: true });
22262
- } catch {
22263
- return;
22264
- }
22265
- for (const entry of entries) {
22266
- if (!entry.isDirectory()) continue;
22267
- if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22268
- const fullPath = path19.join(currentDir, entry.name);
22269
- const relPath = normalizePath(path19.relative(projectRoot2, fullPath));
22270
- if (relPath === "" || relPath === ".") continue;
22271
- const hasMarker = PACKAGE_MARKERS.some((m) => fs15.existsSync(path19.join(fullPath, m)));
22272
- if (hasMarker) {
22273
- results.push({
22274
- suggestedId: pathToId(relPath),
22275
- suggestedName: pathToName(relPath),
22276
- path: relPath,
22277
- type: "package-root",
22278
- alreadyTracked: false
22279
- });
22280
- }
22281
- walkForPackages(projectRoot2, fullPath, depth + 1, results);
22282
- }
22283
- }
22284
- function pathToId(relPath) {
22285
- const basename15 = path19.basename(relPath);
22286
- return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
22287
- }
22288
- function pathToName(relPath) {
22289
- const id = pathToId(relPath);
22290
- return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
22291
- }
22292
- function normalizePath(p) {
22293
- return p.replace(/\\/g, "/");
22294
- }
22295
- var fs15, path19, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
22296
- var init_detection = __esm({
22297
- "src/core/detection.ts"() {
22298
- "use strict";
22299
- fs15 = __toESM(require("fs"));
22300
- path19 = __toESM(require("path"));
22301
- init_defaults();
22302
- PACKAGE_MARKERS = [
22303
- "package.json",
22304
- "pyproject.toml",
22305
- "Cargo.toml",
22306
- "go.mod",
22307
- "build.gradle",
22308
- "build.gradle.kts",
22309
- "pom.xml"
22310
- ];
22311
- MAX_SCAN_DEPTH = 5;
22312
- }
22313
- });
22314
-
22315
- // src/core/templates.ts
22316
- function builtinTemplatesDir() {
22317
- return path20.resolve(__dirname, "..", "templates");
22318
- }
22319
- function loadTemplate(id, globalOverride) {
22320
- const dirs = templateSearchDirs(globalOverride);
22321
- for (const dir of dirs) {
22322
- const filePath = path20.join(dir, `${id}.yaml`);
22323
- if (pathExists(filePath)) {
22324
- const raw = readYamlFile(filePath);
22325
- return { ...TemplateSchema.parse(raw), templateName: id };
22326
- }
22327
- }
22328
- throw new TemplateNotFoundError(id);
22329
- }
22330
- function loadAgentOverride(agentId) {
22331
- const filePath = path20.join(AI_PATHS.root(), "agents", `${agentId}.md`);
22332
- return readFileOrNull(filePath);
22333
- }
22334
- function templateSearchDirs(globalOverride) {
22335
- return [
22336
- AI_PATHS.templatesDir(),
22337
- // 1. project-local
22338
- globalTemplatesDir(globalOverride),
22339
- // 2. global user/org
22340
- builtinTemplatesDir()
22341
- // 3. built-in fallback
22342
- ];
22343
- }
22344
- function renderTemplateInstructions(template, vars) {
22345
- return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
22346
- return key in vars ? vars[key] : `{{${key}}}`;
22347
- });
22348
- }
22349
- var path20;
22350
- var init_templates = __esm({
22351
- "src/core/templates.ts"() {
22352
- "use strict";
22353
- path20 = __toESM(require("path"));
22354
- init_fs();
22355
- init_yaml();
22356
- init_errors();
22357
- init_template();
22358
- init_paths();
22359
- init_defaults();
22360
- }
22361
- });
22362
-
22363
22143
  // src/core/provision.ts
22364
22144
  var provision_exports = {};
22365
22145
  __export(provision_exports, {
@@ -22422,7 +22202,7 @@ function provisionProject(name) {
22422
22202
  function ensureProjectInitialized(fallbackName) {
22423
22203
  const now = (/* @__PURE__ */ new Date()).toISOString();
22424
22204
  const paths = aiPathsAt(getProjectRoot());
22425
- const hasSystem = fs16.existsSync(paths.specsSystem());
22205
+ const hasSystem = fs15.existsSync(paths.specsSystem());
22426
22206
  let name = fallbackName;
22427
22207
  if (hasSystem) {
22428
22208
  const existing = loadSystemSpec();
@@ -22444,11 +22224,11 @@ function ensureProjectInitialized(fallbackName) {
22444
22224
  function walkChainedSubprojects(projectRoot2, onChild) {
22445
22225
  const visited = /* @__PURE__ */ new Set();
22446
22226
  const walk2 = (dir) => {
22447
- const resolved = path21.resolve(dir);
22227
+ const resolved = path19.resolve(dir);
22448
22228
  if (visited.has(resolved)) return;
22449
22229
  visited.add(resolved);
22450
22230
  const specsDir = aiPathsAt(dir).specsDir();
22451
- if (!fs16.existsSync(specsDir)) return;
22231
+ if (!fs15.existsSync(specsDir)) return;
22452
22232
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
22453
22233
  let raw;
22454
22234
  try {
@@ -22466,7 +22246,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
22466
22246
  continue;
22467
22247
  }
22468
22248
  const id = raw.id;
22469
- onChild(childDir, typeof id === "string" ? id : path21.basename(childDir));
22249
+ onChild(childDir, typeof id === "string" ? id : path19.basename(childDir));
22470
22250
  walk2(childDir);
22471
22251
  }
22472
22252
  };
@@ -22475,7 +22255,7 @@ function walkChainedSubprojects(projectRoot2, onChild) {
22475
22255
  function listDirectChainedSubprojects(projectRoot2) {
22476
22256
  const out = [];
22477
22257
  const specsDir = aiPathsAt(projectRoot2).specsDir();
22478
- if (!fs16.existsSync(specsDir)) return out;
22258
+ if (!fs15.existsSync(specsDir)) return out;
22479
22259
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
22480
22260
  let raw;
22481
22261
  try {
@@ -22493,12 +22273,12 @@ function listDirectChainedSubprojects(projectRoot2) {
22493
22273
  continue;
22494
22274
  }
22495
22275
  const id = raw.id;
22496
- out.push({ dir, subsystemId: typeof id === "string" ? id : path21.basename(dir) });
22276
+ out.push({ dir, subsystemId: typeof id === "string" ? id : path19.basename(dir) });
22497
22277
  }
22498
22278
  return out;
22499
22279
  }
22500
22280
  function childHasSpecsButNoConfig(childDir) {
22501
- return fs16.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
22281
+ return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
22502
22282
  }
22503
22283
  function findChainingSubprojectsMissingConfig(projectRoot2) {
22504
22284
  const missing = [];
@@ -22547,14 +22327,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
22547
22327
  const oldDir = assertContainedProjectPath(root, sub.projectPath);
22548
22328
  const newDir = assertContainedProjectPath(root, nextPath);
22549
22329
  if (oldDir !== newDir) {
22550
- if (!fs16.existsSync(oldDir)) {
22330
+ if (!fs15.existsSync(oldDir)) {
22551
22331
  throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
22552
22332
  }
22553
- if (fs16.existsSync(newDir)) {
22333
+ if (fs15.existsSync(newDir)) {
22554
22334
  throw new WaironError(`Target directory already exists: ${newDir}`);
22555
22335
  }
22556
- ensureDir(path21.dirname(newDir));
22557
- fs16.renameSync(oldDir, newDir);
22336
+ ensureDir(path19.dirname(newDir));
22337
+ fs15.renameSync(oldDir, newDir);
22558
22338
  }
22559
22339
  saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
22560
22340
  invalidateSpecCache();
@@ -22563,9 +22343,9 @@ function toPosixPath(p) {
22563
22343
  return p.replace(/\\/g, "/");
22564
22344
  }
22565
22345
  function isWithinDir(dir, file) {
22566
- const d = path21.resolve(dir);
22567
- const f = path21.resolve(file);
22568
- return f === d || f.startsWith(d + path21.sep);
22346
+ const d = path19.resolve(dir);
22347
+ const f = path19.resolve(file);
22348
+ return f === d || f.startsWith(d + path19.sep);
22569
22349
  }
22570
22350
  function externalizeSubsystem(subsystemId, projectPath) {
22571
22351
  if (subsystemId.includes("::")) {
@@ -22577,14 +22357,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
22577
22357
  }
22578
22358
  const parentRoot = getProjectRoot();
22579
22359
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
22580
- const fooDir = path21.join(parentSpecsDir, subsystemId);
22581
- if (!fs16.existsSync(fooDir)) {
22360
+ const fooDir = path19.join(parentSpecsDir, subsystemId);
22361
+ if (!fs15.existsSync(fooDir)) {
22582
22362
  throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
22583
22363
  }
22584
22364
  const relPath = toPosixPath(projectPath);
22585
22365
  const childDir = assertContainedProjectPath(parentRoot, relPath);
22586
- const childFooDir = path21.join(childDir, ".wai", "specs", subsystemId);
22587
- if (fs16.existsSync(childFooDir)) {
22366
+ const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
22367
+ if (fs15.existsSync(childFooDir)) {
22588
22368
  throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
22589
22369
  }
22590
22370
  const renameMap = buildRenameMap(
@@ -22596,18 +22376,18 @@ function externalizeSubsystem(subsystemId, projectPath) {
22596
22376
  runWithProjectRoot(childDir, () => {
22597
22377
  const now = (/* @__PURE__ */ new Date()).toISOString();
22598
22378
  projectConfigRepository.create(defaultProjectConfig(childSystemName, now));
22599
- ensureDir(path21.join(childDir, ".wai", "specs"));
22379
+ ensureDir(path19.join(childDir, ".wai", "specs"));
22600
22380
  saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
22601
22381
  });
22602
- ensureDir(path21.dirname(childFooDir));
22603
- fs16.renameSync(fooDir, childFooDir);
22382
+ ensureDir(path19.dirname(childFooDir));
22383
+ fs15.renameSync(fooDir, childFooDir);
22604
22384
  rebaseImplementationPaths(childFooDir, parentRoot, childDir);
22605
- patchSubsystemIndex(path21.join(childFooDir, ".index.yaml"), (s) => {
22385
+ patchSubsystemIndex(path19.join(childFooDir, ".index.yaml"), (s) => {
22606
22386
  s.parentSystem = childSystemName;
22607
22387
  delete s.projectPath;
22608
22388
  });
22609
22389
  ensureDir(fooDir);
22610
- writeYamlFile(path21.join(fooDir, ".index.yaml"), {
22390
+ writeYamlFile(path19.join(fooDir, ".index.yaml"), {
22611
22391
  id: subsystemId,
22612
22392
  name: foo.name,
22613
22393
  description: foo.description,
@@ -22634,9 +22414,9 @@ function internalizeSubsystem(subsystemId) {
22634
22414
  const parentRoot = getProjectRoot();
22635
22415
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
22636
22416
  const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
22637
- const childWai = path21.join(childDir, ".wai");
22638
- const childFooDir = path21.join(childDir, ".wai", "specs", subsystemId);
22639
- if (!fs16.existsSync(childFooDir)) {
22417
+ const childWai = path19.join(childDir, ".wai");
22418
+ const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
22419
+ if (!fs15.existsSync(childFooDir)) {
22640
22420
  throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
22641
22421
  }
22642
22422
  const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
@@ -22649,16 +22429,16 @@ function internalizeSubsystem(subsystemId) {
22649
22429
  false
22650
22430
  );
22651
22431
  const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
22652
- const fooDir = path21.join(parentSpecsDir, subsystemId);
22653
- fs16.rmSync(fooDir, { recursive: true, force: true });
22654
- ensureDir(path21.dirname(fooDir));
22655
- fs16.renameSync(childFooDir, fooDir);
22432
+ const fooDir = path19.join(parentSpecsDir, subsystemId);
22433
+ fs15.rmSync(fooDir, { recursive: true, force: true });
22434
+ ensureDir(path19.dirname(fooDir));
22435
+ fs15.renameSync(childFooDir, fooDir);
22656
22436
  rebaseImplementationPaths(fooDir, childDir, parentRoot);
22657
- patchSubsystemIndex(path21.join(fooDir, ".index.yaml"), (s) => {
22437
+ patchSubsystemIndex(path19.join(fooDir, ".index.yaml"), (s) => {
22658
22438
  s.parentSystem = parentSystemName;
22659
22439
  delete s.projectPath;
22660
22440
  });
22661
- fs16.rmSync(childWai, { recursive: true, force: true });
22441
+ fs15.rmSync(childWai, { recursive: true, force: true });
22662
22442
  rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22663
22443
  rebaseMovedRefs(fooDir, subsystemId, "outOf");
22664
22444
  invalidateSpecCache();
@@ -22736,8 +22516,8 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
22736
22516
  let changed = false;
22737
22517
  const rebase = (holder, key) => {
22738
22518
  const p = holder[key];
22739
- if (typeof p !== "string" || p === "" || path21.isAbsolute(p)) return;
22740
- const next = toPosixPath(path21.relative(toRoot, path21.resolve(fromRoot, p)));
22519
+ if (typeof p !== "string" || p === "" || path19.isAbsolute(p)) return;
22520
+ const next = toPosixPath(path19.relative(toRoot, path19.resolve(fromRoot, p)));
22741
22521
  if (next !== p) {
22742
22522
  holder[key] = next;
22743
22523
  changed = true;
@@ -22753,7 +22533,7 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
22753
22533
  }
22754
22534
  }
22755
22535
  function patchSubsystemIndex(indexPath, mutate) {
22756
- if (!fs16.existsSync(indexPath)) return;
22536
+ if (!fs15.existsSync(indexPath)) return;
22757
22537
  const raw = readYamlFile(indexPath);
22758
22538
  mutate(raw);
22759
22539
  raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -22814,8 +22594,8 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
22814
22594
  if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
22815
22595
  if (movedImplementation) {
22816
22596
  const oldFile = getImplementationPath(movedImplementation.id);
22817
- if (path21.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path21.resolve(oldFile)) {
22818
- fs16.unlinkSync(oldFile);
22597
+ if (path19.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path19.resolve(oldFile)) {
22598
+ fs15.unlinkSync(oldFile);
22819
22599
  }
22820
22600
  saveImplementationSpec({ ...movedImplementation, id: implementationId });
22821
22601
  }
@@ -22825,17 +22605,17 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
22825
22605
  invalidateSpecCache();
22826
22606
  }
22827
22607
  function removeSpecFile2(file, id) {
22828
- if (!fs16.existsSync(file)) return;
22608
+ if (!fs15.existsSync(file)) return;
22829
22609
  try {
22830
22610
  if (readYamlFile(file)?.id !== id) return;
22831
22611
  } catch {
22832
22612
  return;
22833
22613
  }
22834
- fs16.unlinkSync(file);
22835
- const specsRoot = path21.resolve(aiPathsAt(getProjectRoot()).specsDir());
22836
- for (let dir = path21.dirname(path21.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path21.dirname(dir)) {
22837
- if (fs16.readdirSync(dir).length > 0) break;
22838
- fs16.rmdirSync(dir);
22614
+ fs15.unlinkSync(file);
22615
+ const specsRoot = path19.resolve(aiPathsAt(getProjectRoot()).specsDir());
22616
+ for (let dir = path19.dirname(path19.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path19.dirname(dir)) {
22617
+ if (fs15.readdirSync(dir).length > 0) break;
22618
+ fs15.rmdirSync(dir);
22839
22619
  }
22840
22620
  }
22841
22621
  function renameMethod(componentId, methodName, newName, pinSymbol) {
@@ -22932,12 +22712,12 @@ function collectMentions(specsDir, methodName, moved) {
22932
22712
  function identifierPattern(methodName) {
22933
22713
  return new RegExp(`(?<![A-Za-z0-9_])${methodName}(?![A-Za-z0-9_])`);
22934
22714
  }
22935
- var fs16, path21, METHOD_NAME, PROSE_FIELDS2;
22715
+ var fs15, path19, METHOD_NAME, PROSE_FIELDS2;
22936
22716
  var init_provision = __esm({
22937
22717
  "src/core/provision.ts"() {
22938
22718
  "use strict";
22939
- fs16 = __toESM(require("fs"));
22940
- path21 = __toESM(require("path"));
22719
+ fs15 = __toESM(require("fs"));
22720
+ path19 = __toESM(require("path"));
22941
22721
  init_specs2();
22942
22722
  init_paths();
22943
22723
  init_project_config();
@@ -22960,6 +22740,223 @@ var init_provision = __esm({
22960
22740
  }
22961
22741
  });
22962
22742
 
22743
+ // src/core/detection.ts
22744
+ function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
22745
+ const candidates = /* @__PURE__ */ new Map();
22746
+ for (const c of detectGitSubmodules(projectRoot2)) {
22747
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22748
+ }
22749
+ for (const c of detectNestedGitRepos(projectRoot2)) {
22750
+ if (!candidates.has(c.path)) {
22751
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22752
+ }
22753
+ }
22754
+ const gitPaths = new Set(
22755
+ Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
22756
+ );
22757
+ for (const c of detectPackageRoots(projectRoot2)) {
22758
+ if (candidates.has(c.path)) continue;
22759
+ const insideGit = Array.from(gitPaths).some(
22760
+ (gp) => c.path === gp || c.path.startsWith(gp + "/")
22761
+ );
22762
+ if (insideGit) continue;
22763
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22764
+ }
22765
+ const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
22766
+ return deduplicateIds(sorted, alreadyTrackedIds);
22767
+ }
22768
+ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
22769
+ const idCount = /* @__PURE__ */ new Map();
22770
+ for (const id of existingIds) {
22771
+ idCount.set(id, (idCount.get(id) ?? 0) + 1);
22772
+ }
22773
+ for (const c of candidates) {
22774
+ idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
22775
+ }
22776
+ return candidates.map((c) => {
22777
+ if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
22778
+ const parts = c.path.split("/");
22779
+ const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
22780
+ return { ...c, suggestedId: qualifiedId2 };
22781
+ });
22782
+ }
22783
+ function parseGitmodules(filePath) {
22784
+ const content = fs16.readFileSync(filePath, "utf-8");
22785
+ const entries = [];
22786
+ let current2 = {};
22787
+ for (const line2 of content.split("\n")) {
22788
+ const trimmed = line2.trim();
22789
+ const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
22790
+ if (headerMatch) {
22791
+ if (current2.path) entries.push(current2);
22792
+ current2 = { name: headerMatch[1] };
22793
+ continue;
22794
+ }
22795
+ const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
22796
+ if (keyVal) {
22797
+ const [, key, value] = keyVal;
22798
+ if (key === "path") current2.path = value.trim();
22799
+ if (key === "url") current2.url = value.trim();
22800
+ }
22801
+ }
22802
+ if (current2.path) entries.push(current2);
22803
+ return entries;
22804
+ }
22805
+ function detectGitSubmodules(projectRoot2) {
22806
+ const gitmodulesPath = path20.join(projectRoot2, ".gitmodules");
22807
+ if (!fs16.existsSync(gitmodulesPath)) return [];
22808
+ return parseGitmodules(gitmodulesPath).map((entry) => ({
22809
+ suggestedId: pathToId(entry.path),
22810
+ suggestedName: pathToName(entry.path),
22811
+ path: normalizePath(entry.path),
22812
+ type: "git-submodule",
22813
+ alreadyTracked: false
22814
+ }));
22815
+ }
22816
+ function detectNestedGitRepos(projectRoot2) {
22817
+ const results = [];
22818
+ walkForGit(projectRoot2, projectRoot2, 0, results);
22819
+ return results;
22820
+ }
22821
+ function walkForGit(projectRoot2, currentDir, depth, results) {
22822
+ if (depth > MAX_SCAN_DEPTH) return;
22823
+ let entries;
22824
+ try {
22825
+ entries = fs16.readdirSync(currentDir, { withFileTypes: true });
22826
+ } catch {
22827
+ return;
22828
+ }
22829
+ for (const entry of entries) {
22830
+ if (!entry.isDirectory()) continue;
22831
+ if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22832
+ const fullPath = path20.join(currentDir, entry.name);
22833
+ const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
22834
+ if (relPath === "" || relPath === ".") continue;
22835
+ const gitPath = path20.join(fullPath, ".git");
22836
+ if (fs16.existsSync(gitPath)) {
22837
+ results.push({
22838
+ suggestedId: pathToId(relPath),
22839
+ suggestedName: pathToName(relPath),
22840
+ path: relPath,
22841
+ type: "git-repo",
22842
+ alreadyTracked: false
22843
+ });
22844
+ continue;
22845
+ }
22846
+ walkForGit(projectRoot2, fullPath, depth + 1, results);
22847
+ }
22848
+ }
22849
+ function detectPackageRoots(projectRoot2) {
22850
+ const results = [];
22851
+ walkForPackages(projectRoot2, projectRoot2, 0, results);
22852
+ return results;
22853
+ }
22854
+ function walkForPackages(projectRoot2, currentDir, depth, results) {
22855
+ if (depth > MAX_SCAN_DEPTH) return;
22856
+ let entries;
22857
+ try {
22858
+ entries = fs16.readdirSync(currentDir, { withFileTypes: true });
22859
+ } catch {
22860
+ return;
22861
+ }
22862
+ for (const entry of entries) {
22863
+ if (!entry.isDirectory()) continue;
22864
+ if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22865
+ const fullPath = path20.join(currentDir, entry.name);
22866
+ const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
22867
+ if (relPath === "" || relPath === ".") continue;
22868
+ const hasMarker = PACKAGE_MARKERS.some((m) => fs16.existsSync(path20.join(fullPath, m)));
22869
+ if (hasMarker) {
22870
+ results.push({
22871
+ suggestedId: pathToId(relPath),
22872
+ suggestedName: pathToName(relPath),
22873
+ path: relPath,
22874
+ type: "package-root",
22875
+ alreadyTracked: false
22876
+ });
22877
+ }
22878
+ walkForPackages(projectRoot2, fullPath, depth + 1, results);
22879
+ }
22880
+ }
22881
+ function pathToId(relPath) {
22882
+ const basename15 = path20.basename(relPath);
22883
+ return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
22884
+ }
22885
+ function pathToName(relPath) {
22886
+ const id = pathToId(relPath);
22887
+ return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
22888
+ }
22889
+ function normalizePath(p) {
22890
+ return p.replace(/\\/g, "/");
22891
+ }
22892
+ var fs16, path20, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
22893
+ var init_detection = __esm({
22894
+ "src/core/detection.ts"() {
22895
+ "use strict";
22896
+ fs16 = __toESM(require("fs"));
22897
+ path20 = __toESM(require("path"));
22898
+ init_defaults();
22899
+ PACKAGE_MARKERS = [
22900
+ "package.json",
22901
+ "pyproject.toml",
22902
+ "Cargo.toml",
22903
+ "go.mod",
22904
+ "build.gradle",
22905
+ "build.gradle.kts",
22906
+ "pom.xml"
22907
+ ];
22908
+ MAX_SCAN_DEPTH = 5;
22909
+ }
22910
+ });
22911
+
22912
+ // src/core/templates.ts
22913
+ function builtinTemplatesDir() {
22914
+ return path21.resolve(__dirname, "..", "templates");
22915
+ }
22916
+ function loadTemplate(id, globalOverride) {
22917
+ const dirs = templateSearchDirs(globalOverride);
22918
+ for (const dir of dirs) {
22919
+ const filePath = path21.join(dir, `${id}.yaml`);
22920
+ if (pathExists(filePath)) {
22921
+ const raw = readYamlFile(filePath);
22922
+ return { ...TemplateSchema.parse(raw), templateName: id };
22923
+ }
22924
+ }
22925
+ throw new TemplateNotFoundError(id);
22926
+ }
22927
+ function loadAgentOverride(agentId) {
22928
+ const filePath = path21.join(AI_PATHS.root(), "agents", `${agentId}.md`);
22929
+ return readFileOrNull(filePath);
22930
+ }
22931
+ function templateSearchDirs(globalOverride) {
22932
+ return [
22933
+ AI_PATHS.templatesDir(),
22934
+ // 1. project-local
22935
+ globalTemplatesDir(globalOverride),
22936
+ // 2. global user/org
22937
+ builtinTemplatesDir()
22938
+ // 3. built-in fallback
22939
+ ];
22940
+ }
22941
+ function renderTemplateInstructions(template, vars) {
22942
+ return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
22943
+ return key in vars ? vars[key] : `{{${key}}}`;
22944
+ });
22945
+ }
22946
+ var path21;
22947
+ var init_templates = __esm({
22948
+ "src/core/templates.ts"() {
22949
+ "use strict";
22950
+ path21 = __toESM(require("path"));
22951
+ init_fs();
22952
+ init_yaml();
22953
+ init_errors();
22954
+ init_template();
22955
+ init_paths();
22956
+ init_defaults();
22957
+ }
22958
+ });
22959
+
22963
22960
  // src/core/execution_profile.ts
22964
22961
  function isSweeping(p) {
22965
22962
  return p === "**" || p === "**/*" || p.startsWith("**/");
@@ -23503,326 +23500,267 @@ var init_agent_resolver = __esm({
23503
23500
  }
23504
23501
  });
23505
23502
 
23506
- // src/core/instructions.ts
23507
- function governingProfile() {
23508
- try {
23509
- return loadProjectConfig3()?.projectType ?? null;
23510
- } catch {
23511
- return null;
23503
+ // src/exporters/claude.ts
23504
+ function toolsFor(budget) {
23505
+ const base = TOOLS_BY_CLASS[budget.toolClass];
23506
+ if (!base) return void 0;
23507
+ if (budget.allowNestedDelegation && !base.includes("Agent")) {
23508
+ return [...base, "Agent"];
23509
+ }
23510
+ if (!budget.allowNestedDelegation) {
23511
+ return base.filter((t) => t !== "Agent");
23512
23512
  }
23513
+ return base;
23513
23514
  }
23514
- function blockApplies(block, profile) {
23515
- if (!block.profile || block.profile.length === 0) return true;
23516
- return profile !== null && block.profile.includes(profile);
23515
+ function mcpServersFor(access) {
23516
+ return access === "none" ? "[]" : void 0;
23517
23517
  }
23518
- function skillPointers(skills) {
23519
- if (skills.length === 0) return "(none published by this server)";
23520
- return skills.map((s) => `- \`${s.resourceUri}\` \u2014 ${s.description || s.name}`).join("\n");
23518
+ function yamlScalar(value) {
23519
+ return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
23521
23520
  }
23522
- function waironInstructions(profile, packNames, skills, variants) {
23523
- return `# wairon \u2014 Spec-Driven Development (SDD)
23524
-
23525
- This server owns one system's **spec tree**: its architecture, specified top-down
23526
- and machine-checked. Author it ONLY through this server's \`sdd_*\` tools \u2014 never by
23527
- writing spec files directly, and never via the \`wairon\` CLI (that is the human
23528
- developer's tool).
23529
-
23530
- ## The tree: L0 \u2192 L5
23531
-
23532
- - **L0 System** \u2014 vision, boundaries, global requirements, target language.
23533
- - **L1 Subsystem** \u2014 an isolated service, its \`publicInterfaces\`, its trusted links.
23534
- - **L2 Component** \u2014 ONE building block (\`Portal\`, \`Orchestrator\`, \`Supervisor\`,
23535
- \`Actor\`, \`Store\`, \`Index\`, \`Query\`, \`Registry\`, \`Adapter\`, \`Observer\`, and
23536
- \`View\` for UIs) or pattern (\`Repository\`, and \`FeatureComponent\` or
23537
- \`RouterComponent\` for UIs), plus \`owns\` (a pattern's private members) and
23538
- \`dependsOn\` (collaborators). Logic is an \`Orchestrator\` whose \`dependencyClass\`
23539
- (\`pure\` or \`read\`; unset, a workflow) bounds what it may depend on; a gateway is
23540
- a \`Portal\` with the \`gateway\` variant. \`Specialist\` and \`Gateway\` are retired.
23541
- - **L3 Interface** \u2014 the contract: method signatures with structured \`params\`,
23542
- plus wire \`endpoint\` bindings for a Portal's methods.
23543
- - **L4 Implementation** \u2014 a concrete realization of one L3 contract: narrative
23544
- detail level, bound \`technologies\` (the vendor swap seam), optional \`sourcePath\`.
23545
- - **L5 Narrative** \u2014 each method's logic as a FLAT numbered step list; \`call\` steps
23546
- resolve to real dependency methods, and flow steps jump by step number.
23547
-
23548
- These stereotypes are not labels \u2014 they carry enforced dependency rules (e.g. a
23549
- Portal may not depend on a Store; state lives in a Store, never as fields inside
23550
- an Orchestrator). \`sdd_validate_tree\` is the gate, and it is the authority on
23551
- whether a design is legal.
23552
-
23553
- ## Authoring order
23554
-
23555
- 1. \`sdd_initialize_system\` (L0), then \`sdd_add_subsystem\` (L1).
23556
- 2. Then, **one subsystem at a time**: \`sdd_add_component\` (L2) \u2192 \`sdd_define_interface\`
23557
- (L3) \u2192 \`sdd_set_endpoints\` for Portals \u2192 \`sdd_write_narrative\` (L4+L5).
23558
- 3. Run \`sdd_validate_tree\` as you go \u2014 early and per subsystem, not once at the end.
23559
- \`sdd_get_status\` reports completeness; \`sdd_get_spec\` reads any spec back as JSON.
23560
- 4. Edit granularly with \`sdd_update_spec\` (it renumbers narrative steps and
23561
- relocates jump targets for you).
23562
-
23563
- ## The tool schemas are the field reference
23564
-
23565
- Every \`sdd_*\` tool's input schema is self-describing and authoritative \u2014 read the
23566
- schema rather than guessing field names, and prefer structured \`params\` over prose
23567
- signatures. Specs are ${profile === null ? "validated against the project's configured profile" : `governed by the **\`${profile}\`** profile`}, which decides which stereotypes are legal
23568
- here, which narrative flow constructs the target language supports, and which
23569
- extra rules apply. A subsystem may override the profile for itself.
23570
-
23571
- ## Loaded extension packs
23572
-
23573
- ${packNames.length === 0 ? "None \u2014 only wairon's built-in doctrine applies." : `${packNames.map((n) => `\`${n}\``).join(", ")} \u2014 these inject profiles, language tables, patterns, and
23574
- additional conformance rules. Their doctrine is enforced by the same gate.`}
23575
-
23576
- ${variants.length === 0 ? "" : `## Component variants in play
23577
-
23578
- This project defines variants \u2014 named, base-anchored kinds carrying implementation
23579
- guidance, so every component of the same kind is built alike: ${variants.map((v) => `\`${v.id}\` (a kind of ${v.base})`).join(", ")}.
23580
- Tag a component with \`variant\` where it fits, and read a component's resolved
23581
- guidance and its same-variant siblings from \`sdd_get_spec\` (kind: component).
23582
-
23583
- `}## Read the skills BEFORE authoring
23584
-
23585
- This server publishes its skills as MCP resources. **Read \`wairon-skill://sdd-architect\`
23586
- before you design anything**, and the others when their turn comes \u2014 they carry the
23587
- architecture standard, the narrative step reference, and the review checklist that
23588
- this briefing only points at:
23589
-
23590
- ${skillPointers(skills)}
23591
-
23592
- ## Delegating implementation
23521
+ var path23, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
23522
+ var init_claude = __esm({
23523
+ "src/exporters/claude.ts"() {
23524
+ "use strict";
23525
+ path23 = __toESM(require("path"));
23526
+ init_fs();
23527
+ MODEL_BY_TIER = {
23528
+ small: "haiku",
23529
+ standard: "sonnet",
23530
+ large: "opus",
23531
+ frontier: "fable"
23532
+ };
23533
+ TOOLS_BY_CLASS = {
23534
+ "read-only": ["Read", "Grep", "Glob"],
23535
+ implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
23536
+ orchestrate: ["Agent", "SendMessage", "TodoWrite"],
23537
+ // `full` means "inherit everything" — expressed by omitting the field.
23538
+ full: void 0
23539
+ };
23540
+ ClaudeExporter = class {
23541
+ constructor(options = {}) {
23542
+ this.options = options;
23543
+ this.targetType = "claude";
23544
+ }
23545
+ outputPath(ctx) {
23546
+ const { agent, target, projectRoot: projectRoot2 } = ctx;
23547
+ const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
23548
+ return path23.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23549
+ }
23550
+ export(ctx) {
23551
+ const { agent, renderedInstructions, budget } = ctx;
23552
+ const filePath = this.outputPath(ctx);
23553
+ const frontmatter = [
23554
+ `name: ${agent.name}`,
23555
+ `description: ${yamlScalar(agent.description)}`
23556
+ ];
23557
+ if (budget && this.options.emitBudget) {
23558
+ if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
23559
+ if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
23560
+ if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
23561
+ const tools = toolsFor(budget);
23562
+ if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
23563
+ const servers = mcpServersFor(budget.mcp);
23564
+ if (servers) frontmatter.push(`mcpServers: ${servers}`);
23565
+ }
23566
+ const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
23567
+ const changed = writeFileIfChanged(filePath, content);
23568
+ return { outputPath: filePath, content, unchanged: !changed };
23569
+ }
23570
+ };
23571
+ }
23572
+ });
23593
23573
 
23594
- Delegate component work from a LIVE agent brief, never a generated file: discover
23595
- agents via \`listAgents\` (or the \`wairon-agent://\` resources), fetch the target's
23596
- brief with \`sdd_get_agent_brief\`, and spawn a scoped subagent from it \u2014 its
23597
- \`instructions\` as prompt, its \`ownedPaths\` as write fence. Briefs are composed
23598
- from the current spec tree on every call, so a re-lock never requires a session
23599
- restart \u2014 fetch fresh per delegation. \`wairon-skill://sdd-delegate\` carries the
23600
- full flow.`;
23601
- }
23602
- function buildServerInstructions() {
23603
- const profile = governingProfile();
23604
- let skills = [];
23605
- let blocks = [];
23606
- let packNames = [];
23607
- const variants = [];
23608
- try {
23609
- skills = listSkillResources();
23610
- const extensions = loadProjectExtensions2();
23611
- blocks = extensions.instructions;
23612
- packNames = extensions.packNames;
23613
- } catch {
23574
+ // src/exporters/custom.ts
23575
+ var path24, CustomExporter;
23576
+ var init_custom = __esm({
23577
+ "src/exporters/custom.ts"() {
23578
+ "use strict";
23579
+ path24 = __toESM(require("path"));
23580
+ init_fs();
23581
+ CustomExporter = class {
23582
+ constructor() {
23583
+ this.targetType = "custom";
23584
+ }
23585
+ outputPath(ctx) {
23586
+ const { agent, target, projectRoot: projectRoot2 } = ctx;
23587
+ if (!("outputDir" in target)) {
23588
+ throw new Error("CustomExporter requires target.outputDir");
23589
+ }
23590
+ return path24.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
23591
+ }
23592
+ export(ctx) {
23593
+ const { agent, target, renderedInstructions } = ctx;
23594
+ const label = "label" in target ? target.label : "Custom";
23595
+ const filePath = this.outputPath(ctx);
23596
+ const content = [
23597
+ "---",
23598
+ `name: ${agent.name}`,
23599
+ `description: ${agent.description}`,
23600
+ `id: ${agent.id}`,
23601
+ `target: ${label}`,
23602
+ "---",
23603
+ "",
23604
+ renderedInstructions,
23605
+ ""
23606
+ ].join("\n");
23607
+ const changed = writeFileIfChanged(filePath, content);
23608
+ return { outputPath: filePath, content, unchanged: !changed };
23609
+ }
23610
+ };
23614
23611
  }
23615
- const sections = [waironInstructions(profile, packNames, skills, variants)];
23616
- for (const block of blocks) {
23617
- if (!blockApplies(block, profile)) continue;
23618
- sections.push(`## From pack "${block.pack}"
23612
+ });
23619
23613
 
23620
- ${block.text.trim()}`);
23614
+ // src/exporters/gemini.ts
23615
+ function yamlString(value) {
23616
+ if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
23617
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
23621
23618
  }
23622
- return sections.join("\n\n");
23619
+ return value;
23623
23620
  }
23624
- var init_instructions = __esm({
23625
- "src/core/instructions.ts"() {
23621
+ var path25, GeminiExporter;
23622
+ var init_gemini = __esm({
23623
+ "src/exporters/gemini.ts"() {
23626
23624
  "use strict";
23627
- init_skills();
23625
+ path25 = __toESM(require("path"));
23626
+ init_fs();
23627
+ GeminiExporter = class {
23628
+ constructor() {
23629
+ this.targetType = "gemini";
23630
+ }
23631
+ outputPath(ctx) {
23632
+ const { agent, target, projectRoot: projectRoot2 } = ctx;
23633
+ const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
23634
+ return path25.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
23635
+ }
23636
+ export(ctx) {
23637
+ const { agent, renderedInstructions } = ctx;
23638
+ const filePath = this.outputPath(ctx);
23639
+ const indentedInstructions = renderedInstructions.split("\n").map((line2) => line2.length > 0 ? ` ${line2}` : "").join("\n");
23640
+ const content = [
23641
+ `name: ${yamlString(agent.name)}`,
23642
+ `description: ${yamlString(agent.description)}`,
23643
+ `system_prompt: |`,
23644
+ indentedInstructions,
23645
+ ""
23646
+ ].join("\n");
23647
+ const changed = writeFileIfChanged(filePath, content);
23648
+ return { outputPath: filePath, content, unchanged: !changed };
23649
+ }
23650
+ };
23628
23651
  }
23629
23652
  });
23630
23653
 
23631
- // src/core/skills.ts
23632
- function loadProjectExtensions2() {
23633
- return loadProjectExtensions();
23634
- }
23635
- function loadProjectConfig3() {
23636
- return loadProjectConfig();
23637
- }
23638
- function packSkillId(skill) {
23639
- return `${packNamespace(skill.pack)}-${skill.id}`;
23640
- }
23641
- function packNamespace(pack) {
23642
- return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
23643
- }
23644
- function extensionsFor(builtin, packSkills) {
23645
- return packSkills.filter((s) => s.extends === builtin && fs18.existsSync(s.sourcePath));
23646
- }
23647
- function composeBuiltinSkill(name, packSkills) {
23648
- const srcPath = skillTemplatePath(name);
23649
- const base = fs18.existsSync(srcPath) ? fs18.readFileSync(srcPath, "utf-8") : "";
23650
- const sections = extensionsFor(name, packSkills);
23651
- if (sections.length === 0) return base;
23652
- const parts = [base.trimEnd()];
23653
- for (const section of sections) {
23654
- const body = fs18.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
23655
- parts.push(`## Platform: ${section.pack}`, body.trim());
23654
+ // src/exporters/registry.ts
23655
+ function getExporter(target) {
23656
+ const type = typeof target === "string" ? target : target.type;
23657
+ const exporter = EXPORTERS.get(type);
23658
+ if (!exporter) {
23659
+ throw new WaironError(`No exporter registered for target type: "${type}"`);
23656
23660
  }
23657
- return `${parts.join("\n\n")}
23658
- `;
23659
- }
23660
- function readFrontmatter(raw, fallbackName) {
23661
- const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
23662
- const block = match ? match[1] : "";
23663
- const field = (key) => {
23664
- const m = new RegExp(`^${key}:\\s*(.*)$`, "m").exec(block);
23665
- return m ? m[1].trim() : "";
23666
- };
23667
- return { name: field("name") || fallbackName, description: field("description") };
23668
- }
23669
- function builtinSkillsDir() {
23670
- return path23.resolve(__dirname, "..", "templates", "skills");
23671
- }
23672
- function skillTemplatePath(name) {
23673
- return path23.join(builtinSkillsDir(), `${name}.md`);
23661
+ return exporter;
23674
23662
  }
23675
- function skillDestPath(type, destDir, name) {
23676
- if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
23677
- return path23.join(destDir, name, "SKILL.md");
23663
+ var EXPORTERS;
23664
+ var init_registry2 = __esm({
23665
+ "src/exporters/registry.ts"() {
23666
+ "use strict";
23667
+ init_claude();
23668
+ init_custom();
23669
+ init_gemini();
23670
+ init_errors();
23671
+ EXPORTERS = /* @__PURE__ */ new Map([
23672
+ ["claude", new ClaudeExporter({ emitBudget: true })],
23673
+ ["gemini", new GeminiExporter()],
23674
+ ["agy", new GeminiExporter()],
23675
+ ["cursor", new ClaudeExporter()],
23676
+ ["copilot", new ClaudeExporter()],
23677
+ ["codex", new ClaudeExporter()],
23678
+ ["custom", new CustomExporter()]
23679
+ ]);
23678
23680
  }
23679
- return path23.join(destDir, `${name}.md`);
23680
- }
23681
- function skillsDirForTarget(type) {
23682
- switch (type) {
23683
- case "claude":
23684
- return fromProjectRoot(".claude", "skills");
23685
- case "gemini":
23686
- // Gemini-based Antigravity
23687
- case "agy":
23688
- case "codex":
23689
- return fromProjectRoot(".agents", "skills");
23690
- case "cursor":
23691
- return fromProjectRoot(".cursor", "skills");
23692
- default:
23693
- return null;
23681
+ });
23682
+
23683
+ // src/exporters/base.ts
23684
+ var WAIRON_MANAGED_MARKER, WAIRON_MANAGED_BANNER;
23685
+ var init_base = __esm({
23686
+ "src/exporters/base.ts"() {
23687
+ "use strict";
23688
+ WAIRON_MANAGED_MARKER = "wairon:managed";
23689
+ WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
23694
23690
  }
23695
- }
23696
- function listSkillNames() {
23697
- return [...SKILL_NAMES];
23698
- }
23699
- function exportSddSkills(targetTypes) {
23700
- const types = targetTypes ?? activeTargetTypes2();
23701
- const destinations = [];
23702
- const skipped = [];
23703
- let fileCount = 0;
23704
- const packSkills = loadProjectExtensions2().skills;
23705
- for (const type of types) {
23706
- const destDir = skillsDirForTarget(type);
23707
- if (!destDir) {
23708
- skipped.push(type);
23709
- continue;
23710
- }
23711
- ensureDir(destDir);
23712
- destinations.push(destDir);
23713
- for (const name of SKILL_NAMES) {
23714
- if (!fs18.existsSync(skillTemplatePath(name))) continue;
23715
- const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
23716
- const destPath = skillDestPath(type, destDir, name);
23717
- ensureDir(path23.dirname(destPath));
23718
- fs18.writeFileSync(destPath, content, "utf-8");
23719
- fileCount++;
23691
+ });
23692
+
23693
+ // src/exporters/generate.ts
23694
+ function generateAgent(agent, projectConfig, options = {}) {
23695
+ const projectRoot2 = options.projectRoot ?? getProjectRoot();
23696
+ const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
23697
+ const rendered = `${WAIRON_MANAGED_BANNER}
23698
+ ${composeAgentBrief(agent.id).instructions}`;
23699
+ const results = [];
23700
+ const budget = resolveBudget(
23701
+ deriveExecutionProfile(agent),
23702
+ projectConfig.execution,
23703
+ agent.id
23704
+ );
23705
+ for (const agentTarget of agent.targets) {
23706
+ const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
23707
+ if (!targetConfig) continue;
23708
+ if (options.filterTargets) {
23709
+ const type = "type" in targetConfig ? targetConfig.type : targetConfig;
23710
+ if (!options.filterTargets.includes(type)) continue;
23720
23711
  }
23721
- for (const skill of packSkills) {
23722
- if (skill.extends !== void 0) continue;
23723
- if (!skill.targets.includes(type)) continue;
23724
- if (!fs18.existsSync(skill.sourcePath)) continue;
23725
- const id = packSkillId(skill);
23726
- const content = fs18.readFileSync(skill.sourcePath, "utf-8");
23727
- const destPath = skillDestPath(type, destDir, id);
23728
- ensureDir(path23.dirname(destPath));
23729
- fs18.writeFileSync(destPath, content, "utf-8");
23730
- fileCount++;
23712
+ if (!options.dryRun) {
23713
+ results.push(getExporter(targetConfig).export({
23714
+ agent,
23715
+ template,
23716
+ renderedInstructions: rendered,
23717
+ projectRoot: projectRoot2,
23718
+ target: targetConfig,
23719
+ budget
23720
+ }));
23731
23721
  }
23732
23722
  }
23733
- return { destinations, fileCount, skipped };
23723
+ return { agent, results };
23734
23724
  }
23735
- function checkSkillFreshness(type) {
23736
- const dir = skillsDirForTarget(type);
23737
- const result = { dir, missing: [], stale: [], ok: [] };
23738
- if (!dir) return result;
23739
- const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
23740
- for (const name of SKILL_NAMES) {
23741
- const destPath = skillDestPath(type, dir, name);
23742
- if (!fs18.existsSync(destPath)) {
23743
- result.missing.push(name);
23744
- continue;
23745
- }
23746
- const want = composeBuiltinSkill(name, packSkills);
23747
- const have = fs18.readFileSync(destPath, "utf-8");
23748
- if (have === want) result.ok.push(name);
23749
- else result.stale.push(name);
23725
+ function generateAll(agents, projectConfig, options = {}) {
23726
+ let pool = agents;
23727
+ if (options.filterDomainIds && options.filterDomainIds.length > 0) {
23728
+ const ids = new Set(options.filterDomainIds);
23729
+ pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
23750
23730
  }
23751
- return result;
23752
- }
23753
- function activeTargetTypes2() {
23754
- const config = loadProjectConfig3();
23755
- if (!config) return [];
23756
- return activeTargetTypes(config);
23731
+ return pool.map((agent) => generateAgent(agent, projectConfig, options));
23757
23732
  }
23758
- function readSkillFrontmatter(name) {
23759
- return readFrontmatter(fs18.readFileSync(skillTemplatePath(name), "utf-8"), name);
23733
+ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getProjectRoot()) {
23734
+ const expected = /* @__PURE__ */ new Set();
23735
+ for (const agent of agents) {
23736
+ for (const agentTarget of agent.targets) {
23737
+ const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
23738
+ if (!targetConfig) continue;
23739
+ const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
23740
+ expected.add(path26.resolve(getExporter(targetConfig).outputPath(ctx)));
23741
+ }
23742
+ }
23743
+ return expected;
23760
23744
  }
23761
- function listSkillResources() {
23762
- const builtin = RESOURCE_SKILL_IDS.map((id) => {
23763
- const fm = readSkillFrontmatter(id);
23764
- return {
23765
- id,
23766
- name: fm.name,
23767
- description: fm.description,
23768
- version: WAIRON_VERSION,
23769
- resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
23770
- defaultForHostedMcp: true
23771
- };
23772
- });
23773
- const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs18.existsSync(s.sourcePath)).map((skill) => {
23774
- const id = packSkillId(skill);
23775
- const fm = readFrontmatter(fs18.readFileSync(skill.sourcePath, "utf-8"), id);
23776
- return {
23777
- id,
23778
- name: fm.name,
23779
- description: fm.description,
23780
- version: skill.packVersion ?? WAIRON_VERSION,
23781
- resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
23782
- defaultForHostedMcp: true
23783
- };
23745
+ function resolveTargetConfig(agentTarget, projectConfig) {
23746
+ const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
23747
+ return projectConfig.targets.find((t) => {
23748
+ if (typeof t === "string") return t === targetType;
23749
+ return t.type === targetType;
23784
23750
  });
23785
- return [...builtin, ...pack];
23786
- }
23787
- function readSkillResource(resourceId) {
23788
- const packSkills = loadProjectExtensions2().skills;
23789
- if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
23790
- const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
23791
- if (packSkill) return fs18.readFileSync(packSkill.sourcePath, "utf-8");
23792
- return fs18.readFileSync(skillTemplatePath(resourceId), "utf-8");
23793
23751
  }
23794
- function listResources() {
23795
- return listSkillResources();
23796
- }
23797
- function buildServerInstructions2() {
23798
- return buildServerInstructions();
23799
- }
23800
- function readResource(resourceId) {
23801
- const known = listSkillResources().some((d) => d.id === resourceId);
23802
- if (!known) throw new SkillResourceNotFoundError(resourceId);
23803
- return readSkillResource(resourceId);
23804
- }
23805
- var path23, fs18, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
23806
- var init_skills = __esm({
23807
- "src/core/skills.ts"() {
23752
+ var path26;
23753
+ var init_generate = __esm({
23754
+ "src/exporters/generate.ts"() {
23808
23755
  "use strict";
23809
- path23 = __toESM(require("path"));
23810
- fs18 = __toESM(require("fs"));
23756
+ path26 = __toESM(require("path"));
23757
+ init_templates();
23758
+ init_agent_resolver();
23759
+ init_execution_profile();
23760
+ init_budget_policy();
23811
23761
  init_fs();
23812
- init_defaults();
23813
- init_project();
23814
- init_extensions();
23815
- init_instructions();
23816
- init_core();
23817
- SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
23818
- SKILL_RESOURCE_SCHEME = "wairon-skill";
23819
- RESOURCE_SKILL_IDS = [...SKILL_NAMES].sort();
23820
- SkillResourceNotFoundError = class extends Error {
23821
- constructor(resourceId) {
23822
- super(`Unknown SDD skill resource id: "${resourceId}".`);
23823
- this.name = "SkillResourceNotFoundError";
23824
- }
23825
- };
23762
+ init_registry2();
23763
+ init_base();
23826
23764
  }
23827
23765
  });
23828
23766
 
@@ -27201,41 +27139,41 @@ function hasAuthoredSpecs(root) {
27201
27139
  if (!pathExists(specsDir)) {
27202
27140
  return false;
27203
27141
  }
27204
- const systemIndex = path24.resolve(paths.specsSystem());
27142
+ const systemIndex = path27.resolve(paths.specsSystem());
27205
27143
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
27206
- if (path24.resolve(file) !== systemIndex) {
27144
+ if (path27.resolve(file) !== systemIndex) {
27207
27145
  return true;
27208
27146
  }
27209
27147
  }
27210
27148
  return false;
27211
27149
  }
27212
27150
  function createStagingDir(destDir) {
27213
- const root = path24.resolve(destDir);
27214
- fs19.mkdirSync(root, { recursive: true });
27215
- const stagingDir = fs19.mkdtempSync(path24.join(root, STAGING_PREFIX));
27151
+ const root = path27.resolve(destDir);
27152
+ fs18.mkdirSync(root, { recursive: true });
27153
+ const stagingDir = fs18.mkdtempSync(path27.join(root, STAGING_PREFIX));
27216
27154
  return stagingDir;
27217
27155
  }
27218
27156
  function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
27219
- const root = path24.resolve(destDir);
27220
- const backupDir = path24.join(root, BACKUP_DIR, timestampSlug());
27157
+ const root = path27.resolve(destDir);
27158
+ const backupDir = path27.join(root, BACKUP_DIR, timestampSlug());
27221
27159
  let backedUp = false;
27222
27160
  for (const rootRel of roots) {
27223
27161
  const relative15 = rootRel === "." ? "" : rootRel;
27224
- const stagedSpecDir = path24.join(stagingDir, relative15, ".wai");
27225
- if (!fs19.existsSync(stagedSpecDir)) continue;
27226
- const liveSpecDir = liveSpecDirFor(path24.join(root, relative15));
27227
- if (backupExisting && liveSpecDir && fs19.existsSync(liveSpecDir)) {
27228
- const backupTarget = path24.join(backupDir, relative15, path24.basename(liveSpecDir));
27229
- fs19.mkdirSync(path24.dirname(backupTarget), { recursive: true });
27230
- fs19.renameSync(liveSpecDir, backupTarget);
27162
+ const stagedSpecDir = path27.join(stagingDir, relative15, ".wai");
27163
+ if (!fs18.existsSync(stagedSpecDir)) continue;
27164
+ const liveSpecDir = liveSpecDirFor(path27.join(root, relative15));
27165
+ if (backupExisting && liveSpecDir && fs18.existsSync(liveSpecDir)) {
27166
+ const backupTarget = path27.join(backupDir, relative15, path27.basename(liveSpecDir));
27167
+ fs18.mkdirSync(path27.dirname(backupTarget), { recursive: true });
27168
+ fs18.renameSync(liveSpecDir, backupTarget);
27231
27169
  backedUp = true;
27232
- } else if (liveSpecDir && fs19.existsSync(liveSpecDir)) {
27233
- fs19.rmSync(liveSpecDir, { recursive: true, force: true });
27170
+ } else if (liveSpecDir && fs18.existsSync(liveSpecDir)) {
27171
+ fs18.rmSync(liveSpecDir, { recursive: true, force: true });
27234
27172
  }
27235
- const target = path24.join(root, relative15, ".wai");
27236
- fs19.mkdirSync(path24.dirname(target), { recursive: true });
27237
- if (fs19.existsSync(target)) fs19.rmSync(target, { recursive: true, force: true });
27238
- fs19.renameSync(stagedSpecDir, target);
27173
+ const target = path27.join(root, relative15, ".wai");
27174
+ fs18.mkdirSync(path27.dirname(target), { recursive: true });
27175
+ if (fs18.existsSync(target)) fs18.rmSync(target, { recursive: true, force: true });
27176
+ fs18.renameSync(stagedSpecDir, target);
27239
27177
  }
27240
27178
  if (backedUp) {
27241
27179
  return backupDir;
@@ -27244,7 +27182,7 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
27244
27182
  }
27245
27183
  function discardStagingDir(stagingDir) {
27246
27184
  try {
27247
- fs19.rmSync(stagingDir, { recursive: true, force: true });
27185
+ fs18.rmSync(stagingDir, { recursive: true, force: true });
27248
27186
  } catch {
27249
27187
  }
27250
27188
  }
@@ -27258,7 +27196,7 @@ function exportSpecTree(includeDerived, allowPartial) {
27258
27196
  const skipped = [...inspection.skipped];
27259
27197
  const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
27260
27198
  for (const rel2 of inspection.roots) {
27261
- const waiDir = aiPathsAt(path24.resolve(root, rel2)).root();
27199
+ const waiDir = aiPathsAt(path27.resolve(root, rel2)).root();
27262
27200
  if (pathExists(waiDir)) {
27263
27201
  roots.push({ relativePath: rel2, waiDir });
27264
27202
  } else {
@@ -27289,7 +27227,7 @@ function importSpecTree(archive, options) {
27289
27227
  if (!info.compatible) {
27290
27228
  throw new Error("incompatible .waitree archive (format or minimum wairon version)");
27291
27229
  }
27292
- const destDir = path24.resolve(options.destDir ?? getProjectRoot());
27230
+ const destDir = path27.resolve(options.destDir ?? getProjectRoot());
27293
27231
  const occupied = hasSpecTree(destDir);
27294
27232
  const authored = hasAuthoredSpecs(destDir);
27295
27233
  if (authored && options.replaceExisting !== true) {
@@ -27320,21 +27258,21 @@ function importSpecTree(archive, options) {
27320
27258
  return result;
27321
27259
  }
27322
27260
  function liveSpecDirFor(rootDir) {
27323
- const modern = path24.join(rootDir, ".wai");
27324
- if (fs19.existsSync(modern)) return modern;
27325
- const legacy = path24.join(rootDir, ".wairon");
27326
- if (fs19.existsSync(legacy)) return legacy;
27261
+ const modern = path27.join(rootDir, ".wai");
27262
+ if (fs18.existsSync(modern)) return modern;
27263
+ const legacy = path27.join(rootDir, ".wairon");
27264
+ if (fs18.existsSync(legacy)) return legacy;
27327
27265
  return null;
27328
27266
  }
27329
27267
  function timestampSlug() {
27330
27268
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27331
27269
  }
27332
- var fs19, path24, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27270
+ var fs18, path27, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27333
27271
  var init_treetransfer = __esm({
27334
27272
  "src/core/treetransfer.ts"() {
27335
27273
  "use strict";
27336
- fs19 = __toESM(require("fs"));
27337
- path24 = __toESM(require("path"));
27274
+ fs18 = __toESM(require("fs"));
27275
+ path27 = __toESM(require("path"));
27338
27276
  sdkPortal = __toESM(require_dist());
27339
27277
  init_paths();
27340
27278
  init_fs();
@@ -27345,270 +27283,6 @@ var init_treetransfer = __esm({
27345
27283
  }
27346
27284
  });
27347
27285
 
27348
- // src/exporters/claude.ts
27349
- function toolsFor(budget) {
27350
- const base = TOOLS_BY_CLASS[budget.toolClass];
27351
- if (!base) return void 0;
27352
- if (budget.allowNestedDelegation && !base.includes("Agent")) {
27353
- return [...base, "Agent"];
27354
- }
27355
- if (!budget.allowNestedDelegation) {
27356
- return base.filter((t) => t !== "Agent");
27357
- }
27358
- return base;
27359
- }
27360
- function mcpServersFor(access) {
27361
- return access === "none" ? "[]" : void 0;
27362
- }
27363
- function yamlScalar(value) {
27364
- return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
27365
- }
27366
- var path25, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
27367
- var init_claude = __esm({
27368
- "src/exporters/claude.ts"() {
27369
- "use strict";
27370
- path25 = __toESM(require("path"));
27371
- init_fs();
27372
- MODEL_BY_TIER = {
27373
- small: "haiku",
27374
- standard: "sonnet",
27375
- large: "opus",
27376
- frontier: "fable"
27377
- };
27378
- TOOLS_BY_CLASS = {
27379
- "read-only": ["Read", "Grep", "Glob"],
27380
- implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
27381
- orchestrate: ["Agent", "SendMessage", "TodoWrite"],
27382
- // `full` means "inherit everything" — expressed by omitting the field.
27383
- full: void 0
27384
- };
27385
- ClaudeExporter = class {
27386
- constructor(options = {}) {
27387
- this.options = options;
27388
- this.targetType = "claude";
27389
- }
27390
- outputPath(ctx) {
27391
- const { agent, target, projectRoot: projectRoot2 } = ctx;
27392
- const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
27393
- return path25.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27394
- }
27395
- export(ctx) {
27396
- const { agent, renderedInstructions, budget } = ctx;
27397
- const filePath = this.outputPath(ctx);
27398
- const frontmatter = [
27399
- `name: ${agent.name}`,
27400
- `description: ${yamlScalar(agent.description)}`
27401
- ];
27402
- if (budget && this.options.emitBudget) {
27403
- if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
27404
- if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
27405
- if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
27406
- const tools = toolsFor(budget);
27407
- if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
27408
- const servers = mcpServersFor(budget.mcp);
27409
- if (servers) frontmatter.push(`mcpServers: ${servers}`);
27410
- }
27411
- const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
27412
- const changed = writeFileIfChanged(filePath, content);
27413
- return { outputPath: filePath, content, unchanged: !changed };
27414
- }
27415
- };
27416
- }
27417
- });
27418
-
27419
- // src/exporters/custom.ts
27420
- var path26, CustomExporter;
27421
- var init_custom = __esm({
27422
- "src/exporters/custom.ts"() {
27423
- "use strict";
27424
- path26 = __toESM(require("path"));
27425
- init_fs();
27426
- CustomExporter = class {
27427
- constructor() {
27428
- this.targetType = "custom";
27429
- }
27430
- outputPath(ctx) {
27431
- const { agent, target, projectRoot: projectRoot2 } = ctx;
27432
- if (!("outputDir" in target)) {
27433
- throw new Error("CustomExporter requires target.outputDir");
27434
- }
27435
- return path26.resolve(projectRoot2, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27436
- }
27437
- export(ctx) {
27438
- const { agent, target, renderedInstructions } = ctx;
27439
- const label = "label" in target ? target.label : "Custom";
27440
- const filePath = this.outputPath(ctx);
27441
- const content = [
27442
- "---",
27443
- `name: ${agent.name}`,
27444
- `description: ${agent.description}`,
27445
- `id: ${agent.id}`,
27446
- `target: ${label}`,
27447
- "---",
27448
- "",
27449
- renderedInstructions,
27450
- ""
27451
- ].join("\n");
27452
- const changed = writeFileIfChanged(filePath, content);
27453
- return { outputPath: filePath, content, unchanged: !changed };
27454
- }
27455
- };
27456
- }
27457
- });
27458
-
27459
- // src/exporters/gemini.ts
27460
- function yamlString(value) {
27461
- if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
27462
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
27463
- }
27464
- return value;
27465
- }
27466
- var path27, GeminiExporter;
27467
- var init_gemini = __esm({
27468
- "src/exporters/gemini.ts"() {
27469
- "use strict";
27470
- path27 = __toESM(require("path"));
27471
- init_fs();
27472
- GeminiExporter = class {
27473
- constructor() {
27474
- this.targetType = "gemini";
27475
- }
27476
- outputPath(ctx) {
27477
- const { agent, target, projectRoot: projectRoot2 } = ctx;
27478
- const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
27479
- return path27.resolve(projectRoot2, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
27480
- }
27481
- export(ctx) {
27482
- const { agent, renderedInstructions } = ctx;
27483
- const filePath = this.outputPath(ctx);
27484
- const indentedInstructions = renderedInstructions.split("\n").map((line2) => line2.length > 0 ? ` ${line2}` : "").join("\n");
27485
- const content = [
27486
- `name: ${yamlString(agent.name)}`,
27487
- `description: ${yamlString(agent.description)}`,
27488
- `system_prompt: |`,
27489
- indentedInstructions,
27490
- ""
27491
- ].join("\n");
27492
- const changed = writeFileIfChanged(filePath, content);
27493
- return { outputPath: filePath, content, unchanged: !changed };
27494
- }
27495
- };
27496
- }
27497
- });
27498
-
27499
- // src/exporters/registry.ts
27500
- function getExporter(target) {
27501
- const type = typeof target === "string" ? target : target.type;
27502
- const exporter = EXPORTERS.get(type);
27503
- if (!exporter) {
27504
- throw new WaironError(`No exporter registered for target type: "${type}"`);
27505
- }
27506
- return exporter;
27507
- }
27508
- var EXPORTERS;
27509
- var init_registry2 = __esm({
27510
- "src/exporters/registry.ts"() {
27511
- "use strict";
27512
- init_claude();
27513
- init_custom();
27514
- init_gemini();
27515
- init_errors();
27516
- EXPORTERS = /* @__PURE__ */ new Map([
27517
- ["claude", new ClaudeExporter({ emitBudget: true })],
27518
- ["gemini", new GeminiExporter()],
27519
- ["agy", new GeminiExporter()],
27520
- ["cursor", new ClaudeExporter()],
27521
- ["copilot", new ClaudeExporter()],
27522
- ["codex", new ClaudeExporter()],
27523
- ["custom", new CustomExporter()]
27524
- ]);
27525
- }
27526
- });
27527
-
27528
- // src/exporters/base.ts
27529
- var WAIRON_MANAGED_MARKER, WAIRON_MANAGED_BANNER;
27530
- var init_base = __esm({
27531
- "src/exporters/base.ts"() {
27532
- "use strict";
27533
- WAIRON_MANAGED_MARKER = "wairon:managed";
27534
- WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
27535
- }
27536
- });
27537
-
27538
- // src/exporters/generate.ts
27539
- function generateAgent(agent, projectConfig, options = {}) {
27540
- const projectRoot2 = options.projectRoot ?? getProjectRoot();
27541
- const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
27542
- const rendered = `${WAIRON_MANAGED_BANNER}
27543
- ${composeAgentBrief(agent.id).instructions}`;
27544
- const results = [];
27545
- const budget = resolveBudget(
27546
- deriveExecutionProfile(agent),
27547
- projectConfig.execution,
27548
- agent.id
27549
- );
27550
- for (const agentTarget of agent.targets) {
27551
- const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
27552
- if (!targetConfig) continue;
27553
- if (options.filterTargets) {
27554
- const type = "type" in targetConfig ? targetConfig.type : targetConfig;
27555
- if (!options.filterTargets.includes(type)) continue;
27556
- }
27557
- if (!options.dryRun) {
27558
- results.push(getExporter(targetConfig).export({
27559
- agent,
27560
- template,
27561
- renderedInstructions: rendered,
27562
- projectRoot: projectRoot2,
27563
- target: targetConfig,
27564
- budget
27565
- }));
27566
- }
27567
- }
27568
- return { agent, results };
27569
- }
27570
- function generateAll(agents, projectConfig, options = {}) {
27571
- let pool = agents;
27572
- if (options.filterDomainIds && options.filterDomainIds.length > 0) {
27573
- const ids = new Set(options.filterDomainIds);
27574
- pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
27575
- }
27576
- return pool.map((agent) => generateAgent(agent, projectConfig, options));
27577
- }
27578
- function resolveExpectedOutputPaths(agents, projectConfig, projectRoot2 = getProjectRoot()) {
27579
- const expected = /* @__PURE__ */ new Set();
27580
- for (const agent of agents) {
27581
- for (const agentTarget of agent.targets) {
27582
- const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
27583
- if (!targetConfig) continue;
27584
- const ctx = { agent, projectRoot: projectRoot2, target: targetConfig };
27585
- expected.add(path28.resolve(getExporter(targetConfig).outputPath(ctx)));
27586
- }
27587
- }
27588
- return expected;
27589
- }
27590
- function resolveTargetConfig(agentTarget, projectConfig) {
27591
- const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
27592
- return projectConfig.targets.find((t) => {
27593
- if (typeof t === "string") return t === targetType;
27594
- return t.type === targetType;
27595
- });
27596
- }
27597
- var path28;
27598
- var init_generate = __esm({
27599
- "src/exporters/generate.ts"() {
27600
- "use strict";
27601
- path28 = __toESM(require("path"));
27602
- init_templates();
27603
- init_agent_resolver();
27604
- init_execution_profile();
27605
- init_budget_policy();
27606
- init_fs();
27607
- init_registry2();
27608
- init_base();
27609
- }
27610
- });
27611
-
27612
27286
  // src/core/stereotype-migration.ts
27613
27287
  function retireSpecialists(apply) {
27614
27288
  const components = loadComponentSpecs();
@@ -27756,6 +27430,17 @@ var init_narrative_repair = __esm({
27756
27430
  }
27757
27431
  });
27758
27432
 
27433
+ // src/models/lock.ts
27434
+ function describeApprover(who) {
27435
+ const label = who.name ? `${who.name} (${who.id})` : who.id;
27436
+ return who.source === "hosted" ? `${label} [authenticated]` : label;
27437
+ }
27438
+ var init_lock = __esm({
27439
+ "src/models/lock.ts"() {
27440
+ "use strict";
27441
+ }
27442
+ });
27443
+
27759
27444
  // src/core/approval.ts
27760
27445
  function diffSize(d) {
27761
27446
  return d.added.length + d.changed.length + d.removed.length;
@@ -27765,12 +27450,12 @@ function digest(content) {
27765
27450
  return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
27766
27451
  }
27767
27452
  function currentSpecDigests(root = getProjectRoot()) {
27768
- const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path29.resolve(root, s.projectPath).split(path29.sep).join("/")}/`);
27453
+ const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path28.resolve(root, s.projectPath).split(path28.sep).join("/")}/`);
27769
27454
  const out = {};
27770
27455
  for (const [abs, content] of snapshotSpecFiles()) {
27771
- const normalized = path29.resolve(abs).split(path29.sep).join("/");
27456
+ const normalized = path28.resolve(abs).split(path28.sep).join("/");
27772
27457
  if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
27773
- out[path29.relative(root, abs).split(path29.sep).join("/")] = digest(content);
27458
+ out[path28.relative(root, abs).split(path28.sep).join("/")] = digest(content);
27774
27459
  }
27775
27460
  return out;
27776
27461
  }
@@ -27828,7 +27513,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
27828
27513
  const pins = {};
27829
27514
  for (const mount of mounts) {
27830
27515
  if (!mount.projectPath || mount.id.includes("::")) continue;
27831
- const child = readLockRecordAt(path29.resolve(root, mount.projectPath));
27516
+ const child = readLockRecordAt(path28.resolve(root, mount.projectPath));
27832
27517
  if (child) pins[mount.id] = pinOf(child.stateId);
27833
27518
  }
27834
27519
  return pins;
@@ -27844,15 +27529,62 @@ function movedChildren(mounts, root = getProjectRoot()) {
27844
27529
  }
27845
27530
  return moved;
27846
27531
  }
27847
- var crypto4, path29;
27532
+ function approvalVerdict() {
27533
+ const quiet = { text: "", drifted: false };
27534
+ try {
27535
+ const approval = approvalRecord();
27536
+ if (!approval) return quiet;
27537
+ const by = describeApprover(approval.lockedBy);
27538
+ const moved = movedChildren(loadSubsystemSpecs());
27539
+ const childNote = moved.length ? `
27540
+ ${moved.length} chained child project(s) moved since approval: ${moved.map((m) => m.id).join(", ")}.` : "";
27541
+ const diff = diffAgainstApproval();
27542
+ if (!diff) {
27543
+ return {
27544
+ text: `
27545
+ Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec approval, so drift is only visible at whole-tree level. Re-lock to record it.${childNote}
27546
+ `,
27547
+ drifted: moved.length > 0
27548
+ };
27549
+ }
27550
+ if (diffSize(diff) === 0) {
27551
+ return {
27552
+ text: `
27553
+ Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}
27554
+ `,
27555
+ drifted: moved.length > 0
27556
+ };
27557
+ }
27558
+ const parts = [];
27559
+ if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
27560
+ if (diff.added.length) parts.push(`${diff.added.length} added`);
27561
+ if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
27562
+ const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
27563
+ const rest = diffSize(diff) - named.length;
27564
+ const n = diffSize(diff);
27565
+ const noun = `${n} spec${n === 1 ? "" : "s"}`;
27566
+ const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(diff)).length + 1)} since approval` : `${noun} changed since approval (${parts.join(", ")})`;
27567
+ return {
27568
+ text: `
27569
+ ${headline} \u2014 approved ${approval.lockedAt} by ${by}:
27570
+ ` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
27571
+ \u2026 and ${rest} more` : "") + childNote + "\n",
27572
+ drifted: true
27573
+ };
27574
+ } catch {
27575
+ return quiet;
27576
+ }
27577
+ }
27578
+ var crypto4, path28;
27848
27579
  var init_approval = __esm({
27849
27580
  "src/core/approval.ts"() {
27850
27581
  "use strict";
27851
27582
  crypto4 = __toESM(require("crypto"));
27852
- path29 = __toESM(require("path"));
27583
+ path28 = __toESM(require("path"));
27853
27584
  init_fs();
27854
27585
  init_specs2();
27855
27586
  init_lockfile();
27587
+ init_lock();
27856
27588
  }
27857
27589
  });
27858
27590
 
@@ -27917,13 +27649,13 @@ var init_stamp = __esm({
27917
27649
 
27918
27650
  // src/utils/ai-guide.ts
27919
27651
  function globalGuideFilePath(targetType) {
27920
- if (targetType === "claude") return path30.join(process.env["CLAUDE_CONFIG_DIR"] || path30.join(os6.homedir(), ".claude"), "CLAUDE.md");
27921
- if (targetType === "gemini") return path30.join(process.env["GEMINI_CONFIG_DIR"] || path30.join(os6.homedir(), ".gemini"), "GEMINI.md");
27652
+ if (targetType === "claude") return path29.join(process.env["CLAUDE_CONFIG_DIR"] || path29.join(os6.homedir(), ".claude"), "CLAUDE.md");
27653
+ if (targetType === "gemini") return path29.join(process.env["GEMINI_CONFIG_DIR"] || path29.join(os6.homedir(), ".gemini"), "GEMINI.md");
27922
27654
  return null;
27923
27655
  }
27924
27656
  function localGuideFilePath(projectRoot2, targetType) {
27925
- if (targetType === "claude") return path30.join(projectRoot2, ".claude", "CLAUDE.md");
27926
- if (targetType === "gemini" || targetType === "agy") return path30.join(projectRoot2, ".gemini", "GEMINI.md");
27657
+ if (targetType === "claude") return path29.join(projectRoot2, ".claude", "CLAUDE.md");
27658
+ if (targetType === "gemini" || targetType === "agy") return path29.join(projectRoot2, ".gemini", "GEMINI.md");
27927
27659
  return null;
27928
27660
  }
27929
27661
  function injectGuide(filePath, scope) {
@@ -27935,11 +27667,11 @@ ${versionStamp()}
27935
27667
  ${body}
27936
27668
  ${GUIDE_MARKER_END}
27937
27669
  `;
27938
- const existing = fs20.existsSync(filePath) ? fs20.readFileSync(filePath, "utf-8") : "";
27670
+ const existing = fs19.existsSync(filePath) ? fs19.readFileSync(filePath, "utf-8") : "";
27939
27671
  const stripped = stripGuideSection(existing);
27940
27672
  const newContent = stripped.trimEnd() + section;
27941
- fs20.mkdirSync(path30.dirname(filePath), { recursive: true });
27942
- fs20.writeFileSync(filePath, newContent, "utf-8");
27673
+ fs19.mkdirSync(path29.dirname(filePath), { recursive: true });
27674
+ fs19.writeFileSync(filePath, newContent, "utf-8");
27943
27675
  }
27944
27676
  function stripGuideSection(content) {
27945
27677
  const start2 = content.indexOf(GUIDE_MARKER_START);
@@ -27949,7 +27681,7 @@ function stripGuideSection(content) {
27949
27681
  }
27950
27682
  function writeRootGuideDelegator(projectRoot2, targetType) {
27951
27683
  if (targetType === "claude") {
27952
- const filePath = path30.join(projectRoot2, "CLAUDE.md");
27684
+ const filePath = path29.join(projectRoot2, "CLAUDE.md");
27953
27685
  const content = `@.claude/CLAUDE.md
27954
27686
 
27955
27687
  # Wairon SDD Project
@@ -27962,42 +27694,42 @@ To design or modify the system, invoke the **\`sdd-architect\`** skill
27962
27694
  (in \`.claude/skills/\`). Author and validate specs with the \`sdd_*\` MCP tools;
27963
27695
  the \`wairon\` CLI is the human developer's tool, not yours.
27964
27696
  `;
27965
- fs20.writeFileSync(filePath, content, "utf-8");
27697
+ fs19.writeFileSync(filePath, content, "utf-8");
27966
27698
  } else if (targetType === "gemini" || targetType === "agy") {
27967
- const filePath = path30.join(projectRoot2, "GEMINI.md");
27699
+ const filePath = path29.join(projectRoot2, "GEMINI.md");
27968
27700
  const content = `# Wairon SDD Project
27969
27701
  ${GUIDE_MARKER_START}
27970
27702
  ${versionStamp()}
27971
27703
  ${LOCAL_GUIDE_BODY}
27972
27704
  ${GUIDE_MARKER_END}
27973
27705
  `;
27974
- fs20.writeFileSync(filePath, content, "utf-8");
27706
+ fs19.writeFileSync(filePath, content, "utf-8");
27975
27707
  } else if (targetType === "cursor") {
27976
- const filePath = path30.join(projectRoot2, ".cursorrules");
27708
+ const filePath = path29.join(projectRoot2, ".cursorrules");
27977
27709
  const content = `# Wairon SDD Project
27978
27710
 
27979
27711
  This project uses the Wairon Spec-Driven Development (SDD) framework.
27980
27712
 
27981
27713
  Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
27982
27714
  `;
27983
- fs20.writeFileSync(filePath, content, "utf-8");
27715
+ fs19.writeFileSync(filePath, content, "utf-8");
27984
27716
  } else if (targetType === "copilot") {
27985
- const filePath = path30.join(projectRoot2, ".github", "copilot-instructions.md");
27986
- fs20.mkdirSync(path30.dirname(filePath), { recursive: true });
27717
+ const filePath = path29.join(projectRoot2, ".github", "copilot-instructions.md");
27718
+ fs19.mkdirSync(path29.dirname(filePath), { recursive: true });
27987
27719
  const content = `# Wairon SDD Project
27988
27720
 
27989
27721
  This project uses the Wairon Spec-Driven Development (SDD) framework.
27990
27722
 
27991
27723
  Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
27992
27724
  `;
27993
- fs20.writeFileSync(filePath, content, "utf-8");
27725
+ fs19.writeFileSync(filePath, content, "utf-8");
27994
27726
  } else if (targetType === "codex") {
27995
- const filePath = path30.join(projectRoot2, ".codexrules");
27727
+ const filePath = path29.join(projectRoot2, ".codexrules");
27996
27728
  const content = `# Wairon SDD Project
27997
27729
 
27998
27730
  Refer to [.codex/agents/](.codex/agents/) for full instructions.
27999
27731
  `;
28000
- fs20.writeFileSync(filePath, content, "utf-8");
27732
+ fs19.writeFileSync(filePath, content, "utf-8");
28001
27733
  }
28002
27734
  }
28003
27735
  function reinjectLocalGuides(projectRoot2, targetTypes) {
@@ -28013,13 +27745,13 @@ function reinjectLocalGuides(projectRoot2, targetTypes) {
28013
27745
  }
28014
27746
  return written;
28015
27747
  }
28016
- var fs20, os6, path30, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
27748
+ var fs19, os6, path29, GUIDE_MARKER_START, GUIDE_MARKER_END, GLOBAL_GUIDE_BODY, LOCAL_GUIDE_BODY, GUIDE_TARGETS;
28017
27749
  var init_ai_guide = __esm({
28018
27750
  "src/utils/ai-guide.ts"() {
28019
27751
  "use strict";
28020
- fs20 = __toESM(require("fs"));
27752
+ fs19 = __toESM(require("fs"));
28021
27753
  os6 = __toESM(require("os"));
28022
- path30 = __toESM(require("path"));
27754
+ path29 = __toESM(require("path"));
28023
27755
  init_stamp();
28024
27756
  GUIDE_MARKER_START = "<!-- wairon-guide-start -->";
28025
27757
  GUIDE_MARKER_END = "<!-- wairon-guide-end -->";
@@ -28214,248 +27946,7 @@ var init_context = __esm({
28214
27946
  }
28215
27947
  });
28216
27948
 
28217
- // src/core/index.ts
28218
- function resolveDomains2() {
28219
- return resolveDomains();
28220
- }
28221
- function addDomain(domain) {
28222
- registerDomain(domain);
28223
- }
28224
- function removeDomain(id) {
28225
- unregisterDomain(id);
28226
- }
28227
- function loadProjectConfig() {
28228
- return projectConfigRepository.load();
28229
- }
28230
- function projectConfigExists() {
28231
- return projectConfigRepository.exists();
28232
- }
28233
- var init_core = __esm({
28234
- "src/core/index.ts"() {
28235
- "use strict";
28236
- init_project_config();
28237
- init_domain_projector();
28238
- init_domain_curator();
28239
- init_detection();
28240
- init_templates();
28241
- init_validation();
28242
- init_extensions();
28243
- init_variants();
28244
- init_rules();
28245
- init_specs2();
28246
- init_provision();
28247
- init_diagram();
28248
- init_lockfile();
28249
- init_statehash();
28250
- init_agent_resolver();
28251
- init_skills();
28252
- init_surfaces();
28253
- init_openapi();
28254
- init_packstore();
28255
- init_treetransfer();
28256
- init_extensions();
28257
- init_agent_resolver();
28258
- init_generate();
28259
- init_treetransfer();
28260
- init_provision();
28261
- init_stereotype_migration();
28262
- init_narrative_repair();
28263
- init_approval();
28264
- init_approver();
28265
- init_specs2();
28266
- init_project_config();
28267
- init_execution_profile();
28268
- init_budget_policy();
28269
- init_ai_guide();
28270
- init_stamp();
28271
- init_context();
28272
- }
28273
- });
28274
-
28275
- // src/commands/status.ts
28276
- async function runStatus(options = {}) {
28277
- assertProjectInitialized();
28278
- scanAllSpecs({ recursive: options.recursive ?? true });
28279
- const system = loadSystemSpec();
28280
- const loaderErrors = getLoaderIssues();
28281
- if (loaderErrors.length > 0) {
28282
- logger.error("Failed to parse specification files:");
28283
- for (const issue2 of loaderErrors) {
28284
- const prefix = issue2.specId ? import_chalk4.default.gray(`[${issue2.specId}] `) : "";
28285
- logger.error(`${prefix}[${issue2.code}] ${issue2.message}`);
28286
- }
28287
- process.exit(1);
28288
- }
28289
- let subsystems = loadSubsystemSpecs();
28290
- let components = loadComponentSpecs();
28291
- let interfaces = loadInterfaceSpecs();
28292
- let implementations = loadImplementationSpecs();
28293
- if (options.subsystem) {
28294
- subsystems = subsystems.filter((s) => s.id === options.subsystem || s.id.startsWith(`${options.subsystem}::`));
28295
- components = components.filter((c) => c.subsystem === options.subsystem || c.subsystem.startsWith(`${options.subsystem}::`));
28296
- interfaces = interfaces.filter((i) => {
28297
- const c = components.find((comp) => comp.id === i.component);
28298
- return c !== void 0;
28299
- });
28300
- implementations = implementations.filter((im) => {
28301
- const inf = interfaces.find((i) => i.id === im.contract);
28302
- return inf !== void 0;
28303
- });
28304
- }
28305
- if (!system) {
28306
- logger.error("L0 System specification (system.yaml) is missing. Run `wairon init` first.");
28307
- process.exit(1);
28308
- }
28309
- logger.header("Architecture Status Dashboard");
28310
- logger.blank();
28311
- const componentScores = /* @__PURE__ */ new Map();
28312
- for (const comp of components) {
28313
- let score = 20;
28314
- const intf = interfaces.find((i) => i.component === comp.id);
28315
- if (intf) {
28316
- score += 30;
28317
- }
28318
- const impl = implementations.find((im) => intf && im.contract === intf.id);
28319
- if (impl) {
28320
- score += 30;
28321
- const sourceFiles = implementationSourceFiles(impl);
28322
- if (sourceFiles.length > 0 && sourceFiles.every((f) => pathExists(fromProjectRoot(f)))) {
28323
- score += 20;
28324
- }
28325
- }
28326
- const isDraft = comp.status === "draft" || comp.status === "design" || intf && (intf.status === "draft" || intf.status === "design") || impl && (impl.status === "draft" || impl.status === "design");
28327
- if (isDraft) {
28328
- score = Math.min(score, 50);
28329
- }
28330
- componentScores.set(comp.id, score);
28331
- }
28332
- const getSubsystemScore = (subId) => {
28333
- const sub = subsystems.find((s) => s.id === subId);
28334
- if (!sub) return 0;
28335
- const subComps = components.filter((c) => c.subsystem === subId);
28336
- if (subComps.length === 0) return 0;
28337
- const totalScore = subComps.reduce((acc, c) => acc + (componentScores.get(c.id) ?? 0), 0);
28338
- let avg = Math.round(totalScore / subComps.length);
28339
- if (sub.status === "draft" || sub.status === "design") {
28340
- avg = Math.min(avg, 50);
28341
- }
28342
- return avg;
28343
- };
28344
- const totalSubsystemsScore = subsystems.reduce((acc, s) => acc + getSubsystemScore(s.id), 0);
28345
- const systemScore = subsystems.length > 0 ? Math.round(totalSubsystemsScore / subsystems.length) : 0;
28346
- const systemColor = systemScore === 100 ? import_chalk4.default.green : systemScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
28347
- console.log(`${import_chalk4.default.bold.blue("\u25CF System:")} ${import_chalk4.default.bold(system.name)} ${systemColor(`(${systemScore}% Complete)`)}`);
28348
- for (let i = 0; i < subsystems.length; i++) {
28349
- const sub = subsystems[i];
28350
- const isLastSub = i === subsystems.length - 1;
28351
- const subPrefix = isLastSub ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
28352
- const subIndent = isLastSub ? " " : "\u2502 ";
28353
- const subScore = getSubsystemScore(sub.id);
28354
- const subStatusStr = sub.status !== "complete" ? import_chalk4.default.yellow(` [${sub.status}]`) : "";
28355
- const subScoreColor = subScore === 100 ? import_chalk4.default.green : subScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
28356
- console.log(`${import_chalk4.default.gray(subPrefix)}${import_chalk4.default.bold.cyan(`[Subsystem] ${sub.id}`)}${subStatusStr} ${subScoreColor(`(${subScore}%)`)}`);
28357
- const subComps = components.filter((c) => c.subsystem === sub.id);
28358
- for (let j = 0; j < subComps.length; j++) {
28359
- const comp = subComps[j];
28360
- const isLastComp = j === subComps.length - 1;
28361
- const compPrefix = isLastComp ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
28362
- const compIndent = isLastComp ? " " : "\u2502 ";
28363
- const compScore = componentScores.get(comp.id) ?? 0;
28364
- const compStatusStr = comp.status !== "complete" ? import_chalk4.default.yellow(` [${comp.status}]`) : "";
28365
- const compScoreColor = compScore === 100 ? import_chalk4.default.green : compScore >= 50 ? import_chalk4.default.yellow : import_chalk4.default.red;
28366
- console.log(`${import_chalk4.default.gray(subIndent + compPrefix)}${import_chalk4.default.magenta(`[Component: ${comp.componentType}] ${comp.id}`)}${compStatusStr} ${compScoreColor(`(${compScore}%)`)}`);
28367
- const intf = interfaces.find((inf) => inf.component === comp.id);
28368
- const impl = implementations.find((im) => intf && im.contract === intf.id);
28369
- const intfPrefix = intf && impl ? "\u251C\u2500\u2500 " : "\u2514\u2500\u2500 ";
28370
- if (intf) {
28371
- const intfStatusStr = intf.status !== "complete" ? import_chalk4.default.yellow(` [${intf.status}]`) : "";
28372
- console.log(`${import_chalk4.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk4.default.blue(`Interface: ${intf.id}`)}${intfStatusStr} (${intf.methods.length} methods)`);
28373
- } else {
28374
- console.log(`${import_chalk4.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk4.default.red("Interface: Missing (-30%)")}`);
28375
- }
28376
- if (impl) {
28377
- const implStatusStr = impl.status !== "complete" ? import_chalk4.default.yellow(` [${impl.status}]`) : "";
28378
- const methodsWithOwnPath = impl.methods.filter((m) => m.sourcePath);
28379
- let pathStr;
28380
- if (impl.sourcePath) {
28381
- pathStr = pathExists(fromProjectRoot(impl.sourcePath)) ? import_chalk4.default.green(` -> ${impl.sourcePath}`) : import_chalk4.default.red(` -> ${impl.sourcePath} (File Missing!)`);
28382
- } else if (methodsWithOwnPath.length > 0) {
28383
- pathStr = "";
28384
- } else {
28385
- pathStr = import_chalk4.default.gray(" (No source path)");
28386
- }
28387
- console.log(`${import_chalk4.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk4.default.green(`Implementation: ${impl.id}`)}${implStatusStr}${pathStr}`);
28388
- const methodIndent = subIndent + compIndent + " ";
28389
- for (let k = 0; k < methodsWithOwnPath.length; k++) {
28390
- const method2 = methodsWithOwnPath[k];
28391
- const isLastMethod = k === methodsWithOwnPath.length - 1;
28392
- const methodPrefix = isLastMethod ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
28393
- const methodPath = method2.sourcePath;
28394
- const methodLine = pathExists(fromProjectRoot(methodPath)) ? import_chalk4.default.green(`method ${method2.name} -> ${methodPath}`) : import_chalk4.default.red(`method ${method2.name} -> ${methodPath} (File Missing!)`);
28395
- console.log(`${import_chalk4.default.gray(methodIndent + methodPrefix)}${methodLine}`);
28396
- }
28397
- } else {
28398
- console.log(`${import_chalk4.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk4.default.red("Implementation: Missing (-30%)")}`);
28399
- }
28400
- }
28401
- }
28402
- const lock = lockReport();
28403
- if (lock.text.trim()) {
28404
- logger.blank();
28405
- if (lock.drifted) logger.warn(lock.text.trim());
28406
- else logger.info(lock.text.trim());
28407
- }
28408
- logger.blank();
28409
- }
28410
- function lockLine() {
28411
- return lockReport().text;
28412
- }
28413
- function lockReport() {
28414
- const quiet = { text: "", drifted: false };
28415
- try {
28416
- const approval = approvalRecord();
28417
- if (!approval) return quiet;
28418
- const by = describeApprover(approval.lockedBy);
28419
- const moved = movedChildren(loadSubsystemSpecs());
28420
- const childNote = moved.length ? `
28421
- ${moved.length} chained child project(s) moved since approval: ${moved.map((m) => m.id).join(", ")}.` : "";
28422
- const diff = diffAgainstApproval();
28423
- if (!diff) {
28424
- return {
28425
- text: `
28426
- Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec approval, so drift is only visible at whole-tree level. Re-lock to record it.${childNote}
28427
- `,
28428
- drifted: moved.length > 0
28429
- };
28430
- }
28431
- if (diffSize(diff) === 0) {
28432
- return {
28433
- text: `
28434
- Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}
28435
- `,
28436
- drifted: moved.length > 0
28437
- };
28438
- }
28439
- const parts = [];
28440
- if (diff.changed.length) parts.push(`${diff.changed.length} changed`);
28441
- if (diff.added.length) parts.push(`${diff.added.length} added`);
28442
- if (diff.removed.length) parts.push(`${diff.removed.length} removed`);
28443
- const named = [...diff.changed, ...diff.added, ...diff.removed].slice(0, 5);
28444
- const rest = diffSize(diff) - named.length;
28445
- const n = diffSize(diff);
28446
- const noun = `${n} spec${n === 1 ? "" : "s"}`;
28447
- const headline = parts.length === 1 ? `${noun} ${parts[0].slice(String(diffSize(diff)).length + 1)} since approval` : `${noun} changed since approval (${parts.join(", ")})`;
28448
- return {
28449
- text: `
28450
- ${headline} \u2014 approved ${approval.lockedAt} by ${by}:
28451
- ` + named.map((p) => ` ${p}`).join("\n") + (rest > 0 ? `
28452
- \u2026 and ${rest} more` : "") + childNote + "\n",
28453
- drifted: true
28454
- };
28455
- } catch {
28456
- return quiet;
28457
- }
28458
- }
27949
+ // src/core/status.ts
28459
27950
  function getStatusReport(options = {}) {
28460
27951
  scanAllSpecs({ recursive: options.recursive ?? true });
28461
27952
  const system = loadSystemSpec();
@@ -28585,21 +28076,388 @@ function getStatusReport(options = {}) {
28585
28076
  }
28586
28077
  }
28587
28078
  }
28588
- output += lockLine();
28589
28079
  return output;
28590
28080
  }
28591
- var import_chalk4;
28592
28081
  var init_status = __esm({
28593
- "src/commands/status.ts"() {
28082
+ "src/core/status.ts"() {
28594
28083
  "use strict";
28595
- import_chalk4 = __toESM(require("chalk"));
28596
- init_logger();
28597
- init_paths();
28084
+ init_specs2();
28085
+ init_specs();
28598
28086
  init_fs();
28087
+ }
28088
+ });
28089
+
28090
+ // src/core/index.ts
28091
+ function resolveDomains2() {
28092
+ return resolveDomains();
28093
+ }
28094
+ function addDomain(domain) {
28095
+ registerDomain(domain);
28096
+ }
28097
+ function removeDomain(id) {
28098
+ unregisterDomain(id);
28099
+ }
28100
+ function loadProjectConfig() {
28101
+ return projectConfigRepository.load();
28102
+ }
28103
+ function projectConfigExists() {
28104
+ return projectConfigRepository.exists();
28105
+ }
28106
+ var init_core = __esm({
28107
+ "src/core/index.ts"() {
28108
+ "use strict";
28109
+ init_project_config();
28110
+ init_domain_projector();
28111
+ init_domain_curator();
28112
+ init_rules();
28599
28113
  init_specs2();
28600
- init_core();
28114
+ init_provision();
28115
+ init_statehash();
28601
28116
  init_lockfile();
28602
- init_specs();
28117
+ init_diagram();
28118
+ init_diagram();
28119
+ init_detection();
28120
+ init_extensions();
28121
+ init_packstore();
28122
+ init_agent_resolver();
28123
+ init_generate();
28124
+ init_treetransfer();
28125
+ init_provision();
28126
+ init_stereotype_migration();
28127
+ init_narrative_repair();
28128
+ init_approval();
28129
+ init_approver();
28130
+ init_specs2();
28131
+ init_project_config();
28132
+ init_execution_profile();
28133
+ init_budget_policy();
28134
+ init_ai_guide();
28135
+ init_stamp();
28136
+ init_context();
28137
+ init_status();
28138
+ }
28139
+ });
28140
+
28141
+ // src/core/instructions.ts
28142
+ function governingProfile() {
28143
+ try {
28144
+ return loadProjectConfig4()?.projectType ?? null;
28145
+ } catch {
28146
+ return null;
28147
+ }
28148
+ }
28149
+ function blockApplies(block, profile) {
28150
+ if (!block.profile || block.profile.length === 0) return true;
28151
+ return profile !== null && block.profile.includes(profile);
28152
+ }
28153
+ function skillPointers(skills) {
28154
+ if (skills.length === 0) return "(none published by this server)";
28155
+ return skills.map((s) => `- \`${s.resourceUri}\` \u2014 ${s.description || s.name}`).join("\n");
28156
+ }
28157
+ function waironInstructions(profile, packNames, skills, variants) {
28158
+ return `# wairon \u2014 Spec-Driven Development (SDD)
28159
+
28160
+ This server owns one system's **spec tree**: its architecture, specified top-down
28161
+ and machine-checked. Author it ONLY through this server's \`sdd_*\` tools \u2014 never by
28162
+ writing spec files directly, and never via the \`wairon\` CLI (that is the human
28163
+ developer's tool).
28164
+
28165
+ ## The tree: L0 \u2192 L5
28166
+
28167
+ - **L0 System** \u2014 vision, boundaries, global requirements, target language.
28168
+ - **L1 Subsystem** \u2014 an isolated service, its \`publicInterfaces\`, its trusted links.
28169
+ - **L2 Component** \u2014 ONE building block (\`Portal\`, \`Orchestrator\`, \`Supervisor\`,
28170
+ \`Actor\`, \`Store\`, \`Index\`, \`Query\`, \`Registry\`, \`Adapter\`, \`Observer\`, and
28171
+ \`View\` for UIs) or pattern (\`Repository\`, and \`FeatureComponent\` or
28172
+ \`RouterComponent\` for UIs), plus \`owns\` (a pattern's private members) and
28173
+ \`dependsOn\` (collaborators). Logic is an \`Orchestrator\` whose \`dependencyClass\`
28174
+ (\`pure\` or \`read\`; unset, a workflow) bounds what it may depend on; a gateway is
28175
+ a \`Portal\` with the \`gateway\` variant. \`Specialist\` and \`Gateway\` are retired.
28176
+ - **L3 Interface** \u2014 the contract: method signatures with structured \`params\`,
28177
+ plus wire \`endpoint\` bindings for a Portal's methods.
28178
+ - **L4 Implementation** \u2014 a concrete realization of one L3 contract: narrative
28179
+ detail level, bound \`technologies\` (the vendor swap seam), optional \`sourcePath\`.
28180
+ - **L5 Narrative** \u2014 each method's logic as a FLAT numbered step list; \`call\` steps
28181
+ resolve to real dependency methods, and flow steps jump by step number.
28182
+
28183
+ These stereotypes are not labels \u2014 they carry enforced dependency rules (e.g. a
28184
+ Portal may not depend on a Store; state lives in a Store, never as fields inside
28185
+ an Orchestrator). \`sdd_validate_tree\` is the gate, and it is the authority on
28186
+ whether a design is legal.
28187
+
28188
+ ## Authoring order
28189
+
28190
+ 1. \`sdd_initialize_system\` (L0), then \`sdd_add_subsystem\` (L1).
28191
+ 2. Then, **one subsystem at a time**: \`sdd_add_component\` (L2) \u2192 \`sdd_define_interface\`
28192
+ (L3) \u2192 \`sdd_set_endpoints\` for Portals \u2192 \`sdd_write_narrative\` (L4+L5).
28193
+ 3. Run \`sdd_validate_tree\` as you go \u2014 early and per subsystem, not once at the end.
28194
+ \`sdd_get_status\` reports completeness; \`sdd_get_spec\` reads any spec back as JSON.
28195
+ 4. Edit granularly with \`sdd_update_spec\` (it renumbers narrative steps and
28196
+ relocates jump targets for you).
28197
+
28198
+ ## The tool schemas are the field reference
28199
+
28200
+ Every \`sdd_*\` tool's input schema is self-describing and authoritative \u2014 read the
28201
+ schema rather than guessing field names, and prefer structured \`params\` over prose
28202
+ signatures. Specs are ${profile === null ? "validated against the project's configured profile" : `governed by the **\`${profile}\`** profile`}, which decides which stereotypes are legal
28203
+ here, which narrative flow constructs the target language supports, and which
28204
+ extra rules apply. A subsystem may override the profile for itself.
28205
+
28206
+ ## Loaded extension packs
28207
+
28208
+ ${packNames.length === 0 ? "None \u2014 only wairon's built-in doctrine applies." : `${packNames.map((n) => `\`${n}\``).join(", ")} \u2014 these inject profiles, language tables, patterns, and
28209
+ additional conformance rules. Their doctrine is enforced by the same gate.`}
28210
+
28211
+ ${variants.length === 0 ? "" : `## Component variants in play
28212
+
28213
+ This project defines variants \u2014 named, base-anchored kinds carrying implementation
28214
+ guidance, so every component of the same kind is built alike: ${variants.map((v) => `\`${v.id}\` (a kind of ${v.base})`).join(", ")}.
28215
+ Tag a component with \`variant\` where it fits, and read a component's resolved
28216
+ guidance and its same-variant siblings from \`sdd_get_spec\` (kind: component).
28217
+
28218
+ `}## Read the skills BEFORE authoring
28219
+
28220
+ This server publishes its skills as MCP resources. **Read \`wairon-skill://sdd-architect\`
28221
+ before you design anything**, and the others when their turn comes \u2014 they carry the
28222
+ architecture standard, the narrative step reference, and the review checklist that
28223
+ this briefing only points at:
28224
+
28225
+ ${skillPointers(skills)}
28226
+
28227
+ ## Delegating implementation
28228
+
28229
+ Delegate component work from a LIVE agent brief, never a generated file: discover
28230
+ agents via \`listAgents\` (or the \`wairon-agent://\` resources), fetch the target's
28231
+ brief with \`sdd_get_agent_brief\`, and spawn a scoped subagent from it \u2014 its
28232
+ \`instructions\` as prompt, its \`ownedPaths\` as write fence. Briefs are composed
28233
+ from the current spec tree on every call, so a re-lock never requires a session
28234
+ restart \u2014 fetch fresh per delegation. \`wairon-skill://sdd-delegate\` carries the
28235
+ full flow.`;
28236
+ }
28237
+ function buildServerInstructions() {
28238
+ const profile = governingProfile();
28239
+ let skills = [];
28240
+ let blocks = [];
28241
+ let packNames = [];
28242
+ const variants = [];
28243
+ try {
28244
+ skills = listSkillResources();
28245
+ const extensions = loadProjectExtensions2();
28246
+ blocks = extensions.instructions;
28247
+ packNames = extensions.packNames;
28248
+ } catch {
28249
+ }
28250
+ const sections = [waironInstructions(profile, packNames, skills, variants)];
28251
+ for (const block of blocks) {
28252
+ if (!blockApplies(block, profile)) continue;
28253
+ sections.push(`## From pack "${block.pack}"
28254
+
28255
+ ${block.text.trim()}`);
28256
+ }
28257
+ return sections.join("\n\n");
28258
+ }
28259
+ var init_instructions = __esm({
28260
+ "src/core/instructions.ts"() {
28261
+ "use strict";
28262
+ init_skills();
28263
+ }
28264
+ });
28265
+
28266
+ // src/core/skills.ts
28267
+ function loadProjectExtensions2() {
28268
+ return loadProjectExtensions();
28269
+ }
28270
+ function loadProjectConfig4() {
28271
+ return loadProjectConfig();
28272
+ }
28273
+ function packSkillId(skill) {
28274
+ return `${packNamespace(skill.pack)}-${skill.id}`;
28275
+ }
28276
+ function packNamespace(pack) {
28277
+ return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
28278
+ }
28279
+ function extensionsFor(builtin, packSkills) {
28280
+ return packSkills.filter((s) => s.extends === builtin && fs20.existsSync(s.sourcePath));
28281
+ }
28282
+ function composeBuiltinSkill(name, packSkills) {
28283
+ const srcPath = skillTemplatePath(name);
28284
+ const base = fs20.existsSync(srcPath) ? fs20.readFileSync(srcPath, "utf-8") : "";
28285
+ const sections = extensionsFor(name, packSkills);
28286
+ if (sections.length === 0) return base;
28287
+ const parts = [base.trimEnd()];
28288
+ for (const section of sections) {
28289
+ const body = fs20.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
28290
+ parts.push(`## Platform: ${section.pack}`, body.trim());
28291
+ }
28292
+ return `${parts.join("\n\n")}
28293
+ `;
28294
+ }
28295
+ function readFrontmatter(raw, fallbackName) {
28296
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
28297
+ const block = match ? match[1] : "";
28298
+ const field = (key) => {
28299
+ const m = new RegExp(`^${key}:\\s*(.*)$`, "m").exec(block);
28300
+ return m ? m[1].trim() : "";
28301
+ };
28302
+ return { name: field("name") || fallbackName, description: field("description") };
28303
+ }
28304
+ function builtinSkillsDir() {
28305
+ return path31.resolve(__dirname, "..", "templates", "skills");
28306
+ }
28307
+ function skillTemplatePath(name) {
28308
+ return path31.join(builtinSkillsDir(), `${name}.md`);
28309
+ }
28310
+ function skillDestPath(type, destDir, name) {
28311
+ if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
28312
+ return path31.join(destDir, name, "SKILL.md");
28313
+ }
28314
+ return path31.join(destDir, `${name}.md`);
28315
+ }
28316
+ function skillsDirForTarget(type) {
28317
+ switch (type) {
28318
+ case "claude":
28319
+ return fromProjectRoot(".claude", "skills");
28320
+ case "gemini":
28321
+ // Gemini-based Antigravity
28322
+ case "agy":
28323
+ case "codex":
28324
+ return fromProjectRoot(".agents", "skills");
28325
+ case "cursor":
28326
+ return fromProjectRoot(".cursor", "skills");
28327
+ default:
28328
+ return null;
28329
+ }
28330
+ }
28331
+ function listSkillNames() {
28332
+ return [...SKILL_NAMES];
28333
+ }
28334
+ function exportSddSkills(targetTypes) {
28335
+ const types = targetTypes ?? activeTargetTypes2();
28336
+ const destinations = [];
28337
+ const skipped = [];
28338
+ let fileCount = 0;
28339
+ const packSkills = loadProjectExtensions2().skills;
28340
+ for (const type of types) {
28341
+ const destDir = skillsDirForTarget(type);
28342
+ if (!destDir) {
28343
+ skipped.push(type);
28344
+ continue;
28345
+ }
28346
+ ensureDir(destDir);
28347
+ destinations.push(destDir);
28348
+ for (const name of SKILL_NAMES) {
28349
+ if (!fs20.existsSync(skillTemplatePath(name))) continue;
28350
+ const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
28351
+ const destPath = skillDestPath(type, destDir, name);
28352
+ ensureDir(path31.dirname(destPath));
28353
+ fs20.writeFileSync(destPath, content, "utf-8");
28354
+ fileCount++;
28355
+ }
28356
+ for (const skill of packSkills) {
28357
+ if (skill.extends !== void 0) continue;
28358
+ if (!skill.targets.includes(type)) continue;
28359
+ if (!fs20.existsSync(skill.sourcePath)) continue;
28360
+ const id = packSkillId(skill);
28361
+ const content = fs20.readFileSync(skill.sourcePath, "utf-8");
28362
+ const destPath = skillDestPath(type, destDir, id);
28363
+ ensureDir(path31.dirname(destPath));
28364
+ fs20.writeFileSync(destPath, content, "utf-8");
28365
+ fileCount++;
28366
+ }
28367
+ }
28368
+ return { destinations, fileCount, skipped };
28369
+ }
28370
+ function checkSkillFreshness(type) {
28371
+ const dir = skillsDirForTarget(type);
28372
+ const result = { dir, missing: [], stale: [], ok: [] };
28373
+ if (!dir) return result;
28374
+ const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
28375
+ for (const name of SKILL_NAMES) {
28376
+ const destPath = skillDestPath(type, dir, name);
28377
+ if (!fs20.existsSync(destPath)) {
28378
+ result.missing.push(name);
28379
+ continue;
28380
+ }
28381
+ const want = composeBuiltinSkill(name, packSkills);
28382
+ const have = fs20.readFileSync(destPath, "utf-8");
28383
+ if (have === want) result.ok.push(name);
28384
+ else result.stale.push(name);
28385
+ }
28386
+ return result;
28387
+ }
28388
+ function activeTargetTypes2() {
28389
+ const config = loadProjectConfig4();
28390
+ if (!config) return [];
28391
+ return activeTargetTypes(config);
28392
+ }
28393
+ function readSkillFrontmatter(name) {
28394
+ return readFrontmatter(fs20.readFileSync(skillTemplatePath(name), "utf-8"), name);
28395
+ }
28396
+ function listSkillResources() {
28397
+ const builtin = RESOURCE_SKILL_IDS.map((id) => {
28398
+ const fm = readSkillFrontmatter(id);
28399
+ return {
28400
+ id,
28401
+ name: fm.name,
28402
+ description: fm.description,
28403
+ version: WAIRON_VERSION,
28404
+ resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
28405
+ defaultForHostedMcp: true
28406
+ };
28407
+ });
28408
+ const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs20.existsSync(s.sourcePath)).map((skill) => {
28409
+ const id = packSkillId(skill);
28410
+ const fm = readFrontmatter(fs20.readFileSync(skill.sourcePath, "utf-8"), id);
28411
+ return {
28412
+ id,
28413
+ name: fm.name,
28414
+ description: fm.description,
28415
+ version: skill.packVersion ?? WAIRON_VERSION,
28416
+ resourceUri: `${SKILL_RESOURCE_SCHEME}://${id}`,
28417
+ defaultForHostedMcp: true
28418
+ };
28419
+ });
28420
+ return [...builtin, ...pack];
28421
+ }
28422
+ function readSkillResource(resourceId) {
28423
+ const packSkills = loadProjectExtensions2().skills;
28424
+ if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
28425
+ const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
28426
+ if (packSkill) return fs20.readFileSync(packSkill.sourcePath, "utf-8");
28427
+ return fs20.readFileSync(skillTemplatePath(resourceId), "utf-8");
28428
+ }
28429
+ function listResources() {
28430
+ return listSkillResources();
28431
+ }
28432
+ function buildServerInstructions2() {
28433
+ return buildServerInstructions();
28434
+ }
28435
+ function readResource(resourceId) {
28436
+ const known = listSkillResources().some((d) => d.id === resourceId);
28437
+ if (!known) throw new SkillResourceNotFoundError(resourceId);
28438
+ return readSkillResource(resourceId);
28439
+ }
28440
+ var path31, fs20, SKILL_NAMES, SKILL_RESOURCE_SCHEME, RESOURCE_SKILL_IDS, SkillResourceNotFoundError;
28441
+ var init_skills = __esm({
28442
+ "src/core/skills.ts"() {
28443
+ "use strict";
28444
+ path31 = __toESM(require("path"));
28445
+ fs20 = __toESM(require("fs"));
28446
+ init_fs();
28447
+ init_defaults();
28448
+ init_project();
28449
+ init_extensions();
28450
+ init_instructions();
28451
+ init_core();
28452
+ SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
28453
+ SKILL_RESOURCE_SCHEME = "wairon-skill";
28454
+ RESOURCE_SKILL_IDS = [...SKILL_NAMES].sort();
28455
+ SkillResourceNotFoundError = class extends Error {
28456
+ constructor(resourceId) {
28457
+ super(`Unknown SDD skill resource id: "${resourceId}".`);
28458
+ this.name = "SkillResourceNotFoundError";
28459
+ }
28460
+ };
28603
28461
  }
28604
28462
  });
28605
28463
 
@@ -30143,7 +30001,9 @@ function createMcpServer(options = {}) {
30143
30001
  subsystem,
30144
30002
  recursive: recursive ?? true
30145
30003
  });
30146
- return text(`${statusFamilyContext()}${report2}`);
30004
+ const family = statusFamilyContext();
30005
+ const verdict = approvalVerdict();
30006
+ return text(`${family}${report2}${verdict.text}`);
30147
30007
  } catch (e) {
30148
30008
  return errText(String(e));
30149
30009
  }
@@ -30342,7 +30202,7 @@ var init_server = __esm({
30342
30202
  init_fs();
30343
30203
  init_yaml();
30344
30204
  init_errors();
30345
- init_status();
30205
+ init_core();
30346
30206
  init_core();
30347
30207
  init_defaults();
30348
30208
  init_narrative_labels();
@@ -30695,7 +30555,7 @@ async function runMcpInstall(options = {}) {
30695
30555
  const desiredEntry = options.hostedUrl ? hostedMcpEntry(options) : useDirectNode ? { command: "node", args: [scriptPath, "mcp", "serve"], env } : { command: "wairon", args: ["mcp", "serve"], env };
30696
30556
  const existingEntry = mcpServers["wairon"];
30697
30557
  if (existingEntry && JSON.stringify(existingEntry) === JSON.stringify(desiredEntry)) {
30698
- logger.info(`wairon MCP server already registered (up to date) for ${agentLabel} in ${import_chalk5.default.gray(settingsPath)}.`);
30558
+ logger.info(`wairon MCP server already registered (up to date) for ${agentLabel} in ${import_chalk4.default.gray(settingsPath)}.`);
30699
30559
  continue;
30700
30560
  }
30701
30561
  const wasStale = !!existingEntry;
@@ -30703,7 +30563,7 @@ async function runMcpInstall(options = {}) {
30703
30563
  settings["mcpServers"] = mcpServers;
30704
30564
  if (!fs22.existsSync(configBase)) fs22.mkdirSync(configBase, { recursive: true });
30705
30565
  fs22.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
30706
- logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk5.default.cyan(settingsPath)}.`);
30566
+ logger.success(`wairon MCP server ${wasStale ? "updated (was stale)" : "registered"} for ${agentLabel} in ${import_chalk4.default.cyan(settingsPath)}.`);
30707
30567
  logger.blank();
30708
30568
  logger.info("AI tools using this config will have access to these wairon tools:");
30709
30569
  logger.info(" listAgents \xB7 getAgent \xB7 listDomains \xB7 validateTopology \xB7 getProjectConfig");
@@ -30711,13 +30571,13 @@ async function runMcpInstall(options = {}) {
30711
30571
  logger.info(" sdd_write_narrative \xB7 sdd_validate_tree \xB7 sdd_get_status");
30712
30572
  logger.blank();
30713
30573
  const restartApp = backend === "gemini" ? "Antigravity CLI (agy)" : "claude";
30714
- logger.info(`Restart ${import_chalk5.default.bold(restartApp)} (or reload MCP servers) to activate.`);
30574
+ logger.info(`Restart ${import_chalk4.default.bold(restartApp)} (or reload MCP servers) to activate.`);
30715
30575
  if (backend === "claude" && !useGlobal) {
30716
30576
  logger.warn('Claude shows project .mcp.json servers as "\u23F8 Pending approval" \u2014 approve wairon once on next launch (or run `claude` and accept the trust prompt).');
30717
30577
  }
30718
30578
  if (backend === "gemini" && !useGlobal) {
30719
30579
  logger.warn("Note: Antigravity (agy) reads MCP servers from its global ~/.gemini/antigravity-cli/mcp_config.json, not this project file.");
30720
- logger.warn("If the agent cannot see the sdd_* tools, run: " + import_chalk5.default.bold("wairon mcp install --backend gemini --global"));
30580
+ logger.warn("If the agent cannot see the sdd_* tools, run: " + import_chalk4.default.bold("wairon mcp install --backend gemini --global"));
30721
30581
  }
30722
30582
  logger.blank();
30723
30583
  }
@@ -30731,7 +30591,7 @@ async function runMcpStatus() {
30731
30591
  const geminiProject = path33.join(process.cwd(), ".gemini", "settings.json");
30732
30592
  const geminiGlobal = path33.join(geminiGlobalDir(), "antigravity-cli", "mcp_config.json");
30733
30593
  logger.blank();
30734
- logger.info(`${import_chalk5.default.bold("wairon MCP Server")}`);
30594
+ logger.info(`${import_chalk4.default.bold("wairon MCP Server")}`);
30735
30595
  logger.blank();
30736
30596
  const checks = [
30737
30597
  { label: "Claude (project)", filePath: claudeProject, fallbackName: ".mcp.json" },
@@ -30741,28 +30601,28 @@ async function runMcpStatus() {
30741
30601
  ];
30742
30602
  for (const { label, filePath, fallbackName } of checks) {
30743
30603
  if (!fs22.existsSync(filePath)) {
30744
- console.log(` ${label}: ${import_chalk5.default.gray(`${fallbackName} not found`)}`);
30604
+ console.log(` ${label}: ${import_chalk4.default.gray(`${fallbackName} not found`)}`);
30745
30605
  continue;
30746
30606
  }
30747
30607
  try {
30748
30608
  const s = JSON.parse(fs22.readFileSync(filePath, "utf8"));
30749
30609
  const registered = !!s["mcpServers"]?.["wairon"];
30750
- const mark = registered ? import_chalk5.default.green("\u2713 registered") : import_chalk5.default.gray("not registered");
30751
- console.log(` ${label}: ${mark} ${import_chalk5.default.gray(filePath)}`);
30610
+ const mark = registered ? import_chalk4.default.green("\u2713 registered") : import_chalk4.default.gray("not registered");
30611
+ console.log(` ${label}: ${mark} ${import_chalk4.default.gray(filePath)}`);
30752
30612
  } catch {
30753
- console.log(` ${label}: ${import_chalk5.default.red("parse error")} ${import_chalk5.default.gray(filePath)}`);
30613
+ console.log(` ${label}: ${import_chalk4.default.red("parse error")} ${import_chalk4.default.gray(filePath)}`);
30754
30614
  }
30755
30615
  }
30756
30616
  logger.blank();
30757
- logger.info(`Project: ${import_chalk5.default.bold(projectConfig.name)}`);
30617
+ logger.info(`Project: ${import_chalk4.default.bold(projectConfig.name)}`);
30758
30618
  logger.blank();
30759
- logger.info(`To register for Claude: ${import_chalk5.default.bold("wairon mcp install --backend claude")}`);
30760
- logger.info(`To register for Antigravity (agy): ${import_chalk5.default.bold("wairon mcp install --backend gemini")}`);
30761
- logger.info(`To start manually: ${import_chalk5.default.bold("wairon mcp serve")}`);
30619
+ logger.info(`To register for Claude: ${import_chalk4.default.bold("wairon mcp install --backend claude")}`);
30620
+ logger.info(`To register for Antigravity (agy): ${import_chalk4.default.bold("wairon mcp install --backend gemini")}`);
30621
+ logger.info(`To start manually: ${import_chalk4.default.bold("wairon mcp serve")}`);
30762
30622
  logger.blank();
30763
30623
  const mcpDir = aiDir("mcp");
30764
30624
  if (fs22.existsSync(mcpDir)) {
30765
- logger.info(`MCP state dir: ${import_chalk5.default.gray(mcpDir)}`);
30625
+ logger.info(`MCP state dir: ${import_chalk4.default.gray(mcpDir)}`);
30766
30626
  }
30767
30627
  }
30768
30628
  function hostedMcpEntry(options) {
@@ -30783,7 +30643,7 @@ function removeLegacyGlobalPlugin() {
30783
30643
  try {
30784
30644
  if (fs22.existsSync(pluginDir)) {
30785
30645
  fs22.rmSync(pluginDir, { recursive: true, force: true });
30786
- logger.info(`Removed legacy global Antigravity plugin at ${import_chalk5.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
30646
+ logger.info(`Removed legacy global Antigravity plugin at ${import_chalk4.default.gray(pluginDir)} (it collides with the wairon MCP server).`);
30787
30647
  return true;
30788
30648
  }
30789
30649
  } catch (e) {
@@ -30791,14 +30651,14 @@ function removeLegacyGlobalPlugin() {
30791
30651
  }
30792
30652
  return false;
30793
30653
  }
30794
- var fs22, os7, path33, import_chalk5;
30654
+ var fs22, os7, path33, import_chalk4;
30795
30655
  var init_mcp = __esm({
30796
30656
  "src/commands/mcp.ts"() {
30797
30657
  "use strict";
30798
30658
  fs22 = __toESM(require("fs"));
30799
30659
  os7 = __toESM(require("os"));
30800
30660
  path33 = __toESM(require("path"));
30801
- import_chalk5 = __toESM(require("chalk"));
30661
+ import_chalk4 = __toESM(require("chalk"));
30802
30662
  init_logger();
30803
30663
  init_paths();
30804
30664
  init_core();
@@ -31032,7 +30892,7 @@ function isSupportedAlias(name) {
31032
30892
 
31033
30893
  // src/commands/init.ts
31034
30894
  var path34 = __toESM(require("path"));
31035
- var import_chalk6 = __toESM(require("chalk"));
30895
+ var import_chalk5 = __toESM(require("chalk"));
31036
30896
  var import_inquirer = __toESM(require("inquirer"));
31037
30897
  init_logger();
31038
30898
  init_errors();
@@ -31041,7 +30901,7 @@ init_yaml();
31041
30901
  init_paths();
31042
30902
 
31043
30903
  // src/commands/subsystem.ts
31044
- var path31 = __toESM(require("path"));
30904
+ var path30 = __toESM(require("path"));
31045
30905
  init_logger();
31046
30906
  init_errors();
31047
30907
  init_fs();
@@ -31057,7 +30917,7 @@ function exportSpecTree2(includeDerived, allowPartial) {
31057
30917
  function importSpecTree2(archive, options) {
31058
30918
  return importSpecTree(archive, options);
31059
30919
  }
31060
- function loadProjectConfig4() {
30920
+ function loadProjectConfig3() {
31061
30921
  return loadProjectConfig();
31062
30922
  }
31063
30923
  function createProjectConfig2(config) {
@@ -31179,9 +31039,9 @@ async function runSubsystemAdd(id, options = {}) {
31179
31039
  updatedAt: now
31180
31040
  };
31181
31041
  createChainedSubsystem2(subsystem, displayName);
31182
- const childDir = path31.resolve(getProjectRoot(), options.projectPath);
31042
+ const childDir = path30.resolve(getProjectRoot(), options.projectPath);
31183
31043
  logger.success(`Added external subsystem "${id}" \u2192 ${options.projectPath}`);
31184
- logger.info(`Scaffolded child project at ${path31.relative(process.cwd(), childDir) || "."}`);
31044
+ logger.info(`Scaffolded child project at ${path30.relative(process.cwd(), childDir) || "."}`);
31185
31045
  logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
31186
31046
  }
31187
31047
  async function runSubsystemMove(id, options = {}) {
@@ -31204,9 +31064,9 @@ async function runSubsystemExternalize(id, options = {}) {
31204
31064
  throw new WaironError("--project-path (the subproject destination) is required.");
31205
31065
  }
31206
31066
  externalizeSubsystem(id, options.projectPath);
31207
- const childDir = path31.resolve(getProjectRoot(), options.projectPath);
31067
+ const childDir = path30.resolve(getProjectRoot(), options.projectPath);
31208
31068
  logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
31209
- logger.info(`Moved its specs into ${path31.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
31069
+ logger.info(`Moved its specs into ${path30.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
31210
31070
  logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
31211
31071
  }
31212
31072
  async function runSubsystemInternalize(id) {
@@ -31444,10 +31304,10 @@ async function runInitInteractive() {
31444
31304
  logger.blank();
31445
31305
  logger.header("Init summary");
31446
31306
  logger.blank();
31447
- console.log(` ${import_chalk6.default.bold("Project:")} ${projectName}`);
31448
- console.log(` ${import_chalk6.default.bold("Profile:")} ${projectType}`);
31449
- console.log(` ${import_chalk6.default.bold("Targets:")} ${targets.map((t) => t.type).join(", ")}`);
31450
- console.log(` ${import_chalk6.default.bold("Guide:")} Auto-inject local AI rules card (${selectedTypes.filter((t) => ["claude", "gemini", "agy"].includes(t)).join(", ") || "none"})`);
31307
+ console.log(` ${import_chalk5.default.bold("Project:")} ${projectName}`);
31308
+ console.log(` ${import_chalk5.default.bold("Profile:")} ${projectType}`);
31309
+ console.log(` ${import_chalk5.default.bold("Targets:")} ${targets.map((t) => t.type).join(", ")}`);
31310
+ console.log(` ${import_chalk5.default.bold("Guide:")} Auto-inject local AI rules card (${selectedTypes.filter((t) => ["claude", "gemini", "agy"].includes(t)).join(", ") || "none"})`);
31451
31311
  logger.blank();
31452
31312
  const { confirmed } = await import_inquirer.default.prompt([
31453
31313
  {
@@ -31786,7 +31646,7 @@ async function runGenerate(options = {}) {
31786
31646
  }
31787
31647
  async function generateLayer(options = {}) {
31788
31648
  assertProjectInitialized();
31789
- const projectConfig = loadProjectConfig4();
31649
+ const projectConfig = loadProjectConfig3();
31790
31650
  if (!projectConfig) throw new ProjectNotInitializedError();
31791
31651
  const agents = resolveAgentTopology2();
31792
31652
  if (agents.length === 0) {
@@ -31885,6 +31745,7 @@ init_logger();
31885
31745
  init_defaults();
31886
31746
  var path36 = __toESM(require("path"));
31887
31747
  init_core();
31748
+ init_lock();
31888
31749
  init_fs();
31889
31750
  init_validation();
31890
31751
  function checkApproval(strict) {
@@ -31979,7 +31840,7 @@ async function runLock(options = {}, gate) {
31979
31840
  }
31980
31841
 
31981
31842
  // src/commands/validate.ts
31982
- var import_chalk7 = __toESM(require("chalk"));
31843
+ var import_chalk6 = __toESM(require("chalk"));
31983
31844
  init_logger();
31984
31845
  init_agent_resolver();
31985
31846
  init_paths();
@@ -32049,7 +31910,7 @@ async function runValidate(options = {}) {
32049
31910
  logger.success("Registry is valid.");
32050
31911
  } else {
32051
31912
  for (const issue2 of regResult.issues) {
32052
- const prefix = issue2.agentId ? import_chalk7.default.gray(`[${issue2.agentId}] `) : "";
31913
+ const prefix = issue2.agentId ? import_chalk6.default.gray(`[${issue2.agentId}] `) : "";
32053
31914
  if (issue2.severity === "error") {
32054
31915
  logger.error(`${prefix}[${issue2.code}] ${issue2.message}`);
32055
31916
  hasErrors = true;
@@ -32084,7 +31945,7 @@ async function runValidate(options = {}) {
32084
31945
  let skippedErrors = 0;
32085
31946
  let skippedWarnings = 0;
32086
31947
  for (const issue2 of sddResult.issues) {
32087
- const prefix = issue2.specId ? import_chalk7.default.gray(`[${issue2.specId}] `) : "";
31948
+ const prefix = issue2.specId ? import_chalk6.default.gray(`[${issue2.specId}] `) : "";
32088
31949
  if (issue2.severity === "error") {
32089
31950
  hasErrors = true;
32090
31951
  if (errorCount < MAX_PRINT) {
@@ -32118,12 +31979,12 @@ async function runValidate(options = {}) {
32118
31979
  const summary = carriedDebtSummary(projectConfig.rules?.conformance?.carried);
32119
31980
  if (summary) {
32120
31981
  logger.blank();
32121
- logger.info(import_chalk7.default.yellow(summary.line));
32122
- for (const note of summary.revisits) logger.info(import_chalk7.default.gray(note));
31982
+ logger.info(import_chalk6.default.yellow(summary.line));
31983
+ for (const note of summary.revisits) logger.info(import_chalk6.default.gray(note));
32123
31984
  }
32124
31985
  logger.blank();
32125
31986
  if (options.ci && waivedWarnings > 0) {
32126
- logger.info(import_chalk7.default.gray(`${waivedWarnings} draft-related warning(s) (non-fatal in --ci): excluded from the failure decision because the referenced specs are in draft/design status.`));
31987
+ logger.info(import_chalk6.default.gray(`${waivedWarnings} draft-related warning(s) (non-fatal in --ci): excluded from the failure decision because the referenced specs are in draft/design status.`));
32127
31988
  }
32128
31989
  const failOnWarnings = !!options.ci && hasFatalWarnings;
32129
31990
  if (hasErrors || failOnWarnings) {
@@ -32131,7 +31992,7 @@ async function runValidate(options = {}) {
32131
31992
  logger.error("Validation failed: warnings are treated as errors in --ci mode.");
32132
31993
  } else {
32133
31994
  logger.error("Validation failed. Fix the errors above.");
32134
- logger.info(import_chalk7.default.cyan("Tip: If you need to temporarily bypass an architectural rule, you can override its severity level in your `.wai/project.yaml` config (e.g. `rules.sddRuleSeverity.CIRCULAR_DEPENDENCY: warning`)."));
31995
+ logger.info(import_chalk6.default.cyan("Tip: If you need to temporarily bypass an architectural rule, you can override its severity level in your `.wai/project.yaml` config (e.g. `rules.sddRuleSeverity.CIRCULAR_DEPENDENCY: warning`)."));
32135
31996
  }
32136
31997
  process.exit(1);
32137
31998
  } else {
@@ -32148,7 +32009,7 @@ init_paths();
32148
32009
  init_fs();
32149
32010
 
32150
32011
  // src/commands/list.ts
32151
- var import_chalk8 = __toESM(require("chalk"));
32012
+ var import_chalk7 = __toESM(require("chalk"));
32152
32013
  init_logger();
32153
32014
  init_paths();
32154
32015
  async function runList() {
@@ -32166,26 +32027,26 @@ async function runList() {
32166
32027
  }
32167
32028
  }
32168
32029
  function printAgent(agent) {
32169
- const status2 = agent.status === "active" ? import_chalk8.default.green(agent.status) : agent.status === "draft" ? import_chalk8.default.yellow(agent.status) : import_chalk8.default.gray(agent.status);
32170
- console.log(`${import_chalk8.default.bold(agent.id)} ${import_chalk8.default.gray(`[${agent.template}]`)} ${status2}`);
32030
+ const status2 = agent.status === "active" ? import_chalk7.default.green(agent.status) : agent.status === "draft" ? import_chalk7.default.yellow(agent.status) : import_chalk7.default.gray(agent.status);
32031
+ console.log(`${import_chalk7.default.bold(agent.id)} ${import_chalk7.default.gray(`[${agent.template}]`)} ${status2}`);
32171
32032
  console.log(` ${agent.description}`);
32172
32033
  if (agent.ownedPaths.length > 0) {
32173
- console.log(import_chalk8.default.gray(` owns: ${agent.ownedPaths.join(", ")}`));
32034
+ console.log(import_chalk7.default.gray(` owns: ${agent.ownedPaths.join(", ")}`));
32174
32035
  }
32175
32036
  if (agent.tags.length > 0) {
32176
- console.log(import_chalk8.default.gray(` tags: ${agent.tags.join(", ")}`));
32037
+ console.log(import_chalk7.default.gray(` tags: ${agent.tags.join(", ")}`));
32177
32038
  }
32178
32039
  const targetLabels = agent.targets.map((t) => {
32179
32040
  if (typeof t === "string") return t;
32180
32041
  const obj = t;
32181
32042
  return obj.label ? `${obj.type}(${obj.label})` : obj.type;
32182
32043
  });
32183
- console.log(import_chalk8.default.gray(` targets: ${targetLabels.join(", ")}`));
32044
+ console.log(import_chalk7.default.gray(` targets: ${targetLabels.join(", ")}`));
32184
32045
  console.log();
32185
32046
  }
32186
32047
 
32187
32048
  // src/commands/show.ts
32188
- var import_chalk9 = __toESM(require("chalk"));
32049
+ var import_chalk8 = __toESM(require("chalk"));
32189
32050
  init_logger();
32190
32051
  init_paths();
32191
32052
  init_errors();
@@ -32198,60 +32059,60 @@ async function runShow(agentId) {
32198
32059
  printAgentDetails(agent);
32199
32060
  }
32200
32061
  function printAgentDetails(agent) {
32201
- const statusColor = agent.status === "active" ? import_chalk9.default.green : agent.status === "draft" ? import_chalk9.default.yellow : import_chalk9.default.gray;
32062
+ const statusColor = agent.status === "active" ? import_chalk8.default.green : agent.status === "draft" ? import_chalk8.default.yellow : import_chalk8.default.gray;
32202
32063
  logger.header(agent.name);
32203
32064
  console.log();
32204
- console.log(`${import_chalk9.default.bold("ID")} ${agent.id}`);
32205
- console.log(`${import_chalk9.default.bold("Status")} ${statusColor(agent.status)}`);
32206
- console.log(`${import_chalk9.default.bold("Template")} ${agent.template}`);
32065
+ console.log(`${import_chalk8.default.bold("ID")} ${agent.id}`);
32066
+ console.log(`${import_chalk8.default.bold("Status")} ${statusColor(agent.status)}`);
32067
+ console.log(`${import_chalk8.default.bold("Template")} ${agent.template}`);
32207
32068
  if (agent.bundleOrigin) {
32208
- console.log(`${import_chalk9.default.bold("Bundle")} ${agent.bundleOrigin}`);
32069
+ console.log(`${import_chalk8.default.bold("Bundle")} ${agent.bundleOrigin}`);
32209
32070
  }
32210
32071
  if (agent.domainRoot) {
32211
- console.log(`${import_chalk9.default.bold("Domain")} ${agent.domainRoot}`);
32072
+ console.log(`${import_chalk8.default.bold("Domain")} ${agent.domainRoot}`);
32212
32073
  }
32213
32074
  console.log();
32214
- console.log(`${import_chalk9.default.bold("Description")}`);
32075
+ console.log(`${import_chalk8.default.bold("Description")}`);
32215
32076
  console.log(` ${agent.description}`);
32216
32077
  console.log();
32217
- console.log(`${import_chalk9.default.bold("Reason")}`);
32078
+ console.log(`${import_chalk8.default.bold("Reason")}`);
32218
32079
  console.log(` ${agent.creationReason}`);
32219
32080
  console.log();
32220
32081
  if (agent.ownedPaths.length > 0) {
32221
- console.log(`${import_chalk9.default.bold("Owned paths")}`);
32082
+ console.log(`${import_chalk8.default.bold("Owned paths")}`);
32222
32083
  for (const p of agent.ownedPaths) {
32223
- console.log(` ${import_chalk9.default.cyan(p)}`);
32084
+ console.log(` ${import_chalk8.default.cyan(p)}`);
32224
32085
  }
32225
32086
  console.log();
32226
32087
  }
32227
32088
  if (agent.readPaths.length > 0) {
32228
- console.log(`${import_chalk9.default.bold("Read paths")}`);
32089
+ console.log(`${import_chalk8.default.bold("Read paths")}`);
32229
32090
  for (const p of agent.readPaths) {
32230
- console.log(` ${import_chalk9.default.gray(p)}`);
32091
+ console.log(` ${import_chalk8.default.gray(p)}`);
32231
32092
  }
32232
32093
  console.log();
32233
32094
  }
32234
32095
  if (agent.writePaths.length > 0) {
32235
- console.log(`${import_chalk9.default.bold("Write paths")}`);
32096
+ console.log(`${import_chalk8.default.bold("Write paths")}`);
32236
32097
  for (const p of agent.writePaths) {
32237
- console.log(` ${import_chalk9.default.gray(p)}`);
32098
+ console.log(` ${import_chalk8.default.gray(p)}`);
32238
32099
  }
32239
32100
  console.log();
32240
32101
  }
32241
32102
  if (agent.tags.length > 0) {
32242
- console.log(`${import_chalk9.default.bold("Tags")} ${agent.tags.map((t) => import_chalk9.default.magenta(t)).join(", ")}`);
32103
+ console.log(`${import_chalk8.default.bold("Tags")} ${agent.tags.map((t) => import_chalk8.default.magenta(t)).join(", ")}`);
32243
32104
  }
32244
32105
  if (agent.dependencies.length > 0) {
32245
- console.log(`${import_chalk9.default.bold("Depends on")} ${agent.dependencies.join(", ")}`);
32106
+ console.log(`${import_chalk8.default.bold("Depends on")} ${agent.dependencies.join(", ")}`);
32246
32107
  }
32247
32108
  const targetLabels = agent.targets.map((t) => {
32248
32109
  if (typeof t === "string") return t;
32249
32110
  const obj = t;
32250
32111
  return obj.label ? `${obj.type}(${obj.label})` : obj.type;
32251
32112
  });
32252
- console.log(`${import_chalk9.default.bold("Targets")} ${targetLabels.join(", ")}`);
32113
+ console.log(`${import_chalk8.default.bold("Targets")} ${targetLabels.join(", ")}`);
32253
32114
  console.log();
32254
- console.log(import_chalk9.default.gray(`Created: ${agent.createdAt} Updated: ${agent.updatedAt}`));
32115
+ console.log(import_chalk8.default.gray(`Created: ${agent.createdAt} Updated: ${agent.updatedAt}`));
32255
32116
  }
32256
32117
 
32257
32118
  // src/cli/index.ts
@@ -32595,8 +32456,148 @@ function cleanStaleBinary(oldPath) {
32595
32456
  }
32596
32457
  var isNewer = isNewerVersion;
32597
32458
 
32598
- // src/cli/index.ts
32599
- init_status();
32459
+ // src/commands/status.ts
32460
+ var import_chalk9 = __toESM(require("chalk"));
32461
+ init_logger();
32462
+ init_paths();
32463
+ init_fs();
32464
+ init_specs2();
32465
+ init_core();
32466
+ init_specs();
32467
+ async function runStatus(options = {}) {
32468
+ assertProjectInitialized();
32469
+ scanAllSpecs({ recursive: options.recursive ?? true });
32470
+ const system = loadSystemSpec();
32471
+ const loaderErrors = getLoaderIssues();
32472
+ if (loaderErrors.length > 0) {
32473
+ logger.error("Failed to parse specification files:");
32474
+ for (const issue2 of loaderErrors) {
32475
+ const prefix = issue2.specId ? import_chalk9.default.gray(`[${issue2.specId}] `) : "";
32476
+ logger.error(`${prefix}[${issue2.code}] ${issue2.message}`);
32477
+ }
32478
+ process.exit(1);
32479
+ }
32480
+ let subsystems = loadSubsystemSpecs();
32481
+ let components = loadComponentSpecs();
32482
+ let interfaces = loadInterfaceSpecs();
32483
+ let implementations = loadImplementationSpecs();
32484
+ if (options.subsystem) {
32485
+ subsystems = subsystems.filter((s) => s.id === options.subsystem || s.id.startsWith(`${options.subsystem}::`));
32486
+ components = components.filter((c) => c.subsystem === options.subsystem || c.subsystem.startsWith(`${options.subsystem}::`));
32487
+ interfaces = interfaces.filter((i) => {
32488
+ const c = components.find((comp) => comp.id === i.component);
32489
+ return c !== void 0;
32490
+ });
32491
+ implementations = implementations.filter((im) => {
32492
+ const inf = interfaces.find((i) => i.id === im.contract);
32493
+ return inf !== void 0;
32494
+ });
32495
+ }
32496
+ if (!system) {
32497
+ logger.error("L0 System specification (system.yaml) is missing. Run `wairon init` first.");
32498
+ process.exit(1);
32499
+ }
32500
+ logger.header("Architecture Status Dashboard");
32501
+ logger.blank();
32502
+ const componentScores = /* @__PURE__ */ new Map();
32503
+ for (const comp of components) {
32504
+ let score = 20;
32505
+ const intf = interfaces.find((i) => i.component === comp.id);
32506
+ if (intf) {
32507
+ score += 30;
32508
+ }
32509
+ const impl = implementations.find((im) => intf && im.contract === intf.id);
32510
+ if (impl) {
32511
+ score += 30;
32512
+ const sourceFiles = implementationSourceFiles(impl);
32513
+ if (sourceFiles.length > 0 && sourceFiles.every((f) => pathExists(fromProjectRoot(f)))) {
32514
+ score += 20;
32515
+ }
32516
+ }
32517
+ const isDraft = comp.status === "draft" || comp.status === "design" || intf && (intf.status === "draft" || intf.status === "design") || impl && (impl.status === "draft" || impl.status === "design");
32518
+ if (isDraft) {
32519
+ score = Math.min(score, 50);
32520
+ }
32521
+ componentScores.set(comp.id, score);
32522
+ }
32523
+ const getSubsystemScore = (subId) => {
32524
+ const sub = subsystems.find((s) => s.id === subId);
32525
+ if (!sub) return 0;
32526
+ const subComps = components.filter((c) => c.subsystem === subId);
32527
+ if (subComps.length === 0) return 0;
32528
+ const totalScore = subComps.reduce((acc, c) => acc + (componentScores.get(c.id) ?? 0), 0);
32529
+ let avg = Math.round(totalScore / subComps.length);
32530
+ if (sub.status === "draft" || sub.status === "design") {
32531
+ avg = Math.min(avg, 50);
32532
+ }
32533
+ return avg;
32534
+ };
32535
+ const totalSubsystemsScore = subsystems.reduce((acc, s) => acc + getSubsystemScore(s.id), 0);
32536
+ const systemScore = subsystems.length > 0 ? Math.round(totalSubsystemsScore / subsystems.length) : 0;
32537
+ const systemColor = systemScore === 100 ? import_chalk9.default.green : systemScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
32538
+ console.log(`${import_chalk9.default.bold.blue("\u25CF System:")} ${import_chalk9.default.bold(system.name)} ${systemColor(`(${systemScore}% Complete)`)}`);
32539
+ for (let i = 0; i < subsystems.length; i++) {
32540
+ const sub = subsystems[i];
32541
+ const isLastSub = i === subsystems.length - 1;
32542
+ const subPrefix = isLastSub ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
32543
+ const subIndent = isLastSub ? " " : "\u2502 ";
32544
+ const subScore = getSubsystemScore(sub.id);
32545
+ const subStatusStr = sub.status !== "complete" ? import_chalk9.default.yellow(` [${sub.status}]`) : "";
32546
+ const subScoreColor = subScore === 100 ? import_chalk9.default.green : subScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
32547
+ console.log(`${import_chalk9.default.gray(subPrefix)}${import_chalk9.default.bold.cyan(`[Subsystem] ${sub.id}`)}${subStatusStr} ${subScoreColor(`(${subScore}%)`)}`);
32548
+ const subComps = components.filter((c) => c.subsystem === sub.id);
32549
+ for (let j = 0; j < subComps.length; j++) {
32550
+ const comp = subComps[j];
32551
+ const isLastComp = j === subComps.length - 1;
32552
+ const compPrefix = isLastComp ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
32553
+ const compIndent = isLastComp ? " " : "\u2502 ";
32554
+ const compScore = componentScores.get(comp.id) ?? 0;
32555
+ const compStatusStr = comp.status !== "complete" ? import_chalk9.default.yellow(` [${comp.status}]`) : "";
32556
+ const compScoreColor = compScore === 100 ? import_chalk9.default.green : compScore >= 50 ? import_chalk9.default.yellow : import_chalk9.default.red;
32557
+ console.log(`${import_chalk9.default.gray(subIndent + compPrefix)}${import_chalk9.default.magenta(`[Component: ${comp.componentType}] ${comp.id}`)}${compStatusStr} ${compScoreColor(`(${compScore}%)`)}`);
32558
+ const intf = interfaces.find((inf) => inf.component === comp.id);
32559
+ const impl = implementations.find((im) => intf && im.contract === intf.id);
32560
+ const intfPrefix = intf && impl ? "\u251C\u2500\u2500 " : "\u2514\u2500\u2500 ";
32561
+ if (intf) {
32562
+ const intfStatusStr = intf.status !== "complete" ? import_chalk9.default.yellow(` [${intf.status}]`) : "";
32563
+ console.log(`${import_chalk9.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk9.default.blue(`Interface: ${intf.id}`)}${intfStatusStr} (${intf.methods.length} methods)`);
32564
+ } else {
32565
+ console.log(`${import_chalk9.default.gray(subIndent + compIndent + intfPrefix)}${import_chalk9.default.red("Interface: Missing (-30%)")}`);
32566
+ }
32567
+ if (impl) {
32568
+ const implStatusStr = impl.status !== "complete" ? import_chalk9.default.yellow(` [${impl.status}]`) : "";
32569
+ const methodsWithOwnPath = impl.methods.filter((m) => m.sourcePath);
32570
+ let pathStr;
32571
+ if (impl.sourcePath) {
32572
+ pathStr = pathExists(fromProjectRoot(impl.sourcePath)) ? import_chalk9.default.green(` -> ${impl.sourcePath}`) : import_chalk9.default.red(` -> ${impl.sourcePath} (File Missing!)`);
32573
+ } else if (methodsWithOwnPath.length > 0) {
32574
+ pathStr = "";
32575
+ } else {
32576
+ pathStr = import_chalk9.default.gray(" (No source path)");
32577
+ }
32578
+ console.log(`${import_chalk9.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk9.default.green(`Implementation: ${impl.id}`)}${implStatusStr}${pathStr}`);
32579
+ const methodIndent = subIndent + compIndent + " ";
32580
+ for (let k = 0; k < methodsWithOwnPath.length; k++) {
32581
+ const method2 = methodsWithOwnPath[k];
32582
+ const isLastMethod = k === methodsWithOwnPath.length - 1;
32583
+ const methodPrefix = isLastMethod ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
32584
+ const methodPath = method2.sourcePath;
32585
+ const methodLine = pathExists(fromProjectRoot(methodPath)) ? import_chalk9.default.green(`method ${method2.name} -> ${methodPath}`) : import_chalk9.default.red(`method ${method2.name} -> ${methodPath} (File Missing!)`);
32586
+ console.log(`${import_chalk9.default.gray(methodIndent + methodPrefix)}${methodLine}`);
32587
+ }
32588
+ } else {
32589
+ console.log(`${import_chalk9.default.gray(subIndent + compIndent + "\u2514\u2500\u2500 ")}${import_chalk9.default.red("Implementation: Missing (-30%)")}`);
32590
+ }
32591
+ }
32592
+ }
32593
+ const lock = approvalVerdict();
32594
+ if (lock.text.trim()) {
32595
+ logger.blank();
32596
+ if (lock.drifted) logger.warn(lock.text.trim());
32597
+ else logger.info(lock.text.trim());
32598
+ }
32599
+ logger.blank();
32600
+ }
32600
32601
 
32601
32602
  // src/commands/domains.ts
32602
32603
  var import_chalk11 = __toESM(require("chalk"));
@@ -32982,7 +32983,7 @@ init_fs();
32982
32983
  init_provision();
32983
32984
  init_skills();
32984
32985
  init_specs2();
32985
- init_lockfile();
32986
+ init_lock();
32986
32987
  init_extensions();
32987
32988
  init_mcp();
32988
32989
  function icon(mark) {
@@ -33038,7 +33039,7 @@ async function runDoctor(options = {}) {
33038
33039
  let targets = [];
33039
33040
  let configOk = false;
33040
33041
  try {
33041
- if (!loadProjectConfig4()) throw new ProjectNotInitializedError();
33042
+ if (!loadProjectConfig3()) throw new ProjectNotInitializedError();
33042
33043
  configOk = true;
33043
33044
  line(tally, "ok", ".wai/project.yaml is valid");
33044
33045
  } catch (e) {
@@ -33074,7 +33075,7 @@ async function runDoctor(options = {}) {
33074
33075
  if (hasSystemSpec && configOk) {
33075
33076
  console.log(import_chalk12.default.bold("Spec tree"));
33076
33077
  try {
33077
- const cfg = loadProjectConfig4();
33078
+ const cfg = loadProjectConfig3();
33078
33079
  if (!cfg) throw new ProjectNotInitializedError();
33079
33080
  const result = validateSddTree(cfg.rules, cfg.projectType);
33080
33081
  const errs = result.issues.filter((i) => i.severity === "error").length;
@@ -33256,7 +33257,7 @@ async function applyFixes() {
33256
33257
  if (!projectConfigExists2()) return;
33257
33258
  let targets;
33258
33259
  try {
33259
- if (!loadProjectConfig4()) throw new ProjectNotInitializedError();
33260
+ if (!loadProjectConfig3()) throw new ProjectNotInitializedError();
33260
33261
  targets = activeTargetTypes2();
33261
33262
  } catch (e) {
33262
33263
  logger.warn(`--fix skipped: .wai/project.yaml is invalid (${e instanceof Error ? e.message : String(e)}). Fix it first.`);
@@ -48418,7 +48419,7 @@ var TIER_COLOR = {
48418
48419
  };
48419
48420
  async function showExecution() {
48420
48421
  assertInitialized();
48421
- const config = loadProjectConfig4();
48422
+ const config = loadProjectConfig3();
48422
48423
  if (!config) throw new ProjectNotInitializedError();
48423
48424
  const tier = config.execution.tier;
48424
48425
  logger.header("Execution budgets");
@@ -48480,7 +48481,7 @@ async function setExecutionTier3(raw) {
48480
48481
  );
48481
48482
  }
48482
48483
  const tier = parsed.data;
48483
- const config = loadProjectConfig4();
48484
+ const config = loadProjectConfig3();
48484
48485
  if (!config) throw new ProjectNotInitializedError();
48485
48486
  const previous = config.execution.tier;
48486
48487
  if (previous === tier) {
@@ -48533,7 +48534,7 @@ async function runLock2(options) {
48533
48534
  logger.error("No SDD spec tree found (.wai/specs). Nothing to lock.");
48534
48535
  process.exit(1);
48535
48536
  }
48536
- const projectConfig = loadProjectConfig4();
48537
+ const projectConfig = loadProjectConfig3();
48537
48538
  if (!projectConfig) throw new ProjectNotInitializedError();
48538
48539
  logger.info("Analyzing and validating specifications in-memory...");
48539
48540
  const dry = validateAsComplete({