@clipboard-health/groundcrew 4.47.3 → 4.47.4

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 CHANGED
@@ -111,6 +111,7 @@ crew open <pr> | --branch <name> [--repo <owner/repo>] # iterate on an existing
111
111
  crew cleanup [--force] <TASK> # tear down every worktree for a task
112
112
  crew cleanup [--force] --all # tear down every idle worktree (no live workspace)
113
113
  crew upgrade [<version>] # reinstall crew globally through npm
114
+ crew completions <bash|zsh|fish> # print a shell completion script
114
115
  ```
115
116
 
116
117
  See [command details](./docs/commands.md) for status output, doctor behavior, and the stop/resume workflow.
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,7 @@
1
+ /**
2
+ * The command names shown in `crew --help` and offered by shell completions:
3
+ * every registered subcommand that is neither hidden nor a deprecated alias.
4
+ */
5
+ export declare function visibleCommandNames(): readonly string[];
1
6
  export declare function run(argv: string[]): Promise<void>;
2
7
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA4RA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCvD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAuPA;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,MAAM,EAAE,CAIvD;AAiDD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCvD"}
package/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { cleanupWorkspaceCli } from "./commands/cleanupWorkspace.js";
3
+ import { completionsCli } from "./commands/completions.js";
3
4
  import { doctor } from "./commands/doctor.js";
4
5
  import { initConfigCli } from "./commands/init.js";
5
6
  import { interruptWorkspaceCli } from "./commands/interruptWorkspace.js";
@@ -186,9 +187,26 @@ const SUBCOMMANDS = {
186
187
  usage: "[<version>]",
187
188
  invoke: upgradeCliInvoke,
188
189
  },
190
+ completions: {
191
+ summary: "Print a shell completion script for bash, zsh, or fish",
192
+ usage: "<bash|zsh|fish>",
193
+ invoke: completionsCli,
194
+ },
189
195
  };
196
+ function isVisibleCommand(command) {
197
+ return command.hidden !== true && command.deprecated !== true;
198
+ }
199
+ /**
200
+ * The command names shown in `crew --help` and offered by shell completions:
201
+ * every registered subcommand that is neither hidden nor a deprecated alias.
202
+ */
203
+ export function visibleCommandNames() {
204
+ return Object.entries(SUBCOMMANDS)
205
+ .filter(([, command]) => isVisibleCommand(command))
206
+ .map(([name]) => name);
207
+ }
190
208
  function printHelp() {
191
- const visibleCommands = Object.entries(SUBCOMMANDS).filter(([, command]) => command.hidden !== true && command.deprecated !== true);
209
+ const visibleCommands = Object.entries(SUBCOMMANDS).filter(([, command]) => isVisibleCommand(command));
192
210
  const width = Math.max(...visibleCommands.map(([key]) => key.length));
193
211
  writeOutput("Usage: crew <command> [...args]\n");
194
212
  writeOutput("Options:");
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Emits shell completion scripts for the `crew` CLI. The command tree is
3
+ * declared once in `COMPLETION_SPEC` below and rendered into bash, zsh, and
4
+ * fish scripts. Keep the spec in sync with the parsers in this directory; the
5
+ * `completions.test.ts` drift guard asserts the top-level command names match
6
+ * the visible entries in `SUBCOMMANDS` (see `../cli.ts`).
7
+ */
8
+ /** Describes the value a flag consumes, driving per-shell value completion. */
9
+ type CompletionArg = {
10
+ readonly kind: "enum";
11
+ readonly values: readonly string[];
12
+ } | {
13
+ readonly kind: "file";
14
+ } | {
15
+ readonly kind: "dir";
16
+ } | {
17
+ readonly kind: "string";
18
+ };
19
+ interface CompletionOption {
20
+ readonly name: string;
21
+ readonly short?: string;
22
+ readonly summary: string;
23
+ /** Omitted for boolean flags; present when the flag takes a value. */
24
+ readonly arg?: CompletionArg;
25
+ }
26
+ export interface CompletionCommand {
27
+ readonly name: string;
28
+ readonly summary: string;
29
+ readonly options?: readonly CompletionOption[];
30
+ readonly subcommands?: readonly CompletionCommand[];
31
+ /** Static positional values (e.g. `crew completions <shell>`). */
32
+ readonly argValues?: readonly string[];
33
+ }
34
+ export declare const SUPPORTED_SHELLS: readonly ["bash", "zsh", "fish"];
35
+ export type SupportedShell = (typeof SUPPORTED_SHELLS)[number];
36
+ /**
37
+ * The single source of truth for shell completions. Ordering mirrors
38
+ * `crew --help`; hidden and deprecated commands are intentionally excluded.
39
+ */
40
+ export declare const COMPLETION_SPEC: readonly CompletionCommand[];
41
+ /** Renders the completion script for a supported shell. */
42
+ export declare function generateCompletionScript(shell: SupportedShell): string;
43
+ export declare function completionsCli(argv: string[]): Promise<void>;
44
+ export {};
45
+ //# sourceMappingURL=completions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/commands/completions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,+EAA+E;AAC/E,KAAK,aAAa,GACd;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEhC,UAAU,gBAAgB;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACpD,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,eAAO,MAAM,gBAAgB,YAAI,MAAM,EAAE,KAAK,EAAE,MAAM,CAAU,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AA2B/D;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,iBAAiB,EAqJvD,CAAC;AAqXF,2DAA2D;AAC3D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEtE;AAQD,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlE"}
@@ -0,0 +1,524 @@
1
+ /**
2
+ * Emits shell completion scripts for the `crew` CLI. The command tree is
3
+ * declared once in `COMPLETION_SPEC` below and rendered into bash, zsh, and
4
+ * fish scripts. Keep the spec in sync with the parsers in this directory; the
5
+ * `completions.test.ts` drift guard asserts the top-level command names match
6
+ * the visible entries in `SUBCOMMANDS` (see `../cli.ts`).
7
+ */
8
+ import { LOCAL_RUNNER_SETTINGS } from "../lib/config.js";
9
+ import { shellSingleQuote } from "../lib/shell.js";
10
+ import { writeOutput } from "../lib/util.js";
11
+ import { INIT_AGENTS } from "./init.js";
12
+ export const SUPPORTED_SHELLS = ["bash", "zsh", "fish"];
13
+ // `--runner` and `init --agent` values come from the canonical lists their
14
+ // parsers validate against (config.ts, init.ts) so completions never drift.
15
+ // Status has no exported canonical array to import, so it stays a local literal.
16
+ const STATUS_VALUES = ["todo", "in-progress", "in-review", "done", "other"];
17
+ // `init` validates `--agent` against a fixed built-in set; `open`/`task` accept
18
+ // any configured agent name, so their `--agent` is declared free-form (string).
19
+ const INIT_AGENT_OPTION = {
20
+ name: "--agent",
21
+ summary: "Agent name",
22
+ arg: { kind: "enum", values: INIT_AGENTS },
23
+ };
24
+ const REPO_OPTION = {
25
+ name: "--repo",
26
+ summary: "Repository (owner/repo)",
27
+ arg: { kind: "string" },
28
+ };
29
+ const SOURCE_OPTION = {
30
+ name: "--source",
31
+ summary: "Task source name",
32
+ arg: { kind: "string" },
33
+ };
34
+ const JSON_OPTION = { name: "--json", summary: "Print JSON output" };
35
+ const DRY_RUN_OPTION = { name: "--dry-run", summary: "Preview without acting" };
36
+ /**
37
+ * The single source of truth for shell completions. Ordering mirrors
38
+ * `crew --help`; hidden and deprecated commands are intentionally excluded.
39
+ */
40
+ export const COMPLETION_SPEC = [
41
+ {
42
+ name: "init",
43
+ summary: "Create a crew.config.ts",
44
+ options: [
45
+ { name: "--global", summary: "Write into the XDG config dir" },
46
+ { name: "--local", summary: "Write into the current directory" },
47
+ { name: "--force", summary: "Overwrite an existing config" },
48
+ DRY_RUN_OPTION,
49
+ { name: "--project-dir", summary: "Workspace project directory", arg: { kind: "dir" } },
50
+ REPO_OPTION,
51
+ {
52
+ name: "--runner",
53
+ summary: "Sandbox runner",
54
+ arg: { kind: "enum", values: LOCAL_RUNNER_SETTINGS },
55
+ },
56
+ INIT_AGENT_OPTION,
57
+ ],
58
+ },
59
+ {
60
+ name: "run",
61
+ summary: "Run the orchestrator over eligible tasks",
62
+ options: [{ name: "--watch", summary: "Poll continuously" }, DRY_RUN_OPTION],
63
+ },
64
+ {
65
+ name: "start",
66
+ summary: "Launch one task immediately",
67
+ options: [DRY_RUN_OPTION],
68
+ },
69
+ {
70
+ name: "doctor",
71
+ summary: "Verify host prerequisites",
72
+ },
73
+ {
74
+ name: "source",
75
+ summary: "Inspect configured task sources",
76
+ subcommands: [
77
+ { name: "list", summary: "List configured sources", options: [JSON_OPTION] },
78
+ { name: "verify", summary: "Verify one or all sources", options: [JSON_OPTION] },
79
+ ],
80
+ },
81
+ {
82
+ name: "task",
83
+ summary: "List, get, create, and complete tasks",
84
+ subcommands: [
85
+ {
86
+ name: "list",
87
+ summary: "List tasks across sources",
88
+ options: [
89
+ SOURCE_OPTION,
90
+ {
91
+ name: "--status",
92
+ summary: "Filter by status",
93
+ arg: { kind: "enum", values: STATUS_VALUES },
94
+ },
95
+ { name: "--agent", summary: "Filter by agent name", arg: { kind: "string" } },
96
+ REPO_OPTION,
97
+ { name: "--blocked", summary: "Show only blocked tasks" },
98
+ { name: "--unblocked", summary: "Show only unblocked tasks" },
99
+ JSON_OPTION,
100
+ { name: "--limit", summary: "Limit output count", arg: { kind: "string" } },
101
+ ],
102
+ },
103
+ {
104
+ name: "get",
105
+ summary: "Get one task",
106
+ options: [
107
+ SOURCE_OPTION,
108
+ JSON_OPTION,
109
+ { name: "--prompt", summary: "Print only the task prompt" },
110
+ ],
111
+ },
112
+ {
113
+ name: "create",
114
+ summary: "Create one task",
115
+ options: [
116
+ SOURCE_OPTION,
117
+ { name: "--agent", summary: "Assign an agent", arg: { kind: "string" } },
118
+ REPO_OPTION,
119
+ { name: "--team", summary: "Team key", arg: { kind: "string" } },
120
+ { name: "--id", summary: "Explicit task id", arg: { kind: "string" } },
121
+ { name: "--priority", summary: "Priority", arg: { kind: "string" } },
122
+ { name: "--project", summary: "Project (repeatable)", arg: { kind: "string" } },
123
+ { name: "--context", summary: "Context tag (repeatable)", arg: { kind: "string" } },
124
+ { name: "--dep", summary: "Dependency (repeatable)", arg: { kind: "string" } },
125
+ { name: "--due", summary: "Due date", arg: { kind: "string" } },
126
+ { name: "--rec", summary: "Recurrence", arg: { kind: "string" } },
127
+ { name: "--prompt-file", summary: "Read prompt from file", arg: { kind: "file" } },
128
+ { name: "--description", summary: "Task description", arg: { kind: "string" } },
129
+ { name: "--edit", summary: "Open an editor" },
130
+ JSON_OPTION,
131
+ ],
132
+ },
133
+ {
134
+ name: "done",
135
+ summary: "Mark one task done",
136
+ options: [{ name: "--allow-dirty", summary: "Allow a dirty worktree" }],
137
+ },
138
+ {
139
+ name: "validate",
140
+ summary: "Validate task content",
141
+ options: [JSON_OPTION],
142
+ },
143
+ ],
144
+ },
145
+ {
146
+ name: "status",
147
+ summary: "Print groundcrew state or a task status",
148
+ },
149
+ {
150
+ name: "cleanup",
151
+ summary: "Tear down a worktree, or all idle worktrees",
152
+ options: [
153
+ { name: "--force", summary: "Skip confirmations" },
154
+ { name: "--all", summary: "Clean up every idle worktree" },
155
+ ],
156
+ },
157
+ {
158
+ name: "stop",
159
+ summary: "Stop a live task workspace",
160
+ options: [{ name: "--reason", summary: "Reason text", arg: { kind: "string" } }],
161
+ },
162
+ {
163
+ name: "resume",
164
+ summary: "Reopen an existing task worktree",
165
+ options: [{ name: "--new", summary: "Start a fresh chat session" }],
166
+ },
167
+ {
168
+ name: "open",
169
+ summary: "Open a PR or branch in a new worktree",
170
+ options: [
171
+ { name: "--branch", summary: "Branch name", arg: { kind: "string" } },
172
+ REPO_OPTION,
173
+ { name: "--agent", summary: "Agent name", arg: { kind: "string" } },
174
+ { name: "--prompt", summary: "Prompt text", arg: { kind: "string" } },
175
+ { name: "--prompt-file", summary: "Read prompt from file", arg: { kind: "file" } },
176
+ { name: "--task", summary: "Associate a task id", arg: { kind: "string" } },
177
+ DRY_RUN_OPTION,
178
+ ],
179
+ },
180
+ {
181
+ name: "upgrade",
182
+ summary: "Install the latest version of crew",
183
+ },
184
+ {
185
+ name: "completions",
186
+ summary: "Print a shell completion script",
187
+ argValues: SUPPORTED_SHELLS,
188
+ },
189
+ ];
190
+ /** Global flags valid only as the first token. */
191
+ const TOP_LEVEL_GLOBALS = [
192
+ { name: "--help", short: "-h", summary: "Show help" },
193
+ { name: "--version", short: "-v", summary: "Print version" },
194
+ { name: "--verbose", summary: "Show diagnostic output" },
195
+ ];
196
+ /** The one global flag every subcommand tolerates (stripped before dispatch). */
197
+ const COMMAND_GLOBAL = "--verbose";
198
+ function optionTokens(options) {
199
+ return (options ?? []).flatMap((option) => option.short === undefined ? [option.name] : [option.name, option.short]);
200
+ }
201
+ /**
202
+ * Collects every value-taking flag across the tree, keyed by flag name, for the
203
+ * shell `prev`-based value cases (bash/zsh) which key on flag name globally.
204
+ * A flag can be declared with different kinds across commands (e.g. `--agent`
205
+ * is an enum under `init` but free-form under `open`/`task`); prefer the enum so
206
+ * value suggestions survive regardless of spec order.
207
+ */
208
+ function valueArgsByName() {
209
+ const result = new Map();
210
+ function visit(command) {
211
+ for (const option of command.options ?? []) {
212
+ if (option.arg !== undefined) {
213
+ const existing = result.get(option.name);
214
+ if (existing?.kind !== "enum") {
215
+ result.set(option.name, option.arg);
216
+ }
217
+ }
218
+ }
219
+ for (const subcommand of command.subcommands ?? []) {
220
+ visit(subcommand);
221
+ }
222
+ }
223
+ for (const command of COMPLETION_SPEC) {
224
+ visit(command);
225
+ }
226
+ return result;
227
+ }
228
+ /** Appends the one global flag (`--verbose`) every subcommand tolerates. */
229
+ function withVerbose(tokens) {
230
+ return [...tokens, COMMAND_GLOBAL].join(" ");
231
+ }
232
+ /**
233
+ * Walks every value-taking flag once and emits a per-shell `case` line via the
234
+ * given templates. Enum/file/dir flags each get their own line; plain string
235
+ * flags collapse into one fall-through case that offers no completion.
236
+ */
237
+ function valueCases(render) {
238
+ const lines = [];
239
+ const stringNames = [];
240
+ for (const [name, arg] of valueArgsByName()) {
241
+ if (arg.kind === "enum") {
242
+ lines.push(render.enum(name, arg.values));
243
+ }
244
+ else if (arg.kind === "file") {
245
+ lines.push(render.file(name));
246
+ }
247
+ else if (arg.kind === "dir") {
248
+ lines.push(render.dir(name));
249
+ }
250
+ else {
251
+ stringNames.push(name);
252
+ }
253
+ }
254
+ lines.push(render.string(stringNames));
255
+ return lines.join("\n");
256
+ }
257
+ // -- bash ------------------------------------------------------------------
258
+ function bashValueCases() {
259
+ return valueCases({
260
+ enum: (name, values) => ` ${name}) COMPREPLY=( $(compgen -W "${values.join(" ")}" -- "$cur") ); return ;;`,
261
+ file: (name) => ` ${name}) COMPREPLY=( $(compgen -f -- "$cur") ); return ;;`,
262
+ dir: (name) => ` ${name}) COMPREPLY=( $(compgen -d -- "$cur") ); return ;;`,
263
+ string: (names) => ` ${names.join("|")}) return ;;`,
264
+ });
265
+ }
266
+ function bashCommandCase(command) {
267
+ if (command.subcommands !== undefined) {
268
+ const subNames = command.subcommands.map((sub) => sub.name);
269
+ const subCases = command.subcommands
270
+ .map((sub) => ` ${sub.name}) COMPREPLY=( $(compgen -W "${withVerbose(optionTokens(sub.options))}" -- "$cur") ) ;;`)
271
+ .join("\n");
272
+ return [
273
+ ` ${command.name})`,
274
+ ` if [ -z "$subcommand" ]; then`,
275
+ ` COMPREPLY=( $(compgen -W "${withVerbose(subNames)}" -- "$cur") )`,
276
+ ` else`,
277
+ ` case "$subcommand" in`,
278
+ subCases,
279
+ ` esac`,
280
+ ` fi`,
281
+ ` ;;`,
282
+ ].join("\n");
283
+ }
284
+ if (command.argValues !== undefined) {
285
+ return [
286
+ ` ${command.name})`,
287
+ ` if [ -z "$subcommand" ]; then`,
288
+ ` COMPREPLY=( $(compgen -W "${withVerbose([...command.argValues])}" -- "$cur") )`,
289
+ ` fi`,
290
+ ` ;;`,
291
+ ].join("\n");
292
+ }
293
+ return [
294
+ ` ${command.name})`,
295
+ ` COMPREPLY=( $(compgen -W "${withVerbose(optionTokens(command.options))}" -- "$cur") )`,
296
+ ` ;;`,
297
+ ].join("\n");
298
+ }
299
+ function bashScript() {
300
+ const commandNames = COMPLETION_SPEC.map((command) => command.name).join(" ");
301
+ const topGlobals = optionTokens(TOP_LEVEL_GLOBALS).join(" ");
302
+ const commandCases = COMPLETION_SPEC.map(bashCommandCase).join("\n");
303
+ return `# bash completion for crew
304
+ # Install: source <(crew completions bash)
305
+ _crew() {
306
+ local cur prev cmd subcommand i word
307
+ cur="\${COMP_WORDS[COMP_CWORD]}"
308
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
309
+
310
+ case "$prev" in
311
+ ${bashValueCases()}
312
+ esac
313
+
314
+ cmd=""
315
+ subcommand=""
316
+ for (( i=1; i < COMP_CWORD; i++ )); do
317
+ word="\${COMP_WORDS[i]}"
318
+ case "$word" in -*) continue ;; esac
319
+ if [ -z "$cmd" ]; then
320
+ cmd="$word"
321
+ elif [ -z "$subcommand" ]; then
322
+ subcommand="$word"
323
+ fi
324
+ done
325
+
326
+ if [ -z "$cmd" ]; then
327
+ COMPREPLY=( $(compgen -W "${commandNames} ${topGlobals}" -- "$cur") )
328
+ return
329
+ fi
330
+
331
+ case "$cmd" in
332
+ ${commandCases}
333
+ *)
334
+ COMPREPLY=( $(compgen -W "${COMMAND_GLOBAL}" -- "$cur") )
335
+ ;;
336
+ esac
337
+ }
338
+ complete -F _crew crew
339
+ `;
340
+ }
341
+ // -- zsh -------------------------------------------------------------------
342
+ function zshValueCases() {
343
+ return valueCases({
344
+ enum: (name, values) => ` ${name}) compadd -- ${values.join(" ")}; return ;;`,
345
+ file: (name) => ` ${name}) _files; return ;;`,
346
+ dir: (name) => ` ${name}) _files -/; return ;;`,
347
+ string: (names) => ` ${names.join("|")}) return ;;`,
348
+ });
349
+ }
350
+ function zshDescribeArray(name, entries) {
351
+ const items = entries
352
+ .map((entry) => ` ${shellSingleQuote(`${entry.name}:${entry.summary}`)}`)
353
+ .join("\n");
354
+ return [` local -a ${name}`, ` ${name}=(`, items, ` )`].join("\n");
355
+ }
356
+ function zshCommandCase(command) {
357
+ if (command.subcommands !== undefined) {
358
+ const subCases = command.subcommands
359
+ .map((sub) => ` ${sub.name}) compadd -- ${withVerbose(optionTokens(sub.options))} ;;`)
360
+ .join("\n");
361
+ return [
362
+ ` ${command.name})`,
363
+ ` if [[ -z $subcommand ]]; then`,
364
+ zshDescribeArray(`${command.name}_subcommands`, command.subcommands)
365
+ .split("\n")
366
+ .map((line) => ` ${line}`)
367
+ .join("\n"),
368
+ ` _describe -t subcommands '${command.name} subcommand' ${command.name}_subcommands`,
369
+ ` else`,
370
+ ` case $subcommand in`,
371
+ subCases,
372
+ ` esac`,
373
+ ` fi`,
374
+ ` ;;`,
375
+ ].join("\n");
376
+ }
377
+ if (command.argValues !== undefined) {
378
+ return [
379
+ ` ${command.name})`,
380
+ ` if [[ -z $subcommand ]]; then compadd -- ${withVerbose([...command.argValues])}; fi`,
381
+ ` ;;`,
382
+ ].join("\n");
383
+ }
384
+ return [
385
+ ` ${command.name})`,
386
+ ` compadd -- ${withVerbose(optionTokens(command.options))}`,
387
+ ` ;;`,
388
+ ].join("\n");
389
+ }
390
+ function zshScript() {
391
+ const topGlobals = optionTokens(TOP_LEVEL_GLOBALS).join(" ");
392
+ const commandCases = COMPLETION_SPEC.map(zshCommandCase).join("\n");
393
+ return `#compdef crew
394
+ # zsh completion for crew
395
+ # Install: crew completions zsh > "\${fpath[1]}/_crew" (or: source <(crew completions zsh))
396
+ _crew() {
397
+ local cur prev cmd subcommand i word
398
+ ${zshDescribeArray("commands", COMPLETION_SPEC)}
399
+ cur=$words[CURRENT]
400
+ prev=$words[CURRENT-1]
401
+
402
+ case $prev in
403
+ ${zshValueCases()}
404
+ esac
405
+
406
+ cmd=""
407
+ subcommand=""
408
+ for (( i=2; i < CURRENT; i++ )); do
409
+ word=$words[i]
410
+ case $word in -*) continue ;; esac
411
+ if [[ -z $cmd ]]; then
412
+ cmd=$word
413
+ elif [[ -z $subcommand ]]; then
414
+ subcommand=$word
415
+ fi
416
+ done
417
+
418
+ if [[ -z $cmd ]]; then
419
+ _describe -t commands 'crew command' commands
420
+ compadd -- ${topGlobals}
421
+ return
422
+ fi
423
+
424
+ case $cmd in
425
+ ${commandCases}
426
+ *)
427
+ compadd -- ${COMMAND_GLOBAL}
428
+ ;;
429
+ esac
430
+ }
431
+
432
+ # Support both fpath autoload and sourcing (source <(crew completions zsh)).
433
+ if [ "$funcstack[1]" = "_crew" ]; then
434
+ _crew "$@"
435
+ else
436
+ compdef _crew crew
437
+ fi
438
+ `;
439
+ }
440
+ // -- fish ------------------------------------------------------------------
441
+ function fishDescription(summary) {
442
+ return summary.replaceAll("\\", String.raw `\\`).replaceAll('"', String.raw `\"`);
443
+ }
444
+ function fishArgFlags(arg) {
445
+ if (arg === undefined) {
446
+ return "";
447
+ }
448
+ if (arg.kind === "enum") {
449
+ return ` -x -a "${arg.values.join(" ")}"`;
450
+ }
451
+ if (arg.kind === "file") {
452
+ return " -r -F";
453
+ }
454
+ if (arg.kind === "dir") {
455
+ return ` -x -a "(__fish_complete_directories)"`;
456
+ }
457
+ return " -x";
458
+ }
459
+ function fishOptionLine(condition, option) {
460
+ // Command/subcommand options never carry short forms (only the top-level
461
+ // globals do, and those are emitted separately), so this renders long flags.
462
+ const long = option.name.replace(/^--/, "");
463
+ return `complete -c crew -n '${condition}' -l ${long}${fishArgFlags(option.arg)} -d "${fishDescription(option.summary)}"`;
464
+ }
465
+ function fishOptionLines(condition, options) {
466
+ return (options ?? []).map((option) => fishOptionLine(condition, option));
467
+ }
468
+ function fishCommandLines(command) {
469
+ const lines = [];
470
+ const seenCondition = `__fish_seen_subcommand_from ${command.name}`;
471
+ lines.push(...fishOptionLines(seenCondition, command.options));
472
+ if (command.subcommands !== undefined) {
473
+ const subNames = command.subcommands.map((sub) => sub.name).join(" ");
474
+ const chooseSub = `${seenCondition}; and not __fish_seen_subcommand_from ${subNames}`;
475
+ for (const sub of command.subcommands) {
476
+ lines.push(`complete -c crew -f -n '${chooseSub}' -a ${sub.name} -d "${fishDescription(sub.summary)}"`);
477
+ const subCondition = `${seenCondition}; and __fish_seen_subcommand_from ${sub.name}`;
478
+ lines.push(...fishOptionLines(subCondition, sub.options));
479
+ }
480
+ }
481
+ if (command.argValues !== undefined) {
482
+ const chooseArg = `${seenCondition}; and not __fish_seen_subcommand_from ${command.argValues.join(" ")}`;
483
+ lines.push(`complete -c crew -f -n '${chooseArg}' -a "${command.argValues.join(" ")}" -d "Shell"`);
484
+ }
485
+ return lines;
486
+ }
487
+ function fishScript() {
488
+ const commandLines = COMPLETION_SPEC.map((command) => `complete -c crew -f -n '__fish_use_subcommand' -a ${command.name} -d "${fishDescription(command.summary)}"`);
489
+ const globalLines = [
490
+ `complete -c crew -l verbose -d "Show diagnostic output"`,
491
+ `complete -c crew -n '__fish_use_subcommand' -l help -s h -d "Show help"`,
492
+ `complete -c crew -n '__fish_use_subcommand' -l version -s v -d "Print version"`,
493
+ ];
494
+ const perCommandLines = COMPLETION_SPEC.flatMap(fishCommandLines);
495
+ return `# fish completion for crew
496
+ # Install: crew completions fish > ~/.config/fish/completions/crew.fish
497
+ ${commandLines.join("\n")}
498
+ ${globalLines.join("\n")}
499
+ ${perCommandLines.join("\n")}
500
+ `;
501
+ }
502
+ const GENERATORS = {
503
+ bash: bashScript,
504
+ zsh: zshScript,
505
+ fish: fishScript,
506
+ };
507
+ /** Renders the completion script for a supported shell. */
508
+ export function generateCompletionScript(shell) {
509
+ return GENERATORS[shell]();
510
+ }
511
+ function isSupportedShell(value) {
512
+ return SUPPORTED_SHELLS.includes(value);
513
+ }
514
+ const COMPLETIONS_USAGE = `Usage: crew completions <${SUPPORTED_SHELLS.join("|")}>`;
515
+ export async function completionsCli(argv) {
516
+ const [shell, ...extras] = argv;
517
+ if (shell === undefined || extras.length > 0) {
518
+ throw new Error(COMPLETIONS_USAGE);
519
+ }
520
+ if (!isSupportedShell(shell)) {
521
+ throw new Error(`crew completions: unsupported shell: ${shell}\n${COMPLETIONS_USAGE}`);
522
+ }
523
+ writeOutput(generateCompletionScript(shell));
524
+ }
@@ -5,7 +5,7 @@
5
5
  * `cp` dance documented in the README.
6
6
  */
7
7
  import { type LocalRunnerSetting } from "../lib/config.ts";
8
- declare const INIT_AGENTS: readonly ["claude", "codex", "cursor"];
8
+ export declare const INIT_AGENTS: readonly ["claude", "codex", "cursor"];
9
9
  type InitConfigScope = "global" | "local";
10
10
  type InitAgent = (typeof INIT_AGENTS)[number];
11
11
  interface InitConfigOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAYlF,QAAA,MAAM,WAAW,YAAI,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAE3D,KAAK,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC1C,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,UAAU,iBAAiB;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qDAAqD;IACrD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,iBAAiB,GAAG,qBAAqB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpE,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAoB5E;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAYlF,eAAO,MAAM,WAAW,YAAI,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAElE,KAAK,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC1C,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,UAAU,iBAAiB;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qDAAqD;IACrD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,iBAAiB,GAAG,qBAAqB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpE,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAoB5E;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE"}
@@ -16,7 +16,7 @@ const DEFAULT_EXAMPLE_PROJECT_DIR = "~/dev/groundcrew";
16
16
  const INIT_USAGE = "Usage: crew init [--global | --local] [--force] [--dry-run] [--project-dir <dir>] [--repo <owner/repo>]... [--runner <auto|safehouse|sdx|none>] [--agent <claude|codex|cursor>]";
17
17
  // Model-variant presets with hyphens (e.g. `cursor-grok`) are enabled via
18
18
  // config, not `init`: renderConfig would emit an unquoted, invalid TS key.
19
- const INIT_AGENTS = ["claude", "codex", "cursor"];
19
+ export const INIT_AGENTS = ["claude", "codex", "cursor"];
20
20
  export function initConfig(options = {}) {
21
21
  const scope = options.scope ?? "local";
22
22
  const cwd = options.cwd ?? process.cwd();
package/docs/commands.md CHANGED
@@ -166,3 +166,24 @@ Open checks out the PR's actual head branch (fetching it from the remote when it
166
166
  When `--prompt`/`--prompt-file` is given, the agent starts with that prompt; otherwise it opens its interactive session with no prompt and hands control to you.
167
167
 
168
168
  `crew cleanup <task>` removes the opened worktree but never deletes the remote PR branch. Fork (cross-repository) PRs and `provision`/sparse-checkout repositories are not supported; for a fork, check the branch out locally and use `--branch`.
169
+
170
+ ## Completions
171
+
172
+ `crew completions <bash|zsh|fish>` prints a shell completion script to stdout. The script completes command names, subcommands (for `crew source` and `crew task`), flags, and enumerated flag values (`--runner`, `--status`, `--agent`); `--prompt-file` and `--project-dir` fall back to file and directory completion. Task IDs, source names, and repositories are configuration-specific and are not completed.
173
+
174
+ Load it once per shell session, or install it so your shell loads it automatically.
175
+
176
+ ```bash
177
+ # bash — add to ~/.bashrc
178
+ source <(crew completions bash)
179
+
180
+ # zsh — write into a directory on your $fpath (run once), then restart the shell
181
+ crew completions zsh > "${fpath[1]}/_crew"
182
+ # or, to load in the current session, add to ~/.zshrc:
183
+ source <(crew completions zsh)
184
+
185
+ # fish — install once; fish loads it automatically on the next session
186
+ crew completions fish > ~/.config/fish/completions/crew.fish
187
+ ```
188
+
189
+ The command tree is generated from a single spec in `src/commands/completions.ts`, so completions stay in sync with the CLI as commands and flags change.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.47.3",
3
+ "version": "4.47.4",
4
4
  "description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
5
5
  "keywords": [
6
6
  "agent",