yarkr-cli 0.1.0 → 0.1.5

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.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/dist/yarkr.js +627 -826
  3. package/package.json +6 -7
package/dist/yarkr.js CHANGED
@@ -1206,7 +1206,7 @@ var require_command = __commonJS({
1206
1206
  "use strict";
1207
1207
  var EventEmitter = __require("events").EventEmitter;
1208
1208
  var childProcess = __require("child_process");
1209
- var path16 = __require("path");
1209
+ var path17 = __require("path");
1210
1210
  var fs = __require("fs");
1211
1211
  var process2 = __require("process");
1212
1212
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2219,9 +2219,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2219
2219
  let launchWithNode = false;
2220
2220
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2221
2221
  function findFile(baseDir, baseName) {
2222
- const localBin = path16.resolve(baseDir, baseName);
2222
+ const localBin = path17.resolve(baseDir, baseName);
2223
2223
  if (fs.existsSync(localBin)) return localBin;
2224
- if (sourceExt.includes(path16.extname(baseName))) return void 0;
2224
+ if (sourceExt.includes(path17.extname(baseName))) return void 0;
2225
2225
  const foundExt = sourceExt.find(
2226
2226
  (ext) => fs.existsSync(`${localBin}${ext}`)
2227
2227
  );
@@ -2239,17 +2239,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2239
2239
  } catch {
2240
2240
  resolvedScriptPath = this._scriptPath;
2241
2241
  }
2242
- executableDir = path16.resolve(
2243
- path16.dirname(resolvedScriptPath),
2242
+ executableDir = path17.resolve(
2243
+ path17.dirname(resolvedScriptPath),
2244
2244
  executableDir
2245
2245
  );
2246
2246
  }
2247
2247
  if (executableDir) {
2248
2248
  let localFile = findFile(executableDir, executableFile);
2249
2249
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2250
- const legacyName = path16.basename(
2250
+ const legacyName = path17.basename(
2251
2251
  this._scriptPath,
2252
- path16.extname(this._scriptPath)
2252
+ path17.extname(this._scriptPath)
2253
2253
  );
2254
2254
  if (legacyName !== this._name) {
2255
2255
  localFile = findFile(
@@ -2260,7 +2260,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2260
2260
  }
2261
2261
  executableFile = localFile || executableFile;
2262
2262
  }
2263
- launchWithNode = sourceExt.includes(path16.extname(executableFile));
2263
+ launchWithNode = sourceExt.includes(path17.extname(executableFile));
2264
2264
  let proc;
2265
2265
  if (process2.platform !== "win32") {
2266
2266
  if (launchWithNode) {
@@ -3175,7 +3175,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3175
3175
  * @return {Command}
3176
3176
  */
3177
3177
  nameFromFilename(filename) {
3178
- this._name = path16.basename(filename, path16.extname(filename));
3178
+ this._name = path17.basename(filename, path17.extname(filename));
3179
3179
  return this;
3180
3180
  }
3181
3181
  /**
@@ -3189,9 +3189,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3189
3189
  * @param {string} [path]
3190
3190
  * @return {(string|null|Command)}
3191
3191
  */
3192
- executableDir(path17) {
3193
- if (path17 === void 0) return this._executableDir;
3194
- this._executableDir = path17;
3192
+ executableDir(path18) {
3193
+ if (path18 === void 0) return this._executableDir;
3194
+ this._executableDir = path18;
3195
3195
  return this;
3196
3196
  }
3197
3197
  /**
@@ -3547,17 +3547,17 @@ var require_visit = __commonJS({
3547
3547
  visit2.BREAK = BREAK;
3548
3548
  visit2.SKIP = SKIP2;
3549
3549
  visit2.REMOVE = REMOVE;
3550
- function visit_(key, node2, visitor, path16) {
3551
- const ctrl = callVisitor(key, node2, visitor, path16);
3550
+ function visit_(key, node2, visitor, path17) {
3551
+ const ctrl = callVisitor(key, node2, visitor, path17);
3552
3552
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
3553
- replaceNode(key, path16, ctrl);
3554
- return visit_(key, ctrl, visitor, path16);
3553
+ replaceNode(key, path17, ctrl);
3554
+ return visit_(key, ctrl, visitor, path17);
3555
3555
  }
3556
3556
  if (typeof ctrl !== "symbol") {
3557
3557
  if (identity.isCollection(node2)) {
3558
- path16 = Object.freeze(path16.concat(node2));
3558
+ path17 = Object.freeze(path17.concat(node2));
3559
3559
  for (let i = 0; i < node2.items.length; ++i) {
3560
- const ci = visit_(i, node2.items[i], visitor, path16);
3560
+ const ci = visit_(i, node2.items[i], visitor, path17);
3561
3561
  if (typeof ci === "number")
3562
3562
  i = ci - 1;
3563
3563
  else if (ci === BREAK)
@@ -3568,13 +3568,13 @@ var require_visit = __commonJS({
3568
3568
  }
3569
3569
  }
3570
3570
  } else if (identity.isPair(node2)) {
3571
- path16 = Object.freeze(path16.concat(node2));
3572
- const ck = visit_("key", node2.key, visitor, path16);
3571
+ path17 = Object.freeze(path17.concat(node2));
3572
+ const ck = visit_("key", node2.key, visitor, path17);
3573
3573
  if (ck === BREAK)
3574
3574
  return BREAK;
3575
3575
  else if (ck === REMOVE)
3576
3576
  node2.key = null;
3577
- const cv = visit_("value", node2.value, visitor, path16);
3577
+ const cv = visit_("value", node2.value, visitor, path17);
3578
3578
  if (cv === BREAK)
3579
3579
  return BREAK;
3580
3580
  else if (cv === REMOVE)
@@ -3595,17 +3595,17 @@ var require_visit = __commonJS({
3595
3595
  visitAsync.BREAK = BREAK;
3596
3596
  visitAsync.SKIP = SKIP2;
3597
3597
  visitAsync.REMOVE = REMOVE;
3598
- async function visitAsync_(key, node2, visitor, path16) {
3599
- const ctrl = await callVisitor(key, node2, visitor, path16);
3598
+ async function visitAsync_(key, node2, visitor, path17) {
3599
+ const ctrl = await callVisitor(key, node2, visitor, path17);
3600
3600
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
3601
- replaceNode(key, path16, ctrl);
3602
- return visitAsync_(key, ctrl, visitor, path16);
3601
+ replaceNode(key, path17, ctrl);
3602
+ return visitAsync_(key, ctrl, visitor, path17);
3603
3603
  }
3604
3604
  if (typeof ctrl !== "symbol") {
3605
3605
  if (identity.isCollection(node2)) {
3606
- path16 = Object.freeze(path16.concat(node2));
3606
+ path17 = Object.freeze(path17.concat(node2));
3607
3607
  for (let i = 0; i < node2.items.length; ++i) {
3608
- const ci = await visitAsync_(i, node2.items[i], visitor, path16);
3608
+ const ci = await visitAsync_(i, node2.items[i], visitor, path17);
3609
3609
  if (typeof ci === "number")
3610
3610
  i = ci - 1;
3611
3611
  else if (ci === BREAK)
@@ -3616,13 +3616,13 @@ var require_visit = __commonJS({
3616
3616
  }
3617
3617
  }
3618
3618
  } else if (identity.isPair(node2)) {
3619
- path16 = Object.freeze(path16.concat(node2));
3620
- const ck = await visitAsync_("key", node2.key, visitor, path16);
3619
+ path17 = Object.freeze(path17.concat(node2));
3620
+ const ck = await visitAsync_("key", node2.key, visitor, path17);
3621
3621
  if (ck === BREAK)
3622
3622
  return BREAK;
3623
3623
  else if (ck === REMOVE)
3624
3624
  node2.key = null;
3625
- const cv = await visitAsync_("value", node2.value, visitor, path16);
3625
+ const cv = await visitAsync_("value", node2.value, visitor, path17);
3626
3626
  if (cv === BREAK)
3627
3627
  return BREAK;
3628
3628
  else if (cv === REMOVE)
@@ -3649,23 +3649,23 @@ var require_visit = __commonJS({
3649
3649
  }
3650
3650
  return visitor;
3651
3651
  }
3652
- function callVisitor(key, node2, visitor, path16) {
3652
+ function callVisitor(key, node2, visitor, path17) {
3653
3653
  if (typeof visitor === "function")
3654
- return visitor(key, node2, path16);
3654
+ return visitor(key, node2, path17);
3655
3655
  if (identity.isMap(node2))
3656
- return visitor.Map?.(key, node2, path16);
3656
+ return visitor.Map?.(key, node2, path17);
3657
3657
  if (identity.isSeq(node2))
3658
- return visitor.Seq?.(key, node2, path16);
3658
+ return visitor.Seq?.(key, node2, path17);
3659
3659
  if (identity.isPair(node2))
3660
- return visitor.Pair?.(key, node2, path16);
3660
+ return visitor.Pair?.(key, node2, path17);
3661
3661
  if (identity.isScalar(node2))
3662
- return visitor.Scalar?.(key, node2, path16);
3662
+ return visitor.Scalar?.(key, node2, path17);
3663
3663
  if (identity.isAlias(node2))
3664
- return visitor.Alias?.(key, node2, path16);
3664
+ return visitor.Alias?.(key, node2, path17);
3665
3665
  return void 0;
3666
3666
  }
3667
- function replaceNode(key, path16, node2) {
3668
- const parent = path16[path16.length - 1];
3667
+ function replaceNode(key, path17, node2) {
3668
+ const parent = path17[path17.length - 1];
3669
3669
  if (identity.isCollection(parent)) {
3670
3670
  parent.items[key] = node2;
3671
3671
  } else if (identity.isPair(parent)) {
@@ -4277,10 +4277,10 @@ var require_Collection = __commonJS({
4277
4277
  var createNode = require_createNode();
4278
4278
  var identity = require_identity();
4279
4279
  var Node = require_Node();
4280
- function collectionFromPath(schema, path16, value) {
4280
+ function collectionFromPath(schema, path17, value) {
4281
4281
  let v = value;
4282
- for (let i = path16.length - 1; i >= 0; --i) {
4283
- const k = path16[i];
4282
+ for (let i = path17.length - 1; i >= 0; --i) {
4283
+ const k = path17[i];
4284
4284
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
4285
4285
  const a = [];
4286
4286
  a[k] = v;
@@ -4299,7 +4299,7 @@ var require_Collection = __commonJS({
4299
4299
  sourceObjects: /* @__PURE__ */ new Map()
4300
4300
  });
4301
4301
  }
4302
- var isEmptyPath = (path16) => path16 == null || typeof path16 === "object" && !!path16[Symbol.iterator]().next().done;
4302
+ var isEmptyPath = (path17) => path17 == null || typeof path17 === "object" && !!path17[Symbol.iterator]().next().done;
4303
4303
  var Collection = class extends Node.NodeBase {
4304
4304
  constructor(type, schema) {
4305
4305
  super(type);
@@ -4329,11 +4329,11 @@ var require_Collection = __commonJS({
4329
4329
  * be a Pair instance or a `{ key, value }` object, which may not have a key
4330
4330
  * that already exists in the map.
4331
4331
  */
4332
- addIn(path16, value) {
4333
- if (isEmptyPath(path16))
4332
+ addIn(path17, value) {
4333
+ if (isEmptyPath(path17))
4334
4334
  this.add(value);
4335
4335
  else {
4336
- const [key, ...rest] = path16;
4336
+ const [key, ...rest] = path17;
4337
4337
  const node2 = this.get(key, true);
4338
4338
  if (identity.isCollection(node2))
4339
4339
  node2.addIn(rest, value);
@@ -4347,8 +4347,8 @@ var require_Collection = __commonJS({
4347
4347
  * Removes a value from the collection.
4348
4348
  * @returns `true` if the item was found and removed.
4349
4349
  */
4350
- deleteIn(path16) {
4351
- const [key, ...rest] = path16;
4350
+ deleteIn(path17) {
4351
+ const [key, ...rest] = path17;
4352
4352
  if (rest.length === 0)
4353
4353
  return this.delete(key);
4354
4354
  const node2 = this.get(key, true);
@@ -4362,8 +4362,8 @@ var require_Collection = __commonJS({
4362
4362
  * scalar values from their surrounding node; to disable set `keepScalar` to
4363
4363
  * `true` (collections are always returned intact).
4364
4364
  */
4365
- getIn(path16, keepScalar) {
4366
- const [key, ...rest] = path16;
4365
+ getIn(path17, keepScalar) {
4366
+ const [key, ...rest] = path17;
4367
4367
  const node2 = this.get(key, true);
4368
4368
  if (rest.length === 0)
4369
4369
  return !keepScalar && identity.isScalar(node2) ? node2.value : node2;
@@ -4381,8 +4381,8 @@ var require_Collection = __commonJS({
4381
4381
  /**
4382
4382
  * Checks if the collection includes a value with the key `key`.
4383
4383
  */
4384
- hasIn(path16) {
4385
- const [key, ...rest] = path16;
4384
+ hasIn(path17) {
4385
+ const [key, ...rest] = path17;
4386
4386
  if (rest.length === 0)
4387
4387
  return this.has(key);
4388
4388
  const node2 = this.get(key, true);
@@ -4392,8 +4392,8 @@ var require_Collection = __commonJS({
4392
4392
  * Sets a value in this collection. For `!!set`, `value` needs to be a
4393
4393
  * boolean to add/remove the item from the set.
4394
4394
  */
4395
- setIn(path16, value) {
4396
- const [key, ...rest] = path16;
4395
+ setIn(path17, value) {
4396
+ const [key, ...rest] = path17;
4397
4397
  if (rest.length === 0) {
4398
4398
  this.set(key, value);
4399
4399
  } else {
@@ -6908,9 +6908,9 @@ var require_Document = __commonJS({
6908
6908
  this.contents.add(value);
6909
6909
  }
6910
6910
  /** Adds a value to the document. */
6911
- addIn(path16, value) {
6911
+ addIn(path17, value) {
6912
6912
  if (assertCollection(this.contents))
6913
- this.contents.addIn(path16, value);
6913
+ this.contents.addIn(path17, value);
6914
6914
  }
6915
6915
  /**
6916
6916
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -6985,14 +6985,14 @@ var require_Document = __commonJS({
6985
6985
  * Removes a value from the document.
6986
6986
  * @returns `true` if the item was found and removed.
6987
6987
  */
6988
- deleteIn(path16) {
6989
- if (Collection.isEmptyPath(path16)) {
6988
+ deleteIn(path17) {
6989
+ if (Collection.isEmptyPath(path17)) {
6990
6990
  if (this.contents == null)
6991
6991
  return false;
6992
6992
  this.contents = null;
6993
6993
  return true;
6994
6994
  }
6995
- return assertCollection(this.contents) ? this.contents.deleteIn(path16) : false;
6995
+ return assertCollection(this.contents) ? this.contents.deleteIn(path17) : false;
6996
6996
  }
6997
6997
  /**
6998
6998
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -7007,10 +7007,10 @@ var require_Document = __commonJS({
7007
7007
  * scalar values from their surrounding node; to disable set `keepScalar` to
7008
7008
  * `true` (collections are always returned intact).
7009
7009
  */
7010
- getIn(path16, keepScalar) {
7011
- if (Collection.isEmptyPath(path16))
7010
+ getIn(path17, keepScalar) {
7011
+ if (Collection.isEmptyPath(path17))
7012
7012
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
7013
- return identity.isCollection(this.contents) ? this.contents.getIn(path16, keepScalar) : void 0;
7013
+ return identity.isCollection(this.contents) ? this.contents.getIn(path17, keepScalar) : void 0;
7014
7014
  }
7015
7015
  /**
7016
7016
  * Checks if the document includes a value with the key `key`.
@@ -7021,10 +7021,10 @@ var require_Document = __commonJS({
7021
7021
  /**
7022
7022
  * Checks if the document includes a value at `path`.
7023
7023
  */
7024
- hasIn(path16) {
7025
- if (Collection.isEmptyPath(path16))
7024
+ hasIn(path17) {
7025
+ if (Collection.isEmptyPath(path17))
7026
7026
  return this.contents !== void 0;
7027
- return identity.isCollection(this.contents) ? this.contents.hasIn(path16) : false;
7027
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path17) : false;
7028
7028
  }
7029
7029
  /**
7030
7030
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -7041,13 +7041,13 @@ var require_Document = __commonJS({
7041
7041
  * Sets a value in this document. For `!!set`, `value` needs to be a
7042
7042
  * boolean to add/remove the item from the set.
7043
7043
  */
7044
- setIn(path16, value) {
7045
- if (Collection.isEmptyPath(path16)) {
7044
+ setIn(path17, value) {
7045
+ if (Collection.isEmptyPath(path17)) {
7046
7046
  this.contents = value;
7047
7047
  } else if (this.contents == null) {
7048
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path16), value);
7048
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path17), value);
7049
7049
  } else if (assertCollection(this.contents)) {
7050
- this.contents.setIn(path16, value);
7050
+ this.contents.setIn(path17, value);
7051
7051
  }
7052
7052
  }
7053
7053
  /**
@@ -9008,9 +9008,9 @@ var require_cst_visit = __commonJS({
9008
9008
  visit2.BREAK = BREAK;
9009
9009
  visit2.SKIP = SKIP2;
9010
9010
  visit2.REMOVE = REMOVE;
9011
- visit2.itemAtPath = (cst, path16) => {
9011
+ visit2.itemAtPath = (cst, path17) => {
9012
9012
  let item = cst;
9013
- for (const [field, index2] of path16) {
9013
+ for (const [field, index2] of path17) {
9014
9014
  const tok = item?.[field];
9015
9015
  if (tok && "items" in tok) {
9016
9016
  item = tok.items[index2];
@@ -9019,23 +9019,23 @@ var require_cst_visit = __commonJS({
9019
9019
  }
9020
9020
  return item;
9021
9021
  };
9022
- visit2.parentCollection = (cst, path16) => {
9023
- const parent = visit2.itemAtPath(cst, path16.slice(0, -1));
9024
- const field = path16[path16.length - 1][0];
9022
+ visit2.parentCollection = (cst, path17) => {
9023
+ const parent = visit2.itemAtPath(cst, path17.slice(0, -1));
9024
+ const field = path17[path17.length - 1][0];
9025
9025
  const coll = parent?.[field];
9026
9026
  if (coll && "items" in coll)
9027
9027
  return coll;
9028
9028
  throw new Error("Parent collection not found");
9029
9029
  };
9030
- function _visit(path16, item, visitor) {
9031
- let ctrl = visitor(item, path16);
9030
+ function _visit(path17, item, visitor) {
9031
+ let ctrl = visitor(item, path17);
9032
9032
  if (typeof ctrl === "symbol")
9033
9033
  return ctrl;
9034
9034
  for (const field of ["key", "value"]) {
9035
9035
  const token = item[field];
9036
9036
  if (token && "items" in token) {
9037
9037
  for (let i = 0; i < token.items.length; ++i) {
9038
- const ci = _visit(Object.freeze(path16.concat([[field, i]])), token.items[i], visitor);
9038
+ const ci = _visit(Object.freeze(path17.concat([[field, i]])), token.items[i], visitor);
9039
9039
  if (typeof ci === "number")
9040
9040
  i = ci - 1;
9041
9041
  else if (ci === BREAK)
@@ -9046,10 +9046,10 @@ var require_cst_visit = __commonJS({
9046
9046
  }
9047
9047
  }
9048
9048
  if (typeof ctrl === "function" && field === "key")
9049
- ctrl = ctrl(item, path16);
9049
+ ctrl = ctrl(item, path17);
9050
9050
  }
9051
9051
  }
9052
- return typeof ctrl === "function" ? ctrl(item, path16) : ctrl;
9052
+ return typeof ctrl === "function" ? ctrl(item, path17) : ctrl;
9053
9053
  }
9054
9054
  exports.visit = visit2;
9055
9055
  }
@@ -16023,8 +16023,8 @@ var require_utils = __commonJS({
16023
16023
  }
16024
16024
  return ind;
16025
16025
  }
16026
- function removeDotSegments(path16) {
16027
- let input2 = path16;
16026
+ function removeDotSegments(path17) {
16027
+ let input2 = path17;
16028
16028
  const output3 = [];
16029
16029
  let nextSlash = -1;
16030
16030
  let len = 0;
@@ -16433,8 +16433,8 @@ var require_schemes = __commonJS({
16433
16433
  }
16434
16434
  if (wsComponent.resourceName) {
16435
16435
  const queryIndex = wsComponent.resourceName.indexOf("?");
16436
- const path16 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
16437
- wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
16436
+ const path17 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
16437
+ wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
16438
16438
  wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
16439
16439
  wsComponent.resourceName = void 0;
16440
16440
  }
@@ -20476,10 +20476,10 @@ function mergeDefs(...defs) {
20476
20476
  function cloneDef(schema) {
20477
20477
  return mergeDefs(schema._zod.def);
20478
20478
  }
20479
- function getElementAtPath(obj, path16) {
20480
- if (!path16)
20479
+ function getElementAtPath(obj, path17) {
20480
+ if (!path17)
20481
20481
  return obj;
20482
- return path16.reduce((acc, key) => acc?.[key], obj);
20482
+ return path17.reduce((acc, key) => acc?.[key], obj);
20483
20483
  }
20484
20484
  function promiseAllObject(promisesObj) {
20485
20485
  const keys = Object.keys(promisesObj);
@@ -20819,11 +20819,11 @@ function explicitlyAborted(x, startIndex = 0) {
20819
20819
  }
20820
20820
  return false;
20821
20821
  }
20822
- function prefixIssues(path16, issues) {
20822
+ function prefixIssues(path17, issues) {
20823
20823
  return issues.map((iss) => {
20824
20824
  var _a3;
20825
20825
  (_a3 = iss).path ?? (_a3.path = []);
20826
- iss.path.unshift(path16);
20826
+ iss.path.unshift(path17);
20827
20827
  return iss;
20828
20828
  });
20829
20829
  }
@@ -21273,16 +21273,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
21273
21273
  }
21274
21274
  function formatError(error62, mapper = (issue2) => issue2.message) {
21275
21275
  const fieldErrors = { _errors: [] };
21276
- const processError = (error63, path16 = []) => {
21276
+ const processError = (error63, path17 = []) => {
21277
21277
  for (const issue2 of error63.issues) {
21278
21278
  if (issue2.code === "invalid_union" && issue2.errors.length) {
21279
- issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
21279
+ issue2.errors.map((issues) => processError({ issues }, [...path17, ...issue2.path]));
21280
21280
  } else if (issue2.code === "invalid_key") {
21281
- processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
21281
+ processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
21282
21282
  } else if (issue2.code === "invalid_element") {
21283
- processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
21283
+ processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
21284
21284
  } else {
21285
- const fullpath = [...path16, ...issue2.path];
21285
+ const fullpath = [...path17, ...issue2.path];
21286
21286
  if (fullpath.length === 0) {
21287
21287
  fieldErrors._errors.push(mapper(issue2));
21288
21288
  } else {
@@ -21321,17 +21321,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
21321
21321
  }
21322
21322
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
21323
21323
  const result = { errors: [] };
21324
- const processError = (error63, path16 = []) => {
21324
+ const processError = (error63, path17 = []) => {
21325
21325
  var _a3;
21326
21326
  for (const issue2 of error63.issues) {
21327
21327
  if (issue2.code === "invalid_union" && issue2.errors.length) {
21328
- issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
21328
+ issue2.errors.map((issues) => processError({ issues }, [...path17, ...issue2.path]));
21329
21329
  } else if (issue2.code === "invalid_key") {
21330
- processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
21330
+ processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
21331
21331
  } else if (issue2.code === "invalid_element") {
21332
- processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
21332
+ processError({ issues: issue2.issues }, [...path17, ...issue2.path]);
21333
21333
  } else {
21334
- const fullpath = [...path16, ...issue2.path];
21334
+ const fullpath = [...path17, ...issue2.path];
21335
21335
  if (fullpath.length === 0) {
21336
21336
  result.errors.push(mapper(issue2));
21337
21337
  continue;
@@ -21370,8 +21370,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
21370
21370
  }
21371
21371
  function toDotPath(_path) {
21372
21372
  const segs = [];
21373
- const path16 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
21374
- for (const seg of path16) {
21373
+ const path17 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
21374
+ for (const seg of path17) {
21375
21375
  if (typeof seg === "number")
21376
21376
  segs.push(`[${seg}]`);
21377
21377
  else if (typeof seg === "symbol")
@@ -38473,13 +38473,13 @@ function resolveRef(ref, ctx) {
38473
38473
  if (!ref.startsWith("#")) {
38474
38474
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
38475
38475
  }
38476
- const path16 = ref.slice(1).split("/").filter(Boolean);
38477
- if (path16.length === 0) {
38476
+ const path17 = ref.slice(1).split("/").filter(Boolean);
38477
+ if (path17.length === 0) {
38478
38478
  return ctx.rootSchema;
38479
38479
  }
38480
38480
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
38481
- if (path16[0] === defsKey) {
38482
- const key = path16[1] === void 0 ? void 0 : decodeJSONPointerSegment(path16[1]);
38481
+ if (path17[0] === defsKey) {
38482
+ const key = path17[1] === void 0 ? void 0 : decodeJSONPointerSegment(path17[1]);
38483
38483
  if (!key || !ctx.defs[key]) {
38484
38484
  throw new Error(`Reference not found: ${ref}`);
38485
38485
  }
@@ -39329,47 +39329,14 @@ function date4(params) {
39329
39329
  }
39330
39330
 
39331
39331
  // ../../packages/schema/dist/harness.js
39332
- var HARNESS_IDS = [
39333
- "claude-code",
39334
- "claude-cowork",
39335
- "codex",
39336
- "opencode",
39337
- "cursor",
39338
- "openai-agents-sdk",
39339
- "linear",
39340
- "notion",
39341
- "custom-http",
39342
- "generic"
39343
- ];
39332
+ var HARNESS_IDS = ["claude-code", "codex", "opencode", "cursor", "generic"];
39344
39333
  var HarnessId = external_exports.enum(HARNESS_IDS);
39334
+ var isHarnessId = (id) => HARNESS_IDS.includes(id);
39345
39335
  var HARNESSES = {
39346
39336
  "claude-code": { id: "claude-code", label: "Claude Code", delivery: ["native-folder", "mcp"], evalAdapter: "native" },
39347
- "claude-cowork": {
39348
- id: "claude-cowork",
39349
- label: "Claude Cowork",
39350
- delivery: ["native-folder", "mcp"],
39351
- evalAdapter: "equivalent",
39352
- notes: "Evaluated through the Claude Agent SDK; results are labelled harness-equivalent."
39353
- },
39354
39337
  codex: { id: "codex", label: "Codex", delivery: ["native-folder", "mcp"], evalAdapter: "native" },
39355
39338
  opencode: { id: "opencode", label: "opencode", delivery: ["native-folder", "mcp"], evalAdapter: "native" },
39356
39339
  cursor: { id: "cursor", label: "Cursor", delivery: ["native-folder", "mcp"], evalAdapter: "none" },
39357
- "openai-agents-sdk": { id: "openai-agents-sdk", label: "OpenAI Agents SDK", delivery: ["native-folder"], evalAdapter: "none" },
39358
- linear: {
39359
- id: "linear",
39360
- label: "Linear agents",
39361
- delivery: ["export-bundle", "mcp"],
39362
- evalAdapter: "none",
39363
- notes: "Rendered as an instruction bundle for Linear agent guidance."
39364
- },
39365
- notion: {
39366
- id: "notion",
39367
- label: "Notion agents",
39368
- delivery: ["export-bundle", "mcp"],
39369
- evalAdapter: "none",
39370
- notes: "Rendered as an instruction page for Notion agent instructions."
39371
- },
39372
- "custom-http": { id: "custom-http", label: "Custom HTTP", delivery: ["http", "mcp"], evalAdapter: "native" },
39373
39340
  generic: { id: "generic", label: "Generic folder", delivery: ["native-folder"], evalAdapter: "none" }
39374
39341
  };
39375
39342
 
@@ -39388,8 +39355,8 @@ var AgentSpec = external_exports.object({
39388
39355
  tools: external_exports.array(external_exports.string()).default([]),
39389
39356
  subagents: external_exports.array(PackageName).default([]),
39390
39357
  parameters: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
39391
- /** Per-harness overrides merged into the rendered output. */
39392
- render: external_exports.partialRecord(HarnessId, external_exports.record(external_exports.string(), external_exports.unknown())).optional()
39358
+ /** Per-harness overrides merged into the rendered output, keyed by harness id; unknown harnesses are ignored. */
39359
+ render: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.unknown())).optional()
39393
39360
  });
39394
39361
  var McpServerConfig = external_exports.object({
39395
39362
  name: external_exports.string(),
@@ -39419,7 +39386,8 @@ var Manifest = external_exports.object({
39419
39386
  version: SemVer,
39420
39387
  description: external_exports.string().min(1).max(500),
39421
39388
  owners: external_exports.array(Principal).min(1),
39422
- targets: external_exports.array(HarnessId).min(1),
39389
+ /** Harnesses this package is for. Ids this build doesn't know (another harness, or one Yarkr dropped) are ignored. */
39390
+ targets: external_exports.preprocess((v) => Array.isArray(v) ? v.filter((t) => typeof t !== "string" || isHarnessId(t)) : v, external_exports.array(HarnessId).min(1, "List at least one supported harness in targets")),
39423
39391
  tags: external_exports.array(external_exports.string()).default([]),
39424
39392
  depends: external_exports.object({
39425
39393
  skills: DependencyMap.optional(),
@@ -39485,7 +39453,7 @@ var EvalSuite = external_exports.object({
39485
39453
  cases: external_exports.array(EvalCase).min(1),
39486
39454
  thresholds: Thresholds.default({ pass_rate: ">= baseline - 0.02", no_new_failures_on: ["critical"] })
39487
39455
  });
39488
- var EvalRunStatus = external_exports.enum(["queued", "running", "passed", "failed", "error", "cancelled"]);
39456
+ var EvalRunStatus = external_exports.enum(["passed", "failed", "error"]);
39489
39457
  var EvalMode = external_exports.enum(["smoke", "full"]);
39490
39458
 
39491
39459
  // ../../packages/schema/dist/api.js
@@ -39513,7 +39481,7 @@ var PackageQuery = external_exports.object({
39513
39481
  limit: external_exports.coerce.number().int().min(1).max(200).default(50),
39514
39482
  offset: external_exports.coerce.number().int().min(0).default(0)
39515
39483
  });
39516
- var StartEvalBody = external_exports.object({ mode: EvalMode.default("smoke") });
39484
+ var YankBody = external_exports.object({ reason: external_exports.string().min(1).max(500) });
39517
39485
  var ImportedTrial = external_exports.object({
39518
39486
  suite: external_exports.string(),
39519
39487
  case_id: external_exports.string(),
@@ -39546,14 +39514,16 @@ var EvalImportBody = external_exports.object({
39546
39514
  });
39547
39515
  var ConnectRepoBody = external_exports.object({
39548
39516
  connected: external_exports.boolean().default(true),
39549
- /** Defaults to the org slug. Created in the org when it doesn't exist yet. */
39550
- namespace: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*$/).optional(),
39551
39517
  /** Defaults: `public` for public repos, `restricted` for private ones. Public repos are always `public`. */
39552
39518
  classification: Classification.optional(),
39553
39519
  root_path: external_exports.string().max(200).optional()
39554
39520
  });
39555
39521
  var AuditQuery = external_exports.object({
39556
39522
  package: external_exports.string().optional(),
39523
+ /** `me`: only what you did yourself. Anyone may read their own events. */
39524
+ actor: external_exports.literal("me").optional(),
39525
+ /** An org slug: its members, arks, repos and packages. For that org's admins. */
39526
+ org: external_exports.string().optional(),
39557
39527
  action: external_exports.string().optional(),
39558
39528
  from: external_exports.string().datetime().optional(),
39559
39529
  to: external_exports.string().datetime().optional(),
@@ -39569,18 +39539,11 @@ var CreateUserBody = external_exports.object({
39569
39539
  });
39570
39540
  var OrgRole = external_exports.enum(["member", "admin"]);
39571
39541
  var Slug = external_exports.string().regex(/^[a-z0-9][a-z0-9-]*$/, "Use lowercase letters, digits and dashes");
39572
- var CreateOrgNamespaceBody = external_exports.object({
39573
- name: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*$/).max(64),
39574
- description: external_exports.string().max(500).default("")
39575
- });
39576
39542
  var CreateOrgBody = external_exports.object({
39577
- slug: Slug.refine((s) => s !== "global", '"global" is reserved for global arks'),
39543
+ slug: Slug,
39578
39544
  name: external_exports.string().min(1).max(120)
39579
39545
  });
39580
- var UpdateOrgBody = external_exports.object({
39581
- name: external_exports.string().min(1).max(120).optional(),
39582
- allow_personal_additions: external_exports.boolean().optional()
39583
- });
39546
+ var UpdateOrgBody = external_exports.object({ name: external_exports.string().min(1).max(120).optional() });
39584
39547
  var OrgMemberBody = external_exports.object({ email: external_exports.string().email(), role: OrgRole.default("member") });
39585
39548
  var ArkServerOverride = external_exports.object({
39586
39549
  env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
@@ -39591,13 +39554,11 @@ var ArkServerOverride = external_exports.object({
39591
39554
  var ArkItemConfig = external_exports.object({ servers: external_exports.record(external_exports.string(), ArkServerOverride).optional() });
39592
39555
  var CreateArkBody = external_exports.object({
39593
39556
  name: Slug,
39594
- description: external_exports.string().max(500).default(""),
39595
- idp_group: external_exports.string().nullable().optional()
39557
+ description: external_exports.string().max(500).default("")
39596
39558
  });
39597
39559
  var UpdateArkBody = external_exports.object({
39598
39560
  description: external_exports.string().max(500).optional(),
39599
- idp_group: external_exports.string().nullable().optional(),
39600
- /** Public arks are visible to everyone and can go into anyone's collection. They may only hold public packages. */
39561
+ /** Anyone can see and use a public ark, signed in or not. It may only hold public packages. */
39601
39562
  public: external_exports.boolean().optional()
39602
39563
  });
39603
39564
  var ArkItemBody = external_exports.object({
@@ -39612,30 +39573,14 @@ var EffectiveArkQuery = external_exports.object({
39612
39573
  /** Comma-separated ark refs to narrow to; omit for all. */
39613
39574
  arks: external_exports.string().optional()
39614
39575
  });
39615
- var CollectionVisibility = external_exports.enum(["private", "org", "public"]);
39616
- var COLLECTION_REF = /^@([a-z0-9][a-z0-9-]*)\/([a-z0-9][a-z0-9-]*)(?:@([1-9][0-9]*))?$/;
39617
- function parseCollectionRef(ref) {
39618
- const m = COLLECTION_REF.exec(ref);
39619
- return m ? { handle: m[1], name: m[2], version: m[3] ? Number(m[3]) : null } : null;
39620
- }
39621
- var CreateCollectionBody = external_exports.object({
39622
- name: Slug,
39623
- description: external_exports.string().max(500).default(""),
39624
- visibility: CollectionVisibility.default("private"),
39625
- /** Required for `org` visibility: the org whose members may use it. */
39626
- org: external_exports.string().optional()
39627
- });
39628
- var UpdateCollectionBody = external_exports.object({
39629
- description: external_exports.string().max(500).optional(),
39630
- visibility: CollectionVisibility.optional(),
39631
- org: external_exports.string().optional()
39632
- });
39633
- var CollectionArkBody = external_exports.object({ ark: external_exports.string().min(1) });
39634
- var PublishCollectionBody = external_exports.object({ note: external_exports.string().max(500).default("") });
39635
39576
  var PersonalItemBody = external_exports.object({ package: PackageName, range: external_exports.string().nullable().optional() });
39636
39577
  var ExclusionBody = external_exports.object({ ark: external_exports.string(), package: PackageName });
39637
39578
 
39638
39579
  // src/commands/auth.ts
39580
+ import { spawn } from "child_process";
39581
+ import { randomBytes } from "crypto";
39582
+ import { createServer } from "http";
39583
+ import os3 from "os";
39639
39584
  import { createInterface } from "readline/promises";
39640
39585
  import path3 from "path";
39641
39586
  import { Writable } from "stream";
@@ -39797,8 +39742,8 @@ var Api = class {
39797
39742
  this.conn = conn;
39798
39743
  }
39799
39744
  conn;
39800
- async request(method, path16, body, { allow404 = false } = {}) {
39801
- const url2 = `${this.conn.server}/api/v1${path16}`;
39745
+ async request(method, path17, body, { allow404 = false } = {}) {
39746
+ const url2 = `${this.conn.server}/api/v1${path17}`;
39802
39747
  let res;
39803
39748
  try {
39804
39749
  res = await fetch(url2, {
@@ -39820,7 +39765,7 @@ var Api = class {
39820
39765
  payload = await res.json();
39821
39766
  } catch {
39822
39767
  }
39823
- const msg = payload.message ?? `${method} ${path16} failed with HTTP ${res.status}.`;
39768
+ const msg = payload.message ?? `${method} ${path17} failed with HTTP ${res.status}.`;
39824
39769
  if (res.status === 401) {
39825
39770
  throw new CliError(
39826
39771
  msg,
@@ -39835,6 +39780,8 @@ ${JSON.stringify(payload.details, null, 2)}` : "";
39835
39780
  }
39836
39781
  info = () => this.request("GET", "/info");
39837
39782
  me = () => this.request("GET", "/me");
39783
+ /** Revokes the token this client is using (`yarkr logout`). */
39784
+ revokeCurrentToken = () => this.request("DELETE", "/tokens/current");
39838
39785
  search = (q) => {
39839
39786
  const qs = new URLSearchParams(Object.entries(q).filter((e) => !!e[1])).toString();
39840
39787
  return this.request("GET", `/packages${qs ? `?${qs}` : ""}`);
@@ -39845,23 +39792,22 @@ ${JSON.stringify(payload.details, null, 2)}` : "";
39845
39792
  const qs = new URLSearchParams({ channel: opts.channel, ...opts.range ? { range: opts.range } : {} });
39846
39793
  return this.request("GET", `/packages/${split(name)}/resolve?${qs}`);
39847
39794
  };
39848
- bundle = (name, version2) => this.request("GET", `/packages/${split(name)}/releases/${encodeURIComponent(version2)}/bundle`);
39795
+ /** A whole release. `sync` marks a fetch that installs it, which the hub counts as a download. */
39796
+ bundle = (name, version2, { sync: sync2 = false } = {}) => this.request("GET", `/packages/${split(name)}/releases/${encodeURIComponent(version2)}/bundle${sync2 ? "?use=sync" : ""}`);
39849
39797
  signingKey = () => this.request("GET", "/signing-key", void 0, { allow404: true });
39850
- /** The caller's effective set: global ∪ org arks ∪ personal ark. `only` narrows to some ark refs. */
39798
+ /** The caller's effective set: org arks ∪ personal ark. `only` narrows to some ark refs. */
39851
39799
  myArk = (only) => this.request("GET", `/me/ark${only?.length ? `?arks=${encodeURIComponent(only.join(","))}` : ""}`);
39852
- collections = () => this.request("GET", "/me/collections");
39853
- collection = (name) => this.request("GET", `/me/collections/${encodeURIComponent(name)}`);
39854
- createCollection = (body) => this.request("POST", "/me/collections", body);
39855
- updateCollection = (name, body) => this.request("PATCH", `/me/collections/${encodeURIComponent(name)}`, body);
39856
- deleteCollection = (name) => this.request("DELETE", `/me/collections/${encodeURIComponent(name)}`);
39857
- addCollectionArk = (name, ark) => this.request("POST", `/me/collections/${encodeURIComponent(name)}/arks`, { ark });
39858
- removeCollectionArk = (name, ark) => this.request("DELETE", `/me/collections/${encodeURIComponent(name)}/arks`, { ark });
39859
- publishCollection = (name, note = "") => this.request("POST", `/me/collections/${encodeURIComponent(name)}/versions`, { note });
39860
- /** A published collection version by ref: `@ana/pdf` (latest) or `@ana/pdf@3`. Works without a token for public ones. */
39861
- readCollection = (ref) => {
39862
- const [handle, rest] = [ref.slice(0, ref.indexOf("/")), ref.slice(ref.indexOf("/") + 1)];
39863
- return this.request("GET", `/collections/${encodeURIComponent(handle)}/${encodeURIComponent(rest)}`);
39800
+ /**
39801
+ * One ark by ref (`<org>/<ark>`): yours, or a public one, which works without a token. `sync` marks a fetch that
39802
+ * installs it, which the hub counts to rank public arks.
39803
+ */
39804
+ ark = (ref, { sync: sync2 = false } = {}) => {
39805
+ const [org, name, extra] = ref.split("/");
39806
+ if (!org || !name || extra !== void 0 || ref.startsWith("@")) throw new CliError(`"${ref}" isn't an ark.`, "Arks look like <org>/<ark>, e.g. acme/finance; @me is your personal ark.");
39807
+ return this.request("GET", `/arks/${encodeURIComponent(org)}/${encodeURIComponent(name)}${sync2 ? "?use=sync" : ""}`);
39864
39808
  };
39809
+ /** Yank a release (org admins), or undo it with `reason: null`. */
39810
+ yank = (name, version2, reason) => this.request(reason === null ? "DELETE" : "POST", `/packages/${split(name)}/releases/${encodeURIComponent(version2)}/yank`, reason === null ? void 0 : { reason });
39865
39811
  publicArks = () => this.request("GET", "/arks/public");
39866
39812
  addPersonal = (name, range) => this.request("POST", "/me/ark/items", { package: name, range: range ?? null });
39867
39813
  removePersonal = (name) => this.request("DELETE", `/me/ark/items/${split(name)}`);
@@ -39910,17 +39856,17 @@ function parseManifest(text) {
39910
39856
  }
39911
39857
  return manifest;
39912
39858
  }
39913
- function parseEvalSuite(text, path16 = "suite") {
39859
+ function parseEvalSuite(text, path17 = "suite") {
39914
39860
  let raw;
39915
39861
  try {
39916
39862
  raw = import_yaml.default.parse(text);
39917
39863
  } catch (err) {
39918
- throw new ManifestError(`${path16} is not valid YAML: ${err.message}`);
39864
+ throw new ManifestError(`${path17} is not valid YAML: ${err.message}`);
39919
39865
  }
39920
39866
  const res = EvalSuite.safeParse(raw);
39921
39867
  if (!res.success) {
39922
39868
  const issues = res.error.issues.map((i) => ({ path: i.path.join("."), message: i.message }));
39923
- throw new ManifestError(`${path16} is invalid: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`, issues);
39869
+ throw new ManifestError(`${path17} is invalid: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`, issues);
39924
39870
  }
39925
39871
  return res.data;
39926
39872
  }
@@ -39977,8 +39923,8 @@ function asText(content) {
39977
39923
  return null;
39978
39924
  return typeof content === "string" ? content : new TextDecoder().decode(content);
39979
39925
  }
39980
- function isYarkrOnly(path16) {
39981
- return path16 === MANIFEST_FILE || path16.startsWith("evals/") || path16.startsWith("fixtures/");
39926
+ function isYarkrOnly(path17) {
39927
+ return path17 === MANIFEST_FILE || path17.startsWith("evals/") || path17.startsWith("fixtures/");
39982
39928
  }
39983
39929
  function skillEntryPath(m) {
39984
39930
  return m.entry ?? "SKILL.md";
@@ -40006,10 +39952,10 @@ ${body.replace(/^\n+/, "")}`);
40006
39952
  function skillFolderFiles(pkg, dir, folderName) {
40007
39953
  const entry = skillEntryPath(pkg.manifest);
40008
39954
  const files = [{ package: pkg.manifest.name, path: join(dir, folderName, "SKILL.md"), content: renderSkillMd(pkg, folderName) }];
40009
- for (const [path16, content] of Object.entries(pkg.files)) {
40010
- if (path16 === entry || path16 === "SKILL.md" || isYarkrOnly(path16))
39955
+ for (const [path17, content] of Object.entries(pkg.files)) {
39956
+ if (path17 === entry || path17 === "SKILL.md" || isYarkrOnly(path17))
40011
39957
  continue;
40012
- files.push({ package: pkg.manifest.name, path: join(dir, folderName, path16), content });
39958
+ files.push({ package: pkg.manifest.name, path: join(dir, folderName, path17), content });
40013
39959
  }
40014
39960
  return files;
40015
39961
  }
@@ -40078,20 +40024,6 @@ function standardMcpEntry(pkg, s, style, notes, withType) {
40078
40024
  function clean(o) {
40079
40025
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
40080
40026
  }
40081
- function inlineMarkdown(pkg, skip) {
40082
- const parts = [];
40083
- for (const [path16, content] of Object.entries(pkg.files).sort(([a], [b]) => a < b ? -1 : 1)) {
40084
- if (skip.has(path16) || isYarkrOnly(path16) || !/\.(md|markdown|txt)$/i.test(path16))
40085
- continue;
40086
- const text = asText(content);
40087
- if (text === null)
40088
- continue;
40089
- parts.push(`### Reference: ${path16}
40090
-
40091
- ${splitFrontmatter(text).body.trim()}`);
40092
- }
40093
- return parts.join("\n\n");
40094
- }
40095
40027
 
40096
40028
  // ../../packages/core/dist/harnesses/types.js
40097
40029
  function defineHarness(def) {
@@ -40117,13 +40049,13 @@ function nativeFolderHarness(o) {
40117
40049
  ...o.mcp ? {
40118
40050
  toolsets(servers, ctx) {
40119
40051
  const mcp = o.mcp;
40120
- const path16 = ctx.layout[slot];
40121
- if (!path16) {
40052
+ const path17 = ctx.layout[slot];
40053
+ if (!path17) {
40122
40054
  if (mcp.unavailable)
40123
40055
  ctx.out.notes.push(mcp.unavailable(servers));
40124
40056
  return;
40125
40057
  }
40126
- ctx.out.mcp.push({ path: path16, key: mcp.key, servers: Object.fromEntries(servers.map(({ pkg, server }) => [server.name, mcp.entry(pkg, server, ctx.out.notes)])) });
40058
+ ctx.out.mcp.push({ path: path17, key: mcp.key, servers: Object.fromEntries(servers.map(({ pkg, server }) => [server.name, mcp.entry(pkg, server, ctx.out.notes)])) });
40127
40059
  }
40128
40060
  } : {}
40129
40061
  });
@@ -40165,68 +40097,6 @@ function claudeMcpJson(mcpUrl, tokenEnv) {
40165
40097
  return JSON.stringify({ mcpServers: { yarkr: { type: "http", url: mcpUrl, headers: { Authorization: `Bearer \${${tokenEnv}}` } } } }, null, 2);
40166
40098
  }
40167
40099
 
40168
- // ../../packages/core/dist/harnesses/claude-cowork.js
40169
- var claudeCowork = defineHarness({
40170
- id: "claude-cowork",
40171
- layout: {
40172
- project: { plugin: "yarkr/exports/claude-cowork/yarkr", name: "yarkr" },
40173
- user: { plugin: ".yarkr/exports/claude-cowork/yarkr", name: "yarkr" }
40174
- },
40175
- skill(pkg, ctx) {
40176
- ctx.out.files.push(...skillFolderFiles(pkg, join(ctx.layout.plugin, "skills"), ctx.names.get(pkg.manifest.name)));
40177
- },
40178
- agent(pkg, ctx) {
40179
- const parts = agentParts(pkg, ctx.harness, ctx.names, ctx.out);
40180
- const fm = { ...claudeAgentFrontmatter(pkg, parts, ctx), ...parts.overrides };
40181
- ctx.out.files.push({ package: pkg.manifest.name, path: join(ctx.layout.plugin, "agents", `${ctx.names.get(pkg.manifest.name)}.md`), content: agentMarkdown(pkg, fm, parts) });
40182
- },
40183
- toolsets(servers, ctx) {
40184
- ctx.out.files.push({
40185
- path: join(ctx.layout.plugin, ".mcp.json"),
40186
- content: JSON.stringify({ mcpServers: Object.fromEntries(servers.map(({ pkg, server }) => [server.name, standardMcpEntry(pkg, server, "dollar-brace", ctx.out.notes, true)])) }, null, 2) + "\n"
40187
- });
40188
- },
40189
- finalize({ out, layout: l, input: input2 }) {
40190
- if (!out.files.length)
40191
- return;
40192
- const root = l.plugin;
40193
- out.files.push({
40194
- path: join(root, ".claude-plugin", "plugin.json"),
40195
- content: JSON.stringify({
40196
- name: l.name,
40197
- version: pluginVersion(input2),
40198
- description: "Skills and agents from your Yarkr registry (managed by yarkr).",
40199
- author: { name: "Yarkr" }
40200
- }, null, 2) + "\n"
40201
- });
40202
- out.files.push({
40203
- path: join(root, "README.md"),
40204
- content: withFrontmatter({ "managed-by": "yarkr" }, `# Yarkr plugin for Claude Cowork
40205
-
40206
- This folder is a Cowork plugin generated by \`yarkr sync\`.
40207
-
40208
- 1. Zip this folder (\`${l.name}\`).
40209
- 2. In Claude Cowork, open Customize \u2192 Plugins and upload the zip, or publish it to your organisation's plugin marketplace.
40210
- 3. Re-run \`yarkr sync\` and re-upload after updates.
40211
- `)
40212
- });
40213
- out.notes.push(`Cowork plugin written to ${root}/. Zip and upload it in Cowork (Customize \u2192 Plugins).`);
40214
- },
40215
- mcpSnippet: (mcpUrl, tokenEnv, notes) => ({
40216
- file: "Cowork: Customize \u2192 Connectors \u2192 Add custom connector",
40217
- format: "json",
40218
- content: claudeMcpJson(mcpUrl, tokenEnv),
40219
- notes: [...notes, "Cowork custom connectors take the URL and an Authorization header; paste the token value there."]
40220
- })
40221
- });
40222
- function pluginVersion(input2) {
40223
- const key = input2.packages.map((p) => `${p.manifest.name}@${p.manifest.version}`).sort().join(",");
40224
- let h = 0;
40225
- for (let i = 0; i < key.length; i++)
40226
- h = h * 31 + key.charCodeAt(i) >>> 0;
40227
- return `0.0.${h % 1e5}`;
40228
- }
40229
-
40230
40100
  // ../../packages/core/dist/harnesses/codex.js
40231
40101
  var index = /* @__PURE__ */ new WeakMap();
40232
40102
  var indexOf = (ctx) => index.get(ctx) ?? index.set(ctx, []).get(ctx);
@@ -40370,10 +40240,10 @@ var cursor = defineHarness({
40370
40240
  const entry = skillEntryPath(pkg.manifest);
40371
40241
  const { data, body } = splitFrontmatter(asText(pkg.files[entry]) ?? "");
40372
40242
  const extra = [];
40373
- for (const [path16, content] of Object.entries(pkg.files)) {
40374
- if (path16 === entry || isYarkrOnly(path16))
40243
+ for (const [path17, content] of Object.entries(pkg.files)) {
40244
+ if (path17 === entry || isYarkrOnly(path17))
40375
40245
  continue;
40376
- const p = join(l.assets, name, path16);
40246
+ const p = join(l.assets, name, path17);
40377
40247
  ctx.out.files.push({ package: pkg.manifest.name, path: p, content });
40378
40248
  extra.push(`- \`${p}\``);
40379
40249
  }
@@ -40444,9 +40314,9 @@ function folderExportHarness(id, rootDefault, opts = {}) {
40444
40314
  for (const pkg of byKind(input2, "agent")) {
40445
40315
  const name = names.get(pkg.manifest.name);
40446
40316
  const parts = agentParts(pkg, id, names, out);
40447
- for (const [path16, content] of Object.entries(pkg.files))
40448
- if (!isYarkrOnly(path16))
40449
- out.files.push({ package: pkg.manifest.name, path: join(root, "agents", name, path16), content });
40317
+ for (const [path17, content] of Object.entries(pkg.files))
40318
+ if (!isYarkrOnly(path17))
40319
+ out.files.push({ package: pkg.manifest.name, path: join(root, "agents", name, path17), content });
40450
40320
  index2.push({ kind: "agent", name, package: pkg.manifest.name, version: pkg.manifest.version, description: pkg.manifest.description, model: parts.model, tools: parts.tools, skills: parts.skills, subagents: parts.subagents, system_prompt: join("agents", name, pkg.manifest.agent?.system_prompt ?? "") });
40451
40321
  }
40452
40322
  for (const pkg of byKind(input2, "toolset")) {
@@ -40466,170 +40336,6 @@ function folderExportHarness(id, rootDefault, opts = {}) {
40466
40336
  }
40467
40337
  var generic = folderExportHarness("generic", "yarkr/generic", { acceptsAllTargets: true });
40468
40338
 
40469
- // ../../packages/core/dist/harnesses/custom-http.js
40470
- var customHttp = folderExportHarness("custom-http", "yarkr/custom-http");
40471
-
40472
- // ../../packages/core/dist/harnesses/workspace.js
40473
- function workspaceHarness(t) {
40474
- return defineHarness({
40475
- id: t.id,
40476
- layout: {
40477
- project: { root: `yarkr/exports/${t.id}` },
40478
- user: { root: `.yarkr/exports/${t.id}` }
40479
- },
40480
- render({ input: input2, layout, names, out }) {
40481
- const root = layout.root;
40482
- const byName = new Map(input2.packages.map((p) => [p.manifest.name, p]));
40483
- const rows = [];
40484
- const header = (p) => `<!-- managed by yarkr: ${p.manifest.name}@${p.manifest.version}. Regenerate with \`yarkr sync\`; change it with a pull request in its repo. -->`;
40485
- for (const pkg of byKind(input2, "skill")) {
40486
- const name = names.get(pkg.manifest.name);
40487
- const entry = skillEntryPath(pkg.manifest);
40488
- const body = splitFrontmatter(asText(pkg.files[entry]) ?? "").body.trim();
40489
- const refs = inlineMarkdown(pkg, /* @__PURE__ */ new Set([entry]));
40490
- out.files.push({
40491
- package: pkg.manifest.name,
40492
- path: join(root, `${name}.md`),
40493
- content: `${header(pkg)}
40494
-
40495
- # ${name}
40496
-
40497
- Use when: ${pkg.manifest.description}
40498
-
40499
- ${body}${refs ? `
40500
-
40501
- ${refs}` : ""}
40502
- `
40503
- });
40504
- rows.push(`| \`${name}.md\` | skill | ${pkg.manifest.name}@${pkg.manifest.version} | ${pkg.manifest.description} |`);
40505
- }
40506
- for (const pkg of byKind(input2, "agent")) {
40507
- const name = names.get(pkg.manifest.name);
40508
- const parts = agentParts(pkg, t.id, names, out);
40509
- const skillBlocks = Object.keys(pkg.manifest.depends?.skills ?? {}).map((d) => byName.get(d)).filter((p) => !!p).map((p) => `## Skill: ${names.get(p.manifest.name)}
40510
-
40511
- ${splitFrontmatter(asText(p.files[skillEntryPath(p.manifest)]) ?? "").body.trim()}`);
40512
- out.files.push({
40513
- package: pkg.manifest.name,
40514
- path: join(root, `agent-${name}.md`),
40515
- content: `${header(pkg)}
40516
-
40517
- # ${name}
40518
-
40519
- ${pkg.manifest.description}
40520
-
40521
- ${parts.prompt}${skillBlocks.length ? `
40522
-
40523
- ${skillBlocks.join("\n\n")}` : ""}
40524
- `
40525
- });
40526
- rows.push(`| \`agent-${name}.md\` | agent | ${pkg.manifest.name}@${pkg.manifest.version} | ${pkg.manifest.description} |`);
40527
- }
40528
- const servers = toolsetServers(input2);
40529
- if (servers.length) {
40530
- out.notes.push(`${t.product} can't load MCP config from files. Connect these servers in ${t.product}'s settings: ${servers.map((s) => `${s.server.name} (${s.server.url ?? s.server.command})`).join(", ")}.`);
40531
- }
40532
- if (rows.length) {
40533
- out.files.push({
40534
- path: join(root, "README.md"),
40535
- content: `<!-- managed by yarkr -->
40536
-
40537
- # Yarkr instructions for ${t.product}
40538
-
40539
- ${t.product} doesn't read instruction files from disk, so \`yarkr sync\` writes them here for you to paste.
40540
-
40541
- **Where to paste:** ${t.where}
40542
-
40543
- ${t.limits}
40544
-
40545
- | File | Kind | Release | Use when |
40546
- | --- | --- | --- | --- |
40547
- ${rows.join("\n")}
40548
-
40549
- After \`yarkr update\`, re-paste any file whose release changed.
40550
- `
40551
- });
40552
- out.notes.push(`${t.product} bundle written to ${root}/. Paste each file as described in ${root}/README.md.`);
40553
- }
40554
- },
40555
- mcpSnippet: (mcpUrl) => ({
40556
- file: t.mcpWhere,
40557
- format: "text",
40558
- content: `URL: ${mcpUrl}
40559
- Header: Authorization: Bearer <your Yarkr API token>`,
40560
- notes: [`${t.product} stores the token in its own connection settings. Use a token from a service account with read access only.`]
40561
- })
40562
- });
40563
- }
40564
-
40565
- // ../../packages/core/dist/harnesses/linear.js
40566
- var linear = workspaceHarness({
40567
- id: "linear",
40568
- product: "Linear",
40569
- where: "Workspace guidance: Settings \u2192 Agents \u2192 Additional guidance. Team guidance: Team settings \u2192 Agents \u2192 Additional guidance (team guidance takes priority). Paste the contents of the matching file.",
40570
- limits: "Linear passes this guidance to every agent working on issues in the workspace or team, so keep each file focused.",
40571
- mcpWhere: "Linear agents that support MCP: add a custom MCP server"
40572
- });
40573
-
40574
- // ../../packages/core/dist/harnesses/notion.js
40575
- var notion = workspaceHarness({
40576
- id: "notion",
40577
- product: "Notion",
40578
- where: "Create or open a Custom Agent \u2192 Instructions, and paste the contents of the matching file. For Notion AI personal instructions, use Settings \u2192 Notion AI \u2192 Instructions. MCP toolsets are added under the agent's Connections.",
40579
- limits: "Notion instructions are plain text; reference files are inlined below the main instructions.",
40580
- mcpWhere: "Notion Custom Agent \u2192 Connections \u2192 Add MCP server"
40581
- });
40582
-
40583
- // ../../packages/core/dist/harnesses/openai-agents-sdk.js
40584
- var openaiAgentsSdk = defineHarness({
40585
- id: "openai-agents-sdk",
40586
- layout: {
40587
- project: { root: "yarkr/agents-sdk" },
40588
- user: { root: ".yarkr/agents-sdk" }
40589
- },
40590
- render({ input: input2, layout, names, out }) {
40591
- const root = layout.root;
40592
- for (const pkg of byKind(input2, "skill"))
40593
- out.files.push(...skillFolderFiles(pkg, join(root, "skills"), names.get(pkg.manifest.name)));
40594
- const servers = toolsetServers(input2).map(({ pkg, server: s }) => {
40595
- const conv = (what) => (k, v) => convertSecretRefs(v, "keep", { pkg: pkg.manifest.name, server: s.name, key: `${what} ${k}` }, out.notes);
40596
- return {
40597
- toolset: pkg.manifest.name,
40598
- def: clean({ name: s.name, transport: s.transport === "http" ? "streamable_http" : "stdio", command: s.command, args: s.args, url: s.url, env: mapValues(s.env, conv("env")), headers: mapValues(s.headers, conv("header")) })
40599
- };
40600
- });
40601
- if (servers.length)
40602
- out.files.push({ path: join(root, "mcp.json"), content: JSON.stringify({ managed_by: "yarkr", servers: servers.map((s) => s.def) }, null, 2) + "\n" });
40603
- for (const pkg of byKind(input2, "agent")) {
40604
- const name = names.get(pkg.manifest.name);
40605
- const parts = agentParts(pkg, "openai-agents-sdk", names, out);
40606
- const toolsetDeps = new Set(Object.keys(pkg.manifest.depends?.toolsets ?? {}));
40607
- const def = clean({
40608
- managed_by: "yarkr",
40609
- package: pkg.manifest.name,
40610
- version: pkg.manifest.version,
40611
- name,
40612
- handoff_description: pkg.manifest.description,
40613
- instructions: parts.prompt,
40614
- model: parts.model,
40615
- model_settings: Object.keys(parts.parameters).length ? parts.parameters : void 0,
40616
- tools: parts.tools,
40617
- handoffs: parts.subagents,
40618
- skills: parts.skills.map((s) => join("skills", s)),
40619
- mcp_servers: servers.filter((s) => toolsetDeps.has(s.toolset)).map((s) => s.def.name),
40620
- ...parts.overrides
40621
- });
40622
- out.files.push({ package: pkg.manifest.name, path: join(root, "agents", `${name}.json`), content: JSON.stringify(def, null, 2) + "\n" });
40623
- }
40624
- },
40625
- mcpSnippet: (mcpUrl, tokenEnv, notes) => ({
40626
- file: "your agent code",
40627
- format: "text",
40628
- content: `MCPServerStreamableHttp(params={"url": "${mcpUrl}", "headers": {"Authorization": f"Bearer {os.environ['${tokenEnv}']}"}})`,
40629
- notes
40630
- })
40631
- });
40632
-
40633
40339
  // ../../packages/core/dist/harnesses/opencode.js
40634
40340
  function opencodeModel(model) {
40635
40341
  if (!model || model.includes("/"))
@@ -40673,14 +40379,9 @@ var opencode = nativeFolderHarness({
40673
40379
  // ../../packages/core/dist/harnesses/index.js
40674
40380
  var HARNESS_REGISTRY = {
40675
40381
  "claude-code": claudeCode,
40676
- "claude-cowork": claudeCowork,
40677
40382
  codex,
40678
40383
  opencode,
40679
40384
  cursor,
40680
- "openai-agents-sdk": openaiAgentsSdk,
40681
- linear,
40682
- notion,
40683
- "custom-http": customHttp,
40684
40385
  generic
40685
40386
  };
40686
40387
  function detectHarnesses(exists) {
@@ -40753,24 +40454,18 @@ function yarkrMcpSnippet(harness, mcpUrl, tokenEnv = "YARKR_TOKEN") {
40753
40454
  notes
40754
40455
  };
40755
40456
  }
40756
- var SCOPE = /^(all|public|@me|@[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*(@[1-9][0-9]*)?|global\/[a-z0-9][a-z0-9-]*|[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)?)$/;
40457
+ var SCOPE = /^(all|public|[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)?)$/;
40757
40458
  function isConnectionScope(scope) {
40758
40459
  return SCOPE.test(scope);
40759
40460
  }
40760
40461
  function yarkrConnection(server, scope, tokenEnv = "YARKR_TOKEN") {
40761
40462
  if (!isConnectionScope(scope))
40762
- throw new Error(`"${scope}" isn't an MCP scope. Use all, public, @me, @<handle>/<collection>, <org>, <org>/<ark> or global/<ark>.`);
40463
+ throw new Error(`"${scope}" isn't an MCP scope. Use all, public, <org> or <org>/<ark>.`);
40763
40464
  const base = server.replace(/\/+$/, "");
40764
40465
  if (scope === "all")
40765
40466
  return { scope, name: "yarkr", url: `${base}/mcp`, tokenEnv };
40766
40467
  if (scope === "public")
40767
40468
  return { scope, name: "yarkr-public", url: `${base}/mcp/public`, tokenEnv: null };
40768
- if (scope === "@me")
40769
- return { scope, name: "yarkr-me", url: `${base}/mcp/a/@me`, tokenEnv };
40770
- if (scope.startsWith("@"))
40771
- return { scope, name: `yarkr-${scope.slice(1).replace(/@\d+$/, "").replace("/", "-")}`, url: `${base}/mcp/${scope}`, tokenEnv };
40772
- if (scope.startsWith("global/"))
40773
- return { scope, name: `yarkr-${scope.replace("/", "-")}`, url: `${base}/mcp/a/${scope}`, tokenEnv };
40774
40469
  return { scope, name: `yarkr-${scope.replace("/", "-")}`, url: `${base}/mcp/o/${scope}`, tokenEnv };
40775
40470
  }
40776
40471
  function connectionPackage(c) {
@@ -40832,7 +40527,48 @@ import { readFileSync } from "fs";
40832
40527
  import { chmod, mkdir, readFile, writeFile } from "fs/promises";
40833
40528
  import os from "os";
40834
40529
  import path from "path";
40530
+
40531
+ // src/keychain.ts
40532
+ import { execFileSync, spawnSync } from "child_process";
40533
+ var SERVICE = "yarkr";
40534
+ var detected;
40535
+ function store() {
40536
+ if (process.env.YARKR_CREDENTIAL_STORE === "file") return null;
40537
+ if (detected !== void 0) return detected;
40538
+ const has = (cmd) => spawnSync("which", [cmd], { stdio: "ignore" }).status === 0;
40539
+ detected = process.platform === "darwin" && has("security") ? "macos" : process.platform === "linux" && has("secret-tool") ? "secret-service" : null;
40540
+ return detected;
40541
+ }
40542
+ function keychainName() {
40543
+ const s = store();
40544
+ return s === "macos" ? "the macOS Keychain" : s === "secret-service" ? "your keyring (Secret Service)" : null;
40545
+ }
40546
+ function keychainSet(account, token) {
40547
+ const s = store();
40548
+ if (!s || /["\n]/.test(account + token)) return false;
40549
+ const r = s === "macos" ? spawnSync("security", ["-i"], { input: `add-generic-password -U -s ${SERVICE} -a "${account}" -l "Yarkr (${account})" -w "${token}"
40550
+ `, stdio: ["pipe", "ignore", "ignore"] }) : spawnSync("secret-tool", ["store", `--label=Yarkr (${account})`, "service", SERVICE, "registry", account], { input: token, stdio: ["pipe", "ignore", "ignore"] });
40551
+ return r.status === 0 && keychainGet(account) === token;
40552
+ }
40553
+ function keychainGet(account) {
40554
+ const s = store();
40555
+ if (!s) return null;
40556
+ try {
40557
+ const out = s === "macos" ? execFileSync("security", ["find-generic-password", "-s", SERVICE, "-a", account, "-w"], { stdio: ["ignore", "pipe", "ignore"] }) : execFileSync("secret-tool", ["lookup", "service", SERVICE, "registry", account], { stdio: ["ignore", "pipe", "ignore"] });
40558
+ return out.toString("utf8").trim() || null;
40559
+ } catch {
40560
+ return null;
40561
+ }
40562
+ }
40563
+ function keychainDelete(account) {
40564
+ const s = store();
40565
+ if (s === "macos") spawnSync("security", ["delete-generic-password", "-s", SERVICE, "-a", account], { stdio: "ignore" });
40566
+ else if (s === "secret-service") spawnSync("secret-tool", ["clear", "service", SERVICE, "registry", account], { stdio: "ignore" });
40567
+ }
40568
+
40569
+ // src/rc.ts
40835
40570
  var RC_FILE = ".yarkrrc";
40571
+ var KEYCHAIN_TOKEN = "@keychain";
40836
40572
  var userRcPath = () => process.env.YARKR_USERCONFIG ?? path.join(os.homedir(), RC_FILE);
40837
40573
  function parseRc(text, file2) {
40838
40574
  const rc = { path: file2, tokens: /* @__PURE__ */ new Map() };
@@ -40869,6 +40605,11 @@ function tokenFor(rc, server) {
40869
40605
  for (const k of rc.tokens.keys()) if (key.startsWith(k) && (!best || k.length > best.length)) best = k;
40870
40606
  if (!best) return void 0;
40871
40607
  const raw = rc.tokens.get(best);
40608
+ if (raw === KEYCHAIN_TOKEN) {
40609
+ const token = keychainGet(best);
40610
+ if (!token) throw new CliError(`${rc.path} says the token for ${best} is in your keychain, but it isn't there.`, "Run `yarkr login` again.");
40611
+ return token;
40612
+ }
40872
40613
  const value = raw.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(\?)?\}/g, (_, name, optional2) => {
40873
40614
  const v = process.env[name];
40874
40615
  if (v === void 0 && !optional2) {
@@ -40880,7 +40621,7 @@ function tokenFor(rc, server) {
40880
40621
  }
40881
40622
  function warnOnLiteralTokens(rc) {
40882
40623
  for (const [k, v] of rc.tokens) {
40883
- if (!v.includes("${")) ui.warn(`${rc.path} has a literal token for ${k}. Use \${ENV_VAR} instead if you commit this file.`);
40624
+ if (!v.includes("${") && v !== KEYCHAIN_TOKEN) ui.warn(`${rc.path} has a literal token for ${k}. Use \${ENV_VAR} instead if you commit this file.`);
40884
40625
  }
40885
40626
  }
40886
40627
  var authTokenKey = (server) => `${registryKey(server)}:_authToken`;
@@ -40911,7 +40652,7 @@ async function setRcValue(file2, key, value, { secret = false } = {}) {
40911
40652
  var CONFIG_FILE = "yarkr.json";
40912
40653
  var LOCK_FILE = "yarkr.lock";
40913
40654
  var normalizeServer = (url2) => url2.replace(/\/+$/, "");
40914
- var DEFAULT_REGISTRY = "https://yarkr.com";
40655
+ var DEFAULT_REGISTRY = "https://api.yarkr.com/functions/v1/api";
40915
40656
  var connectionFor = (server, m) => yarkrConnection(server, m.scope, m.public ? null : m.token_env);
40916
40657
  var userStateDir = () => process.env.YARKR_STATE_DIR ?? path2.join(process.env.XDG_CONFIG_HOME ?? path2.join(os2.homedir(), ".config"), "yarkr");
40917
40658
  async function loadProjectConfig(dir, { required: required2 = true } = {}) {
@@ -40933,10 +40674,7 @@ async function loadProjectConfig(dir, { required: required2 = true } = {}) {
40933
40674
  const unknown2 = harnesses.filter((h) => !HARNESS_IDS.includes(h));
40934
40675
  if (unknown2.length) throw new CliError(`${CONFIG_FILE} lists unknown harnesses: ${unknown2.join(", ")}.`, `Known harnesses: ${HARNESS_IDS.join(", ")}.`);
40935
40676
  if (raw.arks !== void 0 && raw.arks !== "auto" && !(Array.isArray(raw.arks) && raw.arks.every((a) => typeof a === "string"))) {
40936
- throw new CliError(`${CONFIG_FILE}: "arks" must be "auto" or a list of ark refs.`, `For example "arks": ["acme/finance", "global/essentials"].`);
40937
- }
40938
- if (raw.collections !== void 0 && !(Array.isArray(raw.collections) && raw.collections.every((c) => typeof c === "string" && parseCollectionRef(c)))) {
40939
- throw new CliError(`${CONFIG_FILE}: "collections" must be a list of collection refs.`, `For example "collections": ["@ana/pdf", "@acme-bot/review@3"].`);
40677
+ throw new CliError(`${CONFIG_FILE}: "arks" must be "auto" or a list of ark refs.`, `For example "arks": ["acme/finance", "@me"].`);
40940
40678
  }
40941
40679
  const mcp = (raw.mcp ?? []).map((m) => typeof m === "string" ? { scope: m } : m);
40942
40680
  for (const m of mcp) {
@@ -40952,7 +40690,6 @@ async function loadProjectConfig(dir, { required: required2 = true } = {}) {
40952
40690
  layout: raw.layout,
40953
40691
  ...raw.arks !== void 0 ? { arks: raw.arks } : {},
40954
40692
  ...raw.exclude?.length ? { exclude: raw.exclude } : {},
40955
- ...raw.collections?.length ? { collections: raw.collections } : {},
40956
40693
  ...mcp.length ? { mcp } : {}
40957
40694
  };
40958
40695
  }
@@ -40965,7 +40702,6 @@ async function saveProjectConfig(dir, cfg) {
40965
40702
  };
40966
40703
  if (cfg.arks !== void 0) out.arks = cfg.arks;
40967
40704
  if (cfg.exclude?.length) out.exclude = [...cfg.exclude].sort();
40968
- if (cfg.collections?.length) out.collections = [...cfg.collections].sort();
40969
40705
  if (cfg.mcp?.length) out.mcp = cfg.mcp.map((m) => m.token_env || m.public ? m : m.scope);
40970
40706
  if (cfg.layout && Object.keys(cfg.layout).length) out.layout = cfg.layout;
40971
40707
  await writeFile2(path2.join(dir, CONFIG_FILE), JSON.stringify(out, null, 2) + "\n");
@@ -41020,12 +40756,13 @@ async function resolveConnection(opts) {
41020
40756
  }
41021
40757
 
41022
40758
  // src/commands/auth.ts
40759
+ async function readStdin() {
40760
+ const chunks = [];
40761
+ for await (const c of process.stdin) chunks.push(c);
40762
+ return Buffer.concat(chunks).toString("utf8").trim();
40763
+ }
41023
40764
  async function promptHidden(question) {
41024
- if (!process.stdin.isTTY) {
41025
- const chunks = [];
41026
- for await (const c of process.stdin) chunks.push(c);
41027
- return Buffer.concat(chunks).toString("utf8").trim();
41028
- }
40765
+ if (!process.stdin.isTTY) return readStdin();
41029
40766
  let muted = false;
41030
40767
  const output3 = new Writable({
41031
40768
  write(chunk, _enc, cb) {
@@ -41041,30 +40778,99 @@ async function promptHidden(question) {
41041
40778
  process.stdout.write("\n");
41042
40779
  return answer.trim();
41043
40780
  }
40781
+ function registryFor(flag, userRc) {
40782
+ return normalizeServer(flag ?? process.env.YARKR_REGISTRY ?? loadRc(path3.join(process.cwd(), RC_FILE))?.registry ?? userRc?.registry ?? DEFAULT_REGISTRY);
40783
+ }
41044
40784
  async function login(opts) {
41045
40785
  const userRc = loadRc(userRcPath());
41046
- const registry2 = normalizeServer(opts.registry ?? process.env.YARKR_REGISTRY ?? loadRc(path3.join(process.cwd(), RC_FILE))?.registry ?? userRc?.registry ?? DEFAULT_REGISTRY);
40786
+ const registry2 = registryFor(opts.registry, userRc);
41047
40787
  let token = opts.token ?? process.env.YARKR_TOKEN;
41048
- if (!token) {
41049
- if (process.stdin.isTTY) ui.info(`Create an API token at ${ui.code(registry2)} under Settings \u2192 API tokens.`);
41050
- token = await promptHidden("Paste the token: ");
41051
- }
40788
+ if (!token && !process.stdin.isTTY) token = await readStdin();
40789
+ if (!token) token = await browserLogin(registry2, opts.browser !== false && !headless());
41052
40790
  if (!token) throw new CliError("No token entered.");
41053
40791
  const me = await new Api({ server: registry2, token }).me();
41054
- await setRcValue(userRcPath(), authTokenKey(registry2), token, { secret: true });
40792
+ const store2 = keychainSet(registryKey(registry2), token) ? keychainName() : null;
40793
+ await setRcValue(userRcPath(), authTokenKey(registry2), store2 ? KEYCHAIN_TOKEN : token, { secret: true });
41055
40794
  if (!userRc?.registry) await setRcValue(userRcPath(), "registry", registry2, { secret: true });
41056
40795
  ui.ok(`Signed in to ${registry2} as ${ui.bold(me.email)}.`);
41057
- ui.step(`Token saved to ${userRcPath()} (readable only by you).`);
40796
+ ui.step(store2 ? `Token saved to ${store2}.` : `Token saved to ${userRcPath()} (readable only by you).`);
40797
+ }
40798
+ var LOGIN_TIMEOUT_MS = 5 * 6e4;
40799
+ var headless = () => !!process.env.SSH_CONNECTION || process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
40800
+ async function browserLogin(registry2, open2, openUrl = openBrowser) {
40801
+ const info2 = await new Api({ server: registry2, token: null }).info();
40802
+ const web = (info2.web_url ?? registry2).replace(/\/+$/, "");
40803
+ const host = os3.hostname().replace(/\.local$/, "");
40804
+ if (!open2) {
40805
+ ui.info("Open this link on any device, sign in and approve:");
40806
+ console.log(`
40807
+ ${ui.code(`${web}/cli/login?${new URLSearchParams({ host })}`)}
40808
+ `);
40809
+ return promptHidden("Paste the token it shows: ");
40810
+ }
40811
+ const state = randomBytes(16).toString("base64url");
40812
+ return new Promise((resolve, reject) => {
40813
+ const done = (err, token) => {
40814
+ clearTimeout(timer);
40815
+ server.close();
40816
+ if (err) reject(err);
40817
+ else resolve(token);
40818
+ };
40819
+ const server = createServer((req, res) => {
40820
+ if (req.method !== "POST" || req.url !== "/callback") return void res.writeHead(404).end();
40821
+ let body = "";
40822
+ req.setEncoding("utf8");
40823
+ req.on("data", (c) => {
40824
+ body += c;
40825
+ if (body.length > 8192) req.destroy();
40826
+ });
40827
+ req.on("end", () => {
40828
+ const form = new URLSearchParams(body);
40829
+ if (form.get("state") !== state) return void res.writeHead(400, { "content-type": "text/plain" }).end("This sign-in link is stale. Run `yarkr login` again.");
40830
+ const token = form.get("token");
40831
+ res.writeHead(303, { location: `${web}/cli/login?${new URLSearchParams({ done: token ? "ok" : "denied" })}`, connection: "close" });
40832
+ res.end(() => done(token ? null : new CliError("Sign-in was cancelled in the browser."), token ?? void 0));
40833
+ });
40834
+ });
40835
+ const timer = setTimeout(() => done(new CliError("Timed out waiting for the browser.", "Run `yarkr login` again, or `yarkr login --no-browser` to paste a token.")), LOGIN_TIMEOUT_MS);
40836
+ server.on("error", (err) => done(err));
40837
+ server.listen(0, "127.0.0.1", () => {
40838
+ const { port } = server.address();
40839
+ const url2 = `${web}/cli/login?${new URLSearchParams({ port: String(port), state, host })}`;
40840
+ ui.info(`Opening your browser to sign in to ${ui.bold(web)}\u2026`);
40841
+ ui.step(`If it doesn't open, visit ${ui.code(url2)}`);
40842
+ openUrl(url2);
40843
+ });
40844
+ });
40845
+ }
40846
+ function openBrowser(url2) {
40847
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url2]] : process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url2]] : ["xdg-open", [url2]];
40848
+ try {
40849
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
40850
+ child.on("error", () => {
40851
+ });
40852
+ child.unref();
40853
+ } catch {
40854
+ }
41058
40855
  }
41059
40856
  async function logout(opts) {
41060
40857
  const userRc = loadRc(userRcPath());
41061
- const registry2 = normalizeServer(opts.registry ?? process.env.YARKR_REGISTRY ?? loadRc(path3.join(process.cwd(), RC_FILE))?.registry ?? userRc?.registry ?? DEFAULT_REGISTRY);
41062
- if (!userRc || tokenFor(userRc, registry2) === void 0) {
40858
+ const registry2 = registryFor(opts.registry, userRc);
40859
+ let token;
40860
+ try {
40861
+ token = userRc ? tokenFor(userRc, registry2) : void 0;
40862
+ } catch {
40863
+ token = null;
40864
+ }
40865
+ if (token === void 0) {
41063
40866
  ui.info("You aren't signed in.");
41064
40867
  return;
41065
40868
  }
40869
+ const revoked = token ? await new Api({ server: registry2, token }).revokeCurrentToken().then(() => true, () => false) : false;
40870
+ keychainDelete(registryKey(registry2));
41066
40871
  await setRcValue(userRcPath(), authTokenKey(registry2), null, { secret: true });
41067
- ui.ok(`Signed out of ${registry2}. The token still exists on the server; revoke it in Settings \u2192 API tokens if you no longer need it.`);
40872
+ ui.ok(`Signed out of ${registry2}.`);
40873
+ if (!revoked) ui.step("The token may still exist on the server; revoke it in Settings \u2192 API tokens if you no longer need it.");
41068
40874
  }
41069
40875
  async function whoami(opts) {
41070
40876
  const conn = await resolveConnection({ registry: opts.registry, dir: process.cwd() });
@@ -41072,28 +40878,28 @@ async function whoami(opts) {
41072
40878
  ui.ok(`${ui.bold(me.name)} <${me.email}> on ${conn.server}`);
41073
40879
  if (conn.tokenSource) ui.step(`token from ${conn.tokenSource}`);
41074
40880
  if (me.is_admin) ui.step("admin");
41075
- if (me.groups.length) ui.step(`groups: ${me.groups.join(", ")}`);
40881
+ if (me.orgs.length) ui.step(`orgs: ${me.orgs.map((o) => `${o.slug}${o.role === "admin" ? " (admin)" : ""}`).join(", ")}`);
41076
40882
  }
41077
40883
 
41078
40884
  // src/commands/registry.ts
41079
40885
  var pct = (v) => v === null ? ui.dim("\u2014") : `${Math.round(v * 100)}%`;
41080
40886
  async function search(query, opts) {
41081
- const api2 = new Api(await resolveConnection({ dir: process.cwd() }));
41082
- const page = await api2.search({ q: query, kind: opts.kind, target: opts.target });
40887
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
40888
+ const page = await api.search({ q: query, kind: opts.kind, target: opts.target });
41083
40889
  if (opts.json) return console.log(JSON.stringify(page, null, 2));
41084
40890
  if (!page.items.length) {
41085
- ui.info(query ? `Nothing matches "${query}".` : "Nothing here yet. An org admin connects GitHub repos under Settings \u2192 Organisations.");
40891
+ ui.info(query ? `Nothing matches "${query}".` : "Nothing here yet. Publish by installing the Yarkr GitHub App on your repos (Settings \u2192 GitHub).");
41086
40892
  return;
41087
40893
  }
41088
40894
  ui.table(
41089
- page.items.map((p) => [ui.bold(p.name), p.kind, p.channels.find((c) => c.name === "stable")?.version ?? ui.dim("unreleased"), pct(p.verified_eval?.pass_rate ?? null), p.description.slice(0, 60)]),
40895
+ page.items.map((p) => [ui.bold(p.name), p.kind, p.channels.find((c) => c.name === "stable")?.version ?? ui.dim("unreleased"), pct(p.reported_eval?.pass_rate ?? null), p.description.slice(0, 60)]),
41090
40896
  ["package", "kind", "stable", "evals", "description"]
41091
40897
  );
41092
40898
  if (page.total > page.items.length) ui.step(`${page.total - page.items.length} more \u2014 narrow the search.`);
41093
40899
  }
41094
40900
  async function info(name, opts) {
41095
- const api2 = new Api(await resolveConnection({ dir: process.cwd() }));
41096
- const p = await api2.pkg(name);
40901
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
40902
+ const p = await api.pkg(name);
41097
40903
  if (opts.json) return console.log(JSON.stringify(p, null, 2));
41098
40904
  ui.heading(`${p.name} ${ui.dim(p.kind)}`);
41099
40905
  console.log(` ${p.description}`);
@@ -41106,20 +40912,28 @@ async function info(name, opts) {
41106
40912
  ["tags", p.tags.join(", ") || ui.dim("\u2014")],
41107
40913
  ["classification", p.classification],
41108
40914
  ["source", p.source ? `${p.source.repo}/${p.source.path}` : ui.dim("\u2014")],
41109
- ["verified evals", pct(p.verified_eval?.pass_rate ?? null)],
41110
- ["reported evals", p.reported_eval ? `${pct(p.reported_eval.pass_rate)} ${ui.dim(p.reported_eval.trust)}` : ui.dim("\u2014")]
40915
+ ["evals", p.reported_eval ? `${pct(p.reported_eval.pass_rate)} ${ui.dim(p.reported_eval.trust)}` : ui.dim("\u2014")]
41111
40916
  ].map(([k, v]) => [ui.dim(k), v])
41112
40917
  );
41113
40918
  if (p.releases.length) {
41114
40919
  console.log();
41115
40920
  ui.table(
41116
- p.releases.slice(0, 10).map((r) => [r.version, r.channels.join(", "), pct(r.eval_score), r.commit_sha?.slice(0, 7) ?? "", r.created_at.slice(0, 10)]),
40921
+ p.releases.slice(0, 10).map((r) => [r.version, r.yanked ? ui.del(`yanked: ${r.yanked.reason}`) : r.channels.join(", "), pct(r.eval_score), r.commit_sha?.slice(0, 7) ?? "", r.created_at.slice(0, 10)]),
41117
40922
  ["release", "channels", "evals", "commit", "date"]
41118
40923
  );
41119
40924
  }
41120
40925
  console.log();
41121
40926
  ui.step(`Install: ${ui.code(`yarkr add ${p.name}`)}`);
41122
40927
  }
40928
+ async function yank(spec, opts) {
40929
+ const at = spec.lastIndexOf("@");
40930
+ if (at < 1) throw new CliError(`Name the release: <namespace/name>@<version>, e.g. acme/pdf@1.4.0.`);
40931
+ const [name, version2] = [spec.slice(0, at), spec.slice(at + 1)];
40932
+ if (!opts.undo && !opts.reason) throw new CliError("Say why with --reason: people who installed it see it.", `yarkr yank ${spec} --reason "leaks a token"`);
40933
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
40934
+ await api.yank(name, version2, opts.undo ? null : opts.reason);
40935
+ ui.ok(opts.undo ? `${name}@${version2} is no longer yanked.` : `Yanked ${name}@${version2}. Ranges and stable skip it now; lockfiles that pin it still install it, with a warning.`);
40936
+ }
41123
40937
  async function mcpConfig(harness, opts) {
41124
40938
  if (!HARNESS_IDS.includes(harness)) throw new CliError(`Unknown harness "${harness}".`, `Choose one of: ${HARNESS_IDS.join(", ")}.`);
41125
40939
  const conn = await resolveConnection({ registry: opts.registry, dir: process.cwd() });
@@ -41140,13 +40954,13 @@ async function mcpConfig(harness, opts) {
41140
40954
  // src/commands/project.ts
41141
40955
  import { existsSync as existsSync3 } from "fs";
41142
40956
  import { mkdir as mkdir3 } from "fs/promises";
41143
- import os4 from "os";
40957
+ import os5 from "os";
41144
40958
  import path5 from "path";
41145
40959
 
41146
40960
  // src/sync.ts
41147
40961
  import { mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile3, readdir, rmdir } from "fs/promises";
41148
40962
  import { existsSync as existsSync2 } from "fs";
41149
- import os3 from "os";
40963
+ import os4 from "os";
41150
40964
  import path4 from "path";
41151
40965
 
41152
40966
  // ../../packages/core/dist/hash.js
@@ -41311,14 +41125,14 @@ var dirname = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "";
41311
41125
  var basename = (p) => p.slice(p.lastIndexOf("/") + 1);
41312
41126
  var under = (dir, p) => dir ? p.startsWith(`${dir}/`) : true;
41313
41127
  var rel = (dir, p) => dir ? p.slice(dir.length + 1) : p;
41314
- function parseOverrides(text, path16, problems) {
41128
+ function parseOverrides(text, path17, problems) {
41315
41129
  if (text === null)
41316
41130
  return null;
41317
41131
  try {
41318
41132
  const raw = import_yaml3.default.parse(text);
41319
41133
  return raw && typeof raw === "object" ? raw : null;
41320
41134
  } catch (err) {
41321
- problems.push({ path: path16, message: `${MANIFEST_FILE} isn't valid YAML: ${err.message}` });
41135
+ problems.push({ path: path17, message: `${MANIFEST_FILE} isn't valid YAML: ${err.message}` });
41322
41136
  return null;
41323
41137
  }
41324
41138
  }
@@ -41332,7 +41146,7 @@ async function detectAssets(input2) {
41332
41146
  const claimed = [];
41333
41147
  const isClaimed = (p) => claimed.some((d) => d ? p === d || p.startsWith(`${d}/`) : true);
41334
41148
  const filesBelow = (dir) => new Map(paths.filter((p) => under(dir, p)).map((p) => [rel(dir, p), p]));
41335
- const make = (name, kind, path16, files, o, extra) => {
41149
+ const make = (name, kind, path17, files, o, extra) => {
41336
41150
  const suites = [...files.keys()].filter((p) => SUITE.test(p));
41337
41151
  const raw = {
41338
41152
  name: `${input2.namespace}/${slugify2(name)}`,
@@ -41351,10 +41165,10 @@ async function detectAssets(input2) {
41351
41165
  };
41352
41166
  const res = Manifest.safeParse(raw);
41353
41167
  if (!res.success) {
41354
- problems.push({ path: path16, message: res.error.issues.map((i) => `${i.path.join(".") || "manifest"}: ${i.message}`).join("; ") });
41168
+ problems.push({ path: path17, message: res.error.issues.map((i) => `${i.path.join(".") || "manifest"}: ${i.message}`).join("; ") });
41355
41169
  return null;
41356
41170
  }
41357
- return { name: slugify2(name), kind, path: path16, manifest: res.data, files };
41171
+ return { name: slugify2(name), kind, path: path17, manifest: res.data, files };
41358
41172
  };
41359
41173
  const manifestNear = async (dir, from) => {
41360
41174
  const mp = dir ? `${dir}/${MANIFEST_FILE}` : MANIFEST_FILE;
@@ -41446,12 +41260,12 @@ async function detectAssets(input2) {
41446
41260
  }
41447
41261
  return { assets: [...byName.values()], problems };
41448
41262
  }
41449
- function toolsetServers2(text, path16, problems) {
41263
+ function toolsetServers2(text, path17, problems) {
41450
41264
  let doc;
41451
41265
  try {
41452
41266
  doc = JSON.parse(text ?? "");
41453
41267
  } catch (err) {
41454
- problems.push({ path: path16, message: `isn't valid JSON: ${err.message}` });
41268
+ problems.push({ path: path17, message: `isn't valid JSON: ${err.message}` });
41455
41269
  return null;
41456
41270
  }
41457
41271
  const out = [];
@@ -41464,7 +41278,7 @@ function toolsetServers2(text, path16, problems) {
41464
41278
  const ref = String(v).replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-[^}]*)?\}/g, "${env:$1}");
41465
41279
  const plain = ref.replace(/^Bearer /, "");
41466
41280
  if (!SECRET_REF.test(plain)) {
41467
- problems.push({ path: path16, message: `${name}.${field}.${k} is a literal value. Use \${${k.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}} or \${vault:path#key} so no secret lives in the repo.` });
41281
+ problems.push({ path: path17, message: `${name}.${field}.${k} is a literal value. Use \${${k.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}} or \${vault:path#key} so no secret lives in the repo.` });
41468
41282
  return null;
41469
41283
  }
41470
41284
  res[k] = ref;
@@ -41484,7 +41298,7 @@ function toolsetServers2(text, path16, problems) {
41484
41298
  });
41485
41299
  }
41486
41300
  if (!out.length) {
41487
- problems.push({ path: path16, message: "has no mcpServers." });
41301
+ problems.push({ path: path17, message: "has no mcpServers." });
41488
41302
  return null;
41489
41303
  }
41490
41304
  return out;
@@ -41498,7 +41312,7 @@ function firstParagraph(md2) {
41498
41312
 
41499
41313
  // src/resolve.ts
41500
41314
  var import_semver2 = __toESM(require_semver2(), 1);
41501
- async function resolveAll(api2, opts) {
41315
+ async function resolveAll(api, opts) {
41502
41316
  const ranges = /* @__PURE__ */ new Map();
41503
41317
  const queue = [];
41504
41318
  const want = (name, range, from) => {
@@ -41509,14 +41323,12 @@ async function resolveAll(api2, opts) {
41509
41323
  };
41510
41324
  for (const [name, range] of Object.entries(opts.roots)) want(name, range, null);
41511
41325
  const channels = /* @__PURE__ */ new Map();
41512
- const pins = /* @__PURE__ */ new Map();
41513
41326
  for (const r of opts.arkRoots ?? []) {
41514
- for (const src of r.sources) want(r.name, r.range ?? "*", `${r.via ?? "ark"}:${src}`);
41327
+ for (const src of r.sources) want(r.name, r.range ?? "*", `ark:${src}`);
41515
41328
  if (r.channel) channels.set(r.name, r.channel);
41516
- if (r.digest && r.range) pins.set(r.name, { version: r.range, digest: r.digest, sources: r.sources });
41517
41329
  }
41518
41330
  const out = /* @__PURE__ */ new Map();
41519
- const key = await signingKey(api2);
41331
+ const key = await signingKey(api);
41520
41332
  while (queue.length) {
41521
41333
  const name = queue.shift();
41522
41334
  const reqs = ranges.get(name);
@@ -41525,22 +41337,10 @@ async function resolveAll(api2, opts) {
41525
41337
  prev.lock.required_by = requiredBy(reqs);
41526
41338
  continue;
41527
41339
  }
41528
- const pin = pins.get(name);
41529
- if (pin) {
41530
- const clash = reqs.find((r) => !satisfies(pin.version, r.range));
41531
- if (clash) {
41532
- throw new CliError(`${name}: ${pin.sources.join(", ")} pins ${pin.version}, but ${clash.from ?? "yarkr.json"} wants ${clash.range}.`, "A collection pins exact releases. Relax the other range, or use another version of the collection.");
41533
- }
41534
- }
41535
- const version2 = pin?.version ?? await pickVersion(api2, name, reqs, { ...opts, channel: channels.get(name) ?? opts.channel });
41536
- const bundle = await api2.bundle(name, version2);
41340
+ const version2 = await pickVersion(api, name, reqs, { ...opts, channel: channels.get(name) ?? opts.channel });
41341
+ const bundle = await api.bundle(name, version2, { sync: true });
41537
41342
  const pkg = verifyBundle(bundle, key, opts.requireSignatures ?? false);
41538
- if (pin && bundle.content_hash !== pin.digest) {
41539
- throw new CliError(
41540
- `${name}@${version2} doesn't match the content hash ${pin.sources.join(", ")} pinned (${pin.digest.slice(0, 12)}\u2026, got ${bundle.content_hash.slice(0, 12)}\u2026).`,
41541
- "Nothing was written. Report this to your Yarkr admin."
41542
- );
41543
- }
41343
+ if (bundle.yanked) ui.warn(`${name}@${version2} was yanked: ${bundle.yanked.reason}. Run \`yarkr update ${name}\` to move off it.`);
41544
41344
  out.set(name, {
41545
41345
  pkg,
41546
41346
  lock: {
@@ -41562,14 +41362,14 @@ async function resolveAll(api2, opts) {
41562
41362
  }
41563
41363
  var satisfies = (v, range) => range === "*" || range === "latest" || import_semver2.default.satisfies(v, range, { includePrerelease: true });
41564
41364
  var requiredBy = (reqs) => [...new Set(reqs.map((r) => r.from).filter((f) => !!f))].sort();
41565
- async function pickVersion(api2, name, reqs, opts) {
41365
+ async function pickVersion(api, name, reqs, opts) {
41566
41366
  const locked = opts.lock?.packages[name];
41567
41367
  const updating = opts.update === true || opts.update?.has(name);
41568
41368
  if (locked && !updating && reqs.every((r) => satisfies(locked.version, r.range))) return locked.version;
41569
41369
  const ranges = reqs.map((r) => r.range).filter((r) => r !== "*" && r !== "latest");
41570
41370
  const combined = ranges.length ? ranges.join(" ") : void 0;
41571
41371
  try {
41572
- const rel2 = await api2.resolve(name, { channel: opts.channel, range: combined });
41372
+ const rel2 = await api.resolve(name, { channel: opts.channel, range: combined });
41573
41373
  if (!reqs.every((r) => satisfies(rel2.version, r.range))) throw new Error("unsatisfied");
41574
41374
  return rel2.version;
41575
41375
  } catch (err) {
@@ -41583,9 +41383,9 @@ async function pickVersion(api2, name, reqs, opts) {
41583
41383
  throw err;
41584
41384
  }
41585
41385
  }
41586
- async function signingKey(api2) {
41386
+ async function signingKey(api) {
41587
41387
  try {
41588
- return await api2.signingKey();
41388
+ return await api.signingKey();
41589
41389
  } catch {
41590
41390
  return null;
41591
41391
  }
@@ -41622,8 +41422,8 @@ function verifyBundle(b, key, requireSignature) {
41622
41422
  return { manifest: b.manifest, files, content_hash: b.content_hash };
41623
41423
  }
41624
41424
  var TEXT_EXT = /\.(md|mdc|mdx|markdown|txt|ya?ml|json|toml|py|sh|bash|js|mjs|cjs|ts|tsx|csv|html|css|xml|sql|rb|go|rs|java|ini|cfg|env)$/i;
41625
- function looksText(path16, bytes) {
41626
- if (TEXT_EXT.test(path16)) return true;
41425
+ function looksText(path17, bytes) {
41426
+ if (TEXT_EXT.test(path17)) return true;
41627
41427
  const n = Math.min(bytes.length, 4096);
41628
41428
  for (let i = 0; i < n; i++) if (bytes[i] === 0) return false;
41629
41429
  try {
@@ -41635,6 +41435,16 @@ function looksText(path16, bytes) {
41635
41435
  }
41636
41436
 
41637
41437
  // src/sync.ts
41438
+ async function loadArks(api, arks, signedIn) {
41439
+ if (!arks) return { ark: null, publicArks: [] };
41440
+ if (arks === "auto") return { ark: signedIn ? await api.myArk() : null, publicArks: [] };
41441
+ const all = signedIn ? await api.myArk() : null;
41442
+ const mine = arks.filter((r) => r === "@me" || all?.arks.some((a) => a.ref === r));
41443
+ if (!signedIn && arks.includes("@me")) throw new CliError("@me is your personal ark: sign in to sync it.", 'Run `yarkr login`, or drop @me from "arks".');
41444
+ const publicArks2 = [];
41445
+ for (const ref of arks.filter((r) => !mine.includes(r))) publicArks2.push(await api.ark(ref, { sync: true }));
41446
+ return { ark: mine.length ? await api.myArk(mine) : null, publicArks: publicArks2 };
41447
+ }
41638
41448
  var hashOf = (c) => sha256(c);
41639
41449
  async function readMaybe(p) {
41640
41450
  try {
@@ -41643,35 +41453,25 @@ async function readMaybe(p) {
41643
41453
  return null;
41644
41454
  }
41645
41455
  }
41646
- async function planSync(api2, opts) {
41456
+ async function planSync(api, opts) {
41647
41457
  const { config: config2 } = opts;
41648
41458
  const harnesses = opts.harnesses ?? config2.harnesses;
41649
41459
  if (!harnesses.length) throw new CliError("No harnesses configured.", "Add some to yarkr.json, e.g. `yarkr init --harness claude-code codex`.");
41650
41460
  const unknown2 = harnesses.filter((h) => !config2.harnesses.includes(h));
41651
41461
  if (unknown2.length) throw new CliError(`${unknown2.join(", ")} isn't in yarkr.json's harnesses.`, "Add it there first so the lockfile can track its files.");
41652
41462
  const warnings = [];
41653
- const ark = config2.arks ? await api2.myArk(config2.arks === "auto" ? void 0 : config2.arks) : null;
41463
+ const { ark, publicArks: publicArks2 } = await loadArks(api, config2.arks, opts.signedIn ?? true);
41464
+ const items = [...ark?.items ?? [], ...publicArks2.flatMap((a) => a.items.map((i) => ({ ...i, sources: [a.ref] })))];
41654
41465
  const arkRoots = [];
41655
41466
  const excluded = new Set(config2.exclude ?? []);
41656
- for (const it of ark?.items ?? []) {
41467
+ for (const it of items) {
41657
41468
  if (excluded.has(it.package)) {
41658
41469
  if (!it.required) continue;
41659
41470
  warnings.push(`${it.package} is required by ${it.sources.join(", ")}; "exclude" can't drop it.`);
41660
41471
  }
41661
41472
  arkRoots.push({ name: it.package, range: it.range, channel: it.channel, sources: it.sources });
41662
41473
  }
41663
- const collections = [];
41664
- for (const ref of config2.collections ?? []) {
41665
- const c = await api2.readCollection(ref);
41666
- collections.push(c);
41667
- for (const it of c.items) {
41668
- const same = arkRoots.find((r) => r.name === it.package && r.digest);
41669
- if (same && same.range !== it.version) throw new CliError(`${it.package}: ${same.sources.join(", ")} pins ${same.range}, but ${c.ref} pins ${it.version}.`, "Use versions of these collections that agree, or drop one.");
41670
- if (same) same.sources.push(c.ref);
41671
- else arkRoots.push({ name: it.package, range: it.version, channel: null, sources: [c.ref], digest: it.digest, via: "collection" });
41672
- }
41673
- }
41674
- const resolved = await resolveAll(api2, {
41474
+ const resolved = await resolveAll(api, {
41675
41475
  channel: config2.channel,
41676
41476
  roots: config2.packages,
41677
41477
  arkRoots,
@@ -41679,13 +41479,13 @@ async function planSync(api2, opts) {
41679
41479
  update: opts.update,
41680
41480
  requireSignatures: opts.requireSignatures
41681
41481
  });
41682
- for (const it of [...ark?.items ?? [], ...collections.flatMap((c) => c.items)]) {
41482
+ for (const it of items) {
41683
41483
  const r = resolved.get(it.package);
41684
41484
  if (r && it.config.servers) r.pkg = withServerOverrides(r.pkg, it.config.servers);
41685
41485
  }
41686
41486
  const packages = [...resolved.values()].map((r) => r.pkg);
41687
41487
  for (const m of config2.mcp ?? []) packages.push(connectionPackage(connectionFor(opts.server, m)));
41688
- const base = config2.scope === "user" ? opts.home ?? os3.homedir() : opts.dir;
41488
+ const base = config2.scope === "user" ? opts.home ?? os4.homedir() : opts.dir;
41689
41489
  const prevOutputs = opts.lock?.outputs ?? {};
41690
41490
  const outputs = {};
41691
41491
  for (const [h, o] of Object.entries(prevOutputs)) if (!harnesses.includes(h) && config2.harnesses.includes(h)) outputs[h] = o;
@@ -41763,11 +41563,10 @@ async function planSync(api2, opts) {
41763
41563
  scope: config2.scope,
41764
41564
  packages: Object.fromEntries([...resolved.entries()].map(([n, r]) => [n, r.lock])),
41765
41565
  outputs,
41766
- ...ark ? { arks: ark.arks.map((a) => a.ref) } : {},
41767
- ...collections.length ? { collections: collections.map((c) => c.ref) } : {}
41566
+ ...ark || publicArks2.length ? { arks: [...ark?.arks.map((a) => a.ref) ?? [], ...publicArks2.map((a) => a.ref)] } : {}
41768
41567
  };
41769
41568
  const lockChanged = !opts.lock || stringifyLock(lock) !== stringifyLock(opts.lock);
41770
- return { resolved, packages, changes, conflicts, kept, notes, warnings, ark, collections, lock, lockChanged, base };
41569
+ return { resolved, packages, changes, conflicts, kept, notes, warnings, ark, publicArks: publicArks2, lock, lockChanged, base };
41771
41570
  }
41772
41571
  function withServerOverrides(pkg, overrides) {
41773
41572
  const ts = pkg.manifest.toolset;
@@ -41915,46 +41714,45 @@ async function init2(opts) {
41915
41714
  async function context(flags = {}) {
41916
41715
  const cwd = process.cwd();
41917
41716
  const conn = await resolveConnection({ dir: cwd, registry: flags.registry });
41918
- const api2 = new Api(conn);
41717
+ const api = new Api(conn);
41919
41718
  const project = flags.user ? null : await loadProjectConfig(cwd, { required: false });
41920
- if (project) return { dir: cwd, config: project, conn, api: api2, lock: await loadLock(cwd), userMode: false };
41719
+ if (project) return { dir: cwd, config: project, conn, api, lock: await loadLock(cwd), userMode: false };
41921
41720
  const dir = userStateDir();
41922
41721
  let config2 = await loadProjectConfig(dir, { required: false });
41923
41722
  if (!config2) {
41924
- const detected = detectHarnesses2(os4.homedir());
41925
- config2 = { channel: "stable", scope: "user", harnesses: detected.length ? detected : ["claude-code"], packages: {}, arks: "auto" };
41723
+ const detected2 = detectHarnesses2(os5.homedir());
41724
+ config2 = { channel: "stable", scope: "user", harnesses: detected2.length ? detected2 : ["claude-code"], packages: {}, arks: "auto" };
41926
41725
  await mkdir3(dir, { recursive: true });
41927
41726
  await saveProjectConfig(dir, config2);
41928
41727
  ui.info(`No ${CONFIG_FILE} here, so yarkr installs your arks into your home directory (${config2.harnesses.join(", ")}). Settings: ${path5.join(dir, CONFIG_FILE)}.`);
41929
41728
  }
41930
- return { dir, config: config2, conn, api: api2, lock: await loadLock(dir), userMode: true };
41729
+ return { dir, config: config2, conn, api, lock: await loadLock(dir), userMode: true };
41931
41730
  }
41932
- async function sync(flags, update2, collectionRefs = []) {
41933
- const { dir, config: config2, conn, api: api2, lock, userMode } = await context(flags);
41731
+ async function sync(flags, update2, arkRefs = []) {
41732
+ const { dir, config: config2, conn, api, lock, userMode } = await context(flags);
41934
41733
  const harnesses = parseHarnesses(flags.harness);
41935
- if (collectionRefs.length) {
41936
- for (const ref of collectionRefs) {
41937
- const parsed = parseCollectionRef(ref);
41938
- if (!parsed) throw new CliError(`"${ref}" isn't a collection.`, "Collections look like @<handle>/<name>, or @<handle>/<name>@<version> for one version.");
41939
- const base = `@${parsed.handle}/${parsed.name}`;
41940
- config2.collections = [...(config2.collections ?? []).filter((c) => c !== base && !c.startsWith(`${base}@`)), ref];
41734
+ if (arkRefs.length) {
41735
+ for (const ref of arkRefs) {
41736
+ if (ref !== "@me" && !/^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/.test(ref)) throw new CliError(`"${ref}" isn't an ark.`, "Arks look like <org>/<ark>, e.g. acme/starter; @me is your personal ark.");
41737
+ const listed = Array.isArray(config2.arks) ? config2.arks : config2.arks === "auto" && conn.token ? (await api.myArk()).arks.map((a) => a.ref) : [];
41738
+ config2.arks = [...listed.filter((a) => a !== ref), ref];
41941
41739
  }
41942
41740
  for (const h of harnesses ?? []) if (!config2.harnesses.includes(h)) config2.harnesses.push(h);
41943
41741
  }
41944
41742
  const runConfig = !conn.token && config2.arks === "auto" ? { ...config2, arks: void 0 } : config2;
41945
- if (runConfig !== config2 && userMode) ui.info("Not signed in, so only collections are synced. `yarkr login` adds your arks.");
41946
- const opts = { dir, config: runConfig, lock, server: conn.server, harnesses, update: update2, requireSignatures: flags.requireSignatures };
41947
- const plan = await planSync(api2, opts);
41948
- if (collectionRefs.length && !flags.dryRun && !flags.check) await saveProjectConfig(dir, config2);
41743
+ if (runConfig !== config2 && userMode) ui.info("Not signed in, so only packages and public arks are synced. `yarkr login` adds your arks.");
41744
+ const opts = { dir, config: runConfig, lock, server: conn.server, harnesses, update: update2, requireSignatures: flags.requireSignatures, signedIn: !!conn.token };
41745
+ const plan = await planSync(api, opts);
41746
+ if (arkRefs.length && !flags.dryRun && !flags.check) await saveProjectConfig(dir, config2);
41949
41747
  if (plan.ark) ui.info(plan.ark.arks.length ? `Arks: ${plan.ark.arks.map((a) => a.ref).join(", ")}` : "No arks give you anything yet.");
41950
- for (const c of plan.collections) ui.info(`Collection ${c.ref}${c.version < c.latest ? ui.dim(` (latest is ${c.latest})`) : ""}${c.description ? ui.dim(` \xB7 ${c.description}`) : ""}`);
41748
+ for (const a of plan.publicArks) ui.info(`Public ark ${a.ref}${a.description ? ui.dim(` \xB7 ${a.description}`) : ""}`);
41951
41749
  for (const [name, r] of [...plan.resolved.entries()].sort(([a], [b]) => a < b ? -1 : 1)) {
41952
41750
  const prev = lock?.packages[name]?.version;
41953
- const via = r.lock.required_by.length ? ui.dim(` (via ${r.lock.required_by.map((v) => v.replace(/^(ark|collection):/, "$1 ")).join(", ")})`) : "";
41751
+ const via = r.lock.required_by.length ? ui.dim(` (via ${r.lock.required_by.map((v) => v.replace(/^ark:/, "ark ")).join(", ")})`) : "";
41954
41752
  const move = prev && prev !== r.lock.version ? `${ui.dim(prev)} \u2192 ${ui.bold(r.lock.version)}` : ui.bold(r.lock.version);
41955
41753
  ui.step(`${name} ${move}${via}`);
41956
41754
  }
41957
- if (!plan.resolved.size && !plan.ark && !plan.collections.length) ui.info(`No packages in ${CONFIG_FILE}. Add one with \`yarkr add <namespace/name>\`.`);
41755
+ if (!plan.resolved.size && !plan.ark && !plan.publicArks.length) ui.info(`No packages in ${CONFIG_FILE}. Add one with \`yarkr add <namespace/name>\`.`);
41958
41756
  for (const w of plan.warnings) ui.warn(w);
41959
41757
  printChanges(plan.changes);
41960
41758
  for (const k of plan.kept) ui.warn(flags.force ? `${k.path}: discarding your local edits (--force).` : `${k.path}: kept your local edits. \`yarkr diff\` shows them; open a pull request in the package's repo to send them upstream.`);
@@ -41997,16 +41795,16 @@ var splitSpec = (spec) => {
41997
41795
  return at > 0 ? { name: spec.slice(0, at), range: spec.slice(at + 1) } : { name: spec, range: "" };
41998
41796
  };
41999
41797
  async function add(specs, flags) {
42000
- const { dir, config: config2, conn, api: api2, userMode } = await context(flags);
41798
+ const { dir, config: config2, conn, api, userMode } = await context(flags);
42001
41799
  const added = [];
42002
41800
  for (const spec of specs) {
42003
41801
  let { name, range } = splitSpec(spec);
42004
41802
  if (!PackageName.safeParse(name).success) throw new CliError(`"${name}" isn't a package name.`, "Use namespace/name, e.g. finance/invoice-extraction.");
42005
41803
  if (!range) {
42006
- const rel2 = await api2.resolve(name, { channel: config2.channel });
41804
+ const rel2 = await api.resolve(name, { channel: config2.channel });
42007
41805
  range = `^${rel2.version}`;
42008
41806
  }
42009
- if (userMode && conn.token) await api2.addPersonal(name, range);
41807
+ if (userMode && conn.token) await api.addPersonal(name, range);
42010
41808
  else config2.packages[name] = range;
42011
41809
  added.push(`${name}@${range}`);
42012
41810
  }
@@ -42017,38 +41815,38 @@ async function add(specs, flags) {
42017
41815
  if (!flags.noSync) await sync(flags, new Set(specs.map((s) => splitSpec(s).name)));
42018
41816
  }
42019
41817
  async function remove(names, flags) {
42020
- const { dir, config: config2, conn, api: api2, lock, userMode } = await context(flags);
42021
- const syncedCollection = (n) => {
42022
- const col = parseCollectionRef(n);
42023
- const base = col && `@${col.handle}/${col.name}`;
42024
- return base ? (config2.collections ?? []).filter((c) => c === base || c.startsWith(`${base}@`)) : [];
42025
- };
42026
- const needsArk = names.some((n) => !(n in config2.packages) && !syncedCollection(n).length);
42027
- const ark = needsArk && config2.arks && (conn.token || config2.arks !== "auto") ? await api2.myArk(config2.arks === "auto" ? void 0 : config2.arks) : null;
41818
+ const { dir, config: config2, conn, api, lock, userMode } = await context(flags);
41819
+ const listedArk = (n) => Array.isArray(config2.arks) && config2.arks.includes(n);
41820
+ const needsArk = names.some((n) => !(n in config2.packages) && !listedArk(n));
41821
+ const ark = needsArk && config2.arks && conn.token ? await api.myArk(config2.arks === "auto" ? void 0 : await ownArks(api, config2.arks)) : null;
42028
41822
  const personalRef = ark?.arks.find((a) => a.kind === "personal")?.ref;
42029
41823
  for (const n of names) {
42030
41824
  if (n in config2.packages) {
42031
41825
  delete config2.packages[n];
42032
41826
  continue;
42033
41827
  }
42034
- const cols = syncedCollection(n);
42035
- if (cols.length) {
42036
- config2.collections = config2.collections.filter((c) => !cols.includes(c));
42037
- if (!config2.collections.length) delete config2.collections;
41828
+ if (listedArk(n)) {
41829
+ config2.arks = config2.arks.filter((a) => a !== n);
42038
41830
  continue;
42039
41831
  }
42040
41832
  const item = ark?.items.find((i) => i.package === n);
42041
41833
  if (!item) {
42042
41834
  const via = lock?.packages[n]?.required_by ?? [];
42043
- throw new CliError(`${n} isn't a direct dependency, an ark item or a collection here.`, via.length ? `It's installed because ${via.join(", ")} needs it.` : void 0);
41835
+ const fromPublicArk = via.some((v) => v.startsWith("ark:") && !ark?.arks.some((a) => `ark:${a.ref}` === v));
41836
+ if (fromPublicArk) {
41837
+ if (userMode) throw new CliError(`${n} comes from a public ark (${via.join(", ")}).`, `Remove the ark itself, or list the package under "exclude" if it's optional.`);
41838
+ config2.exclude = [.../* @__PURE__ */ new Set([...config2.exclude ?? [], n])];
41839
+ continue;
41840
+ }
41841
+ throw new CliError(`${n} isn't a direct dependency, an ark or an ark item here.`, via.length ? `It's installed because ${via.join(", ")} needs it.` : void 0);
42044
41842
  }
42045
41843
  const orgSources = item.sources.filter((src) => src !== personalRef);
42046
41844
  if (orgSources.length && item.required) {
42047
41845
  throw new CliError(`${n} is required by ${orgSources.join(", ")}.`, "Only optional ark items can be removed. Ask the ark's admin.");
42048
41846
  }
42049
- if (personalRef && item.sources.includes(personalRef)) await api2.removePersonal(n);
41847
+ if (personalRef && item.sources.includes(personalRef)) await api.removePersonal(n);
42050
41848
  for (const src of orgSources) {
42051
- if (userMode) await api2.exclude(src, n, true);
41849
+ if (userMode) await api.exclude(src, n, true);
42052
41850
  else config2.exclude = [.../* @__PURE__ */ new Set([...config2.exclude ?? [], n])];
42053
41851
  }
42054
41852
  }
@@ -42057,8 +41855,8 @@ async function remove(names, flags) {
42057
41855
  if (!flags.noSync) await sync(flags);
42058
41856
  }
42059
41857
  async function showArk(refs, opts) {
42060
- const api2 = new Api(await resolveConnection({ dir: process.cwd() }));
42061
- const eff = await api2.myArk(refs.length ? refs : void 0);
41858
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
41859
+ const eff = await api.myArk(refs.length ? refs : void 0);
42062
41860
  if (opts.json) return console.log(JSON.stringify(eff, null, 2));
42063
41861
  if (!eff.arks.length) return ui.info("No arks give you anything yet. Ask an org admin, or add your own with `yarkr add` outside a project.");
42064
41862
  ui.heading("Your arks");
@@ -42071,7 +41869,18 @@ async function showArk(refs, opts) {
42071
41869
  );
42072
41870
  } else ui.info("Your arks are empty.");
42073
41871
  if (eff.excluded.length) ui.step(`Excluded by you: ${eff.excluded.map((e) => `${e.package} (${e.ark})`).join(", ")}`);
42074
- if (!eff.personal_allowed) ui.step("Your org doesn't allow personal additions.");
41872
+ }
41873
+ async function publicArks(opts) {
41874
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
41875
+ const arks = await api.publicArks();
41876
+ if (opts.json) return console.log(JSON.stringify(arks, null, 2));
41877
+ if (!arks.length) return ui.info("No public arks yet. An org admin makes one public on the ark's page.");
41878
+ ui.table(arks.map((a) => [ui.bold(a.ref), String(a.items.length), a.uses_30d === void 0 ? "\u2014" : String(a.uses_30d), a.description]), ["ark", "packages", "uses (30d)", "description"]);
41879
+ ui.step(`Install one with ${ui.code("yarkr sync <org>/<ark>")}, or connect it with ${ui.code("yarkr mcp add <org>/<ark>")}. No sign-in needed.`);
41880
+ }
41881
+ async function ownArks(api, refs) {
41882
+ const all = await api.myArk();
41883
+ return refs.filter((r) => r === "@me" || all.arks.some((a) => a.ref === r));
42075
41884
  }
42076
41885
  async function update(names, flags) {
42077
41886
  return sync(flags, names.length ? new Set(names) : true);
@@ -42079,7 +41888,7 @@ async function update(names, flags) {
42079
41888
 
42080
41889
  // src/commands/diff.ts
42081
41890
  import { readFile as readFile4 } from "fs/promises";
42082
- import os5 from "os";
41891
+ import os6 from "os";
42083
41892
  import path6 from "path";
42084
41893
 
42085
41894
  // ../../node_modules/.pnpm/diff@8.0.4/node_modules/diff/libesm/diff/base.js
@@ -42182,16 +41991,16 @@ var Diff = class {
42182
41991
  }
42183
41992
  }
42184
41993
  }
42185
- addToPath(path16, added, removed, oldPosInc, options) {
42186
- const last = path16.lastComponent;
41994
+ addToPath(path17, added, removed, oldPosInc, options) {
41995
+ const last = path17.lastComponent;
42187
41996
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
42188
41997
  return {
42189
- oldPos: path16.oldPos + oldPosInc,
41998
+ oldPos: path17.oldPos + oldPosInc,
42190
41999
  lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
42191
42000
  };
42192
42001
  } else {
42193
42002
  return {
42194
- oldPos: path16.oldPos + oldPosInc,
42003
+ oldPos: path17.oldPos + oldPosInc,
42195
42004
  lastComponent: { count: 1, added, removed, previousComponent: last }
42196
42005
  };
42197
42006
  }
@@ -42519,7 +42328,7 @@ async function diff(name, opts) {
42519
42328
  const lock = await loadLock(dir);
42520
42329
  if (!lock) throw new CliError("Nothing synced yet.", "Run `yarkr sync` first.");
42521
42330
  if (name && !lock.packages[name]) throw new CliError(`${name} isn't in yarkr.lock.`);
42522
- const base = config2.scope === "user" ? os5.homedir() : dir;
42331
+ const base = config2.scope === "user" ? os6.homedir() : dir;
42523
42332
  const edited = [];
42524
42333
  for (const [harness, out] of Object.entries(lock.outputs)) {
42525
42334
  for (const [p, meta3] of Object.entries(out.files)) {
@@ -42542,9 +42351,9 @@ async function diff(name, opts) {
42542
42351
  for (const e of edited) console.log(e.path);
42543
42352
  return edited;
42544
42353
  }
42545
- const api2 = new Api(await resolveConnection({ dir }));
42546
- const key = await api2.signingKey().catch(() => null);
42547
- const pkgs = await Promise.all(Object.entries(lock.packages).map(async ([n, l]) => verifyBundle(await api2.bundle(n, l.version), key, false)));
42354
+ const api = new Api(await resolveConnection({ dir }));
42355
+ const key = await api.signingKey().catch(() => null);
42356
+ const pkgs = await Promise.all(Object.entries(lock.packages).map(async ([n, l]) => verifyBundle(await api.bundle(n, l.version), key, false)));
42548
42357
  for (const harness of new Set(edited.map((e) => e.harness))) {
42549
42358
  const out = render(harness, { packages: pkgs, scope: config2.scope, layout: config2.layout?.[harness] });
42550
42359
  for (const e of edited.filter((x) => x.harness === harness)) {
@@ -42565,15 +42374,16 @@ async function diff(name, opts) {
42565
42374
 
42566
42375
  // src/commands/mcp.ts
42567
42376
  async function mcpAdd(scopes, flags) {
42568
- const { dir, config: config2, conn, api: api2, userMode } = await context(flags);
42569
- const me = conn.token ? await api2.me().catch(() => null) : null;
42377
+ const { dir, config: config2, conn, api, userMode } = await context(flags);
42378
+ const me = conn.token ? await api.me().catch(() => null) : null;
42570
42379
  for (const scope of scopes) {
42571
- if (!isConnectionScope(scope)) throw new CliError(`"${scope}" isn't an MCP scope.`, "Use an org (acme), a team (acme/finance), global/<ark>, @me, @<handle>/<collection>, all, or public.");
42572
- const org = !["all", "public"].includes(scope) && !scope.startsWith("global/") && !scope.startsWith("@") ? scope.split("/")[0] : null;
42573
- if (org && me && !me.orgs.some((o) => o.slug === org)) {
42574
- throw new CliError(`You aren't a member of ${org} on ${conn.server}.`, `Your orgs: ${me.orgs.map((o) => o.slug).join(", ") || "none"}.`);
42380
+ if (!isConnectionScope(scope)) throw new CliError(`"${scope}" isn't an MCP scope.`, "Use an org (acme), a team or public ark (acme/finance), all, or public.");
42381
+ const org = !["all", "public"].includes(scope) ? scope.split("/")[0] : null;
42382
+ const member = !!org && !!me?.orgs.some((o) => o.slug === org);
42383
+ const isPublic = !!org && !member && scope.includes("/") && await api.ark(scope).then((a) => a.public).catch(() => false);
42384
+ if (org && !member && !isPublic && (me || !scope.includes("/"))) {
42385
+ throw new CliError(`You aren't a member of ${org} on ${conn.server}${scope.includes("/") ? `, and ${scope} isn't a public ark` : ""}.`, `Your orgs: ${me?.orgs.map((o) => o.slug).join(", ") || "none"}.`);
42575
42386
  }
42576
- const isPublic = parseCollectionRef(scope) ? (await api2.readCollection(scope)).visibility === "public" : false;
42577
42387
  const entry = { scope, ...flags.tokenEnv && !isPublic ? { token_env: flags.tokenEnv } : {}, ...isPublic ? { public: true } : {} };
42578
42388
  config2.mcp = [...(config2.mcp ?? []).filter((m) => m.scope !== scope), entry];
42579
42389
  }
@@ -42597,7 +42407,7 @@ async function mcpList(flags) {
42597
42407
  const rows = (config2.mcp ?? []).map((m) => connectionFor(conn.server, m));
42598
42408
  if (flags.json) return console.log(JSON.stringify(rows, null, 2));
42599
42409
  if (!rows.length) {
42600
- ui.info("No MCP connections. Add one with `yarkr mcp add <org>` (or `<org>/<team>`, `@<handle>/<collection>`, `all`, `public`).");
42410
+ ui.info("No MCP connections. Add one with `yarkr mcp add <org>` (or `<org>/<ark>`, `all`, `public`).");
42601
42411
  return;
42602
42412
  }
42603
42413
  ui.table(
@@ -42620,94 +42430,6 @@ function printHints(server, added, harnesses, userMode) {
42620
42430
  }
42621
42431
  }
42622
42432
 
42623
- // src/commands/collection.ts
42624
- var api = async () => new Api(await resolveConnection({ dir: process.cwd() }));
42625
- function visibility(v) {
42626
- if (v === void 0) return void 0;
42627
- const r = CollectionVisibility.safeParse(v);
42628
- if (!r.success) throw new CliError(`"${v}" isn't a visibility.`, "Use private (only you), org (members of --org) or public (anyone, no sign-in).");
42629
- return r.data;
42630
- }
42631
- var audience = (c) => c.visibility === "org" ? `org ${c.org}` : c.visibility;
42632
- function printCollection(c) {
42633
- ui.heading(`${c.ref} ${ui.dim(`(${audience(c)})`)}${c.description ? ui.dim(` \xB7 ${c.description}`) : ""}`);
42634
- if (c.arks.length) ui.table(c.arks.map((a) => [ui.bold(a.ref), a.kind, a.public ? "public" : "", a.description]), ["ark", "kind", "", "description"]);
42635
- else ui.info(`No arks yet. Add some with \`yarkr collection add ${c.name} <ark>\` (@me, <org>/<ark>, global/<ark>, or a public ark).`);
42636
- console.log();
42637
- if (c.versions.length) {
42638
- ui.table(c.versions.map((v) => [ui.bold(`@${v.version}`), String(v.items), v.published_at.slice(0, 10), v.note]), ["version", "packages", "published", "note"]);
42639
- } else ui.info(`Not published yet: \`yarkr collection publish ${c.name}\`.`);
42640
- }
42641
- function printRelease(r) {
42642
- ui.heading(`${r.ref} ${ui.dim(`(${r.visibility}${r.version < r.latest ? `, latest is ${r.latest}` : ""})`)}${r.note ? ui.dim(` \xB7 ${r.note}`) : ""}`);
42643
- if (!r.items.length) return ui.info("Empty.");
42644
- ui.table(
42645
- r.items.map((i) => [ui.bold(i.package), i.kind ?? "?", i.version, ui.dim(`${i.digest.slice(0, 12)}\u2026`), i.sources.join(", ")]),
42646
- ["package", "kind", "version", "digest", "from"]
42647
- );
42648
- }
42649
- async function collectionList(opts) {
42650
- const list = await (await api()).collections();
42651
- if (opts.json) return console.log(JSON.stringify(list, null, 2));
42652
- if (!list.length) return ui.info("No collections yet. Create one with `yarkr collection create <name>`.");
42653
- ui.table(
42654
- list.map((c) => [ui.bold(c.ref), audience(c), c.versions[0] ? `@${c.versions[0].version}` : ui.dim("unpublished"), String(c.arks.length), c.description]),
42655
- ["collection", "visibility", "latest", "arks", "description"]
42656
- );
42657
- }
42658
- async function collectionShow(ref, opts) {
42659
- const client = await api();
42660
- if (ref.startsWith("@")) {
42661
- const r = await client.readCollection(ref);
42662
- return opts.json ? console.log(JSON.stringify(r, null, 2)) : printRelease(r);
42663
- }
42664
- const c = await client.collection(ref);
42665
- if (opts.json) return console.log(JSON.stringify(c, null, 2));
42666
- printCollection(c);
42667
- }
42668
- async function collectionCreate(name, opts) {
42669
- const v = opts.public ? "public" : visibility(opts.visibility) ?? (opts.org ? "org" : "private");
42670
- const c = await (await api()).createCollection({ name, description: opts.description ?? "", visibility: v, ...opts.org ? { org: opts.org } : {} });
42671
- ui.ok(`Created ${c.ref} (${audience(c)}).`);
42672
- ui.step(`Add arks: ${ui.code(`yarkr collection add ${name} @me <org>/<ark> \u2026`)}, then ${ui.code(`yarkr collection publish ${name}`)}.`);
42673
- }
42674
- async function collectionEdit(name, opts) {
42675
- const v = opts.public ? "public" : visibility(opts.visibility) ?? (opts.org ? "org" : void 0);
42676
- if (v === void 0 && opts.description === void 0) throw new CliError("Nothing to change.", "Pass --visibility, --org, --public or --description.");
42677
- const c = await (await api()).updateCollection(name, { ...opts.description !== void 0 ? { description: opts.description } : {}, ...v ? { visibility: v } : {}, ...opts.org ? { org: opts.org } : {} });
42678
- ui.ok(`Updated ${c.ref} (${audience(c)}).`);
42679
- }
42680
- async function collectionDelete(name) {
42681
- await (await api()).deleteCollection(name);
42682
- ui.ok(`Deleted collection ${name}. Its published versions are gone too.`);
42683
- }
42684
- async function collectionAdd(name, refs) {
42685
- const client = await api();
42686
- let c;
42687
- for (const ref of refs) c = await client.addCollectionArk(name, ref);
42688
- ui.ok(`Added ${refs.join(", ")} to ${c.ref}. Publish to make it part of a version.`);
42689
- }
42690
- async function collectionRemove(name, refs) {
42691
- const client = await api();
42692
- let c;
42693
- for (const ref of refs) c = await client.removeCollectionArk(name, ref);
42694
- ui.ok(`Removed ${refs.join(", ")} from ${c.ref}. Publish to make it part of a version.`);
42695
- }
42696
- async function collectionPublish(name, opts) {
42697
- const r = await (await api()).publishCollection(name, opts.message ?? "");
42698
- if (opts.json) return console.log(JSON.stringify(r, null, 2));
42699
- if (r.unchanged) ui.info(`Nothing changed since ${r.ref}.`);
42700
- else ui.ok(`Published ${r.ref}.`);
42701
- printRelease(r);
42702
- ui.step(`Use it: ${ui.code(`yarkr sync ${r.collection}`)} (latest) or ${ui.code(`yarkr sync ${r.ref}`)}, or ${ui.code(`yarkr mcp add ${r.collection}`)}.`);
42703
- }
42704
- async function collectionArks(opts) {
42705
- const list = await (await api()).publicArks();
42706
- if (opts.json) return console.log(JSON.stringify(list, null, 2));
42707
- if (!list.length) return ui.info("No public arks yet.");
42708
- ui.table(list.map((a) => [ui.bold(a.ref), String(a.items.length), a.description]), ["ark", "packages", "description"]);
42709
- }
42710
-
42711
42433
  // src/commands/eval.ts
42712
42434
  var import_ajv2 = __toESM(require_ajv(), 1);
42713
42435
  import { existsSync as existsSync7 } from "fs";
@@ -42735,9 +42457,6 @@ function evalEnvFromProcess(env = process.env) {
42735
42457
  anthropicApiKey: nonEmpty(env.ANTHROPIC_API_KEY),
42736
42458
  openaiApiKey: nonEmpty(env.OPENAI_API_KEY),
42737
42459
  judgeModel: nonEmpty(env.JUDGE_MODEL) ?? "claude-sonnet-5",
42738
- customHttpUrl: nonEmpty(env.EVAL_CUSTOM_HTTP_URL),
42739
- customHttpToken: nonEmpty(env.EVAL_CUSTOM_HTTP_TOKEN),
42740
- customHttpVersion: nonEmpty(env.EVAL_CUSTOM_HTTP_VERSION),
42741
42460
  opencodeProvider: nonEmpty(env.EVAL_OPENCODE_PROVIDER),
42742
42461
  claudeAllowedTools: nonEmpty(env.EVAL_CLAUDE_ALLOWED_TOOLS),
42743
42462
  extraSecrets: { YARKR_TOKEN: nonEmpty(env.YARKR_TOKEN) },
@@ -42806,7 +42525,6 @@ function harnessAccessEnv(harness, env) {
42806
42525
  };
42807
42526
  switch (harness) {
42808
42527
  case "claude-code":
42809
- case "claude-cowork":
42810
42528
  if (gw) {
42811
42529
  out.ANTHROPIC_BASE_URL = gw.url;
42812
42530
  out.ANTHROPIC_AUTH_TOKEN = gw.token;
@@ -42849,7 +42567,6 @@ function secretsOf(env) {
42849
42567
  [env.inferenceTokenName ?? "INFERENCE_API_TOKEN"]: env.inferenceToken,
42850
42568
  ANTHROPIC_API_KEY: env.anthropicApiKey,
42851
42569
  OPENAI_API_KEY: env.openaiApiKey,
42852
- EVAL_CUSTOM_HTTP_TOKEN: env.customHttpToken,
42853
42570
  ...env.extraSecrets
42854
42571
  };
42855
42572
  return Object.fromEntries(Object.entries(all).filter((e) => typeof e[1] === "string" && e[1].length >= 8));
@@ -42858,11 +42575,10 @@ async function nativeRequirements(harness, env, onPath2) {
42858
42575
  const missing = [];
42859
42576
  const gw = gatewayOf(env);
42860
42577
  const access2 = "INFERENCE_API_URL + INFERENCE_API_TOKEN";
42861
- if (gw && !gw.token && harness !== "custom-http")
42578
+ if (gw && !gw.token)
42862
42579
  missing.push("INFERENCE_API_TOKEN (or API_TOKEN), since INFERENCE_API_URL is set");
42863
42580
  switch (harness) {
42864
42581
  case "claude-code":
42865
- case "claude-cowork":
42866
42582
  if (!await onPath2("claude"))
42867
42583
  missing.push("the `claude` CLI (npm i -g @anthropic-ai/claude-code)");
42868
42584
  if (!gw && !env.anthropicApiKey)
@@ -42880,10 +42596,6 @@ async function nativeRequirements(harness, env, onPath2) {
42880
42596
  if (!gw && !env.openaiApiKey && !env.anthropicApiKey)
42881
42597
  missing.push(`${access2}, ANTHROPIC_API_KEY or OPENAI_API_KEY`);
42882
42598
  break;
42883
- case "custom-http":
42884
- if (!env.customHttpUrl)
42885
- missing.push("EVAL_CUSTOM_HTTP_URL");
42886
- break;
42887
42599
  default:
42888
42600
  missing.push(`a native runner (there is none for ${harness} yet; use --simulated)`);
42889
42601
  }
@@ -42930,10 +42642,10 @@ async function loadSuites(candidate, candFiles, shared) {
42930
42642
  }
42931
42643
  return out;
42932
42644
  }
42933
- function parseSuite(bytes, ref, path16, source) {
42645
+ function parseSuite(bytes, ref, path17, source) {
42934
42646
  const text = bytes.toString("utf8");
42935
42647
  try {
42936
- return { suite: parseEvalSuite(text, path16), ref, path: path16, source, hash: sha256(text) };
42648
+ return { suite: parseEvalSuite(text, path17), ref, path: path17, source, hash: sha256(text) };
42937
42649
  } catch (err) {
42938
42650
  throw new SuiteError(err.message);
42939
42651
  }
@@ -42950,7 +42662,7 @@ function narrowSuites(suites, f) {
42950
42662
  }
42951
42663
 
42952
42664
  // ../../packages/evals/dist/runners/shared.js
42953
- import { spawn } from "child_process";
42665
+ import { spawn as spawn2 } from "child_process";
42954
42666
  import { access, readFile as readFile6 } from "fs/promises";
42955
42667
  import { constants } from "fs";
42956
42668
  import path8 from "path";
@@ -43068,7 +42780,7 @@ async function onPath(bin, pathVar = process.env.PATH ?? "") {
43068
42780
  function runProcess(cmd, args, opts) {
43069
42781
  return new Promise((resolve, reject) => {
43070
42782
  const started = Date.now();
43071
- const child = spawn(cmd, args, { cwd: opts.cwd, env: opts.env, stdio: ["pipe", "pipe", "pipe"] });
42783
+ const child = spawn2(cmd, args, { cwd: opts.cwd, env: opts.env, stdio: ["pipe", "pipe", "pipe"] });
43072
42784
  let stdout = "";
43073
42785
  let stderr = "";
43074
42786
  let timedOut = false;
@@ -43291,10 +43003,10 @@ function redact(value, secrets) {
43291
43003
 
43292
43004
  // ../../packages/evals/dist/cache.js
43293
43005
  import { mkdir as mkdir5, readFile as readFile7, rename, writeFile as writeFile5 } from "fs/promises";
43294
- import os6 from "os";
43006
+ import os7 from "os";
43295
43007
  import path9 from "path";
43296
43008
  function defaultCacheDir(env = process.env) {
43297
- return env.YARKR_CACHE_DIR || path9.join(env.XDG_CACHE_HOME || path9.join(os6.homedir(), ".cache"), "yarkr");
43009
+ return env.YARKR_CACHE_DIR || path9.join(env.XDG_CACHE_HOME || path9.join(os7.homedir(), ".cache"), "yarkr");
43298
43010
  }
43299
43011
  function fileTrialCache(dir) {
43300
43012
  const root = path9.join(dir, "evals");
@@ -43324,7 +43036,7 @@ function fileTrialCache(dir) {
43324
43036
  // ../../packages/evals/dist/execute.js
43325
43037
  import { randomUUID } from "crypto";
43326
43038
  import { mkdir as mkdir6, rm as rm2, stat as stat2 } from "fs/promises";
43327
- import os7 from "os";
43039
+ import os8 from "os";
43328
43040
  import path11 from "path";
43329
43041
 
43330
43042
  // ../../packages/evals/dist/graders/index.js
@@ -43561,11 +43273,12 @@ async function gradeReal(ctx) {
43561
43273
  }
43562
43274
 
43563
43275
  // ../../packages/evals/dist/runners/claude-code.js
43564
- function claudeCodeAdapter(env, fidelity = "native") {
43276
+ function claudeCodeAdapter(env) {
43277
+ const fidelity = "native";
43565
43278
  const pathVar = baseProcessEnv(env).PATH;
43566
43279
  let cachedVersion = null;
43567
43280
  return {
43568
- id: fidelity === "native" ? "claude-code" : "claude-code(equivalent)",
43281
+ id: "claude-code",
43569
43282
  fidelity,
43570
43283
  async available() {
43571
43284
  return await onPath("claude", pathVar) !== null;
@@ -43769,67 +43482,6 @@ function codexAdapter(env) {
43769
43482
  };
43770
43483
  }
43771
43484
 
43772
- // ../../packages/evals/dist/runners/custom-http.js
43773
- function customHttpAdapter(env) {
43774
- const url2 = env.customHttpUrl;
43775
- let files = {};
43776
- return {
43777
- id: "custom-http",
43778
- fidelity: "native",
43779
- async available() {
43780
- return !!url2;
43781
- },
43782
- async version() {
43783
- return env.customHttpVersion ?? "custom";
43784
- },
43785
- async install(_pkg, f) {
43786
- files = Object.fromEntries([...f].map(([k, v]) => [k, v.toString("base64")]));
43787
- },
43788
- async run(input2) {
43789
- if (!url2)
43790
- throw new Error("EVAL_CUSTOM_HTTP_URL is not set.");
43791
- const started = Date.now();
43792
- const res = await fetch(url2, {
43793
- method: "POST",
43794
- signal: AbortSignal.timeout(input2.limits.timeoutMs),
43795
- headers: {
43796
- "content-type": "application/json",
43797
- ...env.customHttpToken ? { authorization: `Bearer ${env.customHttpToken}` } : {}
43798
- },
43799
- body: JSON.stringify({
43800
- case: { id: input2.evalCase.id, prompt: input2.evalCase.prompt },
43801
- model: input2.model,
43802
- limits: input2.limits,
43803
- package: { name: input2.pkg.name, version: input2.pkg.label, files, encoding: "base64" }
43804
- })
43805
- });
43806
- if (!res.ok)
43807
- throw new Error(`custom-http runner returned ${res.status}: ${(await res.text()).slice(0, 300)}`);
43808
- const t = await res.json();
43809
- if (typeof t.output !== "string")
43810
- throw new Error("custom-http runner response is missing `output`.");
43811
- return {
43812
- transcript: {
43813
- harness: input2.harness,
43814
- harness_version: t.harness_version ?? await this.version(),
43815
- model: input2.model,
43816
- events: t.events ?? [{ role: "assistant", content: t.output, at_ms: 0 }],
43817
- output: t.output,
43818
- files: t.files ?? {},
43819
- tool_calls: t.tool_calls ?? [],
43820
- skills_loaded: t.skills_loaded ?? [],
43821
- turns: t.turns ?? 1,
43822
- tokens_in: t.tokens_in ?? 0,
43823
- tokens_out: t.tokens_out ?? 0,
43824
- cost_usd: t.cost_usd ?? 0,
43825
- latency_ms: t.latency_ms ?? Date.now() - started,
43826
- fidelity: "native"
43827
- }
43828
- };
43829
- }
43830
- };
43831
- }
43832
-
43833
43485
  // ../../packages/evals/dist/runners/opencode.js
43834
43486
  function opencodeAdapter(env) {
43835
43487
  const pathVar = baseProcessEnv(env).PATH;
@@ -44027,11 +43679,9 @@ function synthesize(input2, c, plan) {
44027
43679
 
44028
43680
  // ../../packages/evals/dist/runners/index.js
44029
43681
  var NATIVE = {
44030
- "claude-code": (env) => claudeCodeAdapter(env, "native"),
44031
- "claude-cowork": (env) => claudeCodeAdapter(env, "equivalent"),
43682
+ "claude-code": claudeCodeAdapter,
44032
43683
  codex: codexAdapter,
44033
- opencode: opencodeAdapter,
44034
- "custom-http": customHttpAdapter
43684
+ opencode: opencodeAdapter
44035
43685
  };
44036
43686
  var NativeUnavailableError = class extends Error {
44037
43687
  };
@@ -44080,7 +43730,7 @@ async function executeRun(input2) {
44080
43730
  const { candidate, baseline, env } = input2;
44081
43731
  const suites = input2.harnesses?.length || input2.models?.length ? narrowSuites(input2.suites, { harnesses: input2.harnesses, models: input2.models }) : input2.suites;
44082
43732
  const shards = planShards(suites, input2.mode, !!baseline);
44083
- const tmpRoot = input2.tmpRoot ?? path11.join(os7.tmpdir(), "yarkr-eval", runId);
43733
+ const tmpRoot = input2.tmpRoot ?? path11.join(os8.tmpdir(), "yarkr-eval", runId);
44084
43734
  const timeoutMs = input2.shardTimeoutMs ?? DEFAULT_SHARD_TIMEOUT_MS;
44085
43735
  const secrets = secretsOf(env);
44086
43736
  const adapterFor = adapterPicker(env, { native: input2.native, strict: input2.strict });
@@ -44227,7 +43877,8 @@ async function executeRun(input2) {
44227
43877
  const draft = buildReport({
44228
43878
  runId,
44229
43879
  mode: input2.mode,
44230
- status: "running",
43880
+ // Placeholder: the status depends on the report, and is set just below.
43881
+ status: "failed",
44231
43882
  baselineVersion: baseline?.label ?? null,
44232
43883
  candidateVersion: input2.candidateVersion ?? null,
44233
43884
  suites,
@@ -44406,7 +44057,7 @@ ${suites.join("\n")}
44406
44057
  import { execFile } from "child_process";
44407
44058
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
44408
44059
  import { mkdtemp, rm as rm3 } from "fs/promises";
44409
- import os8 from "os";
44060
+ import os9 from "os";
44410
44061
  import path12 from "path";
44411
44062
  import { promisify } from "util";
44412
44063
  var exec = promisify(execFile);
@@ -44465,7 +44116,7 @@ async function checkoutRef(dir, ref) {
44465
44116
  if (!commit) {
44466
44117
  throw new CliError(`git ref "${ref}" doesn't exist here.`, "In CI, check out with `fetch-depth: 0` or fetch the base branch first, e.g. `git fetch origin main`.", 2);
44467
44118
  }
44468
- const tmp = await mkdtemp(path12.join(os8.tmpdir(), "yarkr-baseline-"));
44119
+ const tmp = await mkdtemp(path12.join(os9.tmpdir(), "yarkr-baseline-"));
44469
44120
  const wt = path12.join(tmp, "tree");
44470
44121
  try {
44471
44122
  await exec("git", ["worktree", "add", "--detach", "--force", wt, commit], { cwd: top });
@@ -44489,21 +44140,21 @@ async function checkoutRef(dir, ref) {
44489
44140
  }
44490
44141
 
44491
44142
  // src/eval/registry.ts
44492
- async function registrySnapshot(api2, name, opts = {}) {
44493
- const pkg = await api2.pkgOrNull(name);
44143
+ async function registrySnapshot(api, name, opts = {}) {
44144
+ const pkg = await api.pkgOrNull(name);
44494
44145
  if (!pkg) return null;
44495
- const rel2 = await api2.resolve(name, { channel: opts.channel ?? "stable", range: opts.range });
44496
- const bundle = await api2.bundle(name, rel2.version);
44146
+ const rel2 = await api.resolve(name, { channel: opts.channel ?? "stable", range: opts.range });
44147
+ const bundle = await api.bundle(name, rel2.version);
44497
44148
  const files = new Map(bundle.files.map((f) => [f.path, Buffer.from(f.content, "base64")]));
44498
- const tree = buildTree([...files].map(([path16, bytes]) => ({ path: path16, bytes })));
44149
+ const tree = buildTree([...files].map(([path17, bytes]) => ({ path: path17, bytes })));
44499
44150
  return { snapshot: { name, label: bundle.version, contentHash: bundle.content_hash, manifest: bundle.manifest, tree }, files };
44500
44151
  }
44501
44152
  function registrySuiteResolver(getApi) {
44502
44153
  return {
44503
44154
  async resolve(name, range) {
44504
- const api2 = await getApi();
44505
- if (!api2) throw new SuiteError(`Shared suite ${name}@${range} needs a registry. Set YARKR_REGISTRY (and YARKR_TOKEN), or run \`yarkr login\`.`);
44506
- return registrySnapshot(api2, name, { range: range === "*" ? void 0 : range });
44155
+ const api = await getApi();
44156
+ if (!api) throw new SuiteError(`Shared suite ${name}@${range} needs a registry. Set YARKR_REGISTRY (and YARKR_TOKEN), or run \`yarkr login\`.`);
44157
+ return registrySnapshot(api, name, { range: range === "*" ? void 0 : range });
44507
44158
  }
44508
44159
  };
44509
44160
  }
@@ -44574,7 +44225,7 @@ function matchPackage(scan, pkg) {
44574
44225
  }
44575
44226
 
44576
44227
  // src/eval/upload.ts
44577
- import os9 from "os";
44228
+ import os10 from "os";
44578
44229
  function toImportedTrial(t, transcripts) {
44579
44230
  const { events: _events, tool_calls: _calls, files: _files, output: _output, ...summary } = t.transcript;
44580
44231
  return {
@@ -44596,7 +44247,7 @@ function runnerLabel(env = process.env) {
44596
44247
  const url2 = env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID ? ` ${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}` : "";
44597
44248
  return `github-actions${url2}`.slice(0, 200);
44598
44249
  }
44599
- return `yarkr-cli on ${env.CI ? "ci" : os9.platform()}`;
44250
+ return `yarkr-cli on ${env.CI ? "ci" : os10.platform()}`;
44600
44251
  }
44601
44252
  async function githubOidcToken(env = process.env, fetchImpl = fetch) {
44602
44253
  const url2 = env.ACTIONS_ID_TOKEN_REQUEST_URL;
@@ -44830,15 +44481,15 @@ async function resolveBaseline(dir, pkg, opts, getApi, say) {
44830
44481
  }
44831
44482
  }
44832
44483
  if (spec === "registry" || spec === "auto") {
44833
- const api2 = await getApi();
44834
- if (!api2 || spec === "auto" && !pkg.named) {
44484
+ const api = await getApi();
44485
+ if (!api || spec === "auto" && !pkg.named) {
44835
44486
  if (spec === "registry") throw fail("--baseline registry needs a registry.", "Set YARKR_REGISTRY and YARKR_TOKEN, or run `yarkr login`.");
44836
44487
  if (say) ui.step("No baseline: no registry configured or no package name. Use --baseline git:<ref> or <dir> to compare.");
44837
44488
  return noBaseline();
44838
44489
  }
44839
44490
  let rel2;
44840
44491
  try {
44841
- rel2 = await registrySnapshot(api2, pkg.snapshot.name);
44492
+ rel2 = await registrySnapshot(api, pkg.snapshot.name);
44842
44493
  } catch (err) {
44843
44494
  if (spec === "registry") throw fail(`Couldn't fetch the registry baseline for ${pkg.snapshot.name}: ${err.message}`);
44844
44495
  if (say) ui.step(`No baseline: ${err.message}`);
@@ -44859,8 +44510,8 @@ async function resolveBaseline(dir, pkg, opts, getApi, say) {
44859
44510
  } };
44860
44511
  }
44861
44512
  async function upload(dir, pkg, run3, mode, getApi, env, say) {
44862
- const api2 = await getApi();
44863
- if (!api2) throw fail("--report needs a registry and a token.", "Set YARKR_REGISTRY and YARKR_TOKEN, or run `yarkr login`.");
44513
+ const api = await getApi();
44514
+ if (!api) throw fail("--report needs a registry and a token.", "Set YARKR_REGISTRY and YARKR_TOKEN, or run `yarkr login`.");
44864
44515
  if (run3.trials.length > 5e3) throw fail(`${run3.package}: ${run3.trials.length} trials are more than one upload takes (5000). Narrow the run with --suite, --harness or --model.`);
44865
44516
  const git2 = pkg.named ? null : await gitInfo(dir);
44866
44517
  if (!pkg.named && !git2) throw fail(`--report can't tell which package ${pkg.asset.name} is.`, "Pass --package <namespace/name>, or run inside a git checkout with an origin remote.");
@@ -44877,7 +44528,7 @@ async function upload(dir, pkg, run3, mode, getApi, env, say) {
44877
44528
  }),
44878
44529
  secretsOf(env)
44879
44530
  );
44880
- const res = await api2.importEvalRun(body).catch((err) => {
44531
+ const res = await api.importEvalRun(body).catch((err) => {
44881
44532
  throw fail(`Upload failed: ${err.message}`, err instanceof CliError ? err.hint : void 0);
44882
44533
  });
44883
44534
  if (say) ui.ok(`Uploaded (${res.trust}): ${res.url}`);
@@ -45059,8 +44710,165 @@ async function gradeCase(c, caseDir, pkgRoot, runScripts) {
45059
44710
  }
45060
44711
  }
45061
44712
 
44713
+ // src/commands/guide.ts
44714
+ import { mkdir as mkdir8, stat as stat3, writeFile as writeFile7 } from "fs/promises";
44715
+ import path16 from "path";
44716
+
44717
+ // ../../packages/core/dist/guides/content.generated.js
44718
+ var GUIDE_CONTENT = {
44719
+ "create-ark": '---\nname: create-ark\ndescription: Build a Yarkr ark (a set of skills, agents or MCP configs handed to a team) for a given topic and type. Use when asked to create, assemble or curate an ark, or to collect a topic\'s skills, agents or MCP servers for a team in Yarkr.\n---\n\n# Creating an ark from a topic and a type\n\nYou get two inputs and build one [ark](https://github.com/posterus-ltd/yarkr-public/blob/main/docs/arks.md) that hands out packages on that topic.\n\n| Input | Example | Meaning |\n| --- | --- | --- |\n| **topic** | `invoice processing`, `frontend testing` | what the ark is about |\n| **type** | `skill`, `agent` or `mcp` | the kind of package it holds. `mcp` is a **toolset** in Yarkr (`--kind toolset`, `kind: toolset`) |\n\nPlus, from the user or their config: the **org** (its slug is its GitHub login, lowercased), and whether the ark should\nbe **public**. Ask for the org if you can\'t tell; default to private.\n\nAn ark only points at packages. Packages come from GitHub ([github.md](https://github.com/posterus-ltd/yarkr-public/blob/main/docs/github.md)): there is no way to upload content to\nYarkr directly. So the work is: find what exists, write what\'s missing in a repo, then assemble the ark.\n\n```\n1. plan \u2500\u2500\u25B6 2. find existing \u2500\u2500\u25B6 3. author missing (PR) \u2500\u2500\u25B6 4. wait for index \u2500\u2500\u25B6 5. create ark + items \u2500\u2500\u25B6 6. verify\n```\n\n---\n\n## 1. Plan\n\n- **Ark name:** a slug, `^[a-z0-9][a-z0-9-]*$`, short and named after the topic: `invoice-processing`, not\n `ark-for-invoices`. It must be unique in the org.\n- **Description:** one sentence, at most 500 characters, saying who it\'s for and what they get.\n- **Scope:** aim for 3 to 8 packages. Every skill in an ark goes into every member\'s context; too many makes agents\n worse. Split a broad topic into several arks rather than one big one.\n- List the packages you expect to need, each with a one-line purpose, before searching.\n\n## 2. Find what already exists\n\nReuse beats writing. Search the registry for each planned package:\n\n```bash\nyarkr search "<topic words>" --kind <skill|agent|toolset> --json\nyarkr info <namespace>/<name> # source repo, targets, releases, eval results\n```\n\nOver MCP, `search_packages` with `scope: "all"`, then `get_skill` / `get_agent` / `get_toolset` to read one.\n\nKeep a package when:\n\n- its description and body actually cover the planned purpose (read it, don\'t trust the name);\n- the org can put it in an ark: **the org\'s own namespace, or any public package**;\n- for a public ark: **it is public**. A public ark can hold nothing else;\n- it isn\'t yanked or archived, and has evals that pass if any are shown.\n\n## 3. Author what\'s missing\n\nWrite each missing package in one of the org\'s **connected** repos (check **Settings \u2192 GitHub** / `/orgs/<org>/github`;\na repo that isn\'t connected is never indexed). For a public ark the repo must be public. Open **one pull request** with\nall new packages, and describe the ark they\'re for. Merging is publishing; don\'t push to the default branch yourself\nunless the user tells you to.\n\nPackage names come from the folder or `name:` frontmatter, slugified, and publish as `<org>/<name>`. Check that the name\ndoesn\'t already exist in the org.\n\n### Type `skill`\n\nA folder with `SKILL.md`. Everything in the folder ships with it. This guide is itself one.\n\n```\nskills/<name>/\n SKILL.md\n references/\u2026 # optional: long docs the skill loads on demand\n scripts/\u2026 # optional\n evals/core.yaml # optional but recommended, see https://github.com/posterus-ltd/yarkr-public/blob/main/docs/evals.md\n```\n\n```markdown\n---\nname: <name>\ndescription: <what it does and when to use it. Harnesses decide whether to load the skill from this line alone, so name the trigger situations.>\n---\n\n# <Title>\n\n<Steps the agent follows. Concrete and imperative. Point to references/ for anything long.>\n```\n\n`description` is required; without it the skill isn\'t indexed.\n\n### Type `agent`\n\nOne markdown file under `agents/` (or `.claude/agents/`).\n\n```markdown\n---\nname: <name>\ndescription: <when to hand work to this agent>\nmodel: <optional>\ntools: Read, Grep, Bash # optional\nskills: <skill-name>, other-org/skill # optional: dependencies, installed with the agent\n---\n\n<System prompt: role, what it does, how it decides, what it returns.>\n```\n\n- `description` is required; a file without it is treated as a plain doc.\n- A bare name in `skills:` means a skill in the same org. Use `ns/name` for another namespace. A skill the agent needs\n that doesn\'t exist yet is part of this same PR.\n- Add the agent\'s skills to the ark too, or leave them to the dependency: `yarkr sync` installs dependencies either way.\n\n### Type `mcp` (toolset)\n\nOne toolset per folder, in Claude Code\'s `.mcp.json` format:\n\n```\ntoolsets/<name>/\n .mcp.json\n README.md # first paragraph becomes the description\n```\n\n```json\n{\n "mcpServers": {\n "<server>": {\n "type": "http",\n "url": "https://mcp.example.com/mcp",\n "headers": { "Authorization": "Bearer ${EXAMPLE_TOKEN}" }\n }\n }\n}\n```\n\n- **No literal secrets.** Values in `env` and `headers` must be `${VAR}` (becomes `${env:VAR}`), `${VAR:-default}`\n or `${vault:path#key}`. A literal value is refused.\n- Prefer servers you can verify exist: the vendor\'s official server, or a well-known package for `stdio` (`npx -y \u2026`).\n Never invent a URL or package name. If you can\'t confirm one, stop and ask.\n- Document in the README which variables each user must set.\n\n### Optional `yarkr.yaml`\n\nNext to any asset, for what can\'t be inferred:\n\n```yaml\ntargets: [claude-code, codex] # default: every harness\ntags: [<topic>, \u2026] # helps search; use the topic\nevals: [evals/core.yaml]\n```\n\n## 4. Wait for the index\n\nAfter the PR merges, the push is indexed in the background, usually within a minute. Each package\'s first release is a\nsnapshot of the default branch (`0.0.0-main.\u2026`) unless a tag says otherwise.\n\n```bash\nyarkr info <org>/<name> # repeat until it shows a stable version\n```\n\nIf it never appears, check the repo\'s row on the org\'s GitHub page: detection problems (missing description, literal\nsecret, name clash) are listed there. Fix them in a new PR.\n\n## 5. Create the ark and add items\n\nArk changes need an **org admin** and a **read and write** token. There\'s no CLI command for this; use the web app\n(**Arks \u2192 your org**) or the API. The base URL is the registry (`yarkr whoami` prints it; the hub is\n`https://api.yarkr.com/functions/v1/api`).\n\n```bash\nAPI=https://api.yarkr.com/functions/v1/api/api/v1\nAUTH="Authorization: Bearer $YARKR_TOKEN"\n\n# Create\ncurl -sf -X POST "$API/arks/<org>" -H "$AUTH" -H \'content-type: application/json\' \\\n -d \'{"name":"<ark>","description":"<one sentence>"}\'\n\n# One call per package\ncurl -sf -X PUT "$API/arks/<org>/<ark>/items/<ns>/<name>" -H "$AUTH" -H \'content-type: application/json\' \\\n -d \'{"range":"^1","required":true}\'\n```\n\nItem body:\n\n| Field | Use |\n| --- | --- |\n| `range` | Semver range. Use `^<major>` for tagged packages. Omit it (and `channel`) to follow `stable`, which suits new snapshot-only packages |\n| `channel` | Instead of a range, e.g. `stable` |\n| `required` | `true` for the core of the topic; `false` for extras members may exclude |\n| `config` | Toolsets only: per-ark overrides, `{"servers": {"<server>": {"env": {\u2026}, "headers": {\u2026}, "args": [\u2026], "url": "\u2026"}}}`. Secret values must be `${env:\u2026}` or `${vault:\u2026}` |\n\nThe server rejects an item that the org can\'t use, a private package in a public ark, a bad range, a literal secret,\nor a range that conflicts with another ark sharing a member. Read the error and fix the cause; don\'t retry blindly.\n\nThen, only if asked:\n\n```bash\n# Make it public (public packages only)\ncurl -sf -X PATCH "$API/arks/<org>/<ark>" -H "$AUTH" -H \'content-type: application/json\' -d \'{"public":true}\'\n\n# Add members (they must already belong to the org)\ncurl -sf -X POST "$API/arks/<org>/<ark>/members" -H "$AUTH" -H \'content-type: application/json\' -d \'{"email":"\u2026"}\'\n```\n\n## 6. Verify\n\n```bash\ncurl -sf "$API/arks/<org>/<ark>" -H "$AUTH" # items, ranges, description\nyarkr ark lint <org>/<ark> --strict # fix every error and warning it reports\nyarkr sync <org>/<ark> --dry-run --harness claude-code # resolves every item without writing files\n```\n\nFor a public ark, also check `yarkr arks` lists it.\n\n## Report back\n\nTell the user:\n\n- the ark (`<org>/<ark>`), its description and whether it\'s public;\n- each item: reused or new, range, required or optional, one line on why it\'s there;\n- the PR link for new packages, and whether it\'s merged;\n- what\'s left for them: merging the PR, adding members, setting the toolset variables listed in the READMEs, and how\n people get it (`yarkr sync <org>/<ark>`, or `yarkr mcp add <org>/<ark>`).\n\n## Don\'ts\n\n- Don\'t put literal secrets anywhere: repo, ark config or PR description.\n- Don\'t add packages you haven\'t read.\n- Don\'t make an ark public, add members, or merge a PR unless the user asked.\n- Don\'t pad the ark to reach a number. Three good packages beat eight vague ones.\n'
44720
+ };
44721
+
44722
+ // ../../packages/core/dist/guides/index.js
44723
+ var GUIDE_NAMES = Object.keys(GUIDE_CONTENT);
44724
+ var descriptionOf = (md2) => /^---\n[\s\S]*?^description:\s*(.+)$/m.exec(md2)?.[1]?.trim() ?? "";
44725
+ var isGuideName = (n) => Object.hasOwn(GUIDE_CONTENT, n);
44726
+ function getGuide(name) {
44727
+ const content = GUIDE_CONTENT[name];
44728
+ return { name, description: descriptionOf(content), content };
44729
+ }
44730
+ var ARK_TYPES = { skill: "skill", agent: "agent", mcp: "toolset" };
44731
+ function createArkPrompt(inputs = {}) {
44732
+ const body = GUIDE_CONTENT["create-ark"].replace(/^---\n[\s\S]*?\n---\n+/, "");
44733
+ const given = [
44734
+ inputs.topic && `- topic: ${inputs.topic}`,
44735
+ inputs.type && `- type: ${inputs.type} (package kind \`${ARK_TYPES[inputs.type]}\`)`,
44736
+ inputs.org && `- org: ${inputs.org}`
44737
+ ].filter(Boolean);
44738
+ return given.length ? `${body.trimEnd()}
44739
+
44740
+ ---
44741
+
44742
+ Your inputs:
44743
+
44744
+ ${given.join("\n")}
44745
+ ` : body;
44746
+ }
44747
+
44748
+ // src/commands/guide.ts
44749
+ async function guide(name, opts) {
44750
+ if (!name) {
44751
+ ui.table(GUIDE_NAMES.map((n) => [ui.bold(n), getGuide(n).description]), ["guide", "description"]);
44752
+ ui.step(`${ui.code("yarkr guide <name>")} prints one; ${ui.code("--out <dir>")} saves it as <dir>/SKILL.md.`);
44753
+ return;
44754
+ }
44755
+ if (!isGuideName(name)) throw new CliError(`No guide named ${name}.`, `Guides: ${GUIDE_NAMES.join(", ")}.`);
44756
+ const text = name === "create-ark" && (opts.topic || opts.type) ? createArkPrompt({ topic: opts.topic, type: opts.type }) : getGuide(name).content;
44757
+ if (!opts.out) return void process.stdout.write(text);
44758
+ const isDir = opts.out.endsWith("/") || opts.out.endsWith(path16.sep) || (await stat3(opts.out).catch(() => null))?.isDirectory();
44759
+ const file2 = path16.resolve(isDir ? path16.join(opts.out, "SKILL.md") : opts.out);
44760
+ await mkdir8(path16.dirname(file2), { recursive: true });
44761
+ await writeFile7(file2, text);
44762
+ ui.ok(`Wrote ${name} to ${file2}`);
44763
+ }
44764
+
44765
+ // src/commands/lint.ts
44766
+ var import_semver3 = __toESM(require_semver2(), 1);
44767
+ var MAX_ITEMS = 8;
44768
+ var EVAL_PASS = 0.8;
44769
+ var SECRETISH = /(token|secret|password|passwd|api[_-]?key|private[_-]?key|credential|auth)/i;
44770
+ var isTagged = (v) => !import_semver3.default.prerelease(v);
44771
+ async function lintArk(api, ref) {
44772
+ const ark = await api.ark(ref);
44773
+ const findings = [];
44774
+ const add2 = (severity, rule, message, pkg) => findings.push({ severity, rule, message, ...pkg ? { package: pkg } : {} });
44775
+ if (!ark.description.trim()) add2("warning", "description", "The ark has no description. Say in one sentence who it's for and what they get.");
44776
+ if (!ark.items.length) add2("error", "empty", "The ark has no items, so it gives its members nothing.");
44777
+ if (ark.items.length > MAX_ITEMS) add2("warning", "size", `The ark has ${ark.items.length} items. Past ${MAX_ITEMS}, agents get worse; split it into narrower arks.`);
44778
+ const details = /* @__PURE__ */ new Map();
44779
+ const detail = async (name) => {
44780
+ if (!details.has(name)) details.set(name, await api.pkgOrNull(name));
44781
+ return details.get(name);
44782
+ };
44783
+ const releases = await Promise.all(ark.items.map((item) => lintItem(api, ark, item, detail, add2)));
44784
+ if (ark.public) {
44785
+ for (const rel2 of releases) {
44786
+ if (!rel2) continue;
44787
+ const deps = Object.keys({ ...rel2.manifest.depends.skills, ...rel2.manifest.depends.toolsets, ...rel2.manifest.depends.agents });
44788
+ for (const dep of deps) {
44789
+ const d = await detail(dep);
44790
+ if (!d) add2("error", "dependency", `Depends on ${dep}, which doesn't exist or you can't read.`, rel2.package);
44791
+ else if (d.classification !== "public") add2("error", "public-dependency", `Depends on ${dep}, which isn't public, so people outside the org can't install it.`, rel2.package);
44792
+ }
44793
+ }
44794
+ }
44795
+ const order = { error: 0, warning: 1 };
44796
+ findings.sort((a, b) => order[a.severity] - order[b.severity] || (a.package ?? "").localeCompare(b.package ?? ""));
44797
+ return { ark, findings };
44798
+ }
44799
+ async function lintItem(api, ark, item, detail, add2) {
44800
+ const name = item.package;
44801
+ const p = await detail(name);
44802
+ if (!p) {
44803
+ add2("error", "missing", "The package doesn't exist, or you can't read it.", name);
44804
+ return null;
44805
+ }
44806
+ if (p.taken_down) {
44807
+ add2("error", "taken-down", `The package was taken down: ${p.taken_down.reason}`, name);
44808
+ return null;
44809
+ }
44810
+ if (ark.public && p.classification !== "public") add2("error", "public-item", `It's ${p.classification}, but a public ark may only hold public packages.`, name);
44811
+ if (!p.description.trim()) add2("warning", "item-description", "The package has no description, so members can't tell what it's for.", name);
44812
+ if (item.range && !import_semver3.default.validRange(item.range)) {
44813
+ add2("error", "range", `"${item.range}" isn't a semver range.`, name);
44814
+ return null;
44815
+ }
44816
+ let rel2 = null;
44817
+ try {
44818
+ rel2 = await api.resolve(name, { channel: item.channel ?? "stable", range: item.range ?? void 0 });
44819
+ } catch (err) {
44820
+ const what = item.range ? `range ${item.range}` : `channel ${item.channel ?? "stable"}`;
44821
+ add2("error", "unresolvable", `Nothing matches ${what}, so \`yarkr sync\` fails for every member (${err.message})`, name);
44822
+ }
44823
+ const live = p.releases.filter((r) => !r.yanked).map((r) => r.version);
44824
+ const newest = import_semver3.default.maxSatisfying(live.filter(isTagged), "*");
44825
+ if (!item.range && !item.channel && newest) add2("warning", "unpinned", `Follows stable although it has tagged releases. Pin a range like ^${import_semver3.default.major(newest)} so a new major doesn't reach members unreviewed.`, name);
44826
+ if (item.range && rel2 && newest && import_semver3.default.gt(newest, rel2.version) && !import_semver3.default.satisfies(newest, item.range)) {
44827
+ add2("warning", "outdated", `${newest} is out, but the range ${item.range} holds members at ${rel2.version}.`, name);
44828
+ }
44829
+ const ev = p.reported_eval;
44830
+ if (ev && (ev.status === "failed" || ev.pass_rate !== null && ev.pass_rate < EVAL_PASS)) {
44831
+ const rate = ev.pass_rate === null ? "" : ` (${Math.round(ev.pass_rate * 100)}% passed)`;
44832
+ add2("warning", "evals", `Its latest eval run on ${ev.version} failed${rate}.`, name);
44833
+ }
44834
+ const servers = item.config.servers ?? {};
44835
+ if (Object.keys(servers).length && item.kind !== "toolset") add2("error", "config", "Has server config, but only toolsets use it.", name);
44836
+ for (const [server, o] of Object.entries(servers)) {
44837
+ if (rel2?.manifest.toolset && !rel2.manifest.toolset.servers.some((s) => s.name === server)) {
44838
+ add2("warning", "config", `Configures server "${server}", which ${rel2.version} doesn't have (it has ${rel2.manifest.toolset.servers.map((s) => s.name).join(", ") || "none"}).`, name);
44839
+ }
44840
+ for (const field of ["env", "headers"]) {
44841
+ for (const [k, v] of Object.entries(o[field] ?? {})) {
44842
+ if (SECRETISH.test(k) && !SECRET_REF.test(v)) add2("error", "secret", `${server}.${field}.${k} holds a literal value. Use \${env:\u2026} or \${vault:path#key}.`, name);
44843
+ }
44844
+ }
44845
+ }
44846
+ return rel2;
44847
+ }
44848
+ async function arkLint(ref, opts) {
44849
+ const api = new Api(await resolveConnection({ dir: process.cwd() }));
44850
+ const { ark, findings } = await lintArk(api, ref);
44851
+ const errors = findings.filter((f) => f.severity === "error").length;
44852
+ const warnings = findings.length - errors;
44853
+ if (opts.json) console.log(JSON.stringify({ ark: ark.ref, errors, warnings, findings }, null, 2));
44854
+ else {
44855
+ ui.heading(`${ark.ref} ${ui.dim(`${ark.items.length} item(s)${ark.public ? ", public" : ""}`)}`);
44856
+ for (const f of findings) {
44857
+ const line = `${f.package ? `${ui.bold(f.package)} ` : ""}${f.message} ${ui.dim(`[${f.rule}]`)}`;
44858
+ if (f.severity === "error") ui.error(line);
44859
+ else ui.warn(line);
44860
+ }
44861
+ if (!findings.length) ui.ok("No problems found.");
44862
+ }
44863
+ if (errors || opts.strict && warnings) {
44864
+ throw new CliError(`${errors} error(s), ${warnings} warning(s) in ${ark.ref}.`, errors ? void 0 : "Warnings fail the lint because of --strict.");
44865
+ }
44866
+ if (warnings && !opts.json) ui.info(`${warnings} warning(s). Pass --strict to fail on them.`);
44867
+ return findings;
44868
+ }
44869
+
45062
44870
  // src/index.ts
45063
- var VERSION = true ? "0.1.0" : "0.0.0-dev";
44871
+ var VERSION = true ? "0.1.5" : "0.0.0-dev";
45064
44872
  var act = (fn) => async (...a) => {
45065
44873
  await fn(...a);
45066
44874
  };
@@ -45069,30 +44877,23 @@ function buildProgram() {
45069
44877
  const program2 = new Command().name("yarkr").description("Yarkr: install your org's skills, agents and MCP configs into every harness.").version(VERSION).option("-q, --quiet", "only print warnings and errors").hook("preAction", (cmd) => {
45070
44878
  ui.quiet = !!cmd.opts().quiet;
45071
44879
  });
45072
- program2.command("login").description("store an API token for a registry in ~/.yarkrrc").option("--registry <url>").option("--token <token>", "token (or pipe it on stdin)").action(act(login));
45073
- program2.command("logout").description("forget the stored token").option("--registry <url>").action(act(logout));
44880
+ program2.command("login").description("sign in in the browser and keep a token in your keychain (or ~/.yarkrrc)").option("--registry <url>").option("--token <token>", "use this token instead of the browser (or pipe it on stdin)").option("--no-browser", "print a link to open on any device, then paste the token it shows").action(act(login));
44881
+ program2.command("logout").description("forget the stored token and revoke it on the server").option("--registry <url>").action(act(logout));
45074
44882
  program2.command("whoami").description("show who you're signed in as").option("--registry <url>").action(act(whoami));
45075
- program2.command("init").description("create yarkr.json (and .yarkrrc), detecting the harnesses used here").option("--harness <ids...>", "harnesses to target").option("--registry <url>", "registry to pin in .yarkrrc").option("--user", "install into your home directory instead of this project").option("--ark <refs...>", "install from these arks too, e.g. acme/finance global/essentials @me").option("--force", "overwrite an existing yarkr.json").action(act(init2));
44883
+ program2.command("init").description("create yarkr.json (and .yarkrrc), detecting the harnesses used here").option("--harness <ids...>", "harnesses to target").option("--registry <url>", "registry to pin in .yarkrrc").option("--user", "install into your home directory instead of this project").option("--ark <refs...>", "install from these arks too, e.g. acme/finance @me, or another org's public ark").option("--force", "overwrite an existing yarkr.json").action(act(init2));
45076
44884
  program2.command("search").argument("[query]").description("find packages").addOption(new Option("--kind <kind>").choices(["skill", "agent", "toolset"])).option("--target <harness>").option("--json").action(act(search));
45077
44885
  program2.command("info").argument("<package>").description("show a package's channels, releases and owners").option("--json").action(act(info));
45078
- program2.command("ark").argument("[arks...]", "limit to these arks, e.g. acme/finance or @me").description("show what your arks give you").option("--json").action(act((refs, o) => showArk(refs, o)));
45079
- const col = program2.command("collection").alias("col").description("your named sets of arks, published as pinned versions others use as @<handle>/<name>");
45080
- const visibilityFlags = (c) => c.option("-d, --description <text>").addOption(new Option("--visibility <v>", "who can use it").choices(["private", "org", "public"])).option("--public", "same as --visibility public: anyone, without signing in").option("--org <slug>", "org whose members can use it (implies --visibility org)");
45081
- col.command("list").description("list your collections").option("--json").action(act(collectionList));
45082
- col.command("show").argument("<name>", "yours by name, or a published version: @<handle>/<name>[@<version>]").description("show a collection").option("--json").action(act(collectionShow));
45083
- visibilityFlags(col.command("create").argument("<name>").description("create an empty collection (private unless you say otherwise)")).action(act(collectionCreate));
45084
- visibilityFlags(col.command("edit").argument("<name>").description("change a collection's description or visibility")).action(act(collectionEdit));
45085
- col.command("delete").argument("<name>").description("delete a collection and all its versions").action(act(collectionDelete));
45086
- col.command("add").argument("<name>").argument("<arks...>", "@me, <org>/<ark>, global/<ark>, or a public ark").description("put arks in a collection (takes effect when you publish)").action(act(collectionAdd));
45087
- col.command("remove").argument("<name>").argument("<arks...>").description("take arks out of a collection (takes effect when you publish)").action(act(collectionRemove));
45088
- col.command("publish").argument("<name>").description("freeze the collection's arks into the next version, every package pinned").option("-m, --message <note>").option("--json").action(act(collectionPublish));
45089
- col.command("arks").description("list public arks you can put in a collection").option("--json").action(act(collectionArks));
44886
+ const arkCmd = program2.command("ark").argument("[arks...]", "limit to these arks, e.g. acme/finance or @me").description("show what your arks give you").option("--json").action(act((refs, o) => showArk(refs, o)));
44887
+ arkCmd.command("lint").argument("<ark>", "an ark you can read, e.g. acme/finance").description("check an ark's items: missing or private packages, ranges that resolve to nothing, literal secrets, failing evals").option("--strict", "exit 1 on warnings too").option("--json").action(act((ref, o) => arkLint(ref, { ...o, json: o.json || !!arkCmd.opts().json })));
44888
+ program2.command("arks").description("list public arks: curated sets anyone can sync or connect over MCP").option("--json").action(act(publicArks));
44889
+ program2.command("guide").argument("[name]", "a guide, e.g. create-ark; omit to list them").description("print instructions for your agent, e.g. how to build an ark for a topic").option("-o, --out <path>", "write to a file, or <dir>/SKILL.md for a directory (e.g. ~/.claude/skills/create-ark/)").option("--topic <topic>", "create-ark: what the ark is about").addOption(new Option("--type <type>", "create-ark: the kind of package it holds").choices(["skill", "agent", "mcp"])).action(act(guide));
45090
44890
  syncFlags(program2.command("add").argument("<packages...>", "namespace/name[@range]").description("add packages to yarkr.json (outside a project: to your personal ark, or your user config when signed out) and sync").option("--no-sync", "only edit yarkr.json")).action(act((pkgs, o) => add(pkgs, { ...o, noSync: o.sync === false })));
45091
- syncFlags(program2.command("remove").argument("<packages...>").description("remove packages (or opt out of optional ark items) and delete their files").option("--no-sync", "only edit yarkr.json")).action(act((pkgs, o) => remove(pkgs, { ...o, noSync: o.sync === false })));
44891
+ syncFlags(program2.command("remove").argument("<packages...>", "packages, or arks listed in yarkr.json").description("remove packages or arks (or opt out of optional ark items) and delete their files").option("--no-sync", "only edit yarkr.json")).action(act((pkgs, o) => remove(pkgs, { ...o, noSync: o.sync === false })));
45092
44892
  syncFlags(program2.command("update").argument("[packages...]").description("move packages to the newest release their range allows")).action(act((pkgs, o) => update(pkgs, o)));
45093
44893
  syncFlags(
45094
- program2.command("sync").argument("[collections...]", "collections to add first: @<handle>/<name> (latest) or @<handle>/<name>@<version>; public ones need no sign-in").description("install locked releases into each harness's native layout (outside a project: your arks, into your home directory)")
44894
+ program2.command("sync").argument("[arks...]", "arks to add first: <org>/<ark>, yours or another org's public ark (which needs no sign-in)").description("install locked releases into each harness's native layout (outside a project: your arks, into your home directory)")
45095
44895
  ).action(act((refs, o) => sync(o, void 0, refs)));
44896
+ program2.command("yank").argument("<release>", "namespace/name@version").description("yank a release (org admins): ranges and stable skip it, exact pins still install it").option("--reason <text>", "why, shown to everyone who installed it").option("--undo", "un-yank it").action(act(yank));
45096
44897
  program2.command("diff").argument("[package]").description("show local edits to synced files").option("--name-only").action(act(diff));
45097
44898
  const evalCmd = program2.command("eval").argument("[dir]", "package folder", ".").description("check a package's eval suites (--local), or run them against a baseline (eval run)").option("--local", "validate the package and suites, and grade recorded outputs; no model calls").option("--outputs <dir>", "recorded outputs: <dir>/<suite>/<case>/{output.txt,transcript.json,files/}").option("--run-scripts", "run script graders (executes commands from the suite)").option("--json [file]", "print JSON (with run: write it to <file>)").action(
45098
44899
  act(async (dir, o) => {
@@ -45109,7 +44910,7 @@ function buildProgram() {
45109
44910
  );
45110
44911
  const mcp = program2.command("mcp").description("connect harnesses to Yarkr's MCP server: one connection per org or team");
45111
44912
  const mcpFlags = (c) => syncFlags(c).option("--token-env <name>", "environment variable holding the token for this connection (default YARKR_TOKEN)").option("--no-sync", "only edit the config");
45112
- mcpFlags(mcp.command("add").argument("<scopes...>", "acme (an org), acme/finance (a team), global/<ark>, @me, @<handle>/<collection>[@<version>], all or public").description("add MCP connections and write them into each harness's MCP config")).action(
44913
+ mcpFlags(mcp.command("add").argument("<scopes...>", "acme (an org), acme/finance (a team, or a public ark), all or public").description("add MCP connections and write them into each harness's MCP config")).action(
45113
44914
  act((scopes, o) => mcpAdd(scopes, { ...o, noSync: o.sync === false }))
45114
44915
  );
45115
44916
  mcpFlags(mcp.command("remove").argument("<scopes...>").description("remove MCP connections from every harness")).action(act((scopes, o) => mcpRemove(scopes, { ...o, noSync: o.sync === false })));