@tablation/crew 0.65.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.
- package/README.md +1 -1
- package/dist/cli.js +34 -12
- 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
|
@@ -1449,6 +1449,7 @@ var HOOK_NAMES = [
|
|
|
1449
1449
|
"ports",
|
|
1450
1450
|
"isolate",
|
|
1451
1451
|
"handoff",
|
|
1452
|
+
"ci_status",
|
|
1452
1453
|
"version",
|
|
1453
1454
|
"bump",
|
|
1454
1455
|
"pr",
|
|
@@ -2792,7 +2793,8 @@ var HOOK_PURPOSE = [
|
|
|
2792
2793
|
["ports", "this worktree's port assignments; prints KEY=value overrides to export"],
|
|
2793
2794
|
["handoff", "leave the operator able to open what you built; put its output in your progress comment"],
|
|
2794
2795
|
["test", "run the suite"],
|
|
2795
|
-
["build", "build it"]
|
|
2796
|
+
["build", "build it"],
|
|
2797
|
+
["ci_status", "after you push and before you hand off: exit 0 means CI is green for your branch; on failure fix and push again, and say in your progress comment if it is still pending"]
|
|
2796
2798
|
];
|
|
2797
2799
|
function hookTable(repo) {
|
|
2798
2800
|
const declared = HOOK_PURPOSE.filter(([name]) => repo.hooks[name]);
|
|
@@ -5198,6 +5200,7 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
5198
5200
|
"dir",
|
|
5199
5201
|
"extra-path",
|
|
5200
5202
|
"harness",
|
|
5203
|
+
"hook",
|
|
5201
5204
|
"key",
|
|
5202
5205
|
"level",
|
|
5203
5206
|
"limit",
|
|
@@ -13281,11 +13284,11 @@ function detectRecipes(f) {
|
|
|
13281
13284
|
{
|
|
13282
13285
|
id: "github-actions-ci-status",
|
|
13283
13286
|
title: "Check CI status before hand-off",
|
|
13284
|
-
description: "Ask GitHub whether the latest run on the branch passed before a ticket is handed to QA
|
|
13287
|
+
description: "Ask GitHub whether the latest run on the branch passed before a ticket is handed to QA (`hooks.ci_status`).",
|
|
13285
13288
|
evidence,
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
writable:
|
|
13289
|
+
// `gh` prints the conclusion and exits 0 either way, so the test is on the text.
|
|
13290
|
+
hooks: { ci_status: `test "$(gh run list --branch "$CREW_BRANCH" --limit 1 --json conclusion -q '.[0].conclusion')" = success` },
|
|
13291
|
+
writable: true,
|
|
13289
13292
|
defaultOn: false
|
|
13290
13293
|
}
|
|
13291
13294
|
];
|
|
@@ -13318,6 +13321,21 @@ function selectRecipes(s, ids) {
|
|
|
13318
13321
|
}
|
|
13319
13322
|
return { hooks, ...release ? { release } : {} };
|
|
13320
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
|
+
}
|
|
13321
13339
|
function parseHooksArgs(args, routes) {
|
|
13322
13340
|
const rest = [...args];
|
|
13323
13341
|
let route;
|
|
@@ -13326,7 +13344,7 @@ function parseHooksArgs(args, routes) {
|
|
|
13326
13344
|
if (routes.length === 1) route = routes[0];
|
|
13327
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(", ")}`);
|
|
13328
13346
|
}
|
|
13329
|
-
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]");
|
|
13330
13348
|
return { route, name: rest[0] };
|
|
13331
13349
|
}
|
|
13332
13350
|
function repoDirOf(text, base, route, name) {
|
|
@@ -13410,7 +13428,7 @@ var AgentSuggestError = class extends Error {
|
|
|
13410
13428
|
this.reason = reason;
|
|
13411
13429
|
}
|
|
13412
13430
|
};
|
|
13413
|
-
var
|
|
13431
|
+
var HOOK_KEYS2 = ["test", "build", "version", "bump", "released"];
|
|
13414
13432
|
var READ_ONLY_TOOLS = ["Read", "Grep", "Glob"];
|
|
13415
13433
|
var WRITING_TOOLS = ["Bash", "Edit", "Write", "NotebookEdit", "WebFetch", "WebSearch", "Task", "Agent"];
|
|
13416
13434
|
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
@@ -13492,7 +13510,7 @@ function parseAgentHooks(text) {
|
|
|
13492
13510
|
}
|
|
13493
13511
|
const raw = obj && typeof obj === "object" && obj.hooks && typeof obj.hooks === "object" ? obj.hooks : {};
|
|
13494
13512
|
const hooks = {};
|
|
13495
|
-
for (const k of
|
|
13513
|
+
for (const k of HOOK_KEYS2) if (typeof raw[k] === "string" && raw[k].trim()) hooks[k] = raw[k].trim();
|
|
13496
13514
|
const notes = Array.isArray(obj.notes) ? obj.notes.filter((n) => typeof n === "string") : [];
|
|
13497
13515
|
return { hooks, notes };
|
|
13498
13516
|
}
|
|
@@ -13567,7 +13585,7 @@ ${run2.err}` });
|
|
|
13567
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}`);
|
|
13568
13586
|
}
|
|
13569
13587
|
const { hooks, notes } = parseAgentHooks(parsed.text);
|
|
13570
|
-
const differsFromSniff =
|
|
13588
|
+
const differsFromSniff = HOOK_KEYS2.filter((k) => hooks[k] !== void 0 && hooks[k] !== o.sniff.hooks[k]);
|
|
13571
13589
|
return { source: "agent", hooks, filesRead: parsed.filesRead, differsFromSniff, notes, toolchain: o.spec.name, model };
|
|
13572
13590
|
}
|
|
13573
13591
|
|
|
@@ -17160,7 +17178,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
17160
17178
|
case "repos": {
|
|
17161
17179
|
const sub = positional[1];
|
|
17162
17180
|
const usage2 = () => {
|
|
17163
|
-
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");
|
|
17164
17182
|
process.exit(2);
|
|
17165
17183
|
};
|
|
17166
17184
|
if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks") usage2();
|
|
@@ -17207,9 +17225,13 @@ ${changed2.map((c) => ` - ${c}
|
|
|
17207
17225
|
sigintOwnedByCommand = false;
|
|
17208
17226
|
}
|
|
17209
17227
|
}
|
|
17210
|
-
const
|
|
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 } };
|
|
17211
17233
|
let written;
|
|
17212
|
-
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]}`);
|
|
17213
17235
|
if (write === "route") {
|
|
17214
17236
|
const plan = planRouteHooks(configText, a.route, a.name, picked.hooks);
|
|
17215
17237
|
writeFileSync19(cfg.configFile, plan.text);
|
package/package.json
CHANGED