@tablation/crew 0.66.0 → 0.67.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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +27 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -263,7 +263,7 @@ crew inbox [--member NAME] your tickets across every workspace
263
263
  crew connect WS[/PROJECT] resolve a workspace/project's ids into the state tree
264
264
  crew repos add [ROUTE] [NAME] [PATH] attach a local checkout to a route in crew.yaml, creating its Repos row if missing (--dry-run previews)
265
265
  crew repos list ROUTE the checkouts a route has, and the tracker Repos row each matches
266
- crew repos hooks [ROUTE] NAME suggest test/build hooks for a checkout (--write route|repo writes them, --recipes, --agent, --json)
266
+ crew repos hooks [ROUTE] NAME suggest test/build hooks for a checkout (--write route|repo writes them, --recipes, --hook KEY=CMD, --agent, --json)
267
267
  crew install [--replace] write and load this platform's scheduler unit (macOS: clears a crew installed from another path)
268
268
  crew uninstall [--all] unload and remove it (--all: every crew launchd unit, whichever path it came from)
269
269
  ```
package/dist/cli.js CHANGED
@@ -5200,6 +5200,7 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
5200
5200
  "dir",
5201
5201
  "extra-path",
5202
5202
  "harness",
5203
+ "hook",
5203
5204
  "key",
5204
5205
  "level",
5205
5206
  "limit",
@@ -13320,6 +13321,21 @@ function selectRecipes(s, ids) {
13320
13321
  }
13321
13322
  return { hooks, ...release ? { release } : {} };
13322
13323
  }
13324
+ var HOOK_KEYS = ["test", "build", "version", "bump", "released", "ci_status"];
13325
+ function parseHookOverrides(specs) {
13326
+ const out = {};
13327
+ for (const spec of specs) {
13328
+ const eq2 = spec.indexOf("=");
13329
+ const key = eq2 < 0 ? spec : spec.slice(0, eq2);
13330
+ const val = eq2 < 0 ? "" : spec.slice(eq2 + 1);
13331
+ if (!HOOK_KEYS.includes(key)) {
13332
+ throw new ReposError(`--hook: unknown key "${key}" (valid keys: ${HOOK_KEYS.join(", ")})`);
13333
+ }
13334
+ if (val.trim() === "") throw new ReposError(`--hook ${key}= needs a command (expected key=value)`);
13335
+ out[key] = val;
13336
+ }
13337
+ return out;
13338
+ }
13323
13339
  function parseHooksArgs(args, routes) {
13324
13340
  const rest = [...args];
13325
13341
  let route;
@@ -13328,7 +13344,7 @@ function parseHooksArgs(args, routes) {
13328
13344
  if (routes.length === 1) route = routes[0];
13329
13345
  else throw new ReposError(routes.length === 0 ? "the config has no routes \u2014 run `crew connect` first" : `several routes are configured; name one: ${routes.join(", ")}`);
13330
13346
  }
13331
- if (rest.length !== 1) throw new ReposError("expected: crew repos hooks [route] <name> [--suggest] [--agent] [--write route|repo] [--recipes ID,ID] [--json]");
13347
+ if (rest.length !== 1) throw new ReposError("expected: crew repos hooks [route] <name> [--suggest] [--agent] [--write route|repo] [--recipes ID,ID] [--hook KEY=CMD] [--json]");
13332
13348
  return { route, name: rest[0] };
13333
13349
  }
13334
13350
  function repoDirOf(text, base, route, name) {
@@ -13412,7 +13428,7 @@ var AgentSuggestError = class extends Error {
13412
13428
  this.reason = reason;
13413
13429
  }
13414
13430
  };
13415
- var HOOK_KEYS = ["test", "build", "version", "bump", "released"];
13431
+ var HOOK_KEYS2 = ["test", "build", "version", "bump", "released"];
13416
13432
  var READ_ONLY_TOOLS = ["Read", "Grep", "Glob"];
13417
13433
  var WRITING_TOOLS = ["Bash", "Edit", "Write", "NotebookEdit", "WebFetch", "WebSearch", "Task", "Agent"];
13418
13434
  var DEFAULT_TIMEOUT_MS = 18e4;
@@ -13494,7 +13510,7 @@ function parseAgentHooks(text) {
13494
13510
  }
13495
13511
  const raw = obj && typeof obj === "object" && obj.hooks && typeof obj.hooks === "object" ? obj.hooks : {};
13496
13512
  const hooks = {};
13497
- for (const k of HOOK_KEYS) if (typeof raw[k] === "string" && raw[k].trim()) hooks[k] = raw[k].trim();
13513
+ for (const k of HOOK_KEYS2) if (typeof raw[k] === "string" && raw[k].trim()) hooks[k] = raw[k].trim();
13498
13514
  const notes = Array.isArray(obj.notes) ? obj.notes.filter((n) => typeof n === "string") : [];
13499
13515
  return { hooks, notes };
13500
13516
  }
@@ -13569,7 +13585,7 @@ ${run2.err}` });
13569
13585
  throw new AgentSuggestError(reason, reason === "not_installed" ? `${bin} is not installed` : reason === "not_signed_in" ? `${o.spec.name} is not signed in` : (parsed.text || run2.err).trim().slice(0, 300) || `exited ${run2.code}`);
13570
13586
  }
13571
13587
  const { hooks, notes } = parseAgentHooks(parsed.text);
13572
- const differsFromSniff = HOOK_KEYS.filter((k) => hooks[k] !== void 0 && hooks[k] !== o.sniff.hooks[k]);
13588
+ const differsFromSniff = HOOK_KEYS2.filter((k) => hooks[k] !== void 0 && hooks[k] !== o.sniff.hooks[k]);
13573
13589
  return { source: "agent", hooks, filesRead: parsed.filesRead, differsFromSniff, notes, toolchain: o.spec.name, model };
13574
13590
  }
13575
13591
 
@@ -17162,7 +17178,7 @@ ${changed2.map((c) => ` - ${c}
17162
17178
  case "repos": {
17163
17179
  const sub = positional[1];
17164
17180
  const usage2 = () => {
17165
- process.stderr.write("crew repos add [workspace/project] [name] [path] [--name N] [--project P] [--dry-run] [--no-create]\ncrew repos clone [workspace/project] <name> [--dir PATH] [--dry-run] [--json]\ncrew repos list <workspace/project> [--json]\ncrew repos hooks [workspace/project] <name> [--suggest] [--agent] [--write route|repo] [--recipes ID,ID] [--json]\n");
17181
+ process.stderr.write("crew repos add [workspace/project] [name] [path] [--name N] [--project P] [--dry-run] [--no-create]\ncrew repos clone [workspace/project] <name> [--dir PATH] [--dry-run] [--json]\ncrew repos list <workspace/project> [--json]\ncrew repos hooks [workspace/project] <name> [--suggest] [--agent] [--write route|repo] [--recipes ID,ID] [--hook KEY=CMD ...] [--json]\n");
17166
17182
  process.exit(2);
17167
17183
  };
17168
17184
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks") usage2();
@@ -17209,9 +17225,13 @@ ${changed2.map((c) => ` - ${c}
17209
17225
  sigintOwnedByCommand = false;
17210
17226
  }
17211
17227
  }
17212
- const picked = selectRecipes(suggestion, recipeIds);
17228
+ const hookSpecs = argv.flatMap((x, i) => x === "--hook" && argv[i + 1] !== void 0 ? [argv[i + 1]] : x.startsWith("--hook=") ? [x.slice(7)] : []);
17229
+ const overrides = parseHookOverrides(hookSpecs);
17230
+ if (hookSpecs.length > 0 && !write) throw new ReposError("--hook only applies with --write route|repo");
17231
+ const base2 = selectRecipes(suggestion, recipeIds);
17232
+ const picked = { ...base2, hooks: { ...base2.hooks, ...overrides } };
17213
17233
  let written;
17214
- if (write && suggestion.source === null && recipeIds.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
17234
+ if (write && suggestion.source === null && recipeIds.length === 0 && hookSpecs.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
17215
17235
  if (write === "route") {
17216
17236
  const plan = planRouteHooks(configText, a.route, a.name, picked.hooks);
17217
17237
  writeFileSync19(cfg.configFile, plan.text);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.66.0",
3
+ "version": "0.67.0",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",