@clipboard-health/groundcrew 4.24.1 → 4.24.2

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
@@ -95,7 +95,7 @@ crew init [--global | --local] [--force] [--dry-run] # create a crew.config.
95
95
  crew doctor # check setup
96
96
  crew task list [--source <name>] # list tasks across sources
97
97
  crew task get <TASK> [--source <name>] [--prompt] # inspect one task or its prompt
98
- crew task create "Title" --source <name> --agent <name> # create a source task
98
+ crew task create "Title" --source <name> [--agent <name>] # create a source task
99
99
  crew status [<TASK>] # inspect current state or one task
100
100
  crew run [--watch] # one-shot or --watch forever
101
101
  crew start <TASK> # provision + launch one task now
@@ -1 +1 @@
1
- {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAwmBA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3D"}
1
+ {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAomBA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3D"}
@@ -1,5 +1,5 @@
1
1
  import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
2
- import { loadConfig } from "../lib/config.js";
2
+ import { AGENT_ANY_MODEL, loadConfig } from "../lib/config.js";
3
3
  import { naturalIdFromCanonical, } from "../lib/taskSource.js";
4
4
  import { writeOutput } from "../lib/util.js";
5
5
  const TASK_USAGE = `Usage: crew task <subcommand>
@@ -25,7 +25,7 @@ Options:
25
25
  --source <name> Resolve a source-native ID against a specific source.
26
26
  --json Print normalized task JSON.
27
27
  --prompt Print only the task description/prompt.`;
28
- const CREATE_USAGE = `Usage: crew task create "Short title" --source <source> --agent <agent> [options]`;
28
+ const CREATE_USAGE = `Usage: crew task create "Short title" --source <source> [--agent <agent>] [options]`;
29
29
  const CANONICAL_STATUSES = [
30
30
  "todo",
31
31
  "in-progress",
@@ -232,12 +232,9 @@ function parseCreateOptions(argv) {
232
232
  if (state.sourceName === undefined) {
233
233
  throw new Error("crew task create: --source is required");
234
234
  }
235
- if (state.agent === undefined) {
236
- throw new Error("crew task create: --agent is required");
237
- }
238
235
  const input = {
239
236
  title,
240
- agent: state.agent,
237
+ agent: state.agent ?? AGENT_ANY_MODEL,
241
238
  projects: state.projects,
242
239
  contexts: state.contexts,
243
240
  dependencies: state.dependencies,
@@ -1 +1 @@
1
- {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/normalizer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,KAAK,EAAiB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAA8C,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9F,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAqD7E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAYhE"}
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/normalizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsC,KAAK,KAAK,EAAiB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAA8C,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9F,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAqD7E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAShE"}
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { AGENT_ANY_MODEL } from "../../config.js";
2
3
  import { toCanonicalId } from "../../taskSource.js";
3
4
  import { getMetadataAll, getMetadataFirst, hashLine } from "./parser.js";
4
5
  function derivedCanonicalStatus(parsed) {
@@ -58,7 +59,7 @@ export function normalizeToIssue(options) {
58
59
  if (id === undefined) {
59
60
  return undefined;
60
61
  }
61
- const agent = getMetadataFirst(parsed, "agent");
62
+ const agent = getMetadataFirst(parsed, "agent") ?? AGENT_ANY_MODEL;
62
63
  const status = derivedCanonicalStatus(parsed);
63
64
  const repository = getMetadataFirst(parsed, "repo") ?? defaultRepository;
64
65
  const depIds = getMetadataAll(parsed, "dep");
@@ -96,9 +97,6 @@ export function isActiveForFetch(parsed) {
96
97
  if (getMetadataFirst(parsed, "id") === undefined) {
97
98
  return false;
98
99
  }
99
- if (getMetadataFirst(parsed, "agent") === undefined) {
100
- return false;
101
- }
102
100
  const statusValue = getMetadataFirst(parsed, "status");
103
101
  return statusValue === "todo" || statusValue === "in-progress" || statusValue === "in-review";
104
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA+QxD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,cAAc,GACvB,UAAU,CA4IZ"}
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA0RxD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,cAAc,GACvB,UAAU,CA4IZ"}
@@ -8,13 +8,23 @@ import { getMetadataFirst, parseAllLines } from "./parser.js";
8
8
  import { copyPromptFile, updateTaskStatus, validateTodoFile, withLock } from "./writeback.js";
9
9
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
10
10
  const RECURRENCE_RE = /^\+?\d+[dwmy]$/;
11
- function readDescription(promptPath) {
11
+ function readPromptFile(promptPath) {
12
12
  try {
13
13
  return readFileSync(promptPath, "utf8");
14
14
  }
15
15
  catch {
16
- return "";
16
+ return undefined;
17
+ }
18
+ }
19
+ function descriptionFor(parsed, promptPath) {
20
+ const promptContent = readPromptFile(promptPath);
21
+ if (promptContent !== undefined && promptContent.trim().length > 0) {
22
+ return promptContent;
23
+ }
24
+ if (parsed.title.trim().length > 0) {
25
+ return `${parsed.title}\n`;
17
26
  }
27
+ return promptContent ?? "";
18
28
  }
19
29
  function fileUpdatedAt(filePath) {
20
30
  try {
@@ -51,7 +61,7 @@ function buildIssue(options) {
51
61
  }
52
62
  const promptOverride = getMetadataFirst(parsed, "prompt");
53
63
  const promptPath = promptOverride ?? `${tasksDir}/${id}.md`;
54
- const description = readDescription(promptPath);
64
+ const description = descriptionFor(parsed, promptPath);
55
65
  return normalizeToIssue({
56
66
  parsed,
57
67
  allParsed: parsedAll,
@@ -1 +1 @@
1
- {"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/writeback.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAoKD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,gBAAgB,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQxF;AAED,KAAK,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,CAAC;AA6E3D,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,cAAc,GACxB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAsElC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQrE;AAwFD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA0C7E"}
1
+ {"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/todo-txt/writeback.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAoKD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,gBAAgB,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQxF;AAED,KAAK,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,CAAC;AA6E3D,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,cAAc,GACxB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAsElC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQrE;AA4FD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA0C7E"}
@@ -262,16 +262,19 @@ export function copyPromptFile(oldPath, newPath) {
262
262
  // prompt file is optional — copy is best-effort
263
263
  }
264
264
  }
265
- function validatePromptFile(tasksDir, id, promptOverride, prefix, errors) {
265
+ function validatePromptFile(tasksDir, id, promptOverride, title, prefix, errors) {
266
266
  const promptPath = promptOverride ?? path.join(tasksDir, `${id}.md`);
267
+ const shouldRequirePrompt = promptOverride !== undefined || title.trim().length === 0;
267
268
  try {
268
269
  const desc = readFileSync(promptPath, "utf8");
269
- if (desc.trim().length === 0) {
270
+ if (desc.trim().length === 0 && shouldRequirePrompt) {
270
271
  errors.push(`${prefix}: empty prompt file "${promptPath}" for ready task "${id}"`);
271
272
  }
272
273
  }
273
274
  catch {
274
- errors.push(`${prefix}: missing prompt file "${promptPath}" for ready task "${id}"`);
275
+ if (shouldRequirePrompt) {
276
+ errors.push(`${prefix}: missing prompt file "${promptPath}" for ready task "${id}"`);
277
+ }
275
278
  }
276
279
  }
277
280
  function validateDepsAndDates(parsed, parsedAll, id, prefix, errors) {
@@ -308,7 +311,7 @@ function validateActiveTaskLine(parsed, parsedAll, tasksDir, id, prefix, errors)
308
311
  errors.push(`${prefix}: task "${id}" has status:todo but it is not the final token — task will not be dispatched`);
309
312
  }
310
313
  if (statusValue === "todo" && parsed.isStatusFinalToken) {
311
- validatePromptFile(tasksDir, id, parsed.metadata["prompt"]?.[0], prefix, errors);
314
+ validatePromptFile(tasksDir, id, parsed.metadata["prompt"]?.[0], parsed.title, prefix, errors);
312
315
  }
313
316
  validateDepsAndDates(parsed, parsedAll, id, prefix, errors);
314
317
  }
@@ -340,7 +343,7 @@ export function validateTodoFile(todoPath, tasksDir) {
340
343
  idsSeen.set(lower, lineNum);
341
344
  }
342
345
  }
343
- if (id === undefined || parsed.metadata["agent"] === undefined) {
346
+ if (id === undefined) {
344
347
  continue;
345
348
  }
346
349
  if (parsed.completed) {
package/docs/commands.md CHANGED
@@ -18,7 +18,7 @@ crew task get GC-20260608-001 --source todo
18
18
  crew task get todo:GC-20260608-001 --prompt
19
19
  ```
20
20
 
21
- `crew task create "Short title" --source <source> --agent <agent>` creates a task in a source that supports creation. Todo.txt creation appends the todo line, defaults to priority `A` unless `--priority` is provided, writes `.tasks/<id>.md`, and leaves `status:todo` as the final meaningful token, so no separate ready command is required.
21
+ `crew task create "Short title" --source <source> [--agent <agent>]` creates a task in a source that supports creation. When `--agent` is omitted, it defaults to `any`. Todo.txt creation appends the todo line, defaults to priority `A` unless `--priority` is provided, writes `.tasks/<id>.md`, and leaves `status:todo` as the final meaningful token, so no separate ready command is required. Hand-written todo-txt lines can omit `.tasks/<id>.md` when the line has a non-empty title; that title becomes the prompt text.
22
22
 
23
23
  ```bash
24
24
  crew task create "Fix cancellation retry race" \
@@ -78,7 +78,7 @@ export default {
78
78
  };
79
79
  ```
80
80
 
81
- Creating a todo task appends a line with `status:todo` as the final meaningful token and writes the prompt to `.tasks/<id>.md`. New todo tasks default to priority `A`; pass `--priority <letter>` to override it.
81
+ Creating a todo task appends a line with `status:todo` as the final meaningful token and writes the prompt to `.tasks/<id>.md`. New todo tasks default to priority `A`; pass `--priority <letter>` to override it. If `--agent` is omitted, the task uses `agent:any`.
82
82
 
83
83
  ```bash
84
84
  crew task create "Fix cancellation retry race" \
@@ -94,6 +94,12 @@ crew task create "Fix cancellation retry race" \
94
94
  (A) Fix cancellation retry race +marketplace @backend id:GC-20260608-001 repo:ClipboardHealth/api agent:codex status:todo
95
95
  ```
96
96
 
97
+ For hand-written todo lines, a non-empty title is enough prompt text when `.tasks/<id>.md` is absent. Omit `agent:` to default to `agent:any`:
98
+
99
+ ```txt
100
+ Say goodbye repo:ClipboardHealth/groundcrew id:GC-20260608-002 status:todo
101
+ ```
102
+
97
103
  ## Linear
98
104
 
99
105
  The built-in Linear source supports listing, getting, writeback, and task creation through `crew task create`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.24.1",
3
+ "version": "4.24.2",
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",
@@ -81,7 +81,7 @@
81
81
  "@nx/js": "22.7.5",
82
82
  "@tsconfig/node24": "24.0.4",
83
83
  "@tsconfig/strictest": "2.0.8",
84
- "@types/node": "25.9.1",
84
+ "@types/node": "25.9.2",
85
85
  "@typescript/native-preview": "7.0.0-dev.20260604.1",
86
86
  "@vitest/coverage-v8": "4.1.8",
87
87
  "cspell": "10.0.1",