@wayai/cli 0.3.156 → 0.3.157

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
@@ -7554,7 +7554,15 @@ var init_contracts = __esm({
7554
7554
  dataUsageCountersSchema = external_exports.object({
7555
7555
  ops_tenths_this_month: external_exports.number().nonnegative(),
7556
7556
  records_count: external_exports.number().nonnegative(),
7557
- month_key: external_exports.string()
7557
+ month_key: external_exports.string(),
7558
+ // The closed window's meter reading (§2.7's prior-window snapshot). OPTIONAL for the same
7559
+ // reason this object is not `.strict()`: the producer deploys on its own cadence, and a
7560
+ // required field would fail the parse for every counter on any producer build that predates
7561
+ // it — turning an additive wire change into an outage of the gauge above. Absence and a
7562
+ // non-matching key are the SAME outcome for a reader (§2.7 property 1: do not bill), so
7563
+ // nothing is lost by tolerating it.
7564
+ prior_window_key: external_exports.string().optional(),
7565
+ prior_window_ops_tenths: external_exports.number().nonnegative().optional()
7558
7566
  });
7559
7567
  REKOR_ENTITLEMENT_CONTRACT_VERSION = 1;
7560
7568
  entitlementProjectionSchema = external_exports.object({
@@ -13259,7 +13267,15 @@ var init_dist = __esm({
13259
13267
  dataUsageCountersSchema2 = external_exports.object({
13260
13268
  ops_tenths_this_month: external_exports.number().nonnegative(),
13261
13269
  records_count: external_exports.number().nonnegative(),
13262
- month_key: external_exports.string()
13270
+ month_key: external_exports.string(),
13271
+ // The closed window's meter reading (§2.7's prior-window snapshot). OPTIONAL for the same
13272
+ // reason this object is not `.strict()`: the producer deploys on its own cadence, and a
13273
+ // required field would fail the parse for every counter on any producer build that predates
13274
+ // it — turning an additive wire change into an outage of the gauge above. Absence and a
13275
+ // non-matching key are the SAME outcome for a reader (§2.7 property 1: do not bill), so
13276
+ // nothing is lost by tolerating it.
13277
+ prior_window_key: external_exports.string().optional(),
13278
+ prior_window_ops_tenths: external_exports.number().nonnegative().optional()
13263
13279
  });
13264
13280
  REKOR_ENTITLEMENT_CONTRACT_VERSION2 = 1;
13265
13281
  entitlementProjectionSchema2 = external_exports.object({
@@ -17307,78 +17323,6 @@ var init_constants = __esm({
17307
17323
  }
17308
17324
  });
17309
17325
 
17310
- // src/lib/workspace-files.ts
17311
- import * as fs9 from "fs";
17312
- import * as path9 from "path";
17313
- function perHubAgentsMd(hubFolderName) {
17314
- return [
17315
- `# ${hubFolderName}`,
17316
- "",
17317
- "Notes about THIS hub that the config files (hub.yaml, agents/) don't capture:",
17318
- "why it exists, key decisions and the reasons behind them, business rules that",
17319
- "only apply here, terminology, integration quirks.",
17320
- "",
17321
- "Keep it short and about *why / what is different* \u2014 platform mechanics live in",
17322
- "the WayAI skill. Fill this in as you learn about the hub; delete this",
17323
- "placeholder once you do.",
17324
- ""
17325
- ].join("\n");
17326
- }
17327
- function isWorkspace(gitRoot) {
17328
- if (fs9.existsSync(workspaceManifestPath(gitRoot))) return true;
17329
- if (fs9.existsSync(rootConfigPath(gitRoot))) return true;
17330
- return isDirectory(resolveLayout(gitRoot).hubsDir);
17331
- }
17332
- function writeIfAbsent(filePath, content) {
17333
- if (fs9.existsSync(filePath)) return null;
17334
- fs9.writeFileSync(filePath, content, "utf-8");
17335
- return path9.basename(filePath);
17336
- }
17337
- function ensureWorkspaceFiles(gitRoot) {
17338
- if (!isWorkspace(gitRoot)) return [];
17339
- const created = [];
17340
- const agents = writeIfAbsent(path9.join(gitRoot, "AGENTS.md"), ROOT_AGENTS_MD);
17341
- if (agents) created.push(agents);
17342
- const claude = writeIfAbsent(path9.join(gitRoot, "CLAUDE.md"), CLAUDE_MD_SHIM);
17343
- if (claude) created.push(claude);
17344
- return created;
17345
- }
17346
- var CLAUDE_MD_SHIM, ROOT_AGENTS_MD;
17347
- var init_workspace_files = __esm({
17348
- "src/lib/workspace-files.ts"() {
17349
- "use strict";
17350
- init_constants();
17351
- init_layout();
17352
- init_workspace_manifest();
17353
- CLAUDE_MD_SHIM = "@AGENTS.md\n";
17354
- ROOT_AGENTS_MD = [
17355
- "# Working with WayAI",
17356
- "",
17357
- "WayAI is the layer that talks to people across channels, plus the data layer",
17358
- "(Bases) behind it. One CLI, one skill, one login.",
17359
- "",
17360
- "## At the start of every session",
17361
- "",
17362
- "1. Check for updates first, and update whatever is not current:",
17363
- ` - Skill: \`wayai status\` \u2192 if newer, \`${SKILL_INSTALL_COMMAND}\``,
17364
- " - CLI: `wayai update`",
17365
- "2. Load the WayAI skill at the start of every session \u2014 every time, even for a",
17366
- " quick task. It holds the real instructions for everything else.",
17367
- "",
17368
- "More detail if you need it: https://wayai.pro",
17369
- "",
17370
- "## When helping the person you are working with",
17371
- "",
17372
- "- They may be new to WayAI \u2014 use plain language, not jargon.",
17373
- "- Don't explain things they didn't ask about. Only if they seem stuck or ask",
17374
- " what something means, give a one-line answer, then move on \u2014 don't turn",
17375
- " replies into tutorials.",
17376
- "- Keep answers short and to the point.",
17377
- ""
17378
- ].join("\n");
17379
- }
17380
- });
17381
-
17382
17326
  // src/commands/init.ts
17383
17327
  var init_exports = {};
17384
17328
  __export(init_exports, {
@@ -17388,12 +17332,12 @@ __export(init_exports, {
17388
17332
  parseArgs: () => parseArgs3
17389
17333
  });
17390
17334
  import { mkdirSync as mkdirSync6 } from "fs";
17391
- import path10 from "path";
17335
+ import path9 from "path";
17392
17336
  function ensureHubsDir(gitRoot) {
17393
17337
  const hubsDir = resolveLayout(gitRoot).hubsDir;
17394
17338
  if (isDirectory(hubsDir)) return null;
17395
17339
  mkdirSync6(hubsDir, { recursive: true });
17396
- return `${path10.relative(gitRoot, hubsDir)}/`;
17340
+ return `${path9.relative(gitRoot, hubsDir)}/`;
17397
17341
  }
17398
17342
  function parseArgs3(args2) {
17399
17343
  let orgId;
@@ -17436,9 +17380,6 @@ async function initCommand(args2) {
17436
17380
  if (createdDir) {
17437
17381
  console.log(`Created ${createdDir}`);
17438
17382
  }
17439
- for (const file of ensureWorkspaceFiles(gitRoot)) {
17440
- console.log(`Created ${file}`);
17441
- }
17442
17383
  }
17443
17384
  console.log(
17444
17385
  `Run \`${SKILL_INSTALL_CMD}\` to install the WayAI skill in your AI harness, then \`wayai pull\` to fetch hub configuration.`
@@ -17519,7 +17460,6 @@ var init_init = __esm({
17519
17460
  init_repo_config();
17520
17461
  init_workspace();
17521
17462
  init_layout();
17522
- init_workspace_files();
17523
17463
  init_utils();
17524
17464
  init_constants();
17525
17465
  SKILL_INSTALL_CMD = SKILL_INSTALL_COMMAND;
@@ -17527,18 +17467,18 @@ var init_init = __esm({
17527
17467
  });
17528
17468
 
17529
17469
  // src/lib/fs-safety.ts
17530
- import * as fs10 from "fs";
17531
- import * as path11 from "path";
17470
+ import * as fs9 from "fs";
17471
+ import * as path10 from "path";
17532
17472
  function fileHasBytes(abs, data) {
17533
17473
  let st;
17534
17474
  try {
17535
- st = fs10.lstatSync(abs);
17475
+ st = fs9.lstatSync(abs);
17536
17476
  } catch {
17537
17477
  return false;
17538
17478
  }
17539
17479
  if (!st.isFile() || st.isSymbolicLink()) return false;
17540
17480
  try {
17541
- return fs10.readFileSync(abs).equals(data);
17481
+ return fs9.readFileSync(abs).equals(data);
17542
17482
  } catch {
17543
17483
  return false;
17544
17484
  }
@@ -17547,22 +17487,22 @@ function realpathContained(root, abs) {
17547
17487
  let realRoot;
17548
17488
  let realAbs;
17549
17489
  try {
17550
- realRoot = fs10.realpathSync(root);
17551
- realAbs = fs10.realpathSync(abs);
17490
+ realRoot = fs9.realpathSync(root);
17491
+ realAbs = fs9.realpathSync(abs);
17552
17492
  } catch {
17553
17493
  return false;
17554
17494
  }
17555
- return realAbs === realRoot || realAbs.startsWith(realRoot + path11.sep);
17495
+ return realAbs === realRoot || realAbs.startsWith(realRoot + path10.sep);
17556
17496
  }
17557
17497
  function mkdirTolerateRace(dir) {
17558
17498
  try {
17559
- fs10.mkdirSync(dir);
17499
+ fs9.mkdirSync(dir);
17560
17500
  return true;
17561
17501
  } catch (err) {
17562
17502
  if (err.code !== "EEXIST") throw err;
17563
17503
  let st;
17564
17504
  try {
17565
- st = fs10.lstatSync(dir);
17505
+ st = fs9.lstatSync(dir);
17566
17506
  } catch {
17567
17507
  st = void 0;
17568
17508
  }
@@ -17570,22 +17510,22 @@ function mkdirTolerateRace(dir) {
17570
17510
  }
17571
17511
  }
17572
17512
  function ensureRealSubdirNoSymlink(root, target, create) {
17573
- const rootResolved = path11.resolve(root);
17574
- const targetResolved = path11.resolve(target);
17575
- if (targetResolved !== rootResolved && !targetResolved.startsWith(rootResolved + path11.sep)) return false;
17513
+ const rootResolved = path10.resolve(root);
17514
+ const targetResolved = path10.resolve(target);
17515
+ if (targetResolved !== rootResolved && !targetResolved.startsWith(rootResolved + path10.sep)) return false;
17576
17516
  try {
17577
- if (!fs10.lstatSync(rootResolved).isDirectory()) return false;
17517
+ if (!fs9.lstatSync(rootResolved).isDirectory()) return false;
17578
17518
  } catch {
17579
17519
  return false;
17580
17520
  }
17581
- const rel = path11.relative(rootResolved, targetResolved);
17521
+ const rel = path10.relative(rootResolved, targetResolved);
17582
17522
  if (rel === "") return true;
17583
17523
  let cur = rootResolved;
17584
- for (const segment of rel.split(path11.sep)) {
17585
- cur = path11.join(cur, segment);
17524
+ for (const segment of rel.split(path10.sep)) {
17525
+ cur = path10.join(cur, segment);
17586
17526
  let st;
17587
17527
  try {
17588
- st = fs10.lstatSync(cur);
17528
+ st = fs9.lstatSync(cur);
17589
17529
  } catch {
17590
17530
  st = void 0;
17591
17531
  }
@@ -17600,24 +17540,24 @@ function ensureRealSubdirNoSymlink(root, target, create) {
17600
17540
  return true;
17601
17541
  }
17602
17542
  function readFileNoFollow(root, abs) {
17603
- if (!ensureRealSubdirNoSymlink(root, path11.dirname(abs), false)) return null;
17543
+ if (!ensureRealSubdirNoSymlink(root, path10.dirname(abs), false)) return null;
17604
17544
  try {
17605
- if (!fs10.lstatSync(abs).isFile()) return null;
17606
- return fs10.readFileSync(abs);
17545
+ if (!fs9.lstatSync(abs).isFile()) return null;
17546
+ return fs9.readFileSync(abs);
17607
17547
  } catch {
17608
17548
  return null;
17609
17549
  }
17610
17550
  }
17611
17551
  function writeFileNoFollow(root, abs, data) {
17612
- const parent = path11.dirname(abs);
17552
+ const parent = path10.dirname(abs);
17613
17553
  if (!ensureRealSubdirNoSymlink(root, parent, true)) return false;
17614
17554
  let st;
17615
17555
  try {
17616
- st = fs10.lstatSync(abs);
17556
+ st = fs9.lstatSync(abs);
17617
17557
  } catch {
17618
17558
  }
17619
- if (st?.isSymbolicLink()) fs10.rmSync(abs);
17620
- fs10.writeFileSync(abs, data);
17559
+ if (st?.isSymbolicLink()) fs9.rmSync(abs);
17560
+ fs9.writeFileSync(abs, data);
17621
17561
  return true;
17622
17562
  }
17623
17563
  var init_fs_safety = __esm({
@@ -17627,8 +17567,8 @@ var init_fs_safety = __esm({
17627
17567
  });
17628
17568
 
17629
17569
  // src/lib/resource-files.ts
17630
- import * as fs11 from "fs";
17631
- import * as path12 from "path";
17570
+ import * as fs10 from "fs";
17571
+ import * as path11 from "path";
17632
17572
  import * as crypto3 from "crypto";
17633
17573
  function isBinaryFile(filename) {
17634
17574
  const ext = filename.split(".").pop()?.toLowerCase() || "";
@@ -17674,14 +17614,14 @@ function computeHash(data) {
17674
17614
  }
17675
17615
  function scanResourceFiles(dir, prefix = "") {
17676
17616
  const files = [];
17677
- const entries = fs11.readdirSync(dir, { withFileTypes: true });
17617
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
17678
17618
  for (const entry of entries) {
17679
17619
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
17680
17620
  if (entry.isDirectory()) {
17681
- files.push(...scanResourceFiles(path12.join(dir, entry.name), relPath));
17621
+ files.push(...scanResourceFiles(path11.join(dir, entry.name), relPath));
17682
17622
  } else if (entry.isFile()) {
17683
- const fullPath = path12.join(dir, entry.name);
17684
- const stat2 = fs11.statSync(fullPath);
17623
+ const fullPath = path11.join(dir, entry.name);
17624
+ const stat2 = fs10.statSync(fullPath);
17685
17625
  if (stat2.size > MAX_RESOURCE_FILE_SIZE3) {
17686
17626
  console.warn(` Warning: skipping ${relPath} (${(stat2.size / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit)`);
17687
17627
  continue;
@@ -17691,7 +17631,7 @@ function scanResourceFiles(dir, prefix = "") {
17691
17631
  mime_type: guessMimeType(entry.name),
17692
17632
  file_size: stat2.size
17693
17633
  };
17694
- const data = fs11.readFileSync(fullPath);
17634
+ const data = fs10.readFileSync(fullPath);
17695
17635
  fileEntry.hash = computeHash(data);
17696
17636
  if (isBinaryFile(entry.name)) {
17697
17637
  fileEntry.content_base64 = data.toString("base64");
@@ -17751,13 +17691,13 @@ function writeResourceFileTree(resDir, files, root, log) {
17751
17691
  return;
17752
17692
  }
17753
17693
  if (files.length === 0) {
17754
- if (fs11.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
17694
+ if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
17755
17695
  return;
17756
17696
  }
17757
17697
  const writtenPaths = /* @__PURE__ */ new Set();
17758
17698
  for (const file of files) {
17759
- const filePath = path12.join(resDir, file.path);
17760
- if (!filePath.startsWith(resDir + path12.sep) && filePath !== resDir) {
17699
+ const filePath = path11.join(resDir, file.path);
17700
+ if (!filePath.startsWith(resDir + path11.sep) && filePath !== resDir) {
17761
17701
  console.warn(` Warning: skipping ${file.path} (path traversal detected)`);
17762
17702
  continue;
17763
17703
  }
@@ -17766,25 +17706,25 @@ function writeResourceFileTree(resDir, files, root, log) {
17766
17706
  if (buf === void 0) continue;
17767
17707
  if (fileHasBytes(filePath, buf)) continue;
17768
17708
  if (writeFileNoFollow(resDir, filePath, buf)) {
17769
- log?.changed.push(path12.relative(root, filePath));
17709
+ log?.changed.push(path11.relative(root, filePath));
17770
17710
  } else {
17771
17711
  console.warn(` Warning: skipping ${file.path} (destination escapes the resource folder via a symlink)`);
17772
17712
  writtenPaths.delete(file.path);
17773
17713
  }
17774
17714
  }
17775
- if (fs11.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
17715
+ if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
17776
17716
  }
17777
17717
  function cleanOrphanFiles(dir, prefix, writtenPaths, root, log) {
17778
- const entries = fs11.readdirSync(dir, { withFileTypes: true });
17718
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
17779
17719
  for (const entry of entries) {
17780
17720
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
17781
- const fullPath = path12.join(dir, entry.name);
17721
+ const fullPath = path11.join(dir, entry.name);
17782
17722
  if (entry.isDirectory()) {
17783
17723
  cleanOrphanFiles(fullPath, relPath, writtenPaths, root, log);
17784
- if (fs11.readdirSync(fullPath).length === 0) fs11.rmdirSync(fullPath);
17724
+ if (fs10.readdirSync(fullPath).length === 0) fs10.rmdirSync(fullPath);
17785
17725
  } else if (!writtenPaths.has(relPath)) {
17786
- fs11.unlinkSync(fullPath);
17787
- if (log && root) log.removed.push(path12.relative(root, fullPath));
17726
+ fs10.unlinkSync(fullPath);
17727
+ if (log && root) log.removed.push(path11.relative(root, fullPath));
17788
17728
  }
17789
17729
  }
17790
17730
  }
@@ -17797,15 +17737,15 @@ async function downloadBinaryFiles(resDir, files, root, log) {
17797
17737
  for (const file of files) {
17798
17738
  if (!file.download_url || file.content !== void 0) continue;
17799
17739
  try {
17800
- const filePath = path12.join(resDir, file.path);
17801
- if (!filePath.startsWith(resDir + path12.sep) && filePath !== resDir) {
17740
+ const filePath = path11.join(resDir, file.path);
17741
+ if (!filePath.startsWith(resDir + path11.sep) && filePath !== resDir) {
17802
17742
  console.warn(` Warning: skipping ${file.path} (path traversal detected)`);
17803
17743
  continue;
17804
17744
  }
17805
17745
  if (file.hash) {
17806
17746
  try {
17807
- const st = fs11.lstatSync(filePath);
17808
- if (st.isFile() && !st.isSymbolicLink() && computeHash(fs11.readFileSync(filePath)) === file.hash) continue;
17747
+ const st = fs10.lstatSync(filePath);
17748
+ if (st.isFile() && !st.isSymbolicLink() && computeHash(fs10.readFileSync(filePath)) === file.hash) continue;
17809
17749
  } catch {
17810
17750
  }
17811
17751
  }
@@ -17818,7 +17758,7 @@ async function downloadBinaryFiles(resDir, files, root, log) {
17818
17758
  if (fileHasBytes(filePath, buffer)) continue;
17819
17759
  if (writeFileNoFollow(resDir, filePath, buffer)) {
17820
17760
  count++;
17821
- log?.changed.push(path12.relative(root, filePath));
17761
+ log?.changed.push(path11.relative(root, filePath));
17822
17762
  } else {
17823
17763
  console.warn(` Warning: skipping ${file.path} (destination escapes the resource folder via a symlink)`);
17824
17764
  }
@@ -17859,8 +17799,8 @@ var init_resource_files = __esm({
17859
17799
  });
17860
17800
 
17861
17801
  // src/lib/eval-attachments.ts
17862
- import * as fs12 from "fs";
17863
- import * as path13 from "path";
17802
+ import * as fs11 from "fs";
17803
+ import * as path12 from "path";
17864
17804
  function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17865
17805
  const raw = turn.attachments;
17866
17806
  if (raw === void 0 || raw === null) return void 0;
@@ -17876,14 +17816,14 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17876
17816
  if (typeof entry !== "string" || !entry.trim()) {
17877
17817
  throw expected(`${label}: each attachment must be a hub-relative file path string.`);
17878
17818
  }
17879
- const abs = path13.resolve(hubFolder, entry);
17880
- const root = path13.resolve(hubFolder);
17881
- if (abs !== root && !abs.startsWith(root + path13.sep)) {
17819
+ const abs = path12.resolve(hubFolder, entry);
17820
+ const root = path12.resolve(hubFolder);
17821
+ if (abs !== root && !abs.startsWith(root + path12.sep)) {
17882
17822
  throw expected(`${label}: attachment "${entry}" escapes the hub folder.`);
17883
17823
  }
17884
17824
  let stat2;
17885
17825
  try {
17886
- stat2 = fs12.statSync(abs);
17826
+ stat2 = fs11.statSync(abs);
17887
17827
  } catch {
17888
17828
  }
17889
17829
  if (!stat2?.isFile()) {
@@ -17897,9 +17837,9 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17897
17837
  `${label}: attachment "${entry}" is ${(stat2.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
17898
17838
  );
17899
17839
  }
17900
- const data = fs12.readFileSync(abs);
17840
+ const data = fs11.readFileSync(abs);
17901
17841
  const hash = computeHash(data);
17902
- const fileName = path13.basename(abs);
17842
+ const fileName = path12.basename(abs);
17903
17843
  const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
17904
17844
  refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat2.size });
17905
17845
  if (!bytesByHash.has(hash)) {
@@ -17942,7 +17882,7 @@ function forEachAttachableTurn(payload, fn) {
17942
17882
  function rewriteEvalAttachmentsToLocalPaths(payload) {
17943
17883
  const urlByHash = new Map((payload.eval_attachment_files ?? []).map((f) => [f.hash, f.download_url]));
17944
17884
  const safeName = (ref) => {
17945
- const base = path13.basename(ref.file_name);
17885
+ const base = path12.basename(ref.file_name);
17946
17886
  return base === "" || base === "." || base === ".." ? ref.hash.slice(0, 16) : base;
17947
17887
  };
17948
17888
  const hashesByName = /* @__PURE__ */ new Map();
@@ -17967,8 +17907,8 @@ function rewriteEvalAttachmentsToLocalPaths(payload) {
17967
17907
  const key = `${ref.hash} ${name}`;
17968
17908
  const existing = pathByKey.get(key);
17969
17909
  if (existing) return existing;
17970
- const ext = path13.extname(name);
17971
- const stem = path13.basename(name, ext);
17910
+ const ext = path12.extname(name);
17911
+ const stem = path12.basename(name, ext);
17972
17912
  const collides = (hashesByName.get(name)?.size ?? 0) > 1;
17973
17913
  let rel = collides ? `${ATTACHMENTS_DIR}/${stem}-${ref.hash.slice(0, 8)}${ext}` : `${ATTACHMENTS_DIR}/${name}`;
17974
17914
  if (usedLower.has(rel.toLowerCase())) {
@@ -17999,22 +17939,22 @@ function rewriteEvalAttachmentsToLocalPaths(payload) {
17999
17939
  return [...downloads.values()];
18000
17940
  }
18001
17941
  async function downloadEvalAttachments(hubFolder, downloads) {
18002
- const attachDir = path13.resolve(hubFolder, ATTACHMENTS_DIR);
17942
+ const attachDir = path12.resolve(hubFolder, ATTACHMENTS_DIR);
18003
17943
  const result = { written: 0, changed: [], removed: [] };
18004
17944
  if (!ensureRealSubdirNoSymlink(hubFolder, attachDir, downloads.length > 0)) {
18005
17945
  console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
18006
17946
  return result;
18007
17947
  }
18008
- if (!fs12.existsSync(attachDir)) return result;
17948
+ if (!fs11.existsSync(attachDir)) return result;
18009
17949
  for (const dl of downloads) {
18010
- const abs = path13.resolve(hubFolder, dl.relPath);
18011
- if (abs !== attachDir && !abs.startsWith(attachDir + path13.sep)) continue;
17950
+ const abs = path12.resolve(hubFolder, dl.relPath);
17951
+ if (abs !== attachDir && !abs.startsWith(attachDir + path12.sep)) continue;
18012
17952
  let existing;
18013
17953
  try {
18014
- existing = fs12.lstatSync(abs);
17954
+ existing = fs11.lstatSync(abs);
18015
17955
  } catch {
18016
17956
  }
18017
- if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs12.readFileSync(abs)) === dl.hash) {
17957
+ if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs11.readFileSync(abs)) === dl.hash) {
18018
17958
  continue;
18019
17959
  }
18020
17960
  try {
@@ -18031,13 +17971,13 @@ async function downloadEvalAttachments(hubFolder, downloads) {
18031
17971
  console.warn(` Warning: failed to download ${dl.relPath}: ${err instanceof Error ? err.message : String(err)}`);
18032
17972
  }
18033
17973
  }
18034
- const keep = new Set(downloads.map((d) => path13.resolve(hubFolder, d.relPath)));
18035
- for (const name of fs12.readdirSync(attachDir)) {
18036
- const abs = path13.join(attachDir, name);
18037
- const st = fs12.lstatSync(abs);
17974
+ const keep = new Set(downloads.map((d) => path12.resolve(hubFolder, d.relPath)));
17975
+ for (const name of fs11.readdirSync(attachDir)) {
17976
+ const abs = path12.join(attachDir, name);
17977
+ const st = fs11.lstatSync(abs);
18038
17978
  if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) {
18039
- fs12.rmSync(abs);
18040
- result.removed.push(path13.relative(hubFolder, abs));
17979
+ fs11.rmSync(abs);
17980
+ result.removed.push(path12.relative(hubFolder, abs));
18041
17981
  }
18042
17982
  }
18043
17983
  return result;
@@ -18054,15 +17994,15 @@ var init_eval_attachments = __esm({
18054
17994
  });
18055
17995
 
18056
17996
  // src/lib/parser.ts
18057
- import * as fs13 from "fs";
18058
- import * as path14 from "path";
17997
+ import * as fs12 from "fs";
17998
+ import * as path13 from "path";
18059
17999
  import * as yaml6 from "js-yaml";
18060
18000
  function parseHubFolder(hubFolder, opts) {
18061
18001
  const yamlPath = resolveHubYamlPath(hubFolder);
18062
18002
  if (!yamlPath) {
18063
18003
  throw expected(`hub.yaml not found in ${hubFolder}`);
18064
18004
  }
18065
- const yamlContent = fs13.readFileSync(yamlPath, "utf-8");
18005
+ const yamlContent = fs12.readFileSync(yamlPath, "utf-8");
18066
18006
  let config;
18067
18007
  try {
18068
18008
  config = yaml6.load(yamlContent);
@@ -18089,18 +18029,18 @@ function parseHubFolder(hubFolder, opts) {
18089
18029
  const resolved = { ...agent };
18090
18030
  if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
18091
18031
  const instrValue = resolved.instructions;
18092
- const instructionsPath = instrValue.startsWith("agents/") ? path14.join(hubFolder, instrValue) : path14.join(hubFolder, "agents", instrValue);
18093
- if (fs13.existsSync(instructionsPath)) {
18094
- resolved.instructions = fs13.readFileSync(instructionsPath, "utf-8");
18032
+ const instructionsPath = instrValue.startsWith("agents/") ? path13.join(hubFolder, instrValue) : path13.join(hubFolder, "agents", instrValue);
18033
+ if (fs12.existsSync(instructionsPath)) {
18034
+ resolved.instructions = fs12.readFileSync(instructionsPath, "utf-8");
18095
18035
  } else {
18096
18036
  throw expected(
18097
18037
  `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
18098
18038
  );
18099
18039
  }
18100
18040
  } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
18101
- const conventionPath = path14.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
18102
- if (fs13.existsSync(conventionPath)) {
18103
- resolved.instructions = fs13.readFileSync(conventionPath, "utf-8");
18041
+ const conventionPath = path13.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
18042
+ if (fs12.existsSync(conventionPath)) {
18043
+ resolved.instructions = fs12.readFileSync(conventionPath, "utf-8");
18104
18044
  }
18105
18045
  }
18106
18046
  return resolved;
@@ -18154,26 +18094,26 @@ function parseHubFolder(hubFolder, opts) {
18154
18094
  return payload;
18155
18095
  }
18156
18096
  function scanEvalYamlFiles(hubFolder, bytesByHash) {
18157
- const evalsDir = path14.join(hubFolder, "evals");
18158
- if (!fs13.existsSync(evalsDir)) return [];
18097
+ const evalsDir = path13.join(hubFolder, "evals");
18098
+ if (!fs12.existsSync(evalsDir)) return [];
18159
18099
  const evals = [];
18160
18100
  const seen = /* @__PURE__ */ new Set();
18161
- const topEntries = fs13.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18101
+ const topEntries = fs12.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18162
18102
  for (const entry of topEntries) {
18163
18103
  if (entry.isFile() && entry.name.endsWith(".yaml")) {
18164
- collectEval(evals, seen, path14.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
18104
+ collectEval(evals, seen, path13.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
18165
18105
  } else if (entry.isDirectory()) {
18166
18106
  const setName = entry.name;
18167
- const setDir = path14.join(evalsDir, setName);
18168
- const setEntries = fs13.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18107
+ const setDir = path13.join(evalsDir, setName);
18108
+ const setEntries = fs12.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18169
18109
  for (const sub of setEntries) {
18170
18110
  if (sub.isDirectory()) {
18171
18111
  throw expected(
18172
- `Nested scenario sets are not supported: ${path14.relative(hubFolder, path14.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
18112
+ `Nested scenario sets are not supported: ${path13.relative(hubFolder, path13.join(setDir, sub.name))}. Move scenarios up to evals/${setName}/.`
18173
18113
  );
18174
18114
  }
18175
18115
  if (sub.isFile() && sub.name.endsWith(".yaml")) {
18176
- collectEval(evals, seen, path14.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
18116
+ collectEval(evals, seen, path13.join(setDir, sub.name), `evals/${setName}/${sub.name}`, setName, hubFolder, bytesByHash);
18177
18117
  }
18178
18118
  }
18179
18119
  }
@@ -18187,14 +18127,14 @@ function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesBy
18187
18127
  if (seen.has(key)) {
18188
18128
  const where = setName ? `scenario set "${setName}"` : "root";
18189
18129
  throw expected(
18190
- `Duplicate eval at ${where}: "${evalEntry.name}" (in ${path14.basename(filePath)}). Each (name, scenario set) pair must be unique.`
18130
+ `Duplicate eval at ${where}: "${evalEntry.name}" (in ${path13.basename(filePath)}). Each (name, scenario set) pair must be unique.`
18191
18131
  );
18192
18132
  }
18193
18133
  seen.add(key);
18194
18134
  evals.push(evalEntry);
18195
18135
  }
18196
18136
  function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
18197
- const content = fs13.readFileSync(filePath, "utf-8");
18137
+ const content = fs12.readFileSync(filePath, "utf-8");
18198
18138
  let raw;
18199
18139
  try {
18200
18140
  raw = yaml6.load(content);
@@ -18209,7 +18149,7 @@ function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
18209
18149
  return null;
18210
18150
  }
18211
18151
  const data = raw;
18212
- const fileSlug = path14.basename(filePath, ".yaml");
18152
+ const fileSlug = path13.basename(filePath, ".yaml");
18213
18153
  const evalName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
18214
18154
  if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
18215
18155
  throw expected(`Eval "${evalName}" in ${relPath}: missing required field "agent" (string).`);
@@ -18296,19 +18236,19 @@ function parseFixtureField(raw, label, key) {
18296
18236
  return trimmed.length > 0 ? trimmed : void 0;
18297
18237
  }
18298
18238
  function scanJourneyYamlFiles(hubFolder, bytesByHash) {
18299
- const journeysDir = path14.join(hubFolder, "journeys");
18300
- if (!fs13.existsSync(journeysDir)) return [];
18239
+ const journeysDir = path13.join(hubFolder, "journeys");
18240
+ if (!fs12.existsSync(journeysDir)) return [];
18301
18241
  const journeys = [];
18302
18242
  const seen = /* @__PURE__ */ new Set();
18303
- const entries = fs13.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18243
+ const entries = fs12.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
18304
18244
  for (const entry of entries) {
18305
18245
  if (entry.isDirectory()) {
18306
18246
  throw expected(
18307
- `Subfolders are not supported under journeys/: ${path14.relative(hubFolder, path14.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
18247
+ `Subfolders are not supported under journeys/: ${path13.relative(hubFolder, path13.join(journeysDir, entry.name))}. A journey owns its own managed scenario set \u2014 put each journey in a single journeys/<slug>.yaml file.`
18308
18248
  );
18309
18249
  }
18310
18250
  if (!entry.isFile() || !entry.name.endsWith(".yaml")) continue;
18311
- const journeyEntry = parseJourneyYaml(path14.join(journeysDir, entry.name), `journeys/${entry.name}`, hubFolder, bytesByHash);
18251
+ const journeyEntry = parseJourneyYaml(path13.join(journeysDir, entry.name), `journeys/${entry.name}`, hubFolder, bytesByHash);
18312
18252
  if (!journeyEntry) continue;
18313
18253
  if (seen.has(journeyEntry.name)) {
18314
18254
  throw expected(
@@ -18321,7 +18261,7 @@ function scanJourneyYamlFiles(hubFolder, bytesByHash) {
18321
18261
  return journeys;
18322
18262
  }
18323
18263
  function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
18324
- const content = fs13.readFileSync(filePath, "utf-8");
18264
+ const content = fs12.readFileSync(filePath, "utf-8");
18325
18265
  let raw;
18326
18266
  try {
18327
18267
  raw = yaml6.load(content);
@@ -18336,7 +18276,7 @@ function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
18336
18276
  return null;
18337
18277
  }
18338
18278
  const data = raw;
18339
- const fileSlug = path14.basename(filePath, ".yaml");
18279
+ const fileSlug = path13.basename(filePath, ".yaml");
18340
18280
  const journeyName = typeof data.name === "string" && data.name.trim().length > 0 ? data.name : fileSlug;
18341
18281
  if (typeof data.agent !== "string" || data.agent.trim().length === 0) {
18342
18282
  throw expected(`Journey "${journeyName}" in ${relPath}: missing required field "agent" (string).`);
@@ -18376,14 +18316,14 @@ function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
18376
18316
  return journeyEntry;
18377
18317
  }
18378
18318
  function scanAgentYamlFiles(hubFolder) {
18379
- const agentsDir = path14.join(hubFolder, "agents");
18380
- if (!fs13.existsSync(agentsDir)) return [];
18381
- const yamlFiles = fs13.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
18319
+ const agentsDir = path13.join(hubFolder, "agents");
18320
+ if (!fs12.existsSync(agentsDir)) return [];
18321
+ const yamlFiles = fs12.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
18382
18322
  if (yamlFiles.length === 0) return [];
18383
18323
  const agents = [];
18384
18324
  for (const file of yamlFiles) {
18385
- const filePath = path14.join(agentsDir, file);
18386
- const content = fs13.readFileSync(filePath, "utf-8");
18325
+ const filePath = path13.join(agentsDir, file);
18326
+ const content = fs12.readFileSync(filePath, "utf-8");
18387
18327
  let agent;
18388
18328
  try {
18389
18329
  agent = yaml6.load(content);
@@ -18403,7 +18343,7 @@ function scanAgentYamlFiles(hubFolder) {
18403
18343
  return agents;
18404
18344
  }
18405
18345
  function parseResources(hubFolder, configResources) {
18406
- const resourcesDir = path14.join(hubFolder, "resources");
18346
+ const resourcesDir = path13.join(hubFolder, "resources");
18407
18347
  const results = [];
18408
18348
  for (const res of configResources) {
18409
18349
  const resource = {
@@ -18417,8 +18357,8 @@ function parseResources(hubFolder, configResources) {
18417
18357
  if (res.user_browsable) resource.user_browsable = res.user_browsable;
18418
18358
  if (res.skill_name) resource.skill_name = res.skill_name;
18419
18359
  const resSlug = slugify(resource.name);
18420
- const resDir = path14.join(resourcesDir, resSlug);
18421
- if (fs13.existsSync(resDir)) {
18360
+ const resDir = path13.join(resourcesDir, resSlug);
18361
+ if (fs12.existsSync(resDir)) {
18422
18362
  const files = scanResourceFiles(resDir, "");
18423
18363
  if (files.length > 0) {
18424
18364
  resource.files = files;
@@ -18465,6 +18405,36 @@ var init_diff_display = __esm({
18465
18405
  }
18466
18406
  });
18467
18407
 
18408
+ // src/lib/workspace-files.ts
18409
+ import * as fs13 from "fs";
18410
+ import * as path14 from "path";
18411
+ function perHubAgentsMd(hubFolderName) {
18412
+ return [
18413
+ `# ${hubFolderName}`,
18414
+ "",
18415
+ "Notes about THIS hub that the config files (hub.yaml, agents/) don't capture:",
18416
+ "why it exists, key decisions and the reasons behind them, business rules that",
18417
+ "only apply here, terminology, integration quirks.",
18418
+ "",
18419
+ "Keep it short and about *why / what is different* \u2014 platform mechanics live in",
18420
+ "the WayAI skill. Fill this in as you learn about the hub; delete this",
18421
+ "placeholder once you do.",
18422
+ ""
18423
+ ].join("\n");
18424
+ }
18425
+ function writeIfAbsent(filePath, content) {
18426
+ if (fs13.existsSync(filePath)) return null;
18427
+ fs13.writeFileSync(filePath, content, "utf-8");
18428
+ return path14.basename(filePath);
18429
+ }
18430
+ var CLAUDE_MD_SHIM;
18431
+ var init_workspace_files = __esm({
18432
+ "src/lib/workspace-files.ts"() {
18433
+ "use strict";
18434
+ CLAUDE_MD_SHIM = "@AGENTS.md\n";
18435
+ }
18436
+ });
18437
+
18468
18438
  // src/lib/yaml-writer.ts
18469
18439
  import * as fs14 from "fs";
18470
18440
  import * as path15 from "path";
@@ -20482,11 +20452,6 @@ async function pushCommand(args2) {
20482
20452
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
20483
20453
  const workspaceDir = resolveWorkspaceDir();
20484
20454
  const wsLabel = hubsDirLabel(gitRoot);
20485
- if (gitRoot) {
20486
- for (const file of ensureWorkspaceFiles(gitRoot)) {
20487
- console.error(`Created ${file}`);
20488
- }
20489
- }
20490
20455
  if (!fs18.existsSync(workspaceDir)) {
20491
20456
  console.error(`No ${wsLabel}/ directory found. Run \`wayai pull\` first or create hub files in ${wsLabel}/<hub>/hub.yaml.`);
20492
20457
  process.exit(1);
@@ -20528,7 +20493,6 @@ var init_push = __esm({
20528
20493
  init_utils();
20529
20494
  init_workspace();
20530
20495
  init_layout();
20531
- init_workspace_files();
20532
20496
  init_repo_config();
20533
20497
  init_worktree_scope();
20534
20498
  init_subtree_routing();
@@ -20591,11 +20555,6 @@ async function pullCommand(args2) {
20591
20555
  const { config, accessToken } = await requireAuth();
20592
20556
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
20593
20557
  const workspaceDir = resolveWorkspaceDir();
20594
- if (gitRoot) {
20595
- for (const file of ensureWorkspaceFiles(gitRoot)) {
20596
- console.error(`Created ${file}`);
20597
- }
20598
- }
20599
20558
  const { hubId, hubFolder: existingFolder } = resolveHubTarget(workspaceDir, hubSelector);
20600
20559
  console.log("Fetching hub configuration...");
20601
20560
  const payload = await client.pull(hubId, organizationId);
@@ -20723,7 +20682,6 @@ var init_pull = __esm({
20723
20682
  init_push();
20724
20683
  init_workspace();
20725
20684
  init_layout();
20726
- init_workspace_files();
20727
20685
  init_repo_config();
20728
20686
  init_worktree_scope();
20729
20687
  init_subtree_routing();
@@ -27969,12 +27927,12 @@ var init_providers = __esm({
27969
27927
  // src/data/commands/report.ts
27970
27928
  import { Command as Command6 } from "commander";
27971
27929
  import { readFileSync as readFileSync21 } from "fs";
27972
- import { dirname as dirname12, join as join31 } from "path";
27930
+ import { dirname as dirname12, join as join30 } from "path";
27973
27931
  import { fileURLToPath as fileURLToPath2 } from "url";
27974
27932
  function resolveCliVersion() {
27975
27933
  for (const candidate of [
27976
- join31(here, "..", "package.json"),
27977
- join31(here, "..", "..", "..", "package.json")
27934
+ join30(here, "..", "package.json"),
27935
+ join30(here, "..", "..", "..", "package.json")
27978
27936
  ]) {
27979
27937
  try {
27980
27938
  const version = JSON.parse(readFileSync21(candidate, "utf-8")).version;
@@ -30106,7 +30064,7 @@ init_utils();
30106
30064
  init_registry();
30107
30065
  import { readFileSync as readFileSync25 } from "fs";
30108
30066
  import { fileURLToPath as fileURLToPath3 } from "url";
30109
- import { dirname as dirname13, join as join32 } from "path";
30067
+ import { dirname as dirname13, join as join31 } from "path";
30110
30068
 
30111
30069
  // src/lib/version-refresh.ts
30112
30070
  init_version_cache();
@@ -30262,7 +30220,7 @@ Run \`wayai admin skill install\` to update.`);
30262
30220
 
30263
30221
  // src/index.ts
30264
30222
  var __dirname = dirname13(fileURLToPath3(import.meta.url));
30265
- var pkg = JSON.parse(readFileSync25(join32(__dirname, "..", "package.json"), "utf-8"));
30223
+ var pkg = JSON.parse(readFileSync25(join31(__dirname, "..", "package.json"), "utf-8"));
30266
30224
  var [, , command, ...args] = process.argv;
30267
30225
  var isBackgroundRefresh = command === REFRESH_COMMAND;
30268
30226
  if (!isBackgroundRefresh) initSentry(command, pkg.version);