@wairon/cli 5.1.1-dev.54 → 5.1.1-dev.56

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/index.js CHANGED
@@ -1233,7 +1233,7 @@ var init_defaults = __esm({
1233
1233
  copilot: ".github/prompts",
1234
1234
  codex: ".codex/agents"
1235
1235
  };
1236
- WAIRON_VERSION = "5.1.1-dev.54";
1236
+ WAIRON_VERSION = "5.1.1-dev.56";
1237
1237
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1238
1238
  ARCHITECT_AGENT_ID = "agent-architect";
1239
1239
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -3053,204 +3053,97 @@ var init_models = __esm({
3053
3053
  }
3054
3054
  });
3055
3055
 
3056
- // src/core/templates.ts
3057
- function builtinTemplatesDir() {
3058
- return path6.resolve(__dirname, "..", "templates");
3059
- }
3060
- function loadTemplate(id, globalOverride) {
3061
- const dirs = templateSearchDirs(globalOverride);
3062
- for (const dir of dirs) {
3063
- const filePath = path6.join(dir, `${id}.yaml`);
3064
- if (pathExists(filePath)) {
3065
- const raw = readYamlFile(filePath);
3066
- return { ...TemplateSchema.parse(raw), templateName: id };
3067
- }
3068
- }
3069
- throw new TemplateNotFoundError(id);
3056
+ // src/config/paths.ts
3057
+ function aiPathsAt(rootDir) {
3058
+ const resolvedRoot = path6.resolve(rootDir);
3059
+ const aiDirAt2 = (...segments) => {
3060
+ const waiPath = path6.join(resolvedRoot, ".wai");
3061
+ const waironPath = path6.join(resolvedRoot, ".wairon");
3062
+ const base = !fs4.existsSync(waiPath) && fs4.existsSync(waironPath) ? waironPath : waiPath;
3063
+ return path6.join(base, ...segments);
3064
+ };
3065
+ const specsDir = () => projectConfigRepositoryAt(resolvedRoot).specsDir();
3066
+ return {
3067
+ root: () => aiDirAt2(),
3068
+ projectConfig: () => aiDirAt2("project.yaml"),
3069
+ topologyConfig: () => aiDirAt2("topology.yaml"),
3070
+ templatesDir: () => aiDirAt2("templates"),
3071
+ rulesDir: () => aiDirAt2("rules"),
3072
+ docsDir: () => aiDirAt2("docs"),
3073
+ generatedDir: () => aiDirAt2("generated"),
3074
+ contextDir: () => aiDirAt2("context"),
3075
+ contextProjectMd: () => aiDirAt2("context", "project.md"),
3076
+ contextArchitectureMd: () => aiDirAt2("context", "architecture.md"),
3077
+ contextDomainsMd: () => aiDirAt2("context", "domains.md"),
3078
+ contextWaironGuideMd: () => aiDirAt2("context", "wairon-guide.md"),
3079
+ specsDir,
3080
+ specsSystem: () => path6.join(specsDir(), ".index.yaml"),
3081
+ specsSubsystemsDir: () => path6.join(specsDir(), "subsystems"),
3082
+ specsComponentsDir: () => path6.join(specsDir(), "components"),
3083
+ specsInterfacesDir: () => path6.join(specsDir(), "interfaces"),
3084
+ specsImplementationsDir: () => path6.join(specsDir(), "implementations"),
3085
+ specsTypesDir: () => path6.join(specsDir(), "types")
3086
+ };
3070
3087
  }
3071
- function loadAgentOverride(agentId) {
3072
- const filePath = path6.join(AI_PATHS.root(), "agents", `${agentId}.md`);
3073
- return readFileOrNull(filePath);
3088
+ function isProjectInitialized() {
3089
+ return projectConfigRepository.exists();
3074
3090
  }
3075
- function listTemplateIds(globalOverride) {
3076
- const seen = /* @__PURE__ */ new Set();
3077
- for (const dir of templateSearchDirs(globalOverride)) {
3078
- for (const file of listFiles(dir, ".yaml")) {
3079
- seen.add(path6.basename(file, ".yaml"));
3080
- }
3091
+ function assertProjectInitialized() {
3092
+ if (!isProjectInitialized()) {
3093
+ throw new ProjectNotInitializedError();
3081
3094
  }
3082
- return Array.from(seen).sort();
3083
- }
3084
- function templateSearchDirs(globalOverride) {
3085
- return [
3086
- AI_PATHS.templatesDir(),
3087
- // 1. project-local
3088
- globalTemplatesDir(globalOverride),
3089
- // 2. global user/org
3090
- builtinTemplatesDir()
3091
- // 3. built-in fallback
3092
- ];
3093
- }
3094
- function renderTemplateInstructions(template, vars) {
3095
- return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
3096
- return key in vars ? vars[key] : `{{${key}}}`;
3097
- });
3098
- }
3099
- function parseTemplate(yamlContent) {
3100
- return TemplateSchema.parse(parseYaml(yamlContent));
3101
3095
  }
3102
- var path6;
3103
- var init_templates = __esm({
3104
- "src/core/templates.ts"() {
3096
+ var fs4, path6, AI_PATHS;
3097
+ var init_paths = __esm({
3098
+ "src/config/paths.ts"() {
3105
3099
  "use strict";
3100
+ fs4 = __toESM(require("fs"));
3106
3101
  path6 = __toESM(require("path"));
3107
3102
  init_fs();
3108
- init_yaml();
3109
3103
  init_errors();
3110
- init_template();
3111
- init_loader();
3112
- init_defaults();
3113
- }
3114
- });
3115
-
3116
- // src/core/execution_profile.ts
3117
- function isSweeping(p) {
3118
- return p === "**" || p === "**/*" || p.startsWith("**/");
3119
- }
3120
- function deriveBreadth(agent) {
3121
- if (agent.readPaths.some(isSweeping)) {
3122
- return MANAGER_TEMPLATES.has(agent.template) || READ_ONLY_TEMPLATES.has(agent.template) ? "wide" : "moderate";
3123
- }
3124
- const owned = agent.ownedPaths.length;
3125
- if (owned >= WIDE_PATH_COUNT) return "wide";
3126
- if (owned >= MODERATE_PATH_COUNT) return "moderate";
3127
- return "narrow";
3128
- }
3129
- function deriveReasoningDepth(agent) {
3130
- for (const tag of agent.tags) {
3131
- if (DEEP_STEREOTYPES.has(tag)) return "deep";
3132
- if (MECHANICAL_STEREOTYPES.has(tag)) return "mechanical";
3133
- }
3134
- if (MANAGER_TEMPLATES.has(agent.template)) return "deep";
3135
- if (agent.template === "reviewer") return "deep";
3136
- if (agent.template === "tester") return "standard";
3137
- return "standard";
3138
- }
3139
- function deriveWrites(agent) {
3140
- return !READ_ONLY_TEMPLATES.has(agent.template);
3141
- }
3142
- function deriveExecutionProfile(agent) {
3143
- const breadth = deriveBreadth(agent);
3144
- const reasoningDepth = deriveReasoningDepth(agent);
3145
- const writes = deriveWrites(agent);
3146
- const delegates = MANAGER_TEMPLATES.has(agent.template);
3147
- const because = [];
3148
- if (delegates) {
3149
- because.push(`${agent.template} routes work rather than performing it`);
3150
- }
3151
- const stereotype = agent.tags.find(
3152
- (t) => MECHANICAL_STEREOTYPES.has(t) || DEEP_STEREOTYPES.has(t)
3153
- );
3154
- if (stereotype) {
3155
- because.push(
3156
- MECHANICAL_STEREOTYPES.has(stereotype) ? `${stereotype} work is specified by its contract and narrative` : `${stereotype} carries decision logic`
3157
- );
3158
- }
3159
- because.push(
3160
- breadth === "wide" ? "reads broadly across the tree" : breadth === "moderate" ? `spans ${agent.ownedPaths.length} owned path(s)` : "scoped to a small owned set"
3161
- );
3162
- if (!writes) because.push("read-only");
3163
- return {
3164
- breadth,
3165
- writes,
3166
- reasoningDepth,
3167
- delegates,
3168
- rationale: because.join("; ")
3169
- };
3170
- }
3171
- var MECHANICAL_STEREOTYPES, DEEP_STEREOTYPES, MANAGER_TEMPLATES, READ_ONLY_TEMPLATES, WIDE_PATH_COUNT, MODERATE_PATH_COUNT;
3172
- var init_execution_profile = __esm({
3173
- "src/core/execution_profile.ts"() {
3174
- "use strict";
3175
- MECHANICAL_STEREOTYPES = /* @__PURE__ */ new Set(["store", "index", "registry", "adapter"]);
3176
- DEEP_STEREOTYPES = /* @__PURE__ */ new Set(["orchestrator", "supervisor", "specialist"]);
3177
- MANAGER_TEMPLATES = /* @__PURE__ */ new Set(["architect", "domain-owner"]);
3178
- READ_ONLY_TEMPLATES = /* @__PURE__ */ new Set(["reviewer", "guardian"]);
3179
- WIDE_PATH_COUNT = 12;
3180
- MODERATE_PATH_COUNT = 4;
3104
+ init_project_config();
3105
+ AI_PATHS = {
3106
+ root: () => aiPathsAt(getProjectRoot()).root(),
3107
+ projectConfig: () => aiPathsAt(getProjectRoot()).projectConfig(),
3108
+ topologyConfig: () => aiPathsAt(getProjectRoot()).topologyConfig(),
3109
+ templatesDir: () => aiPathsAt(getProjectRoot()).templatesDir(),
3110
+ rulesDir: () => aiPathsAt(getProjectRoot()).rulesDir(),
3111
+ docsDir: () => aiPathsAt(getProjectRoot()).docsDir(),
3112
+ generatedDir: () => aiPathsAt(getProjectRoot()).generatedDir(),
3113
+ contextDir: () => aiPathsAt(getProjectRoot()).contextDir(),
3114
+ contextProjectMd: () => aiPathsAt(getProjectRoot()).contextProjectMd(),
3115
+ contextArchitectureMd: () => aiPathsAt(getProjectRoot()).contextArchitectureMd(),
3116
+ contextDomainsMd: () => aiPathsAt(getProjectRoot()).contextDomainsMd(),
3117
+ contextWaironGuideMd: () => aiPathsAt(getProjectRoot()).contextWaironGuideMd(),
3118
+ specsDir: () => aiPathsAt(getProjectRoot()).specsDir(),
3119
+ specsSystem: () => aiPathsAt(getProjectRoot()).specsSystem(),
3120
+ specsSubsystemsDir: () => aiPathsAt(getProjectRoot()).specsSubsystemsDir(),
3121
+ specsComponentsDir: () => aiPathsAt(getProjectRoot()).specsComponentsDir(),
3122
+ specsInterfacesDir: () => aiPathsAt(getProjectRoot()).specsInterfacesDir(),
3123
+ specsImplementationsDir: () => aiPathsAt(getProjectRoot()).specsImplementationsDir(),
3124
+ specsTypesDir: () => aiPathsAt(getProjectRoot()).specsTypesDir()
3125
+ };
3181
3126
  }
3182
3127
  });
3183
3128
 
3184
- // src/core/budget_policy.ts
3185
- function atLeast(tier, floor) {
3186
- return TIER_ORDER.indexOf(tier) >= TIER_ORDER.indexOf(floor);
3187
- }
3188
- function baseModelTier(profile) {
3189
- switch (profile.reasoningDepth) {
3190
- case "mechanical":
3191
- return "small";
3192
- case "standard":
3193
- return "standard";
3194
- case "deep":
3195
- return "large";
3196
- }
3197
- }
3198
- function stepDown(tier, steps) {
3199
- const i = TIER_STEPS.indexOf(tier);
3200
- return TIER_STEPS[Math.max(0, i - steps)];
3201
- }
3202
- function maxTurnsFor(profile, tier) {
3203
- if (!atLeast(tier, "default")) return void 0;
3204
- const ceiling = profile.breadth === "wide" ? 60 : profile.breadth === "moderate" ? 40 : 25;
3205
- return atLeast(tier, "aggressive") ? Math.round(ceiling / 2) : ceiling;
3206
- }
3207
- function effortFor(profile, tier) {
3208
- if (!atLeast(tier, "trade")) return void 0;
3209
- if (profile.reasoningDepth === "mechanical") {
3210
- return atLeast(tier, "aggressive") ? "low" : "medium";
3211
- }
3212
- return void 0;
3213
- }
3214
- function toolClassFor(profile) {
3215
- return profile.writes ? "implement" : "read-only";
3216
- }
3217
- function mcpFor(profile, tier) {
3218
- if (!atLeast(tier, "free")) return "all";
3219
- if (profile.delegates) return "project";
3220
- return profile.breadth === "wide" ? "project" : "none";
3129
+ // src/config/loader.ts
3130
+ function loadTopologyConfig() {
3131
+ assertProjectInitialized();
3132
+ if (!pathExists(AI_PATHS.topologyConfig())) return createEmptyTopologyConfig();
3133
+ const raw = readYamlFile(AI_PATHS.topologyConfig());
3134
+ if (!raw) return createEmptyTopologyConfig();
3135
+ return TopologyConfigSchema.parse(raw);
3221
3136
  }
3222
- function resolveBudget(profile, config, agentId) {
3223
- const tier = config.tier;
3224
- if (tier === "off") return void 0;
3225
- let modelTier = baseModelTier(profile);
3226
- if (atLeast(tier, "trade") && profile.reasoningDepth === "standard") {
3227
- modelTier = stepDown(modelTier, 1);
3228
- }
3229
- if (atLeast(tier, "aggressive") && profile.reasoningDepth !== "deep") {
3230
- modelTier = "small";
3231
- }
3232
- const budget = {
3233
- // At `free` no model selection is expressed at all — that tier is defined
3234
- // as having no quality tradeoff, and choosing a model is a quality
3235
- // decision. Leaving it absent is not the same as choosing a default.
3236
- modelTier: atLeast(tier, "default") ? modelTier : void 0,
3237
- effort: effortFor(profile, tier),
3238
- maxTurns: maxTurnsFor(profile, tier),
3239
- toolClass: toolClassFor(profile),
3240
- // Only managers may spawn. Withholding the tool from workers is what stops
3241
- // a worker quietly becoming a second orchestrator three levels down.
3242
- allowNestedDelegation: profile.delegates,
3243
- mcp: mcpFor(profile, tier)
3244
- };
3245
- const override = config.overrides[agentId];
3246
- return override ? { ...budget, ...override } : budget;
3137
+ function saveTopologyConfig(config) {
3138
+ writeYamlFile(AI_PATHS.topologyConfig(), config);
3247
3139
  }
3248
- var TIER_ORDER, TIER_STEPS;
3249
- var init_budget_policy = __esm({
3250
- "src/core/budget_policy.ts"() {
3140
+ var init_loader = __esm({
3141
+ "src/config/loader.ts"() {
3251
3142
  "use strict";
3252
- TIER_ORDER = ["off", "free", "default", "trade", "aggressive"];
3253
- TIER_STEPS = ["small", "standard", "large", "frontier"];
3143
+ init_fs();
3144
+ init_yaml();
3145
+ init_models();
3146
+ init_paths();
3254
3147
  }
3255
3148
  });
3256
3149
 
@@ -3329,14 +3222,14 @@ function describeApprover(who) {
3329
3222
  }
3330
3223
  function readLockRecord() {
3331
3224
  try {
3332
- return normalizeRecord(JSON.parse(fs4.readFileSync(lockPath(), "utf8")));
3225
+ return normalizeRecord(JSON.parse(fs5.readFileSync(lockPath(), "utf8")));
3333
3226
  } catch {
3334
3227
  return null;
3335
3228
  }
3336
3229
  }
3337
3230
  function readLockRecordAt(root) {
3338
3231
  try {
3339
- return normalizeRecord(JSON.parse(fs4.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
3232
+ return normalizeRecord(JSON.parse(fs5.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
3340
3233
  } catch {
3341
3234
  return null;
3342
3235
  }
@@ -3347,10 +3240,10 @@ function normalizeRecord(raw) {
3347
3240
  }
3348
3241
  function writeLockRecord(record) {
3349
3242
  const p = lockPath();
3350
- fs4.mkdirSync(path7.dirname(p), { recursive: true });
3243
+ fs5.mkdirSync(path7.dirname(p), { recursive: true });
3351
3244
  const tmp = `${p}.tmp`;
3352
- fs4.writeFileSync(tmp, JSON.stringify(withSortedMaps(record), null, 2) + "\n");
3353
- fs4.renameSync(tmp, p);
3245
+ fs5.writeFileSync(tmp, JSON.stringify(withSortedMaps(record), null, 2) + "\n");
3246
+ fs5.renameSync(tmp, p);
3354
3247
  }
3355
3248
  function withSortedMaps(record) {
3356
3249
  const sorted = (m) => {
@@ -3367,11 +3260,11 @@ function withSortedMaps(record) {
3367
3260
  ...children ? { children } : {}
3368
3261
  };
3369
3262
  }
3370
- var fs4, path7;
3263
+ var fs5, path7;
3371
3264
  var init_lockfile = __esm({
3372
3265
  "src/core/lockfile.ts"() {
3373
3266
  "use strict";
3374
- fs4 = __toESM(require("fs"));
3267
+ fs5 = __toESM(require("fs"));
3375
3268
  path7 = __toESM(require("path"));
3376
3269
  init_fs();
3377
3270
  }
@@ -3388,24 +3281,24 @@ function listSpecFiles(specsDir) {
3388
3281
  return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
3389
3282
  }
3390
3283
  function removeSpecFile(filePath, specsRoot) {
3391
- if (!fs5.existsSync(filePath)) return false;
3392
- fs5.unlinkSync(filePath);
3284
+ if (!fs6.existsSync(filePath)) return false;
3285
+ fs6.unlinkSync(filePath);
3393
3286
  pruneEmptyDirs(path8.dirname(filePath), path8.resolve(specsRoot));
3394
3287
  return true;
3395
3288
  }
3396
3289
  function pruneEmptyDirs(dir, specsRoot) {
3397
3290
  let at = dir;
3398
3291
  while (at !== specsRoot && at.startsWith(specsRoot)) {
3399
- if (!fs5.existsSync(at) || fs5.readdirSync(at).length > 0) return;
3400
- fs5.rmdirSync(at);
3292
+ if (!fs6.existsSync(at) || fs6.readdirSync(at).length > 0) return;
3293
+ fs6.rmdirSync(at);
3401
3294
  at = path8.dirname(at);
3402
3295
  }
3403
3296
  }
3404
- var fs5, path8, SPEC_FILE_EXTENSION;
3297
+ var fs6, path8, SPEC_FILE_EXTENSION;
3405
3298
  var init_spec_files = __esm({
3406
3299
  "src/core/spec-files.ts"() {
3407
3300
  "use strict";
3408
- fs5 = __toESM(require("fs"));
3301
+ fs6 = __toESM(require("fs"));
3409
3302
  path8 = __toESM(require("path"));
3410
3303
  init_fs();
3411
3304
  init_yaml();
@@ -4157,8 +4050,8 @@ function loadCytoscapeLib() {
4157
4050
  // dist (library entry)
4158
4051
  ];
4159
4052
  for (const p of candidates) {
4160
- if (fs6.existsSync(p)) {
4161
- return fs6.readFileSync(p, "utf-8").replace(/<\/script/gi, "<\\/script");
4053
+ if (fs7.existsSync(p)) {
4054
+ return fs7.readFileSync(p, "utf-8").replace(/<\/script/gi, "<\\/script");
4162
4055
  }
4163
4056
  }
4164
4057
  throw new Error("Vendored cytoscape bundle not found (templates/canvas/cytoscape.min.js) \u2014 the wairon installation is incomplete.");
@@ -4170,11 +4063,11 @@ function renderCanvasHtml(model) {
4170
4063
  function escapeHtml(s) {
4171
4064
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4172
4065
  }
4173
- var fs6, path9, CANVAS_TEMPLATE;
4066
+ var fs7, path9, CANVAS_TEMPLATE;
4174
4067
  var init_canvas = __esm({
4175
4068
  "src/core/canvas.ts"() {
4176
4069
  "use strict";
4177
- fs6 = __toESM(require("fs"));
4070
+ fs7 = __toESM(require("fs"));
4178
4071
  path9 = __toESM(require("path"));
4179
4072
  init_specs2();
4180
4073
  init_models();
@@ -8370,7 +8263,7 @@ function packStoreDir() {
8370
8263
  function packFiles(dir, prefix = "") {
8371
8264
  let entries;
8372
8265
  try {
8373
- entries = fs7.readdirSync(dir, { withFileTypes: true });
8266
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
8374
8267
  } catch {
8375
8268
  return [];
8376
8269
  }
@@ -8388,7 +8281,7 @@ function computePackDigest(packDir) {
8388
8281
  const abs = path10.resolve(packDir);
8389
8282
  let isFile = false;
8390
8283
  try {
8391
- isFile = fs7.statSync(abs).isFile();
8284
+ isFile = fs8.statSync(abs).isFile();
8392
8285
  } catch {
8393
8286
  }
8394
8287
  const entries = isFile ? [[path10.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path10.join(abs, rel2)]);
@@ -8396,7 +8289,7 @@ function computePackDigest(packDir) {
8396
8289
  hash.update(rel2);
8397
8290
  hash.update("\0");
8398
8291
  try {
8399
- hash.update(fs7.readFileSync(full));
8292
+ hash.update(fs8.readFileSync(full));
8400
8293
  } catch {
8401
8294
  }
8402
8295
  hash.update("\0");
@@ -8435,7 +8328,7 @@ function installedFrom(location, name, version) {
8435
8328
  function versionedEntries(nameDir, fallbackName) {
8436
8329
  let versions;
8437
8330
  try {
8438
- versions = fs7.readdirSync(nameDir, { withFileTypes: true });
8331
+ versions = fs8.readdirSync(nameDir, { withFileTypes: true });
8439
8332
  } catch {
8440
8333
  return [];
8441
8334
  }
@@ -8465,7 +8358,7 @@ function listInstalledPacks() {
8465
8358
  const store = packStoreDir();
8466
8359
  let entries;
8467
8360
  try {
8468
- entries = fs7.readdirSync(store, { withFileTypes: true });
8361
+ entries = fs8.readdirSync(store, { withFileTypes: true });
8469
8362
  } catch {
8470
8363
  return [];
8471
8364
  }
@@ -8490,9 +8383,9 @@ function installPackFromDirectory(sourceDir, origin) {
8490
8383
  const manifest = readManifest(entryFile, path10.dirname(entryFile));
8491
8384
  const version = manifest.version && manifest.version.length > 0 ? manifest.version : UNVERSIONED;
8492
8385
  const dest = path10.join(packStoreDir(), manifest.name, version);
8493
- fs7.rmSync(dest, { recursive: true, force: true });
8494
- fs7.mkdirSync(path10.dirname(dest), { recursive: true });
8495
- fs7.cpSync(abs, dest, { recursive: true });
8386
+ fs8.rmSync(dest, { recursive: true, force: true });
8387
+ fs8.mkdirSync(path10.dirname(dest), { recursive: true });
8388
+ fs8.cpSync(abs, dest, { recursive: true });
8496
8389
  const digest2 = computePackDigest(dest);
8497
8390
  const record = {
8498
8391
  origin,
@@ -8515,22 +8408,22 @@ function uninstallPack(name, version) {
8515
8408
  const targets = listInstalledPacks().filter((p) => p.name === name && (!version || p.version === version));
8516
8409
  if (targets.length === 0) return false;
8517
8410
  for (const target of targets) {
8518
- fs7.rmSync(target.path, { recursive: true, force: true });
8411
+ fs8.rmSync(target.path, { recursive: true, force: true });
8519
8412
  const parent = path10.dirname(target.path);
8520
8413
  if (path10.resolve(parent) === path10.resolve(packStoreDir())) continue;
8521
8414
  try {
8522
- if (fs7.readdirSync(parent).length === 0) fs7.rmdirSync(parent);
8415
+ if (fs8.readdirSync(parent).length === 0) fs8.rmdirSync(parent);
8523
8416
  } catch {
8524
8417
  }
8525
8418
  }
8526
8419
  return true;
8527
8420
  }
8528
- var crypto2, fs7, path10, UNVERSIONED, INSTALL_RECORD, DIGEST_EXCLUDED, PACK_FILE_RE;
8421
+ var crypto2, fs8, path10, UNVERSIONED, INSTALL_RECORD, DIGEST_EXCLUDED, PACK_FILE_RE;
8529
8422
  var init_packstore = __esm({
8530
8423
  "src/core/packstore.ts"() {
8531
8424
  "use strict";
8532
8425
  crypto2 = __toESM(require("crypto"));
8533
- fs7 = __toESM(require("fs"));
8426
+ fs8 = __toESM(require("fs"));
8534
8427
  path10 = __toESM(require("path"));
8535
8428
  init_yaml();
8536
8429
  init_defaults();
@@ -8563,14 +8456,14 @@ function globalPacksDir() {
8563
8456
  function packDirEntry(dir) {
8564
8457
  for (const name of PACK_DIR_ENTRIES) {
8565
8458
  const p = path11.join(dir, name);
8566
- if (fs8.existsSync(p)) return p;
8459
+ if (fs9.existsSync(p)) return p;
8567
8460
  }
8568
8461
  return null;
8569
8462
  }
8570
8463
  function latestVersionDir(nameDir) {
8571
8464
  let versions;
8572
8465
  try {
8573
- versions = fs8.readdirSync(nameDir, { withFileTypes: true });
8466
+ versions = fs9.readdirSync(nameDir, { withFileTypes: true });
8574
8467
  } catch {
8575
8468
  return null;
8576
8469
  }
@@ -8586,7 +8479,7 @@ function latestVersionDir(nameDir) {
8586
8479
  function discoverPacks(dir) {
8587
8480
  let entries;
8588
8481
  try {
8589
- entries = fs8.readdirSync(dir, { withFileTypes: true });
8482
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
8590
8483
  } catch {
8591
8484
  return [];
8592
8485
  }
@@ -8632,7 +8525,7 @@ function resolvePackRef(ref, projectRoot) {
8632
8525
  const pathLike = ref.startsWith(".") || path11.isAbsolute(ref) || isYamlPath(ref);
8633
8526
  if (!pathLike) return ref;
8634
8527
  const resolved = path11.resolve(projectRoot, ref);
8635
- if (fs8.existsSync(resolved) && fs8.statSync(resolved).isDirectory()) {
8528
+ if (fs9.existsSync(resolved) && fs9.statSync(resolved).isDirectory()) {
8636
8529
  const entry = packDirEntry(resolved);
8637
8530
  if (!entry) throw new Error(`directory pack has no entry file (${PACK_DIR_ENTRIES.join(" | ")})`);
8638
8531
  return entry;
@@ -8858,7 +8751,7 @@ function findBundledPack(projectRoot, selection) {
8858
8751
  if (packDirEntry(base)) return base;
8859
8752
  let versions;
8860
8753
  try {
8861
- versions = fs8.readdirSync(base, { withFileTypes: true });
8754
+ versions = fs9.readdirSync(base, { withFileTypes: true });
8862
8755
  } catch {
8863
8756
  return null;
8864
8757
  }
@@ -8871,11 +8764,11 @@ function findBundledPack(projectRoot, selection) {
8871
8764
  }
8872
8765
  return best?.dir ?? null;
8873
8766
  }
8874
- var fs8, os2, path11, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
8767
+ var fs9, os2, path11, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
8875
8768
  var init_extensions = __esm({
8876
8769
  "src/core/extensions.ts"() {
8877
8770
  "use strict";
8878
- fs8 = __toESM(require("fs"));
8771
+ fs9 = __toESM(require("fs"));
8879
8772
  os2 = __toESM(require("os"));
8880
8773
  path11 = __toESM(require("path"));
8881
8774
  import_module = require("module");
@@ -16101,7 +15994,7 @@ function resolveRelativeModule(fromFile, specifier) {
16101
15994
  ];
16102
15995
  for (const c of candidates) {
16103
15996
  try {
16104
- if (fs9.statSync(c).isFile()) return c;
15997
+ if (fs10.statSync(c).isFile()) return c;
16105
15998
  } catch {
16106
15999
  }
16107
16000
  }
@@ -16113,7 +16006,7 @@ function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCach
16113
16006
  let targetFacts = exactCache.get(target);
16114
16007
  if (targetFacts === void 0) {
16115
16008
  try {
16116
- targetFacts = walkExact(ts, fs9.readFileSync(target, "utf8"), target);
16009
+ targetFacts = walkExact(ts, fs10.readFileSync(target, "utf8"), target);
16117
16010
  } catch {
16118
16011
  targetFacts = null;
16119
16012
  }
@@ -16170,7 +16063,7 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
16170
16063
  const descend = (dir) => {
16171
16064
  let entries;
16172
16065
  try {
16173
- entries = fs9.readdirSync(dir, { withFileTypes: true });
16066
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
16174
16067
  } catch {
16175
16068
  return;
16176
16069
  }
@@ -16193,7 +16086,7 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
16193
16086
  if (isExcluded(key)) continue;
16194
16087
  let stat;
16195
16088
  try {
16196
- stat = fs9.statSync(absolute);
16089
+ stat = fs10.statSync(absolute);
16197
16090
  } catch {
16198
16091
  continue;
16199
16092
  }
@@ -16230,12 +16123,12 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
16230
16123
  }
16231
16124
  let buffer;
16232
16125
  try {
16233
- const stat = fs9.statSync(absolute);
16126
+ const stat = fs10.statSync(absolute);
16234
16127
  if (!stat.isFile()) {
16235
16128
  files.push({ path: sourcePath, status: "missing", ...empty });
16236
16129
  continue;
16237
16130
  }
16238
- buffer = fs9.readFileSync(absolute);
16131
+ buffer = fs10.readFileSync(absolute);
16239
16132
  } catch {
16240
16133
  files.push({ path: sourcePath, status: "missing", ...empty });
16241
16134
  continue;
@@ -16289,7 +16182,7 @@ function readTestFiles(testRoots, projectRoot) {
16289
16182
  for (const key of walkDeclaredRoots(testRoots, [], projectRoot)) {
16290
16183
  let text;
16291
16184
  try {
16292
- const buffer = fs9.readFileSync(path12.resolve(projectRoot, key));
16185
+ const buffer = fs10.readFileSync(path12.resolve(projectRoot, key));
16293
16186
  if (looksBinary(buffer)) continue;
16294
16187
  text = buffer.toString("utf8");
16295
16188
  } catch {
@@ -16326,11 +16219,11 @@ function findTestsReferencing(methods, projectRoot, testRoots = []) {
16326
16219
  }
16327
16220
  return found;
16328
16221
  }
16329
- var fs9, path12, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16222
+ var fs10, path12, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16330
16223
  var init_source_analysis = __esm({
16331
16224
  "src/core/source-analysis.ts"() {
16332
16225
  "use strict";
16333
- fs9 = __toESM(require("fs"));
16226
+ fs10 = __toESM(require("fs"));
16334
16227
  path12 = __toESM(require("path"));
16335
16228
  import_module2 = require("module");
16336
16229
  init_models();
@@ -17107,7 +17000,7 @@ function builtinVariantsDir() {
17107
17000
  path13.resolve(__dirname, "templates", "variants")
17108
17001
  // dist (library entry)
17109
17002
  ];
17110
- return candidates.find((dir) => fs10.existsSync(dir)) ?? candidates[0];
17003
+ return candidates.find((dir) => fs11.existsSync(dir)) ?? candidates[0];
17111
17004
  }
17112
17005
  function projectVariantsDir() {
17113
17006
  return path13.join(getProjectRoot(), ".wai", "variants");
@@ -17115,7 +17008,7 @@ function projectVariantsDir() {
17115
17008
  function variantFiles(dir) {
17116
17009
  let entries;
17117
17010
  try {
17118
- entries = fs10.readdirSync(dir, { withFileTypes: true });
17011
+ entries = fs11.readdirSync(dir, { withFileTypes: true });
17119
17012
  } catch {
17120
17013
  return [];
17121
17014
  }
@@ -17228,11 +17121,11 @@ function rebasedText(content, fromBase, toBase, rebasedContent) {
17228
17121
  }
17229
17122
  return serializeYaml(rebasedContent);
17230
17123
  }
17231
- var fs10, os3, path13, import_zod10, VariantDefSchema;
17124
+ var fs11, os3, path13, import_zod10, VariantDefSchema;
17232
17125
  var init_variants = __esm({
17233
17126
  "src/core/variants.ts"() {
17234
17127
  "use strict";
17235
- fs10 = __toESM(require("fs"));
17128
+ fs11 = __toESM(require("fs"));
17236
17129
  os3 = __toESM(require("os"));
17237
17130
  path13 = __toESM(require("path"));
17238
17131
  import_zod10 = require("zod");
@@ -17782,9 +17675,9 @@ function projectSubsystemSurface(subsystemId) {
17782
17675
  }
17783
17676
  function listSnapshots(rootDir = getProjectRoot()) {
17784
17677
  const dir = surfacesDir(rootDir);
17785
- if (!fs11.existsSync(dir)) return [];
17678
+ if (!fs12.existsSync(dir)) return [];
17786
17679
  const out = [];
17787
- for (const file of fs11.readdirSync(dir)) {
17680
+ for (const file of fs12.readdirSync(dir)) {
17788
17681
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
17789
17682
  try {
17790
17683
  out.push(SurfaceSnapshotSchema.parse(readYamlFile(path14.join(dir, file))));
@@ -17801,10 +17694,10 @@ function snapshotFilename(projectName) {
17801
17694
  }
17802
17695
  function writeSnapshotIfChanged(snapshot, rootDir) {
17803
17696
  const dir = surfacesDir(rootDir);
17804
- fs11.mkdirSync(dir, { recursive: true });
17697
+ fs12.mkdirSync(dir, { recursive: true });
17805
17698
  const p = path14.join(dir, snapshotFilename(snapshot.projectName));
17806
17699
  const next = SurfaceSnapshotSchema.parse(snapshot);
17807
- if (fs11.existsSync(p)) {
17700
+ if (fs12.existsSync(p)) {
17808
17701
  try {
17809
17702
  const existing = SurfaceSnapshotSchema.parse(readYamlFile(p));
17810
17703
  if (surfaceContentKey(existing) === surfaceContentKey(next)) {
@@ -17822,18 +17715,18 @@ function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
17822
17715
  function removeSnapshot(projectName, rootDir = getProjectRoot()) {
17823
17716
  const dir = surfacesDir(rootDir);
17824
17717
  const direct = path14.join(dir, snapshotFilename(projectName));
17825
- if (fs11.existsSync(direct)) {
17826
- fs11.unlinkSync(direct);
17718
+ if (fs12.existsSync(direct)) {
17719
+ fs12.unlinkSync(direct);
17827
17720
  return true;
17828
17721
  }
17829
- if (!fs11.existsSync(dir)) return false;
17830
- for (const file of fs11.readdirSync(dir)) {
17722
+ if (!fs12.existsSync(dir)) return false;
17723
+ for (const file of fs12.readdirSync(dir)) {
17831
17724
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
17832
17725
  const p = path14.join(dir, file);
17833
17726
  try {
17834
17727
  const snap = SurfaceSnapshotSchema.parse(readYamlFile(p));
17835
17728
  if (snap.projectName === projectName) {
17836
- fs11.unlinkSync(p);
17729
+ fs12.unlinkSync(p);
17837
17730
  return true;
17838
17731
  }
17839
17732
  } catch {
@@ -17873,18 +17766,18 @@ function perPortalPath(resolvedOut, portalId) {
17873
17766
  }
17874
17767
  function writeSurfaceFile(outPath, snapshot, renderedSet) {
17875
17768
  const resolved = path14.resolve(outPath);
17876
- fs11.mkdirSync(path14.dirname(resolved), { recursive: true });
17769
+ fs12.mkdirSync(path14.dirname(resolved), { recursive: true });
17877
17770
  if (!renderedSet || renderedSet.length === 0) {
17878
17771
  writeYamlFile(resolved, snapshot);
17879
17772
  return [resolved];
17880
17773
  }
17881
17774
  if (renderedSet.length === 1) {
17882
- fs11.writeFileSync(resolved, renderedSet[0].document);
17775
+ fs12.writeFileSync(resolved, renderedSet[0].document);
17883
17776
  return [resolved];
17884
17777
  }
17885
17778
  return renderedSet.map((spec) => {
17886
17779
  const target = perPortalPath(resolved, spec.portalId);
17887
- fs11.writeFileSync(target, spec.document);
17780
+ fs12.writeFileSync(target, spec.document);
17888
17781
  return target;
17889
17782
  });
17890
17783
  }
@@ -17907,10 +17800,10 @@ function exportSurface(maxAudience, format, outPath, portalId) {
17907
17800
  }
17908
17801
  function importSurface(sourcePath, origin) {
17909
17802
  const resolved = path14.resolve(sourcePath);
17910
- if (!fs11.existsSync(resolved)) {
17803
+ if (!fs12.existsSync(resolved)) {
17911
17804
  throw new Error(`Surface document not found: ${resolved}`);
17912
17805
  }
17913
- const body = fs11.readFileSync(resolved, "utf8");
17806
+ const body = fs12.readFileSync(resolved, "utf8");
17914
17807
  let snapshot;
17915
17808
  if (isOpenApiDocument(body)) {
17916
17809
  const projectName = path14.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
@@ -17976,11 +17869,11 @@ function surfaceContentKey(snapshot) {
17976
17869
  const { stateId, generatedAt, origin, ...content } = snapshot;
17977
17870
  return JSON.stringify(content);
17978
17871
  }
17979
- var fs11, path14, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
17872
+ var fs12, path14, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
17980
17873
  var init_surfaces = __esm({
17981
17874
  "src/core/surfaces.ts"() {
17982
17875
  "use strict";
17983
- fs11 = __toESM(require("fs"));
17876
+ fs12 = __toESM(require("fs"));
17984
17877
  path14 = __toESM(require("path"));
17985
17878
  init_fs();
17986
17879
  init_yaml();
@@ -19048,7 +18941,7 @@ function canonicalPath(target) {
19048
18941
  const rest = [];
19049
18942
  for (; ; ) {
19050
18943
  try {
19051
- fs12.lstatSync(existing);
18944
+ fs13.lstatSync(existing);
19052
18945
  break;
19053
18946
  } catch {
19054
18947
  const up = path16.dirname(existing);
@@ -19058,7 +18951,7 @@ function canonicalPath(target) {
19058
18951
  }
19059
18952
  }
19060
18953
  try {
19061
- return path16.join(fs12.realpathSync.native(existing), ...rest);
18954
+ return path16.join(fs13.realpathSync.native(existing), ...rest);
19062
18955
  } catch {
19063
18956
  return null;
19064
18957
  }
@@ -19150,7 +19043,7 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
19150
19043
  let reason;
19151
19044
  if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
19152
19045
  else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
19153
- else if (!fs12.existsSync(childDir)) reason = "missing";
19046
+ else if (!fs13.existsSync(childDir)) reason = "missing";
19154
19047
  else if (depth >= 32) reason = "too-deep";
19155
19048
  if (reason) {
19156
19049
  const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
@@ -19284,9 +19177,9 @@ function findOwner(id, components) {
19284
19177
  }
19285
19178
  function moveComponentFolder(fromDir, toDir) {
19286
19179
  if (path16.normalize(fromDir) === path16.normalize(toDir)) return false;
19287
- if (!fs12.existsSync(fromDir) || fs12.existsSync(toDir)) return false;
19180
+ if (!fs13.existsSync(fromDir) || fs13.existsSync(toDir)) return false;
19288
19181
  ensureDir(path16.dirname(toDir));
19289
- fs12.renameSync(fromDir, toDir);
19182
+ fs13.renameSync(fromDir, toDir);
19290
19183
  return true;
19291
19184
  }
19292
19185
  function formatZodIssues(error) {
@@ -19302,13 +19195,13 @@ function parseOrThrow(schema, value, kind, id) {
19302
19195
  function computeSpecTreeSignature(dirs) {
19303
19196
  const parts = [];
19304
19197
  for (const dir of dirs) {
19305
- if (!fs12.existsSync(dir)) {
19198
+ if (!fs13.existsSync(dir)) {
19306
19199
  parts.push(`${dir}:missing`);
19307
19200
  continue;
19308
19201
  }
19309
19202
  for (const f of listSpecFiles(dir)) {
19310
19203
  try {
19311
- const st = fs12.statSync(f);
19204
+ const st = fs13.statSync(f);
19312
19205
  parts.push(`${f}:${st.mtimeMs}:${st.size}`);
19313
19206
  } catch {
19314
19207
  parts.push(`${f}:gone`);
@@ -19834,7 +19727,7 @@ function consumedSurfaceInputsAt(rootDir) {
19834
19727
  const dir = path16.join(rootDir, ".wai", "surfaces");
19835
19728
  if (!pathExists(dir)) return [];
19836
19729
  const keys = [];
19837
- for (const file of fs12.readdirSync(dir)) {
19730
+ for (const file of fs13.readdirSync(dir)) {
19838
19731
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
19839
19732
  try {
19840
19733
  keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path16.join(dir, file)))));
@@ -19919,7 +19812,7 @@ function snapshotSpecFiles() {
19919
19812
  }
19920
19813
  function restoreSpecFiles(snapshot) {
19921
19814
  for (const [file, content] of snapshot) {
19922
- fs12.writeFileSync(file, content);
19815
+ fs13.writeFileSync(file, content);
19923
19816
  }
19924
19817
  }
19925
19818
  function findLegacySpecFiles() {
@@ -19931,13 +19824,13 @@ function updateSpec(kind, id, delta, hooks, dryRun) {
19931
19824
  function moveMethods(from, to, methods, hooks, dryRun) {
19932
19825
  return current().moveMethods(from, to, methods, hooks, dryRun);
19933
19826
  }
19934
- var fs12, path16, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, COMPONENT_AUTH_SOURCE, DELTA_VERBS, JUMP_LABEL_TWINS, LABEL_TWIN_CONTAINERS, SpecWorkspace, DEFAULT_DEPENDENCY_LIMIT, PROSE_FIELDS, workspaces;
19827
+ var fs13, path16, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, COMPONENT_AUTH_SOURCE, DELTA_VERBS, JUMP_LABEL_TWINS, LABEL_TWIN_CONTAINERS, SpecWorkspace, DEFAULT_DEPENDENCY_LIMIT, PROSE_FIELDS, workspaces;
19935
19828
  var init_specs2 = __esm({
19936
19829
  "src/core/specs.ts"() {
19937
19830
  "use strict";
19938
- fs12 = __toESM(require("fs"));
19831
+ fs13 = __toESM(require("fs"));
19939
19832
  path16 = __toESM(require("path"));
19940
- init_loader();
19833
+ init_paths();
19941
19834
  init_project_config();
19942
19835
  init_fs();
19943
19836
  init_statehash();
@@ -20218,7 +20111,7 @@ var init_specs2 = __esm({
20218
20111
  });
20219
20112
  continue;
20220
20113
  }
20221
- if (!fs12.existsSync(childDir)) {
20114
+ if (!fs13.existsSync(childDir)) {
20222
20115
  this.loaderIssues.push({
20223
20116
  severity: "error",
20224
20117
  code: "SUBPROJECT_NOT_FOUND",
@@ -21152,8 +21045,8 @@ var init_specs2 = __esm({
21152
21045
  }
21153
21046
  }
21154
21047
  for (const file of files) {
21155
- if (fs12.existsSync(file)) {
21156
- snapshot.set(file, fs12.readFileSync(file, "utf8"));
21048
+ if (fs13.existsSync(file)) {
21049
+ snapshot.set(file, fs13.readFileSync(file, "utf8"));
21157
21050
  }
21158
21051
  }
21159
21052
  return snapshot;
@@ -22183,567 +22076,135 @@ var init_specs2 = __esm({
22183
22076
  }
22184
22077
  });
22185
22078
 
22186
- // src/core/agent_resolver.ts
22187
- var agent_resolver_exports = {};
22188
- __export(agent_resolver_exports, {
22189
- UnknownAgentError: () => UnknownAgentError,
22190
- composeAgentBrief: () => composeAgentBrief,
22191
- resolveAgentTopology: () => resolveAgentTopology
22079
+ // src/core/domains.ts
22080
+ var domains_exports = {};
22081
+ __export(domains_exports, {
22082
+ addFreeStandingDomain: () => addFreeStandingDomain,
22083
+ deriveSubsystemDomains: () => deriveSubsystemDomains,
22084
+ findDomain: () => findDomain,
22085
+ listFreeStandingDomains: () => listFreeStandingDomains,
22086
+ removeFreeStandingDomain: () => removeFreeStandingDomain,
22087
+ resolveDomains: () => resolveDomains
22192
22088
  });
22193
- function listFilesRecursiveSafe(dirPath, ext) {
22194
- if (!fs13.existsSync(dirPath)) return [];
22195
- const nameLower = path17.basename(dirPath).toLowerCase();
22196
- const IGNORED_DIRS = /* @__PURE__ */ new Set([
22197
- "node_modules",
22198
- "target",
22199
- "dist",
22200
- "build",
22201
- ".git",
22202
- ".wai",
22203
- ".claude",
22204
- ".gemini",
22205
- ".codex",
22206
- ".agents",
22207
- ".vscode"
22208
- ]);
22209
- if (IGNORED_DIRS.has(nameLower)) return [];
22210
- const entries = fs13.readdirSync(dirPath, { withFileTypes: true });
22211
- const files = [];
22212
- for (const entry of entries) {
22213
- const fullPath = path17.join(dirPath, entry.name);
22214
- if (entry.isDirectory()) {
22215
- files.push(...listFilesRecursiveSafe(fullPath, ext));
22216
- } else if (entry.isFile() && entry.name.endsWith(ext)) {
22217
- files.push(fullPath);
22218
- }
22089
+ function rel(p) {
22090
+ return path17.relative(process.cwd(), p).replace(/\\/g, "/");
22091
+ }
22092
+ function deriveSubsystemDomains() {
22093
+ const subsystems = loadSubsystemSpecs();
22094
+ const components = loadComponentSpecs();
22095
+ return subsystems.map((sub) => {
22096
+ const ownedPaths = [
22097
+ rel(getSubsystemPath(sub.id)),
22098
+ ...components.filter((c) => c.subsystem === sub.id).map((c) => rel(getComponentPath(c.id, sub.id)))
22099
+ ];
22100
+ return {
22101
+ id: sub.id,
22102
+ name: sub.name,
22103
+ description: sub.description,
22104
+ boundTo: sub.id,
22105
+ ownedPaths
22106
+ };
22107
+ });
22108
+ }
22109
+ function listFreeStandingDomains() {
22110
+ return loadTopologyConfig().domains;
22111
+ }
22112
+ function resolveDomains() {
22113
+ return [...deriveSubsystemDomains(), ...listFreeStandingDomains()];
22114
+ }
22115
+ function findDomain(id) {
22116
+ return resolveDomains().find((d) => d.id === id);
22117
+ }
22118
+ function addFreeStandingDomain(domain) {
22119
+ const config = loadTopologyConfig();
22120
+ if (config.domains.some((d) => d.id === domain.id)) {
22121
+ throw new WaironError(`A free-standing domain "${domain.id}" already exists in .wai/topology.yaml.`);
22219
22122
  }
22220
- return files;
22123
+ if (deriveSubsystemDomains().some((d) => d.id === domain.id)) {
22124
+ throw new WaironError(`Domain id "${domain.id}" collides with a subsystem-derived domain.`);
22125
+ }
22126
+ config.domains.push(domain);
22127
+ saveTopologyConfig(config);
22221
22128
  }
22222
- function getProjectFiles(projectDir) {
22223
- let files = projectFilesCache.get(projectDir);
22224
- if (!files) {
22225
- files = [];
22226
- const srcDir = path17.join(projectDir, "src");
22227
- const legacySrcDir = path17.join(projectDir, "legacy-src");
22228
- let searchDir = projectDir;
22229
- if (pathExists(srcDir)) {
22230
- searchDir = srcDir;
22231
- } else if (pathExists(legacySrcDir)) {
22232
- searchDir = legacySrcDir;
22233
- } else if (projectDir === getProjectRoot()) {
22234
- projectFilesCache.set(projectDir, []);
22235
- return [];
22236
- }
22237
- const extensions = [".rs", ".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".c", ".cpp", ".cs", ".java", ".kt", ".swift", ".rb", ".php", ".lua"];
22238
- for (const ext of extensions) {
22239
- files.push(...listFilesRecursiveSafe(searchDir, ext));
22240
- }
22241
- const rootDir = getProjectRoot();
22242
- files = files.map((f) => path17.relative(rootDir, f).replace(/\\/g, "/"));
22243
- projectFilesCache.set(projectDir, files);
22129
+ function removeFreeStandingDomain(id) {
22130
+ const config = loadTopologyConfig();
22131
+ const idx = config.domains.findIndex((d) => d.id === id);
22132
+ if (idx === -1) {
22133
+ throw new WaironError(
22134
+ `"${id}" is not a free-standing domain. Subsystem-backed domains are derived from the spec tree and cannot be removed here.`
22135
+ );
22244
22136
  }
22245
- return files;
22137
+ config.domains.splice(idx, 1);
22138
+ saveTopologyConfig(config);
22246
22139
  }
22247
- function inferSourcePathForComponent(comp, subsystems) {
22248
- try {
22249
- const projectDir = resolveSubprojectForNamespace(comp.subsystem) || getProjectRoot();
22250
- const files = getProjectFiles(projectDir);
22251
- if (files.length === 0) return null;
22252
- const compRelativeId = comp.id.split("::").pop() || comp.id;
22253
- const subRelativeId = comp.subsystem.split("::").pop() || comp.subsystem;
22254
- let cleanName = compRelativeId;
22255
- if (cleanName.startsWith(`${subRelativeId}-`)) {
22256
- cleanName = cleanName.slice(subRelativeId.length + 1);
22140
+ var path17;
22141
+ var init_domains = __esm({
22142
+ "src/core/domains.ts"() {
22143
+ "use strict";
22144
+ path17 = __toESM(require("path"));
22145
+ init_loader();
22146
+ init_specs2();
22147
+ init_errors();
22148
+ }
22149
+ });
22150
+
22151
+ // src/core/templates.ts
22152
+ function builtinTemplatesDir() {
22153
+ return path18.resolve(__dirname, "..", "templates");
22154
+ }
22155
+ function loadTemplate(id, globalOverride) {
22156
+ const dirs = templateSearchDirs(globalOverride);
22157
+ for (const dir of dirs) {
22158
+ const filePath = path18.join(dir, `${id}.yaml`);
22159
+ if (pathExists(filePath)) {
22160
+ const raw = readYamlFile(filePath);
22161
+ return { ...TemplateSchema.parse(raw), templateName: id };
22257
22162
  }
22258
- const candidates = /* @__PURE__ */ new Set();
22259
- candidates.add(cleanName.toLowerCase());
22260
- candidates.add(cleanName.replace(/-/g, "_").toLowerCase());
22261
- candidates.add(compRelativeId.toLowerCase());
22262
- candidates.add(compRelativeId.replace(/-/g, "_").toLowerCase());
22263
- candidates.add(comp.componentType.toLowerCase());
22264
- let bestFile = null;
22265
- let bestScore = -1;
22266
- for (const f of files) {
22267
- const ext = path17.extname(f);
22268
- const base = path17.basename(f, ext).toLowerCase();
22269
- if (candidates.has(base)) {
22270
- let score = 0;
22271
- const normalizedPath = f.toLowerCase();
22272
- const subPattern1 = `/${subRelativeId.toLowerCase()}/`;
22273
- const subPattern2 = `/${subRelativeId.replace(/-/g, "_").toLowerCase()}/`;
22274
- const hasSubsystemSegmentMatch = subRelativeId.split(/[-_]/).some((seg) => seg.length >= 3 && normalizedPath.includes(`/${seg.toLowerCase()}/`));
22275
- const hasSubsystemMatch = normalizedPath.includes(subPattern1) || normalizedPath.includes(subPattern2) || hasSubsystemSegmentMatch;
22276
- if (hasSubsystemMatch) {
22277
- score += 10;
22278
- }
22279
- let belongsToOtherSubsystem = false;
22280
- for (const otherSub of subsystems) {
22281
- if (otherSub.id === comp.subsystem) continue;
22282
- const otherSubRelativeId = otherSub.id.split("::").pop() || otherSub.id;
22283
- const otherPattern1 = `/${otherSubRelativeId.toLowerCase()}/`;
22284
- const otherPattern2 = `/${otherSubRelativeId.replace(/-/g, "_").toLowerCase()}/`;
22285
- const otherSegmentMatch = otherSubRelativeId.split(/[-_]/).some((seg) => seg.length >= 3 && normalizedPath.includes(`/${seg.toLowerCase()}/`));
22286
- if (normalizedPath.includes(otherPattern1) || normalizedPath.includes(otherPattern2) || otherSegmentMatch) {
22287
- belongsToOtherSubsystem = true;
22288
- break;
22289
- }
22290
- }
22291
- if (belongsToOtherSubsystem) {
22292
- continue;
22293
- }
22294
- const isCleanNameMatch = base === cleanName.toLowerCase() || base === cleanName.replace(/-/g, "_").toLowerCase();
22295
- if (isCleanNameMatch) {
22296
- score += 5;
22297
- }
22298
- const isIdMatch = base === compRelativeId.toLowerCase() || base === compRelativeId.replace(/-/g, "_").toLowerCase();
22299
- if (isIdMatch) {
22300
- score += 3;
22301
- }
22302
- const isSpecificNameMatch = isCleanNameMatch && cleanName.toLowerCase() !== comp.componentType.toLowerCase();
22303
- if (!hasSubsystemMatch && !isSpecificNameMatch && !isIdMatch) {
22304
- continue;
22305
- }
22306
- if (normalizedPath.startsWith("src/") || normalizedPath.includes("/src/") || normalizedPath.startsWith("legacy-src/") || normalizedPath.includes("/legacy-src/")) {
22307
- score += 2;
22308
- }
22309
- if (base === comp.componentType.toLowerCase()) {
22310
- score += 1;
22311
- }
22312
- if (score > bestScore) {
22313
- bestScore = score;
22314
- bestFile = f;
22315
- }
22316
- }
22163
+ }
22164
+ throw new TemplateNotFoundError(id);
22165
+ }
22166
+ function loadAgentOverride(agentId) {
22167
+ const filePath = path18.join(AI_PATHS.root(), "agents", `${agentId}.md`);
22168
+ return readFileOrNull(filePath);
22169
+ }
22170
+ function listTemplateIds(globalOverride) {
22171
+ const seen = /* @__PURE__ */ new Set();
22172
+ for (const dir of templateSearchDirs(globalOverride)) {
22173
+ for (const file of listFiles(dir, ".yaml")) {
22174
+ seen.add(path18.basename(file, ".yaml"));
22317
22175
  }
22318
- return bestFile;
22319
- } catch {
22320
- return null;
22321
22176
  }
22177
+ return Array.from(seen).sort();
22322
22178
  }
22323
- function summarize(text, max = 140) {
22324
- const clean = text.replace(/\s+/g, " ").trim();
22325
- const period = clean.indexOf(". ");
22326
- const firstSentence = period > 0 ? clean.slice(0, period + 1) : clean;
22327
- if (firstSentence.length <= max) return firstSentence;
22328
- return `${firstSentence.slice(0, max - 1).trimEnd()}\u2026`;
22179
+ function templateSearchDirs(globalOverride) {
22180
+ return [
22181
+ AI_PATHS.templatesDir(),
22182
+ // 1. project-local
22183
+ globalTemplatesDir(globalOverride),
22184
+ // 2. global user/org
22185
+ builtinTemplatesDir()
22186
+ // 3. built-in fallback
22187
+ ];
22329
22188
  }
22330
- function buildVariantGuidance(comps, allComponents, variantsById) {
22331
- return composeVariantGuidance(comps, allComponents, variantsById);
22189
+ function renderTemplateInstructions(template, vars) {
22190
+ return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
22191
+ return key in vars ? vars[key] : `{{${key}}}`;
22192
+ });
22332
22193
  }
22333
- function resolveAgentTopology() {
22334
- projectFilesCache.clear();
22335
- const system = loadSystemSpec();
22336
- if (!system) return [];
22337
- const isLocal = (id) => !id.includes("::");
22338
- const subsystems = loadSubsystemSpecs().filter((s) => isLocal(s.id));
22339
- const components = loadComponentSpecs().filter((c) => isLocal(c.id) && isLocal(c.subsystem));
22340
- const interfaces = loadInterfaceSpecs();
22341
- const implementations = loadImplementationSpecs();
22342
- const variantsById = new Map(loadProjectVariants().map((v) => [v.id, v]));
22343
- const config = projectConfigRepository.load();
22344
- if (!config) throw new ProjectNotInitializedError();
22345
- const activeTargets = config.targets.filter((t) => !("enabled" in t) || t.enabled).map((t) => typeof t === "string" ? t : t.type);
22346
- const agents = [];
22347
- agents.push({
22348
- id: "system-architect",
22349
- name: `${system.name} Architect`,
22350
- description: `Global architect for ${system.name} \u2014 owns the spec tree and topology. ${summarize(system.vision)}`,
22351
- template: "architect",
22352
- creationReason: "Automatically inferred from L0 system spec",
22353
- ownedPaths: [".wai/specs/**"],
22354
- readPaths: ["**"],
22355
- writePaths: [".wai/specs/**"],
22356
- tags: ["architect", "global", "sdd"],
22357
- dependencies: subsystems.map((s) => `${s.id}-owner`),
22358
- status: "active",
22359
- targets: activeTargets,
22360
- createdAt: system.createdAt,
22361
- updatedAt: system.updatedAt
22362
- });
22363
- for (const sub of subsystems) {
22364
- if (sub.projectPath) {
22365
- const mountSpecPath = path17.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
22366
- agents.push({
22367
- id: `${sub.id}-owner`,
22368
- name: `${sub.name} (chained subproject)`,
22369
- description: `Delegates into the "${sub.id}" chained subproject at ${sub.projectPath}. Its own agents live in that subproject's .wai \u2014 run \`wairon generate\` there (or spawn from ${sub.projectPath}/.claude/agents). Do not implement its internals from this layer.`,
22370
- template: "domain-owner",
22371
- creationReason: `Automatically inferred from a chained subproject subsystem: ${sub.id}`,
22372
- domainRoot: sub.id,
22373
- ownedPaths: [mountSpecPath],
22374
- readPaths: ["**"],
22375
- writePaths: [mountSpecPath],
22376
- tags: ["owner", "subproject", "delegate", "sdd"],
22377
- dependencies: [],
22378
- status: "active",
22379
- targets: activeTargets,
22380
- createdAt: sub.createdAt,
22381
- updatedAt: sub.updatedAt
22382
- });
22383
- continue;
22384
- }
22385
- const subComponents = components.filter((c) => c.subsystem === sub.id);
22386
- const ownedPaths = [];
22387
- ownedPaths.push(path17.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
22388
- for (const c of subComponents) {
22389
- ownedPaths.push(path17.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
22390
- }
22391
- if (!config.rules.generateComponentImplementers) {
22392
- for (const comp of subComponents) {
22393
- const compInterfaces = interfaces.filter((i) => i.component === comp.id);
22394
- const compInterfaceIds = compInterfaces.map((i) => i.id);
22395
- const compImpls = implementations.filter((impl) => compInterfaceIds.includes(impl.contract));
22396
- let hasExplicitSource = false;
22397
- for (const impl of compImpls) {
22398
- const files = implementationSourceFiles(impl);
22399
- if (files.length > 0) hasExplicitSource = true;
22400
- for (const file of files) {
22401
- if (!ownedPaths.includes(file)) ownedPaths.push(file);
22402
- }
22403
- }
22404
- if (!hasExplicitSource) {
22405
- const inferred = inferSourcePathForComponent(comp, subsystems);
22406
- if (inferred && !ownedPaths.includes(inferred)) {
22407
- ownedPaths.push(inferred);
22408
- }
22409
- }
22410
- }
22411
- }
22412
- let dependencies = [];
22413
- if (config.rules.generateComponentImplementers) {
22414
- dependencies = subComponents.map((c) => `${c.id}-implementer`);
22415
- } else {
22416
- const depSubsystems = /* @__PURE__ */ new Set();
22417
- for (const c of subComponents) {
22418
- for (const depId of c.dependsOn) {
22419
- const depComp = components.find((other) => other.id === depId);
22420
- if (depComp && depComp.subsystem !== sub.id) {
22421
- depSubsystems.add(`${depComp.subsystem}-owner`);
22422
- }
22423
- }
22424
- }
22425
- dependencies = Array.from(depSubsystems);
22426
- }
22427
- agents.push({
22428
- id: `${sub.id}-owner`,
22429
- name: `${sub.name} Owner`,
22430
- description: `Owns the ${sub.id} subsystem. ${summarize(sub.description)}`,
22431
- template: "domain-owner",
22432
- creationReason: `Automatically inferred from L1 subsystem spec: ${sub.id}`,
22433
- domainRoot: sub.id,
22434
- ownedPaths,
22435
- readPaths: ["**"],
22436
- writePaths: ownedPaths,
22437
- tags: ["owner", "domain", "sdd"],
22438
- dependencies,
22439
- variantGuidance: buildVariantGuidance(subComponents, components, variantsById),
22440
- status: "active",
22441
- targets: activeTargets,
22442
- createdAt: sub.createdAt,
22443
- updatedAt: sub.updatedAt
22444
- });
22445
- }
22446
- if (config.rules.generateComponentImplementers) {
22447
- for (const comp of components) {
22448
- const compInterfaces = interfaces.filter((i) => i.component === comp.id);
22449
- const compInterfaceIds = compInterfaces.map((i) => i.id);
22450
- const compImpls = implementations.filter((impl) => compInterfaceIds.includes(impl.contract));
22451
- const ownedPaths = [];
22452
- for (const impl of compImpls) {
22453
- for (const file of implementationSourceFiles(impl)) {
22454
- if (!ownedPaths.includes(file)) ownedPaths.push(file);
22455
- }
22456
- }
22457
- if (ownedPaths.length === 0) {
22458
- const inferred = inferSourcePathForComponent(comp, subsystems);
22459
- if (inferred) {
22460
- ownedPaths.push(inferred);
22461
- }
22462
- }
22463
- const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
22464
- const readPaths = [
22465
- path17.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
22466
- path17.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
22467
- ...compInterfaces.map((i) => path17.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
22468
- ...compImpls.map((impl) => path17.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
22469
- ];
22470
- agents.push({
22471
- id: `${comp.id}-implementer`,
22472
- name: `${comp.name} Implementer`,
22473
- description: `Developer agent implementing ${comp.name} (${comp.componentType})`,
22474
- template: "implementer",
22475
- creationReason: `Automatically inferred from L2 component spec: ${comp.id}`,
22476
- domainRoot: comp.subsystem,
22477
- ownedPaths,
22478
- readPaths,
22479
- writePaths: ownedPaths,
22480
- tags: ["implementer", "component", "sdd", comp.componentType.toLowerCase()],
22481
- dependencies,
22482
- variantGuidance: buildVariantGuidance([comp], components, variantsById),
22483
- status: "active",
22484
- targets: activeTargets,
22485
- createdAt: comp.createdAt,
22486
- updatedAt: comp.updatedAt
22487
- });
22488
- }
22489
- }
22490
- const now = (/* @__PURE__ */ new Date()).toISOString();
22491
- for (const dom of loadTopologyConfig().domains) {
22492
- agents.push({
22493
- id: `${dom.id}-owner`,
22494
- name: `${dom.name ?? dom.id} Owner`,
22495
- description: dom.description ? summarize(dom.description) : `Owner agent for the free-standing "${dom.id}" domain.`,
22496
- template: "domain-owner",
22497
- creationReason: "Inferred from a free-standing domain in .wai/topology.yaml",
22498
- domainRoot: dom.id,
22499
- ownedPaths: dom.ownedPaths,
22500
- readPaths: ["**"],
22501
- writePaths: dom.ownedPaths,
22502
- tags: ["owner", "domain"],
22503
- dependencies: [],
22504
- status: "active",
22505
- targets: activeTargets,
22506
- createdAt: now,
22507
- updatedAt: now
22508
- });
22509
- }
22510
- return agents;
22511
- }
22512
- function composeAgentBrief(agentId) {
22513
- const records = resolveAgentTopology();
22514
- const record = records.find((r) => r.id === agentId);
22515
- if (!record) {
22516
- throw new UnknownAgentError(agentId, records.map((r) => r.id));
22517
- }
22518
- const config = projectConfigRepository.load();
22519
- if (!config) throw new ProjectNotInitializedError();
22520
- const template = loadTemplate(record.template, config.globalTemplatesDir);
22521
- let instructions = renderTemplateInstructions(template, {
22522
- agentId: record.id,
22523
- agentName: record.name,
22524
- agentDescription: record.description,
22525
- ownedPaths: record.ownedPaths.join("\n"),
22526
- tags: record.tags.join(", "),
22527
- renderContext: "root",
22528
- contextNote: "",
22529
- domainPath: ".",
22530
- domainName: "",
22531
- variantGuidance: record.variantGuidance ?? ""
22532
- });
22533
- const guidance = loadAgentOverride(agentId);
22534
- if (guidance !== null) {
22535
- instructions = `${instructions.trimEnd()}
22536
-
22537
- ## Project guidance
22538
-
22539
- ${guidance.trim()}
22540
- `;
22541
- }
22542
- const profile = deriveExecutionProfile(record);
22543
- const budget = resolveBudget(profile, config.execution, record.id);
22544
- return {
22545
- agentId: record.id,
22546
- name: record.name,
22547
- template: record.template,
22548
- domainRoot: record.domainRoot,
22549
- ownedPaths: record.ownedPaths,
22550
- readPaths: record.readPaths,
22551
- instructions,
22552
- variantGuidance: record.variantGuidance || void 0,
22553
- profile: budget ? profile : void 0,
22554
- budget
22555
- };
22556
- }
22557
- var path17, fs13, projectFilesCache, UnknownAgentError;
22558
- var init_agent_resolver = __esm({
22559
- "src/core/agent_resolver.ts"() {
22560
- "use strict";
22561
- path17 = __toESM(require("path"));
22562
- fs13 = __toESM(require("fs"));
22563
- init_loader();
22564
- init_project_config();
22565
- init_fs();
22566
- init_errors();
22567
- init_templates();
22568
- init_execution_profile();
22569
- init_budget_policy();
22570
- init_specs2();
22571
- init_specs();
22572
- init_variants();
22573
- projectFilesCache = /* @__PURE__ */ new Map();
22574
- UnknownAgentError = class extends WaironError {
22575
- constructor(agentId, knownIds) {
22576
- super(`Unknown agent id: "${agentId}". Known agent ids: ${knownIds.join(", ")}`);
22577
- this.name = "UnknownAgentError";
22578
- }
22579
- };
22580
- }
22581
- });
22582
-
22583
- // src/config/loader.ts
22584
- function aiPathsAt(rootDir) {
22585
- const resolvedRoot = path18.resolve(rootDir);
22586
- const aiDirAt2 = (...segments) => {
22587
- const waiPath = path18.join(resolvedRoot, ".wai");
22588
- const waironPath = path18.join(resolvedRoot, ".wairon");
22589
- const base = !fs14.existsSync(waiPath) && fs14.existsSync(waironPath) ? waironPath : waiPath;
22590
- return path18.join(base, ...segments);
22591
- };
22592
- const specsDir = () => projectConfigRepositoryAt(resolvedRoot).specsDir();
22593
- return {
22594
- root: () => aiDirAt2(),
22595
- projectConfig: () => aiDirAt2("project.yaml"),
22596
- topologyConfig: () => aiDirAt2("topology.yaml"),
22597
- templatesDir: () => aiDirAt2("templates"),
22598
- rulesDir: () => aiDirAt2("rules"),
22599
- docsDir: () => aiDirAt2("docs"),
22600
- generatedDir: () => aiDirAt2("generated"),
22601
- contextDir: () => aiDirAt2("context"),
22602
- contextProjectMd: () => aiDirAt2("context", "project.md"),
22603
- contextArchitectureMd: () => aiDirAt2("context", "architecture.md"),
22604
- contextDomainsMd: () => aiDirAt2("context", "domains.md"),
22605
- contextWaironGuideMd: () => aiDirAt2("context", "wairon-guide.md"),
22606
- specsDir,
22607
- specsSystem: () => path18.join(specsDir(), ".index.yaml"),
22608
- specsSubsystemsDir: () => path18.join(specsDir(), "subsystems"),
22609
- specsComponentsDir: () => path18.join(specsDir(), "components"),
22610
- specsInterfacesDir: () => path18.join(specsDir(), "interfaces"),
22611
- specsImplementationsDir: () => path18.join(specsDir(), "implementations"),
22612
- specsTypesDir: () => path18.join(specsDir(), "types")
22613
- };
22614
- }
22615
- function isProjectInitialized() {
22616
- return projectConfigRepository.exists();
22617
- }
22618
- function assertProjectInitialized() {
22619
- if (!isProjectInitialized()) {
22620
- throw new ProjectNotInitializedError();
22621
- }
22622
- }
22623
- function loadRegistry() {
22624
- assertProjectInitialized();
22625
- if (!pathExists(AI_PATHS.specsSystem())) return createEmptyRegistry();
22626
- const { resolveAgentTopology: resolveAgentTopology2 } = (init_agent_resolver(), __toCommonJS(agent_resolver_exports));
22627
- return {
22628
- schemaVersion: "1.0.0",
22629
- agents: resolveAgentTopology2(),
22630
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22631
- };
22632
- }
22633
- function loadTopologyConfig() {
22634
- assertProjectInitialized();
22635
- if (!pathExists(AI_PATHS.topologyConfig())) return createEmptyTopologyConfig();
22636
- const raw = readYamlFile(AI_PATHS.topologyConfig());
22637
- if (!raw) return createEmptyTopologyConfig();
22638
- return TopologyConfigSchema.parse(raw);
22639
- }
22640
- function saveTopologyConfig(config) {
22641
- writeYamlFile(AI_PATHS.topologyConfig(), config);
22642
- }
22643
- var fs14, path18, AI_PATHS;
22644
- var init_loader = __esm({
22645
- "src/config/loader.ts"() {
22646
- "use strict";
22647
- fs14 = __toESM(require("fs"));
22648
- path18 = __toESM(require("path"));
22649
- init_fs();
22650
- init_yaml();
22651
- init_errors();
22652
- init_models();
22653
- init_project_config();
22654
- AI_PATHS = {
22655
- root: () => aiPathsAt(getProjectRoot()).root(),
22656
- projectConfig: () => aiPathsAt(getProjectRoot()).projectConfig(),
22657
- topologyConfig: () => aiPathsAt(getProjectRoot()).topologyConfig(),
22658
- templatesDir: () => aiPathsAt(getProjectRoot()).templatesDir(),
22659
- rulesDir: () => aiPathsAt(getProjectRoot()).rulesDir(),
22660
- docsDir: () => aiPathsAt(getProjectRoot()).docsDir(),
22661
- generatedDir: () => aiPathsAt(getProjectRoot()).generatedDir(),
22662
- contextDir: () => aiPathsAt(getProjectRoot()).contextDir(),
22663
- contextProjectMd: () => aiPathsAt(getProjectRoot()).contextProjectMd(),
22664
- contextArchitectureMd: () => aiPathsAt(getProjectRoot()).contextArchitectureMd(),
22665
- contextDomainsMd: () => aiPathsAt(getProjectRoot()).contextDomainsMd(),
22666
- contextWaironGuideMd: () => aiPathsAt(getProjectRoot()).contextWaironGuideMd(),
22667
- specsDir: () => aiPathsAt(getProjectRoot()).specsDir(),
22668
- specsSystem: () => aiPathsAt(getProjectRoot()).specsSystem(),
22669
- specsSubsystemsDir: () => aiPathsAt(getProjectRoot()).specsSubsystemsDir(),
22670
- specsComponentsDir: () => aiPathsAt(getProjectRoot()).specsComponentsDir(),
22671
- specsInterfacesDir: () => aiPathsAt(getProjectRoot()).specsInterfacesDir(),
22672
- specsImplementationsDir: () => aiPathsAt(getProjectRoot()).specsImplementationsDir(),
22673
- specsTypesDir: () => aiPathsAt(getProjectRoot()).specsTypesDir()
22674
- };
22675
- }
22676
- });
22677
-
22678
- // src/core/domains.ts
22679
- var domains_exports = {};
22680
- __export(domains_exports, {
22681
- addFreeStandingDomain: () => addFreeStandingDomain,
22682
- deriveSubsystemDomains: () => deriveSubsystemDomains,
22683
- findDomain: () => findDomain,
22684
- listFreeStandingDomains: () => listFreeStandingDomains,
22685
- removeFreeStandingDomain: () => removeFreeStandingDomain,
22686
- resolveDomains: () => resolveDomains
22687
- });
22688
- function rel(p) {
22689
- return path19.relative(process.cwd(), p).replace(/\\/g, "/");
22690
- }
22691
- function deriveSubsystemDomains() {
22692
- const subsystems = loadSubsystemSpecs();
22693
- const components = loadComponentSpecs();
22694
- return subsystems.map((sub) => {
22695
- const ownedPaths = [
22696
- rel(getSubsystemPath(sub.id)),
22697
- ...components.filter((c) => c.subsystem === sub.id).map((c) => rel(getComponentPath(c.id, sub.id)))
22698
- ];
22699
- return {
22700
- id: sub.id,
22701
- name: sub.name,
22702
- description: sub.description,
22703
- boundTo: sub.id,
22704
- ownedPaths
22705
- };
22706
- });
22707
- }
22708
- function listFreeStandingDomains() {
22709
- return loadTopologyConfig().domains;
22710
- }
22711
- function resolveDomains() {
22712
- return [...deriveSubsystemDomains(), ...listFreeStandingDomains()];
22713
- }
22714
- function findDomain(id) {
22715
- return resolveDomains().find((d) => d.id === id);
22716
- }
22717
- function addFreeStandingDomain(domain) {
22718
- const config = loadTopologyConfig();
22719
- if (config.domains.some((d) => d.id === domain.id)) {
22720
- throw new WaironError(`A free-standing domain "${domain.id}" already exists in .wai/topology.yaml.`);
22721
- }
22722
- if (deriveSubsystemDomains().some((d) => d.id === domain.id)) {
22723
- throw new WaironError(`Domain id "${domain.id}" collides with a subsystem-derived domain.`);
22724
- }
22725
- config.domains.push(domain);
22726
- saveTopologyConfig(config);
22727
- }
22728
- function removeFreeStandingDomain(id) {
22729
- const config = loadTopologyConfig();
22730
- const idx = config.domains.findIndex((d) => d.id === id);
22731
- if (idx === -1) {
22732
- throw new WaironError(
22733
- `"${id}" is not a free-standing domain. Subsystem-backed domains are derived from the spec tree and cannot be removed here.`
22734
- );
22735
- }
22736
- config.domains.splice(idx, 1);
22737
- saveTopologyConfig(config);
22194
+ function parseTemplate(yamlContent) {
22195
+ return TemplateSchema.parse(parseYaml(yamlContent));
22738
22196
  }
22739
- var path19;
22740
- var init_domains = __esm({
22741
- "src/core/domains.ts"() {
22197
+ var path18;
22198
+ var init_templates = __esm({
22199
+ "src/core/templates.ts"() {
22742
22200
  "use strict";
22743
- path19 = __toESM(require("path"));
22744
- init_loader();
22745
- init_specs2();
22201
+ path18 = __toESM(require("path"));
22202
+ init_fs();
22203
+ init_yaml();
22746
22204
  init_errors();
22205
+ init_template();
22206
+ init_paths();
22207
+ init_defaults();
22747
22208
  }
22748
22209
  });
22749
22210
 
@@ -22795,7 +22256,7 @@ function provisionProject(name) {
22795
22256
  function ensureProjectInitialized(fallbackName) {
22796
22257
  const now = (/* @__PURE__ */ new Date()).toISOString();
22797
22258
  const paths = aiPathsAt(getProjectRoot());
22798
- const hasSystem = fs15.existsSync(paths.specsSystem());
22259
+ const hasSystem = fs14.existsSync(paths.specsSystem());
22799
22260
  let name = fallbackName;
22800
22261
  if (hasSystem) {
22801
22262
  const existing = loadSystemSpec();
@@ -22817,11 +22278,11 @@ function ensureProjectInitialized(fallbackName) {
22817
22278
  function walkChainedSubprojects(projectRoot, onChild) {
22818
22279
  const visited = /* @__PURE__ */ new Set();
22819
22280
  const walk2 = (dir) => {
22820
- const resolved = path20.resolve(dir);
22281
+ const resolved = path19.resolve(dir);
22821
22282
  if (visited.has(resolved)) return;
22822
22283
  visited.add(resolved);
22823
22284
  const specsDir = aiPathsAt(dir).specsDir();
22824
- if (!fs15.existsSync(specsDir)) return;
22285
+ if (!fs14.existsSync(specsDir)) return;
22825
22286
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
22826
22287
  let raw;
22827
22288
  try {
@@ -22839,497 +22300,1040 @@ function walkChainedSubprojects(projectRoot, onChild) {
22839
22300
  continue;
22840
22301
  }
22841
22302
  const id = raw.id;
22842
- onChild(childDir, typeof id === "string" ? id : path20.basename(childDir));
22303
+ onChild(childDir, typeof id === "string" ? id : path19.basename(childDir));
22843
22304
  walk2(childDir);
22844
22305
  }
22845
- };
22846
- walk2(projectRoot);
22306
+ };
22307
+ walk2(projectRoot);
22308
+ }
22309
+ function listDirectChainedSubprojects(projectRoot) {
22310
+ const out = [];
22311
+ const specsDir = aiPathsAt(projectRoot).specsDir();
22312
+ if (!fs14.existsSync(specsDir)) return out;
22313
+ for (const file of listFilesRecursive(specsDir, ".yaml")) {
22314
+ let raw;
22315
+ try {
22316
+ raw = readYamlFile(file);
22317
+ } catch {
22318
+ continue;
22319
+ }
22320
+ if (!(raw && typeof raw === "object" && "parentSystem" in raw)) continue;
22321
+ const pp = raw.projectPath;
22322
+ if (typeof pp !== "string" || pp.trim() === "") continue;
22323
+ let dir;
22324
+ try {
22325
+ dir = assertContainedProjectPath(projectRoot, pp);
22326
+ } catch {
22327
+ continue;
22328
+ }
22329
+ const id = raw.id;
22330
+ out.push({ dir, subsystemId: typeof id === "string" ? id : path19.basename(dir) });
22331
+ }
22332
+ return out;
22333
+ }
22334
+ function childHasSpecsButNoConfig(childDir) {
22335
+ return fs14.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
22336
+ }
22337
+ function findChainingSubprojectsMissingConfig(projectRoot) {
22338
+ const missing = [];
22339
+ walkChainedSubprojects(projectRoot, (childDir) => {
22340
+ if (childHasSpecsButNoConfig(childDir)) missing.push(childDir);
22341
+ });
22342
+ return missing;
22343
+ }
22344
+ function backfillChainedSubprojectConfigs(projectRoot) {
22345
+ const backfilled = [];
22346
+ walkChainedSubprojects(projectRoot, (childDir, subsystemId) => {
22347
+ if (childHasSpecsButNoConfig(childDir)) {
22348
+ runWithProjectRoot(childDir, () => {
22349
+ ensureProjectInitialized(subsystemId);
22350
+ });
22351
+ backfilled.push(childDir);
22352
+ }
22353
+ });
22354
+ return backfilled;
22355
+ }
22356
+ function createChainedSubsystem(subsystem, projectName) {
22357
+ if (!subsystem.projectPath || subsystem.projectPath.trim() === "") {
22358
+ throw new WaironError("projectPath is required to create a chained subsystem.");
22359
+ }
22360
+ const projectPath = toPosixPath(subsystem.projectPath);
22361
+ saveSubsystemSpec({ ...subsystem, projectPath });
22362
+ const childDir = assertContainedProjectPath(getProjectRoot(), projectPath);
22363
+ runWithProjectRoot(childDir, () => {
22364
+ ensureDir(aiPathsAt(childDir).specsDir());
22365
+ ensureProjectInitialized(projectName);
22366
+ });
22367
+ invalidateSpecCache();
22368
+ }
22369
+ function moveSubsystemProject(subsystemId, newProjectPath) {
22370
+ const sub = loadSubsystemSpec(subsystemId);
22371
+ if (!sub) {
22372
+ throw new WaironError(`Subsystem "${subsystemId}" does not exist.`);
22373
+ }
22374
+ if (!sub.projectPath || sub.projectPath.trim() === "") {
22375
+ throw new WaironError(
22376
+ `Subsystem "${subsystemId}" has no projectPath to move (not an external subproject).`
22377
+ );
22378
+ }
22379
+ const nextPath = toPosixPath(newProjectPath);
22380
+ const root = getProjectRoot();
22381
+ const oldDir = assertContainedProjectPath(root, sub.projectPath);
22382
+ const newDir = assertContainedProjectPath(root, nextPath);
22383
+ if (oldDir !== newDir) {
22384
+ if (!fs14.existsSync(oldDir)) {
22385
+ throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
22386
+ }
22387
+ if (fs14.existsSync(newDir)) {
22388
+ throw new WaironError(`Target directory already exists: ${newDir}`);
22389
+ }
22390
+ ensureDir(path19.dirname(newDir));
22391
+ fs14.renameSync(oldDir, newDir);
22392
+ }
22393
+ saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
22394
+ invalidateSpecCache();
22395
+ }
22396
+ function toPosixPath(p) {
22397
+ return p.replace(/\\/g, "/");
22398
+ }
22399
+ function isWithinDir(dir, file) {
22400
+ const d = path19.resolve(dir);
22401
+ const f = path19.resolve(file);
22402
+ return f === d || f.startsWith(d + path19.sep);
22403
+ }
22404
+ function externalizeSubsystem(subsystemId, projectPath) {
22405
+ if (subsystemId.includes("::")) {
22406
+ throw new WaironError("cannot externalize a nested/namespaced subsystem; run from its owning project.");
22407
+ }
22408
+ const foo = loadSubsystemSpec(subsystemId);
22409
+ if (!foo || foo.projectPath) {
22410
+ throw new WaironError(`cannot externalize: subsystem "${subsystemId}" is missing or already external.`);
22411
+ }
22412
+ const parentRoot = getProjectRoot();
22413
+ const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
22414
+ const fooDir = path19.join(parentSpecsDir, subsystemId);
22415
+ if (!fs14.existsSync(fooDir)) {
22416
+ throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
22417
+ }
22418
+ const relPath = toPosixPath(projectPath);
22419
+ const childDir = assertContainedProjectPath(parentRoot, relPath);
22420
+ const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
22421
+ if (fs14.existsSync(childFooDir)) {
22422
+ throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
22423
+ }
22424
+ const renameMap = buildRenameMap(
22425
+ subsystemId,
22426
+ /* externalize */
22427
+ true
22428
+ );
22429
+ const childSystemName = foo.name || subsystemId;
22430
+ runWithProjectRoot(childDir, () => {
22431
+ const now = (/* @__PURE__ */ new Date()).toISOString();
22432
+ projectConfigRepository.create(defaultProjectConfig(childSystemName, now));
22433
+ ensureDir(path19.join(childDir, ".wai", "specs"));
22434
+ saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
22435
+ });
22436
+ ensureDir(path19.dirname(childFooDir));
22437
+ fs14.renameSync(fooDir, childFooDir);
22438
+ rebaseImplementationPaths(childFooDir, parentRoot, childDir);
22439
+ patchSubsystemIndex(path19.join(childFooDir, ".index.yaml"), (s) => {
22440
+ s.parentSystem = childSystemName;
22441
+ delete s.projectPath;
22442
+ });
22443
+ ensureDir(fooDir);
22444
+ writeYamlFile(path19.join(fooDir, ".index.yaml"), {
22445
+ id: subsystemId,
22446
+ name: foo.name,
22447
+ description: foo.description,
22448
+ parentSystem: foo.parentSystem,
22449
+ publicInterfaces: [],
22450
+ projectPath: relPath,
22451
+ trustedLinks: [],
22452
+ status: foo.status ?? "draft",
22453
+ createdAt: foo.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
22454
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22455
+ });
22456
+ rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22457
+ rebaseMovedRefs(childFooDir, subsystemId, "into");
22458
+ invalidateSpecCache();
22459
+ }
22460
+ function internalizeSubsystem(subsystemId) {
22461
+ if (subsystemId.includes("::")) {
22462
+ throw new WaironError("cannot internalize a nested/namespaced subsystem; run from its owning project.");
22463
+ }
22464
+ const foo = loadSubsystemSpec(subsystemId);
22465
+ if (!foo || !foo.projectPath) {
22466
+ throw new WaironError(`cannot internalize: subsystem "${subsystemId}" is missing or not external.`);
22467
+ }
22468
+ const parentRoot = getProjectRoot();
22469
+ const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
22470
+ const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
22471
+ const childWai = path19.join(childDir, ".wai");
22472
+ const childFooDir = path19.join(childDir, ".wai", "specs", subsystemId);
22473
+ if (!fs14.existsSync(childFooDir)) {
22474
+ throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
22475
+ }
22476
+ const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
22477
+ if (childOwnSubs.length !== 1 || childOwnSubs[0].id !== subsystemId) {
22478
+ throw new WaironError(`cannot internalize: subproject is a multi-subsystem system, not a flat "${subsystemId}".`);
22479
+ }
22480
+ const renameMap = buildRenameMap(
22481
+ subsystemId,
22482
+ /* externalize */
22483
+ false
22484
+ );
22485
+ const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
22486
+ const fooDir = path19.join(parentSpecsDir, subsystemId);
22487
+ fs14.rmSync(fooDir, { recursive: true, force: true });
22488
+ ensureDir(path19.dirname(fooDir));
22489
+ fs14.renameSync(childFooDir, fooDir);
22490
+ rebaseImplementationPaths(fooDir, childDir, parentRoot);
22491
+ patchSubsystemIndex(path19.join(fooDir, ".index.yaml"), (s) => {
22492
+ s.parentSystem = parentSystemName;
22493
+ delete s.projectPath;
22494
+ });
22495
+ fs14.rmSync(childWai, { recursive: true, force: true });
22496
+ rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22497
+ rebaseMovedRefs(fooDir, subsystemId, "outOf");
22498
+ invalidateSpecCache();
22499
+ }
22500
+ function buildRenameMap(subsystemId, externalize) {
22501
+ const map = /* @__PURE__ */ new Map();
22502
+ const prefix = `${subsystemId}::`;
22503
+ const add = (bare, namespaced) => externalize ? map.set(bare, namespaced) : map.set(namespaced, bare);
22504
+ const comps = loadComponentSpecs().filter((c) => c.subsystem === subsystemId);
22505
+ const compIds = new Set(comps.map((c) => c.id));
22506
+ for (const c of comps) {
22507
+ const bare = c.id.startsWith(prefix) ? c.id.slice(prefix.length) : c.id;
22508
+ add(bare, `${prefix}${bare}`);
22509
+ }
22510
+ for (const i of loadInterfaceSpecs()) {
22511
+ if (!compIds.has(i.component)) continue;
22512
+ const bare = i.id.startsWith(prefix) ? i.id.slice(prefix.length) : i.id;
22513
+ add(bare, `${prefix}${bare}`);
22514
+ }
22515
+ for (const t of loadTypeSpecs()) {
22516
+ if (t.subsystem !== subsystemId) continue;
22517
+ const bare = t.id.startsWith(prefix) ? t.id.slice(prefix.length) : t.id;
22518
+ add(bare, `${prefix}${bare}`);
22519
+ }
22520
+ return map;
22521
+ }
22522
+ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22523
+ if (renameMap.size === 0) return;
22524
+ rewriteRefFields(specsDir, (ref) => renameMap.get(ref) ?? ref, excludeDir);
22525
+ }
22526
+ function rebaseMovedRefs(movedDir, mount, direction) {
22527
+ const declared = componentIdsUnder(movedDir);
22528
+ rewriteRefFields(movedDir, (ref, position) => position === "component" ? rebaseReference(ref, mount, direction, (id) => declared.has(id)) : ref);
22529
+ }
22530
+ function componentIdsUnder(dir) {
22531
+ const ids = /* @__PURE__ */ new Set();
22532
+ for (const file of listFilesRecursive(dir, ".yaml")) {
22533
+ let raw;
22534
+ try {
22535
+ raw = readYamlFile(file);
22536
+ } catch {
22537
+ continue;
22538
+ }
22539
+ if (raw && typeof raw === "object" && "componentType" in raw && typeof raw.id === "string") ids.add(raw.id);
22540
+ }
22541
+ return ids;
22847
22542
  }
22848
- function listDirectChainedSubprojects(projectRoot) {
22849
- const out = [];
22850
- const specsDir = aiPathsAt(projectRoot).specsDir();
22851
- if (!fs15.existsSync(specsDir)) return out;
22543
+ function rewriteRefFields(specsDir, remap, excludeDir) {
22544
+ const rewritten = [];
22852
22545
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
22546
+ if (excludeDir && isWithinDir(excludeDir, file)) continue;
22853
22547
  let raw;
22854
22548
  try {
22855
22549
  raw = readYamlFile(file);
22856
22550
  } catch {
22857
22551
  continue;
22858
22552
  }
22859
- if (!(raw && typeof raw === "object" && "parentSystem" in raw)) continue;
22860
- const pp = raw.projectPath;
22861
- if (typeof pp !== "string" || pp.trim() === "") continue;
22862
- let dir;
22553
+ if (!raw || typeof raw !== "object") continue;
22554
+ const kind = specKind(raw);
22555
+ if (!rewriteSpecRefs(raw, remap) || !kind) continue;
22556
+ writeYamlFile(file, raw);
22557
+ rewritten.push({ kind, id: kind === "system" ? "system" : String(raw.id) });
22558
+ }
22559
+ return rewritten;
22560
+ }
22561
+ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
22562
+ for (const file of listFilesRecursive(specsDir, ".yaml")) {
22563
+ let raw;
22863
22564
  try {
22864
- dir = assertContainedProjectPath(projectRoot, pp);
22565
+ raw = readYamlFile(file);
22865
22566
  } catch {
22866
22567
  continue;
22867
22568
  }
22868
- const id = raw.id;
22869
- out.push({ dir, subsystemId: typeof id === "string" ? id : path20.basename(dir) });
22569
+ if (!raw || typeof raw !== "object" || !("contract" in raw)) continue;
22570
+ let changed = false;
22571
+ const rebase = (holder, key) => {
22572
+ const p = holder[key];
22573
+ if (typeof p !== "string" || p === "" || path19.isAbsolute(p)) return;
22574
+ const next = toPosixPath(path19.relative(toRoot, path19.resolve(fromRoot, p)));
22575
+ if (next !== p) {
22576
+ holder[key] = next;
22577
+ changed = true;
22578
+ }
22579
+ };
22580
+ for (const key of ["sourcePath", "simPath"]) rebase(raw, key);
22581
+ if (Array.isArray(raw.methods)) {
22582
+ for (const method of raw.methods) {
22583
+ if (method && typeof method === "object") rebase(method, "sourcePath");
22584
+ }
22585
+ }
22586
+ if (changed) writeYamlFile(file, raw);
22870
22587
  }
22871
- return out;
22872
- }
22873
- function childHasSpecsButNoConfig(childDir) {
22874
- return fs15.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
22875
22588
  }
22876
- function findChainingSubprojectsMissingConfig(projectRoot) {
22877
- const missing = [];
22878
- walkChainedSubprojects(projectRoot, (childDir) => {
22879
- if (childHasSpecsButNoConfig(childDir)) missing.push(childDir);
22880
- });
22881
- return missing;
22589
+ function patchSubsystemIndex(indexPath, mutate) {
22590
+ if (!fs14.existsSync(indexPath)) return;
22591
+ const raw = readYamlFile(indexPath);
22592
+ mutate(raw);
22593
+ raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
22594
+ writeYamlFile(indexPath, raw);
22882
22595
  }
22883
- function backfillChainedSubprojectConfigs(projectRoot) {
22884
- const backfilled = [];
22885
- walkChainedSubprojects(projectRoot, (childDir, subsystemId) => {
22886
- if (childHasSpecsButNoConfig(childDir)) {
22887
- runWithProjectRoot(childDir, () => {
22888
- ensureProjectInitialized(subsystemId);
22889
- });
22890
- backfilled.push(childDir);
22596
+ function renameComponent(componentId, newId) {
22597
+ const component = loadComponentSpec(componentId);
22598
+ if (!component) {
22599
+ throw new WaironError(`component-missing: no component has the id "${componentId}".`);
22600
+ }
22601
+ if (componentId.includes("::")) {
22602
+ throw new WaironError(
22603
+ `chained-component: "${componentId}" lives in a chained subproject; rename it from that project's own root.`
22604
+ );
22605
+ }
22606
+ if (!SpecIdSchema.safeParse(newId).success) {
22607
+ throw new WaironError(`invalid-id: "${newId}" is not a lowercase identifier with no namespace separator.`);
22608
+ }
22609
+ const components = loadComponentSpecs();
22610
+ const interfaces = loadInterfaceSpecs();
22611
+ const implementations = loadImplementationSpecs();
22612
+ const interfaceId = `i${newId}`;
22613
+ const implementationId = `${newId}_impl`;
22614
+ const taken = [
22615
+ components.some((c) => c.id === newId) ? `component "${newId}"` : null,
22616
+ interfaces.some((i) => i.id === interfaceId) ? `interface "${interfaceId}"` : null,
22617
+ implementations.some((impl) => impl.id === implementationId) ? `implementation "${implementationId}"` : null
22618
+ ].filter((holder) => holder !== null);
22619
+ if (taken.length > 0) {
22620
+ throw new WaironError(`id-taken: the new id, or an id it implies, is already in use: ${taken.join(", ")}.`);
22621
+ }
22622
+ const contracts = interfaces.filter((i) => i.component === componentId);
22623
+ const movingInterface = contracts.find((i) => i.id === `i${componentId}`);
22624
+ const movingImplementation = implementations.find((impl) => impl.id === `${componentId}_impl` && contracts.some((i) => i.id === impl.contract));
22625
+ const renamed = [
22626
+ { kind: "component", from: componentId, to: newId },
22627
+ ...movingInterface ? [{ kind: "interface", from: movingInterface.id, to: interfaceId }] : [],
22628
+ ...movingImplementation ? [{ kind: "implementation", from: movingImplementation.id, to: implementationId }] : []
22629
+ ];
22630
+ const rewritten = rewriteRefFields(aiPathsAt(getProjectRoot()).specsDir(), (ref, position) => {
22631
+ if (position === "component" || position === "entity-class" || position === "auth-source") {
22632
+ return ref === componentId ? newId : ref;
22891
22633
  }
22892
- });
22893
- return backfilled;
22634
+ if (position === "interface" && ref === movingInterface?.id) return interfaceId;
22635
+ return ref;
22636
+ }).filter((spec) => !renamed.some((moved) => moved.kind === spec.kind && moved.from === spec.id)).map((spec) => spec.id);
22637
+ invalidateSpecCache();
22638
+ moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation);
22639
+ return { renamed, rewritten };
22894
22640
  }
22895
- function createChainedSubsystem(subsystem, projectName) {
22896
- if (!subsystem.projectPath || subsystem.projectPath.trim() === "") {
22897
- throw new WaironError("projectPath is required to create a chained subsystem.");
22641
+ function moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation) {
22642
+ const interfaceId = `i${newId}`;
22643
+ const implementationId = `${newId}_impl`;
22644
+ const movedComponent = loadComponentSpec(componentId) ?? component;
22645
+ const movedInterface = movingInterface ? loadInterfaceSpec(movingInterface.id) ?? movingInterface : null;
22646
+ const movedImplementation = movingImplementation ? loadImplementationSpec(movingImplementation.id) ?? movingImplementation : null;
22647
+ saveComponentSpec({ ...movedComponent, id: newId });
22648
+ if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
22649
+ if (movedImplementation) {
22650
+ const oldFile = getImplementationPath(movedImplementation.id);
22651
+ if (path19.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path19.resolve(oldFile)) {
22652
+ fs14.unlinkSync(oldFile);
22653
+ }
22654
+ saveImplementationSpec({ ...movedImplementation, id: implementationId });
22898
22655
  }
22899
- const projectPath = toPosixPath(subsystem.projectPath);
22900
- saveSubsystemSpec({ ...subsystem, projectPath });
22901
- const childDir = assertContainedProjectPath(getProjectRoot(), projectPath);
22902
- runWithProjectRoot(childDir, () => {
22903
- ensureDir(aiPathsAt(childDir).specsDir());
22904
- ensureProjectInitialized(projectName);
22905
- });
22656
+ removeSpecFile2(getComponentPath(componentId), componentId);
22657
+ if (movingInterface) removeSpecFile2(getInterfacePath(movingInterface.id), movingInterface.id);
22658
+ if (movingImplementation) removeSpecFile2(getImplementationPath(movingImplementation.id), movingImplementation.id);
22906
22659
  invalidateSpecCache();
22907
22660
  }
22908
- function moveSubsystemProject(subsystemId, newProjectPath) {
22909
- const sub = loadSubsystemSpec(subsystemId);
22910
- if (!sub) {
22911
- throw new WaironError(`Subsystem "${subsystemId}" does not exist.`);
22661
+ function removeSpecFile2(file, id) {
22662
+ if (!fs14.existsSync(file)) return;
22663
+ try {
22664
+ if (readYamlFile(file)?.id !== id) return;
22665
+ } catch {
22666
+ return;
22912
22667
  }
22913
- if (!sub.projectPath || sub.projectPath.trim() === "") {
22668
+ fs14.unlinkSync(file);
22669
+ const specsRoot = path19.resolve(aiPathsAt(getProjectRoot()).specsDir());
22670
+ for (let dir = path19.dirname(path19.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path19.dirname(dir)) {
22671
+ if (fs14.readdirSync(dir).length > 0) break;
22672
+ fs14.rmdirSync(dir);
22673
+ }
22674
+ }
22675
+ function renameMethod(componentId, methodName, newName, pinSymbol) {
22676
+ const component = loadComponentSpec(componentId);
22677
+ if (!component) {
22678
+ throw new WaironError(`component-missing: no component has the id "${componentId}".`);
22679
+ }
22680
+ if (componentId.includes("::")) {
22914
22681
  throw new WaironError(
22915
- `Subsystem "${subsystemId}" has no projectPath to move (not an external subproject).`
22682
+ `chained-component: "${componentId}" lives in a chained subproject; rename its method from that project's own root.`
22916
22683
  );
22917
22684
  }
22918
- const nextPath = toPosixPath(newProjectPath);
22919
- const root = getProjectRoot();
22920
- const oldDir = assertContainedProjectPath(root, sub.projectPath);
22921
- const newDir = assertContainedProjectPath(root, nextPath);
22922
- if (oldDir !== newDir) {
22923
- if (!fs15.existsSync(oldDir)) {
22924
- throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
22925
- }
22926
- if (fs15.existsSync(newDir)) {
22927
- throw new WaironError(`Target directory already exists: ${newDir}`);
22928
- }
22929
- ensureDir(path20.dirname(newDir));
22930
- fs15.renameSync(oldDir, newDir);
22685
+ if (!METHOD_NAME.test(newName)) {
22686
+ throw new WaironError(`invalid-name: "${newName}" is not a camel-case identifier.`);
22687
+ }
22688
+ const contracts = loadInterfaceSpecs().filter((i) => i.component === componentId);
22689
+ const moving = contracts.filter((i) => i.methods.some((m) => m.name === methodName));
22690
+ if (moving.length === 0) {
22691
+ throw new WaironError(`method-missing: no contract of "${componentId}" declares the method "${methodName}".`);
22692
+ }
22693
+ const taken = moving.filter((i) => i.methods.some((m) => m.name === newName)).map((i) => `"${i.id}"`);
22694
+ if (taken.length > 0) {
22695
+ throw new WaironError(`name-taken: ${taken.join(", ")} already declares a method under the name "${newName}".`);
22696
+ }
22697
+ const implementations = loadImplementationSpecs();
22698
+ const movingContracts = new Set(moving.map((i) => i.id));
22699
+ const renamed = [];
22700
+ for (const contract of moving) {
22701
+ saveInterfaceSpec({
22702
+ ...contract,
22703
+ methods: contract.methods.map((m) => m.name === methodName ? { ...m, name: newName, signature: renameInSignature(m.signature, methodName, newName) } : m)
22704
+ });
22705
+ renamed.push(contract.id);
22706
+ }
22707
+ let pinnedSymbol;
22708
+ for (const implementation of implementations) {
22709
+ if (!movingContracts.has(implementation.contract)) continue;
22710
+ const realization = implementation.methods.find((m) => m.name === methodName);
22711
+ if (!realization) continue;
22712
+ const pin = realization.symbol === void 0 && pinSymbol !== false;
22713
+ if (pin) pinnedSymbol = methodName;
22714
+ saveImplementationSpec({
22715
+ ...implementation,
22716
+ methods: implementation.methods.map((m) => m.name === methodName ? { ...m, name: newName, ...pin ? { symbol: methodName } : {} } : m)
22717
+ });
22718
+ renamed.push(implementation.id);
22719
+ }
22720
+ invalidateSpecCache();
22721
+ const specsDir = aiPathsAt(getProjectRoot()).specsDir();
22722
+ const rewritten = rewriteRefFields(specsDir, (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref).map((spec) => spec.id);
22723
+ const mentions = collectMentions(specsDir, methodName, moving);
22724
+ return {
22725
+ component: componentId,
22726
+ from: methodName,
22727
+ to: newName,
22728
+ renamed,
22729
+ rewritten,
22730
+ mentions,
22731
+ ...pinnedSymbol ? { pinnedSymbol } : {}
22732
+ };
22733
+ }
22734
+ function renameInSignature(signature, from, to) {
22735
+ return signature.replace(identifierPattern(from), to);
22736
+ }
22737
+ function collectMentions(specsDir, methodName, moved) {
22738
+ const mentions = /* @__PURE__ */ new Set();
22739
+ for (const contract of moved) {
22740
+ const wireBound = contract.methods.some((m) => {
22741
+ const endpoint = m.endpoint;
22742
+ return endpoint?.transport === "gRPC" && endpoint.method === methodName;
22743
+ });
22744
+ if (wireBound) mentions.add(contract.id);
22745
+ }
22746
+ const named = identifierPattern(methodName);
22747
+ const namesMethod = (value) => {
22748
+ if (Array.isArray(value)) return value.some(namesMethod);
22749
+ if (!value || typeof value !== "object") return false;
22750
+ return Object.entries(value).some(([key, nested]) => typeof nested === "string" ? PROSE_FIELDS2.has(key) && named.test(nested) : namesMethod(nested));
22751
+ };
22752
+ for (const file of listFilesRecursive(specsDir, ".yaml")) {
22753
+ let raw;
22754
+ try {
22755
+ raw = readYamlFile(file);
22756
+ } catch {
22757
+ continue;
22758
+ }
22759
+ if (!raw || typeof raw !== "object") continue;
22760
+ const kind = specKind(raw);
22761
+ if (!kind || !namesMethod(raw)) continue;
22762
+ mentions.add(kind === "system" ? "system" : String(raw.id));
22763
+ }
22764
+ return [...mentions];
22765
+ }
22766
+ function identifierPattern(methodName) {
22767
+ return new RegExp(`(?<![A-Za-z0-9_])${methodName}(?![A-Za-z0-9_])`);
22768
+ }
22769
+ var fs14, path19, METHOD_NAME, PROSE_FIELDS2;
22770
+ var init_provision = __esm({
22771
+ "src/core/provision.ts"() {
22772
+ "use strict";
22773
+ fs14 = __toESM(require("fs"));
22774
+ path19 = __toESM(require("path"));
22775
+ init_specs2();
22776
+ init_paths();
22777
+ init_project_config();
22778
+ init_fs();
22779
+ init_yaml();
22780
+ init_errors();
22781
+ init_models();
22782
+ METHOD_NAME = /^[a-z][a-zA-Z0-9]*$/;
22783
+ PROSE_FIELDS2 = /* @__PURE__ */ new Set([
22784
+ "description",
22785
+ "intent",
22786
+ "summary",
22787
+ "condition",
22788
+ "over",
22789
+ "outcome",
22790
+ "error",
22791
+ "note",
22792
+ "caller"
22793
+ ]);
22794
+ }
22795
+ });
22796
+
22797
+ // src/core/execution_profile.ts
22798
+ function isSweeping(p) {
22799
+ return p === "**" || p === "**/*" || p.startsWith("**/");
22800
+ }
22801
+ function deriveBreadth(agent) {
22802
+ if (agent.readPaths.some(isSweeping)) {
22803
+ return MANAGER_TEMPLATES.has(agent.template) || READ_ONLY_TEMPLATES.has(agent.template) ? "wide" : "moderate";
22931
22804
  }
22932
- saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
22933
- invalidateSpecCache();
22805
+ const owned = agent.ownedPaths.length;
22806
+ if (owned >= WIDE_PATH_COUNT) return "wide";
22807
+ if (owned >= MODERATE_PATH_COUNT) return "moderate";
22808
+ return "narrow";
22934
22809
  }
22935
- function toPosixPath(p) {
22936
- return p.replace(/\\/g, "/");
22810
+ function deriveReasoningDepth(agent) {
22811
+ for (const tag of agent.tags) {
22812
+ if (DEEP_STEREOTYPES.has(tag)) return "deep";
22813
+ if (MECHANICAL_STEREOTYPES.has(tag)) return "mechanical";
22814
+ }
22815
+ if (MANAGER_TEMPLATES.has(agent.template)) return "deep";
22816
+ if (agent.template === "reviewer") return "deep";
22817
+ if (agent.template === "tester") return "standard";
22818
+ return "standard";
22937
22819
  }
22938
- function isWithinDir(dir, file) {
22939
- const d = path20.resolve(dir);
22940
- const f = path20.resolve(file);
22941
- return f === d || f.startsWith(d + path20.sep);
22820
+ function deriveWrites(agent) {
22821
+ return !READ_ONLY_TEMPLATES.has(agent.template);
22942
22822
  }
22943
- function externalizeSubsystem(subsystemId, projectPath) {
22944
- if (subsystemId.includes("::")) {
22945
- throw new WaironError("cannot externalize a nested/namespaced subsystem; run from its owning project.");
22946
- }
22947
- const foo = loadSubsystemSpec(subsystemId);
22948
- if (!foo || foo.projectPath) {
22949
- throw new WaironError(`cannot externalize: subsystem "${subsystemId}" is missing or already external.`);
22950
- }
22951
- const parentRoot = getProjectRoot();
22952
- const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
22953
- const fooDir = path20.join(parentSpecsDir, subsystemId);
22954
- if (!fs15.existsSync(fooDir)) {
22955
- throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
22956
- }
22957
- const relPath = toPosixPath(projectPath);
22958
- const childDir = assertContainedProjectPath(parentRoot, relPath);
22959
- const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
22960
- if (fs15.existsSync(childFooDir)) {
22961
- throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
22823
+ function deriveExecutionProfile(agent) {
22824
+ const breadth = deriveBreadth(agent);
22825
+ const reasoningDepth = deriveReasoningDepth(agent);
22826
+ const writes = deriveWrites(agent);
22827
+ const delegates = MANAGER_TEMPLATES.has(agent.template);
22828
+ const because = [];
22829
+ if (delegates) {
22830
+ because.push(`${agent.template} routes work rather than performing it`);
22962
22831
  }
22963
- const renameMap = buildRenameMap(
22964
- subsystemId,
22965
- /* externalize */
22966
- true
22832
+ const stereotype = agent.tags.find(
22833
+ (t) => MECHANICAL_STEREOTYPES.has(t) || DEEP_STEREOTYPES.has(t)
22967
22834
  );
22968
- const childSystemName = foo.name || subsystemId;
22969
- runWithProjectRoot(childDir, () => {
22970
- const now = (/* @__PURE__ */ new Date()).toISOString();
22971
- projectConfigRepository.create(defaultProjectConfig(childSystemName, now));
22972
- ensureDir(path20.join(childDir, ".wai", "specs"));
22973
- saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
22974
- });
22975
- ensureDir(path20.dirname(childFooDir));
22976
- fs15.renameSync(fooDir, childFooDir);
22977
- rebaseImplementationPaths(childFooDir, parentRoot, childDir);
22978
- patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
22979
- s.parentSystem = childSystemName;
22980
- delete s.projectPath;
22981
- });
22982
- ensureDir(fooDir);
22983
- writeYamlFile(path20.join(fooDir, ".index.yaml"), {
22984
- id: subsystemId,
22985
- name: foo.name,
22986
- description: foo.description,
22987
- parentSystem: foo.parentSystem,
22988
- publicInterfaces: [],
22989
- projectPath: relPath,
22990
- trustedLinks: [],
22991
- status: foo.status ?? "draft",
22992
- createdAt: foo.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
22993
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22994
- });
22995
- rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22996
- rebaseMovedRefs(childFooDir, subsystemId, "into");
22997
- invalidateSpecCache();
22998
- }
22999
- function internalizeSubsystem(subsystemId) {
23000
- if (subsystemId.includes("::")) {
23001
- throw new WaironError("cannot internalize a nested/namespaced subsystem; run from its owning project.");
23002
- }
23003
- const foo = loadSubsystemSpec(subsystemId);
23004
- if (!foo || !foo.projectPath) {
23005
- throw new WaironError(`cannot internalize: subsystem "${subsystemId}" is missing or not external.`);
23006
- }
23007
- const parentRoot = getProjectRoot();
23008
- const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
23009
- const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
23010
- const childWai = path20.join(childDir, ".wai");
23011
- const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
23012
- if (!fs15.existsSync(childFooDir)) {
23013
- throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
23014
- }
23015
- const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
23016
- if (childOwnSubs.length !== 1 || childOwnSubs[0].id !== subsystemId) {
23017
- throw new WaironError(`cannot internalize: subproject is a multi-subsystem system, not a flat "${subsystemId}".`);
22835
+ if (stereotype) {
22836
+ because.push(
22837
+ MECHANICAL_STEREOTYPES.has(stereotype) ? `${stereotype} work is specified by its contract and narrative` : `${stereotype} carries decision logic`
22838
+ );
23018
22839
  }
23019
- const renameMap = buildRenameMap(
23020
- subsystemId,
23021
- /* externalize */
23022
- false
22840
+ because.push(
22841
+ breadth === "wide" ? "reads broadly across the tree" : breadth === "moderate" ? `spans ${agent.ownedPaths.length} owned path(s)` : "scoped to a small owned set"
23023
22842
  );
23024
- const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
23025
- const fooDir = path20.join(parentSpecsDir, subsystemId);
23026
- fs15.rmSync(fooDir, { recursive: true, force: true });
23027
- ensureDir(path20.dirname(fooDir));
23028
- fs15.renameSync(childFooDir, fooDir);
23029
- rebaseImplementationPaths(fooDir, childDir, parentRoot);
23030
- patchSubsystemIndex(path20.join(fooDir, ".index.yaml"), (s) => {
23031
- s.parentSystem = parentSystemName;
23032
- delete s.projectPath;
23033
- });
23034
- fs15.rmSync(childWai, { recursive: true, force: true });
23035
- rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
23036
- rebaseMovedRefs(fooDir, subsystemId, "outOf");
23037
- invalidateSpecCache();
22843
+ if (!writes) because.push("read-only");
22844
+ return {
22845
+ breadth,
22846
+ writes,
22847
+ reasoningDepth,
22848
+ delegates,
22849
+ rationale: because.join("; ")
22850
+ };
23038
22851
  }
23039
- function buildRenameMap(subsystemId, externalize) {
23040
- const map = /* @__PURE__ */ new Map();
23041
- const prefix = `${subsystemId}::`;
23042
- const add = (bare, namespaced) => externalize ? map.set(bare, namespaced) : map.set(namespaced, bare);
23043
- const comps = loadComponentSpecs().filter((c) => c.subsystem === subsystemId);
23044
- const compIds = new Set(comps.map((c) => c.id));
23045
- for (const c of comps) {
23046
- const bare = c.id.startsWith(prefix) ? c.id.slice(prefix.length) : c.id;
23047
- add(bare, `${prefix}${bare}`);
22852
+ var MECHANICAL_STEREOTYPES, DEEP_STEREOTYPES, MANAGER_TEMPLATES, READ_ONLY_TEMPLATES, WIDE_PATH_COUNT, MODERATE_PATH_COUNT;
22853
+ var init_execution_profile = __esm({
22854
+ "src/core/execution_profile.ts"() {
22855
+ "use strict";
22856
+ MECHANICAL_STEREOTYPES = /* @__PURE__ */ new Set(["store", "index", "registry", "adapter"]);
22857
+ DEEP_STEREOTYPES = /* @__PURE__ */ new Set(["orchestrator", "supervisor", "specialist"]);
22858
+ MANAGER_TEMPLATES = /* @__PURE__ */ new Set(["architect", "domain-owner"]);
22859
+ READ_ONLY_TEMPLATES = /* @__PURE__ */ new Set(["reviewer", "guardian"]);
22860
+ WIDE_PATH_COUNT = 12;
22861
+ MODERATE_PATH_COUNT = 4;
23048
22862
  }
23049
- for (const i of loadInterfaceSpecs()) {
23050
- if (!compIds.has(i.component)) continue;
23051
- const bare = i.id.startsWith(prefix) ? i.id.slice(prefix.length) : i.id;
23052
- add(bare, `${prefix}${bare}`);
22863
+ });
22864
+
22865
+ // src/core/budget_policy.ts
22866
+ function atLeast(tier, floor) {
22867
+ return TIER_ORDER.indexOf(tier) >= TIER_ORDER.indexOf(floor);
22868
+ }
22869
+ function baseModelTier(profile) {
22870
+ switch (profile.reasoningDepth) {
22871
+ case "mechanical":
22872
+ return "small";
22873
+ case "standard":
22874
+ return "standard";
22875
+ case "deep":
22876
+ return "large";
23053
22877
  }
23054
- for (const t of loadTypeSpecs()) {
23055
- if (t.subsystem !== subsystemId) continue;
23056
- const bare = t.id.startsWith(prefix) ? t.id.slice(prefix.length) : t.id;
23057
- add(bare, `${prefix}${bare}`);
22878
+ }
22879
+ function stepDown(tier, steps) {
22880
+ const i = TIER_STEPS.indexOf(tier);
22881
+ return TIER_STEPS[Math.max(0, i - steps)];
22882
+ }
22883
+ function maxTurnsFor(profile, tier) {
22884
+ if (!atLeast(tier, "default")) return void 0;
22885
+ const ceiling = profile.breadth === "wide" ? 60 : profile.breadth === "moderate" ? 40 : 25;
22886
+ return atLeast(tier, "aggressive") ? Math.round(ceiling / 2) : ceiling;
22887
+ }
22888
+ function effortFor(profile, tier) {
22889
+ if (!atLeast(tier, "trade")) return void 0;
22890
+ if (profile.reasoningDepth === "mechanical") {
22891
+ return atLeast(tier, "aggressive") ? "low" : "medium";
23058
22892
  }
23059
- return map;
22893
+ return void 0;
23060
22894
  }
23061
- function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
23062
- if (renameMap.size === 0) return;
23063
- rewriteRefFields(specsDir, (ref) => renameMap.get(ref) ?? ref, excludeDir);
22895
+ function toolClassFor(profile) {
22896
+ return profile.writes ? "implement" : "read-only";
23064
22897
  }
23065
- function rebaseMovedRefs(movedDir, mount, direction) {
23066
- const declared = componentIdsUnder(movedDir);
23067
- rewriteRefFields(movedDir, (ref, position) => position === "component" ? rebaseReference(ref, mount, direction, (id) => declared.has(id)) : ref);
22898
+ function mcpFor(profile, tier) {
22899
+ if (!atLeast(tier, "free")) return "all";
22900
+ if (profile.delegates) return "project";
22901
+ return profile.breadth === "wide" ? "project" : "none";
23068
22902
  }
23069
- function componentIdsUnder(dir) {
23070
- const ids = /* @__PURE__ */ new Set();
23071
- for (const file of listFilesRecursive(dir, ".yaml")) {
23072
- let raw;
23073
- try {
23074
- raw = readYamlFile(file);
23075
- } catch {
23076
- continue;
22903
+ function resolveBudget(profile, config, agentId) {
22904
+ const tier = config.tier;
22905
+ if (tier === "off") return void 0;
22906
+ let modelTier = baseModelTier(profile);
22907
+ if (atLeast(tier, "trade") && profile.reasoningDepth === "standard") {
22908
+ modelTier = stepDown(modelTier, 1);
22909
+ }
22910
+ if (atLeast(tier, "aggressive") && profile.reasoningDepth !== "deep") {
22911
+ modelTier = "small";
22912
+ }
22913
+ const budget = {
22914
+ // At `free` no model selection is expressed at all — that tier is defined
22915
+ // as having no quality tradeoff, and choosing a model is a quality
22916
+ // decision. Leaving it absent is not the same as choosing a default.
22917
+ modelTier: atLeast(tier, "default") ? modelTier : void 0,
22918
+ effort: effortFor(profile, tier),
22919
+ maxTurns: maxTurnsFor(profile, tier),
22920
+ toolClass: toolClassFor(profile),
22921
+ // Only managers may spawn. Withholding the tool from workers is what stops
22922
+ // a worker quietly becoming a second orchestrator three levels down.
22923
+ allowNestedDelegation: profile.delegates,
22924
+ mcp: mcpFor(profile, tier)
22925
+ };
22926
+ const override = config.overrides[agentId];
22927
+ return override ? { ...budget, ...override } : budget;
22928
+ }
22929
+ var TIER_ORDER, TIER_STEPS;
22930
+ var init_budget_policy = __esm({
22931
+ "src/core/budget_policy.ts"() {
22932
+ "use strict";
22933
+ TIER_ORDER = ["off", "free", "default", "trade", "aggressive"];
22934
+ TIER_STEPS = ["small", "standard", "large", "frontier"];
22935
+ }
22936
+ });
22937
+
22938
+ // src/core/agent_resolver.ts
22939
+ function listFilesRecursiveSafe(dirPath, ext) {
22940
+ if (!fs15.existsSync(dirPath)) return [];
22941
+ const nameLower = path20.basename(dirPath).toLowerCase();
22942
+ const IGNORED_DIRS = /* @__PURE__ */ new Set([
22943
+ "node_modules",
22944
+ "target",
22945
+ "dist",
22946
+ "build",
22947
+ ".git",
22948
+ ".wai",
22949
+ ".claude",
22950
+ ".gemini",
22951
+ ".codex",
22952
+ ".agents",
22953
+ ".vscode"
22954
+ ]);
22955
+ if (IGNORED_DIRS.has(nameLower)) return [];
22956
+ const entries = fs15.readdirSync(dirPath, { withFileTypes: true });
22957
+ const files = [];
22958
+ for (const entry of entries) {
22959
+ const fullPath = path20.join(dirPath, entry.name);
22960
+ if (entry.isDirectory()) {
22961
+ files.push(...listFilesRecursiveSafe(fullPath, ext));
22962
+ } else if (entry.isFile() && entry.name.endsWith(ext)) {
22963
+ files.push(fullPath);
23077
22964
  }
23078
- if (raw && typeof raw === "object" && "componentType" in raw && typeof raw.id === "string") ids.add(raw.id);
23079
22965
  }
23080
- return ids;
22966
+ return files;
23081
22967
  }
23082
- function rewriteRefFields(specsDir, remap, excludeDir) {
23083
- const rewritten = [];
23084
- for (const file of listFilesRecursive(specsDir, ".yaml")) {
23085
- if (excludeDir && isWithinDir(excludeDir, file)) continue;
23086
- let raw;
23087
- try {
23088
- raw = readYamlFile(file);
23089
- } catch {
23090
- continue;
22968
+ function getProjectFiles(projectDir) {
22969
+ let files = projectFilesCache.get(projectDir);
22970
+ if (!files) {
22971
+ files = [];
22972
+ const srcDir = path20.join(projectDir, "src");
22973
+ const legacySrcDir = path20.join(projectDir, "legacy-src");
22974
+ let searchDir = projectDir;
22975
+ if (pathExists(srcDir)) {
22976
+ searchDir = srcDir;
22977
+ } else if (pathExists(legacySrcDir)) {
22978
+ searchDir = legacySrcDir;
22979
+ } else if (projectDir === getProjectRoot()) {
22980
+ projectFilesCache.set(projectDir, []);
22981
+ return [];
23091
22982
  }
23092
- if (!raw || typeof raw !== "object") continue;
23093
- const kind = specKind(raw);
23094
- if (!rewriteSpecRefs(raw, remap) || !kind) continue;
23095
- writeYamlFile(file, raw);
23096
- rewritten.push({ kind, id: kind === "system" ? "system" : String(raw.id) });
22983
+ const extensions = [".rs", ".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".c", ".cpp", ".cs", ".java", ".kt", ".swift", ".rb", ".php", ".lua"];
22984
+ for (const ext of extensions) {
22985
+ files.push(...listFilesRecursiveSafe(searchDir, ext));
22986
+ }
22987
+ const rootDir = getProjectRoot();
22988
+ files = files.map((f) => path20.relative(rootDir, f).replace(/\\/g, "/"));
22989
+ projectFilesCache.set(projectDir, files);
23097
22990
  }
23098
- return rewritten;
22991
+ return files;
23099
22992
  }
23100
- function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
23101
- for (const file of listFilesRecursive(specsDir, ".yaml")) {
23102
- let raw;
23103
- try {
23104
- raw = readYamlFile(file);
23105
- } catch {
23106
- continue;
22993
+ function inferSourcePathForComponent(comp, subsystems) {
22994
+ try {
22995
+ const projectDir = resolveSubprojectForNamespace(comp.subsystem) || getProjectRoot();
22996
+ const files = getProjectFiles(projectDir);
22997
+ if (files.length === 0) return null;
22998
+ const compRelativeId = comp.id.split("::").pop() || comp.id;
22999
+ const subRelativeId = comp.subsystem.split("::").pop() || comp.subsystem;
23000
+ let cleanName = compRelativeId;
23001
+ if (cleanName.startsWith(`${subRelativeId}-`)) {
23002
+ cleanName = cleanName.slice(subRelativeId.length + 1);
23107
23003
  }
23108
- if (!raw || typeof raw !== "object" || !("contract" in raw)) continue;
23109
- let changed = false;
23110
- const rebase = (holder, key) => {
23111
- const p = holder[key];
23112
- if (typeof p !== "string" || p === "" || path20.isAbsolute(p)) return;
23113
- const next = toPosixPath(path20.relative(toRoot, path20.resolve(fromRoot, p)));
23114
- if (next !== p) {
23115
- holder[key] = next;
23116
- changed = true;
23117
- }
23118
- };
23119
- for (const key of ["sourcePath", "simPath"]) rebase(raw, key);
23120
- if (Array.isArray(raw.methods)) {
23121
- for (const method of raw.methods) {
23122
- if (method && typeof method === "object") rebase(method, "sourcePath");
23004
+ const candidates = /* @__PURE__ */ new Set();
23005
+ candidates.add(cleanName.toLowerCase());
23006
+ candidates.add(cleanName.replace(/-/g, "_").toLowerCase());
23007
+ candidates.add(compRelativeId.toLowerCase());
23008
+ candidates.add(compRelativeId.replace(/-/g, "_").toLowerCase());
23009
+ candidates.add(comp.componentType.toLowerCase());
23010
+ let bestFile = null;
23011
+ let bestScore = -1;
23012
+ for (const f of files) {
23013
+ const ext = path20.extname(f);
23014
+ const base = path20.basename(f, ext).toLowerCase();
23015
+ if (candidates.has(base)) {
23016
+ let score = 0;
23017
+ const normalizedPath = f.toLowerCase();
23018
+ const subPattern1 = `/${subRelativeId.toLowerCase()}/`;
23019
+ const subPattern2 = `/${subRelativeId.replace(/-/g, "_").toLowerCase()}/`;
23020
+ const hasSubsystemSegmentMatch = subRelativeId.split(/[-_]/).some((seg) => seg.length >= 3 && normalizedPath.includes(`/${seg.toLowerCase()}/`));
23021
+ const hasSubsystemMatch = normalizedPath.includes(subPattern1) || normalizedPath.includes(subPattern2) || hasSubsystemSegmentMatch;
23022
+ if (hasSubsystemMatch) {
23023
+ score += 10;
23024
+ }
23025
+ let belongsToOtherSubsystem = false;
23026
+ for (const otherSub of subsystems) {
23027
+ if (otherSub.id === comp.subsystem) continue;
23028
+ const otherSubRelativeId = otherSub.id.split("::").pop() || otherSub.id;
23029
+ const otherPattern1 = `/${otherSubRelativeId.toLowerCase()}/`;
23030
+ const otherPattern2 = `/${otherSubRelativeId.replace(/-/g, "_").toLowerCase()}/`;
23031
+ const otherSegmentMatch = otherSubRelativeId.split(/[-_]/).some((seg) => seg.length >= 3 && normalizedPath.includes(`/${seg.toLowerCase()}/`));
23032
+ if (normalizedPath.includes(otherPattern1) || normalizedPath.includes(otherPattern2) || otherSegmentMatch) {
23033
+ belongsToOtherSubsystem = true;
23034
+ break;
23035
+ }
23036
+ }
23037
+ if (belongsToOtherSubsystem) {
23038
+ continue;
23039
+ }
23040
+ const isCleanNameMatch = base === cleanName.toLowerCase() || base === cleanName.replace(/-/g, "_").toLowerCase();
23041
+ if (isCleanNameMatch) {
23042
+ score += 5;
23043
+ }
23044
+ const isIdMatch = base === compRelativeId.toLowerCase() || base === compRelativeId.replace(/-/g, "_").toLowerCase();
23045
+ if (isIdMatch) {
23046
+ score += 3;
23047
+ }
23048
+ const isSpecificNameMatch = isCleanNameMatch && cleanName.toLowerCase() !== comp.componentType.toLowerCase();
23049
+ if (!hasSubsystemMatch && !isSpecificNameMatch && !isIdMatch) {
23050
+ continue;
23051
+ }
23052
+ if (normalizedPath.startsWith("src/") || normalizedPath.includes("/src/") || normalizedPath.startsWith("legacy-src/") || normalizedPath.includes("/legacy-src/")) {
23053
+ score += 2;
23054
+ }
23055
+ if (base === comp.componentType.toLowerCase()) {
23056
+ score += 1;
23057
+ }
23058
+ if (score > bestScore) {
23059
+ bestScore = score;
23060
+ bestFile = f;
23061
+ }
23123
23062
  }
23124
23063
  }
23125
- if (changed) writeYamlFile(file, raw);
23064
+ return bestFile;
23065
+ } catch {
23066
+ return null;
23126
23067
  }
23127
23068
  }
23128
- function patchSubsystemIndex(indexPath, mutate) {
23129
- if (!fs15.existsSync(indexPath)) return;
23130
- const raw = readYamlFile(indexPath);
23131
- mutate(raw);
23132
- raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23133
- writeYamlFile(indexPath, raw);
23069
+ function summarize(text, max = 140) {
23070
+ const clean = text.replace(/\s+/g, " ").trim();
23071
+ const period = clean.indexOf(". ");
23072
+ const firstSentence = period > 0 ? clean.slice(0, period + 1) : clean;
23073
+ if (firstSentence.length <= max) return firstSentence;
23074
+ return `${firstSentence.slice(0, max - 1).trimEnd()}\u2026`;
23134
23075
  }
23135
- function renameComponent(componentId, newId) {
23136
- const component = loadComponentSpec(componentId);
23137
- if (!component) {
23138
- throw new WaironError(`component-missing: no component has the id "${componentId}".`);
23139
- }
23140
- if (componentId.includes("::")) {
23141
- throw new WaironError(
23142
- `chained-component: "${componentId}" lives in a chained subproject; rename it from that project's own root.`
23143
- );
23144
- }
23145
- if (!SpecIdSchema.safeParse(newId).success) {
23146
- throw new WaironError(`invalid-id: "${newId}" is not a lowercase identifier with no namespace separator.`);
23147
- }
23148
- const components = loadComponentSpecs();
23076
+ function buildVariantGuidance(comps, allComponents, variantsById) {
23077
+ return composeVariantGuidance(comps, allComponents, variantsById);
23078
+ }
23079
+ function resolveAgentTopology() {
23080
+ projectFilesCache.clear();
23081
+ const system = loadSystemSpec();
23082
+ if (!system) return [];
23083
+ const isLocal = (id) => !id.includes("::");
23084
+ const subsystems = loadSubsystemSpecs().filter((s) => isLocal(s.id));
23085
+ const components = loadComponentSpecs().filter((c) => isLocal(c.id) && isLocal(c.subsystem));
23149
23086
  const interfaces = loadInterfaceSpecs();
23150
23087
  const implementations = loadImplementationSpecs();
23151
- const interfaceId = `i${newId}`;
23152
- const implementationId = `${newId}_impl`;
23153
- const taken = [
23154
- components.some((c) => c.id === newId) ? `component "${newId}"` : null,
23155
- interfaces.some((i) => i.id === interfaceId) ? `interface "${interfaceId}"` : null,
23156
- implementations.some((impl) => impl.id === implementationId) ? `implementation "${implementationId}"` : null
23157
- ].filter((holder) => holder !== null);
23158
- if (taken.length > 0) {
23159
- throw new WaironError(`id-taken: the new id, or an id it implies, is already in use: ${taken.join(", ")}.`);
23160
- }
23161
- const contracts = interfaces.filter((i) => i.component === componentId);
23162
- const movingInterface = contracts.find((i) => i.id === `i${componentId}`);
23163
- const movingImplementation = implementations.find((impl) => impl.id === `${componentId}_impl` && contracts.some((i) => i.id === impl.contract));
23164
- const renamed = [
23165
- { kind: "component", from: componentId, to: newId },
23166
- ...movingInterface ? [{ kind: "interface", from: movingInterface.id, to: interfaceId }] : [],
23167
- ...movingImplementation ? [{ kind: "implementation", from: movingImplementation.id, to: implementationId }] : []
23168
- ];
23169
- const rewritten = rewriteRefFields(aiPathsAt(getProjectRoot()).specsDir(), (ref, position) => {
23170
- if (position === "component" || position === "entity-class" || position === "auth-source") {
23171
- return ref === componentId ? newId : ref;
23088
+ const variantsById = new Map(loadProjectVariants().map((v) => [v.id, v]));
23089
+ const config = projectConfigRepository.load();
23090
+ if (!config) throw new ProjectNotInitializedError();
23091
+ const activeTargets = config.targets.filter((t) => !("enabled" in t) || t.enabled).map((t) => typeof t === "string" ? t : t.type);
23092
+ const agents = [];
23093
+ agents.push({
23094
+ id: "system-architect",
23095
+ name: `${system.name} Architect`,
23096
+ description: `Global architect for ${system.name} \u2014 owns the spec tree and topology. ${summarize(system.vision)}`,
23097
+ template: "architect",
23098
+ creationReason: "Automatically inferred from L0 system spec",
23099
+ ownedPaths: [".wai/specs/**"],
23100
+ readPaths: ["**"],
23101
+ writePaths: [".wai/specs/**"],
23102
+ tags: ["architect", "global", "sdd"],
23103
+ dependencies: subsystems.map((s) => `${s.id}-owner`),
23104
+ status: "active",
23105
+ targets: activeTargets,
23106
+ createdAt: system.createdAt,
23107
+ updatedAt: system.updatedAt
23108
+ });
23109
+ for (const sub of subsystems) {
23110
+ if (sub.projectPath) {
23111
+ const mountSpecPath = path20.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
23112
+ agents.push({
23113
+ id: `${sub.id}-owner`,
23114
+ name: `${sub.name} (chained subproject)`,
23115
+ description: `Delegates into the "${sub.id}" chained subproject at ${sub.projectPath}. Its own agents live in that subproject's .wai \u2014 run \`wairon generate\` there (or spawn from ${sub.projectPath}/.claude/agents). Do not implement its internals from this layer.`,
23116
+ template: "domain-owner",
23117
+ creationReason: `Automatically inferred from a chained subproject subsystem: ${sub.id}`,
23118
+ domainRoot: sub.id,
23119
+ ownedPaths: [mountSpecPath],
23120
+ readPaths: ["**"],
23121
+ writePaths: [mountSpecPath],
23122
+ tags: ["owner", "subproject", "delegate", "sdd"],
23123
+ dependencies: [],
23124
+ status: "active",
23125
+ targets: activeTargets,
23126
+ createdAt: sub.createdAt,
23127
+ updatedAt: sub.updatedAt
23128
+ });
23129
+ continue;
23130
+ }
23131
+ const subComponents = components.filter((c) => c.subsystem === sub.id);
23132
+ const ownedPaths = [];
23133
+ ownedPaths.push(path20.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
23134
+ for (const c of subComponents) {
23135
+ ownedPaths.push(path20.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
23136
+ }
23137
+ if (!config.rules.generateComponentImplementers) {
23138
+ for (const comp of subComponents) {
23139
+ const compInterfaces = interfaces.filter((i) => i.component === comp.id);
23140
+ const compInterfaceIds = compInterfaces.map((i) => i.id);
23141
+ const compImpls = implementations.filter((impl) => compInterfaceIds.includes(impl.contract));
23142
+ let hasExplicitSource = false;
23143
+ for (const impl of compImpls) {
23144
+ const files = implementationSourceFiles(impl);
23145
+ if (files.length > 0) hasExplicitSource = true;
23146
+ for (const file of files) {
23147
+ if (!ownedPaths.includes(file)) ownedPaths.push(file);
23148
+ }
23149
+ }
23150
+ if (!hasExplicitSource) {
23151
+ const inferred = inferSourcePathForComponent(comp, subsystems);
23152
+ if (inferred && !ownedPaths.includes(inferred)) {
23153
+ ownedPaths.push(inferred);
23154
+ }
23155
+ }
23156
+ }
23172
23157
  }
23173
- if (position === "interface" && ref === movingInterface?.id) return interfaceId;
23174
- return ref;
23175
- }).filter((spec) => !renamed.some((moved) => moved.kind === spec.kind && moved.from === spec.id)).map((spec) => spec.id);
23176
- invalidateSpecCache();
23177
- moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation);
23178
- return { renamed, rewritten };
23179
- }
23180
- function moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation) {
23181
- const interfaceId = `i${newId}`;
23182
- const implementationId = `${newId}_impl`;
23183
- const movedComponent = loadComponentSpec(componentId) ?? component;
23184
- const movedInterface = movingInterface ? loadInterfaceSpec(movingInterface.id) ?? movingInterface : null;
23185
- const movedImplementation = movingImplementation ? loadImplementationSpec(movingImplementation.id) ?? movingImplementation : null;
23186
- saveComponentSpec({ ...movedComponent, id: newId });
23187
- if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
23188
- if (movedImplementation) {
23189
- const oldFile = getImplementationPath(movedImplementation.id);
23190
- if (path20.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path20.resolve(oldFile)) {
23191
- fs15.unlinkSync(oldFile);
23158
+ let dependencies = [];
23159
+ if (config.rules.generateComponentImplementers) {
23160
+ dependencies = subComponents.map((c) => `${c.id}-implementer`);
23161
+ } else {
23162
+ const depSubsystems = /* @__PURE__ */ new Set();
23163
+ for (const c of subComponents) {
23164
+ for (const depId of c.dependsOn) {
23165
+ const depComp = components.find((other) => other.id === depId);
23166
+ if (depComp && depComp.subsystem !== sub.id) {
23167
+ depSubsystems.add(`${depComp.subsystem}-owner`);
23168
+ }
23169
+ }
23170
+ }
23171
+ dependencies = Array.from(depSubsystems);
23192
23172
  }
23193
- saveImplementationSpec({ ...movedImplementation, id: implementationId });
23194
- }
23195
- removeSpecFile2(getComponentPath(componentId), componentId);
23196
- if (movingInterface) removeSpecFile2(getInterfacePath(movingInterface.id), movingInterface.id);
23197
- if (movingImplementation) removeSpecFile2(getImplementationPath(movingImplementation.id), movingImplementation.id);
23198
- invalidateSpecCache();
23199
- }
23200
- function removeSpecFile2(file, id) {
23201
- if (!fs15.existsSync(file)) return;
23202
- try {
23203
- if (readYamlFile(file)?.id !== id) return;
23204
- } catch {
23205
- return;
23206
- }
23207
- fs15.unlinkSync(file);
23208
- const specsRoot = path20.resolve(aiPathsAt(getProjectRoot()).specsDir());
23209
- for (let dir = path20.dirname(path20.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path20.dirname(dir)) {
23210
- if (fs15.readdirSync(dir).length > 0) break;
23211
- fs15.rmdirSync(dir);
23212
- }
23213
- }
23214
- function renameMethod(componentId, methodName, newName, pinSymbol) {
23215
- const component = loadComponentSpec(componentId);
23216
- if (!component) {
23217
- throw new WaironError(`component-missing: no component has the id "${componentId}".`);
23218
- }
23219
- if (componentId.includes("::")) {
23220
- throw new WaironError(
23221
- `chained-component: "${componentId}" lives in a chained subproject; rename its method from that project's own root.`
23222
- );
23223
- }
23224
- if (!METHOD_NAME.test(newName)) {
23225
- throw new WaironError(`invalid-name: "${newName}" is not a camel-case identifier.`);
23226
- }
23227
- const contracts = loadInterfaceSpecs().filter((i) => i.component === componentId);
23228
- const moving = contracts.filter((i) => i.methods.some((m) => m.name === methodName));
23229
- if (moving.length === 0) {
23230
- throw new WaironError(`method-missing: no contract of "${componentId}" declares the method "${methodName}".`);
23231
- }
23232
- const taken = moving.filter((i) => i.methods.some((m) => m.name === newName)).map((i) => `"${i.id}"`);
23233
- if (taken.length > 0) {
23234
- throw new WaironError(`name-taken: ${taken.join(", ")} already declares a method under the name "${newName}".`);
23235
- }
23236
- const implementations = loadImplementationSpecs();
23237
- const movingContracts = new Set(moving.map((i) => i.id));
23238
- const renamed = [];
23239
- for (const contract of moving) {
23240
- saveInterfaceSpec({
23241
- ...contract,
23242
- methods: contract.methods.map((m) => m.name === methodName ? { ...m, name: newName, signature: renameInSignature(m.signature, methodName, newName) } : m)
23173
+ agents.push({
23174
+ id: `${sub.id}-owner`,
23175
+ name: `${sub.name} Owner`,
23176
+ description: `Owns the ${sub.id} subsystem. ${summarize(sub.description)}`,
23177
+ template: "domain-owner",
23178
+ creationReason: `Automatically inferred from L1 subsystem spec: ${sub.id}`,
23179
+ domainRoot: sub.id,
23180
+ ownedPaths,
23181
+ readPaths: ["**"],
23182
+ writePaths: ownedPaths,
23183
+ tags: ["owner", "domain", "sdd"],
23184
+ dependencies,
23185
+ variantGuidance: buildVariantGuidance(subComponents, components, variantsById),
23186
+ status: "active",
23187
+ targets: activeTargets,
23188
+ createdAt: sub.createdAt,
23189
+ updatedAt: sub.updatedAt
23243
23190
  });
23244
- renamed.push(contract.id);
23245
23191
  }
23246
- let pinnedSymbol;
23247
- for (const implementation of implementations) {
23248
- if (!movingContracts.has(implementation.contract)) continue;
23249
- const realization = implementation.methods.find((m) => m.name === methodName);
23250
- if (!realization) continue;
23251
- const pin = realization.symbol === void 0 && pinSymbol !== false;
23252
- if (pin) pinnedSymbol = methodName;
23253
- saveImplementationSpec({
23254
- ...implementation,
23255
- methods: implementation.methods.map((m) => m.name === methodName ? { ...m, name: newName, ...pin ? { symbol: methodName } : {} } : m)
23192
+ if (config.rules.generateComponentImplementers) {
23193
+ for (const comp of components) {
23194
+ const compInterfaces = interfaces.filter((i) => i.component === comp.id);
23195
+ const compInterfaceIds = compInterfaces.map((i) => i.id);
23196
+ const compImpls = implementations.filter((impl) => compInterfaceIds.includes(impl.contract));
23197
+ const ownedPaths = [];
23198
+ for (const impl of compImpls) {
23199
+ for (const file of implementationSourceFiles(impl)) {
23200
+ if (!ownedPaths.includes(file)) ownedPaths.push(file);
23201
+ }
23202
+ }
23203
+ if (ownedPaths.length === 0) {
23204
+ const inferred = inferSourcePathForComponent(comp, subsystems);
23205
+ if (inferred) {
23206
+ ownedPaths.push(inferred);
23207
+ }
23208
+ }
23209
+ const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
23210
+ const readPaths = [
23211
+ path20.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
23212
+ path20.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
23213
+ ...compInterfaces.map((i) => path20.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
23214
+ ...compImpls.map((impl) => path20.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
23215
+ ];
23216
+ agents.push({
23217
+ id: `${comp.id}-implementer`,
23218
+ name: `${comp.name} Implementer`,
23219
+ description: `Developer agent implementing ${comp.name} (${comp.componentType})`,
23220
+ template: "implementer",
23221
+ creationReason: `Automatically inferred from L2 component spec: ${comp.id}`,
23222
+ domainRoot: comp.subsystem,
23223
+ ownedPaths,
23224
+ readPaths,
23225
+ writePaths: ownedPaths,
23226
+ tags: ["implementer", "component", "sdd", comp.componentType.toLowerCase()],
23227
+ dependencies,
23228
+ variantGuidance: buildVariantGuidance([comp], components, variantsById),
23229
+ status: "active",
23230
+ targets: activeTargets,
23231
+ createdAt: comp.createdAt,
23232
+ updatedAt: comp.updatedAt
23233
+ });
23234
+ }
23235
+ }
23236
+ const now = (/* @__PURE__ */ new Date()).toISOString();
23237
+ for (const dom of loadTopologyConfig().domains) {
23238
+ agents.push({
23239
+ id: `${dom.id}-owner`,
23240
+ name: `${dom.name ?? dom.id} Owner`,
23241
+ description: dom.description ? summarize(dom.description) : `Owner agent for the free-standing "${dom.id}" domain.`,
23242
+ template: "domain-owner",
23243
+ creationReason: "Inferred from a free-standing domain in .wai/topology.yaml",
23244
+ domainRoot: dom.id,
23245
+ ownedPaths: dom.ownedPaths,
23246
+ readPaths: ["**"],
23247
+ writePaths: dom.ownedPaths,
23248
+ tags: ["owner", "domain"],
23249
+ dependencies: [],
23250
+ status: "active",
23251
+ targets: activeTargets,
23252
+ createdAt: now,
23253
+ updatedAt: now
23256
23254
  });
23257
- renamed.push(implementation.id);
23258
23255
  }
23259
- invalidateSpecCache();
23260
- const specsDir = aiPathsAt(getProjectRoot()).specsDir();
23261
- const rewritten = rewriteRefFields(specsDir, (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref).map((spec) => spec.id);
23262
- const mentions = collectMentions(specsDir, methodName, moving);
23263
- return {
23264
- component: componentId,
23265
- from: methodName,
23266
- to: newName,
23267
- renamed,
23268
- rewritten,
23269
- mentions,
23270
- ...pinnedSymbol ? { pinnedSymbol } : {}
23271
- };
23272
- }
23273
- function renameInSignature(signature, from, to) {
23274
- return signature.replace(identifierPattern(from), to);
23256
+ return agents;
23275
23257
  }
23276
- function collectMentions(specsDir, methodName, moved) {
23277
- const mentions = /* @__PURE__ */ new Set();
23278
- for (const contract of moved) {
23279
- const wireBound = contract.methods.some((m) => {
23280
- const endpoint = m.endpoint;
23281
- return endpoint?.transport === "gRPC" && endpoint.method === methodName;
23282
- });
23283
- if (wireBound) mentions.add(contract.id);
23258
+ function composeAgentBrief(agentId) {
23259
+ const records = resolveAgentTopology();
23260
+ const record = records.find((r) => r.id === agentId);
23261
+ if (!record) {
23262
+ throw new UnknownAgentError(agentId, records.map((r) => r.id));
23284
23263
  }
23285
- const named = identifierPattern(methodName);
23286
- const namesMethod = (value) => {
23287
- if (Array.isArray(value)) return value.some(namesMethod);
23288
- if (!value || typeof value !== "object") return false;
23289
- return Object.entries(value).some(([key, nested]) => typeof nested === "string" ? PROSE_FIELDS2.has(key) && named.test(nested) : namesMethod(nested));
23290
- };
23291
- for (const file of listFilesRecursive(specsDir, ".yaml")) {
23292
- let raw;
23293
- try {
23294
- raw = readYamlFile(file);
23295
- } catch {
23296
- continue;
23297
- }
23298
- if (!raw || typeof raw !== "object") continue;
23299
- const kind = specKind(raw);
23300
- if (!kind || !namesMethod(raw)) continue;
23301
- mentions.add(kind === "system" ? "system" : String(raw.id));
23264
+ const config = projectConfigRepository.load();
23265
+ if (!config) throw new ProjectNotInitializedError();
23266
+ const template = loadTemplate(record.template, config.globalTemplatesDir);
23267
+ let instructions = renderTemplateInstructions(template, {
23268
+ agentId: record.id,
23269
+ agentName: record.name,
23270
+ agentDescription: record.description,
23271
+ ownedPaths: record.ownedPaths.join("\n"),
23272
+ tags: record.tags.join(", "),
23273
+ renderContext: "root",
23274
+ contextNote: "",
23275
+ domainPath: ".",
23276
+ domainName: "",
23277
+ variantGuidance: record.variantGuidance ?? ""
23278
+ });
23279
+ const guidance = loadAgentOverride(agentId);
23280
+ if (guidance !== null) {
23281
+ instructions = `${instructions.trimEnd()}
23282
+
23283
+ ## Project guidance
23284
+
23285
+ ${guidance.trim()}
23286
+ `;
23302
23287
  }
23303
- return [...mentions];
23288
+ const profile = deriveExecutionProfile(record);
23289
+ const budget = resolveBudget(profile, config.execution, record.id);
23290
+ return {
23291
+ agentId: record.id,
23292
+ name: record.name,
23293
+ template: record.template,
23294
+ domainRoot: record.domainRoot,
23295
+ ownedPaths: record.ownedPaths,
23296
+ readPaths: record.readPaths,
23297
+ instructions,
23298
+ variantGuidance: record.variantGuidance || void 0,
23299
+ profile: budget ? profile : void 0,
23300
+ budget
23301
+ };
23304
23302
  }
23305
- function identifierPattern(methodName) {
23306
- return new RegExp(`(?<![A-Za-z0-9_])${methodName}(?![A-Za-z0-9_])`);
23303
+ function loadRegistry() {
23304
+ assertProjectInitialized();
23305
+ if (!pathExists(AI_PATHS.specsSystem())) return createEmptyRegistry();
23306
+ return {
23307
+ schemaVersion: "1.0.0",
23308
+ agents: resolveAgentTopology(),
23309
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23310
+ };
23307
23311
  }
23308
- var fs15, path20, METHOD_NAME, PROSE_FIELDS2;
23309
- var init_provision = __esm({
23310
- "src/core/provision.ts"() {
23312
+ var path20, fs15, projectFilesCache, UnknownAgentError;
23313
+ var init_agent_resolver = __esm({
23314
+ "src/core/agent_resolver.ts"() {
23311
23315
  "use strict";
23312
- fs15 = __toESM(require("fs"));
23313
23316
  path20 = __toESM(require("path"));
23314
- init_specs2();
23317
+ fs15 = __toESM(require("fs"));
23315
23318
  init_loader();
23319
+ init_paths();
23320
+ init_registry();
23316
23321
  init_project_config();
23317
23322
  init_fs();
23318
- init_yaml();
23319
23323
  init_errors();
23320
- init_models();
23321
- METHOD_NAME = /^[a-z][a-zA-Z0-9]*$/;
23322
- PROSE_FIELDS2 = /* @__PURE__ */ new Set([
23323
- "description",
23324
- "intent",
23325
- "summary",
23326
- "condition",
23327
- "over",
23328
- "outcome",
23329
- "error",
23330
- "note",
23331
- "caller"
23332
- ]);
23324
+ init_templates();
23325
+ init_execution_profile();
23326
+ init_budget_policy();
23327
+ init_specs2();
23328
+ init_specs();
23329
+ init_variants();
23330
+ projectFilesCache = /* @__PURE__ */ new Map();
23331
+ UnknownAgentError = class extends WaironError {
23332
+ constructor(agentId, knownIds) {
23333
+ super(`Unknown agent id: "${agentId}". Known agent ids: ${knownIds.join(", ")}`);
23334
+ this.name = "UnknownAgentError";
23335
+ }
23336
+ };
23333
23337
  }
23334
23338
  });
23335
23339
 
@@ -27241,116 +27245,383 @@ function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
27241
27245
  } else if (liveSpecDir && fs17.existsSync(liveSpecDir)) {
27242
27246
  fs17.rmSync(liveSpecDir, { recursive: true, force: true });
27243
27247
  }
27244
- const target = path22.join(root, relative9, ".wai");
27245
- fs17.mkdirSync(path22.dirname(target), { recursive: true });
27246
- if (fs17.existsSync(target)) fs17.rmSync(target, { recursive: true, force: true });
27247
- fs17.renameSync(stagedSpecDir, target);
27248
- }
27249
- if (backedUp) {
27250
- return backupDir;
27248
+ const target = path22.join(root, relative9, ".wai");
27249
+ fs17.mkdirSync(path22.dirname(target), { recursive: true });
27250
+ if (fs17.existsSync(target)) fs17.rmSync(target, { recursive: true, force: true });
27251
+ fs17.renameSync(stagedSpecDir, target);
27252
+ }
27253
+ if (backedUp) {
27254
+ return backupDir;
27255
+ }
27256
+ return "";
27257
+ }
27258
+ function discardStagingDir(stagingDir) {
27259
+ try {
27260
+ fs17.rmSync(stagingDir, { recursive: true, force: true });
27261
+ } catch {
27262
+ }
27263
+ }
27264
+ function exportSpecTree(includeDerived, allowPartial) {
27265
+ const system = loadSystemSpec();
27266
+ if (!system) {
27267
+ throw new Error("no spec tree to export at this project root");
27268
+ }
27269
+ const inspection = inspectChainedRoots();
27270
+ const root = getProjectRoot();
27271
+ const skipped = [...inspection.skipped];
27272
+ const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
27273
+ for (const rel2 of inspection.roots) {
27274
+ const waiDir = aiPathsAt(path22.resolve(root, rel2)).root();
27275
+ if (pathExists(waiDir)) {
27276
+ roots.push({ relativePath: rel2, waiDir });
27277
+ } else {
27278
+ skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
27279
+ }
27280
+ }
27281
+ if (skipped.length > 0 && !allowPartial) {
27282
+ const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
27283
+ throw new Error(
27284
+ `cannot export the whole spec tree \u2014 skipped: ${detail}. Pass allowPartial to export the rest anyway.`
27285
+ );
27286
+ }
27287
+ const stateId = computeStateId();
27288
+ const built = buildTreeArchive2(roots, system.name, stateId.digest, includeDerived);
27289
+ const result = {
27290
+ archive: built.archive,
27291
+ suggestedFileName: built.suggestedFileName,
27292
+ projectName: built.manifest.projectName,
27293
+ roots: built.manifest.roots,
27294
+ fileCount: built.fileCount,
27295
+ stateId: stateId.digest,
27296
+ skipped
27297
+ };
27298
+ return result;
27299
+ }
27300
+ function importSpecTree(archive, options) {
27301
+ const info = inspectTreeArchive2(archive);
27302
+ if (!info.compatible) {
27303
+ throw new Error("incompatible .waitree archive (format or minimum wairon version)");
27304
+ }
27305
+ const destDir = path22.resolve(options.destDir ?? getProjectRoot());
27306
+ const occupied = hasSpecTree(destDir);
27307
+ const authored = hasAuthoredSpecs(destDir);
27308
+ if (authored && options.replaceExisting !== true) {
27309
+ throw new Error(
27310
+ "the destination already holds an authored spec tree; pass replaceExisting to move it aside and import over it"
27311
+ );
27312
+ }
27313
+ const stagingDir = createStagingDir(destDir);
27314
+ let extraction;
27315
+ try {
27316
+ extraction = extractTreeArchive2(archive, stagingDir, options.refuseExecutableEntries);
27317
+ } catch (err) {
27318
+ discardStagingDir(stagingDir);
27319
+ throw err;
27320
+ }
27321
+ const backupPath = commitStagedTree(stagingDir, destDir, extraction.roots, occupied);
27322
+ discardStagingDir(stagingDir);
27323
+ invalidateSpecCache();
27324
+ const result = {
27325
+ destDir,
27326
+ projectName: extraction.manifest.projectName,
27327
+ roots: extraction.roots,
27328
+ fileCount: extraction.writtenPaths.length,
27329
+ replaced: occupied
27330
+ };
27331
+ if (backupPath) result.backupPath = backupPath;
27332
+ if (extraction.manifest.stateId) result.stateId = extraction.manifest.stateId;
27333
+ return result;
27334
+ }
27335
+ function liveSpecDirFor(rootDir) {
27336
+ const modern = path22.join(rootDir, ".wai");
27337
+ if (fs17.existsSync(modern)) return modern;
27338
+ const legacy = path22.join(rootDir, ".wairon");
27339
+ if (fs17.existsSync(legacy)) return legacy;
27340
+ return null;
27341
+ }
27342
+ function timestampSlug() {
27343
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27344
+ }
27345
+ var fs17, path22, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27346
+ var init_treetransfer = __esm({
27347
+ "src/core/treetransfer.ts"() {
27348
+ "use strict";
27349
+ fs17 = __toESM(require("fs"));
27350
+ path22 = __toESM(require("path"));
27351
+ sdkPortal = __toESM(require_dist());
27352
+ init_paths();
27353
+ init_fs();
27354
+ init_specs2();
27355
+ init_statehash();
27356
+ STAGING_PREFIX = ".wai-staging-";
27357
+ BACKUP_DIR = ".wai-backups";
27358
+ }
27359
+ });
27360
+
27361
+ // src/exporters/claude.ts
27362
+ function toolsFor(budget) {
27363
+ const base = TOOLS_BY_CLASS[budget.toolClass];
27364
+ if (!base) return void 0;
27365
+ if (budget.allowNestedDelegation && !base.includes("Agent")) {
27366
+ return [...base, "Agent"];
27367
+ }
27368
+ if (!budget.allowNestedDelegation) {
27369
+ return base.filter((t) => t !== "Agent");
27370
+ }
27371
+ return base;
27372
+ }
27373
+ function mcpServersFor(access) {
27374
+ return access === "none" ? "[]" : void 0;
27375
+ }
27376
+ function yamlScalar(value) {
27377
+ return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
27378
+ }
27379
+ var path23, MODEL_BY_TIER, TOOLS_BY_CLASS, ClaudeExporter;
27380
+ var init_claude = __esm({
27381
+ "src/exporters/claude.ts"() {
27382
+ "use strict";
27383
+ path23 = __toESM(require("path"));
27384
+ init_fs();
27385
+ MODEL_BY_TIER = {
27386
+ small: "haiku",
27387
+ standard: "sonnet",
27388
+ large: "opus",
27389
+ frontier: "fable"
27390
+ };
27391
+ TOOLS_BY_CLASS = {
27392
+ "read-only": ["Read", "Grep", "Glob"],
27393
+ implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
27394
+ orchestrate: ["Agent", "SendMessage", "TodoWrite"],
27395
+ // `full` means "inherit everything" — expressed by omitting the field.
27396
+ full: void 0
27397
+ };
27398
+ ClaudeExporter = class {
27399
+ constructor(options = {}) {
27400
+ this.options = options;
27401
+ this.targetType = "claude";
27402
+ }
27403
+ outputPath(ctx) {
27404
+ const { agent, target, projectRoot } = ctx;
27405
+ const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
27406
+ return path23.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27407
+ }
27408
+ export(ctx) {
27409
+ const { agent, renderedInstructions, budget } = ctx;
27410
+ const filePath = this.outputPath(ctx);
27411
+ const frontmatter = [
27412
+ `name: ${agent.name}`,
27413
+ `description: ${yamlScalar(agent.description)}`
27414
+ ];
27415
+ if (budget && this.options.emitBudget) {
27416
+ if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
27417
+ if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
27418
+ if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
27419
+ const tools = toolsFor(budget);
27420
+ if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
27421
+ const servers = mcpServersFor(budget.mcp);
27422
+ if (servers) frontmatter.push(`mcpServers: ${servers}`);
27423
+ }
27424
+ const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
27425
+ const changed = writeFileIfChanged(filePath, content);
27426
+ return { outputPath: filePath, content, unchanged: !changed };
27427
+ }
27428
+ };
27429
+ }
27430
+ });
27431
+
27432
+ // src/exporters/custom.ts
27433
+ var path24, CustomExporter;
27434
+ var init_custom = __esm({
27435
+ "src/exporters/custom.ts"() {
27436
+ "use strict";
27437
+ path24 = __toESM(require("path"));
27438
+ init_fs();
27439
+ CustomExporter = class {
27440
+ constructor() {
27441
+ this.targetType = "custom";
27442
+ }
27443
+ outputPath(ctx) {
27444
+ const { agent, target, projectRoot } = ctx;
27445
+ if (!("outputDir" in target)) {
27446
+ throw new Error("CustomExporter requires target.outputDir");
27447
+ }
27448
+ return path24.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
27449
+ }
27450
+ export(ctx) {
27451
+ const { agent, target, renderedInstructions } = ctx;
27452
+ const label = "label" in target ? target.label : "Custom";
27453
+ const filePath = this.outputPath(ctx);
27454
+ const content = [
27455
+ "---",
27456
+ `name: ${agent.name}`,
27457
+ `description: ${agent.description}`,
27458
+ `id: ${agent.id}`,
27459
+ `target: ${label}`,
27460
+ "---",
27461
+ "",
27462
+ renderedInstructions,
27463
+ ""
27464
+ ].join("\n");
27465
+ const changed = writeFileIfChanged(filePath, content);
27466
+ return { outputPath: filePath, content, unchanged: !changed };
27467
+ }
27468
+ };
27469
+ }
27470
+ });
27471
+
27472
+ // src/exporters/gemini.ts
27473
+ function yamlString(value) {
27474
+ if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
27475
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
27476
+ }
27477
+ return value;
27478
+ }
27479
+ var path25, GeminiExporter;
27480
+ var init_gemini = __esm({
27481
+ "src/exporters/gemini.ts"() {
27482
+ "use strict";
27483
+ path25 = __toESM(require("path"));
27484
+ init_fs();
27485
+ GeminiExporter = class {
27486
+ constructor() {
27487
+ this.targetType = "gemini";
27488
+ }
27489
+ outputPath(ctx) {
27490
+ const { agent, target, projectRoot } = ctx;
27491
+ const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
27492
+ return path25.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
27493
+ }
27494
+ export(ctx) {
27495
+ const { agent, renderedInstructions } = ctx;
27496
+ const filePath = this.outputPath(ctx);
27497
+ const indentedInstructions = renderedInstructions.split("\n").map((line) => line.length > 0 ? ` ${line}` : "").join("\n");
27498
+ const content = [
27499
+ `name: ${yamlString(agent.name)}`,
27500
+ `description: ${yamlString(agent.description)}`,
27501
+ `system_prompt: |`,
27502
+ indentedInstructions,
27503
+ ""
27504
+ ].join("\n");
27505
+ const changed = writeFileIfChanged(filePath, content);
27506
+ return { outputPath: filePath, content, unchanged: !changed };
27507
+ }
27508
+ };
27509
+ }
27510
+ });
27511
+
27512
+ // src/exporters/registry.ts
27513
+ function getExporter(target) {
27514
+ const type = typeof target === "string" ? target : target.type;
27515
+ const exporter = EXPORTERS.get(type);
27516
+ if (!exporter) {
27517
+ throw new WaironError(`No exporter registered for target type: "${type}"`);
27518
+ }
27519
+ return exporter;
27520
+ }
27521
+ function registerExporter(type, exporter) {
27522
+ EXPORTERS.set(type, exporter);
27523
+ }
27524
+ var EXPORTERS;
27525
+ var init_registry2 = __esm({
27526
+ "src/exporters/registry.ts"() {
27527
+ "use strict";
27528
+ init_claude();
27529
+ init_custom();
27530
+ init_gemini();
27531
+ init_errors();
27532
+ EXPORTERS = /* @__PURE__ */ new Map([
27533
+ ["claude", new ClaudeExporter({ emitBudget: true })],
27534
+ ["gemini", new GeminiExporter()],
27535
+ ["agy", new GeminiExporter()],
27536
+ ["cursor", new ClaudeExporter()],
27537
+ ["copilot", new ClaudeExporter()],
27538
+ ["codex", new ClaudeExporter()],
27539
+ ["custom", new CustomExporter()]
27540
+ ]);
27541
+ }
27542
+ });
27543
+
27544
+ // src/exporters/base.ts
27545
+ var WAIRON_MANAGED_MARKER, WAIRON_MANAGED_BANNER;
27546
+ var init_base = __esm({
27547
+ "src/exporters/base.ts"() {
27548
+ "use strict";
27549
+ WAIRON_MANAGED_MARKER = "wairon:managed";
27550
+ WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
27551
+ }
27552
+ });
27553
+
27554
+ // src/exporters/generate.ts
27555
+ function generateAgent(agent, projectConfig, options = {}) {
27556
+ const projectRoot = options.projectRoot ?? getProjectRoot();
27557
+ const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
27558
+ const rendered = `${WAIRON_MANAGED_BANNER}
27559
+ ${composeAgentBrief(agent.id).instructions}`;
27560
+ const results = [];
27561
+ const budget = resolveBudget(
27562
+ deriveExecutionProfile(agent),
27563
+ projectConfig.execution,
27564
+ agent.id
27565
+ );
27566
+ for (const agentTarget of agent.targets) {
27567
+ const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
27568
+ if (!targetConfig) continue;
27569
+ if (options.filterTargets) {
27570
+ const type = "type" in targetConfig ? targetConfig.type : targetConfig;
27571
+ if (!options.filterTargets.includes(type)) continue;
27572
+ }
27573
+ if (!options.dryRun) {
27574
+ results.push(getExporter(targetConfig).export({
27575
+ agent,
27576
+ template,
27577
+ renderedInstructions: rendered,
27578
+ projectRoot,
27579
+ target: targetConfig,
27580
+ budget
27581
+ }));
27582
+ }
27251
27583
  }
27252
- return "";
27584
+ return { agent, results };
27253
27585
  }
27254
- function discardStagingDir(stagingDir) {
27255
- try {
27256
- fs17.rmSync(stagingDir, { recursive: true, force: true });
27257
- } catch {
27586
+ function generateAll(agents, projectConfig, options = {}) {
27587
+ let pool = agents;
27588
+ if (options.filterDomainIds && options.filterDomainIds.length > 0) {
27589
+ const ids = new Set(options.filterDomainIds);
27590
+ pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
27258
27591
  }
27592
+ return pool.map((agent) => generateAgent(agent, projectConfig, options));
27259
27593
  }
27260
- function exportSpecTree(includeDerived, allowPartial) {
27261
- const system = loadSystemSpec();
27262
- if (!system) {
27263
- throw new Error("no spec tree to export at this project root");
27264
- }
27265
- const inspection = inspectChainedRoots();
27266
- const root = getProjectRoot();
27267
- const skipped = [...inspection.skipped];
27268
- const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
27269
- for (const rel2 of inspection.roots) {
27270
- const waiDir = aiPathsAt(path22.resolve(root, rel2)).root();
27271
- if (pathExists(waiDir)) {
27272
- roots.push({ relativePath: rel2, waiDir });
27273
- } else {
27274
- skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
27594
+ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProjectRoot()) {
27595
+ const expected = /* @__PURE__ */ new Set();
27596
+ for (const agent of agents) {
27597
+ for (const agentTarget of agent.targets) {
27598
+ const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
27599
+ if (!targetConfig) continue;
27600
+ const ctx = { agent, projectRoot, target: targetConfig };
27601
+ expected.add(path26.resolve(getExporter(targetConfig).outputPath(ctx)));
27275
27602
  }
27276
27603
  }
27277
- if (skipped.length > 0 && !allowPartial) {
27278
- const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
27279
- throw new Error(
27280
- `cannot export the whole spec tree \u2014 skipped: ${detail}. Pass allowPartial to export the rest anyway.`
27281
- );
27282
- }
27283
- const stateId = computeStateId();
27284
- const built = buildTreeArchive2(roots, system.name, stateId.digest, includeDerived);
27285
- const result = {
27286
- archive: built.archive,
27287
- suggestedFileName: built.suggestedFileName,
27288
- projectName: built.manifest.projectName,
27289
- roots: built.manifest.roots,
27290
- fileCount: built.fileCount,
27291
- stateId: stateId.digest,
27292
- skipped
27293
- };
27294
- return result;
27295
- }
27296
- function importSpecTree(archive, options) {
27297
- const info = inspectTreeArchive2(archive);
27298
- if (!info.compatible) {
27299
- throw new Error("incompatible .waitree archive (format or minimum wairon version)");
27300
- }
27301
- const destDir = path22.resolve(options.destDir ?? getProjectRoot());
27302
- const occupied = hasSpecTree(destDir);
27303
- const authored = hasAuthoredSpecs(destDir);
27304
- if (authored && options.replaceExisting !== true) {
27305
- throw new Error(
27306
- "the destination already holds an authored spec tree; pass replaceExisting to move it aside and import over it"
27307
- );
27308
- }
27309
- const stagingDir = createStagingDir(destDir);
27310
- let extraction;
27311
- try {
27312
- extraction = extractTreeArchive2(archive, stagingDir, options.refuseExecutableEntries);
27313
- } catch (err) {
27314
- discardStagingDir(stagingDir);
27315
- throw err;
27316
- }
27317
- const backupPath = commitStagedTree(stagingDir, destDir, extraction.roots, occupied);
27318
- discardStagingDir(stagingDir);
27319
- invalidateSpecCache();
27320
- const result = {
27321
- destDir,
27322
- projectName: extraction.manifest.projectName,
27323
- roots: extraction.roots,
27324
- fileCount: extraction.writtenPaths.length,
27325
- replaced: occupied
27326
- };
27327
- if (backupPath) result.backupPath = backupPath;
27328
- if (extraction.manifest.stateId) result.stateId = extraction.manifest.stateId;
27329
- return result;
27330
- }
27331
- function liveSpecDirFor(rootDir) {
27332
- const modern = path22.join(rootDir, ".wai");
27333
- if (fs17.existsSync(modern)) return modern;
27334
- const legacy = path22.join(rootDir, ".wairon");
27335
- if (fs17.existsSync(legacy)) return legacy;
27336
- return null;
27604
+ return expected;
27337
27605
  }
27338
- function timestampSlug() {
27339
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27606
+ function resolveTargetConfig(agentTarget, projectConfig) {
27607
+ const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
27608
+ return projectConfig.targets.find((t) => {
27609
+ if (typeof t === "string") return t === targetType;
27610
+ return t.type === targetType;
27611
+ });
27340
27612
  }
27341
- var fs17, path22, sdkPortal, STAGING_PREFIX, BACKUP_DIR;
27342
- var init_treetransfer = __esm({
27343
- "src/core/treetransfer.ts"() {
27613
+ var path26;
27614
+ var init_generate = __esm({
27615
+ "src/exporters/generate.ts"() {
27344
27616
  "use strict";
27345
- fs17 = __toESM(require("fs"));
27346
- path22 = __toESM(require("path"));
27347
- sdkPortal = __toESM(require_dist());
27348
- init_loader();
27617
+ path26 = __toESM(require("path"));
27618
+ init_templates();
27619
+ init_agent_resolver();
27620
+ init_execution_profile();
27621
+ init_budget_policy();
27349
27622
  init_fs();
27350
- init_specs2();
27351
- init_statehash();
27352
- STAGING_PREFIX = ".wai-staging-";
27353
- BACKUP_DIR = ".wai-backups";
27623
+ init_registry2();
27624
+ init_base();
27354
27625
  }
27355
27626
  });
27356
27627
 
@@ -27510,12 +27781,12 @@ function digest(content) {
27510
27781
  return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
27511
27782
  }
27512
27783
  function currentSpecDigests(root = getProjectRoot()) {
27513
- const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path23.resolve(root, s.projectPath).split(path23.sep).join("/")}/`);
27784
+ const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path27.resolve(root, s.projectPath).split(path27.sep).join("/")}/`);
27514
27785
  const out = {};
27515
27786
  for (const [abs, content] of snapshotSpecFiles()) {
27516
- const normalized = path23.resolve(abs).split(path23.sep).join("/");
27787
+ const normalized = path27.resolve(abs).split(path27.sep).join("/");
27517
27788
  if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
27518
- out[path23.relative(root, abs).split(path23.sep).join("/")] = digest(content);
27789
+ out[path27.relative(root, abs).split(path27.sep).join("/")] = digest(content);
27519
27790
  }
27520
27791
  return out;
27521
27792
  }
@@ -27573,7 +27844,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
27573
27844
  const pins = {};
27574
27845
  for (const mount of mounts) {
27575
27846
  if (!mount.projectPath || mount.id.includes("::")) continue;
27576
- const child = readLockRecordAt(path23.resolve(root, mount.projectPath));
27847
+ const child = readLockRecordAt(path27.resolve(root, mount.projectPath));
27577
27848
  if (child) pins[mount.id] = pinOf(child.stateId);
27578
27849
  }
27579
27850
  return pins;
@@ -27589,12 +27860,12 @@ function movedChildren(mounts, root = getProjectRoot()) {
27589
27860
  }
27590
27861
  return moved;
27591
27862
  }
27592
- var crypto4, path23;
27863
+ var crypto4, path27;
27593
27864
  var init_approval = __esm({
27594
27865
  "src/core/approval.ts"() {
27595
27866
  "use strict";
27596
27867
  crypto4 = __toESM(require("crypto"));
27597
- path23 = __toESM(require("path"));
27868
+ path27 = __toESM(require("path"));
27598
27869
  init_fs();
27599
27870
  init_specs2();
27600
27871
  init_lockfile();
@@ -27678,6 +27949,7 @@ var init_core = __esm({
27678
27949
  init_treetransfer();
27679
27950
  init_extensions();
27680
27951
  init_agent_resolver();
27952
+ init_generate();
27681
27953
  init_treetransfer();
27682
27954
  init_provision();
27683
27955
  init_stereotype_migration();
@@ -28140,236 +28412,18 @@ init_models();
28140
28412
  // src/config/index.ts
28141
28413
  init_defaults();
28142
28414
  init_loader();
28415
+ init_paths();
28143
28416
 
28144
28417
  // src/index.ts
28145
28418
  init_core();
28146
28419
 
28147
- // src/exporters/base.ts
28148
- var WAIRON_MANAGED_MARKER = "wairon:managed";
28149
- var WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
28150
-
28151
- // src/exporters/claude.ts
28152
- var path24 = __toESM(require("path"));
28153
- init_fs();
28154
- var MODEL_BY_TIER = {
28155
- small: "haiku",
28156
- standard: "sonnet",
28157
- large: "opus",
28158
- frontier: "fable"
28159
- };
28160
- var TOOLS_BY_CLASS = {
28161
- "read-only": ["Read", "Grep", "Glob"],
28162
- implement: ["Read", "Grep", "Glob", "Edit", "Write", "Bash"],
28163
- orchestrate: ["Agent", "SendMessage", "TodoWrite"],
28164
- // `full` means "inherit everything" — expressed by omitting the field.
28165
- full: void 0
28166
- };
28167
- function toolsFor(budget) {
28168
- const base = TOOLS_BY_CLASS[budget.toolClass];
28169
- if (!base) return void 0;
28170
- if (budget.allowNestedDelegation && !base.includes("Agent")) {
28171
- return [...base, "Agent"];
28172
- }
28173
- if (!budget.allowNestedDelegation) {
28174
- return base.filter((t) => t !== "Agent");
28175
- }
28176
- return base;
28177
- }
28178
- function mcpServersFor(access) {
28179
- return access === "none" ? "[]" : void 0;
28180
- }
28181
- function yamlScalar(value) {
28182
- return /[:#\n]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
28183
- }
28184
- var ClaudeExporter = class {
28185
- constructor(options = {}) {
28186
- this.options = options;
28187
- this.targetType = "claude";
28188
- }
28189
- outputPath(ctx) {
28190
- const { agent, target, projectRoot } = ctx;
28191
- const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
28192
- return path24.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
28193
- }
28194
- export(ctx) {
28195
- const { agent, renderedInstructions, budget } = ctx;
28196
- const filePath = this.outputPath(ctx);
28197
- const frontmatter = [
28198
- `name: ${agent.name}`,
28199
- `description: ${yamlScalar(agent.description)}`
28200
- ];
28201
- if (budget && this.options.emitBudget) {
28202
- if (budget.modelTier) frontmatter.push(`model: ${MODEL_BY_TIER[budget.modelTier]}`);
28203
- if (budget.effort) frontmatter.push(`effort: ${budget.effort}`);
28204
- if (budget.maxTurns !== void 0) frontmatter.push(`maxTurns: ${budget.maxTurns}`);
28205
- const tools = toolsFor(budget);
28206
- if (tools) frontmatter.push(`tools: ${tools.join(", ")}`);
28207
- const servers = mcpServersFor(budget.mcp);
28208
- if (servers) frontmatter.push(`mcpServers: ${servers}`);
28209
- }
28210
- const content = ["---", ...frontmatter, "---", "", renderedInstructions, ""].join("\n");
28211
- const changed = writeFileIfChanged(filePath, content);
28212
- return { outputPath: filePath, content, unchanged: !changed };
28213
- }
28214
- };
28215
-
28216
- // src/exporters/custom.ts
28217
- var path25 = __toESM(require("path"));
28218
- init_fs();
28219
- var CustomExporter = class {
28220
- constructor() {
28221
- this.targetType = "custom";
28222
- }
28223
- outputPath(ctx) {
28224
- const { agent, target, projectRoot } = ctx;
28225
- if (!("outputDir" in target)) {
28226
- throw new Error("CustomExporter requires target.outputDir");
28227
- }
28228
- return path25.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
28229
- }
28230
- export(ctx) {
28231
- const { agent, target, renderedInstructions } = ctx;
28232
- const label = "label" in target ? target.label : "Custom";
28233
- const filePath = this.outputPath(ctx);
28234
- const content = [
28235
- "---",
28236
- `name: ${agent.name}`,
28237
- `description: ${agent.description}`,
28238
- `id: ${agent.id}`,
28239
- `target: ${label}`,
28240
- "---",
28241
- "",
28242
- renderedInstructions,
28243
- ""
28244
- ].join("\n");
28245
- const changed = writeFileIfChanged(filePath, content);
28246
- return { outputPath: filePath, content, unchanged: !changed };
28247
- }
28248
- };
28249
-
28250
- // src/exporters/gemini.ts
28251
- var path26 = __toESM(require("path"));
28252
- init_fs();
28253
- var GeminiExporter = class {
28254
- constructor() {
28255
- this.targetType = "gemini";
28256
- }
28257
- outputPath(ctx) {
28258
- const { agent, target, projectRoot } = ctx;
28259
- const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
28260
- return path26.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
28261
- }
28262
- export(ctx) {
28263
- const { agent, renderedInstructions } = ctx;
28264
- const filePath = this.outputPath(ctx);
28265
- const indentedInstructions = renderedInstructions.split("\n").map((line) => line.length > 0 ? ` ${line}` : "").join("\n");
28266
- const content = [
28267
- `name: ${yamlString(agent.name)}`,
28268
- `description: ${yamlString(agent.description)}`,
28269
- `system_prompt: |`,
28270
- indentedInstructions,
28271
- ""
28272
- ].join("\n");
28273
- const changed = writeFileIfChanged(filePath, content);
28274
- return { outputPath: filePath, content, unchanged: !changed };
28275
- }
28276
- };
28277
- function yamlString(value) {
28278
- if (/[:#\[\]{},&*!|>'"%@`]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
28279
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
28280
- }
28281
- return value;
28282
- }
28283
-
28284
- // src/exporters/generate.ts
28285
- var path27 = __toESM(require("path"));
28286
- init_core();
28287
- init_execution_profile();
28288
- init_budget_policy();
28289
- init_fs();
28290
-
28291
- // src/exporters/registry.ts
28292
- init_errors();
28293
- var EXPORTERS = /* @__PURE__ */ new Map([
28294
- ["claude", new ClaudeExporter({ emitBudget: true })],
28295
- ["gemini", new GeminiExporter()],
28296
- ["agy", new GeminiExporter()],
28297
- ["cursor", new ClaudeExporter()],
28298
- ["copilot", new ClaudeExporter()],
28299
- ["codex", new ClaudeExporter()],
28300
- ["custom", new CustomExporter()]
28301
- ]);
28302
- function getExporter(target) {
28303
- const type = typeof target === "string" ? target : target.type;
28304
- const exporter = EXPORTERS.get(type);
28305
- if (!exporter) {
28306
- throw new WaironError(`No exporter registered for target type: "${type}"`);
28307
- }
28308
- return exporter;
28309
- }
28310
- function registerExporter(type, exporter) {
28311
- EXPORTERS.set(type, exporter);
28312
- }
28313
-
28314
- // src/exporters/generate.ts
28315
- function generateAgent(agent, projectConfig, options = {}) {
28316
- const projectRoot = options.projectRoot ?? getProjectRoot();
28317
- const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
28318
- const rendered = `${WAIRON_MANAGED_BANNER}
28319
- ${composeAgentBrief(agent.id).instructions}`;
28320
- const results = [];
28321
- const budget = resolveBudget(
28322
- deriveExecutionProfile(agent),
28323
- projectConfig.execution,
28324
- agent.id
28325
- );
28326
- for (const agentTarget of agent.targets) {
28327
- const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
28328
- if (!targetConfig) continue;
28329
- if (options.filterTargets) {
28330
- const type = "type" in targetConfig ? targetConfig.type : targetConfig;
28331
- if (!options.filterTargets.includes(type)) continue;
28332
- }
28333
- if (!options.dryRun) {
28334
- results.push(getExporter(targetConfig).export({
28335
- agent,
28336
- template,
28337
- renderedInstructions: rendered,
28338
- projectRoot,
28339
- target: targetConfig,
28340
- budget
28341
- }));
28342
- }
28343
- }
28344
- return { agent, results };
28345
- }
28346
- function generateAll(agents, projectConfig, options = {}) {
28347
- let pool = agents;
28348
- if (options.filterDomainIds && options.filterDomainIds.length > 0) {
28349
- const ids = new Set(options.filterDomainIds);
28350
- pool = agents.filter((a) => ids.has(a.domainRoot ?? "root"));
28351
- }
28352
- return pool.map((agent) => generateAgent(agent, projectConfig, options));
28353
- }
28354
- function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProjectRoot()) {
28355
- const expected = /* @__PURE__ */ new Set();
28356
- for (const agent of agents) {
28357
- for (const agentTarget of agent.targets) {
28358
- const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
28359
- if (!targetConfig) continue;
28360
- const ctx = { agent, projectRoot, target: targetConfig };
28361
- expected.add(path27.resolve(getExporter(targetConfig).outputPath(ctx)));
28362
- }
28363
- }
28364
- return expected;
28365
- }
28366
- function resolveTargetConfig(agentTarget, projectConfig) {
28367
- const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
28368
- return projectConfig.targets.find((t) => {
28369
- if (typeof t === "string") return t === targetType;
28370
- return t.type === targetType;
28371
- });
28372
- }
28420
+ // src/exporters/index.ts
28421
+ init_base();
28422
+ init_claude();
28423
+ init_custom();
28424
+ init_gemini();
28425
+ init_generate();
28426
+ init_registry2();
28373
28427
 
28374
28428
  // src/utils/index.ts
28375
28429
  init_errors();