@wayai/cli 0.3.151 → 0.3.152

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
@@ -8942,7 +8942,7 @@ var init_api_client = __esm({
8942
8942
  init_sentry();
8943
8943
  init_mask_secrets();
8944
8944
  RETRYABLE_BACKOFF_MS = [500, 1e3, 2e3];
8945
- delay = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
8945
+ delay = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
8946
8946
  ApiError = class extends Error {
8947
8947
  status;
8948
8948
  body;
@@ -9607,7 +9607,7 @@ var init_api_client = __esm({
9607
9607
  * `dataErrorMessage` recovers the envelope's own message for display.
9608
9608
  *
9609
9609
  * `orgId` is the caller's org SELECTOR (the `--org` flag, else the repo's
9610
- * `.wayai.yaml`), not an authorization claim: the backend validates it against
9610
+ * `wayai-ws/wayai.yaml`), not an authorization claim: the backend validates it against
9611
9611
  * the caller's grants and attests only the validated result. A multi-org user
9612
9612
  * needs it to pick; a single-org user can omit it.
9613
9613
  *
@@ -14516,34 +14516,15 @@ function resolveLayout(gitRoot, layout = WAYAI_LAYOUT) {
14516
14516
  const newWs = path.join(gitRoot, layout.wsDir);
14517
14517
  const legacyWs = path.join(gitRoot, layout.legacy.wsDir);
14518
14518
  const legacyOrg = path.join(gitRoot, layout.legacy.orgAtRoot);
14519
- const newExists = isDirectory(newWs);
14520
14519
  const legacyExists = isDirectory(legacyWs) || isDirectory(legacyOrg);
14521
14520
  const basesDir = path.join(newWs, layout.basesSubdir);
14522
- if (newExists) {
14523
- return {
14524
- hubsDir: path.join(newWs, layout.hubsSubdir),
14525
- orgDir: path.join(newWs, layout.orgSubdir),
14526
- basesDir,
14527
- isLegacy: false,
14528
- legacyAlsoPresent: legacyExists
14529
- };
14530
- }
14531
- if (legacyExists) {
14532
- return {
14533
- hubsDir: legacyWs,
14534
- orgDir: legacyOrg,
14535
- basesDir,
14536
- isLegacy: true,
14537
- legacyAlsoPresent: false
14538
- };
14521
+ const hubsDir = path.join(newWs, layout.hubsSubdir);
14522
+ const orgDir = path.join(newWs, layout.orgSubdir);
14523
+ const newDisplacesLegacy = () => isDirectory(hubsDir) || isDirectory(orgDir) || isDirectory(basesDir);
14524
+ if (legacyExists && !newDisplacesLegacy()) {
14525
+ return { hubsDir: legacyWs, orgDir: legacyOrg, basesDir, isLegacy: true, legacyAlsoPresent: false };
14539
14526
  }
14540
- return {
14541
- hubsDir: path.join(newWs, layout.hubsSubdir),
14542
- orgDir: path.join(newWs, layout.orgSubdir),
14543
- basesDir,
14544
- isLegacy: false,
14545
- legacyAlsoPresent: false
14546
- };
14527
+ return { hubsDir, orgDir, basesDir, isLegacy: false, legacyAlsoPresent: legacyExists };
14547
14528
  }
14548
14529
  function resolveBasesDir(gitRoot) {
14549
14530
  return resolveLayout(gitRoot).basesDir;
@@ -14960,111 +14941,344 @@ var init_workspace = __esm({
14960
14941
  }
14961
14942
  });
14962
14943
 
14944
+ // src/lib/workspace-manifest.ts
14945
+ import * as fs3 from "fs";
14946
+ import * as path3 from "path";
14947
+ import * as yaml2 from "js-yaml";
14948
+ function workspaceManifestPath(gitRoot) {
14949
+ return path3.join(gitRoot, WAYAI_LAYOUT.wsDir, WORKSPACE_MANIFEST_FILE);
14950
+ }
14951
+ function rootConfigPath(gitRoot) {
14952
+ return path3.join(gitRoot, ROOT_CONFIG_FILE);
14953
+ }
14954
+ function loadYamlMapping(file) {
14955
+ let raw;
14956
+ try {
14957
+ raw = fs3.readFileSync(file, "utf-8");
14958
+ } catch (err) {
14959
+ if (err.code === "ENOENT") return { kind: "absent" };
14960
+ return { kind: "malformed", reason: err instanceof Error ? err.message : String(err) };
14961
+ }
14962
+ let doc;
14963
+ try {
14964
+ doc = yaml2.load(raw);
14965
+ } catch (err) {
14966
+ return { kind: "malformed", reason: err instanceof Error ? err.message : String(err) };
14967
+ }
14968
+ if (doc === void 0 || doc === null) return { kind: "ok", doc: {} };
14969
+ if (typeof doc !== "object" || Array.isArray(doc)) {
14970
+ return { kind: "malformed", reason: "expected a YAML mapping of settings" };
14971
+ }
14972
+ return { kind: "ok", doc };
14973
+ }
14974
+ function loadWorkspaceManifest(gitRoot) {
14975
+ return loadYamlMapping(workspaceManifestPath(gitRoot));
14976
+ }
14977
+ var WORKSPACE_MANIFEST_FILE, WORKSPACE_MANIFEST_LABEL, ROOT_CONFIG_FILE;
14978
+ var init_workspace_manifest = __esm({
14979
+ "src/lib/workspace-manifest.ts"() {
14980
+ "use strict";
14981
+ init_layout();
14982
+ WORKSPACE_MANIFEST_FILE = "wayai.yaml";
14983
+ WORKSPACE_MANIFEST_LABEL = `${WAYAI_LAYOUT.wsDir}/${WORKSPACE_MANIFEST_FILE}`;
14984
+ ROOT_CONFIG_FILE = ".wayai.yaml";
14985
+ }
14986
+ });
14987
+
14963
14988
  // src/lib/repo-config.ts
14964
14989
  var repo_config_exports = {};
14965
14990
  __export(repo_config_exports, {
14991
+ _resetRepoConfigNotices: () => _resetRepoConfigNotices,
14992
+ planOrgMigration: () => planOrgMigration,
14966
14993
  readRepoConfig: () => readRepoConfig,
14994
+ readRepoConfigUnlessBlocked: () => readRepoConfigUnlessBlocked,
14995
+ readRepoScopeBlocker: () => readRepoScopeBlocker,
14967
14996
  requireRepoConfig: () => requireRepoConfig,
14997
+ resolveRepoConfig: () => resolveRepoConfig,
14968
14998
  writeRepoConfig: () => writeRepoConfig
14969
14999
  });
14970
- import * as fs3 from "fs";
14971
- import * as path3 from "path";
14972
- import * as yaml2 from "js-yaml";
14973
- function warnAboutLegacyFields(found) {
14974
- if (legacyFieldsWarned || found.length === 0) return;
14975
- legacyFieldsWarned = true;
14976
- console.warn(
14977
- `Note: .wayai.yaml contains deprecated field(s) ${found.join(", ")}. These are ignored \u2014 only organization_id is used now. You can remove them.`
14978
- );
15000
+ import * as fs4 from "fs";
15001
+ import * as path4 from "path";
15002
+ import * as yaml3 from "js-yaml";
15003
+ function noticeOnce(key, emit) {
15004
+ if (noticed.has(key)) return;
15005
+ noticed.add(key);
15006
+ emit();
15007
+ }
15008
+ function _resetRepoConfigNotices() {
15009
+ noticed.clear();
15010
+ }
15011
+ function readOrgFields(doc) {
15012
+ const raw = doc.organization_id;
15013
+ if (raw === void 0 || raw === null) {
15014
+ if (typeof doc.organization === "string") {
15015
+ return {
15016
+ kind: "invalid",
15017
+ detail: "it uses the old format (organization: <name>) \u2014 replace it with organization_id: <uuid>"
15018
+ };
15019
+ }
15020
+ return { kind: "none" };
15021
+ }
15022
+ if (typeof raw !== "string" || !UUID_RE2.test(raw.trim())) {
15023
+ return { kind: "invalid", detail: "organization_id must be a UUID" };
15024
+ }
15025
+ const name = typeof doc.organization_name === "string" && doc.organization_name.trim() ? doc.organization_name.trim() : void 0;
15026
+ const config = { organization_id: raw.trim() };
15027
+ if (name) config.organization_name = name;
15028
+ return { kind: "declared", config };
15029
+ }
15030
+ function declarationFrom(load11) {
15031
+ if (load11.kind === "absent") return { kind: "none" };
15032
+ if (load11.kind === "malformed") return { kind: "invalid", detail: `it does not parse (${load11.reason})` };
15033
+ return readOrgFields(load11.doc);
15034
+ }
15035
+ function legacyFieldsIn(load11) {
15036
+ if (load11.kind !== "ok") return [];
15037
+ return LEGACY_FIELDS.filter((f) => load11.doc[f] !== void 0);
15038
+ }
15039
+ function resolve2(gitRoot) {
15040
+ const manifestFile = workspaceManifestPath(gitRoot);
15041
+ const manifest = declarationFrom(loadWorkspaceManifest(gitRoot));
15042
+ if (manifest.kind === "invalid") {
15043
+ return { kind: "invalid", source: "manifest", file: manifestFile, detail: manifest.detail };
15044
+ }
15045
+ const rootFile = rootConfigPath(gitRoot);
15046
+ const rootLoad = loadYamlMapping(rootFile);
15047
+ const root = declarationFrom(rootLoad);
15048
+ if (manifest.kind === "declared") {
15049
+ if (root.kind === "declared" && root.config.organization_id !== manifest.config.organization_id) {
15050
+ return {
15051
+ kind: "conflict",
15052
+ manifestOrg: manifest.config.organization_id,
15053
+ rootOrg: root.config.organization_id
15054
+ };
15055
+ }
15056
+ return {
15057
+ kind: "ok",
15058
+ resolved: { config: manifest.config, source: "manifest", path: manifestFile },
15059
+ legacyRootFields: []
15060
+ };
15061
+ }
15062
+ if (root.kind === "declared") {
15063
+ return {
15064
+ kind: "ok",
15065
+ resolved: { config: root.config, source: "root", path: rootFile },
15066
+ legacyRootFields: legacyFieldsIn(rootLoad)
15067
+ };
15068
+ }
15069
+ if (root.kind === "invalid") {
15070
+ return { kind: "invalid", source: "root", file: rootFile, detail: root.detail };
15071
+ }
15072
+ return { kind: "none" };
15073
+ }
15074
+ function conflictMessage(manifestOrg, rootOrg, remedy = RECONCILE_REMEDY) {
15075
+ return [
15076
+ "Refusing to guess the organization: two files in this repo name different ones.",
15077
+ ` ${WORKSPACE_MANIFEST_LABEL} -> ${manifestOrg} (the workspace manifest)`,
15078
+ ` ${ROOT_CONFIG_FILE} -> ${rootOrg} (deprecated fallback)`,
15079
+ "",
15080
+ ...remedy
15081
+ ].join("\n");
15082
+ }
15083
+ function rebindRefusal(requestedOrg, rootOrg) {
15084
+ return [
15085
+ `Refusing to re-bind the organization: ${ROOT_CONFIG_FILE} still names a different one, and it is read as a fallback.`,
15086
+ ` requested -> ${requestedOrg}`,
15087
+ ` ${ROOT_CONFIG_FILE} -> ${rootOrg} (deprecated fallback)`,
15088
+ "",
15089
+ `Delete ${ROOT_CONFIG_FILE} (or set its organization_id to ${requestedOrg}), then re-run.`,
15090
+ "Nothing was written \u2014 this repo still resolves to the organization it had."
15091
+ ].join("\n");
15092
+ }
15093
+ function invalidMessage(file, detail) {
15094
+ return [
15095
+ `Invalid organization binding in ${file}: ${detail}.`,
15096
+ "",
15097
+ " organization_id: <your-org-uuid>",
15098
+ "",
15099
+ "Run `wayai init` to set up the workspace."
15100
+ ].join("\n");
15101
+ }
15102
+ function missingMessage() {
15103
+ return [
15104
+ "This repository is not scoped to an organization.",
15105
+ `Declare it in ${WORKSPACE_MANIFEST_LABEL}:`,
15106
+ "",
15107
+ " organization_id: <your-org-uuid>",
15108
+ "",
15109
+ "Run `wayai init` to set up the workspace."
15110
+ ].join("\n");
15111
+ }
15112
+ function report(r, posture) {
15113
+ switch (r.kind) {
15114
+ case "ok":
15115
+ if (r.resolved.source !== "root") return;
15116
+ noticeOnce(
15117
+ "root-fallback",
15118
+ () => console.warn(
15119
+ `Note: this repo's organization binding is still in ${ROOT_CONFIG_FILE} at the repository root. It belongs in the workspace manifest ${WORKSPACE_MANIFEST_LABEL} \u2014 run \`wayai migrate\` to copy it there.`
15120
+ )
15121
+ );
15122
+ if (r.legacyRootFields.length > 0) {
15123
+ noticeOnce(
15124
+ "legacy-fields",
15125
+ () => console.warn(
15126
+ `Note: ${ROOT_CONFIG_FILE} contains deprecated field(s) ${r.legacyRootFields.join(", ")}. These are ignored \u2014 only organization_id is used now. You can remove them.`
15127
+ )
15128
+ );
15129
+ }
15130
+ return;
15131
+ case "invalid": {
15132
+ const emit = () => console.error(invalidMessage(r.file, r.detail));
15133
+ if (posture === "hard") emit();
15134
+ else noticeOnce(`invalid:${r.source}`, emit);
15135
+ return;
15136
+ }
15137
+ case "conflict":
15138
+ console.error(conflictMessage(r.manifestOrg, r.rootOrg));
15139
+ return;
15140
+ case "none":
15141
+ if (posture === "hard") console.error(missingMessage());
15142
+ return;
15143
+ }
15144
+ }
15145
+ function resolveRepoConfig(root) {
15146
+ const gitRoot = root ?? findGitRoot();
15147
+ if (!gitRoot) return null;
15148
+ const r = resolve2(gitRoot);
15149
+ report(r, "soft");
15150
+ return r.kind === "ok" ? r.resolved : null;
14979
15151
  }
14980
15152
  function readRepoConfig(root) {
15153
+ return resolveRepoConfig(root)?.config ?? null;
15154
+ }
15155
+ function readRepoConfigUnlessBlocked(root) {
14981
15156
  const gitRoot = root ?? findGitRoot();
14982
15157
  if (!gitRoot) return null;
14983
- const configPath3 = path3.join(gitRoot, ".wayai.yaml");
14984
- if (!fs3.existsSync(configPath3)) return null;
14985
- try {
14986
- const content = fs3.readFileSync(configPath3, "utf-8");
14987
- const config = yaml2.load(content);
14988
- if (!config?.organization_id || typeof config.organization_id !== "string") {
14989
- if (config?.organization && typeof config.organization === "string") {
14990
- console.error(
14991
- ".wayai.yaml uses the old format (organization: name). Replace it with organization_id: <uuid>."
14992
- );
14993
- }
15158
+ const r = resolve2(gitRoot);
15159
+ switch (r.kind) {
15160
+ case "ok":
15161
+ report(r, "soft");
15162
+ return r.resolved.config;
15163
+ case "none":
14994
15164
  return null;
14995
- }
14996
- if (!UUID_RE2.test(config.organization_id)) return null;
14997
- const presentLegacy = LEGACY_FIELDS.filter((f) => config[f] !== void 0);
14998
- warnAboutLegacyFields(presentLegacy);
14999
- const organizationName = typeof config.organization_name === "string" && config.organization_name.trim() ? config.organization_name.trim() : void 0;
15165
+ case "conflict":
15166
+ throw expected(conflictMessage(r.manifestOrg, r.rootOrg));
15167
+ case "invalid":
15168
+ throw expected(invalidMessage(r.file, r.detail));
15169
+ }
15170
+ }
15171
+ function requireRepoConfig(root) {
15172
+ const gitRoot = root ?? findGitRoot();
15173
+ const r = gitRoot ? resolve2(gitRoot) : { kind: "none" };
15174
+ report(r, "hard");
15175
+ if (r.kind === "ok") return r.resolved.config;
15176
+ return process.exit(1);
15177
+ }
15178
+ function readRepoScopeBlocker(root) {
15179
+ const gitRoot = root ?? findGitRoot();
15180
+ if (!gitRoot) return null;
15181
+ const r = resolve2(gitRoot);
15182
+ if (r.kind === "conflict") {
15000
15183
  return {
15001
- organization_id: config.organization_id,
15002
- organization_name: organizationName
15184
+ kind: "conflict",
15185
+ manifest_organization_id: r.manifestOrg,
15186
+ root_organization_id: r.rootOrg
15003
15187
  };
15004
- } catch {
15005
- return null;
15006
15188
  }
15189
+ if (r.kind === "invalid") return { kind: "invalid", path: r.file, detail: r.detail };
15190
+ return null;
15007
15191
  }
15008
- function requireRepoConfig(root) {
15009
- const config = readRepoConfig(root);
15010
- if (!config) {
15011
- console.error("Missing or invalid .wayai.yaml at the repository root.");
15012
- console.error("The file must contain organization_id:");
15013
- console.error("");
15014
- console.error(" organization_id: <your-org-uuid>");
15015
- console.error("");
15016
- console.error("Run `wayai init` to set up the workspace.");
15017
- process.exit(1);
15192
+ function planOrgMigration(gitRoot) {
15193
+ const manifest = declarationFrom(loadWorkspaceManifest(gitRoot));
15194
+ const root = declarationFrom(loadYamlMapping(rootConfigPath(gitRoot)));
15195
+ if (root.kind !== "declared") return { kind: "nothing" };
15196
+ if (manifest.kind === "declared") {
15197
+ if (manifest.config.organization_id === root.config.organization_id) return { kind: "nothing" };
15198
+ return {
15199
+ kind: "refuse",
15200
+ message: conflictMessage(manifest.config.organization_id, root.config.organization_id, [
15201
+ "Reconcile them by hand \u2014 migrate cannot know which one you meant."
15202
+ ])
15203
+ };
15204
+ }
15205
+ if (manifest.kind === "invalid") {
15206
+ return {
15207
+ kind: "refuse",
15208
+ message: `Cannot copy the organization binding: ${WORKSPACE_MANIFEST_LABEL} is unusable \u2014 ${manifest.detail}. Fix or delete it, then re-run \`wayai migrate\`.`
15209
+ };
15018
15210
  }
15019
- return config;
15211
+ return { kind: "copy", config: root.config };
15020
15212
  }
15021
15213
  function writeRepoConfig(config, root) {
15022
15214
  const gitRoot = root ?? findGitRoot();
15023
15215
  if (!gitRoot) {
15024
15216
  throw new Error("Not inside a git repository. Run `git init` first.");
15025
15217
  }
15026
- const configPath3 = path3.join(gitRoot, ".wayai.yaml");
15218
+ const stale = declarationFrom(loadYamlMapping(rootConfigPath(gitRoot)));
15219
+ if (stale.kind === "declared" && stale.config.organization_id !== config.organization_id) {
15220
+ throw expected(rebindRefusal(config.organization_id, stale.config.organization_id));
15221
+ }
15222
+ const load11 = loadWorkspaceManifest(gitRoot);
15223
+ if (load11.kind === "malformed") {
15224
+ throw expected(
15225
+ `Cannot write the organization binding: ${WORKSPACE_MANIFEST_LABEL} does not parse (${load11.reason}). Fix or delete it, then re-run.`
15226
+ );
15227
+ }
15228
+ const { organization_id: _id, organization_name: _name, ...rest } = load11.kind === "ok" ? load11.doc : {};
15027
15229
  const doc = { organization_id: config.organization_id };
15028
15230
  if (config.organization_name?.trim()) doc.organization_name = config.organization_name.trim();
15029
- const content = yaml2.dump(doc, { quotingType: '"', forceQuotes: false });
15030
- fs3.writeFileSync(configPath3, content, "utf-8");
15031
- return configPath3;
15231
+ Object.assign(doc, rest);
15232
+ const file = workspaceManifestPath(gitRoot);
15233
+ fs4.mkdirSync(path4.dirname(file), { recursive: true });
15234
+ fs4.writeFileSync(
15235
+ file,
15236
+ yaml3.dump(doc, { lineWidth: -1, quotingType: '"', forceQuotes: false }),
15237
+ "utf-8"
15238
+ );
15239
+ return file;
15032
15240
  }
15033
- var LEGACY_FIELDS, legacyFieldsWarned;
15241
+ var LEGACY_FIELDS, noticed, RECONCILE_REMEDY;
15034
15242
  var init_repo_config = __esm({
15035
15243
  "src/lib/repo-config.ts"() {
15036
15244
  "use strict";
15245
+ init_expected();
15037
15246
  init_workspace();
15038
15247
  init_utils();
15248
+ init_workspace_manifest();
15039
15249
  LEGACY_FIELDS = ["project_id", "project_name", "hub_id", "hub_name"];
15040
- legacyFieldsWarned = false;
15250
+ noticed = /* @__PURE__ */ new Set();
15251
+ RECONCILE_REMEDY = [
15252
+ "Keep the organization_id you actually work in and delete the other declaration.",
15253
+ `\`wayai init\` rewrites ${WORKSPACE_MANIFEST_LABEL}; the root file is yours to remove.`
15254
+ ];
15041
15255
  }
15042
15256
  });
15043
15257
 
15044
15258
  // src/lib/utils.ts
15045
- import * as fs4 from "fs";
15046
- import * as path4 from "path";
15259
+ import * as fs5 from "fs";
15260
+ import * as path5 from "path";
15047
15261
  import * as readline from "readline";
15048
15262
  function prompt(question) {
15049
15263
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
15050
- return new Promise((resolve7) => {
15264
+ return new Promise((resolve8) => {
15051
15265
  rl.question(question, (answer) => {
15052
15266
  rl.close();
15053
- resolve7(answer.trim());
15267
+ resolve8(answer.trim());
15054
15268
  });
15055
15269
  });
15056
15270
  }
15057
15271
  function confirm(question) {
15058
15272
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
15059
- return new Promise((resolve7) => {
15273
+ return new Promise((resolve8) => {
15060
15274
  rl.question(`${question} [y/N]: `, (answer) => {
15061
15275
  rl.close();
15062
- resolve7(answer.trim().toLowerCase() === "y");
15276
+ resolve8(answer.trim().toLowerCase() === "y");
15063
15277
  });
15064
15278
  });
15065
15279
  }
15066
15280
  function promptSecret(question) {
15067
- return new Promise((resolve7) => {
15281
+ return new Promise((resolve8) => {
15068
15282
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
15069
15283
  const originalWrite = rl._writeToOutput;
15070
15284
  let firstWrite = true;
@@ -15080,25 +15294,25 @@ function promptSecret(question) {
15080
15294
  rl._writeToOutput = originalWrite;
15081
15295
  process.stdout.write("\n");
15082
15296
  rl.close();
15083
- resolve7(answer);
15297
+ resolve8(answer);
15084
15298
  });
15085
15299
  });
15086
15300
  }
15087
15301
  function readStdin() {
15088
- return new Promise((resolve7, reject) => {
15302
+ return new Promise((resolve8, reject) => {
15089
15303
  let data = "";
15090
15304
  process.stdin.setEncoding("utf-8");
15091
15305
  process.stdin.on("data", (chunk) => {
15092
15306
  data += chunk;
15093
15307
  });
15094
- process.stdin.on("end", () => resolve7(data.trim()));
15308
+ process.stdin.on("end", () => resolve8(data.trim()));
15095
15309
  process.stdin.on("error", reject);
15096
15310
  });
15097
15311
  }
15098
15312
  function resolveHubYamlPath(hubFolder) {
15099
15313
  for (const filename of ["hub.yaml", "wayai.yaml"]) {
15100
- const yamlPath = path4.join(hubFolder, filename);
15101
- if (fs4.existsSync(yamlPath)) return yamlPath;
15314
+ const yamlPath = path5.join(hubFolder, filename);
15315
+ if (fs5.existsSync(yamlPath)) return yamlPath;
15102
15316
  }
15103
15317
  return null;
15104
15318
  }
@@ -15175,8 +15389,8 @@ async function resolveOrganizationId(client, orgIdFlag) {
15175
15389
  }
15176
15390
  return { organizationId: orgIdFlag };
15177
15391
  }
15178
- const { readRepoConfig: readRepoConfig2 } = await Promise.resolve().then(() => (init_repo_config(), repo_config_exports));
15179
- const repoConfig = readRepoConfig2();
15392
+ const { readRepoConfigUnlessBlocked: readRepoConfigUnlessBlocked2 } = await Promise.resolve().then(() => (init_repo_config(), repo_config_exports));
15393
+ const repoConfig = readRepoConfigUnlessBlocked2();
15180
15394
  if (repoConfig) {
15181
15395
  return { organizationId: repoConfig.organization_id, organizationName: repoConfig.organization_name };
15182
15396
  }
@@ -15312,7 +15526,7 @@ var init_registry = __esm({
15312
15526
  surviving: null,
15313
15527
  clause: 1,
15314
15528
  shipped: true,
15315
- reason: "One root org binding (`.wayai.yaml`), so one `init`."
15529
+ reason: "One org binding per repo (`wayai-ws/wayai.yaml`), so one `init`."
15316
15530
  },
15317
15531
  {
15318
15532
  original: "update",
@@ -15563,8 +15777,8 @@ var init_registry = __esm({
15563
15777
  });
15564
15778
 
15565
15779
  // src/lib/version-cache.ts
15566
- import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync } from "fs";
15567
- import { dirname as dirname3, join as join6 } from "path";
15780
+ import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
15781
+ import { dirname as dirname4, join as join6 } from "path";
15568
15782
  import { homedir as homedir2 } from "os";
15569
15783
  function getVersionCachePath(filename = CLI_CACHE_FILE) {
15570
15784
  return join6(homedir2(), ".wayai", filename);
@@ -15572,7 +15786,7 @@ function getVersionCachePath(filename = CLI_CACHE_FILE) {
15572
15786
  function readVersionCache(filename = CLI_CACHE_FILE) {
15573
15787
  try {
15574
15788
  const path35 = getVersionCachePath(filename);
15575
- if (!existsSync4(path35)) return null;
15789
+ if (!existsSync3(path35)) return null;
15576
15790
  const parsed = JSON.parse(readFileSync5(path35, "utf-8"));
15577
15791
  if (typeof parsed.lastCheck !== "number") return null;
15578
15792
  if (parsed.latest !== null && typeof parsed.latest !== "string") return null;
@@ -15594,8 +15808,8 @@ function isVersionCacheStale(filename = CLI_CACHE_FILE, maxAgeMs = MAX_AGE_24H_M
15594
15808
  }
15595
15809
  function writeVersionCache(filename, cache) {
15596
15810
  const path35 = getVersionCachePath(filename);
15597
- const dir = dirname3(path35);
15598
- if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
15811
+ const dir = dirname4(path35);
15812
+ if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
15599
15813
  writeFileSync2(path35, JSON.stringify(cache));
15600
15814
  }
15601
15815
  function touchVersionCache(filename) {
@@ -15613,9 +15827,9 @@ var init_version_cache = __esm({
15613
15827
  });
15614
15828
 
15615
15829
  // src/lib/skill-version.ts
15616
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
15830
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
15617
15831
  import { join as join7 } from "path";
15618
- import * as yaml3 from "js-yaml";
15832
+ import * as yaml4 from "js-yaml";
15619
15833
  function skillInstallPaths(skillName) {
15620
15834
  return HARNESS_SKILL_DIRS.map((dir) => `${dir}/skills/${skillName}/${SKILL_FILENAME}`);
15621
15835
  }
@@ -15624,7 +15838,7 @@ function parseFrontmatterVersion(content) {
15624
15838
  if (!match) return null;
15625
15839
  let parsed;
15626
15840
  try {
15627
- parsed = yaml3.load(match[1]);
15841
+ parsed = yaml4.load(match[1]);
15628
15842
  } catch {
15629
15843
  return null;
15630
15844
  }
@@ -15636,7 +15850,7 @@ function findInstalledSkills(projectRoot, paths = SKILL_INSTALL_PATHS) {
15636
15850
  const found = [];
15637
15851
  for (const rel of paths) {
15638
15852
  const path35 = join7(projectRoot, rel);
15639
- if (!existsSync5(path35)) continue;
15853
+ if (!existsSync4(path35)) continue;
15640
15854
  let version = null;
15641
15855
  try {
15642
15856
  version = parseFrontmatterVersion(readFileSync6(path35, "utf-8"));
@@ -15673,22 +15887,22 @@ __export(config_exports, {
15673
15887
  readConfig: () => readConfig,
15674
15888
  writeConfig: () => writeConfig
15675
15889
  });
15676
- import * as fs5 from "fs";
15677
- import * as path5 from "path";
15890
+ import * as fs6 from "fs";
15891
+ import * as path6 from "path";
15678
15892
  import * as os from "os";
15679
15893
  function configDir() {
15680
- return path5.join(os.homedir(), ".wayai");
15894
+ return path6.join(os.homedir(), ".wayai");
15681
15895
  }
15682
15896
  function configPath() {
15683
- return path5.join(configDir(), "config.json");
15897
+ return path6.join(configDir(), "config.json");
15684
15898
  }
15685
15899
  function getConfigPath() {
15686
15900
  return configPath();
15687
15901
  }
15688
15902
  function readConfig() {
15689
- if (!fs5.existsSync(configPath())) return null;
15903
+ if (!fs6.existsSync(configPath())) return null;
15690
15904
  try {
15691
- const content = fs5.readFileSync(configPath(), "utf-8");
15905
+ const content = fs6.readFileSync(configPath(), "utf-8");
15692
15906
  const parsed = JSON.parse(content);
15693
15907
  if (!parsed.api_url) return null;
15694
15908
  return {
@@ -15701,13 +15915,13 @@ function readConfig() {
15701
15915
  }
15702
15916
  }
15703
15917
  function writeConfig(config) {
15704
- if (!fs5.existsSync(configDir())) {
15705
- fs5.mkdirSync(configDir(), { recursive: true });
15918
+ if (!fs6.existsSync(configDir())) {
15919
+ fs6.mkdirSync(configDir(), { recursive: true });
15706
15920
  }
15707
15921
  let existing = {};
15708
- if (fs5.existsSync(configPath())) {
15922
+ if (fs6.existsSync(configPath())) {
15709
15923
  try {
15710
- existing = JSON.parse(fs5.readFileSync(configPath(), "utf-8"));
15924
+ existing = JSON.parse(fs6.readFileSync(configPath(), "utf-8"));
15711
15925
  } catch {
15712
15926
  }
15713
15927
  }
@@ -15715,14 +15929,14 @@ function writeConfig(config) {
15715
15929
  delete existing.access_token;
15716
15930
  delete existing.refresh_token;
15717
15931
  Object.assign(existing, config);
15718
- fs5.writeFileSync(configPath(), JSON.stringify(existing, null, 2) + "\n", {
15932
+ fs6.writeFileSync(configPath(), JSON.stringify(existing, null, 2) + "\n", {
15719
15933
  mode: 384
15720
15934
  // owner-only read/write
15721
15935
  });
15722
15936
  }
15723
15937
  function deleteConfig() {
15724
- if (fs5.existsSync(configPath())) {
15725
- fs5.unlinkSync(configPath());
15938
+ if (fs6.existsSync(configPath())) {
15939
+ fs6.unlinkSync(configPath());
15726
15940
  }
15727
15941
  }
15728
15942
  var init_config = __esm({
@@ -15732,14 +15946,14 @@ var init_config = __esm({
15732
15946
  });
15733
15947
 
15734
15948
  // src/lib/token-store.ts
15735
- import * as fs6 from "fs";
15736
- import * as path6 from "path";
15949
+ import * as fs7 from "fs";
15950
+ import * as path7 from "path";
15737
15951
  import * as os2 from "os";
15738
15952
  function configDir2() {
15739
- return path6.join(os2.homedir(), ".wayai");
15953
+ return path7.join(os2.homedir(), ".wayai");
15740
15954
  }
15741
15955
  function configPath2() {
15742
- return path6.join(configDir2(), "config.json");
15956
+ return path7.join(configDir2(), "config.json");
15743
15957
  }
15744
15958
  function manualCleanupCommand(account) {
15745
15959
  if (process.platform === "darwin") {
@@ -15791,9 +16005,9 @@ async function getKeyring() {
15791
16005
  return keyringCache;
15792
16006
  }
15793
16007
  function readLegacyConfigFile() {
15794
- if (!fs6.existsSync(configPath2())) return null;
16008
+ if (!fs7.existsSync(configPath2())) return null;
15795
16009
  try {
15796
- return JSON.parse(fs6.readFileSync(configPath2(), "utf-8"));
16010
+ return JSON.parse(fs7.readFileSync(configPath2(), "utf-8"));
15797
16011
  } catch {
15798
16012
  return null;
15799
16013
  }
@@ -15806,7 +16020,7 @@ function stripTokensFromFile() {
15806
16020
  if (k === "token" || k === "access_token" || k === "refresh_token") continue;
15807
16021
  cleaned[k] = v;
15808
16022
  }
15809
- fs6.writeFileSync(configPath2(), JSON.stringify(cleaned, null, 2) + "\n", { mode: 384 });
16023
+ fs7.writeFileSync(configPath2(), JSON.stringify(cleaned, null, 2) + "\n", { mode: 384 });
15810
16024
  }
15811
16025
  async function migrateLegacyTokens() {
15812
16026
  const legacy = readLegacyConfigFile();
@@ -15957,8 +16171,8 @@ async function clearTokens() {
15957
16171
  }
15958
16172
  }
15959
16173
  }
15960
- if (fs6.existsSync(configPath2())) {
15961
- fs6.unlinkSync(configPath2());
16174
+ if (fs7.existsSync(configPath2())) {
16175
+ fs7.unlinkSync(configPath2());
15962
16176
  }
15963
16177
  }
15964
16178
  function readMetadataExpiresAt() {
@@ -15972,7 +16186,7 @@ function writeMetadataExpiresAt(expires_at) {
15972
16186
  delete existing.refresh_token;
15973
16187
  existing.token_expires_at = expires_at;
15974
16188
  ensureConfigDir();
15975
- fs6.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
16189
+ fs7.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
15976
16190
  }
15977
16191
  function clearMetadataExpiresAt() {
15978
16192
  const existing = readLegacyConfigFile();
@@ -15982,11 +16196,11 @@ function clearMetadataExpiresAt() {
15982
16196
  delete existing.refresh_token;
15983
16197
  delete existing.token_expires_at;
15984
16198
  ensureConfigDir();
15985
- fs6.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
16199
+ fs7.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
15986
16200
  }
15987
16201
  function ensureConfigDir() {
15988
- if (!fs6.existsSync(configDir2())) {
15989
- fs6.mkdirSync(configDir2(), { recursive: true });
16202
+ if (!fs7.existsSync(configDir2())) {
16203
+ fs7.mkdirSync(configDir2(), { recursive: true });
15990
16204
  }
15991
16205
  }
15992
16206
  function writeFileFallback(tokens) {
@@ -15997,7 +16211,7 @@ function writeFileFallback(tokens) {
15997
16211
  delete existing.token_expires_at;
15998
16212
  Object.assign(existing, tokens);
15999
16213
  ensureConfigDir();
16000
- fs6.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
16214
+ fs7.writeFileSync(configPath2(), JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
16001
16215
  }
16002
16216
  var KEYRING_SERVICE, KEY_WAY_TOKEN, KEY_OAUTH_ACCESS, KEY_OAUTH_REFRESH, keyringCache, StaleKeyringSlotError;
16003
16217
  var init_token_store = __esm({
@@ -16106,7 +16320,7 @@ async function validateToken(apiUrl, token) {
16106
16320
  }
16107
16321
  }
16108
16322
  function startCallbackServer(port, expectedState, timeoutMs = 12e4) {
16109
- return new Promise((resolve7, reject) => {
16323
+ return new Promise((resolve8, reject) => {
16110
16324
  const server = http.createServer((req, res) => {
16111
16325
  const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
16112
16326
  if (url.pathname === "/callback") {
@@ -16132,7 +16346,7 @@ function startCallbackServer(port, expectedState, timeoutMs = 12e4) {
16132
16346
  res.writeHead(200, { "Content-Type": "text/html" });
16133
16347
  res.end("<html><body><h2>Login successful!</h2><p>You can close this tab and return to your terminal.</p></body></html>");
16134
16348
  server.close();
16135
- resolve7({ code, port });
16349
+ resolve8({ code, port });
16136
16350
  } else {
16137
16351
  res.writeHead(400, { "Content-Type": "text/html" });
16138
16352
  res.end("<html><body><h2>Login failed</h2><p>No authorization code received</p></body></html>");
@@ -16239,14 +16453,14 @@ __export(skill_symlink_exports, {
16239
16453
  healClaudeSkillLink: () => healClaudeSkillLink,
16240
16454
  healSkillLinkForCommand: () => healSkillLinkForCommand
16241
16455
  });
16242
- import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync4, rmSync, symlinkSync } from "fs";
16456
+ import { existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync5, rmSync, symlinkSync } from "fs";
16243
16457
  import { join as join10 } from "path";
16244
16458
  function healClaudeSkillLink(root) {
16245
16459
  try {
16246
16460
  const source = join10(root, ".agents", "skills", SKILL_NAME);
16247
- if (!existsSync8(join10(source, SKILL_FILENAME))) return false;
16461
+ if (!existsSync7(join10(source, SKILL_FILENAME))) return false;
16248
16462
  const link = join10(root, ".claude", "skills", SKILL_NAME);
16249
- if (existsSync8(join10(link, SKILL_FILENAME))) return false;
16463
+ if (existsSync7(join10(link, SKILL_FILENAME))) return false;
16250
16464
  let entry = null;
16251
16465
  try {
16252
16466
  entry = lstatSync(link);
@@ -16257,7 +16471,7 @@ function healClaudeSkillLink(root) {
16257
16471
  if (!entry.isSymbolicLink()) return false;
16258
16472
  rmSync(link, { force: true });
16259
16473
  }
16260
- mkdirSync4(join10(root, ".claude", "skills"), { recursive: true });
16474
+ mkdirSync5(join10(root, ".claude", "skills"), { recursive: true });
16261
16475
  symlinkSync(join10("..", "..", ".agents", "skills", SKILL_NAME), link, "dir");
16262
16476
  return true;
16263
16477
  } catch {
@@ -16344,10 +16558,10 @@ import * as readline2 from "readline";
16344
16558
  function prompt2(question, defaultValue) {
16345
16559
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
16346
16560
  const display = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
16347
- return new Promise((resolve7) => {
16561
+ return new Promise((resolve8) => {
16348
16562
  rl.question(display, (answer) => {
16349
16563
  rl.close();
16350
- resolve7(answer.trim() || defaultValue || "");
16564
+ resolve8(answer.trim() || defaultValue || "");
16351
16565
  });
16352
16566
  });
16353
16567
  }
@@ -16475,8 +16689,8 @@ async function tryOpenBrowser(url) {
16475
16689
  cmd = "xdg-open";
16476
16690
  args2 = [url];
16477
16691
  }
16478
- return new Promise((resolve7) => {
16479
- execFile(cmd, args2, (err) => resolve7(!err));
16692
+ return new Promise((resolve8) => {
16693
+ execFile(cmd, args2, (err) => resolve8(!err));
16480
16694
  });
16481
16695
  } catch {
16482
16696
  return false;
@@ -16541,9 +16755,9 @@ var init_base_id = __esm({
16541
16755
 
16542
16756
  // src/lib/worktree-scope.ts
16543
16757
  import { execFileSync as execFileSync2 } from "child_process";
16544
- import * as fs7 from "fs";
16545
- import * as path7 from "path";
16546
- import * as yaml4 from "js-yaml";
16758
+ import * as fs8 from "fs";
16759
+ import * as path8 from "path";
16760
+ import * as yaml5 from "js-yaml";
16547
16761
  function axisNoun(axis) {
16548
16762
  return AXES[axis].noun;
16549
16763
  }
@@ -16557,7 +16771,7 @@ function resolveGitDir() {
16557
16771
  encoding: "utf-8",
16558
16772
  stdio: ["pipe", "pipe", "pipe"]
16559
16773
  }).trim();
16560
- gitDir = raw ? path7.resolve(cwd, raw) : null;
16774
+ gitDir = raw ? path8.resolve(cwd, raw) : null;
16561
16775
  } catch {
16562
16776
  gitDir = null;
16563
16777
  }
@@ -16566,12 +16780,12 @@ function resolveGitDir() {
16566
16780
  }
16567
16781
  function getScopePath() {
16568
16782
  const gitDir = resolveGitDir();
16569
- return gitDir ? path7.join(gitDir, SCOPE_FILE) : null;
16783
+ return gitDir ? path8.join(gitDir, SCOPE_FILE) : null;
16570
16784
  }
16571
16785
  function legacyPaths() {
16572
16786
  const gitDir = resolveGitDir();
16573
16787
  if (!gitDir) return [];
16574
- return AXIS_ORDER.flatMap((axis) => AXES[axis].legacyFiles).map((f) => path7.join(gitDir, f));
16788
+ return AXIS_ORDER.flatMap((axis) => AXES[axis].legacyFiles).map((f) => path8.join(gitDir, f));
16575
16789
  }
16576
16790
  function emptyScope() {
16577
16791
  return { hubs: [], bases: [] };
@@ -16595,7 +16809,7 @@ function readLegacyAxis(axis) {
16595
16809
  for (const filename of legacyFiles) {
16596
16810
  let raw;
16597
16811
  try {
16598
- raw = fs7.readFileSync(path7.join(gitDir, filename), "utf-8");
16812
+ raw = fs8.readFileSync(path8.join(gitDir, filename), "utf-8");
16599
16813
  } catch (err) {
16600
16814
  if (err.code === "ENOENT") continue;
16601
16815
  throw err;
@@ -16610,7 +16824,7 @@ function readScope() {
16610
16824
  if (!scopePath) return emptyScope();
16611
16825
  let raw;
16612
16826
  try {
16613
- raw = fs7.readFileSync(scopePath, "utf-8");
16827
+ raw = fs8.readFileSync(scopePath, "utf-8");
16614
16828
  } catch (err) {
16615
16829
  if (err.code === "ENOENT") {
16616
16830
  return { hubs: readLegacyAxis("hubs"), bases: readLegacyAxis("bases") };
@@ -16619,7 +16833,7 @@ function readScope() {
16619
16833
  }
16620
16834
  let doc;
16621
16835
  try {
16622
- doc = yaml4.load(raw);
16836
+ doc = yaml5.load(raw);
16623
16837
  } catch {
16624
16838
  return emptyScope();
16625
16839
  }
@@ -16630,7 +16844,7 @@ function readScope() {
16630
16844
  function sweepLegacyFiles() {
16631
16845
  for (const legacyPath of legacyPaths()) {
16632
16846
  try {
16633
- fs7.unlinkSync(legacyPath);
16847
+ fs8.unlinkSync(legacyPath);
16634
16848
  } catch {
16635
16849
  }
16636
16850
  }
@@ -16646,14 +16860,14 @@ function writeScope(scope) {
16646
16860
  if (!scopePath) {
16647
16861
  throw expected("Not inside a git repository \u2014 cannot write the worktree scope.");
16648
16862
  }
16649
- const body = yaml4.dump({ hubs: scope.hubs, bases: scope.bases }, { flowLevel: 1 });
16863
+ const body = yaml5.dump({ hubs: scope.hubs, bases: scope.bases }, { flowLevel: 1 });
16650
16864
  const tmpPath = `${scopePath}.tmp-${process.pid}`;
16651
16865
  try {
16652
- fs7.writeFileSync(tmpPath, body, "utf-8");
16653
- fs7.renameSync(tmpPath, scopePath);
16866
+ fs8.writeFileSync(tmpPath, body, "utf-8");
16867
+ fs8.renameSync(tmpPath, scopePath);
16654
16868
  } catch (err) {
16655
16869
  try {
16656
- fs7.unlinkSync(tmpPath);
16870
+ fs8.unlinkSync(tmpPath);
16657
16871
  } catch {
16658
16872
  }
16659
16873
  throw asExpectedEnvironmentError(err);
@@ -16689,7 +16903,7 @@ function clearScope() {
16689
16903
  let removed = false;
16690
16904
  for (const target of [...scopePath ? [scopePath] : [], ...legacyPaths()]) {
16691
16905
  try {
16692
- fs7.unlinkSync(target);
16906
+ fs8.unlinkSync(target);
16693
16907
  removed = true;
16694
16908
  } catch (err) {
16695
16909
  if (err.code !== "ENOENT") throw err;
@@ -16794,7 +17008,6 @@ __export(status_exports, {
16794
17008
  parseArgs: () => parseArgs,
16795
17009
  statusCommand: () => statusCommand
16796
17010
  });
16797
- import * as path8 from "path";
16798
17011
  function parseArgs(args2) {
16799
17012
  return { json: args2.includes("--json") };
16800
17013
  }
@@ -16814,19 +17027,19 @@ function buildSkillState() {
16814
17027
  paths: installs.map((i) => i.path)
16815
17028
  };
16816
17029
  }
16817
- function buildWorkspaceState(repoConfig) {
16818
- if (!repoConfig) return { scoped: false, path: null, hub_count: 0 };
17030
+ function buildWorkspaceState(resolved) {
17031
+ if (!resolved) return { scoped: false, path: null, hub_count: 0 };
16819
17032
  const ws = detectWorkspace();
16820
- const gitRoot = ws?.gitRoot ?? findGitRoot();
16821
17033
  const hubCount = ws ? scanWorkspaceHubs(ws.workspaceDir).length : 0;
16822
- const yamlPath = gitRoot ? path8.join(gitRoot, ".wayai.yaml") : null;
16823
- return { scoped: true, path: yamlPath, hub_count: hubCount };
17034
+ return { scoped: true, path: resolved.path, hub_count: hubCount };
16824
17035
  }
16825
17036
  async function statusCommand(args2, pkg2) {
16826
17037
  const { json } = parseArgs(args2);
16827
17038
  const config = readConfig();
16828
- const repoConfig = readRepoConfig();
16829
- const workspace = buildWorkspaceState(repoConfig);
17039
+ const resolvedRepo = resolveRepoConfig();
17040
+ const repoConfig = resolvedRepo?.config ?? null;
17041
+ const workspace = buildWorkspaceState(resolvedRepo);
17042
+ const repoScopeBlocker = resolvedRepo ? null : readRepoScopeBlocker();
16830
17043
  const cachedCliLatest = readCachedLatest(CLI_CACHE_FILE);
16831
17044
  const cliLatest = cachedCliLatest && isNewerVersion(cachedCliLatest, pkg2.version) ? cachedCliLatest : null;
16832
17045
  const skill = buildSkillState();
@@ -16849,6 +17062,7 @@ async function statusCommand(args2, pkg2) {
16849
17062
  orgs: [],
16850
17063
  active_org: null,
16851
17064
  workspace,
17065
+ repo_scope_blocker: repoScopeBlocker,
16852
17066
  worktree_scope: scope,
16853
17067
  worktree_binding: { hub_id: boundHubId },
16854
17068
  worktree_lock: { hub_id: boundHubId }
@@ -16905,6 +17119,7 @@ async function statusCommand(args2, pkg2) {
16905
17119
  orgs,
16906
17120
  active_org: activeOrg,
16907
17121
  workspace,
17122
+ repo_scope_blocker: repoScopeBlocker,
16908
17123
  worktree_scope: scope,
16909
17124
  worktree_binding: { hub_id: boundHubId },
16910
17125
  worktree_lock: { hub_id: boundHubId }
@@ -16934,8 +17149,17 @@ async function statusCommand(args2, pkg2) {
16934
17149
  console.log(`Repository scope:`);
16935
17150
  console.log(` Organization: ${orgLine}`);
16936
17151
  console.log(` Hubs: ${workspace.hub_count}`);
17152
+ } else if (repoScopeBlocker?.kind === "conflict") {
17153
+ console.log("Repository scope: UNRESOLVED \u2014 two files name different organizations:");
17154
+ console.log(` ${WORKSPACE_MANIFEST_LABEL} -> ${repoScopeBlocker.manifest_organization_id}`);
17155
+ console.log(` ${ROOT_CONFIG_FILE} -> ${repoScopeBlocker.root_organization_id}`);
17156
+ console.log(` Delete or correct ${ROOT_CONFIG_FILE}; until then every command refuses.`);
17157
+ } else if (repoScopeBlocker?.kind === "invalid") {
17158
+ console.log(`Repository scope: UNRESOLVED \u2014 ${repoScopeBlocker.path} is unusable (${repoScopeBlocker.detail}).`);
16937
17159
  } else {
16938
- console.log("No .wayai.yaml found \u2014 run `wayai init` to scope this repo to an organization.");
17160
+ console.log(
17161
+ `No organization binding found in ${WORKSPACE_MANIFEST_LABEL} \u2014 run \`wayai init\` to scope this repo to an organization.`
17162
+ );
16939
17163
  }
16940
17164
  if (scope.hubs.length > 0 || scope.bases.length > 0) {
16941
17165
  console.log("Worktree scope: (run `wayai unbind` to clear)");
@@ -16952,6 +17176,7 @@ var init_status = __esm({
16952
17176
  init_config();
16953
17177
  init_auth();
16954
17178
  init_repo_config();
17179
+ init_workspace_manifest();
16955
17180
  init_workspace();
16956
17181
  init_worktree_scope();
16957
17182
  init_api_client();
@@ -17035,7 +17260,7 @@ var init_constants = __esm({
17035
17260
  });
17036
17261
 
17037
17262
  // src/lib/workspace-files.ts
17038
- import * as fs8 from "fs";
17263
+ import * as fs9 from "fs";
17039
17264
  import * as path9 from "path";
17040
17265
  function perHubAgentsMd(hubFolderName) {
17041
17266
  return [
@@ -17052,12 +17277,13 @@ function perHubAgentsMd(hubFolderName) {
17052
17277
  ].join("\n");
17053
17278
  }
17054
17279
  function isWorkspace(gitRoot) {
17055
- if (fs8.existsSync(path9.join(gitRoot, ".wayai.yaml"))) return true;
17280
+ if (fs9.existsSync(workspaceManifestPath(gitRoot))) return true;
17281
+ if (fs9.existsSync(rootConfigPath(gitRoot))) return true;
17056
17282
  return isDirectory(resolveLayout(gitRoot).hubsDir);
17057
17283
  }
17058
17284
  function writeIfAbsent(filePath, content) {
17059
- if (fs8.existsSync(filePath)) return null;
17060
- fs8.writeFileSync(filePath, content, "utf-8");
17285
+ if (fs9.existsSync(filePath)) return null;
17286
+ fs9.writeFileSync(filePath, content, "utf-8");
17061
17287
  return path9.basename(filePath);
17062
17288
  }
17063
17289
  function ensureWorkspaceFiles(gitRoot) {
@@ -17075,6 +17301,7 @@ var init_workspace_files = __esm({
17075
17301
  "use strict";
17076
17302
  init_constants();
17077
17303
  init_layout();
17304
+ init_workspace_manifest();
17078
17305
  CLAUDE_MD_SHIM = "@AGENTS.md\n";
17079
17306
  ROOT_AGENTS_MD = [
17080
17307
  "# Working with WayAI",
@@ -17112,12 +17339,12 @@ __export(init_exports, {
17112
17339
  initCommand: () => initCommand,
17113
17340
  parseArgs: () => parseArgs3
17114
17341
  });
17115
- import { mkdirSync as mkdirSync5 } from "fs";
17342
+ import { mkdirSync as mkdirSync6 } from "fs";
17116
17343
  import path10 from "path";
17117
17344
  function ensureHubsDir(gitRoot) {
17118
17345
  const hubsDir = resolveLayout(gitRoot).hubsDir;
17119
17346
  if (isDirectory(hubsDir)) return null;
17120
- mkdirSync5(hubsDir, { recursive: true });
17347
+ mkdirSync6(hubsDir, { recursive: true });
17121
17348
  return `${path10.relative(gitRoot, hubsDir)}/`;
17122
17349
  }
17123
17350
  function parseArgs3(args2) {
@@ -17252,18 +17479,18 @@ var init_init = __esm({
17252
17479
  });
17253
17480
 
17254
17481
  // src/lib/fs-safety.ts
17255
- import * as fs9 from "fs";
17482
+ import * as fs10 from "fs";
17256
17483
  import * as path11 from "path";
17257
17484
  function fileHasBytes(abs, data) {
17258
17485
  let st;
17259
17486
  try {
17260
- st = fs9.lstatSync(abs);
17487
+ st = fs10.lstatSync(abs);
17261
17488
  } catch {
17262
17489
  return false;
17263
17490
  }
17264
17491
  if (!st.isFile() || st.isSymbolicLink()) return false;
17265
17492
  try {
17266
- return fs9.readFileSync(abs).equals(data);
17493
+ return fs10.readFileSync(abs).equals(data);
17267
17494
  } catch {
17268
17495
  return false;
17269
17496
  }
@@ -17272,8 +17499,8 @@ function realpathContained(root, abs) {
17272
17499
  let realRoot;
17273
17500
  let realAbs;
17274
17501
  try {
17275
- realRoot = fs9.realpathSync(root);
17276
- realAbs = fs9.realpathSync(abs);
17502
+ realRoot = fs10.realpathSync(root);
17503
+ realAbs = fs10.realpathSync(abs);
17277
17504
  } catch {
17278
17505
  return false;
17279
17506
  }
@@ -17281,13 +17508,13 @@ function realpathContained(root, abs) {
17281
17508
  }
17282
17509
  function mkdirTolerateRace(dir) {
17283
17510
  try {
17284
- fs9.mkdirSync(dir);
17511
+ fs10.mkdirSync(dir);
17285
17512
  return true;
17286
17513
  } catch (err) {
17287
17514
  if (err.code !== "EEXIST") throw err;
17288
17515
  let st;
17289
17516
  try {
17290
- st = fs9.lstatSync(dir);
17517
+ st = fs10.lstatSync(dir);
17291
17518
  } catch {
17292
17519
  st = void 0;
17293
17520
  }
@@ -17299,7 +17526,7 @@ function ensureRealSubdirNoSymlink(root, target, create) {
17299
17526
  const targetResolved = path11.resolve(target);
17300
17527
  if (targetResolved !== rootResolved && !targetResolved.startsWith(rootResolved + path11.sep)) return false;
17301
17528
  try {
17302
- if (!fs9.lstatSync(rootResolved).isDirectory()) return false;
17529
+ if (!fs10.lstatSync(rootResolved).isDirectory()) return false;
17303
17530
  } catch {
17304
17531
  return false;
17305
17532
  }
@@ -17310,7 +17537,7 @@ function ensureRealSubdirNoSymlink(root, target, create) {
17310
17537
  cur = path11.join(cur, segment);
17311
17538
  let st;
17312
17539
  try {
17313
- st = fs9.lstatSync(cur);
17540
+ st = fs10.lstatSync(cur);
17314
17541
  } catch {
17315
17542
  st = void 0;
17316
17543
  }
@@ -17327,8 +17554,8 @@ function ensureRealSubdirNoSymlink(root, target, create) {
17327
17554
  function readFileNoFollow(root, abs) {
17328
17555
  if (!ensureRealSubdirNoSymlink(root, path11.dirname(abs), false)) return null;
17329
17556
  try {
17330
- if (!fs9.lstatSync(abs).isFile()) return null;
17331
- return fs9.readFileSync(abs);
17557
+ if (!fs10.lstatSync(abs).isFile()) return null;
17558
+ return fs10.readFileSync(abs);
17332
17559
  } catch {
17333
17560
  return null;
17334
17561
  }
@@ -17338,11 +17565,11 @@ function writeFileNoFollow(root, abs, data) {
17338
17565
  if (!ensureRealSubdirNoSymlink(root, parent, true)) return false;
17339
17566
  let st;
17340
17567
  try {
17341
- st = fs9.lstatSync(abs);
17568
+ st = fs10.lstatSync(abs);
17342
17569
  } catch {
17343
17570
  }
17344
- if (st?.isSymbolicLink()) fs9.rmSync(abs);
17345
- fs9.writeFileSync(abs, data);
17571
+ if (st?.isSymbolicLink()) fs10.rmSync(abs);
17572
+ fs10.writeFileSync(abs, data);
17346
17573
  return true;
17347
17574
  }
17348
17575
  var init_fs_safety = __esm({
@@ -17352,7 +17579,7 @@ var init_fs_safety = __esm({
17352
17579
  });
17353
17580
 
17354
17581
  // src/lib/resource-files.ts
17355
- import * as fs10 from "fs";
17582
+ import * as fs11 from "fs";
17356
17583
  import * as path12 from "path";
17357
17584
  import * as crypto3 from "crypto";
17358
17585
  function isBinaryFile(filename) {
@@ -17399,14 +17626,14 @@ function computeHash(data) {
17399
17626
  }
17400
17627
  function scanResourceFiles(dir, prefix = "") {
17401
17628
  const files = [];
17402
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
17629
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
17403
17630
  for (const entry of entries) {
17404
17631
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
17405
17632
  if (entry.isDirectory()) {
17406
17633
  files.push(...scanResourceFiles(path12.join(dir, entry.name), relPath));
17407
17634
  } else if (entry.isFile()) {
17408
17635
  const fullPath = path12.join(dir, entry.name);
17409
- const stat2 = fs10.statSync(fullPath);
17636
+ const stat2 = fs11.statSync(fullPath);
17410
17637
  if (stat2.size > MAX_RESOURCE_FILE_SIZE3) {
17411
17638
  console.warn(` Warning: skipping ${relPath} (${(stat2.size / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit)`);
17412
17639
  continue;
@@ -17416,7 +17643,7 @@ function scanResourceFiles(dir, prefix = "") {
17416
17643
  mime_type: guessMimeType(entry.name),
17417
17644
  file_size: stat2.size
17418
17645
  };
17419
- const data = fs10.readFileSync(fullPath);
17646
+ const data = fs11.readFileSync(fullPath);
17420
17647
  fileEntry.hash = computeHash(data);
17421
17648
  if (isBinaryFile(entry.name)) {
17422
17649
  fileEntry.content_base64 = data.toString("base64");
@@ -17476,7 +17703,7 @@ function writeResourceFileTree(resDir, files, root, log) {
17476
17703
  return;
17477
17704
  }
17478
17705
  if (files.length === 0) {
17479
- if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
17706
+ if (fs11.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
17480
17707
  return;
17481
17708
  }
17482
17709
  const writtenPaths = /* @__PURE__ */ new Set();
@@ -17497,18 +17724,18 @@ function writeResourceFileTree(resDir, files, root, log) {
17497
17724
  writtenPaths.delete(file.path);
17498
17725
  }
17499
17726
  }
17500
- if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
17727
+ if (fs11.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
17501
17728
  }
17502
17729
  function cleanOrphanFiles(dir, prefix, writtenPaths, root, log) {
17503
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
17730
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
17504
17731
  for (const entry of entries) {
17505
17732
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
17506
17733
  const fullPath = path12.join(dir, entry.name);
17507
17734
  if (entry.isDirectory()) {
17508
17735
  cleanOrphanFiles(fullPath, relPath, writtenPaths, root, log);
17509
- if (fs10.readdirSync(fullPath).length === 0) fs10.rmdirSync(fullPath);
17736
+ if (fs11.readdirSync(fullPath).length === 0) fs11.rmdirSync(fullPath);
17510
17737
  } else if (!writtenPaths.has(relPath)) {
17511
- fs10.unlinkSync(fullPath);
17738
+ fs11.unlinkSync(fullPath);
17512
17739
  if (log && root) log.removed.push(path12.relative(root, fullPath));
17513
17740
  }
17514
17741
  }
@@ -17529,8 +17756,8 @@ async function downloadBinaryFiles(resDir, files, root, log) {
17529
17756
  }
17530
17757
  if (file.hash) {
17531
17758
  try {
17532
- const st = fs10.lstatSync(filePath);
17533
- if (st.isFile() && !st.isSymbolicLink() && computeHash(fs10.readFileSync(filePath)) === file.hash) continue;
17759
+ const st = fs11.lstatSync(filePath);
17760
+ if (st.isFile() && !st.isSymbolicLink() && computeHash(fs11.readFileSync(filePath)) === file.hash) continue;
17534
17761
  } catch {
17535
17762
  }
17536
17763
  }
@@ -17584,7 +17811,7 @@ var init_resource_files = __esm({
17584
17811
  });
17585
17812
 
17586
17813
  // src/lib/eval-attachments.ts
17587
- import * as fs11 from "fs";
17814
+ import * as fs12 from "fs";
17588
17815
  import * as path13 from "path";
17589
17816
  function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17590
17817
  const raw = turn.attachments;
@@ -17608,7 +17835,7 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17608
17835
  }
17609
17836
  let stat2;
17610
17837
  try {
17611
- stat2 = fs11.statSync(abs);
17838
+ stat2 = fs12.statSync(abs);
17612
17839
  } catch {
17613
17840
  }
17614
17841
  if (!stat2?.isFile()) {
@@ -17622,7 +17849,7 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17622
17849
  `${label}: attachment "${entry}" is ${(stat2.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
17623
17850
  );
17624
17851
  }
17625
- const data = fs11.readFileSync(abs);
17852
+ const data = fs12.readFileSync(abs);
17626
17853
  const hash = computeHash(data);
17627
17854
  const fileName = path13.basename(abs);
17628
17855
  const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
@@ -17730,16 +17957,16 @@ async function downloadEvalAttachments(hubFolder, downloads) {
17730
17957
  console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
17731
17958
  return result;
17732
17959
  }
17733
- if (!fs11.existsSync(attachDir)) return result;
17960
+ if (!fs12.existsSync(attachDir)) return result;
17734
17961
  for (const dl of downloads) {
17735
17962
  const abs = path13.resolve(hubFolder, dl.relPath);
17736
17963
  if (abs !== attachDir && !abs.startsWith(attachDir + path13.sep)) continue;
17737
17964
  let existing;
17738
17965
  try {
17739
- existing = fs11.lstatSync(abs);
17966
+ existing = fs12.lstatSync(abs);
17740
17967
  } catch {
17741
17968
  }
17742
- if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs11.readFileSync(abs)) === dl.hash) {
17969
+ if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs12.readFileSync(abs)) === dl.hash) {
17743
17970
  continue;
17744
17971
  }
17745
17972
  try {
@@ -17757,11 +17984,11 @@ async function downloadEvalAttachments(hubFolder, downloads) {
17757
17984
  }
17758
17985
  }
17759
17986
  const keep = new Set(downloads.map((d) => path13.resolve(hubFolder, d.relPath)));
17760
- for (const name of fs11.readdirSync(attachDir)) {
17987
+ for (const name of fs12.readdirSync(attachDir)) {
17761
17988
  const abs = path13.join(attachDir, name);
17762
- const st = fs11.lstatSync(abs);
17989
+ const st = fs12.lstatSync(abs);
17763
17990
  if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) {
17764
- fs11.rmSync(abs);
17991
+ fs12.rmSync(abs);
17765
17992
  result.removed.push(path13.relative(hubFolder, abs));
17766
17993
  }
17767
17994
  }
@@ -17779,20 +18006,20 @@ var init_eval_attachments = __esm({
17779
18006
  });
17780
18007
 
17781
18008
  // src/lib/parser.ts
17782
- import * as fs12 from "fs";
18009
+ import * as fs13 from "fs";
17783
18010
  import * as path14 from "path";
17784
- import * as yaml5 from "js-yaml";
18011
+ import * as yaml6 from "js-yaml";
17785
18012
  function parseHubFolder(hubFolder, opts) {
17786
18013
  const yamlPath = resolveHubYamlPath(hubFolder);
17787
18014
  if (!yamlPath) {
17788
18015
  throw expected(`hub.yaml not found in ${hubFolder}`);
17789
18016
  }
17790
- const yamlContent = fs12.readFileSync(yamlPath, "utf-8");
18017
+ const yamlContent = fs13.readFileSync(yamlPath, "utf-8");
17791
18018
  let config;
17792
18019
  try {
17793
- config = yaml5.load(yamlContent);
18020
+ config = yaml6.load(yamlContent);
17794
18021
  } catch (err) {
17795
- if (err instanceof yaml5.YAMLException) {
18022
+ if (err instanceof yaml6.YAMLException) {
17796
18023
  throw expected(`Invalid YAML in ${yamlPath}: ${err.message}`);
17797
18024
  }
17798
18025
  throw err;
@@ -17815,8 +18042,8 @@ function parseHubFolder(hubFolder, opts) {
17815
18042
  if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
17816
18043
  const instrValue = resolved.instructions;
17817
18044
  const instructionsPath = instrValue.startsWith("agents/") ? path14.join(hubFolder, instrValue) : path14.join(hubFolder, "agents", instrValue);
17818
- if (fs12.existsSync(instructionsPath)) {
17819
- resolved.instructions = fs12.readFileSync(instructionsPath, "utf-8");
18045
+ if (fs13.existsSync(instructionsPath)) {
18046
+ resolved.instructions = fs13.readFileSync(instructionsPath, "utf-8");
17820
18047
  } else {
17821
18048
  throw expected(
17822
18049
  `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
@@ -17824,8 +18051,8 @@ function parseHubFolder(hubFolder, opts) {
17824
18051
  }
17825
18052
  } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
17826
18053
  const conventionPath = path14.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
17827
- if (fs12.existsSync(conventionPath)) {
17828
- resolved.instructions = fs12.readFileSync(conventionPath, "utf-8");
18054
+ if (fs13.existsSync(conventionPath)) {
18055
+ resolved.instructions = fs13.readFileSync(conventionPath, "utf-8");
17829
18056
  }
17830
18057
  }
17831
18058
  return resolved;
@@ -17880,17 +18107,17 @@ function parseHubFolder(hubFolder, opts) {
17880
18107
  }
17881
18108
  function scanEvalYamlFiles(hubFolder, bytesByHash) {
17882
18109
  const evalsDir = path14.join(hubFolder, "evals");
17883
- if (!fs12.existsSync(evalsDir)) return [];
18110
+ if (!fs13.existsSync(evalsDir)) return [];
17884
18111
  const evals = [];
17885
18112
  const seen = /* @__PURE__ */ new Set();
17886
- const topEntries = fs12.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18113
+ const topEntries = fs13.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
17887
18114
  for (const entry of topEntries) {
17888
18115
  if (entry.isFile() && entry.name.endsWith(".yaml")) {
17889
18116
  collectEval(evals, seen, path14.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
17890
18117
  } else if (entry.isDirectory()) {
17891
18118
  const setName = entry.name;
17892
18119
  const setDir = path14.join(evalsDir, setName);
17893
- const setEntries = fs12.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18120
+ const setEntries = fs13.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
17894
18121
  for (const sub of setEntries) {
17895
18122
  if (sub.isDirectory()) {
17896
18123
  throw expected(
@@ -17919,12 +18146,12 @@ function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesBy
17919
18146
  evals.push(evalEntry);
17920
18147
  }
17921
18148
  function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
17922
- const content = fs12.readFileSync(filePath, "utf-8");
18149
+ const content = fs13.readFileSync(filePath, "utf-8");
17923
18150
  let raw;
17924
18151
  try {
17925
- raw = yaml5.load(content);
18152
+ raw = yaml6.load(content);
17926
18153
  } catch (err) {
17927
- if (err instanceof yaml5.YAMLException) {
18154
+ if (err instanceof yaml6.YAMLException) {
17928
18155
  throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
17929
18156
  }
17930
18157
  throw err;
@@ -18022,10 +18249,10 @@ function parseFixtureField(raw, label, key) {
18022
18249
  }
18023
18250
  function scanJourneyYamlFiles(hubFolder, bytesByHash) {
18024
18251
  const journeysDir = path14.join(hubFolder, "journeys");
18025
- if (!fs12.existsSync(journeysDir)) return [];
18252
+ if (!fs13.existsSync(journeysDir)) return [];
18026
18253
  const journeys = [];
18027
18254
  const seen = /* @__PURE__ */ new Set();
18028
- const entries = fs12.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18255
+ const entries = fs13.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18029
18256
  for (const entry of entries) {
18030
18257
  if (entry.isDirectory()) {
18031
18258
  throw expected(
@@ -18046,12 +18273,12 @@ function scanJourneyYamlFiles(hubFolder, bytesByHash) {
18046
18273
  return journeys;
18047
18274
  }
18048
18275
  function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
18049
- const content = fs12.readFileSync(filePath, "utf-8");
18276
+ const content = fs13.readFileSync(filePath, "utf-8");
18050
18277
  let raw;
18051
18278
  try {
18052
- raw = yaml5.load(content);
18279
+ raw = yaml6.load(content);
18053
18280
  } catch (err) {
18054
- if (err instanceof yaml5.YAMLException) {
18281
+ if (err instanceof yaml6.YAMLException) {
18055
18282
  throw expected(`Invalid YAML in ${relPath}: ${err.message}`);
18056
18283
  }
18057
18284
  throw err;
@@ -18102,18 +18329,18 @@ function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
18102
18329
  }
18103
18330
  function scanAgentYamlFiles(hubFolder) {
18104
18331
  const agentsDir = path14.join(hubFolder, "agents");
18105
- if (!fs12.existsSync(agentsDir)) return [];
18106
- const yamlFiles = fs12.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
18332
+ if (!fs13.existsSync(agentsDir)) return [];
18333
+ const yamlFiles = fs13.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
18107
18334
  if (yamlFiles.length === 0) return [];
18108
18335
  const agents = [];
18109
18336
  for (const file of yamlFiles) {
18110
18337
  const filePath = path14.join(agentsDir, file);
18111
- const content = fs12.readFileSync(filePath, "utf-8");
18338
+ const content = fs13.readFileSync(filePath, "utf-8");
18112
18339
  let agent;
18113
18340
  try {
18114
- agent = yaml5.load(content);
18341
+ agent = yaml6.load(content);
18115
18342
  } catch (err) {
18116
- if (err instanceof yaml5.YAMLException) {
18343
+ if (err instanceof yaml6.YAMLException) {
18117
18344
  throw expected(`Invalid YAML in agents/${file}: ${err.message}`);
18118
18345
  }
18119
18346
  throw err;
@@ -18143,7 +18370,7 @@ function parseResources(hubFolder, configResources) {
18143
18370
  if (res.skill_name) resource.skill_name = res.skill_name;
18144
18371
  const resSlug = slugify(resource.name);
18145
18372
  const resDir = path14.join(resourcesDir, resSlug);
18146
- if (fs12.existsSync(resDir)) {
18373
+ if (fs13.existsSync(resDir)) {
18147
18374
  const files = scanResourceFiles(resDir, "");
18148
18375
  if (files.length > 0) {
18149
18376
  resource.files = files;
@@ -18191,9 +18418,9 @@ var init_diff_display = __esm({
18191
18418
  });
18192
18419
 
18193
18420
  // src/lib/yaml-writer.ts
18194
- import * as fs13 from "fs";
18421
+ import * as fs14 from "fs";
18195
18422
  import * as path15 from "path";
18196
- import * as yaml6 from "js-yaml";
18423
+ import * as yaml7 from "js-yaml";
18197
18424
  function writeFileIfChanged(hubFolder, absPath, content, log) {
18198
18425
  const buf = Buffer.from(content, "utf-8");
18199
18426
  if (fileHasBytes(absPath, buf)) return;
@@ -18242,15 +18469,15 @@ function buildEvalYamlObject(evalEntry, slug) {
18242
18469
  function writeHubFolder(hubFolder, payload, options = {}) {
18243
18470
  const log = { changed: [], removed: [] };
18244
18471
  const agentsDir = path15.join(hubFolder, "agents");
18245
- if (!fs13.existsSync(hubFolder)) {
18246
- fs13.mkdirSync(hubFolder, { recursive: true });
18472
+ if (!fs14.existsSync(hubFolder)) {
18473
+ fs14.mkdirSync(hubFolder, { recursive: true });
18247
18474
  }
18248
- if (!fs13.existsSync(agentsDir)) {
18249
- fs13.mkdirSync(agentsDir, { recursive: true });
18475
+ if (!fs14.existsSync(agentsDir)) {
18476
+ fs14.mkdirSync(agentsDir, { recursive: true });
18250
18477
  }
18251
18478
  const yamlPayload = buildYamlPayload(payload);
18252
18479
  const agentFiles = extractAgentFiles(payload);
18253
- const yamlContent = yaml6.dump(yamlPayload, YAML_DUMP_OPTIONS);
18480
+ const yamlContent = yaml7.dump(yamlPayload, YAML_DUMP_OPTIONS);
18254
18481
  writeFileIfChanged(hubFolder, path15.join(hubFolder, "hub.yaml"), yamlContent, log);
18255
18482
  if (options.seedAgentContext ?? true) {
18256
18483
  for (const seeded of [
@@ -18261,8 +18488,8 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18261
18488
  }
18262
18489
  }
18263
18490
  const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
18264
- if (fs13.existsSync(oldYamlPath)) {
18265
- fs13.unlinkSync(oldYamlPath);
18491
+ if (fs14.existsSync(oldYamlPath)) {
18492
+ fs14.unlinkSync(oldYamlPath);
18266
18493
  log.removed.push(path15.relative(hubFolder, oldYamlPath));
18267
18494
  }
18268
18495
  const yamlSlugs = writeAgentYamlFiles(hubFolder, agentsDir, payload.agents || [], log);
@@ -18271,20 +18498,20 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18271
18498
  mdSlugs.add(slug);
18272
18499
  writeFileIfChanged(hubFolder, path15.join(agentsDir, `${slug}.md`), content, log);
18273
18500
  }
18274
- const existingFiles = fs13.readdirSync(agentsDir);
18501
+ const existingFiles = fs14.readdirSync(agentsDir);
18275
18502
  for (const file of existingFiles) {
18276
18503
  if (file.endsWith(".yaml")) {
18277
18504
  const slug = file.slice(0, -5);
18278
18505
  if (!yamlSlugs.has(slug)) {
18279
18506
  const orphan = path15.join(agentsDir, file);
18280
- fs13.unlinkSync(orphan);
18507
+ fs14.unlinkSync(orphan);
18281
18508
  log.removed.push(path15.relative(hubFolder, orphan));
18282
18509
  }
18283
18510
  } else if (file.endsWith(".md")) {
18284
18511
  const slug = file.slice(0, -3);
18285
18512
  if (!mdSlugs.has(slug)) {
18286
18513
  const orphan = path15.join(agentsDir, file);
18287
- fs13.unlinkSync(orphan);
18514
+ fs14.unlinkSync(orphan);
18288
18515
  log.removed.push(path15.relative(hubFolder, orphan));
18289
18516
  }
18290
18517
  }
@@ -18297,10 +18524,10 @@ function writeHubFolder(hubFolder, payload, options = {}) {
18297
18524
  function setPreviewLabelInHubYaml(hubFolder, label) {
18298
18525
  const yamlPath = resolveHubYamlPath(hubFolder);
18299
18526
  if (!yamlPath) return;
18300
- const obj = yaml6.load(fs13.readFileSync(yamlPath, "utf-8")) ?? {};
18527
+ const obj = yaml7.load(fs14.readFileSync(yamlPath, "utf-8")) ?? {};
18301
18528
  if (label) obj.preview_label = label;
18302
18529
  else delete obj.preview_label;
18303
- fs13.writeFileSync(yamlPath, yaml6.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
18530
+ fs14.writeFileSync(yamlPath, yaml7.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
18304
18531
  }
18305
18532
  function buildYamlPayload(payload) {
18306
18533
  const result = {
@@ -18345,7 +18572,7 @@ function writeAgentYamlFiles(hubFolder, agentsDir, agents, log) {
18345
18572
  slugs.add(slug);
18346
18573
  const entry = { ...agent };
18347
18574
  delete entry.instructions;
18348
- const yamlContent = yaml6.dump(entry, YAML_DUMP_OPTIONS);
18575
+ const yamlContent = yaml7.dump(entry, YAML_DUMP_OPTIONS);
18349
18576
  writeFileIfChanged(hubFolder, path15.join(agentsDir, `${slug}.yaml`), yamlContent, log);
18350
18577
  }
18351
18578
  return slugs;
@@ -18365,17 +18592,17 @@ function extractAgentFiles(payload) {
18365
18592
  function writeEvalYamlFiles(hubFolder, evals, log) {
18366
18593
  const evalsDir = path15.join(hubFolder, "evals");
18367
18594
  if (evals.length === 0) {
18368
- if (fs13.existsSync(evalsDir)) {
18595
+ if (fs14.existsSync(evalsDir)) {
18369
18596
  cleanEvalOrphans(hubFolder, evalsDir, /* @__PURE__ */ new Set(), log);
18370
18597
  try {
18371
- if (fs13.readdirSync(evalsDir).length === 0) fs13.rmdirSync(evalsDir);
18598
+ if (fs14.readdirSync(evalsDir).length === 0) fs14.rmdirSync(evalsDir);
18372
18599
  } catch {
18373
18600
  }
18374
18601
  }
18375
18602
  return;
18376
18603
  }
18377
- if (!fs13.existsSync(evalsDir)) {
18378
- fs13.mkdirSync(evalsDir, { recursive: true });
18604
+ if (!fs14.existsSync(evalsDir)) {
18605
+ fs14.mkdirSync(evalsDir, { recursive: true });
18379
18606
  }
18380
18607
  const writtenRelPaths = /* @__PURE__ */ new Set();
18381
18608
  for (const evalEntry of evals) {
@@ -18389,42 +18616,42 @@ function writeEvalYamlFiles(hubFolder, evals, log) {
18389
18616
  }
18390
18617
  if (setName) {
18391
18618
  const setDir = path15.join(evalsDir, setName);
18392
- if (!fs13.existsSync(setDir)) {
18393
- fs13.mkdirSync(setDir, { recursive: true });
18619
+ if (!fs14.existsSync(setDir)) {
18620
+ fs14.mkdirSync(setDir, { recursive: true });
18394
18621
  }
18395
18622
  }
18396
- const yamlContent = yaml6.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
18623
+ const yamlContent = yaml7.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
18397
18624
  writeFileIfChanged(hubFolder, targetPath, yamlContent, log);
18398
18625
  writtenRelPaths.add(relPath);
18399
18626
  }
18400
18627
  cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log);
18401
18628
  }
18402
18629
  function cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log) {
18403
- const entries = fs13.readdirSync(evalsDir, { withFileTypes: true });
18630
+ const entries = fs14.readdirSync(evalsDir, { withFileTypes: true });
18404
18631
  for (const entry of entries) {
18405
18632
  const fullPath = path15.join(evalsDir, entry.name);
18406
18633
  if (entry.isFile()) {
18407
18634
  if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
18408
- fs13.unlinkSync(fullPath);
18635
+ fs14.unlinkSync(fullPath);
18409
18636
  log.removed.push(path15.relative(hubFolder, fullPath));
18410
18637
  }
18411
18638
  continue;
18412
18639
  }
18413
18640
  if (entry.isDirectory()) {
18414
18641
  const setName = entry.name;
18415
- const subEntries = fs13.readdirSync(fullPath, { withFileTypes: true });
18642
+ const subEntries = fs14.readdirSync(fullPath, { withFileTypes: true });
18416
18643
  for (const sub of subEntries) {
18417
18644
  if (sub.isFile() && sub.name.endsWith(".yaml")) {
18418
18645
  const relPath = `${setName}/${sub.name}`;
18419
18646
  if (!writtenRelPaths.has(relPath)) {
18420
18647
  const orphan = path15.join(fullPath, sub.name);
18421
- fs13.unlinkSync(orphan);
18648
+ fs14.unlinkSync(orphan);
18422
18649
  log.removed.push(path15.relative(hubFolder, orphan));
18423
18650
  }
18424
18651
  }
18425
18652
  }
18426
18653
  try {
18427
- if (fs13.readdirSync(fullPath).length === 0) fs13.rmdirSync(fullPath);
18654
+ if (fs14.readdirSync(fullPath).length === 0) fs14.rmdirSync(fullPath);
18428
18655
  } catch {
18429
18656
  }
18430
18657
  }
@@ -18463,35 +18690,35 @@ function buildJourneyYamlObject(journeyEntry, slug) {
18463
18690
  function writeJourneyYamlFiles(hubFolder, journeys, log) {
18464
18691
  const journeysDir = path15.join(hubFolder, "journeys");
18465
18692
  if (journeys.length === 0) {
18466
- if (fs13.existsSync(journeysDir)) {
18693
+ if (fs14.existsSync(journeysDir)) {
18467
18694
  cleanJourneyOrphans(hubFolder, journeysDir, /* @__PURE__ */ new Set(), log);
18468
18695
  try {
18469
- if (fs13.readdirSync(journeysDir).length === 0) fs13.rmdirSync(journeysDir);
18696
+ if (fs14.readdirSync(journeysDir).length === 0) fs14.rmdirSync(journeysDir);
18470
18697
  } catch {
18471
18698
  }
18472
18699
  }
18473
18700
  return;
18474
18701
  }
18475
- if (!fs13.existsSync(journeysDir)) {
18476
- fs13.mkdirSync(journeysDir, { recursive: true });
18702
+ if (!fs14.existsSync(journeysDir)) {
18703
+ fs14.mkdirSync(journeysDir, { recursive: true });
18477
18704
  }
18478
18705
  const writtenFiles = /* @__PURE__ */ new Set();
18479
18706
  const usedSlugs = /* @__PURE__ */ new Set();
18480
18707
  for (const journeyEntry of journeys) {
18481
18708
  const slug = ensureUniqueSlug(slugify(journeyEntry.name), usedSlugs);
18482
18709
  const fileName = `${slug}.yaml`;
18483
- const yamlContent = yaml6.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
18710
+ const yamlContent = yaml7.dump(buildJourneyYamlObject(journeyEntry, slug), YAML_DUMP_OPTIONS);
18484
18711
  writeFileIfChanged(hubFolder, path15.join(journeysDir, fileName), yamlContent, log);
18485
18712
  writtenFiles.add(fileName);
18486
18713
  }
18487
18714
  cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log);
18488
18715
  }
18489
18716
  function cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log) {
18490
- const entries = fs13.readdirSync(journeysDir, { withFileTypes: true });
18717
+ const entries = fs14.readdirSync(journeysDir, { withFileTypes: true });
18491
18718
  for (const entry of entries) {
18492
18719
  if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
18493
18720
  const orphan = path15.join(journeysDir, entry.name);
18494
- fs13.unlinkSync(orphan);
18721
+ fs14.unlinkSync(orphan);
18495
18722
  log.removed.push(path15.relative(hubFolder, orphan));
18496
18723
  }
18497
18724
  }
@@ -18505,12 +18732,12 @@ function writeResourceFiles(hubFolder, resources, log) {
18505
18732
  const resDir = path15.join(resourcesDir, resSlug);
18506
18733
  writeResourceFileTree(resDir, resource.files || [], hubFolder, log);
18507
18734
  }
18508
- if (fs13.existsSync(resourcesDir)) {
18509
- const existingDirs = fs13.readdirSync(resourcesDir, { withFileTypes: true });
18735
+ if (fs14.existsSync(resourcesDir)) {
18736
+ const existingDirs = fs14.readdirSync(resourcesDir, { withFileTypes: true });
18510
18737
  for (const entry of existingDirs) {
18511
18738
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
18512
18739
  const orphanDir = path15.join(resourcesDir, entry.name);
18513
- fs13.rmSync(orphanDir, { recursive: true, force: true });
18740
+ fs14.rmSync(orphanDir, { recursive: true, force: true });
18514
18741
  log.removed.push(`${path15.relative(hubFolder, orphanDir)}/`);
18515
18742
  }
18516
18743
  }
@@ -18599,15 +18826,15 @@ var init_terminal_output = __esm({
18599
18826
  });
18600
18827
 
18601
18828
  // src/lib/base-workspace.ts
18602
- import * as fs14 from "fs";
18829
+ import * as fs15 from "fs";
18603
18830
  import * as path17 from "path";
18604
- import * as yaml7 from "js-yaml";
18831
+ import * as yaml8 from "js-yaml";
18605
18832
  function readBaseMeta(folder) {
18606
18833
  const bytes = readFileNoFollow(folder, path17.join(folder, BASE_META_FILE));
18607
18834
  if (bytes === null) return null;
18608
18835
  let doc;
18609
18836
  try {
18610
- doc = yaml7.load(bytes.toString("utf-8"));
18837
+ doc = yaml8.load(bytes.toString("utf-8"));
18611
18838
  } catch {
18612
18839
  return null;
18613
18840
  }
@@ -18617,7 +18844,7 @@ function readBaseMeta(folder) {
18617
18844
  function listBaseFolders(basesDir) {
18618
18845
  let entries;
18619
18846
  try {
18620
- entries = fs14.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
18847
+ entries = fs15.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
18621
18848
  } catch {
18622
18849
  return [];
18623
18850
  }
@@ -18704,7 +18931,7 @@ function resolveBaseSelectorToId(gitRoot, selector) {
18704
18931
  }
18705
18932
  function hasBaseMetaFile(folder) {
18706
18933
  try {
18707
- return fs14.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
18934
+ return fs15.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
18708
18935
  } catch {
18709
18936
  return false;
18710
18937
  }
@@ -18722,25 +18949,17 @@ var init_base_workspace = __esm({
18722
18949
  });
18723
18950
 
18724
18951
  // src/lib/subtree-routing.ts
18725
- import * as fs15 from "fs";
18726
18952
  import * as path18 from "path";
18727
- import * as yaml8 from "js-yaml";
18728
18953
  function readRepoDefaults(gitRoot) {
18729
- const file = path18.join(gitRoot, WAYAI_LAYOUT.wsDir, REPO_DEFAULTS_FILE);
18730
- let doc;
18731
- try {
18732
- doc = yaml8.load(fs15.readFileSync(file, "utf-8"));
18733
- } catch {
18734
- return {};
18735
- }
18736
- if (!doc || typeof doc !== "object") return {};
18737
- const raw = doc;
18954
+ const load11 = loadWorkspaceManifest(gitRoot);
18955
+ if (load11.kind !== "ok") return {};
18956
+ const raw = load11.doc;
18738
18957
  const str = (v) => {
18739
18958
  if (typeof v !== "string" || !v.trim()) return void 0;
18740
18959
  const value = v.trim();
18741
18960
  if (value.includes("/") || value.includes(path18.sep) || value === "." || value === "..") {
18742
18961
  console.warn(
18743
- `Warning: ignoring ${JSON.stringify(value)} in ${WAYAI_LAYOUT.wsDir}/${REPO_DEFAULTS_FILE} \u2014 a default names a hub or base, not a path.`
18962
+ `Warning: ignoring ${JSON.stringify(value)} in ${WORKSPACE_MANIFEST_LABEL} \u2014 a default names a hub or base, not a path.`
18744
18963
  );
18745
18964
  return void 0;
18746
18965
  }
@@ -18828,11 +19047,11 @@ function routeSubtree(options) {
18828
19047
  return {
18829
19048
  ok: false,
18830
19049
  refusal: [
18831
- `\`wayai ${verb}\` does not take --org: it operates on the organization named by .wayai.yaml at the repo root.`,
19050
+ `\`wayai ${verb}\` does not take --org: it operates on the organization named by ${WORKSPACE_MANIFEST_LABEL}.`,
18832
19051
  "",
18833
19052
  "To act on another organization:",
18834
19053
  " wayai bases <command> --org <uuid> # the Data surface honors it",
18835
- " # or run from a checkout whose .wayai.yaml names that organization"
19054
+ " # or run from a checkout whose workspace manifest names that organization"
18836
19055
  ].join("\n")
18837
19056
  };
18838
19057
  }
@@ -18899,15 +19118,15 @@ function requireSubtree(verb, args2, gitRoot) {
18899
19118
  }
18900
19119
  return { subtree: result.subtree, selector: result.selector };
18901
19120
  }
18902
- var REPO_DEFAULTS_FILE, VALUE_FLAGS;
19121
+ var VALUE_FLAGS;
18903
19122
  var init_subtree_routing = __esm({
18904
19123
  "src/lib/subtree-routing.ts"() {
18905
19124
  "use strict";
18906
19125
  init_layout();
18907
19126
  init_terminal_output();
19127
+ init_workspace_manifest();
18908
19128
  init_workspace();
18909
19129
  init_base_workspace();
18910
- REPO_DEFAULTS_FILE = "wayai.yaml";
18911
19130
  VALUE_FLAGS = /* @__PURE__ */ new Set(["--hub", "--base", "--label", "--org"]);
18912
19131
  }
18913
19132
  });
@@ -18934,12 +19153,12 @@ async function createDataClient(orgId) {
18934
19153
  if (selected !== void 0 && !UUID_RE2.test(selected)) {
18935
19154
  throw expected(`Invalid --org: ${JSON.stringify(selected)}. Expected an organization UUID.`);
18936
19155
  }
18937
- const org = selected ?? readRepoConfig()?.organization_id;
19156
+ const org = selected ?? readRepoConfigUnlessBlocked()?.organization_id;
18938
19157
  async function requireOrgForSetupRoute(what) {
18939
19158
  const orgId2 = org ?? (await api.me()).org_id;
18940
19159
  if (!orgId2) {
18941
19160
  throw expected(
18942
- `No organization to ${what} against. Pass --org <uuid>, or run this from a repo with .wayai.yaml.`
19161
+ `No organization to ${what} against. Pass --org <uuid>, or run this from a repo scoped by \`wayai init\`.`
18943
19162
  );
18944
19163
  }
18945
19164
  return orgId2;
@@ -19025,7 +19244,7 @@ var init_client = __esm({
19025
19244
  });
19026
19245
 
19027
19246
  // src/data/helpers.ts
19028
- import { readFileSync as readFileSync16 } from "fs";
19247
+ import { readFileSync as readFileSync15 } from "fs";
19029
19248
  function pathSegment(id, label = "id") {
19030
19249
  if (!isPathSafeId(id)) {
19031
19250
  throw expected(`Invalid ${label}: ${JSON.stringify(id)}. An id may use ${PATH_SAFE_ID_RULE}.`);
@@ -19053,7 +19272,7 @@ function parseData(data, flag) {
19053
19272
  let text = data;
19054
19273
  if (source !== void 0) {
19055
19274
  try {
19056
- text = readFileSync16(source, "utf-8");
19275
+ text = readFileSync15(source, "utf-8");
19057
19276
  } catch (e) {
19058
19277
  throw expected(`${prefix}${source}: ${e instanceof Error ? e.message : "could not be read"}`);
19059
19278
  }
@@ -21148,9 +21367,10 @@ async function migrateCommand(_args) {
21148
21367
  const newOrg = path29.join(newWs, WAYAI_LAYOUT.orgSubdir);
21149
21368
  const hasLegacyWs = isDirectory(legacyWs);
21150
21369
  const hasLegacyOrg = isDirectory(legacyOrg);
21151
- if (!hasLegacyWs && !hasLegacyOrg) {
21152
- console.log(`Already migrated \u2014 no legacy ${WAYAI_LAYOUT.legacy.wsDir}/ or ${WAYAI_LAYOUT.legacy.orgAtRoot}/ to move.`);
21153
- return;
21370
+ const orgPlan = planOrgMigration(gitRoot);
21371
+ if (orgPlan.kind === "refuse") {
21372
+ console.error(orgPlan.message);
21373
+ process.exit(1);
21154
21374
  }
21155
21375
  if (hasLegacyWs && isDirectory(newHubs)) {
21156
21376
  console.error(
@@ -21176,10 +21396,30 @@ async function migrateCommand(_args) {
21176
21396
  usedFsFallback ||= how === "fs";
21177
21397
  moved.push(`${WAYAI_LAYOUT.legacy.orgAtRoot}/ \u2192 ${rel(newOrg)}/`);
21178
21398
  }
21179
- console.log(`Migrated to the ${WAYAI_LAYOUT.wsDir}/ layout:`);
21180
- for (const m of moved) console.log(` ${m}`);
21181
- if (usedFsFallback) {
21182
- console.log("\n(Some dirs were moved on disk, not via git \u2014 they will show as untracked.)");
21399
+ if (moved.length > 0) {
21400
+ console.log(`Migrated to the ${WAYAI_LAYOUT.wsDir}/ layout:`);
21401
+ for (const m of moved) console.log(` ${m}`);
21402
+ if (usedFsFallback) {
21403
+ console.log("\n(Some dirs were moved on disk, not via git \u2014 they will show as untracked.)");
21404
+ }
21405
+ }
21406
+ const copiedOrg = orgPlan.kind === "copy";
21407
+ if (copiedOrg) {
21408
+ writeRepoConfig(orgPlan.config, gitRoot);
21409
+ console.log(`${moved.length > 0 ? "\n" : ""}Copied the organization binding into ${WORKSPACE_MANIFEST_LABEL}.`);
21410
+ console.log(
21411
+ ` ${ROOT_CONFIG_FILE} is KEPT on purpose \u2014 an older CLI in a teammate's environment or in CI still requires it.`
21412
+ );
21413
+ console.log(
21414
+ " It stays a supported fallback until the next major, which removes both the fallback and the file."
21415
+ );
21416
+ console.log(" Delete it once every environment that runs this repo reads the manifest.");
21417
+ }
21418
+ if (moved.length === 0 && !copiedOrg) {
21419
+ console.log(
21420
+ `Already migrated \u2014 no legacy ${WAYAI_LAYOUT.legacy.wsDir}/ or ${WAYAI_LAYOUT.legacy.orgAtRoot}/ to move, and no organization binding to copy into ${WORKSPACE_MANIFEST_LABEL}.`
21421
+ );
21422
+ return;
21183
21423
  }
21184
21424
  console.log("\nNext steps:");
21185
21425
  console.log(' git add -A && git commit -m "chore: migrate to wayai-ws layout"');
@@ -21190,6 +21430,8 @@ var init_migrate = __esm({
21190
21430
  "use strict";
21191
21431
  init_workspace();
21192
21432
  init_layout();
21433
+ init_repo_config();
21434
+ init_workspace_manifest();
21193
21435
  }
21194
21436
  });
21195
21437
 
@@ -22583,8 +22825,8 @@ function installEvalSignalHandlers(input) {
22583
22825
  let signalCount = 0;
22584
22826
  let disposed = false;
22585
22827
  let settle;
22586
- const settled = new Promise((resolve7) => {
22587
- settle = resolve7;
22828
+ const settled = new Promise((resolve8) => {
22829
+ settle = resolve8;
22588
22830
  });
22589
22831
  const listeners = /* @__PURE__ */ new Map();
22590
22832
  const dispose = () => {
@@ -22934,7 +23176,7 @@ Timeout after ${timeoutSeconds}s${queuedSeconds > 0 ? ` (${queuedSeconds}s of it
22934
23176
  process.exit(1);
22935
23177
  }
22936
23178
  function sleep(ms) {
22937
- return new Promise((resolve7) => setTimeout(resolve7, ms));
23179
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
22938
23180
  }
22939
23181
  function parseRunNumbers(raw) {
22940
23182
  if (!raw || raw.startsWith("--")) {
@@ -26790,26 +27032,26 @@ async function reportGetCommand(args2) {
26790
27032
  console.log(JSON.stringify(result, null, 2));
26791
27033
  return;
26792
27034
  }
26793
- const { report, messages } = result;
26794
- const title = sanitizeTerminalText(report.title);
26795
- const description = sanitizeTerminalText(report.description);
26796
- console.log(`Report ${report.report_id}`);
27035
+ const { report: report2, messages } = result;
27036
+ const title = sanitizeTerminalText(report2.title);
27037
+ const description = sanitizeTerminalText(report2.description);
27038
+ console.log(`Report ${report2.report_id}`);
26797
27039
  console.log(` title: ${title}`);
26798
27040
  console.log(` description: ${description.replace(/\n/g, "\n ")}`);
26799
- console.log(` status: ${report.status}`);
26800
- console.log(` classification: ${report.classification}`);
26801
- if (report.contest_count > 0) console.log(` contests: ${report.contest_count}`);
26802
- if (report.dismissal_final) console.log(" dismissal: final (non-contestable)");
27041
+ console.log(` status: ${report2.status}`);
27042
+ console.log(` classification: ${report2.classification}`);
27043
+ if (report2.contest_count > 0) console.log(` contests: ${report2.contest_count}`);
27044
+ if (report2.dismissal_final) console.log(" dismissal: final (non-contestable)");
26803
27045
  if (messages.length > 0) {
26804
27046
  console.log("\n Thread:");
26805
27047
  for (const m of messages) {
26806
27048
  console.log(` [${m.author_role}] ${sanitizeTerminalText(m.body)}`);
26807
27049
  }
26808
27050
  }
26809
- if (report.status === "shipped") {
27051
+ if (report2.status === "shipped") {
26810
27052
  console.log(`
26811
- This fix is shipped. Run \`wayai report accept ${report.report_id}\` if it works,`);
26812
- console.log(` or \`wayai report contest ${report.report_id} --reason "..."\` if it does not.`);
27053
+ This fix is shipped. Run \`wayai report accept ${report2.report_id}\` if it works,`);
27054
+ console.log(` or \`wayai report contest ${report2.report_id} --reason "..."\` if it does not.`);
26813
27055
  }
26814
27056
  } catch (err) {
26815
27057
  reportActionError(err);
@@ -27089,7 +27331,7 @@ var init_actions = __esm({
27089
27331
 
27090
27332
  // src/data/commands/attachments.ts
27091
27333
  import { Command as Command2 } from "commander";
27092
- import { readFileSync as readFileSync21 } from "fs";
27334
+ import { readFileSync as readFileSync20 } from "fs";
27093
27335
  function findAttachmentByFilename(attachments, filename) {
27094
27336
  return attachments.find((a) => a.key.endsWith(`/${filename}`)) ?? null;
27095
27337
  }
@@ -27130,7 +27372,7 @@ function buildAttachmentsCommand() {
27130
27372
  printOutput(data, outputFormat(this));
27131
27373
  return;
27132
27374
  }
27133
- const body = readFileSync21(opts.file);
27375
+ const body = readFileSync20(opts.file);
27134
27376
  await client.upload(uploadPathFrom(data?.upload_url), body, opts.contentType);
27135
27377
  printOutput({ ...data, uploaded: true }, outputFormat(this));
27136
27378
  });
@@ -27610,16 +27852,16 @@ var init_providers = __esm({
27610
27852
 
27611
27853
  // src/data/commands/report.ts
27612
27854
  import { Command as Command6 } from "commander";
27613
- import { readFileSync as readFileSync22 } from "fs";
27614
- import { dirname as dirname11, join as join32 } from "path";
27855
+ import { readFileSync as readFileSync21 } from "fs";
27856
+ import { dirname as dirname12, join as join31 } from "path";
27615
27857
  import { fileURLToPath as fileURLToPath2 } from "url";
27616
27858
  function resolveCliVersion() {
27617
27859
  for (const candidate of [
27618
- join32(here, "..", "package.json"),
27619
- join32(here, "..", "..", "..", "package.json")
27860
+ join31(here, "..", "package.json"),
27861
+ join31(here, "..", "..", "..", "package.json")
27620
27862
  ]) {
27621
27863
  try {
27622
- const version = JSON.parse(readFileSync22(candidate, "utf-8")).version;
27864
+ const version = JSON.parse(readFileSync21(candidate, "utf-8")).version;
27623
27865
  if (typeof version === "string" && version) return version;
27624
27866
  } catch {
27625
27867
  }
@@ -27653,10 +27895,10 @@ function reportActionError2(err) {
27653
27895
  throw err;
27654
27896
  }
27655
27897
  function buildBasesReportCommand() {
27656
- const report = new Command6("report").description(
27898
+ const report2 = new Command6("report").description(
27657
27899
  "File a report to the Data triage queue and verify the outcome"
27658
27900
  );
27659
- report.command("create").description("Submit a report to the Data triage queue (deduplicated)").requiredOption("--title <title>", "Short summary").requiredOption("--description <text>", "What happened").option("--source <source>", "Origin: cli_report (default) | review | security_audit").option("--classification <c>", "Nature: bug (default) | flaky_test | security | enhancement").option(
27901
+ report2.command("create").description("Submit a report to the Data triage queue (deduplicated)").requiredOption("--title <title>", "Short summary").requiredOption("--description <text>", "What happened").option("--source <source>", "Origin: cli_report (default) | review | security_audit").option("--classification <c>", "Nature: bug (default) | flaky_test | security | enhancement").option(
27660
27902
  "--dedup-key <key>",
27661
27903
  'Stable dedup key (e.g. "<file>::<test>"); collapses repeat occurrences'
27662
27904
  ).option("--severity <level>", "low | medium | high | critical").option("--steps <text>", "Steps to reproduce").option("--error-message <text>", "Exact error text if any").option("--context <text>", "Additional context (logs, request ids)").option("--locale <code>", "Notification locale (en | pt | es)", "en").option("--reporter-email <addr>", "Override reporter email (defaults to session email)").option("--base-id <id>", "Base the error relates to (defaults to the configured base)").option("--record-type <name>", "Record type the error relates to").option("--record-id <id>", "Record id (UUID) the error relates to").option("--external-id <id>", "External id of the related record").option("--external-source <src>", "External source of the related record").option("--relationship-id <id>", "Relationship id the error relates to").action(async function(opts) {
@@ -27707,7 +27949,7 @@ function buildBasesReportCommand() {
27707
27949
  );
27708
27950
  }
27709
27951
  });
27710
- report.command("list").description("List your reports, newest first").option("--status <s>", "Filter by status (e.g. shipped \u2014 awaiting your verification)").action(async function(opts) {
27952
+ report2.command("list").description("List your reports, newest first").option("--status <s>", "Filter by status (e.g. shipped \u2014 awaiting your verification)").action(async function(opts) {
27711
27953
  const client = await createDataClient();
27712
27954
  const qs = opts.status ? `?status=${encodeURIComponent(opts.status)}` : "";
27713
27955
  try {
@@ -27730,7 +27972,7 @@ function buildBasesReportCommand() {
27730
27972
  reportActionError2(err);
27731
27973
  }
27732
27974
  });
27733
- report.command("get <report_id>").description("Show one of your reports: status + the message thread").action(async function(reportId) {
27975
+ report2.command("get <report_id>").description("Show one of your reports: status + the message thread").action(async function(reportId) {
27734
27976
  const client = await createDataClient();
27735
27977
  try {
27736
27978
  const data = await client.request("GET", `/v1/reports/${pathSegment(reportId, "report id")}`);
@@ -27767,7 +28009,7 @@ function buildBasesReportCommand() {
27767
28009
  reportActionError2(err);
27768
28010
  }
27769
28011
  });
27770
- report.command("accept <report_id>").description("Accept a shipped fix (\u2192 addressed)").action(async function(reportId) {
28012
+ report2.command("accept <report_id>").description("Accept a shipped fix (\u2192 addressed)").action(async function(reportId) {
27771
28013
  const client = await createDataClient();
27772
28014
  try {
27773
28015
  const data = await client.request(
@@ -27783,7 +28025,7 @@ function buildBasesReportCommand() {
27783
28025
  reportActionError2(err);
27784
28026
  }
27785
28027
  });
27786
- report.command("contest <report_id>").description("Contest a shipped fix or a dismissal (\u2192 back to triage)").requiredOption("--reason <text>", "Why the fix/dismissal is wrong").action(async function(reportId, opts) {
28028
+ report2.command("contest <report_id>").description("Contest a shipped fix or a dismissal (\u2192 back to triage)").requiredOption("--reason <text>", "Why the fix/dismissal is wrong").action(async function(reportId, opts) {
27787
28029
  const client = await createDataClient();
27788
28030
  try {
27789
28031
  const data = await client.request(
@@ -27802,7 +28044,7 @@ function buildBasesReportCommand() {
27802
28044
  reportActionError2(err);
27803
28045
  }
27804
28046
  });
27805
- return report;
28047
+ return report2;
27806
28048
  }
27807
28049
  var here;
27808
28050
  var init_report2 = __esm({
@@ -27815,13 +28057,13 @@ var init_report2 = __esm({
27815
28057
  init_terminal_output();
27816
28058
  init_workspace();
27817
28059
  init_skill_version();
27818
- here = dirname11(fileURLToPath2(import.meta.url));
28060
+ here = dirname12(fileURLToPath2(import.meta.url));
27819
28061
  }
27820
28062
  });
27821
28063
 
27822
28064
  // src/data/commands/credentials.ts
27823
28065
  import { Command as Command7 } from "commander";
27824
- import { readFileSync as readFileSync23 } from "fs";
28066
+ import { readFileSync as readFileSync22 } from "fs";
27825
28067
  function withValueSourceOptions(cmd, what) {
27826
28068
  return cmd.option(
27827
28069
  "--file <path>",
@@ -27834,7 +28076,7 @@ async function resolveValue(opts, label) {
27834
28076
  throw expected("--file cannot be combined with --value-stdin or --value-prompt \u2014 pass one.");
27835
28077
  }
27836
28078
  try {
27837
- return readFileSync23(opts.file).toString("base64");
28079
+ return readFileSync22(opts.file).toString("base64");
27838
28080
  } catch (e) {
27839
28081
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
27840
28082
  }
@@ -27983,7 +28225,7 @@ var init_credentials = __esm({
27983
28225
 
27984
28226
  // src/data/commands/sql.ts
27985
28227
  import { Command as Command8 } from "commander";
27986
- import { readFileSync as readFileSync24 } from "fs";
28228
+ import { readFileSync as readFileSync23 } from "fs";
27987
28229
  function buildBasesSqlCommand() {
27988
28230
  return withBaseOption(new Command8("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
27989
28231
  "--param <kv...>",
@@ -27993,7 +28235,7 @@ function buildBasesSqlCommand() {
27993
28235
  let query;
27994
28236
  if (opts.file) {
27995
28237
  try {
27996
- query = readFileSync24(opts.file, "utf-8").trim();
28238
+ query = readFileSync23(opts.file, "utf-8").trim();
27997
28239
  } catch (e) {
27998
28240
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
27999
28241
  }
@@ -28648,7 +28890,7 @@ var init_file_types = __esm({
28648
28890
 
28649
28891
  // src/data/commands/files.ts
28650
28892
  import { Command as Command12 } from "commander";
28651
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync16 } from "fs";
28893
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
28652
28894
  import { basename as basename18 } from "path";
28653
28895
  function renderFileDiff(fileType, filePath, from, to, d) {
28654
28896
  console.log(sanitizeTerminalText(`${fileType}/${filePath}: v${from} \u2192 v${to}`));
@@ -28697,7 +28939,7 @@ function buildFilesCommand() {
28697
28939
  "Upload a local file to a path (e.g. wayai files put reports q3/summary.pdf --file ./summary.pdf)"
28698
28940
  ).requiredOption("--file <local>", "Local file to upload").option("--content-type <type>", "MIME type", "application/octet-stream").action(async function(fileType, filePath, opts) {
28699
28941
  const base = pathSegment(requireBase(this), "--base");
28700
- const body = readFileSync25(opts.file);
28942
+ const body = readFileSync24(opts.file);
28701
28943
  const client = await createDataClient();
28702
28944
  printOutput(
28703
28945
  await client.upload(
@@ -29658,7 +29900,7 @@ __export(program_exports, {
29658
29900
  });
29659
29901
  import { Command as Command22 } from "commander";
29660
29902
  function withDataGlobals(command2) {
29661
- return command2.option("--org <uuid>", "Organization to operate against (overrides .wayai.yaml)").option("--output <format>", "Output format: json or table", "table").option("--json", "Shorthand for --output json");
29903
+ return command2.option("--org <uuid>", "Organization to operate against (overrides the repo's org binding)").option("--output <format>", "Output format: json or table", "table").option("--json", "Shorthand for --output json");
29662
29904
  }
29663
29905
  function routeErrorsToCli(command2) {
29664
29906
  command2.exitOverride().configureOutput({ outputError: () => {
@@ -29746,9 +29988,9 @@ init_errors2();
29746
29988
  init_mask_secrets();
29747
29989
  init_utils();
29748
29990
  init_registry();
29749
- import { readFileSync as readFileSync26 } from "fs";
29991
+ import { readFileSync as readFileSync25 } from "fs";
29750
29992
  import { fileURLToPath as fileURLToPath3 } from "url";
29751
- import { dirname as dirname12, join as join33 } from "path";
29993
+ import { dirname as dirname13, join as join32 } from "path";
29752
29994
 
29753
29995
  // src/lib/version-refresh.ts
29754
29996
  init_version_cache();
@@ -29756,7 +29998,7 @@ init_skill_version();
29756
29998
  import { exec } from "child_process";
29757
29999
  var REFRESH_TIMEOUT_MS = 1e4;
29758
30000
  function refreshCliCache() {
29759
- return new Promise((resolve7) => {
30001
+ return new Promise((resolve8) => {
29760
30002
  exec("npm view @wayai/cli version", { timeout: REFRESH_TIMEOUT_MS }, (err, stdout) => {
29761
30003
  if (!err) {
29762
30004
  const latest = stdout.trim();
@@ -29767,7 +30009,7 @@ function refreshCliCache() {
29767
30009
  }
29768
30010
  }
29769
30011
  }
29770
- resolve7();
30012
+ resolve8();
29771
30013
  });
29772
30014
  });
29773
30015
  }
@@ -29793,8 +30035,8 @@ async function refreshSkillCache() {
29793
30035
  }
29794
30036
  async function refreshAdminSkillCache() {
29795
30037
  let timer;
29796
- const deadline = new Promise((resolve7) => {
29797
- timer = setTimeout(resolve7, REFRESH_TIMEOUT_MS);
30038
+ const deadline = new Promise((resolve8) => {
30039
+ timer = setTimeout(resolve8, REFRESH_TIMEOUT_MS);
29798
30040
  });
29799
30041
  await Promise.race([deadline, fetchAndCacheAdminSkillVersion().catch(() => {
29800
30042
  })]);
@@ -29903,8 +30145,8 @@ Run \`wayai admin skill install\` to update.`);
29903
30145
  }
29904
30146
 
29905
30147
  // src/index.ts
29906
- var __dirname = dirname12(fileURLToPath3(import.meta.url));
29907
- var pkg = JSON.parse(readFileSync26(join33(__dirname, "..", "package.json"), "utf-8"));
30148
+ var __dirname = dirname13(fileURLToPath3(import.meta.url));
30149
+ var pkg = JSON.parse(readFileSync25(join32(__dirname, "..", "package.json"), "utf-8"));
29908
30150
  var [, , command, ...args] = process.argv;
29909
30151
  var isBackgroundRefresh = command === REFRESH_COMMAND;
29910
30152
  if (!isBackgroundRefresh) initSentry(command, pkg.version);
@@ -30139,7 +30381,7 @@ Commands:
30139
30381
  logout Clear stored credentials
30140
30382
  status Show current auth and config status
30141
30383
  whoami Show the authenticated identity (supports --json)
30142
- init Set up .wayai.yaml (pick organization)
30384
+ init Scope this repo to an organization (pick one)
30143
30385
  create-credential Create an organization credential (API key, token, etc.)
30144
30386
  update-credential Update / rotate an organization credential (by name)
30145
30387
  set-connection-credential Set a connection's credential directly (org link or --field/--stdin), preview or production
@@ -30222,7 +30464,7 @@ Flags:
30222
30464
  --version, -v Show CLI version
30223
30465
 
30224
30466
  Org-scoped mode:
30225
- Each repository is scoped to a single organization via .wayai.yaml
30467
+ Each repository is scoped to a single organization via wayai-ws/wayai.yaml
30226
30468
  (only \`organization_id\` is required). Hubs self-identify via \`hub.yaml\`
30227
30469
  inside their folder under \`workspace/\`.
30228
30470