@warmhub/cli 0.67.0 → 0.69.0

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 (2) hide show
  1. package/dist/wh.js +1301 -552
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -18478,10 +18478,21 @@ var import_awaitAsyncGenerator = __toESM(require_awaitAsyncGenerator(), 1);
18478
18478
  var import_wrapAsyncGenerator = __toESM(require_wrapAsyncGenerator(), 1);
18479
18479
  var import_objectSpread29 = __toESM(require_objectSpread2(), 1);
18480
18480
  // ../../packages/rules/src/builtin-shapes.ts
18481
- var BUILTIN_SHAPE_NAMES = ["Pair", "Triple", "Set", "List"];
18481
+ var BUILTIN_SHAPE_NAMES = ["Pair", "Set", "List"];
18482
+ var RETIRED_COLLECTION_SHAPE_NAMES = ["Triple"];
18483
+ var RESERVED_COLLECTION_SHAPE_NAMES = [
18484
+ ...BUILTIN_SHAPE_NAMES,
18485
+ ...RETIRED_COLLECTION_SHAPE_NAMES
18486
+ ];
18482
18487
  function isBuiltinCollectionShape(name) {
18483
18488
  return BUILTIN_SHAPE_NAMES.includes(name);
18484
18489
  }
18490
+ function isRetiredCollectionShape(name) {
18491
+ return RETIRED_COLLECTION_SHAPE_NAMES.includes(name);
18492
+ }
18493
+ function isReservedCollectionShape(name) {
18494
+ return RESERVED_COLLECTION_SHAPE_NAMES.includes(name);
18495
+ }
18485
18496
  var BUILTIN_CONTENT_SHAPE_NAMES = ["Content"];
18486
18497
  var STORED_CONTENT_NAMES = ["Readme", "Agents"];
18487
18498
  var SYNTHESIZED_CONTENT_NAMES = ["LlmsTxt"];
@@ -18499,14 +18510,6 @@ var BUILTIN_SHAPE_DEFS = {
18499
18510
  },
18500
18511
  description: "An ordered pair of two things"
18501
18512
  },
18502
- Triple: {
18503
- fields: {
18504
- first: { type: "wref", description: "First member of the triple" },
18505
- second: { type: "wref", description: "Second member of the triple" },
18506
- third: { type: "wref", description: "Third member of the triple" }
18507
- },
18508
- description: "An ordered triple of three things"
18509
- },
18510
18513
  Set: {
18511
18514
  fields: {
18512
18515
  members: [{ type: "wref", description: "Members of the set" }]
@@ -18553,36 +18556,6 @@ var COMMIT_OPERATION_KINDS = [
18553
18556
  "assertion",
18554
18557
  "collection"
18555
18558
  ];
18556
- function isCollectionAbout(value) {
18557
- if (typeof value !== "object" || value === null || Array.isArray(value))
18558
- return false;
18559
- const record = value;
18560
- const keys = Object.keys(record);
18561
- if (keys.length !== 1)
18562
- return false;
18563
- const key = keys[0];
18564
- if (key !== "pair" && key !== "triple" && key !== "set" && key !== "list")
18565
- return false;
18566
- return Array.isArray(record[key]);
18567
- }
18568
- function collectionAboutType(about) {
18569
- if ("pair" in about)
18570
- return "pair";
18571
- if ("triple" in about)
18572
- return "triple";
18573
- if ("set" in about)
18574
- return "set";
18575
- return "list";
18576
- }
18577
- function collectionAboutMembers(about) {
18578
- if ("pair" in about)
18579
- return about.pair;
18580
- if ("triple" in about)
18581
- return about.triple;
18582
- if ("set" in about)
18583
- return about.set;
18584
- return about.list;
18585
- }
18586
18559
  function splitLocalPath(name) {
18587
18560
  const idx = name.indexOf("/");
18588
18561
  if (idx === -1)
@@ -18645,6 +18618,26 @@ function stableJsonEquals(left, right) {
18645
18618
  return stableJson(left) === stableJson(right);
18646
18619
  }
18647
18620
 
18621
+ // ../../packages/rules/src/subscribable-events.ts
18622
+ var COMMIT_EVENT_TYPE = "commit";
18623
+ var REPO_RENAMED_EVENT_TYPE = "repo.renamed";
18624
+ var ORG_RENAMED_EVENT_TYPE = "org.renamed";
18625
+ var THING_RENAMED_EVENT_TYPE = "thing.renamed";
18626
+ var SHAPE_RENAMED_EVENT_TYPE = "shape.renamed";
18627
+ var SUBSCRIBABLE_EVENT_TYPES = [
18628
+ COMMIT_EVENT_TYPE,
18629
+ REPO_RENAMED_EVENT_TYPE,
18630
+ ORG_RENAMED_EVENT_TYPE,
18631
+ THING_RENAMED_EVENT_TYPE,
18632
+ SHAPE_RENAMED_EVENT_TYPE
18633
+ ];
18634
+ var REPO_SCOPED_EVENT_TYPES = [
18635
+ COMMIT_EVENT_TYPE,
18636
+ REPO_RENAMED_EVENT_TYPE,
18637
+ THING_RENAMED_EVENT_TYPE,
18638
+ SHAPE_RENAMED_EVENT_TYPE
18639
+ ];
18640
+
18648
18641
  // ../../packages/rules/src/component-install.ts
18649
18642
  function manifestShapeData(shape) {
18650
18643
  const data = { fields: shape.fields };
@@ -19195,6 +19188,24 @@ function validateCredential(cred, i, errors) {
19195
19188
  }
19196
19189
  validateProvisioning(cred, path, errors);
19197
19190
  }
19191
+ function validateEventTrigger(trigger, path, errors) {
19192
+ const event = trigger.event;
19193
+ if (event !== undefined) {
19194
+ if (typeof event !== "string" || !REPO_SCOPED_EVENT_TYPES.includes(event)) {
19195
+ errors.push(`${path}.event must be one of: ${REPO_SCOPED_EVENT_TYPES.join(", ")}`);
19196
+ return;
19197
+ }
19198
+ }
19199
+ if (event === undefined || event === COMMIT_EVENT_TYPE) {
19200
+ requireString(trigger, "shape", path, errors);
19201
+ return;
19202
+ }
19203
+ for (const field of ["shape", "filter"]) {
19204
+ if (trigger[field] !== undefined) {
19205
+ errors.push(`${path}.${field} is not valid for a "${event}" trigger — metadata events have no shape or filter`);
19206
+ }
19207
+ }
19208
+ }
19198
19209
  function validateSubscription(sub, i, errors) {
19199
19210
  const path = `manifest.subscriptions[${i}]`;
19200
19211
  if (!isObject2(sub)) {
@@ -19215,7 +19226,7 @@ function validateSubscription(sub, i, errors) {
19215
19226
  errors.push(`${path}.trigger must be an object`);
19216
19227
  } else {
19217
19228
  if (sub.trigger.kind === "event") {
19218
- requireString(sub.trigger, "shape", `${path}.trigger`, errors);
19229
+ validateEventTrigger(sub.trigger, `${path}.trigger`, errors);
19219
19230
  } else if (sub.trigger.kind === "cron") {
19220
19231
  errors.push(`${path}.trigger.kind "cron" is no longer supported; cron subscriptions were removed from the public surface — use an "event" trigger`);
19221
19232
  } else {
@@ -19434,7 +19445,7 @@ function validateManifestSemantics(manifest) {
19434
19445
  ]);
19435
19446
  const knownSeedShapes = new Set([...shapeNames, "ComponentConfig"]);
19436
19447
  for (const sub of manifest.subscriptions) {
19437
- if (sub.trigger.kind === "event" && !knownSubscriptionTriggerShapes.has(sub.trigger.shape)) {
19448
+ if (sub.trigger.kind === "event" && sub.trigger.shape !== undefined && !knownSubscriptionTriggerShapes.has(sub.trigger.shape)) {
19438
19449
  findings.push({
19439
19450
  level: "error",
19440
19451
  code: "MISSING_SUBSCRIPTION_TRIGGER_SHAPE_REF",
@@ -19949,9 +19960,99 @@ var PLATFORM_STATUS_PROBE_ARTIFACT_FIELDS = {
19949
19960
  description: "Optional URL associated with this artifact."
19950
19961
  }
19951
19962
  };
19963
+ // ../../packages/rules/src/tokens.ts
19964
+ var COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE = "$N/#N commit-token syntax is no longer supported. Use explicit names and explicit wrefs. For assertions about newly created things, create the thing with a deterministic name and set about to that wref in the same commit.";
19965
+ var ANY_TOKEN_RE = /[$#]\d+/;
19966
+ function hasAnyTokens(s) {
19967
+ return ANY_TOKEN_RE.test(s);
19968
+ }
19969
+
19970
+ // ../../packages/rules/src/preflight-commit.ts
19971
+ function preflightCommitDiagnostics(operations, options) {
19972
+ const errors = [];
19973
+ rejectCommitTokenSyntax(operations, errors);
19974
+ illegalOpSequences(operations, errors, options?.checkAddAdd ?? true);
19975
+ return errors;
19976
+ }
19977
+ function getOpName(op) {
19978
+ return op.name;
19979
+ }
19980
+ function tokenStringFields(op) {
19981
+ const fields = [getOpName(op), op.newName];
19982
+ if (typeof op.about === "string") {
19983
+ fields.push(op.about);
19984
+ }
19985
+ if (op.members) {
19986
+ fields.push(...op.members);
19987
+ }
19988
+ return fields;
19989
+ }
19990
+ function rejectCommitTokenSyntax(operations, errors) {
19991
+ for (let i = 0;i < operations.length; i++) {
19992
+ const op = operations[i];
19993
+ if (!op)
19994
+ continue;
19995
+ for (const field of tokenStringFields(op)) {
19996
+ if (field && hasAnyTokens(field)) {
19997
+ errors.push({
19998
+ code: "COMMIT_TOKEN_SYNTAX_REMOVED",
19999
+ operationIndex: i,
20000
+ message: COMMIT_TOKEN_SYNTAX_REMOVED_MESSAGE
20001
+ });
20002
+ break;
20003
+ }
20004
+ }
20005
+ }
20006
+ }
20007
+ function illegalOpSequences(operations, errors, checkAddAdd) {
20008
+ const opHistory = new Map;
20009
+ for (let i = 0;i < operations.length; i++) {
20010
+ const op = operations[i];
20011
+ if (!op)
20012
+ continue;
20013
+ const name = getOpName(op);
20014
+ if (!name)
20015
+ continue;
20016
+ if (hasAnyTokens(name))
20017
+ continue;
20018
+ const kind = inferOperationKind({ ...op, name });
20019
+ const qualName = kind === "shape" ? `shape:${name}` : `thing:${name}`;
20020
+ const history = opHistory.get(qualName) ?? [];
20021
+ history.push({ operation: op.operation, index: i });
20022
+ opHistory.set(qualName, history);
20023
+ }
20024
+ for (const [qualName, history] of opHistory) {
20025
+ if (history.length < 2)
20026
+ continue;
20027
+ for (let i = 1;i < history.length; i++) {
20028
+ const prev = history[i - 1];
20029
+ const curr = history[i];
20030
+ if (!prev || !curr)
20031
+ continue;
20032
+ const pair = `${prev.operation}+${curr.operation}`;
20033
+ if (checkAddAdd && pair === "add+add") {
20034
+ errors.push({
20035
+ code: "ILLEGAL_OP_SEQUENCE",
20036
+ operationIndex: curr.index,
20037
+ message: `Cannot add "${qualName}" twice in the same commit`
20038
+ });
20039
+ }
20040
+ if (pair === "revise+add") {
20041
+ errors.push({
20042
+ code: "ILLEGAL_OP_SEQUENCE",
20043
+ operationIndex: curr.index,
20044
+ message: `Cannot revise then add "${qualName}" in the same commit`
20045
+ });
20046
+ }
20047
+ }
20048
+ }
20049
+ }
20050
+
19952
20051
  // ../../packages/rules/src/preflight-operation.ts
19953
- var collectionTypes = ["pair", "triple", "set", "list"];
20052
+ var collectionTypes = ["pair", "set", "list"];
19954
20053
  var collectionOps = ["add", "revise"];
20054
+ var COLLECTION_CREATE_REQUIRES_NAME_MESSAGE = "Collection create requires a name. Collections are ordinary named things (ADR 0004).";
20055
+ var COLLECTION_ABOUT_REMOVED_MESSAGE = 'about accepts a wref. Create the collection as its own named operation, then point the assertion at it. Prefer deterministic relationship names, for example: [{"operation":"add","kind":"collection","type":"pair","name":"a-b-relationship","members":["A","B"]},{"operation":"add","kind":"assertion","about":"Pair/a-b-relationship","name":"Assertion/example","data":{}}]. For CLI usage, use wh commit submit --file with the two operations.';
19955
20056
  function preflightOpDiagnostics(op, operationIndex) {
19956
20057
  const errors = [];
19957
20058
  errors.push(...builtinShapeGuard(op, operationIndex));
@@ -19964,17 +20065,41 @@ function preflightOpDiagnostics(op, operationIndex) {
19964
20065
  function builtinShapeGuard(op, operationIndex) {
19965
20066
  const errors = [];
19966
20067
  const name = op.name;
19967
- if (op.kind === "shape" && name && isBuiltinShape(name)) {
20068
+ if (name && isRetiredCollectionShape(name)) {
20069
+ errors.push({
20070
+ code: "RESERVED_NAME",
20071
+ operationIndex,
20072
+ message: `Shape "${name}" is a retired collection shape and cannot be written manually`
20073
+ });
20074
+ }
20075
+ if (op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name)) && op.newName && isRetiredCollectionShape(op.newName)) {
19968
20076
  errors.push({
19969
20077
  code: "RESERVED_NAME",
19970
20078
  operationIndex,
19971
- message: `Shape "${name}" is a built-in shape and cannot be ${op.operation === "add" ? "created" : "revised"} manually`
20079
+ message: `Shape "${op.newName}" is a retired collection shape and cannot be written manually`
19972
20080
  });
20081
+ } else {
20082
+ const isShapeRename = op.operation === "rename" && (op.kind === "shape" || op.name !== undefined && !splitLocalPath(op.name));
20083
+ const builtinShapeName = name && isBuiltinShape(name) ? name : isShapeRename && op.newName && isBuiltinShape(op.newName) ? op.newName : undefined;
20084
+ if (builtinShapeName && (isShapeRename || op.operation !== "retract" && op.kind === "shape")) {
20085
+ const action = op.operation === "add" ? "created" : op.operation === "rename" ? "renamed" : "revised";
20086
+ errors.push({
20087
+ code: "RESERVED_NAME",
20088
+ operationIndex,
20089
+ message: `Shape "${builtinShapeName}" is a built-in shape and cannot be ${action} manually`
20090
+ });
20091
+ }
19973
20092
  }
19974
- if (op.kind === "thing" && name) {
20093
+ if (name) {
19975
20094
  const local = splitLocalPath(name);
19976
- if (local && isBuiltinCollectionShape(local.shapePrefix)) {
19977
- const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" or the about sugar instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Collections are immutable.`;
20095
+ if (local && isRetiredCollectionShape(local.shapePrefix)) {
20096
+ errors.push({
20097
+ code: "VALIDATION_ERROR",
20098
+ operationIndex,
20099
+ message: `Cannot ${op.operation} under retired collection shape "${local.shapePrefix}". Triple is read-only and retired for new collection writes.`
20100
+ });
20101
+ } else if (op.kind === "thing" && local && isBuiltinCollectionShape(local.shapePrefix)) {
20102
+ const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" to revise a collection.`;
19978
20103
  errors.push({
19979
20104
  code: "VALIDATION_ERROR",
19980
20105
  operationIndex,
@@ -20014,16 +20139,32 @@ function contentNameGuard(op, operationIndex) {
20014
20139
  function plusSignGuard(op, operationIndex) {
20015
20140
  const errors = [];
20016
20141
  const name = op.name;
20017
- if (op.kind === "collection")
20142
+ if (op.newName?.includes("+")) {
20143
+ errors.push({
20144
+ code: "VALIDATION_ERROR",
20145
+ operationIndex,
20146
+ message: `Name "${op.newName}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20147
+ });
20018
20148
  return errors;
20149
+ }
20019
20150
  if (name?.includes("+")) {
20151
+ if (op.kind === "collection" && op.operation === "add") {
20152
+ errors.push({
20153
+ code: "VALIDATION_ERROR",
20154
+ operationIndex,
20155
+ message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20156
+ });
20157
+ return errors;
20158
+ }
20159
+ if (op.kind === "collection")
20160
+ return errors;
20020
20161
  const local = splitLocalPath(name);
20021
- if (local && isBuiltinCollectionShape(local.shapePrefix))
20162
+ if (local && isReservedCollectionShape(local.shapePrefix))
20022
20163
  return errors;
20023
20164
  errors.push({
20024
20165
  code: "VALIDATION_ERROR",
20025
20166
  operationIndex,
20026
- message: `Name "${name}" contains reserved character "+". The "+" character is reserved for collection names.`
20167
+ message: `Name "${name}" contains reserved character "+". The "+" character is reserved for the historical collection auto-name namespace and is not allowed in user-supplied names.`
20027
20168
  });
20028
20169
  }
20029
20170
  return errors;
@@ -20032,102 +20173,79 @@ function validateCollectionAbouts(op, operationIndex) {
20032
20173
  const errors = [];
20033
20174
  if (!op.about || typeof op.about === "string")
20034
20175
  return errors;
20035
- if (!isCollectionAbout(op.about)) {
20036
- const keys = typeof op.about === "object" && op.about !== null ? Object.keys(op.about) : [];
20176
+ errors.push({
20177
+ code: "VALIDATION_ERROR",
20178
+ operationIndex,
20179
+ message: COLLECTION_ABOUT_REMOVED_MESSAGE
20180
+ });
20181
+ return errors;
20182
+ }
20183
+ function validateCollectionOps(op, operationIndex) {
20184
+ const errors = [];
20185
+ if (op.kind !== "collection")
20186
+ return errors;
20187
+ if (!collectionOps.includes(op.operation)) {
20188
+ return errors;
20189
+ }
20190
+ if (op.operation === "add" && !op.name) {
20037
20191
  errors.push({
20038
20192
  code: "VALIDATION_ERROR",
20039
20193
  operationIndex,
20040
- message: `Structured about must have exactly one key: pair, triple, set, or list. Got: ${keys.join(", ") || typeof op.about}`
20194
+ message: COLLECTION_CREATE_REQUIRES_NAME_MESSAGE
20041
20195
  });
20042
- return errors;
20043
- }
20044
- const tag = collectionAboutType(op.about);
20045
- const members = collectionAboutMembers(op.about);
20046
- for (let i = 0;i < members.length; i++) {
20047
- if (typeof members[i] !== "string") {
20048
- errors.push({
20049
- code: "VALIDATION_ERROR",
20050
- operationIndex,
20051
- message: `Collection member at index ${i} must be a string, got ${typeof members[i]}`
20052
- });
20053
- }
20054
20196
  }
20055
- const arityError = collectionArityError(tag, members);
20056
- if (arityError) {
20197
+ if (!op.type || !collectionTypes.includes(op.type)) {
20057
20198
  errors.push({
20058
20199
  code: "VALIDATION_ERROR",
20059
20200
  operationIndex,
20060
- message: arityError
20201
+ message: `Collection "type" must be one of: pair, set, list. Got: "${op.type ?? ""}"`
20061
20202
  });
20062
20203
  }
20063
- return errors;
20064
- }
20065
- function validateCollectionOps(op, operationIndex) {
20066
- const errors = [];
20067
- if (op.kind !== "collection")
20068
- return errors;
20069
- if (!collectionOps.includes(op.operation)) {
20204
+ if (!op.members || !Array.isArray(op.members)) {
20070
20205
  errors.push({
20071
20206
  code: "VALIDATION_ERROR",
20072
20207
  operationIndex,
20073
- message: `Collection kind only supports "add" or "revise" operation, got "${op.operation}"`
20208
+ message: 'Collection requires a "members" array'
20074
20209
  });
20075
- return errors;
20076
- }
20077
- if (op.operation === "add") {
20078
- if (!op.type || !collectionTypes.includes(op.type)) {
20079
- errors.push({
20080
- code: "VALIDATION_ERROR",
20081
- operationIndex,
20082
- message: `Collection "type" must be one of: pair, triple, set, list. Got: "${op.type ?? ""}"`
20083
- });
20084
- }
20085
- if (!op.members || !Array.isArray(op.members)) {
20086
- errors.push({
20087
- code: "VALIDATION_ERROR",
20088
- operationIndex,
20089
- message: 'Collection requires a "members" array'
20090
- });
20091
- } else {
20092
- for (let i = 0;i < op.members.length; i++) {
20093
- if (typeof op.members[i] !== "string") {
20094
- errors.push({
20095
- code: "VALIDATION_ERROR",
20096
- operationIndex,
20097
- message: `Collection member at index ${i} must be a string`
20098
- });
20099
- }
20100
- }
20101
- }
20102
- if (op.type && op.members) {
20103
- const arityError = collectionArityError(op.type, op.members);
20104
- if (arityError) {
20210
+ } else {
20211
+ for (let i = 0;i < op.members.length; i++) {
20212
+ if (typeof op.members[i] !== "string") {
20105
20213
  errors.push({
20106
20214
  code: "VALIDATION_ERROR",
20107
20215
  operationIndex,
20108
- message: arityError
20216
+ message: `Collection member at index ${i} must be a string`
20109
20217
  });
20110
20218
  }
20111
20219
  }
20112
20220
  }
20221
+ if (op.type && op.members) {
20222
+ const arityError = collectionArityError(op.type, op.members);
20223
+ if (arityError) {
20224
+ errors.push({
20225
+ code: "VALIDATION_ERROR",
20226
+ operationIndex,
20227
+ message: arityError
20228
+ });
20229
+ }
20230
+ }
20113
20231
  return errors;
20114
20232
  }
20115
20233
  function collectionArityError(tag, members) {
20116
20234
  switch (tag) {
20117
20235
  case "pair":
20118
20236
  return members.length !== 2 ? `Pair requires exactly 2 members, got ${members.length}` : null;
20119
- case "triple":
20120
- return members.length !== 3 ? `Triple requires exactly 3 members, got ${members.length}` : null;
20121
20237
  case "set":
20122
20238
  case "list":
20123
20239
  return members.length < 1 ? `${tag === "set" ? "Set" : "List"} requires at least 1 member, got 0` : null;
20124
20240
  }
20125
20241
  }
20126
-
20127
20242
  // ../../packages/rules/src/preflight.ts
20128
20243
  function preflightOpDiagnostics2(op, operationIndex) {
20129
20244
  return preflightOpDiagnostics(op, operationIndex);
20130
20245
  }
20246
+ function preflightCommitDiagnostics2(operations, options) {
20247
+ return preflightCommitDiagnostics(operations, options);
20248
+ }
20131
20249
  // ../../packages/rules/src/reserved-orgs.ts
20132
20250
  var RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES = [
20133
20251
  "_app",
@@ -27281,15 +27399,6 @@ function validateShapeDefinition(data, options = {}) {
27281
27399
  }
27282
27400
  return { valid: true };
27283
27401
  }
27284
- // ../../packages/rules/src/subscribable-events.ts
27285
- var COMMIT_EVENT_TYPE = "commit";
27286
- var REPO_RENAMED_EVENT_TYPE = "repo.renamed";
27287
- var ORG_RENAMED_EVENT_TYPE = "org.renamed";
27288
- var SUBSCRIBABLE_EVENT_TYPES = [
27289
- COMMIT_EVENT_TYPE,
27290
- REPO_RENAMED_EVENT_TYPE,
27291
- ORG_RENAMED_EVENT_TYPE
27292
- ];
27293
27402
  // ../../packages/rules/src/system-components/system.ts
27294
27403
  var SYSTEM_COMPONENT_ID = "com.warmhub.system";
27295
27404
  var COMPONENT_INSTALL_FIELDS = {
@@ -27352,7 +27461,7 @@ function findSystemComponent(componentId) {
27352
27461
  // ../../packages/sdk-ts/package.json
27353
27462
  var package_default = {
27354
27463
  name: "@warmhub/sdk-ts",
27355
- version: "0.66.0",
27464
+ version: "0.68.0",
27356
27465
  private: false,
27357
27466
  type: "module",
27358
27467
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -27460,16 +27569,38 @@ function shapeDefinitionPreflightError(name, data, verb) {
27460
27569
  }
27461
27570
 
27462
27571
  // ../../packages/sdk-ts/src/collection-operation-normalize.ts
27463
- var collectionTypes2 = new Set([
27464
- "pair",
27465
- "triple",
27466
- "set",
27467
- "list"
27468
- ]);
27572
+ var collectionTypes2 = new Set(["pair", "set", "list"]);
27469
27573
  function normalizeBackendCollectionAdd(operation, source) {
27470
- const diagnostics = preflightOpDiagnostics2({
27574
+ const normalized = normalizeCollectionWrite(operation, source, "add");
27575
+ return {
27471
27576
  operation: "add",
27472
27577
  kind: "collection",
27578
+ name: normalized.name,
27579
+ type: normalized.type,
27580
+ members: normalized.members,
27581
+ ...operation.skipExisting === true ? { skipExisting: true } : {}
27582
+ };
27583
+ }
27584
+ function normalizeBackendCollectionRevise(operation, source) {
27585
+ const normalized = normalizeCollectionWrite(operation, source, "revise");
27586
+ if (!normalized.name) {
27587
+ throw new Error(`${source}: collection revise requires a target name`);
27588
+ }
27589
+ return {
27590
+ operation: "revise",
27591
+ kind: "collection",
27592
+ name: normalized.name,
27593
+ type: normalized.type,
27594
+ members: normalized.members,
27595
+ ...typeof operation.expectedVersion === "number" ? { expectedVersion: operation.expectedVersion } : {},
27596
+ ...typeof operation.leaseId === "string" && operation.leaseId.length > 0 ? { leaseId: operation.leaseId } : {}
27597
+ };
27598
+ }
27599
+ function normalizeCollectionWrite(operation, source, writeOperation) {
27600
+ const diagnostics = preflightOpDiagnostics2({
27601
+ operation: writeOperation,
27602
+ kind: "collection",
27603
+ name: typeof operation.name === "string" ? operation.name : undefined,
27473
27604
  type: typeof operation.type === "string" ? operation.type : undefined,
27474
27605
  members: Array.isArray(operation.members) ? operation.members : undefined
27475
27606
  }, 0);
@@ -27478,29 +27609,32 @@ function normalizeBackendCollectionAdd(operation, source) {
27478
27609
  }
27479
27610
  const type = normalizeCollectionType(operation.type);
27480
27611
  if (!type) {
27481
- throw new Error(`${source}: collection add requires 'type' to be one of: pair, triple, set, list`);
27612
+ throw new Error(`${source}: collection ${writeOperation} requires 'type' to be one of: pair, set, list`);
27482
27613
  }
27483
27614
  if (!Array.isArray(operation.members)) {
27484
- throw new Error(`${source}: collection add requires a 'members' array`);
27615
+ throw new Error(`${source}: collection ${writeOperation} requires a 'members' array`);
27485
27616
  }
27486
27617
  const name = normalizeOptionalName(operation.name);
27487
- if (operation.name !== undefined && !name) {
27488
- throw new Error(`${source}: collection add name must be a non-empty string when provided`);
27618
+ if (!name) {
27619
+ throw new Error(`${source}: collection ${writeOperation} name must be a non-empty string`);
27489
27620
  }
27490
- assertNoUnsupportedCollectionAddFields(operation, source);
27621
+ assertNoUnsupportedCollectionFields(operation, source, writeOperation);
27491
27622
  return {
27492
- operation: "add",
27493
- kind: "collection",
27494
- ...name ? { name } : {},
27623
+ name,
27495
27624
  type,
27496
- members: operation.members,
27497
- ...operation.skipExisting === true ? { skipExisting: true } : {}
27625
+ members: operation.members
27498
27626
  };
27499
27627
  }
27500
- function assertNoUnsupportedCollectionAddFields(operation, source) {
27501
- const unsupportedFields = ["about", "aboutWref", "shapeWref", "data"].filter((field) => operation[field] !== undefined);
27628
+ function assertNoUnsupportedCollectionFields(operation, source, writeOperation) {
27629
+ const unsupportedFields = [
27630
+ "about",
27631
+ "aboutWref",
27632
+ "shapeWref",
27633
+ "data",
27634
+ ...writeOperation === "revise" ? ["skipExisting"] : []
27635
+ ].filter((field) => operation[field] !== undefined);
27502
27636
  if (unsupportedFields.length > 0) {
27503
- throw new Error(`${source}: collection add does not support ${unsupportedFields.map((field) => `'${field}'`).join(", ")}`);
27637
+ throw new Error(`${source}: collection ${writeOperation} does not support ${unsupportedFields.map((field) => `'${field}'`).join(", ")}`);
27504
27638
  }
27505
27639
  }
27506
27640
  function normalizeCollectionType(value) {
@@ -27550,12 +27684,12 @@ function toBackendStreamOperation(operation) {
27550
27684
  if (Object.hasOwn(operation, "active")) {
27551
27685
  throw new Error(`${kind2} revise operation no longer supports 'active' — use retract('${name}') instead`);
27552
27686
  }
27687
+ if (kind2 === "collection") {
27688
+ return normalizeBackendCollectionRevise(operation, "commit.apply");
27689
+ }
27553
27690
  if (operation.data === undefined) {
27554
27691
  throw new Error(`${kind2} revise operation requires 'data'`);
27555
27692
  }
27556
- if (kind2 === "collection") {
27557
- throw new Error(`collection revise is no longer supported — use retract('${name}') instead`);
27558
- }
27559
27693
  if (kind2 === "assertion") {
27560
27694
  return {
27561
27695
  operation: "revise",
@@ -27590,6 +27724,9 @@ function toBackendStreamOperation(operation) {
27590
27724
  if (!("about" in operation) || operation.about === undefined) {
27591
27725
  throw new Error("assertion add operation requires 'about'");
27592
27726
  }
27727
+ if (typeof operation.about !== "string") {
27728
+ throw new Error(COLLECTION_ABOUT_REMOVED_MESSAGE);
27729
+ }
27593
27730
  if (operation.data === undefined) {
27594
27731
  throw new Error("assertion add operation requires 'data'");
27595
27732
  }
@@ -27782,34 +27919,14 @@ function computeBackoffDelayMs(attempt, policy) {
27782
27919
  function sleep2(ms) {
27783
27920
  return new Promise((resolve) => setTimeout(resolve, ms));
27784
27921
  }
27785
- var TOKEN_REF_REGEX = /[$#]\d+/;
27786
- function stringHasTokenRef(value) {
27787
- return typeof value === "string" && TOKEN_REF_REGEX.test(value);
27788
- }
27789
- function structuralValueHasTokenRef(value, seen = new WeakSet) {
27790
- if (stringHasTokenRef(value))
27791
- return true;
27792
- if (value !== null && typeof value === "object") {
27793
- if (seen.has(value))
27794
- return false;
27795
- seen.add(value);
27796
- if (Array.isArray(value)) {
27797
- return value.some((entry) => structuralValueHasTokenRef(entry, seen));
27798
- }
27799
- return Object.values(value).some((entry) => structuralValueHasTokenRef(entry, seen));
27800
- }
27801
- return false;
27802
- }
27803
- function opUsesTokens(op) {
27804
- return stringHasTokenRef(op.name) || stringHasTokenRef(op.wref) || structuralValueHasTokenRef(op.about) || stringHasTokenRef(op.aboutWref) || structuralValueHasTokenRef(op.members) || structuralValueHasTokenRef(op.data);
27805
- }
27806
27922
 
27807
27923
  // ../../packages/sdk-ts/src/stream-submit-submit.ts
27808
27924
  class StreamValidationError extends Error {
27809
- code = "VALIDATION_ERROR";
27925
+ code;
27810
27926
  status = 400;
27811
- constructor(message) {
27927
+ constructor(message, code = "VALIDATION_ERROR") {
27812
27928
  super(message);
27929
+ this.code = code;
27813
27930
  this.name = "WarmHubError";
27814
27931
  }
27815
27932
  }
@@ -27830,6 +27947,7 @@ async function submitOperationsViaStream(client, args) {
27830
27947
  }
27831
27948
  return streamOperation;
27832
27949
  });
27950
+ validateNormalizedOperations(operations);
27833
27951
  const chunkSize = normalizeChunkSize(args.chunkSize);
27834
27952
  let streamId = args.streamId ?? createStreamId();
27835
27953
  const policy = args.streamId !== undefined ? false : resolveRetryPolicy(args.retry);
@@ -27841,7 +27959,6 @@ async function submitOperationsViaStream(client, args) {
27841
27959
  let attempt = 1;
27842
27960
  let priorAttemptAmbiguous = false;
27843
27961
  const chunkIsAtomic = chunk.length === 1;
27844
- const chunkUsesTokens = chunk.some(opUsesTokens);
27845
27962
  while (true) {
27846
27963
  try {
27847
27964
  const appendResult = await client.stream.append({
@@ -27864,7 +27981,7 @@ async function submitOperationsViaStream(client, args) {
27864
27981
  if (chunkResults.length === 0 && !priorAttemptAmbiguous && isDefiniteClientError(cause)) {
27865
27982
  throw cause;
27866
27983
  }
27867
- if (chunkResults.length === 0 && chunkIsAtomic && !chunkUsesTokens && policy !== false && attempt < policy.maxAttempts && isTransientStreamFailure(cause)) {
27984
+ if (chunkResults.length === 0 && chunkIsAtomic && policy !== false && attempt < policy.maxAttempts && isTransientStreamFailure(cause)) {
27868
27985
  await sleep2(computeBackoffDelayMs(attempt, policy));
27869
27986
  attempt += 1;
27870
27987
  priorAttemptAmbiguous = true;
@@ -27898,6 +28015,16 @@ async function submitOperationsViaStream(client, args) {
27898
28015
  }
27899
28016
  return result;
27900
28017
  }
28018
+ function validateNormalizedOperations(operations) {
28019
+ const diagnostics = preflightCommitDiagnostics2(operations).filter((diagnostic) => !isServerAuthoritativeSequenceDiagnostic(diagnostic));
28020
+ const firstDiagnostic = diagnostics[0];
28021
+ if (!firstDiagnostic)
28022
+ return;
28023
+ throw new StreamValidationError(`Invalid operation at index ${firstDiagnostic.operationIndex}: ${firstDiagnostic.message}`, firstDiagnostic.code);
28024
+ }
28025
+ function isServerAuthoritativeSequenceDiagnostic(diagnostic) {
28026
+ return diagnostic.code === "ILLEGAL_OP_SEQUENCE" && diagnostic.message.includes("Cannot revise then add ");
28027
+ }
27901
28028
  function isAllSubmittedOperationsFailed(result, submittedOperationCount) {
27902
28029
  if (submittedOperationCount <= 0) {
27903
28030
  return false;
@@ -28098,6 +28225,35 @@ function sanitizeSubscriptionUpdateInput(input) {
28098
28225
  } = input;
28099
28226
  return supported;
28100
28227
  }
28228
+ function hasCollectionQuerySource(source) {
28229
+ return !!source && (!!source.shape || !!source.kind || !!source.about || !!source.match || !!source.componentRef || source.excludeComponents === true || (source.where?.length ?? 0) > 0);
28230
+ }
28231
+ function hasCollectionSelectorAnchor(source) {
28232
+ return !!source && (!!source.shape || !!source.about || !!source.match || !!source.componentRef || (source.where?.length ?? 0) > 0);
28233
+ }
28234
+ function hasExplicitCollectionMembers(opts) {
28235
+ const members = opts.members;
28236
+ return Array.isArray(members) && members.length > 0;
28237
+ }
28238
+ function collectionTypeFromWref(wref) {
28239
+ const local = wref.replace(/^wh:[^/]+\/[^/]+\//, "").replace(/@(?:HEAD|ALL|v\d+)$/, "");
28240
+ const shape = local.split("/")[0]?.toLowerCase();
28241
+ return shape === "pair" || shape === "set" || shape === "list" ? shape : undefined;
28242
+ }
28243
+ function assertSelectorBackedCollectionType(type, opts) {
28244
+ if (hasCollectionQuerySource(opts.query) && !hasCollectionSelectorAnchor(opts.query)) {
28245
+ throw new WarmHubError("VALIDATION_ERROR", "Selector-backed collection sources require shape, about, match, componentRef, or where; kind and excludeComponents only narrow an existing selector.");
28246
+ }
28247
+ if (opts.sourceRepo && !hasCollectionSelectorAnchor(opts.query)) {
28248
+ throw new WarmHubError("VALIDATION_ERROR", "sourceRepo requires a selector-backed collection query");
28249
+ }
28250
+ if (opts.sourceRepo && hasExplicitCollectionMembers(opts)) {
28251
+ throw new WarmHubError("VALIDATION_ERROR", "sourceRepo cannot be combined with explicit members; use canonical wh:org/repo/... member wrefs or omit sourceRepo.");
28252
+ }
28253
+ if (hasCollectionQuerySource(opts.query) && type !== "set") {
28254
+ throw new WarmHubError("VALIDATION_ERROR", "Selector-backed collection sources are only supported for set collections");
28255
+ }
28256
+ }
28101
28257
  function trpcClientCodeToWarmHubCode(code) {
28102
28258
  switch (code) {
28103
28259
  case "BAD_REQUEST":
@@ -29319,93 +29475,232 @@ class WarmHubClient {
29319
29475
  }
29320
29476
  }
29321
29477
  };
29322
- thing = {
29323
- head: async (orgName, repoName, opts) => {
29478
+ collection = {
29479
+ create: async (orgName, repoName, opts) => {
29324
29480
  try {
29325
- const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
29326
- return await this.trpc.thing.head.query({
29481
+ assertSelectorBackedCollectionType(opts.type, opts);
29482
+ return await this.trpc.collection.create.mutate({
29327
29483
  orgName,
29328
29484
  repoName,
29329
- shape: opts?.shape,
29330
- kind,
29331
- match: opts?.match,
29332
- dataMode: opts?.dataMode,
29333
- includeRetracted: opts?.includeRetracted,
29485
+ type: opts.type,
29486
+ name: opts.name,
29487
+ members: opts.members,
29488
+ from: opts.from,
29489
+ add: opts.add,
29490
+ remove: opts.remove,
29491
+ replaceMembers: opts.replaceMembers,
29492
+ query: opts.query,
29493
+ sourceOrgName: opts.sourceRepo?.orgName,
29494
+ sourceRepoName: opts.sourceRepo?.repoName,
29495
+ skipExisting: opts.skipExisting,
29496
+ message: opts.message,
29497
+ committer: opts.committer
29498
+ });
29499
+ } catch (error) {
29500
+ throw toWarmHubError(error);
29501
+ }
29502
+ },
29503
+ members: async (orgName, repoName, wref, opts) => {
29504
+ try {
29505
+ return await this.trpc.collection.members.query({
29506
+ orgName,
29507
+ repoName,
29508
+ wref,
29509
+ version: opts?.version,
29334
29510
  limit: opts?.limit,
29335
- cursor: opts?.cursor,
29336
- componentRef: opts?.componentRef,
29337
- excludeComponents: opts?.excludeComponents,
29338
- excludeInfraShapes: opts?.excludeInfraShapes,
29339
- where: opts?.where
29511
+ cursor: opts?.cursor
29340
29512
  });
29341
29513
  } catch (error) {
29342
29514
  throw toWarmHubError(error);
29343
29515
  }
29344
29516
  },
29345
- headIter: (orgName, repoName, opts) => {
29346
- return paginate((cursor) => this.thing.head(orgName, repoName, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29517
+ membersIter: (orgName, repoName, wref, opts) => {
29518
+ let snapshotVersion = opts?.version;
29519
+ return paginate(async (cursor) => {
29520
+ const page = await this.collection.members(orgName, repoName, wref, {
29521
+ ...opts,
29522
+ version: snapshotVersion,
29523
+ cursor
29524
+ });
29525
+ snapshotVersion ??= page.version;
29526
+ return page;
29527
+ }, (page) => page.items, opts?.cursor);
29347
29528
  },
29348
- headAll: async (orgName, repoName, opts) => {
29529
+ membersAll: async (orgName, repoName, wref, opts) => {
29349
29530
  const { max, ...pageOpts } = opts ?? {};
29350
- return await collectPaginatedPages((cursor) => this.thing.head(orgName, repoName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29531
+ let snapshotVersion = pageOpts.version;
29532
+ return await collectPaginatedPages(async (cursor) => {
29533
+ const page = await this.collection.members(orgName, repoName, wref, {
29534
+ ...pageOpts,
29535
+ version: snapshotVersion,
29536
+ cursor
29537
+ });
29538
+ snapshotVersion ??= page.version;
29539
+ return page;
29540
+ }, (page) => page.items, max, pageOpts.cursor);
29351
29541
  },
29352
- get: async (orgName, repoName, wref, version, opts) => {
29542
+ contains: async (orgName, repoName, wref, members, opts) => {
29353
29543
  try {
29354
- return await this.trpc.thing.get.query({
29544
+ return await this.trpc.collection.contains.query({
29355
29545
  orgName,
29356
29546
  repoName,
29357
29547
  wref,
29358
- version,
29359
- includeRetracted: opts?.includeRetracted
29548
+ version: opts?.version,
29549
+ members,
29550
+ position: opts?.position
29360
29551
  });
29361
29552
  } catch (error) {
29362
29553
  throw toWarmHubError(error);
29363
29554
  }
29364
29555
  },
29365
- getWithLease: async (orgName, repoName, wref, opts) => {
29556
+ diff: async (orgName, repoName, leftWref, rightWref, opts) => {
29366
29557
  try {
29367
- return await this.trpc.thing.getWithLease.mutate({
29558
+ return await this.trpc.collection.diff.query({
29368
29559
  orgName,
29369
29560
  repoName,
29370
- wref,
29371
- ttlMs: opts?.ttlMs
29561
+ leftWref,
29562
+ rightWref,
29563
+ leftVersion: opts?.leftVersion,
29564
+ rightVersion: opts?.rightVersion,
29565
+ mode: opts?.mode
29372
29566
  });
29373
29567
  } catch (error) {
29374
29568
  throw toWarmHubError(error);
29375
29569
  }
29376
29570
  },
29377
- releaseLease: async (orgName, repoName, wref, leaseId) => {
29571
+ revise: async (orgName, repoName, wref, opts) => {
29378
29572
  try {
29379
- await this.trpc.thing.releaseLease.mutate({
29573
+ const targetType = collectionTypeFromWref(wref);
29574
+ if (hasCollectionQuerySource(opts.query) && !hasCollectionSelectorAnchor(opts.query)) {
29575
+ throw new WarmHubError("VALIDATION_ERROR", "Selector-backed collection sources require shape, about, match, componentRef, or where; kind and excludeComponents only narrow an existing selector.");
29576
+ }
29577
+ if (opts.sourceRepo && !hasCollectionSelectorAnchor(opts.query)) {
29578
+ throw new WarmHubError("VALIDATION_ERROR", "sourceRepo requires a selector-backed collection query");
29579
+ }
29580
+ if (opts.sourceRepo && hasExplicitCollectionMembers(opts)) {
29581
+ throw new WarmHubError("VALIDATION_ERROR", "sourceRepo cannot be combined with explicit members; use canonical wh:org/repo/... member wrefs or omit sourceRepo.");
29582
+ }
29583
+ if (hasCollectionQuerySource(opts.query) && targetType && targetType !== "set") {
29584
+ throw new WarmHubError("VALIDATION_ERROR", "Selector-backed revise requires a Set/<name> target wref");
29585
+ }
29586
+ return await this.trpc.collection.revise.mutate({
29380
29587
  orgName,
29381
29588
  repoName,
29382
29589
  wref,
29383
- leaseId
29590
+ members: opts.members,
29591
+ add: opts.add,
29592
+ remove: opts.remove,
29593
+ query: opts.query,
29594
+ sourceOrgName: opts.sourceRepo?.orgName,
29595
+ sourceRepoName: opts.sourceRepo?.repoName,
29596
+ message: opts.message,
29597
+ committer: opts.committer
29384
29598
  });
29385
29599
  } catch (error) {
29386
29600
  throw toWarmHubError(error);
29387
29601
  }
29388
29602
  },
29389
- graph: async (orgName, repoName, wref, opts) => {
29603
+ stats: async (orgName, repoName, wref, opts) => {
29390
29604
  try {
29391
- return await this.trpc.thing.graph.query({
29605
+ return await this.trpc.collection.stats.query({
29392
29606
  orgName,
29393
29607
  repoName,
29394
29608
  wref,
29395
- version: opts?.version,
29396
- depth: opts?.depth,
29397
- limit: opts?.limit
29609
+ version: opts?.version
29398
29610
  });
29399
29611
  } catch (error) {
29400
29612
  throw toWarmHubError(error);
29401
29613
  }
29402
- },
29403
- getMany: async (orgName, repoName, wrefs, version, opts) => {
29614
+ }
29615
+ };
29616
+ thing = {
29617
+ head: async (orgName, repoName, opts) => {
29404
29618
  try {
29405
- if (wrefs.length === 0) {
29406
- return {
29407
- requested: 0,
29408
- items: [],
29619
+ const kind = opts?.kind === "shape" || opts?.kind === "thing" || opts?.kind === "assertion" || opts?.kind === "collection" ? opts.kind : undefined;
29620
+ return await this.trpc.thing.head.query({
29621
+ orgName,
29622
+ repoName,
29623
+ shape: opts?.shape,
29624
+ kind,
29625
+ match: opts?.match,
29626
+ dataMode: opts?.dataMode,
29627
+ includeRetracted: opts?.includeRetracted,
29628
+ limit: opts?.limit,
29629
+ cursor: opts?.cursor,
29630
+ componentRef: opts?.componentRef,
29631
+ excludeComponents: opts?.excludeComponents,
29632
+ excludeInfraShapes: opts?.excludeInfraShapes,
29633
+ where: opts?.where
29634
+ });
29635
+ } catch (error) {
29636
+ throw toWarmHubError(error);
29637
+ }
29638
+ },
29639
+ headIter: (orgName, repoName, opts) => {
29640
+ return paginate((cursor) => this.thing.head(orgName, repoName, { ...opts, cursor }), (page) => page.items, opts?.cursor);
29641
+ },
29642
+ headAll: async (orgName, repoName, opts) => {
29643
+ const { max, ...pageOpts } = opts ?? {};
29644
+ return await collectPaginatedPages((cursor) => this.thing.head(orgName, repoName, { ...pageOpts, cursor }), (page) => page.items, max, pageOpts.cursor);
29645
+ },
29646
+ get: async (orgName, repoName, wref, version, opts) => {
29647
+ try {
29648
+ return await this.trpc.thing.get.query({
29649
+ orgName,
29650
+ repoName,
29651
+ wref,
29652
+ version,
29653
+ includeRetracted: opts?.includeRetracted,
29654
+ dataMode: opts?.dataMode
29655
+ });
29656
+ } catch (error) {
29657
+ throw toWarmHubError(error);
29658
+ }
29659
+ },
29660
+ getWithLease: async (orgName, repoName, wref, opts) => {
29661
+ try {
29662
+ return await this.trpc.thing.getWithLease.mutate({
29663
+ orgName,
29664
+ repoName,
29665
+ wref,
29666
+ ttlMs: opts?.ttlMs
29667
+ });
29668
+ } catch (error) {
29669
+ throw toWarmHubError(error);
29670
+ }
29671
+ },
29672
+ releaseLease: async (orgName, repoName, wref, leaseId) => {
29673
+ try {
29674
+ await this.trpc.thing.releaseLease.mutate({
29675
+ orgName,
29676
+ repoName,
29677
+ wref,
29678
+ leaseId
29679
+ });
29680
+ } catch (error) {
29681
+ throw toWarmHubError(error);
29682
+ }
29683
+ },
29684
+ graph: async (orgName, repoName, wref, opts) => {
29685
+ try {
29686
+ return await this.trpc.thing.graph.query({
29687
+ orgName,
29688
+ repoName,
29689
+ wref,
29690
+ version: opts?.version,
29691
+ depth: opts?.depth,
29692
+ limit: opts?.limit
29693
+ });
29694
+ } catch (error) {
29695
+ throw toWarmHubError(error);
29696
+ }
29697
+ },
29698
+ getMany: async (orgName, repoName, wrefs, version, opts) => {
29699
+ try {
29700
+ if (wrefs.length === 0) {
29701
+ return {
29702
+ requested: 0,
29703
+ items: [],
29409
29704
  missing: []
29410
29705
  };
29411
29706
  }
@@ -29417,7 +29712,8 @@ class WarmHubClient {
29417
29712
  repoName,
29418
29713
  wrefs: chunkWrefs,
29419
29714
  version,
29420
- includeRetracted: opts?.includeRetracted
29715
+ includeRetracted: opts?.includeRetracted,
29716
+ dataMode: opts?.dataMode
29421
29717
  });
29422
29718
  };
29423
29719
  if (wrefs.length <= chunkSize) {
@@ -32882,7 +33178,7 @@ function emitPartialPageHint(ctx, count, _nextCursor, _limit) {
32882
33178
  }
32883
33179
 
32884
33180
  // ../../packages/warmhub-cli/src/domains/assertion/shared.ts
32885
- var COLLECTION_TAGS = ["pair", "triple", "set", "list"];
33181
+ var COLLECTION_TAGS = ["pair", "set", "list"];
32886
33182
  function parseAbout(raw) {
32887
33183
  const colonIdx = raw.indexOf(":");
32888
33184
  if (colonIdx === -1) {
@@ -32892,32 +33188,7 @@ function parseAbout(raw) {
32892
33188
  if (!COLLECTION_TAGS.includes(tag)) {
32893
33189
  return raw;
32894
33190
  }
32895
- const membersStr = raw.slice(colonIdx + 1);
32896
- const members = membersStr.split(",").map((m) => m.trim()).filter(Boolean);
32897
- switch (tag) {
32898
- case "pair":
32899
- if (members.length !== 2) {
32900
- throw new CliError(2 /* UserInput */, "USER_INPUT", `pair requires exactly 2 members, got ${members.length}`, undefined, "Example: --about pair:Location/a,Location/b");
32901
- }
32902
- return { pair: members };
32903
- case "triple":
32904
- if (members.length !== 3) {
32905
- throw new CliError(2 /* UserInput */, "USER_INPUT", `triple requires exactly 3 members, got ${members.length}`, undefined, "Example: --about triple:Location/a,Location/b,Location/c");
32906
- }
32907
- return { triple: members };
32908
- case "set":
32909
- if (members.length === 0) {
32910
- throw new CliError(2 /* UserInput */, "USER_INPUT", "set requires at least 1 member", undefined, "Example: --about set:Location/a,Location/b");
32911
- }
32912
- return { set: members };
32913
- case "list":
32914
- if (members.length === 0) {
32915
- throw new CliError(2 /* UserInput */, "USER_INPUT", "list requires at least 1 member", undefined, "Example: --about list:Location/a,Location/b");
32916
- }
32917
- return { list: members };
32918
- default:
32919
- return raw;
32920
- }
33191
+ throw new CliError(2 /* UserInput */, "USER_INPUT", COLLECTION_ABOUT_REMOVED_MESSAGE, undefined, "Use wh commit submit --file with a named collection add followed by an assertion add.");
32921
33192
  }
32922
33193
  function renderAbout(out, c, result) {
32923
33194
  const target = result.target;
@@ -33015,24 +33286,24 @@ var createFlags = {
33015
33286
  name: flag.string({ description: "Assertion name" }),
33016
33287
  shape: flag.string({ description: "Shape for assertion (required)" }),
33017
33288
  data: flag.string({ description: "Data payload (JSON)" }),
33018
- about: flag.string({ description: "Target wref or collection (pair:a,b)" }),
33289
+ about: flag.string({ description: "Target wref" }),
33019
33290
  message: flag.string({ short: "m", description: "Commit message" }),
33020
33291
  committer: flag.string({
33021
- description: "Committer thing wref (e.g. Agent/bot-1)"
33292
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
33022
33293
  })
33023
33294
  };
33024
33295
  var reviseFlags = {
33025
33296
  data: flag.string({ description: "Data payload (JSON)" }),
33026
33297
  message: flag.string({ short: "m", description: "Commit message" }),
33027
33298
  committer: flag.string({
33028
- description: "Committer thing wref (e.g. Agent/bot-1)"
33299
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
33029
33300
  })
33030
33301
  };
33031
33302
  var retractFlags = {
33032
33303
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
33033
33304
  message: flag.string({ short: "m", description: "Commit message" }),
33034
33305
  committer: flag.string({
33035
- description: "Committer thing wref (e.g. Agent/bot-1)"
33306
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
33036
33307
  })
33037
33308
  };
33038
33309
  var handleRevise = async (ctx, { flags, args }) => {
@@ -33119,9 +33390,14 @@ var handleCreate = async (ctx, { flags, args }) => {
33119
33390
 
33120
33391
  // ../../packages/warmhub-cli/src/domains/thing/shared.ts
33121
33392
  var DURABLE_ID_PATTERN_RE = /^[0-9a-zA-HJ-NP-Tv-z]{60}(@(v\d+|HEAD|ALL))?$/i;
33393
+ var WREF_SEGMENT = String.raw`[^/?#@:\s$]+`;
33394
+ var CANONICAL_WREF_PATTERN_RE = new RegExp(String.raw`^wh:${WREF_SEGMENT}/${WREF_SEGMENT}/${WREF_SEGMENT}(?:/${WREF_SEGMENT})*(?:@(?:v[1-9]\d*|HEAD|ALL))?$`, "i");
33122
33395
  function looksLikeDurableId(wref) {
33123
33396
  return DURABLE_ID_PATTERN_RE.test(wref);
33124
33397
  }
33398
+ function looksLikeCanonicalWref(wref) {
33399
+ return CANONICAL_WREF_PATTERN_RE.test(wref);
33400
+ }
33125
33401
  var DEFAULT_PAGE_LIMIT = 50;
33126
33402
  var DEFAULT_SEARCH_LIMIT = 25;
33127
33403
  var MAX_PAGE_LIMIT = 500;
@@ -33273,7 +33549,7 @@ var createFlags2 = {
33273
33549
  }),
33274
33550
  message: flag.string({ short: "m", description: "Commit message" }),
33275
33551
  committer: flag.string({
33276
- description: "Committer thing wref (e.g. Agent/bot-1)"
33552
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
33277
33553
  })
33278
33554
  };
33279
33555
  var handleCreate2 = async (ctx, { flags, args }) => {
@@ -33374,6 +33650,9 @@ function renderThing(out, c, result) {
33374
33650
  out(` ${c.dim}revisedOn:${c.reset} ${formatTime(meta.revisedOn, now)}`);
33375
33651
  }
33376
33652
  }
33653
+ if (result.collection) {
33654
+ renderCollectionSummary(out, c, result.collection);
33655
+ }
33377
33656
  const fields = shapeName && result.data ? collectionFields(shapeName, result.data) : null;
33378
33657
  if (fields) {
33379
33658
  for (const field of fields) {
@@ -33396,6 +33675,21 @@ function renderThing(out, c, result) {
33396
33675
  }
33397
33676
  }
33398
33677
  }
33678
+ function renderCollectionSummary(out, c, collection) {
33679
+ out(` ${c.dim}collection:${c.reset} ${collection.type}`);
33680
+ out(` ${c.dim}members:${c.reset} ${collection.memberCount}`);
33681
+ if (collection.fullData)
33682
+ return;
33683
+ const limit = collection.inlineLimit ? ` > ${collection.inlineLimit}` : " above inline limit";
33684
+ out(` ${c.dim}data:${c.reset} elided (${collection.memberCount}${limit}; use --data-mode full)`);
33685
+ const preview = collection.preview ?? [];
33686
+ if (preview.length === 0)
33687
+ return;
33688
+ out(` ${c.dim}preview:${c.reset}`);
33689
+ for (const wref of preview) {
33690
+ out(` ${pinnedWref(c, wref)}`);
33691
+ }
33692
+ }
33399
33693
  function renderDataBlock(out, data, indent) {
33400
33694
  const lines = JSON.stringify(data, null, 2).split(`
33401
33695
  `);
@@ -33587,7 +33881,7 @@ var historyFlags = {
33587
33881
  description: "Allow retracted shape/about targets to resolve"
33588
33882
  }),
33589
33883
  "resolve-collections": flag.boolean({
33590
- description: "Include assertions about collections (Pair/Triple/Set/List) containing the target. Only applies with --about."
33884
+ description: "Include assertions about collections (Pair/Set/List) containing the target. Only applies with --about."
33591
33885
  })
33592
33886
  };
33593
33887
  var handleHistory = async (ctx, { flags, args }) => {
@@ -33978,7 +34272,7 @@ var queryFlags = {
33978
34272
  description: "Include retracted things"
33979
34273
  }),
33980
34274
  "resolve-collections": flag.boolean({
33981
- description: "Include assertions about collections (Pair/Triple/Set/List) containing the target. Only applies with --about."
34275
+ description: "Include assertions about collections (Pair/Set/List) containing the target. Only applies with --about."
33982
34276
  }),
33983
34277
  component: flag.string({
33984
34278
  description: "Filter to things owned by this component (Org/Name ref)"
@@ -34159,10 +34453,10 @@ function renderQueryResults(out, c, result) {
34159
34453
  // ../../packages/warmhub-cli/src/domains/thing/refs.ts
34160
34454
  var refsFlags = {
34161
34455
  inbound: flag.boolean({
34162
- description: "Show inbound refs (what references this thing) [default]"
34456
+ description: "Show inbound refs (what references this target) [default]"
34163
34457
  }),
34164
34458
  outbound: flag.boolean({
34165
- description: "Show outbound refs (what this thing references)"
34459
+ description: "Show outbound refs (what this target references)"
34166
34460
  }),
34167
34461
  field: flag.string({ description: "Filter by field path (inbound only)" }),
34168
34462
  limit: flag.number({
@@ -34277,7 +34571,7 @@ var handleResolve = async (ctx, { args }) => {
34277
34571
  if (!wref) {
34278
34572
  usageError("Usage: wh thing resolve <wref>", "wh thing resolve player");
34279
34573
  }
34280
- const { org, repo } = looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34574
+ const { org, repo } = looksLikeDurableId(wref) || looksLikeCanonicalWref(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34281
34575
  const c = ctx.colors;
34282
34576
  const result = await ctx.client.thing.resolve(org, repo, wref);
34283
34577
  writeOutput(ctx, result, () => {
@@ -34297,7 +34591,7 @@ var retractFlags2 = {
34297
34591
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
34298
34592
  message: flag.string({ short: "m", description: "Commit message" }),
34299
34593
  committer: flag.string({
34300
- description: "Committer thing wref (e.g. Agent/bot-1)"
34594
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
34301
34595
  }),
34302
34596
  "lease-id": flag.string({
34303
34597
  description: "Read-lease token from `wh thing lease` (auto-released on success)"
@@ -34337,7 +34631,7 @@ var reviseFlags2 = {
34337
34631
  data: flag.string({ description: "Data payload (JSON)" }),
34338
34632
  message: flag.string({ short: "m", description: "Commit message" }),
34339
34633
  committer: flag.string({
34340
- description: "Committer thing wref (e.g. Agent/bot-1)"
34634
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
34341
34635
  }),
34342
34636
  "expected-version": flag.number({
34343
34637
  description: "Only apply if the target is still at this version (optimistic concurrency)"
@@ -34391,7 +34685,7 @@ var searchFlags = {
34391
34685
  description: "Include retracted things"
34392
34686
  }),
34393
34687
  "resolve-collections": flag.boolean({
34394
- description: "Include assertions about collections (Pair/Triple/Set/List) containing the target. Only applies with --about."
34688
+ description: "Include assertions about collections (Pair/Set/List) containing the target. Only applies with --about."
34395
34689
  }),
34396
34690
  limit: flag.number({ description: "Max results (default: 25, max: 500)" }),
34397
34691
  cursor: flag.string({ description: "Opaque pagination cursor (text mode)" }),
@@ -34550,10 +34844,13 @@ var viewFlags = {
34550
34844
  description: "Resolve embedded graph to this depth (1-5)"
34551
34845
  }),
34552
34846
  "include-retracted": flag.boolean({
34553
- description: "View a retracted thing"
34847
+ description: "View a retracted shape or shaped thing"
34554
34848
  }),
34555
34849
  file: flag.string({
34556
34850
  description: "Read additional wrefs from <path>, one per line. Use `--file=-` for stdin (the `=` form is required) or pass bare `-` as a positional. Lines starting with '#' and blank lines are ignored; lines are not split on any other character."
34851
+ }),
34852
+ "data-mode": flag.string({
34853
+ description: "Collection data mode: auto (default) or full. Use full to force large collection bodies into thing view output."
34557
34854
  })
34558
34855
  };
34559
34856
  var MAX_GET_MANY_WREFS = 500;
@@ -34568,6 +34865,13 @@ async function readStreamUtf8(input) {
34568
34865
  function parseLineList(text) {
34569
34866
  return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
34570
34867
  }
34868
+ function validateDataMode(value) {
34869
+ if (value === undefined)
34870
+ return;
34871
+ if (value === "auto" || value === "full")
34872
+ return value;
34873
+ usageError("--data-mode must be auto or full", "wh thing view Set/wake-voters --data-mode full");
34874
+ }
34571
34875
  async function collectWrefs(opts) {
34572
34876
  const dashPositional = opts.positionals.includes("-");
34573
34877
  const cleanPositionals = opts.positionals.filter((a) => a !== "-");
@@ -34606,12 +34910,16 @@ async function runSingleView(ctx, wref, flags) {
34606
34910
  const depth = flags.depth;
34607
34911
  const { org, repo } = looksLikeDurableId(wref) && depth === undefined ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34608
34912
  const includeRetracted = flags["include-retracted"] || version !== undefined;
34913
+ const dataMode = validateDataMode(flags["data-mode"]);
34609
34914
  if (depth !== undefined && (depth < 1 || depth > 5)) {
34610
34915
  usageError("Usage: wh thing view <wref> --depth <1-5>", "wh thing view Game/base --depth 2");
34611
34916
  }
34612
34917
  if (depth !== undefined && ctx.liveMode) {
34613
34918
  usageError("--depth is not supported with --live", "wh thing view Game/base --depth 2");
34614
34919
  }
34920
+ if (depth !== undefined && dataMode !== undefined) {
34921
+ usageError("--data-mode cannot be combined with --depth on `view` (graph reads use a separate payload shape).", "wh thing view Set/wake-voters --data-mode full");
34922
+ }
34615
34923
  if (depth !== undefined && (flags["include-retracted"] || version !== undefined)) {
34616
34924
  usageError("--depth cannot be combined with --include-retracted or --version on `view` (graph reads are active-only). Use `wh thing graph` for version-pinned graph reads.", "wh thing view Game/base --depth 2");
34617
34925
  }
@@ -34619,7 +34927,8 @@ async function runSingleView(ctx, wref, flags) {
34619
34927
  await runLive({
34620
34928
  apiUrl: ctx.config.apiUrl,
34621
34929
  poll: (c) => c.thing.get(org, repo, wref, version, {
34622
- includeRetracted
34930
+ includeRetracted,
34931
+ dataMode
34623
34932
  }),
34624
34933
  render: (r) => renderThing(ctx.out, ctx.colors, r),
34625
34934
  out: ctx.out,
@@ -34644,7 +34953,8 @@ async function runSingleView(ctx, wref, flags) {
34644
34953
  return;
34645
34954
  }
34646
34955
  const result = await ctx.client.thing.get(org, repo, wref, version, {
34647
- includeRetracted
34956
+ includeRetracted,
34957
+ dataMode
34648
34958
  });
34649
34959
  writeOutput(ctx, result, () => renderThing(ctx.out, ctx.colors, result));
34650
34960
  }
@@ -34652,7 +34962,8 @@ async function runBatchView(ctx, wrefs, flags) {
34652
34962
  const allDurable = wrefs.length > 0 && wrefs.every(looksLikeDurableId);
34653
34963
  const { org, repo } = allDurable ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34654
34964
  const includeRetracted = flags["include-retracted"] || flags.version !== undefined;
34655
- const result = await ctx.client.thing.getMany(org, repo, wrefs, flags.version, { includeRetracted });
34965
+ const dataMode = validateDataMode(flags["data-mode"]);
34966
+ const result = await ctx.client.thing.getMany(org, repo, wrefs, flags.version, { includeRetracted, dataMode });
34656
34967
  if (ctx.format === "jsonl") {
34657
34968
  for (const event of walkBatchResult(wrefs, result, flags.version)) {
34658
34969
  if (event.kind === "miss") {
@@ -34760,7 +35071,7 @@ var THING_DOMAIN = defineDomain({
34760
35071
  },
34761
35072
  resolve: {
34762
35073
  prime: true,
34763
- summary: "Resolve wref to thing",
35074
+ summary: "Resolve a wref to its canonical thing identity",
34764
35075
  args: "<wref>",
34765
35076
  handler: handleResolve
34766
35077
  },
@@ -34784,7 +35095,7 @@ var THING_DOMAIN = defineDomain({
34784
35095
  },
34785
35096
  retract: {
34786
35097
  prime: true,
34787
- summary: "Withdraw a thing, assertion, shape, or collection. Irreversible for the given identity.",
35098
+ summary: "Withdraw a thing. Irreversible for the given identity.",
34788
35099
  args: "<wref>",
34789
35100
  flags: retractFlags2,
34790
35101
  examples: [
@@ -34822,7 +35133,7 @@ var THING_DOMAIN = defineDomain({
34822
35133
  },
34823
35134
  refs: {
34824
35135
  prime: true,
34825
- summary: "Show refs (backlinks or cross-references) for a thing",
35136
+ summary: "Show refs (backlinks or cross-references) for a target",
34826
35137
  args: "<wref>",
34827
35138
  flags: refsFlags,
34828
35139
  examples: [
@@ -34854,7 +35165,7 @@ var THING_DOMAIN = defineDomain({
34854
35165
  "wh thing graph Game/base --depth 2"
34855
35166
  ],
34856
35167
  notes: [
34857
- "`graph` does not traverse inbound wref-field references; use `wh thing refs <wref> --inbound` to find things whose fields point at this thing."
35168
+ "`graph` does not traverse inbound wref-field references; use `wh thing refs <wref> --inbound` to find things whose fields point at this target."
34858
35169
  ],
34859
35170
  handler: handleThingGraph
34860
35171
  }
@@ -34879,9 +35190,9 @@ var handleView2 = async (ctx, { flags, args }) => {
34879
35190
  if (!wref) {
34880
35191
  usageError("Usage: wh assertion view <wref> [--version <n>] [--depth <n>]", "wh assertion view Belief/cave-safe --depth 2");
34881
35192
  }
34882
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
34883
35193
  const version = flags.version;
34884
35194
  const depth = flags.depth;
35195
+ const { org, repo } = looksLikeDurableId(wref) && depth === undefined ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34885
35196
  const includeRetracted = flags["include-retracted"] || version !== undefined;
34886
35197
  if (depth !== undefined && (depth < 1 || depth > 5)) {
34887
35198
  usageError("Usage: wh assertion view <wref> --depth <1-5>", "wh assertion view Belief/cave-safe --depth 2");
@@ -34943,7 +35254,7 @@ var handleHistory2 = async (ctx, { flags, args }) => {
34943
35254
  if (ctx.liveMode && flags.all) {
34944
35255
  usageError("Usage: wh assertion history <wref> [--limit N] [--cursor TOKEN] [--live]", "wh assertion history Belief/cave-safe --limit 50 --live");
34945
35256
  }
34946
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
35257
+ const { org, repo } = looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
34947
35258
  const limit = Math.min(flags.limit ?? DEFAULT_PAGE_LIMIT2, MAX_PAGE_LIMIT2);
34948
35259
  if (ctx.liveMode) {
34949
35260
  await runLive({
@@ -34995,7 +35306,9 @@ var handleHistory2 = async (ctx, { flags, args }) => {
34995
35306
  writePageOutput(ctx, result.versions, { limit, nextCursor: result.nextCursor ?? null }, () => renderHistory(ctx.out, ctx.colors, result));
34996
35307
  };
34997
35308
  var listFlags = {
34998
- about: flag.string({ description: "Target thing wref (required)" }),
35309
+ about: flag.string({
35310
+ description: "Target wref (thing or shape, required)"
35311
+ }),
34999
35312
  shape: flag.string({ description: "Filter by shape" }),
35000
35313
  depth: flag.number({ description: "Assertion depth" }),
35001
35314
  limit: flag.number({
@@ -35006,7 +35319,7 @@ var listFlags = {
35006
35319
  count: flag.boolean({ description: "Return count of matching assertions" }),
35007
35320
  match: flag.string({ description: "Filter by wref glob pattern" }),
35008
35321
  "resolve-collections": flag.boolean({
35009
- description: "Include assertions about collections (Pair/Triple/Set/List) containing the target."
35322
+ description: "Include assertions about collections (Pair/Set/List) containing the target."
35010
35323
  }),
35011
35324
  "include-retracted": flag.boolean({
35012
35325
  description: "Include retracted assertions"
@@ -35189,7 +35502,7 @@ var ASSERTION_DOMAIN = defineDomain({
35189
35502
  flags: createFlags,
35190
35503
  examples: [
35191
35504
  `wh assertion create --shape belief --about player --data '{"confidence":0.8}'`,
35192
- `wh assertion create --shape Distance --about pair:Location/a,Location/b --data '{"value":5}'`
35505
+ `wh assertion create --shape Distance --about Pair/location-distance --data '{"value":5}'`
35193
35506
  ],
35194
35507
  handler: handleCreate
35195
35508
  },
@@ -36580,6 +36893,693 @@ var CHANNEL_DOMAIN = defineDomain({
36580
36893
  handler: handleChannel
36581
36894
  });
36582
36895
 
36896
+ // ../../packages/warmhub-cli/src/domains/collection-helpers.ts
36897
+ import { readFileSync as readFileSync6 } from "node:fs";
36898
+ function parseMemberList(values) {
36899
+ return (values ?? []).flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean);
36900
+ }
36901
+ async function readMembersFromSources(ctx, opts) {
36902
+ const field = opts.wrefField ?? "wref";
36903
+ const members = [
36904
+ ...opts.positionals ?? [],
36905
+ ...parseMemberList(opts.members)
36906
+ ];
36907
+ const fileIsStdin = opts.file === "-";
36908
+ if ((opts.stdin || fileIsStdin) && isTTY(ctx.stdin ?? process.stdin)) {
36909
+ throw new CliError(2 /* UserInput */, "USER_INPUT", "Stdin requested but stdin is a terminal. Pipe input or remove the stdin flag.", undefined, "Example: wh collection create --type set --name audited --stdin");
36910
+ }
36911
+ if (opts.file && !fileIsStdin) {
36912
+ let raw;
36913
+ try {
36914
+ raw = readFileSync6(opts.file, "utf8");
36915
+ } catch (error) {
36916
+ const code = error.code;
36917
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Cannot read ${opts.file}${code ? ` (${code})` : ""}.`, error, "Check the path or use --file - to read collection members from stdin.");
36918
+ }
36919
+ members.push(...parseMembersPayload(raw, opts.label ?? "--file", field));
36920
+ }
36921
+ if (opts.stdin || fileIsStdin) {
36922
+ const raw = await readStreamUtf82(ctx.stdin ?? process.stdin);
36923
+ members.push(...parseMembersPayload(raw, opts.label ?? "stdin", field));
36924
+ }
36925
+ return members;
36926
+ }
36927
+ function hasCollectionQuerySource2(source) {
36928
+ return !!source.shape || !!source.kind || !!source.about || !!source.match || !!source.componentRef || source.excludeComponents === true || (source.where?.length ?? 0) > 0;
36929
+ }
36930
+ function hasCollectionSelectorAnchor2(source) {
36931
+ return !!source.shape || !!source.about || !!source.match || !!source.componentRef || (source.where?.length ?? 0) > 0;
36932
+ }
36933
+ function requireMembers(members, example) {
36934
+ if (members.length === 0) {
36935
+ usageError("At least one collection member is required", example);
36936
+ }
36937
+ }
36938
+ function renderMutation(ctx, result) {
36939
+ const c = ctx.colors;
36940
+ const action = result.status === "noop" ? "=" : "+";
36941
+ const color = result.status === "noop" ? c.dim : c.green;
36942
+ ctx.out(`${color}${action}${c.reset} ${displayName(c, result.wref)} ${c.dim}${result.type} ${result.memberCount} members${c.reset}`);
36943
+ }
36944
+ function renderMembers(ctx, result) {
36945
+ const c = ctx.colors;
36946
+ if (result.items.length === 0) {
36947
+ ctx.out(`${c.dim}No members${c.reset}`);
36948
+ return;
36949
+ }
36950
+ for (const item of result.items) {
36951
+ const position = item.position ?? 0;
36952
+ ctx.out(`${String(position).padStart(4, " ")} ${pinnedWref(c, item.wref)}`);
36953
+ }
36954
+ }
36955
+ function renderContains(ctx, result) {
36956
+ const c = ctx.colors;
36957
+ for (const item of result.results) {
36958
+ const marker = item.contains ? ctx.chars.check : ctx.chars.cross;
36959
+ const color = item.contains ? c.green : c.red;
36960
+ const at = item.positions === undefined || item.positions.length === 0 ? "" : ` ${c.dim}@${item.positions.join(",")}${c.reset}`;
36961
+ ctx.out(`${color}${marker}${c.reset} ${pinnedWref(c, item.member)}${at}`);
36962
+ }
36963
+ }
36964
+ function renderDiff(ctx, result) {
36965
+ const c = ctx.colors;
36966
+ if (result.mode === "ordered") {
36967
+ if (result.changed.length === 0) {
36968
+ ctx.out(`${c.dim}No ordered differences${c.reset}`);
36969
+ return;
36970
+ }
36971
+ for (const item of result.changed) {
36972
+ const left = item.left?.wref ?? `${c.dim}(none)${c.reset}`;
36973
+ const right = item.right?.wref ?? `${c.dim}(none)${c.reset}`;
36974
+ ctx.out(`${String(item.position).padStart(4, " ")} ${left} -> ${right}`);
36975
+ }
36976
+ return;
36977
+ }
36978
+ if (result.added.length === 0 && result.removed.length === 0) {
36979
+ ctx.out(`${c.dim}No membership differences${c.reset}`);
36980
+ return;
36981
+ }
36982
+ for (const member of result.added) {
36983
+ ctx.out(`${c.green}+${c.reset} ${member.wref}`);
36984
+ }
36985
+ for (const member of result.removed) {
36986
+ ctx.out(`${c.red}-${c.reset} ${member.wref}`);
36987
+ }
36988
+ }
36989
+ function renderStats(ctx, result) {
36990
+ const c = ctx.colors;
36991
+ ctx.out(`${pinnedWref(c, result.wref)} ${c.dim}${result.type}${c.reset}`);
36992
+ ctx.out(`members: ${result.memberCount}`);
36993
+ ctx.out(`unique: ${result.uniqueMemberCount}`);
36994
+ }
36995
+ function memberFromUnknown(value, field, label) {
36996
+ if (typeof value === "string" && value.trim().length > 0) {
36997
+ return value.trim();
36998
+ }
36999
+ if (value && typeof value === "object" && !Array.isArray(value)) {
37000
+ const raw = value[field];
37001
+ if (typeof raw === "string" && raw.trim().length > 0) {
37002
+ return raw.trim();
37003
+ }
37004
+ }
37005
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid member entry in ${label}. Expected a string wref or an object with "${field}".`, undefined, `Use string wrefs or objects like {"${field}":"Shape/name"}.`);
37006
+ }
37007
+ function parseMembersPayload(raw, label, field = "wref") {
37008
+ const trimmed = raw.trim();
37009
+ if (!trimmed)
37010
+ return [];
37011
+ if (trimmed.startsWith("[")) {
37012
+ const parsed = safeParseJson(trimmed, label);
37013
+ if (!Array.isArray(parsed)) {
37014
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid ${label}. Expected a JSON array.`, undefined, `Use a JSON array of string wrefs or objects with "${field}".`);
37015
+ }
37016
+ return parsed.map((entry) => memberFromUnknown(entry, field, label));
37017
+ }
37018
+ if (trimmed.startsWith("{")) {
37019
+ let objectParseError;
37020
+ try {
37021
+ const parsed = safeParseJson(trimmed, label);
37022
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && Array.isArray(parsed.members)) {
37023
+ return parsed.members.map((entry) => memberFromUnknown(entry, field, label));
37024
+ }
37025
+ return [memberFromUnknown(parsed, field, label)];
37026
+ } catch (error) {
37027
+ objectParseError = error;
37028
+ }
37029
+ const lines = trimmed.split(/\r?\n/).filter((line) => line.trim());
37030
+ if (lines.length > 1) {
37031
+ return lines.map((line) => memberFromUnknown(safeParseJson(line, label), field, label));
37032
+ }
37033
+ throw objectParseError;
37034
+ }
37035
+ return trimmed.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
37036
+ }
37037
+ async function readStreamUtf82(input) {
37038
+ let data = "";
37039
+ input.setEncoding?.("utf8");
37040
+ for await (const chunk of input) {
37041
+ data += typeof chunk === "string" ? chunk : chunk.toString("utf8");
37042
+ }
37043
+ return data;
37044
+ }
37045
+
37046
+ // ../../packages/warmhub-cli/src/domains/collection.ts
37047
+ var COLLECTION_TYPES = ["pair", "set", "list"];
37048
+ var commonWriteFlags = {
37049
+ message: flag.string({ short: "m", description: "Commit message" }),
37050
+ committer: flag.string({
37051
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
37052
+ })
37053
+ };
37054
+ var collectionInputFlags = {
37055
+ members: flag.string({
37056
+ multiple: true,
37057
+ description: "Collection member wrefs. May be repeated; comma-separated values are also accepted."
37058
+ }),
37059
+ file: flag.string({
37060
+ description: "Read member wrefs from a file. Supports newline text, JSON array, JSON object with members, or JSONL. Use --file=- for stdin."
37061
+ }),
37062
+ stdin: flag.boolean({
37063
+ description: "Read member wrefs from stdin. Supports newline text, JSON array, JSON object with members, or JSONL."
37064
+ }),
37065
+ "wref-field": flag.string({
37066
+ description: "Object field to read when --file/--stdin contains JSON objects (default: wref)."
37067
+ })
37068
+ };
37069
+ var collectionQueryFlags = {
37070
+ shape: flag.string({ description: "Select set members by shape" }),
37071
+ kind: flag.string({ description: "Select set members by kind" }),
37072
+ about: flag.string({ description: "Select set members by about wref" }),
37073
+ match: flag.string({
37074
+ description: "Select set members by the same name pattern accepted by wh thing query --match"
37075
+ }),
37076
+ component: flag.string({
37077
+ description: "Select set members owned by this component (Org/Name ref)"
37078
+ }),
37079
+ "exclude-components": flag.boolean({
37080
+ description: "Exclude component-owned records from selector-backed sets"
37081
+ }),
37082
+ where: flag.string({
37083
+ multiple: true,
37084
+ description: `Select set members by field-value WHERE predicate (repeatable). Same syntax as wh thing query --where; in:[...] accepts at most ${MAX_WHERE_IN_VALUES} values.`
37085
+ }),
37086
+ "source-repo": flag.string({
37087
+ description: "Read selector-backed set members from this source repo (org/repo). The collection is written to the target repo."
37088
+ })
37089
+ };
37090
+ var collectionCreateFlags = {
37091
+ type: flag.string({
37092
+ description: "Collection type: pair, set, or list"
37093
+ }),
37094
+ name: flag.string({
37095
+ description: "Collection local name. Collections are ordinary named things."
37096
+ }),
37097
+ "skip-existing": flag.boolean({
37098
+ description: "No-op if a named collection already exists. Only valid with --name."
37099
+ }),
37100
+ from: flag.string({
37101
+ description: "Initialize from an existing collection wref"
37102
+ }),
37103
+ add: flag.string({
37104
+ multiple: true,
37105
+ description: "With --from, member wrefs to add. May be repeated; comma-separated values are also accepted."
37106
+ }),
37107
+ remove: flag.string({
37108
+ multiple: true,
37109
+ description: "With --from, member wrefs to remove. May be repeated; comma-separated values are also accepted."
37110
+ }),
37111
+ "replace-file": flag.string({
37112
+ description: "With --from, replace membership from a file. Supports newline text, JSON array, JSON object with members, or JSONL."
37113
+ }),
37114
+ "replace-stdin": flag.boolean({
37115
+ description: "With --from, replace membership from stdin. Supports newline text, JSON array, JSON object with members, or JSONL."
37116
+ }),
37117
+ ...collectionInputFlags,
37118
+ ...collectionQueryFlags,
37119
+ ...commonWriteFlags
37120
+ };
37121
+ var collectionMembersFlags = {
37122
+ limit: flag.number({
37123
+ description: "Max members per page (default: 50, max: 500)"
37124
+ }),
37125
+ cursor: flag.string({ description: "Opaque pagination cursor" }),
37126
+ all: flag.boolean({ description: "Fetch all pages" }),
37127
+ version: flag.number({ description: "Specific version number" })
37128
+ };
37129
+ var collectionContainsFlags = {
37130
+ position: flag.number({
37131
+ description: "For list checks, require the member at this zero-based index"
37132
+ }),
37133
+ version: flag.number({
37134
+ description: "Specific collection version number. Member inputs are still pinned before comparison; use pinned @vN members for historical membership checks."
37135
+ }),
37136
+ ...collectionInputFlags
37137
+ };
37138
+ var collectionDiffFlags = {
37139
+ mode: flag.string({
37140
+ description: "Diff mode: auto, membership, or ordered"
37141
+ }),
37142
+ "left-version": flag.number({
37143
+ description: "Specific left collection version"
37144
+ }),
37145
+ "right-version": flag.number({
37146
+ description: "Specific right collection version"
37147
+ })
37148
+ };
37149
+ var collectionReviseFlags = {
37150
+ add: flag.string({
37151
+ multiple: true,
37152
+ description: "For set collections, member wrefs to add. May be repeated; comma-separated values are also accepted."
37153
+ }),
37154
+ remove: flag.string({
37155
+ multiple: true,
37156
+ description: "For set collections, member wrefs to remove. May be repeated; comma-separated values are also accepted."
37157
+ }),
37158
+ ...collectionInputFlags,
37159
+ ...collectionQueryFlags,
37160
+ ...commonWriteFlags
37161
+ };
37162
+ var collectionStatsFlags = {
37163
+ version: flag.number({ description: "Specific collection version number" })
37164
+ };
37165
+ function validateCollectionType(value) {
37166
+ if (!value || !COLLECTION_TYPES.includes(value)) {
37167
+ usageError("Usage: wh collection create --type <pair|set|list> --members <wref...>", 'wh collection create --type set --name audited --members Location/a,Location/b -m "audit snapshot"');
37168
+ }
37169
+ return value;
37170
+ }
37171
+ function validateDiffMode(value) {
37172
+ if (value === undefined)
37173
+ return;
37174
+ if (value === "auto" || value === "membership" || value === "ordered") {
37175
+ return value;
37176
+ }
37177
+ usageError("--mode must be one of: auto, membership, ordered", "wh collection diff Set/a Set/b --mode membership");
37178
+ }
37179
+ function collectionQuerySourceFromFlags(flags) {
37180
+ const where = (flags.where ?? []).map(parseWhereFlag);
37181
+ const kind = validateKind(flags.kind);
37182
+ return {
37183
+ ...flags.shape ? { shape: flags.shape } : {},
37184
+ ...kind ? { kind } : {},
37185
+ ...flags.about ? { about: flags.about } : {},
37186
+ ...flags.match ? { match: flags.match } : {},
37187
+ ...flags.component ? { componentRef: flags.component } : {},
37188
+ ...flags["exclude-components"] ? { excludeComponents: flags["exclude-components"] } : {},
37189
+ ...where.length > 0 ? { where } : {}
37190
+ };
37191
+ }
37192
+ function parseSourceRepoFlag(value) {
37193
+ if (!value)
37194
+ return;
37195
+ const parsed = splitRepoSlug(value);
37196
+ if (!parsed) {
37197
+ usageError("--source-repo must be an org/repo slug", "wh collection create --type set --name wake-voters --source-repo data/nc-voters --shape Voter --where county=Wake");
37198
+ }
37199
+ return { orgName: parsed.org, repoName: parsed.repo };
37200
+ }
37201
+ function requireQuerySourceForSourceRepo(sourceRepo, querySource, example) {
37202
+ if (!sourceRepo || hasCollectionSelectorAnchor2(querySource))
37203
+ return;
37204
+ usageError("--source-repo requires a selector source", example);
37205
+ }
37206
+ function requireSelectorAnchorForQuerySource(querySource, example) {
37207
+ if (!hasCollectionQuerySource2(querySource) || hasCollectionSelectorAnchor2(querySource)) {
37208
+ return;
37209
+ }
37210
+ usageError("Selector-backed collection sources require shape, about, match, componentRef, or where; kind and exclude-components only narrow an existing selector.", example);
37211
+ }
37212
+ function requireSetForQuerySource(type, source, example) {
37213
+ if (!hasCollectionQuerySource2(source) || type === "set")
37214
+ return;
37215
+ usageError("Selector-backed collection sources are only supported for set collections", example);
37216
+ }
37217
+ function normalizeCreateMembers(type, members) {
37218
+ return type === "set" ? Array.from(new Set(members)) : members;
37219
+ }
37220
+ function hasDeferredMemberSource(flags) {
37221
+ return !!flags.file || !!flags.stdin;
37222
+ }
37223
+ function isMissingCollectionMemberError(error) {
37224
+ const candidate = error;
37225
+ const code = candidate.code ?? candidate.kind;
37226
+ return code === "VALIDATION_ERROR" && /requires (?:at least|exactly) \d+ member/.test(candidate.message ?? "");
37227
+ }
37228
+ function collectionTypeFromWref2(wref) {
37229
+ const local = wref.replace(/^wh:[^/]+\/[^/]+\//, "").replace(/@(?:HEAD|ALL|v\d+)$/, "");
37230
+ const shape = local.split("/")[0]?.toLowerCase();
37231
+ return COLLECTION_TYPES.includes(shape) ? shape : undefined;
37232
+ }
37233
+ function parseCollectionReadRepo(ctx, wrefs) {
37234
+ const allDurable = wrefs.length > 0 && wrefs.every(looksLikeDurableId);
37235
+ return allDurable ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
37236
+ }
37237
+ var handleCollectionCreate = async (ctx, { flags, args }) => {
37238
+ if (args.length > 0) {
37239
+ usageError(`Unexpected argument: '${args[0]}'`, "Use --members for explicit collection members: wh collection create --type set --name audited --members Location/a,Location/b");
37240
+ }
37241
+ const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
37242
+ const type = validateCollectionType(flags.type);
37243
+ const querySource = collectionQuerySourceFromFlags(flags);
37244
+ const sourceRepo = parseSourceRepoFlag(flags["source-repo"]);
37245
+ const add = parseMemberList(flags.add);
37246
+ const remove = parseMemberList(flags.remove);
37247
+ requireQuerySourceForSourceRepo(sourceRepo, querySource, "wh collection create --type set --name wake-voters --source-repo data/nc-voters --shape Voter --where county=Wake");
37248
+ requireSelectorAnchorForQuerySource(querySource, 'wh collection create --type set --name voters --match "Voter/*" -m "snapshot"');
37249
+ requireSetForQuerySource(type, querySource, 'wh collection create --type set --name voters --match "Voter/*" -m "snapshot"');
37250
+ if (flags.from && hasCollectionQuerySource2(querySource)) {
37251
+ usageError("--from cannot be combined with selector flags", 'wh collection create --type set --name today --from Set/yesterday --add Location/c -m "delta"');
37252
+ }
37253
+ if (flags.from && sourceRepo) {
37254
+ usageError("--from cannot be combined with --source-repo", 'wh collection create --type set --name today --from Set/yesterday --add Location/c -m "delta"');
37255
+ }
37256
+ if (flags.from && (flags.members || flags.file || flags.stdin)) {
37257
+ usageError("--from cannot be combined with --members, --file, or --stdin; use --add/--remove for set deltas or --replace-file/--replace-stdin for full replacement", 'wh collection create --type set --name today --from Set/yesterday --add Location/c -m "delta"');
37258
+ }
37259
+ if ((flags["replace-file"] || flags["replace-stdin"]) && (add.length > 0 || remove.length > 0)) {
37260
+ usageError("--replace-file/--replace-stdin cannot be combined with --add or --remove", 'wh collection create --type set --name today --from Set/yesterday --replace-file members.txt -m "snapshot copy"');
37261
+ }
37262
+ if (!flags.from && (add.length > 0 || remove.length > 0 || flags["replace-file"] || flags["replace-stdin"])) {
37263
+ usageError("--add, --remove, --replace-file, and --replace-stdin require --from", 'wh collection create --type set --name today --from Set/yesterday --add Location/c -m "delta"');
37264
+ }
37265
+ if (!flags.name) {
37266
+ usageError("Collection create requires --name", 'wh collection create --type set --name audited --members Location/a,Location/b -m "audit snapshot"');
37267
+ }
37268
+ if (flags["skip-existing"] && !flags.name) {
37269
+ usageError("--skip-existing requires --name", "wh collection create --type set --name voters --members Voter/a --skip-existing");
37270
+ }
37271
+ const queryBacked = hasCollectionQuerySource2(querySource);
37272
+ const replaceMembers = flags.from && (flags["replace-file"] || flags["replace-stdin"]) ? await readMembersFromSources(ctx, {
37273
+ file: flags["replace-file"],
37274
+ stdin: flags["replace-stdin"],
37275
+ wrefField: flags["wref-field"],
37276
+ label: "replacement members"
37277
+ }) : undefined;
37278
+ const inlineMembers = normalizeCreateMembers(type, parseMemberList(flags.members));
37279
+ if (flags["skip-existing"] && flags.name && !queryBacked && inlineMembers.length === 0 && hasDeferredMemberSource(flags)) {
37280
+ try {
37281
+ const result2 = await ctx.client.collection.create(org, repo, {
37282
+ type,
37283
+ name: flags.name,
37284
+ members: [],
37285
+ skipExisting: true,
37286
+ message: flags.message,
37287
+ committer: flags.committer
37288
+ });
37289
+ writeOutput(ctx, result2, () => renderMutation(ctx, result2));
37290
+ return;
37291
+ } catch (error) {
37292
+ if (!isMissingCollectionMemberError(error)) {
37293
+ throw error;
37294
+ }
37295
+ }
37296
+ }
37297
+ const explicitMembers = await readMembersFromSources(ctx, {
37298
+ members: flags.members,
37299
+ file: flags.from ? undefined : flags.file,
37300
+ stdin: flags.from ? undefined : flags.stdin,
37301
+ wrefField: flags["wref-field"],
37302
+ label: "collection members"
37303
+ });
37304
+ const members = normalizeCreateMembers(type, explicitMembers);
37305
+ if (sourceRepo && members.length > 0) {
37306
+ usageError("--source-repo cannot be combined with explicit members; use canonical wh:org/repo/... member wrefs or omit --source-repo.", "wh collection create --type set --name wake-voters --source-repo data/nc-voters --shape Voter --where county=Wake");
37307
+ }
37308
+ if (!queryBacked && !flags.from) {
37309
+ requireMembers(members, 'wh collection create --type set --name audited --members Location/a,Location/b -m "audit snapshot"');
37310
+ }
37311
+ const result = queryBacked ? sourceRepo ? await ctx.client.collection.create(org, repo, {
37312
+ type,
37313
+ name: flags.name,
37314
+ ...flags.from ? { from: flags.from } : {},
37315
+ ...add.length > 0 ? { add } : {},
37316
+ ...remove.length > 0 ? { remove } : {},
37317
+ ...replaceMembers ? { replaceMembers } : {},
37318
+ query: querySource,
37319
+ sourceRepo,
37320
+ skipExisting: flags["skip-existing"],
37321
+ message: flags.message,
37322
+ committer: flags.committer
37323
+ }) : await ctx.client.collection.create(org, repo, {
37324
+ type,
37325
+ name: flags.name,
37326
+ members,
37327
+ ...flags.from ? { from: flags.from } : {},
37328
+ ...add.length > 0 ? { add } : {},
37329
+ ...remove.length > 0 ? { remove } : {},
37330
+ ...replaceMembers ? { replaceMembers } : {},
37331
+ query: querySource,
37332
+ skipExisting: flags["skip-existing"],
37333
+ message: flags.message,
37334
+ committer: flags.committer
37335
+ }) : await ctx.client.collection.create(org, repo, {
37336
+ type,
37337
+ name: flags.name,
37338
+ members,
37339
+ ...flags.from ? { from: flags.from } : {},
37340
+ ...add.length > 0 ? { add } : {},
37341
+ ...remove.length > 0 ? { remove } : {},
37342
+ ...replaceMembers ? { replaceMembers } : {},
37343
+ skipExisting: flags["skip-existing"],
37344
+ message: flags.message,
37345
+ committer: flags.committer
37346
+ });
37347
+ writeOutput(ctx, result, () => renderMutation(ctx, result));
37348
+ };
37349
+ var handleCollectionMembers = async (ctx, { flags, args }) => {
37350
+ const wref = args[0];
37351
+ if (!wref) {
37352
+ usageError("Usage: wh collection members <wref> [--limit N] [--cursor TOKEN] [--all]", "wh collection members Set/audited --all");
37353
+ }
37354
+ if (flags.cursor && !flags.limit) {
37355
+ usageError("Usage: wh collection members <wref> --limit N --cursor TOKEN", "wh collection members Set/audited --limit 50 --cursor <token>");
37356
+ }
37357
+ const { org, repo } = parseCollectionReadRepo(ctx, [wref]);
37358
+ const boundedLimit = Math.min(flags.limit ?? DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT);
37359
+ const pageLimit = flags.all ? Math.min(flags.limit ?? AUTO_PAGE_LIMIT, MAX_PAGE_LIMIT) : boundedLimit;
37360
+ if (flags.all) {
37361
+ const items = [];
37362
+ let cursor = flags.cursor;
37363
+ let snapshotVersion = flags.version;
37364
+ let firstPage;
37365
+ while (true) {
37366
+ const page = await ctx.client.collection.members(org, repo, wref, {
37367
+ version: snapshotVersion,
37368
+ limit: pageLimit,
37369
+ cursor
37370
+ });
37371
+ firstPage ??= {
37372
+ type: page.type,
37373
+ wref: page.wref,
37374
+ version: page.version
37375
+ };
37376
+ items.push(...page.items);
37377
+ snapshotVersion ??= page.version;
37378
+ if (!page.nextCursor)
37379
+ break;
37380
+ cursor = page.nextCursor;
37381
+ }
37382
+ writeCollectionMembersOutput(ctx, {
37383
+ type: firstPage?.type ?? "set",
37384
+ wref: firstPage?.wref ?? wref,
37385
+ version: snapshotVersion ?? firstPage?.version ?? flags.version ?? 1,
37386
+ items,
37387
+ nextCursor: undefined
37388
+ }, { limit: pageLimit, nextCursor: null });
37389
+ return;
37390
+ }
37391
+ const result = await ctx.client.collection.members(org, repo, wref, {
37392
+ version: flags.version,
37393
+ limit: boundedLimit,
37394
+ cursor: flags.cursor
37395
+ });
37396
+ if (result.nextCursor) {
37397
+ emitPartialPageHint(ctx, result.items.length, result.nextCursor, boundedLimit);
37398
+ }
37399
+ writeCollectionMembersOutput(ctx, result, {
37400
+ limit: boundedLimit,
37401
+ nextCursor: result.nextCursor ?? null
37402
+ });
37403
+ };
37404
+ function writeCollectionMembersOutput(ctx, result, page) {
37405
+ if (ctx.format === "json") {
37406
+ const envelope = pageEnvelope(result.items, page);
37407
+ printJson(ctx.out, {
37408
+ type: result.type,
37409
+ wref: result.wref,
37410
+ version: result.version,
37411
+ items: result.items,
37412
+ page: envelope.page
37413
+ });
37414
+ return;
37415
+ }
37416
+ if (ctx.format === "jsonl") {
37417
+ printJsonl(ctx.out, result.items);
37418
+ return;
37419
+ }
37420
+ renderMembers(ctx, result);
37421
+ }
37422
+ var handleCollectionContains = async (ctx, { flags, args }) => {
37423
+ const wref = args[0];
37424
+ if (!wref) {
37425
+ usageError("Usage: wh collection contains <wref> <member...>", "wh collection contains Set/audited Location/a Location/b");
37426
+ }
37427
+ const members = await readMembersFromSources(ctx, {
37428
+ positionals: args.slice(1),
37429
+ members: flags.members,
37430
+ file: flags.file,
37431
+ stdin: flags.stdin,
37432
+ wrefField: flags["wref-field"],
37433
+ label: "collection contains members"
37434
+ });
37435
+ requireMembers(members, "wh collection contains Set/audited Location/a Location/b");
37436
+ const { org, repo } = parseCollectionReadRepo(ctx, [wref]);
37437
+ const result = await ctx.client.collection.contains(org, repo, wref, members, {
37438
+ version: flags.version,
37439
+ position: flags.position
37440
+ });
37441
+ writeOutput(ctx, result, () => renderContains(ctx, result));
37442
+ };
37443
+ var handleCollectionDiff = async (ctx, { flags, args }) => {
37444
+ const [leftWref, rightWref] = args;
37445
+ if (!leftWref || !rightWref) {
37446
+ usageError("Usage: wh collection diff <left-wref> <right-wref> [--mode auto|membership|ordered]", "wh collection diff Set/yesterday Set/today --mode membership");
37447
+ }
37448
+ const { org, repo } = parseCollectionReadRepo(ctx, [leftWref, rightWref]);
37449
+ const result = await ctx.client.collection.diff(org, repo, leftWref, rightWref, {
37450
+ leftVersion: flags["left-version"],
37451
+ rightVersion: flags["right-version"],
37452
+ mode: validateDiffMode(flags.mode)
37453
+ });
37454
+ writeOutput(ctx, result, () => renderDiff(ctx, result));
37455
+ };
37456
+ var handleCollectionRevise = async (ctx, { flags, args }) => {
37457
+ const wref = args[0];
37458
+ if (!wref) {
37459
+ usageError('Usage: wh collection revise <named-wref> --file members.txt -m "message"', 'wh collection revise Set/audited --file members.txt -m "refresh audit set"');
37460
+ }
37461
+ const add = parseMemberList(flags.add);
37462
+ const remove = parseMemberList(flags.remove);
37463
+ if ((add.length > 0 || remove.length > 0) && (flags.members || flags.file || flags.stdin)) {
37464
+ usageError("--add/--remove cannot be combined with replacement members or selector flags", 'wh collection revise Set/audited --add Location/c --remove Location/a -m "delta"');
37465
+ }
37466
+ const members = await readMembersFromSources(ctx, {
37467
+ members: flags.members,
37468
+ file: add.length > 0 || remove.length > 0 ? undefined : flags.file,
37469
+ stdin: add.length > 0 || remove.length > 0 ? undefined : flags.stdin,
37470
+ wrefField: flags["wref-field"],
37471
+ label: "replacement members"
37472
+ });
37473
+ const querySource = collectionQuerySourceFromFlags(flags);
37474
+ const sourceRepo = parseSourceRepoFlag(flags["source-repo"]);
37475
+ requireQuerySourceForSourceRepo(sourceRepo, querySource, 'wh collection revise Set/audited --source-repo data/nc-voters --shape Voter -m "refresh audit set"');
37476
+ requireSelectorAnchorForQuerySource(querySource, 'wh collection revise Set/audited --match "Location/*" -m "refresh audit set"');
37477
+ if (sourceRepo && members.length > 0) {
37478
+ usageError("--source-repo cannot be combined with explicit members; use canonical wh:org/repo/... member wrefs or omit --source-repo.", 'wh collection revise Set/audited --source-repo data/nc-voters --shape Voter -m "refresh audit set"');
37479
+ }
37480
+ if ((add.length > 0 || remove.length > 0) && (members.length > 0 || hasCollectionQuerySource2(querySource))) {
37481
+ usageError("--add/--remove cannot be combined with replacement members or selector flags", 'wh collection revise Set/audited --add Location/c --remove Location/a -m "delta"');
37482
+ }
37483
+ const targetType = collectionTypeFromWref2(wref);
37484
+ if (hasCollectionQuerySource2(querySource) && targetType && targetType !== "set") {
37485
+ usageError("Selector-backed revise requires a Set/<name> target wref", 'wh collection revise Set/audited --match "Location/*" -m "refresh audit set"');
37486
+ }
37487
+ const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
37488
+ const queryBacked = hasCollectionQuerySource2(querySource);
37489
+ if (!queryBacked && add.length === 0 && remove.length === 0) {
37490
+ requireMembers(members, 'wh collection revise Set/audited --file members.txt -m "refresh audit set"');
37491
+ }
37492
+ const result = queryBacked ? sourceRepo ? await ctx.client.collection.revise(org, repo, wref, {
37493
+ query: querySource,
37494
+ sourceRepo,
37495
+ message: flags.message,
37496
+ committer: flags.committer
37497
+ }) : await ctx.client.collection.revise(org, repo, wref, {
37498
+ members,
37499
+ query: querySource,
37500
+ message: flags.message,
37501
+ committer: flags.committer
37502
+ }) : await ctx.client.collection.revise(org, repo, wref, {
37503
+ members,
37504
+ ...add.length > 0 ? { add } : {},
37505
+ ...remove.length > 0 ? { remove } : {},
37506
+ message: flags.message,
37507
+ committer: flags.committer
37508
+ });
37509
+ writeOutput(ctx, result, () => renderMutation(ctx, result));
37510
+ };
37511
+ var handleCollectionStats = async (ctx, { flags, args }) => {
37512
+ const wref = args[0];
37513
+ if (!wref) {
37514
+ usageError("Usage: wh collection stats <wref>", "wh collection stats Set/audited");
37515
+ }
37516
+ const { org, repo } = parseCollectionReadRepo(ctx, [wref]);
37517
+ const result = await ctx.client.collection.stats(org, repo, wref, {
37518
+ version: flags.version
37519
+ });
37520
+ writeOutput(ctx, result, () => renderStats(ctx, result));
37521
+ };
37522
+ var COLLECTION_DOMAIN = defineDomain({
37523
+ name: "collection",
37524
+ summary: "Create, inspect, compare, and revise WarmHub collections (Pair, Set, List).",
37525
+ group: "resource",
37526
+ verbs: {
37527
+ create: {
37528
+ summary: "Create a collection thing from explicit members or a set selector",
37529
+ args: "",
37530
+ flags: collectionCreateFlags,
37531
+ examples: [
37532
+ 'wh collection create --type set --name audited --members Location/a,Location/b -m "audit snapshot"',
37533
+ 'wh collection create --type set --name voters --match "Voter/*" -m "voter snapshot"',
37534
+ 'wh collection create --type set --name wake-voters --shape Voter --where state=NC --where county=Wake -m "snapshot Wake County voters"',
37535
+ 'wh collection create --type set --name today --from Set/yesterday --add Location/c --remove Location/a -m "delta"'
37536
+ ],
37537
+ handler: handleCollectionCreate
37538
+ },
37539
+ revise: {
37540
+ summary: "Revise a named collection by replacement, selector, or set delta",
37541
+ args: "<named-wref>",
37542
+ flags: collectionReviseFlags,
37543
+ examples: [
37544
+ 'wh collection revise Set/audited --file members.txt -m "refresh audit set"',
37545
+ 'wh collection revise Set/audited --match "Location/*" -m "refresh audit set"',
37546
+ 'wh collection revise Set/audited --add Location/c --remove Location/a -m "delta"'
37547
+ ],
37548
+ handler: handleCollectionRevise
37549
+ },
37550
+ members: {
37551
+ summary: "List collection members with pagination",
37552
+ args: "<wref>",
37553
+ flags: collectionMembersFlags,
37554
+ examples: ["wh collection members Set/audited --all"],
37555
+ handler: handleCollectionMembers
37556
+ },
37557
+ contains: {
37558
+ summary: "Check collection membership for one or more wrefs",
37559
+ args: "<wref> <member...>",
37560
+ flags: collectionContainsFlags,
37561
+ examples: ["wh collection contains Set/audited Location/a Location/b"],
37562
+ handler: handleCollectionContains
37563
+ },
37564
+ diff: {
37565
+ summary: "Compare two collections by membership or order",
37566
+ args: "<left-wref> <right-wref>",
37567
+ flags: collectionDiffFlags,
37568
+ examples: [
37569
+ "wh collection diff Set/yesterday Set/today --mode membership"
37570
+ ],
37571
+ handler: handleCollectionDiff
37572
+ },
37573
+ stats: {
37574
+ summary: "Summarize collection size and identity",
37575
+ args: "<wref>",
37576
+ flags: collectionStatsFlags,
37577
+ examples: ["wh collection stats Set/audited"],
37578
+ handler: handleCollectionStats
37579
+ }
37580
+ }
37581
+ });
37582
+
36583
37583
  // ../../packages/warmhub-cli/src/domains/commit-submit-flags.ts
36584
37584
  var createFlags3 = {
36585
37585
  ops: flag.string({
@@ -36612,7 +37612,7 @@ var createFlags3 = {
36612
37612
  }),
36613
37613
  message: flag.string({ short: "m", description: "Commit message" }),
36614
37614
  committer: flag.string({
36615
- description: "Committer thing wref (e.g. Agent/bot-1)"
37615
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
36616
37616
  }),
36617
37617
  add: flag.string({
36618
37618
  description: "Add a new thing (bare name — use --shape to set the shape). Repeatable; pair each --add with its own --data. For >20 ops, use --file <path>.",
@@ -36636,7 +37636,7 @@ var createFlags3 = {
36636
37636
  multiple: true
36637
37637
  }),
36638
37638
  about: flag.string({
36639
- description: "Target thing for assertions. Repeatable; one per --add, or a single value broadcast to all.",
37639
+ description: "Target shape or shaped thing for assertions. Repeatable; one per --add, or a single value broadcast to all.",
36640
37640
  multiple: true
36641
37641
  }),
36642
37642
  reason: flag.string({
@@ -36644,7 +37644,10 @@ var createFlags3 = {
36644
37644
  multiple: true
36645
37645
  }),
36646
37646
  type: flag.string({
36647
- description: "Collection type: pair, triple, set, list"
37647
+ description: "Collection type: pair, set, list"
37648
+ }),
37649
+ name: flag.string({
37650
+ description: "Collection name for --type shorthand"
36648
37651
  }),
36649
37652
  members: flag.string({
36650
37653
  description: "Collection members (comma-separated wrefs)"
@@ -36789,32 +37792,12 @@ function emitTemplateHint(ctx, operationType) {
36789
37792
  const assertionArgs = operationType === "add" ? "--kind assertion --about <Target/FILL_IN>" : "--kind assertion";
36790
37793
  ctx.err(`Template note: shapes define payload fields. To scaffold an assertion, rerun with ${assertionArgs}.`);
36791
37794
  }
36792
- var COLLECTION_ABOUT_PREFIX_RE = /^(pair|triple|set|list):(.*)$/;
37795
+ var COLLECTION_ABOUT_PREFIX_RE = /^(pair|set|list):(.*)$/;
36793
37796
  function parseCollectionAboutFlag(raw) {
36794
37797
  const match = COLLECTION_ABOUT_PREFIX_RE.exec(raw);
36795
37798
  if (!match)
36796
37799
  return raw;
36797
- const tag = match[1];
36798
- const members = match[2].split(",").map((m) => m.trim()).filter(Boolean);
36799
- switch (tag) {
36800
- case "pair":
36801
- if (members.length !== 2) {
36802
- throw new CliError(2 /* UserInput */, "USER_INPUT", `pair requires exactly 2 members, got ${members.length}`, undefined, "Example: --about pair:Location/a,Location/b");
36803
- }
36804
- break;
36805
- case "triple":
36806
- if (members.length !== 3) {
36807
- throw new CliError(2 /* UserInput */, "USER_INPUT", `triple requires exactly 3 members, got ${members.length}`, undefined, "Example: --about triple:Location/a,Location/b,Location/c");
36808
- }
36809
- break;
36810
- case "set":
36811
- case "list":
36812
- if (members.length === 0) {
36813
- throw new CliError(2 /* UserInput */, "USER_INPUT", `${tag} requires at least 1 member`, undefined, `Example: --about ${tag}:Location/a,Location/b`);
36814
- }
36815
- break;
36816
- }
36817
- return { [tag]: members };
37800
+ throw new CliError(2 /* UserInput */, "USER_INPUT", COLLECTION_ABOUT_REMOVED_MESSAGE, undefined, "Use wh commit submit --file with a named collection add followed by an assertion add.");
36818
37801
  }
36819
37802
  var handleTemplate = async (ctx, { flags, args }) => {
36820
37803
  const shapeNames = args;
@@ -37618,7 +38601,7 @@ var handleSubmit = async (ctx, { flags, args }) => {
37618
38601
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid --kind "${k}". Must be one of: ${validKinds.join(", ")}`);
37619
38602
  }
37620
38603
  }
37621
- const validCollectionTypes = ["pair", "triple", "set", "list"];
38604
+ const validCollectionTypes = ["pair", "set", "list"];
37622
38605
  if (flags.type && !validCollectionTypes.includes(flags.type)) {
37623
38606
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid --type "${flags.type}". Must be one of: ${validCollectionTypes.join(", ")}`);
37624
38607
  }
@@ -37650,11 +38633,14 @@ var handleSubmit = async (ctx, { flags, args }) => {
37650
38633
  if (streamInput) {
37651
38634
  operations = [];
37652
38635
  } else if (collectionType) {
38636
+ if (!flags.name) {
38637
+ usageError("Usage: wh commit submit --type <pair|set|list> --name <collection-name> --members <wref1,wref2,...>", "wh commit submit --type pair --name location-distance --members Location/a,Location/b");
38638
+ }
37653
38639
  if (!flags.members) {
37654
- usageError("Usage: wh commit submit --type <pair|triple|set|list> --members <wref1,wref2,...>", "wh commit submit --type pair --members Location/a,Location/b");
38640
+ usageError("Usage: wh commit submit --type <pair|set|list> --name <collection-name> --members <wref1,wref2,...>", "wh commit submit --type pair --name location-distance --members Location/a,Location/b");
37655
38641
  }
37656
38642
  const members = flags.members.split(",").map((m) => m.trim()).filter(Boolean);
37657
- const arityMap = { pair: 2, triple: 3 };
38643
+ const arityMap = { pair: 2 };
37658
38644
  const expected = arityMap[collectionType];
37659
38645
  if (expected && members.length !== expected) {
37660
38646
  throw new CliError(2 /* UserInput */, "USER_INPUT", `${collectionType} requires exactly ${expected} members, got ${members.length}`);
@@ -37667,6 +38653,7 @@ var handleSubmit = async (ctx, { flags, args }) => {
37667
38653
  operation: "add",
37668
38654
  kind: "collection",
37669
38655
  type: collectionType,
38656
+ name: flags.name,
37670
38657
  members
37671
38658
  }
37672
38659
  ];
@@ -37817,8 +38804,8 @@ var COMMIT_DOMAIN = defineDomain({
37817
38804
  `printf '%s\\n' '{"operation":"add","kind":"thing","name":"Player/alice","data":{"score":1}}' | wh commit submit --stream --stream-id bulk-2026-06-04 --skip-existing --repo acme/world -m "stdin stream"`,
37818
38805
  'wh commit submit --file dataset.jsonl --stream-id bulk-2026-06-04 --skip-existing --progress -m "bulk stream"',
37819
38806
  "wh shape template Session HypothesisCandidate -o ops.json",
37820
- "wh commit submit --type pair --members Location/a,Location/b",
37821
- 'wh commit submit --type set --members Location/a,Location/b,Location/c -m "Create location set"'
38807
+ "wh commit submit --type pair --name location-distance --members Location/a,Location/b",
38808
+ 'wh commit submit --type set --name active-locations --members Location/a,Location/b,Location/c -m "Create location set"'
37822
38809
  ],
37823
38810
  notes: [
37824
38811
  "Need to build an ops file? Run `wh shape template <Shape>` to scaffold the JSON from a shape definition, edit the FILL_IN placeholders, then pass it to `--file`.",
@@ -37832,7 +38819,7 @@ var COMMIT_DOMAIN = defineDomain({
37832
38819
  import {
37833
38820
  existsSync as existsSync7,
37834
38821
  mkdirSync as mkdirSync6,
37835
- readFileSync as readFileSync6,
38822
+ readFileSync as readFileSync7,
37836
38823
  renameSync as renameSync3,
37837
38824
  rmSync as rmSync4,
37838
38825
  writeFileSync as writeFileSync6
@@ -37868,7 +38855,7 @@ function loadInstallSnapshotCacheRaw(repoSlug) {
37868
38855
  if (!path2 || !existsSync7(path2))
37869
38856
  return null;
37870
38857
  try {
37871
- const raw = readFileSync6(path2, "utf-8");
38858
+ const raw = readFileSync7(path2, "utf-8");
37872
38859
  return JSON.parse(raw);
37873
38860
  } catch {
37874
38861
  return null;
@@ -38638,7 +39625,7 @@ function formatReservedNameWarning(name) {
38638
39625
  }
38639
39626
 
38640
39627
  // ../../packages/warmhub-cli/src/manifest/parser.ts
38641
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
39628
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
38642
39629
  import { resolve } from "node:path";
38643
39630
  function parseComponentPackage(dirPath) {
38644
39631
  const rootDir = resolve(dirPath);
@@ -38651,7 +39638,7 @@ function parseComponentPackage(dirPath) {
38651
39638
  }
38652
39639
  let componentRaw;
38653
39640
  try {
38654
- componentRaw = JSON.parse(readFileSync7(componentJsonPath, "utf-8"));
39641
+ componentRaw = JSON.parse(readFileSync8(componentJsonPath, "utf-8"));
38655
39642
  } catch (err) {
38656
39643
  errors.push(`Failed to parse warmhub/component.json: ${err instanceof Error ? err.message : String(err)}`);
38657
39644
  return { ok: false, errors, warnings };
@@ -38666,7 +39653,7 @@ function parseComponentPackage(dirPath) {
38666
39653
  }
38667
39654
  let manifestRaw;
38668
39655
  try {
38669
- manifestRaw = JSON.parse(readFileSync7(manifestJsonPath, "utf-8"));
39656
+ manifestRaw = JSON.parse(readFileSync8(manifestJsonPath, "utf-8"));
38670
39657
  } catch (err) {
38671
39658
  errors.push(`Failed to parse warmhub/manifest.json: ${err instanceof Error ? err.message : String(err)}`);
38672
39659
  return { ok: false, errors, warnings };
@@ -38867,7 +39854,7 @@ function formatLifecycleUrl(url, colors) {
38867
39854
  }
38868
39855
 
38869
39856
  // ../../packages/warmhub-cli/src/domains/component-utils.ts
38870
- import { readFileSync as readFileSync8 } from "node:fs";
39857
+ import { readFileSync as readFileSync9 } from "node:fs";
38871
39858
  function isRegisteredComponentSource(source) {
38872
39859
  return /^[a-z0-9-]+\/[a-z0-9-]+$/.test(source);
38873
39860
  }
@@ -38907,7 +39894,7 @@ function resolveMintedTokensFlag(args) {
38907
39894
  function readManifestArg(path2) {
38908
39895
  let raw;
38909
39896
  try {
38910
- raw = readFileSync8(path2, "utf8");
39897
+ raw = readFileSync9(path2, "utf8");
38911
39898
  } catch {
38912
39899
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Cannot read manifest file: ${path2}`, undefined, "Pass --manifest <path to warmhub/manifest.json>");
38913
39900
  }
@@ -41492,245 +42479,7 @@ var ORG_DOMAIN = defineDomain({
41492
42479
  });
41493
42480
 
41494
42481
  // ../../packages/warmhub-cli/src/domains/prime-content.md
41495
- var prime_content_default = `# WarmHub CLI Context
41496
- > **Context Recovery**: Run \`wh prime\` after compaction or new session
41497
-
41498
- ## Environment
41499
- {{REPO_LINE}}
41500
-
41501
- ## Core Concepts
41502
- - **Thing**: A named entity versioned by write operations. **Assertion**: A claim about a thing with shape-validated data.
41503
- - **Shape**: Schema defining data structure. **Write**: One or more add/revise/retract operations with per-operation results.
41504
- - **wref**: Reference as \`Shape/name\` (e.g., \`Player/alice\`). Cross-repo: \`wh:org/repo/Shape/name\`.
41505
-
41506
- ## Versioned Things
41507
- - \`Shape/name\` identifies the logical thing. \`Shape/name@vN\` pins an exact version.
41508
- - Read surfaces may show pinned wrefs (\`@vN\`) in data. Treat them as version metadata, not a different thing.
41509
-
41510
- ## Key Workflows
41511
-
41512
- **Write data** (discover shapes → scaffold ops → submit):
41513
- \`\`\`bash
41514
- wh shape list --repo org/repo # list available shapes
41515
- wh shape view ShapeName --repo org/repo # inspect fields
41516
- wh shape template ShapeName -o ops.json --repo org/repo # scaffold write ops (supports --kind assertion)
41517
- # edit ops.json — fill FILL_IN placeholders — then:
41518
- wh commit submit --file ops.json -m "msg" --repo org/repo # submit operations (bare \`wh commit\` also works)
41519
- # or single assertion (no file needed):
41520
- wh assertion create --shape ShapeName --about Target/name --name my-assertion --data '{"field":1}' --repo org/repo
41521
- # Relay failed operation details when present. Never hand-guess ops JSON — use \`wh shape template <Shape>\`.
41522
- \`\`\`
41523
-
41524
- **Read data:**
41525
- \`\`\`bash
41526
- wh thing list --repo org/repo # all things at HEAD
41527
- wh thing view Shape/name --repo org/repo # inspect a thing
41528
- wh thing query --shape MyShape --repo org/repo # find things by shape
41529
- wh thing about Shape/name --repo org/repo # assertions about a thing
41530
- wh assertion list --repo org/repo # all assertions at HEAD
41531
- wh thing history Shape/name --repo org/repo # version history
41532
-
41533
- # Batch read — wh thing view is variadic (max 500 wrefs/call):
41534
- wh thing view Player/alice Player/bob # variadic positionals
41535
- wh thing view --file wrefs.txt --json # one wref per line
41536
- cat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped* requested wref
41537
- \`\`\`
41538
-
41539
- ## Wref Quick Reference
41540
-
41541
- **Batch tokens** (in write operations):
41542
- - \`$N\` in last segment of ADD name — generates an opaque stream-scoped identifier
41543
- - \`#N\` anywhere — references value allocated by \`$N\`
41544
- - Example: \`Player/player-$1\` + about \`Player/player-#1\` → \`Player/player-a1b2c3d4e5f6a7b8\`
41545
-
41546
- ## Command Reference
41547
-
41548
- **Global flags**: \`--repo\`, \`--format\`, \`--json\`, \`--live\`
41549
- ### thing — Thing operations
41550
- - \`wh thing list [--shape] [--kind] [--match] [--include-retracted]\` — Current HEAD state
41551
- - \`wh thing view [<wref>...] [--file <path>] [--version] [--depth] [--include-retracted]\` — Thing details. Variadic: singleton routes to \`thing.get\`; multiple wrefs or \`--file\` routes to batch fetch (max 500). Wrefs from positionals, \`--file <path>\` (use \`--file=-\` for stdin), bare \`-\` positional (stdin), or piped stdin when no other source given. Explicit positionals never implicitly consume stdin. \`--version\` implies \`--include-retracted\`. Batch returns \`{ requested, items, missing }\` in \`--json\`; \`--format jsonl\` emits one record per deduped requested wref.
41552
- - \`wh thing history [wref] [--shape] [--about] [--include-retracted]\` — Version history
41553
- - \`wh thing resolve <wref>\` — Resolve wref
41554
- - \`wh thing create <name|Shape/name> --data <json-object> [--shape] [--message] [--committer]\` — Create
41555
- - \`wh thing revise <name> [--data] [--message] [--committer] [--expected-version]\` — Revise (CONFLICT if HEAD≠n)
41556
- - \`wh thing retract <wref> -m <message> [--reason] [--kind]\` — Retract
41557
- - \`wh thing query [--shape] [--kind] [--about] [--match]\` — Query by filters
41558
- - \`wh thing search <query> [--shape] [--kind] [--about] [--mode]\` — Search text
41559
- - \`wh thing rename <Shape/oldName> <newName>\` — Rename
41560
- - \`wh thing refs <wref> [--inbound] [--outbound] [--field]\` — Show field references; use \`wh thing about\` for assertions about a thing
41561
- - \`wh thing about <wref> [--shape] [--match] [--depth] [--resolve-collections] [--limit] [--include-retracted]\` — Show assertions about the target identity; \`--resolve-collections\` expands collection members for bare/@HEAD/@ALL inputs, not pinned @vN
41562
-
41563
- ### commit — Write operations
41564
- - \`wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--expected-version] [--chunk-size] [--skip-existing] [--progress] [--stream-id]\` — Submit operations (bare \`wh commit\` is equivalent). Use \`--file ops.jsonl --stream-id <id> --chunk-size 5000 --skip-existing\` for bulk ingest.
41565
- - \`wh shape template <shape> [shape2 ...] [--kind] [--operation add|revise|retract] [-o file]\` — Generate sample ops
41566
-
41567
- ### assertion — Assertion operations
41568
- - \`wh assertion list [--about wref] [--shape] [--match] [--include-retracted]\` — Browse assertions
41569
- - \`wh assertion view <wref> [--version] [--include-retracted]\` — Assertion details
41570
- - \`wh assertion create [--name] [--shape] [--data] [--about] [--message] [--committer]\` — Create assertion
41571
- - \`wh assertion revise <wref> --data <json> [--message] [--committer]\` — Revise assertion
41572
- - \`wh assertion retract <wref> -m <message> [--reason] [--committer]\` — Retract assertion
41573
- - \`wh assertion history <wref> [--include-retracted]\` — Assertion history
41574
-
41575
- ### shape — Shape management
41576
- - \`wh shape list [--match] [--include-retracted]\` — List all shapes
41577
- - \`wh shape view <name> [--include-retracted]\` — Shape details
41578
- - \`wh shape revise <name> [--fields]\` — Revise shape
41579
- - \`wh shape create <name> [--fields]\` — Create shape
41580
- - \`wh shape retract <name> -m <message> [--reason]\` — Retract shape
41581
- - \`wh shape history <name> [--include-retracted]\` — Shape history
41582
- - \`wh shape rename <oldName> <newName>\` — Rename shape
41583
-
41584
- ### repo — Repository management
41585
- - \`wh repo create <org/name> [--display-name] [--description] [--visibility]\` — Create repo
41586
- - \`wh repo list [org]\` — List repos
41587
- - \`wh repo view [org/repo]\` — Repo details
41588
-
41589
- ### org — Organization management
41590
- - \`wh org create <name> [--display-name]\` — Create a new organization
41591
- - \`wh org view <name>\` — View organization details (alias: info)
41592
- - \`wh org list\` — List all organizations
41593
-
41594
- ### sub — Subscription management
41595
- - \`wh sub create <name> [flags]\` — Create a subscription
41596
- - \`wh sub view <name>\` — View subscription details
41597
- - \`wh sub list\` — List all subscriptions
41598
- - \`wh sub log <name>\` — Tail subscription delivery feed
41599
- - \`wh sub attempts <runId>\` — Show attempt history for a run
41600
- - \`wh sub pause <name>\` — Pause a subscription
41601
- - \`wh sub resume <name>\` — Resume a paused subscription
41602
- - \`wh sub bind <name> [--credentials]\` — Bind a credential set to a subscription for webhook auth
41603
- - \`wh sub unbind <name>\` — Remove credential binding from a subscription
41604
- - \`wh sub delete <name>\` — Delete a subscription
41605
-
41606
- ### notifications — Action notification listing
41607
- - \`wh notifications [--limit] [--since]\` — List repo-scoped action notifications
41608
-
41609
- ### credential — Credential set management
41610
- - \`wh credential create <name> [--repo org/repo | --org org] [--scope] [--description]\` — Create an empty credential set
41611
- - \`wh credential list [--repo org/repo | --org org]\` — List credential sets accessible from a repo or org
41612
- - \`wh credential view <name> [--repo org/repo | --org org]\` — View a credential set (key names only, no values)
41613
- - \`wh credential delete <name> [--repo org/repo | --org org]\` — Delete a credential set and its Vault object
41614
- - \`wh credential set <setName> [<keyName>] [--repo org/repo | --org org] [--value]\` — Set credential key(s). With \`<keyName>\`: single-key form (reads value from \`--value\` or stdin). Without \`<keyName>\`: batch form (reads JSON object from stdin, e.g. \`{"KEY":"val"}\`)
41615
- - \`wh credential unset <setName> <keyName> [--repo org/repo | --org org]\` — Remove a key from a credential set
41616
- - \`wh credential audit <setName> [--repo org/repo | --org org]\` — View audit log for a credential set
41617
- - \`wh credential revoke <setName> [--repo org/repo | --org org] [--reason]\` — Revoke a credential set (blocks new binds and strips auth from existing webhook deliveries)
41618
-
41619
- ### component — Component management
41620
- - \`wh component validate <path>\` — Validate package
41621
- - \`wh component install <org/name>\` — Install a registered component
41622
- - \`wh component register <name> --org <org> --manifest <path> [flags]\` — Register component identity
41623
- - \`wh component unregister <org/name>\` — Remove a registered component identity
41624
- - \`wh component registry list --org <org>\` — List registered components
41625
- - \`wh component registry view <org/name>\` — View a registered component
41626
- - \`wh component registry update <org/name> [flags]\` — Update a registered component
41627
- - \`wh component list\` — List installed components
41628
- - \`wh component update <org/name>\` — Update installed component
41629
- - \`wh component view <org/name>\` — Show component details (alias: show)
41630
- - \`wh component doctor <org/name>\` — Run component health checks
41631
- - \`wh component teardown <org/name>\` — Pause component subscriptions
41632
-
41633
- ### Getting More Info
41634
- - \`wh help\` — full help overview
41635
- - \`wh <domain>\` — list verbs for a domain
41636
- - \`wh <domain> <verb> --help\` — verb details with flags and examples
41637
- - \`wh help --format json\` — full CLI spec as JSON (best for agents)
41638
-
41639
- ## Common Workflows
41640
-
41641
- **Explore a repo:**
41642
- \`\`\`bash
41643
- wh thing list --repo org/repo # see all things in HEAD
41644
- wh thing view Shape/name --repo org/repo # inspect a specific thing
41645
- wh thing history Shape/name --repo org/repo # inspect version history
41646
- wh thing about Shape/name # assertions about a thing
41647
- \`\`\`
41648
-
41649
- **Create an assertion** (most common write):
41650
- \`\`\`bash
41651
- # --about takes a wref (Shape/name). Use \`wh thing list\` to find valid wrefs.
41652
- wh assertion create --shape MyShape --about TargetShape/target-name \\
41653
- --name my-assertion --data '{"field_a":1,"field_b":"value"}' --repo org/repo
41654
- # Output includes per-operation status; relay failures when present.
41655
- \`\`\`
41656
-
41657
- **Create via write entrypoint** (alternative, supports batches and streams):
41658
- \`\`\`bash
41659
- wh commit submit --add my-item --shape MyShape --kind assertion \\
41660
- --about TargetShape/target-name --data '{"field_a":1}' --repo org/repo
41661
- \`\`\`
41662
-
41663
- **Batch write via file** (generate template → edit → submit):
41664
- \`\`\`bash
41665
- wh shape template Hypothesis Evidence -o ops.json # add --kind assertion --about TargetShape/name for assertions
41666
- # edit ops.json — fill FILL_IN placeholders
41667
- wh commit submit --file ops.json -m "batch update" # submit all operations (bare \`wh commit\` is equivalent)
41668
- # --file format: docs.warmhub.ai/cli-reference/commit-operations
41669
- \`\`\`
41670
-
41671
- **Bulk write (JSONL stream, >1k ops)** — one chunked stream beats per-record commits (~60× faster):
41672
- \`\`\`bash
41673
- wh shape template MyShape -o ops.jsonl # one op per line (.jsonl)
41674
- ID="bulk-$(date +%s)" # choose your own; set it up front so reruns are safe
41675
- wh commit submit --file ops.jsonl --stream-id "$ID" --chunk-size 5000 \\
41676
- --skip-existing --progress -m "bulk ingest" --repo org/repo
41677
- # --chunk-size: ops per append chunk (default 1000, max 10000); chunk≈1 is ~60× slower
41678
- # --skip-existing: skips already-written add ops (drops per-row read-before-write)
41679
- # Add-stream restart: rerun the WHOLE file with the SAME --stream-id.
41680
- # Fixed-name adds are idempotent via --skip-existing; tokenized ($N/#N) names
41681
- # derive from streamId, so rerun rebuilds token state. Omitting --stream-id is
41682
- # rejected because reruns must reuse the same token namespace. Mid-stream resume
41683
- # is not a CLI mode. Mixed revise/retract JSONL streams are not full-rerun safe
41684
- # after an ambiguous append; inspect repo state and reconcile explicitly.
41685
- \`\`\`
41686
-
41687
- **Create collections:**
41688
- \`\`\`bash
41689
- wh commit submit --type pair --members Location/a,Location/b --repo org/repo
41690
- wh assertion create --shape Distance --about pair:Location/a,Location/b --data '{"value":5}' --repo org/repo
41691
- \`\`\`
41692
-
41693
- **Modify data:**
41694
- \`\`\`bash
41695
- wh thing revise Shape/name --data '{"x":5,"y":3}' -m "update" --repo org/repo
41696
- wh thing retract Shape/old-item -m "withdrawn" --reason "data feed contaminated" --repo org/repo
41697
- \`\`\`
41698
-
41699
- **Query and filter:**
41700
- \`\`\`bash
41701
- wh thing query --shape MyShape # by shape
41702
- wh thing query --kind assertion --about Shape/name # by kind + target
41703
- wh thing history Shape/name --limit 10 # version history
41704
- \`\`\`
41705
-
41706
- ## Built-in Content shape
41707
-
41708
- WarmHub repos expose three well-known content wrefs:
41709
- - \`Content/Readme\` — stored markdown for humans
41710
- - \`Content/Agents\` — stored markdown guidance for AI agents
41711
- - \`Content/LlmsTxt\` — synthesized per-request sitemap (read-only)
41712
-
41713
- Fetch via \`wh repo content get --kind readme|agents|llms-txt\`,
41714
- \`client.repo.getReadme/getAgents/getLlmsTxt\`, MCP \`warmhub_repo_content_get\`,
41715
- or raw HTTP \`GET /{org}/{repo}/readme.md|agents.md|llms.txt\`.
41716
- See \`wh repo describe\` → \`additionalInformation\` for the discovery field.
41717
-
41718
- ## Query Discipline
41719
- - Plan the repo, shapes, and wrefs you need before the first query.
41720
- - Gather the needed facts from one repo before switching to another.
41721
- - Do the queries first, then write one complete answer.
41722
-
41723
- ## Agent Tips
41724
- - **Always run commands for live data** — this context describes the CLI, not repo contents
41725
- - **Before writing, discover wrefs** — run \`wh thing list\` or \`wh shape list\`
41726
- - **Shape field types**: \`string\`, \`number\`, \`boolean\`, \`wref\`, arrays, optionals, nested objects
41727
- - **Write commands return per-operation results** — relay failures and affected wrefs to the user
41728
- - **Pass data inline** with \`--data '{...}'\` — do NOT create temp files
41729
- - Add \`--json\` to any command for machine-readable JSON output
41730
- - **Aliases teach canonical commands** — executing aliases emit a status hint; misleading lifecycle aliases reject and point to \`retract\`
41731
- - Writes go through \`wh commit submit\` (or bare \`wh commit\`) or wrappers (\`create\`, \`revise\`, \`retract\`, \`assertion create\`). \`retract\` irreversibly withdraws an identity and creates a history entry.
41732
- - Use \`wh doctor\` to check environment health
41733
- `;
42482
+ var prime_content_default = "# WarmHub CLI Context\n> **Context Recovery**: Run `wh prime` after compaction or new session\n\n## Environment\n{{REPO_LINE}}\n\n## Core Concepts\n- **Thing**: A named entity versioned by writes. **Assertion**: A thing that makes a shape-validated claim about another thing.\n- **Shape**: A thing defining data structure; every other thing has one. **Write**: One or more add/revise/retract operations with per-operation results.\n- **wref**: A reference to a thing. Local: `Player` (the shape) or `Player/alice` (a thing with that shape). Cross-repo: `wh:org/repo/Shape` or `wh:org/repo/Shape/name`.\n\n## Versioned Wrefs\n- `Shape` addresses the shape itself; `Shape/name` addresses a thing with that shape. `@vN` pins either.\n- Floating write refs to retracted targets fail; existing pinned versions remain valid.\n- Rename invalidates old spellings, including `@vN`; the new name resolves history.\n- Untyped wrefs accept any thing. `wref<T>` requires the target's shape to be `T`; a shape has no shape, so never satisfies it. `wref?` coalesces only `thing_absent`, never a missing shape.\n\n## Key Workflows\n\n**Write data** (discover shapes → scaffold ops → submit):\n```bash\nwh shape list --repo org/repo # list available shapes\nwh shape view ShapeName --repo org/repo # inspect fields\nwh shape template ShapeName -o ops.json --repo org/repo # scaffold write ops (supports --kind assertion)\n# edit ops.json — fill FILL_IN placeholders — then:\nwh commit submit --file ops.json -m \"msg\" --repo org/repo # submit operations (bare `wh commit` also works)\n# or single assertion (no file needed):\nwh assertion create --shape ShapeName --about Target/name --name my-assertion --data '{\"field\":1}' --repo org/repo\n# Relay failed operation details when present. Never hand-guess ops JSON — use `wh shape template <Shape>`.\n```\n\n**Read data:**\n```bash\nwh thing list --repo org/repo # all things at HEAD\nwh thing view Shape/name --repo org/repo # inspect a thing\nwh thing query --shape MyShape --repo org/repo # find things by shape\nwh thing about Shape --repo org/repo # assertions about a shape\nwh assertion list --repo org/repo # all assertions at HEAD\nwh thing history Shape/name --repo org/repo # version history\n\n# Batch read — wh thing view is variadic (max 500 wrefs/call):\nwh thing view Player/alice Player/bob # variadic positionals\nwh thing view --file wrefs.txt --json # one wref per line\ncat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped* requested wref\n```\n\n## Wref Quick Reference\n\nWrites use explicit names and wrefs. Untyped fields, collection members,\nassertion `about`, and committers accept any thing. Create a deterministic\ntarget before referencing it in the same commit.\n\n## Command Reference\n\n**Global flags**: `--repo`, `--format`, `--json`, `--live`\n### thing — Thing operations\n- `wh thing list [--shape] [--kind] [--match] [--include-retracted]` — Current HEAD state\n- `wh thing view [<wref>...] [--file <path>] [--version] [--depth] [--include-retracted] [--data-mode auto|full]` — Thing details. Variadic (max 500). `--version` implies `--include-retracted`. Batch JSON returns `{ requested, items, missing }`; jsonl emits one row per deduped requested wref. Large Set/List bodies summarize by default; use `--data-mode full` for canonical collection JSON.\n- `wh thing history [wref] [--shape] [--about] [--include-retracted]` — Version history\n- `wh thing resolve <wref>` — Resolve a wref to its canonical thing identity\n- `wh thing create <name|Shape/name> --data <json-object> [--shape] [--message] [--committer]` — Create\n- `wh thing revise <name> [--data] [--message] [--committer] [--expected-version]` — Revise (CONFLICT if HEAD≠n)\n- `wh thing retract <wref> -m <message> [--reason] [--kind]` — Retract\n- `wh thing query [--shape] [--kind] [--about] [--match]` — Query by filters\n- `wh thing search <query> [--shape] [--kind] [--about] [--mode]` — Search text\n- `wh thing rename <Shape/oldName> <newName>` — Rename\n- `wh thing refs <wref> [--inbound] [--outbound] [--field]` — Show field references; use `wh thing about` for assertions about the target thing\n- `wh thing about <wref> [--shape] [--match] [--depth] [--resolve-collections] [--limit] [--include-retracted]` — Show assertions about the target identity; `--resolve-collections` expands collection members for bare/@HEAD/@ALL inputs, not pinned @vN\n\n### commit — Write operations\n- `wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--expected-version] [--chunk-size] [--skip-existing] [--progress] [--stream-id]` — Submit operations (bare `wh commit` is equivalent). Use `--file ops.jsonl --stream-id <id> --chunk-size 5000 --skip-existing` for bulk ingest.\n- `wh shape template <shape> [shape2 ...] [--kind] [--operation add|revise|retract] [-o file]` — Generate sample ops\n\n### assertion — Assertion operations\n- `wh assertion list [--about wref] [--shape] [--match] [--include-retracted]` — Browse assertions\n- `wh assertion view <wref> [--version] [--include-retracted]` — Assertion details\n- `wh assertion create [--name] [--shape] [--data] [--about] [--message] [--committer]` — Create assertion\n- `wh assertion revise <wref> --data <json> [--message] [--committer]` — Revise assertion\n- `wh assertion retract <wref> -m <message> [--reason] [--committer]` — Retract assertion\n- `wh assertion history <wref> [--include-retracted]` — Assertion history\n\n### shape — Shape management\n- `wh shape list [--match] [--include-retracted]` — List all shapes\n- `wh shape view <name> [--include-retracted]` — Shape details\n- `wh shape revise <name> [--fields]` — Revise shape\n- `wh shape create <name> [--fields]` — Create shape\n- `wh shape retract <name> -m <message> [--reason]` — Retract shape\n- `wh shape history <name> [--include-retracted]` — Shape history\n- `wh shape rename <oldName> <newName>` — Rename shape\n\n### repo — Repository management\n- `wh repo create <org/name> [--display-name] [--description] [--visibility]` — Create repo\n- `wh repo list [org]` — List repos\n- `wh repo view [org/repo]` — Repo details\n\n### org — Organization management\n- `wh org create <name> [--display-name]` — Create a new organization\n- `wh org view <name>` — View organization details (alias: info)\n- `wh org list` — List all organizations\n\n### sub — Subscription management\n- `wh sub create <name> [flags]` — Create a subscription\n- `wh sub view <name>` — View subscription details\n- `wh sub list` — List all subscriptions\n- `wh sub log <name>` — Tail subscription delivery feed\n- `wh sub attempts <runId>` — Show attempt history for a run\n- `wh sub pause <name>` — Pause a subscription\n- `wh sub resume <name>` — Resume a paused subscription\n- `wh sub bind <name> [--credentials]` — Bind a credential set to a subscription for webhook auth\n- `wh sub unbind <name>` — Remove credential binding from a subscription\n- `wh sub delete <name>` — Delete a subscription\n\n### notifications — Action notification listing\n- `wh notifications [--limit] [--since]` — List repo-scoped action notifications\n\n### credential — Credential set management\n- `wh credential create <name> [--repo org/repo | --org org] [--scope] [--description]` — Create an empty credential set\n- `wh credential list [--repo org/repo | --org org]` — List credential sets accessible from a repo or org\n- `wh credential view <name> [--repo org/repo | --org org]` — View a credential set (key names only, no values)\n- `wh credential delete <name> [--repo org/repo | --org org]` — Delete a credential set and its Vault object\n- `wh credential set <setName> [<keyName>] [--repo org/repo | --org org] [--value]` — Set credential key(s). With `<keyName>`: single-key form (reads value from `--value` or stdin). Without `<keyName>`: batch form (reads JSON object from stdin, e.g. `{\"KEY\":\"val\"}`)\n- `wh credential unset <setName> <keyName> [--repo org/repo | --org org]` — Remove a key from a credential set\n- `wh credential audit <setName> [--repo org/repo | --org org]` — View audit log for a credential set\n- `wh credential revoke <setName> [--repo org/repo | --org org] [--reason]` — Revoke a credential set (blocks new binds and strips auth from existing webhook deliveries)\n\n### component — Component management\n- `wh component validate <path>` — Validate package\n- `wh component install <org/name>` — Install a registered component\n- `wh component register <name> --org <org> --manifest <path> [flags]` — Register component identity\n- `wh component unregister <org/name>` — Remove a registered component identity\n- `wh component registry list --org <org>` — List registered components\n- `wh component registry view <org/name>` — View a registered component\n- `wh component registry update <org/name> [flags]` — Update a registered component\n- `wh component list` — List installed components\n- `wh component update <org/name>` — Update installed component\n- `wh component view <org/name>` — Show component details (alias: show)\n- `wh component doctor <org/name>` — Run component health checks\n- `wh component teardown <org/name>` — Pause component subscriptions\n\n### Getting More Info\n- `wh help` — full help overview\n- `wh <domain>` — list verbs for a domain\n- `wh <domain> <verb> --help` — verb details with flags and examples\n- `wh help --format json` — full CLI spec as JSON (best for agents)\n\n## Common Workflows\n\n**Explore a repo:**\n```bash\nwh thing list --repo org/repo # see all things in HEAD\nwh thing view Shape/name --repo org/repo # inspect a specific thing\nwh thing history Shape/name --repo org/repo # inspect version history\nwh thing about Shape/name # assertions about thing/shape\n```\n\n**Create an assertion** (most common write):\n```bash\n# --about takes an untyped target wref: Shape or Shape/name.\nwh assertion create --shape MyShape --about TargetShape/target-name \\\n --name my-assertion --data '{\"field_a\":1,\"field_b\":\"value\"}' --repo org/repo\n# Output includes per-operation status; relay failures when present.\n```\n\n**Create via write entrypoint** (alternative, supports batches and streams):\n```bash\nwh commit submit --add my-item --shape MyShape --kind assertion \\\n --about TargetShape/target-name --data '{\"field_a\":1}' --repo org/repo\n```\n\n**Batch write via file** (generate template → edit → submit):\n```bash\nwh shape template Hypothesis Evidence -o ops.json # add --kind assertion --about TargetShape/name for assertions\n# edit ops.json — fill FILL_IN placeholders\nwh commit submit --file ops.json -m \"batch update\" # submit all operations (bare `wh commit` is equivalent)\n# --file format: docs.warmhub.ai/cli-reference/commit-operations\n```\n\n**Bulk write (JSONL stream, >1k ops)** — one chunked stream beats per-record commits (~60× faster):\n```bash\nwh shape template MyShape -o ops.jsonl # one op per line (.jsonl)\nID=\"bulk-$(date +%s)\" # choose your own; set it up front so reruns are safe\nwh commit submit --file ops.jsonl --stream-id \"$ID\" --chunk-size 5000 \\\n --skip-existing --progress -m \"bulk ingest\" --repo org/repo\n# --chunk-size: ops per append chunk (default 1000, max 10000); chunk≈1 is ~60× slower\n# --skip-existing: skips already-written add ops (drops per-row read-before-write)\n# Add-stream restart: rerun the WHOLE file with the SAME --stream-id.\n# Fixed-name adds are idempotent via --skip-existing. Mid-stream resume is not\n# a CLI mode. Mixed revise/retract JSONL streams are not full-rerun safe after\n# an ambiguous append; inspect repo state and reconcile explicitly.\n```\n\n**Create collections:**\n```bash\nwh commit submit --type pair --name location-distance --members Location,Location/a --repo org/repo\nwh assertion create --shape Distance --about Pair/location-distance --data '{\"value\":5}' --repo org/repo\n```\n\n**Modify data:**\n```bash\nwh thing revise Shape/name --data '{\"x\":5,\"y\":3}' -m \"update\" --repo org/repo\nwh thing retract Shape/old-item -m \"withdrawn\" --reason \"data feed contaminated\" --repo org/repo\n```\n\n**Query and filter:**\n```bash\nwh thing query --shape MyShape # by shape\nwh thing query --kind assertion --about Shape/name # by kind + target\nwh thing history Shape/name --limit 10 # version history\n```\n\n## Built-in Content shape\n\nWarmHub repos expose three well-known content wrefs:\n- `Content/Readme` — stored markdown for humans\n- `Content/Agents` — stored markdown guidance for AI agents\n- `Content/LlmsTxt` — synthesized per-request sitemap (read-only)\n\nFetch via `wh repo content get --kind readme|agents|llms-txt`,\n`client.repo.getReadme/getAgents/getLlmsTxt`, MCP `warmhub_repo_content_get`,\nor raw HTTP `GET /{org}/{repo}/readme.md|agents.md|llms.txt`.\nSee `wh repo describe` → `additionalInformation` for the discovery field.\n\n## Query Discipline\n- Plan the repo, shapes, and wrefs you need before the first query.\n- Gather the needed facts from one repo before switching to another.\n- Do the queries first, then write one complete answer.\n\n## Agent Tips\n- **Always run commands for live data** — this context describes the CLI, not repo contents\n- **Before writing, discover wrefs** — run `wh thing list` or `wh shape list`\n- **Shape field types**: `string`, `number`, `boolean`, `wref`, arrays, optionals, nested objects\n- **Write commands return per-operation results** — relay failures and affected wrefs to the user\n- **Pass data inline** with `--data '{...}'` — do NOT create temp files\n- Add `--json` to any command for machine-readable JSON output\n- **Aliases teach canonical commands** — executing aliases emit a status hint; misleading lifecycle aliases reject and point to `retract`\n- Writes go through `wh commit submit` (or bare `wh commit`) or wrappers (`create`, `revise`, `retract`, `assertion create`). `retract` irreversibly withdraws an identity and creates a history entry.\n- Use `wh doctor` to check environment health\n";
41734
42483
 
41735
42484
  // ../../packages/warmhub-cli/src/domains/prime.ts
41736
42485
  function buildMarkdown(config) {
@@ -41776,9 +42525,8 @@ var wrefSyntax = {
41776
42525
  "Player/alice",
41777
42526
  "GameState/round-1/state"
41778
42527
  ],
41779
- canonicalFormat: "wh:org/repo/Shape/name",
41780
- versionModifiers: ["@HEAD", "@vN", "@ALL"],
41781
- batchTokens: { allocate: "$N", reference: "#N" }
42528
+ canonicalFormat: "wh:org/repo/Shape or wh:org/repo/Shape/name",
42529
+ versionModifiers: ["@HEAD", "@vN", "@ALL"]
41782
42530
  };
41783
42531
  var handlePrime = async (ctx) => {
41784
42532
  writeOutput(ctx, {
@@ -42886,7 +43634,7 @@ var retractFlags3 = {
42886
43634
  reason: flag.string({ description: "Reason for retraction (<=500 chars)" }),
42887
43635
  message: flag.string({ short: "m", description: "Commit message" }),
42888
43636
  committer: flag.string({
42889
- description: "Committer thing wref (e.g. Agent/bot-1)"
43637
+ description: "Committer wref (shape or shaped thing; e.g. Agent/bot-1)"
42890
43638
  })
42891
43639
  };
42892
43640
  var handleCreate6 = async (ctx, { flags, args }) => {
@@ -43062,7 +43810,7 @@ var createFlags8 = {
43062
43810
  description: "Shape to subscribe to"
43063
43811
  }),
43064
43812
  event: flag.string({
43065
- description: "Event to watch: commit (default), repo.renamed, or org.renamed"
43813
+ description: "Event to watch: commit (default), repo.renamed, org.renamed, thing.renamed, or shape.renamed"
43066
43814
  }),
43067
43815
  org: flag.string({
43068
43816
  description: "Org slug for an org-scoped subscription (org.renamed)"
@@ -43259,7 +44007,7 @@ function scopeLabel(scope) {
43259
44007
  // ../../packages/warmhub-cli/src/domains/sub/handlers-create.ts
43260
44008
  var handleCreate7 = async (ctx, { flags, args }) => {
43261
44009
  const name = args[0] ?? flags.name;
43262
- const usage = "Usage: wh sub create <name> (--repo org/repo | --org org) [--event commit|repo.renamed|org.renamed] [options]";
44010
+ const usage = "Usage: wh sub create <name> (--repo org/repo | --org org) [--event commit|repo.renamed|org.renamed|thing.renamed|shape.renamed] [options]";
43263
44011
  const example = `wh sub create signal-hook --repo myorg/myrepo --on Signal --filter '{"shape":"Signal"}' --webhook-url https://example.com/hook`;
43264
44012
  if (!name) {
43265
44013
  usageError(usage, example);
@@ -43298,7 +44046,7 @@ var handleCreate7 = async (ctx, { flags, args }) => {
43298
44046
  return;
43299
44047
  }
43300
44048
  const { org, repo } = resolveRepoContext(ctx);
43301
- if (eventType === "repo.renamed") {
44049
+ if (eventType !== "commit") {
43302
44050
  rejectCommitFlags(flags, eventType);
43303
44051
  const result2 = await ctx.client.subscription.create({
43304
44052
  orgName: org,
@@ -43311,7 +44059,7 @@ var handleCreate7 = async (ctx, { flags, args }) => {
43311
44059
  });
43312
44060
  writeOutput(ctx, result2, () => {
43313
44061
  const c = ctx.colors;
43314
- ctx.status(`${c.green}Created subscription${c.reset} ${c.cyan}${name}${c.reset} on ${c.cyan}${org}/${repo}${c.reset} (repo.renamed)`);
44062
+ ctx.status(`${c.green}Created subscription${c.reset} ${c.cyan}${name}${c.reset} on ${c.cyan}${org}/${repo}${c.reset} (${eventType})`);
43315
44063
  });
43316
44064
  return;
43317
44065
  }
@@ -44102,7 +44850,7 @@ var TOKEN_DOMAIN = defineDomain({
44102
44850
  });
44103
44851
 
44104
44852
  // ../../packages/warmhub-cli/src/update-check-cache.ts
44105
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
44853
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
44106
44854
  import { homedir as homedir5 } from "node:os";
44107
44855
  import { dirname as dirname7, resolve as resolve3 } from "node:path";
44108
44856
 
@@ -44171,7 +44919,7 @@ var WH_CACHE_PACKAGE_NAME = WH_CLI_PACKAGE_NAME;
44171
44919
  var cachePath = (homePath) => resolve3(homePath, ".warmhub", "cli", "update-check.json");
44172
44920
  var readCache = (homePath) => {
44173
44921
  try {
44174
- return JSON.parse(readFileSync9(cachePath(homePath), "utf8"));
44922
+ return JSON.parse(readFileSync10(cachePath(homePath), "utf8"));
44175
44923
  } catch {
44176
44924
  return;
44177
44925
  }
@@ -44195,7 +44943,7 @@ var markUpdateNoticeShown = ({
44195
44943
 
44196
44944
  // ../../packages/warmhub-cli/src/domains/update-install.ts
44197
44945
  import { spawnSync as spawnSync2 } from "node:child_process";
44198
- import { existsSync as existsSync9, readFileSync as readFileSync10, realpathSync } from "node:fs";
44946
+ import { existsSync as existsSync9, readFileSync as readFileSync11, realpathSync } from "node:fs";
44199
44947
  import { homedir as homedir6 } from "node:os";
44200
44948
  import { dirname as dirname8, resolve as resolve4 } from "node:path";
44201
44949
  var DEV_INSTALL_PACKAGE_SEARCH_DEPTH = 8;
@@ -44345,7 +45093,7 @@ var isDevInstall = (scriptPath) => {
44345
45093
  for (let i = 0;i < DEV_INSTALL_PACKAGE_SEARCH_DEPTH; i += 1) {
44346
45094
  const pkgPath = resolve4(dir, "package.json");
44347
45095
  try {
44348
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
45096
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
44349
45097
  if (pkg.name === "@warmhub/cli") {
44350
45098
  if (pkg.private === true)
44351
45099
  return true;
@@ -44631,6 +45379,7 @@ function registerAllDomains(registry2) {
44631
45379
  registry2.register(INIT_DOMAIN);
44632
45380
  registry2.register(REPO_DOMAIN);
44633
45381
  registry2.register(THING_DOMAIN);
45382
+ registry2.register(COLLECTION_DOMAIN);
44634
45383
  registry2.register(COMMIT_DOMAIN);
44635
45384
  registry2.register(ASSERTION_DOMAIN);
44636
45385
  registry2.register(SHAPE_DOMAIN);
@@ -45753,7 +46502,7 @@ function resolveLogLevel(flags, env) {
45753
46502
  // package.json
45754
46503
  var package_default3 = {
45755
46504
  name: "@warmhub/cli",
45756
- version: "0.67.0",
46505
+ version: "0.69.0",
45757
46506
  private: false,
45758
46507
  type: "module",
45759
46508
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -46408,4 +47157,4 @@ if (!updateCheckSuppressedByArgv && shouldRunUpdateCheck(updateEligibility)) {
46408
47157
  var interceptedExitCode = await maybeHandleComponentShellBoundary(dispatchArgv);
46409
47158
  process.exitCode = interceptedExitCode === undefined ? await runCli(dispatchArgv, { version: package_default3.version }) : interceptedExitCode;
46410
47159
 
46411
- //# debugId=F47337F68EA6DF1364756E2164756E21
47160
+ //# debugId=C8123553A9E5F6D464756E2164756E21