@clipboard-health/groundcrew 4.18.2 → 4.20.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 +22 -22
- package/crew.config.example.ts +5 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +39 -33
- package/dist/commands/cleaner.d.ts +4 -4
- package/dist/commands/cleaner.d.ts.map +1 -1
- package/dist/commands/cleaner.js +7 -7
- package/dist/commands/cleanupWorkspace.d.ts +1 -1
- package/dist/commands/cleanupWorkspace.d.ts.map +1 -1
- package/dist/commands/cleanupWorkspace.js +14 -14
- package/dist/commands/dispatcher.d.ts +4 -4
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +32 -32
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/eligibility.d.ts +2 -2
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +4 -4
- package/dist/commands/init.js +1 -1
- package/dist/commands/interruptWorkspace.d.ts +1 -1
- package/dist/commands/interruptWorkspace.d.ts.map +1 -1
- package/dist/commands/interruptWorkspace.js +18 -18
- package/dist/commands/orchestrator.d.ts +1 -1
- package/dist/commands/orchestrator.js +2 -2
- package/dist/commands/resumeWorkspace.d.ts +1 -1
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +35 -35
- package/dist/commands/reviewer.d.ts +7 -7
- package/dist/commands/reviewer.d.ts.map +1 -1
- package/dist/commands/reviewer.js +27 -27
- package/dist/commands/setupWorkspace.d.ts +5 -5
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +39 -39
- package/dist/commands/source.d.ts +2 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +126 -0
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +103 -103
- package/dist/commands/teardownReporter.js +10 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/adapterDefinition.d.ts +3 -3
- package/dist/lib/adapterDefinition.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.d.ts +3 -3
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +6 -6
- package/dist/lib/adapters/linear/fetch.d.ts +18 -18
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/linear/fetch.js +20 -20
- package/dist/lib/adapters/linear/index.js +2 -2
- package/dist/lib/adapters/linear/parsing.d.ts +1 -1
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.js +4 -4
- package/dist/lib/adapters/linear/statusNames.d.ts +1 -1
- package/dist/lib/adapters/linear/statusNames.d.ts.map +1 -1
- package/dist/lib/adapters/linear/writeback.d.ts +3 -3
- package/dist/lib/adapters/linear/writeback.d.ts.map +1 -1
- package/dist/lib/adapters/linear/writeback.js +1 -1
- package/dist/lib/adapters/shell/factory.d.ts +4 -4
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
- package/dist/lib/adapters/shell/factory.js +40 -14
- package/dist/lib/adapters/shell/index.js +2 -2
- package/dist/lib/adapters/shell/invoke.d.ts +2 -2
- package/dist/lib/adapters/shell/invoke.js +3 -3
- package/dist/lib/adapters/shell/schema.d.ts +5 -1
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
- package/dist/lib/adapters/shell/schema.js +24 -3
- package/dist/lib/adapters/todo-txt/index.d.ts +5 -0
- package/dist/lib/adapters/todo-txt/index.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/index.js +8 -0
- package/dist/lib/adapters/todo-txt/normalizer.d.ts +22 -0
- package/dist/lib/adapters/todo-txt/normalizer.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/normalizer.js +104 -0
- package/dist/lib/adapters/todo-txt/parser.d.ts +20 -0
- package/dist/lib/adapters/todo-txt/parser.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/parser.js +93 -0
- package/dist/lib/adapters/todo-txt/schema.d.ts +12 -0
- package/dist/lib/adapters/todo-txt/schema.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/schema.js +13 -0
- package/dist/lib/adapters/todo-txt/source.d.ts +5 -0
- package/dist/lib/adapters/todo-txt/source.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/source.js +142 -0
- package/dist/lib/adapters/todo-txt/writeback.d.ts +18 -0
- package/dist/lib/adapters/todo-txt/writeback.d.ts.map +1 -0
- package/dist/lib/adapters/todo-txt/writeback.js +352 -0
- package/dist/lib/agentLaunch.js +1 -1
- package/dist/lib/board.d.ts +13 -13
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +5 -5
- package/dist/lib/buildSources.d.ts +8 -4
- package/dist/lib/buildSources.d.ts.map +1 -1
- package/dist/lib/buildSources.js +2 -2
- package/dist/lib/config.d.ts +8 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -8
- package/dist/lib/launchCommand.js +1 -1
- package/dist/lib/repositoryValidation.d.ts +2 -2
- package/dist/lib/repositoryValidation.d.ts.map +1 -1
- package/dist/lib/repositoryValidation.js +1 -1
- package/dist/lib/runState.d.ts +11 -11
- package/dist/lib/runState.d.ts.map +1 -1
- package/dist/lib/runState.js +19 -19
- package/dist/lib/runStateCleanup.js +2 -2
- package/dist/lib/sourceCapabilities.d.ts +17 -0
- package/dist/lib/sourceCapabilities.d.ts.map +1 -0
- package/dist/lib/sourceCapabilities.js +63 -0
- package/dist/lib/srtLaunch.d.ts +1 -1
- package/dist/lib/srtLaunch.d.ts.map +1 -1
- package/dist/lib/srtLaunch.js +1 -1
- package/dist/lib/srtPolicy.js +1 -1
- package/dist/lib/stagedLaunch.d.ts +3 -3
- package/dist/lib/stagedLaunch.d.ts.map +1 -1
- package/dist/lib/stagedLaunch.js +3 -3
- package/dist/lib/{ticketSource.d.ts → taskSource.d.ts} +30 -30
- package/dist/lib/taskSource.d.ts.map +1 -0
- package/dist/lib/{ticketSource.js → taskSource.js} +9 -9
- package/dist/lib/testing/canonicalFixtures.d.ts +1 -1
- package/dist/lib/testing/canonicalFixtures.d.ts.map +1 -1
- package/dist/lib/testing/canonicalFixtures.js +1 -1
- package/dist/lib/tmuxAdapter.d.ts +1 -1
- package/dist/lib/tmuxAdapter.js +2 -2
- package/dist/lib/util.d.ts +3 -3
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +4 -4
- package/dist/lib/workspaceAdapter.d.ts +8 -8
- package/dist/lib/workspaceAdapter.d.ts.map +1 -1
- package/dist/lib/workspaceAdapter.js +2 -2
- package/dist/lib/workspaces.d.ts +1 -1
- package/dist/lib/workspaces.js +1 -1
- package/dist/lib/worktrees.d.ts +11 -11
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +47 -47
- package/docs/adr/{0002-one-ticket-source-path-linear-is-an-adapter.md → 0002-one-task-source-path-linear-is-an-adapter.md} +3 -3
- package/docs/commands.md +10 -10
- package/docs/configuration.md +19 -19
- package/docs/credentials.md +2 -2
- package/docs/runners.md +1 -1
- package/docs/setup-hook-agent-prompt.md +2 -2
- package/docs/setup-hooks.md +1 -1
- package/docs/{ticket-sources.md → task-sources.md} +9 -9
- package/docs/troubleshooting.md +5 -5
- package/package.json +13 -13
- package/dist/lib/ticketSource.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
Dispatch your
|
|
9
|
+
Dispatch your task backlog to local, interactive AI coding agents. One git worktree per task, sandboxed by default.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<a href="./static/demo.tape"><img alt="Groundcrew dispatching
|
|
20
|
+
<a href="./static/demo.tape"><img alt="Groundcrew dispatching tasks into tmux panes with coding agents running in parallel" src="./static/demo.gif" width="800"></a>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
|
-
Groundcrew watches assigned
|
|
23
|
+
Groundcrew watches assigned tasks, creates isolated worktrees, launches agent CLIs in dedicated terminals, and leaves each task's work on its own PR-ready branch. For the backstory, read _[Tasks to pull requests while you sleep](https://www.clipboardworks.com/resources/blog/tasks-to-pull-requests-while-you-sleep)_.
|
|
24
24
|
|
|
25
25
|
## Why
|
|
26
26
|
|
|
27
27
|
- **Local.** Agents run on your machine with your tools, shell, and credentials. That makes them more steerable than remote agents, and easy to nudge when they drift.
|
|
28
|
-
- **Interactive.** Each
|
|
29
|
-
- **One worktree per
|
|
28
|
+
- **Interactive.** Each task launches the real `claude` or `codex` CLI in its own terminal pane, not a wrapper that approximates it. Watch any session live and take over when you need to.
|
|
29
|
+
- **One worktree per task.** Agents work in parallel without stepping on each other.
|
|
30
30
|
- **Sandboxed by default.** Safehouse or Docker Sandboxes isolate each agent on the host; `none` is an explicit escape hatch.
|
|
31
|
-
- **Pluggable
|
|
31
|
+
- **Pluggable task sources.** Linear by default; Jira and local files via [task sources](./docs/task-sources.md).
|
|
32
32
|
- **Multi-agent routing.** Ships `claude` and `codex` presets; bring your own CLI in config.
|
|
33
33
|
|
|
34
34
|
## Prerequisites
|
|
@@ -56,7 +56,7 @@ crew init --global --project-dir ~/dev --repo OWNER/REPO --model claude
|
|
|
56
56
|
# 4. Set the clearance egress proxy allowlist.
|
|
57
57
|
export CLEARANCE_ALLOW_HOSTS_FILES="$(npm root -g)/@clipboard-health/groundcrew/clearance-allow-hosts"
|
|
58
58
|
|
|
59
|
-
# 5. Using Linear? Export your API key. (Jira and other trackers: see
|
|
59
|
+
# 5. Using Linear? Export your API key. (Jira and other trackers: see Task Pickup.)
|
|
60
60
|
export GROUNDCREW_LINEAR_API_KEY="lin_api_..."
|
|
61
61
|
|
|
62
62
|
# 6. Verify setup, then dispatch.
|
|
@@ -66,25 +66,25 @@ crew run --watch
|
|
|
66
66
|
|
|
67
67
|
`crew init --global` writes config to `${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/`. Pass `--repo` more than once for multiple repos. `--model claude` or `--model codex` chooses the single built-in model preset to enable in the generated config.
|
|
68
68
|
|
|
69
|
-
##
|
|
69
|
+
## Task Pickup
|
|
70
70
|
|
|
71
|
-
**Not on Linear?** Use Jira or local files via [
|
|
71
|
+
**Not on Linear?** Use Jira or local files via [task sources](./docs/task-sources.md).
|
|
72
72
|
|
|
73
|
-
Linear works out of the box: assign
|
|
73
|
+
Linear works out of the box: assign tasks to yourself and add an `agent-*` label.
|
|
74
74
|
|
|
75
75
|
- `agent-claude`, `agent-codex`, or `agent-<name>` routes to that model.
|
|
76
76
|
- `agent-any` routes to the enabled model with the most session headroom, after skipping models over their session limit or weekly paced budget.
|
|
77
|
-
-
|
|
77
|
+
- Tasks without an `agent-*` label are ignored by `crew run`; dispatch one manually with `crew start <TASK>`.
|
|
78
78
|
|
|
79
|
-
Groundcrew scans `workspace.knownRepositories` to infer which repo a
|
|
79
|
+
Groundcrew scans `workspace.knownRepositories` to infer which repo a task belongs to.
|
|
80
80
|
|
|
81
|
-
A
|
|
81
|
+
A task blocked by non-terminal blockers is skipped until those blockers are done.
|
|
82
82
|
|
|
83
|
-
### The
|
|
83
|
+
### The task description is the prompt
|
|
84
84
|
|
|
85
|
-
Groundcrew sends each agent a generic unattended-execution prompt plus the
|
|
85
|
+
Groundcrew sends each agent a generic unattended-execution prompt plus the task title and description. The prompt says how to work: read the repo instructions, make the smallest sensible change, verify it, and produce the requested output. The task description says what to do.
|
|
86
86
|
|
|
87
|
-
Write
|
|
87
|
+
Write tasks as complete agent instructions: the goal, the context and constraints, links to logs or screenshots, how to verify, and the output you want. A vague task gets a vague PR.
|
|
88
88
|
|
|
89
89
|
## Commands
|
|
90
90
|
|
|
@@ -93,12 +93,12 @@ crew init [--global | --local] [--force] [--dry-run] # create a crew.config.
|
|
|
93
93
|
[--project-dir <dir>] [--repo <repo>]...
|
|
94
94
|
[--runner <auto|safehouse|sdx|none>] [--model <claude|codex>]
|
|
95
95
|
crew doctor # check setup
|
|
96
|
-
crew status [<
|
|
96
|
+
crew status [<TASK>] # inspect current state or one task
|
|
97
97
|
crew run [--watch] # one-shot or --watch forever
|
|
98
|
-
crew start <
|
|
99
|
-
crew stop <
|
|
100
|
-
crew resume <
|
|
101
|
-
crew cleanup <
|
|
98
|
+
crew start <TASK> # provision + launch one task now
|
|
99
|
+
crew stop <TASK> [--reason <text>] # stop workspace, keep worktree
|
|
100
|
+
crew resume <TASK> # reopen a paused task
|
|
101
|
+
crew cleanup <TASK> # tear down every worktree for a task
|
|
102
102
|
crew upgrade [<version>] # reinstall crew globally through npm
|
|
103
103
|
```
|
|
104
104
|
|
|
@@ -147,7 +147,7 @@ There is no `linear` config block. Groundcrew reads `GROUNDCREW_LINEAR_API_KEY`
|
|
|
147
147
|
- [Runners](./docs/runners.md): Safehouse, Docker Sandboxes, and the `none` escape hatch.
|
|
148
148
|
- [Credentials](./docs/credentials.md): Linear API keys, 1Password, build secrets, and `preLaunch`.
|
|
149
149
|
- [Prepare worktree hooks](./docs/setup-hooks.md): `.groundcrew/config.json` `hooks.prepareWorktree` for per-repo dependency setup.
|
|
150
|
-
- [
|
|
150
|
+
- [Task sources](./docs/task-sources.md): custom shell/Jira/local-plan adapters.
|
|
151
151
|
- [Troubleshooting](./docs/troubleshooting.md): common operational pitfalls and fixes.
|
|
152
152
|
|
|
153
153
|
## Development
|
package/crew.config.example.ts
CHANGED
|
@@ -10,18 +10,18 @@ export default {
|
|
|
10
10
|
// `state.type` (`unstarted` → todo, `started` → in progress,
|
|
11
11
|
// `completed`/`canceled`/`duplicate` → terminal).
|
|
12
12
|
//
|
|
13
|
-
// Opt a
|
|
13
|
+
// Opt a task in: assign it to yourself and add an `agent-<model>`
|
|
14
14
|
// label (e.g. `agent-claude`, `agent-any`).
|
|
15
15
|
workspace: {
|
|
16
16
|
// Parent directory under which groundcrew clones repositories and (by
|
|
17
|
-
// default) creates per-
|
|
17
|
+
// default) creates per-task worktrees.
|
|
18
18
|
projectDir: "~/dev/groundcrew",
|
|
19
19
|
// Optional: collect ALL worktrees here instead of beside each repo. Useful
|
|
20
20
|
// when your repos live in more than one place. Defaults to projectDir.
|
|
21
21
|
// worktreeDir: "~/dev/worktrees",
|
|
22
22
|
// Repositories groundcrew is allowed to set up worktrees in. Add
|
|
23
23
|
// `<owner>/<repo>` or bare `<repo>` strings; the orchestrator scopes
|
|
24
|
-
//
|
|
24
|
+
// tasks to these and refuses unknown repos by default. Use the object
|
|
25
25
|
// form to point a repo at a different parent directory:
|
|
26
26
|
// { name: "other-org/other-repo", projectDirOverride: "~/work" }
|
|
27
27
|
knownRepositories: ["your-org/your-repo"],
|
|
@@ -30,7 +30,7 @@ export default {
|
|
|
30
30
|
default: "claude",
|
|
31
31
|
// `definitions` is the enabled model set. Built-in keys can use `{}` to
|
|
32
32
|
// opt into the shipped command/color/usage preset. Add `codex: {}` if you
|
|
33
|
-
// want both shipped agents, or add a custom entry and tag
|
|
33
|
+
// want both shipped agents, or add a custom entry and tag tasks with
|
|
34
34
|
// `agent-<name>`.
|
|
35
35
|
definitions: {
|
|
36
36
|
claude: {},
|
|
@@ -53,7 +53,7 @@ export default {
|
|
|
53
53
|
// Everything below is optional — defaults shown for reference. Uncomment
|
|
54
54
|
// and edit to override.
|
|
55
55
|
//
|
|
56
|
-
// // Additional pluggable
|
|
56
|
+
// // Additional pluggable task sources beyond the implicit built-in
|
|
57
57
|
// // Linear adapter. The most common use is `kind: "shell"`, which wires
|
|
58
58
|
// // any external system via command templates that emit/consume JSON.
|
|
59
59
|
// // See the shell adapter's ShellIssue schema for the JSON contract
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA+QA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwCvD"}
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,10 @@ import { interruptWorkspaceCli } from "./commands/interruptWorkspace.js";
|
|
|
6
6
|
import { orchestrate } from "./commands/orchestrator.js";
|
|
7
7
|
import { resumeWorkspaceCli } from "./commands/resumeWorkspace.js";
|
|
8
8
|
import { setupWorkspaceCli } from "./commands/setupWorkspace.js";
|
|
9
|
+
import { sourceCli } from "./commands/source.js";
|
|
9
10
|
import { statusCli } from "./commands/status.js";
|
|
10
11
|
import { createDefaultUpgradeCliOptions, upgradeCli } from "./commands/upgrade.js";
|
|
11
|
-
import { errorMessage, parseDryRunPositionals, readEnvironmentVariable,
|
|
12
|
+
import { errorMessage, parseDryRunPositionals, readEnvironmentVariable, readTaskArgument, setVerbose, writeError, writeOutput, } from "./lib/util.js";
|
|
12
13
|
const REMOVED_SANDBOX_COMMAND_MESSAGE = [
|
|
13
14
|
"`crew sandbox` is no longer supported.",
|
|
14
15
|
"Groundcrew now launches agents inside existing sbx sandboxes but does not list, create, regenerate, authenticate, or remove them.",
|
|
@@ -40,7 +41,7 @@ async function setupCli(argv) {
|
|
|
40
41
|
async function runCli(argv) {
|
|
41
42
|
let watch = false;
|
|
42
43
|
let dryRun = false;
|
|
43
|
-
let
|
|
44
|
+
let task;
|
|
44
45
|
for (let index = 0; index < argv.length; index += 1) {
|
|
45
46
|
const argument = argv[index];
|
|
46
47
|
if (argument === "--watch") {
|
|
@@ -51,31 +52,31 @@ async function runCli(argv) {
|
|
|
51
52
|
dryRun = true;
|
|
52
53
|
continue;
|
|
53
54
|
}
|
|
54
|
-
if (argument === "--
|
|
55
|
-
|
|
55
|
+
if (argument === "--task") {
|
|
56
|
+
task = readTaskArgument(argv, index, "run");
|
|
56
57
|
index += 1;
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
59
60
|
throw new Error(`crew run: unknown argument: ${argument}`);
|
|
60
61
|
}
|
|
61
|
-
if (
|
|
62
|
-
throw new Error("crew run: --watch and --
|
|
62
|
+
if (task !== undefined && watch) {
|
|
63
|
+
throw new Error("crew run: --watch and --task are mutually exclusive");
|
|
63
64
|
}
|
|
64
|
-
if (
|
|
65
|
+
if (task === undefined) {
|
|
65
66
|
await orchestrate({ watch, dryRun });
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
|
-
warnDeprecated({ oldForm: "run --
|
|
69
|
-
await setupWorkspaceCli(
|
|
69
|
+
warnDeprecated({ oldForm: "run --task", newForm: "start" });
|
|
70
|
+
await setupWorkspaceCli(task, { dryRun });
|
|
70
71
|
}
|
|
71
|
-
const START_USAGE = "crew start <
|
|
72
|
+
const START_USAGE = "crew start <task> [--dry-run]";
|
|
72
73
|
async function startCli(argv) {
|
|
73
74
|
const { dryRun, positionals } = parseDryRunPositionals(argv, START_USAGE);
|
|
74
|
-
const [
|
|
75
|
-
if (
|
|
75
|
+
const [task, ...extras] = positionals;
|
|
76
|
+
if (task === undefined || task.length === 0 || extras.length > 0) {
|
|
76
77
|
throw new Error(`Usage: ${START_USAGE}`);
|
|
77
78
|
}
|
|
78
|
-
await setupWorkspaceCli(
|
|
79
|
+
await setupWorkspaceCli(task, { dryRun });
|
|
79
80
|
}
|
|
80
81
|
async function upgradeCliInvoke(argv) {
|
|
81
82
|
const metadata = packageMetadata();
|
|
@@ -84,21 +85,21 @@ async function upgradeCliInvoke(argv) {
|
|
|
84
85
|
cliMetaUrl: import.meta.url,
|
|
85
86
|
}));
|
|
86
87
|
}
|
|
87
|
-
function
|
|
88
|
-
if (argv[0] !== "--
|
|
88
|
+
function doctorTaskAlias(argv) {
|
|
89
|
+
if (argv[0] !== "--task") {
|
|
89
90
|
return undefined;
|
|
90
91
|
}
|
|
91
|
-
const
|
|
92
|
+
const task = readTaskArgument(argv, 0, "doctor");
|
|
92
93
|
if (argv.length > 2) {
|
|
93
|
-
throw new Error("Usage: crew status [<
|
|
94
|
+
throw new Error("Usage: crew status [<task>]");
|
|
94
95
|
}
|
|
95
|
-
return
|
|
96
|
+
return task;
|
|
96
97
|
}
|
|
97
98
|
async function doctorCli(argv) {
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
100
|
-
warnDeprecated({ oldForm: "doctor --
|
|
101
|
-
await statusCli([
|
|
99
|
+
const aliasTask = doctorTaskAlias(argv);
|
|
100
|
+
if (aliasTask !== undefined) {
|
|
101
|
+
warnDeprecated({ oldForm: "doctor --task", newForm: "status" });
|
|
102
|
+
await statusCli([aliasTask]);
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
105
|
if (argv.length > 0) {
|
|
@@ -114,13 +115,13 @@ const SUBCOMMANDS = {
|
|
|
114
115
|
invoke: initConfigCli,
|
|
115
116
|
},
|
|
116
117
|
run: {
|
|
117
|
-
summary: "Run the orchestrator: poll sources and start eligible
|
|
118
|
+
summary: "Run the orchestrator: poll sources and start eligible tasks (one-shot by default)",
|
|
118
119
|
usage: "[--watch] [--dry-run]",
|
|
119
120
|
invoke: runCli,
|
|
120
121
|
},
|
|
121
122
|
start: {
|
|
122
|
-
summary: "Launch one
|
|
123
|
-
usage: "<
|
|
123
|
+
summary: "Launch one task immediately, bypassing eligibility",
|
|
124
|
+
usage: "<task> [--dry-run]",
|
|
124
125
|
invoke: startCli,
|
|
125
126
|
},
|
|
126
127
|
doctor: {
|
|
@@ -128,24 +129,29 @@ const SUBCOMMANDS = {
|
|
|
128
129
|
usage: "",
|
|
129
130
|
invoke: doctorCli,
|
|
130
131
|
},
|
|
132
|
+
source: {
|
|
133
|
+
summary: "Inspect configured task sources",
|
|
134
|
+
usage: "<list|verify> [...]",
|
|
135
|
+
invoke: sourceCli,
|
|
136
|
+
},
|
|
131
137
|
status: {
|
|
132
|
-
summary: "Print read-only groundcrew state, or one
|
|
133
|
-
usage: "[<
|
|
138
|
+
summary: "Print read-only groundcrew state, or one task's local/Linear status",
|
|
139
|
+
usage: "[<task>]",
|
|
134
140
|
invoke: statusCli,
|
|
135
141
|
},
|
|
136
142
|
cleanup: {
|
|
137
143
|
summary: "Tear down a worktree",
|
|
138
|
-
usage: "[--force] <
|
|
144
|
+
usage: "[--force] <task>",
|
|
139
145
|
invoke: cleanupWorkspaceCli,
|
|
140
146
|
},
|
|
141
147
|
stop: {
|
|
142
|
-
summary: "Stop a live
|
|
143
|
-
usage: "<
|
|
148
|
+
summary: "Stop a live task workspace while preserving its worktree",
|
|
149
|
+
usage: "<task> [--reason <text>]",
|
|
144
150
|
invoke: interruptWorkspaceCli,
|
|
145
151
|
},
|
|
146
152
|
interrupt: {
|
|
147
153
|
summary: "Deprecated alias for `crew stop`",
|
|
148
|
-
usage: "<
|
|
154
|
+
usage: "<task> [--reason <text>]",
|
|
149
155
|
deprecated: true,
|
|
150
156
|
invoke: async (argv) => {
|
|
151
157
|
warnDeprecated({ oldForm: "interrupt", newForm: "stop" });
|
|
@@ -153,8 +159,8 @@ const SUBCOMMANDS = {
|
|
|
153
159
|
},
|
|
154
160
|
},
|
|
155
161
|
resume: {
|
|
156
|
-
summary: "Reopen an existing
|
|
157
|
-
usage: "<
|
|
162
|
+
summary: "Reopen an existing task worktree with a continuation prompt",
|
|
163
|
+
usage: "<task>",
|
|
158
164
|
invoke: resumeWorkspaceCli,
|
|
159
165
|
},
|
|
160
166
|
setup: {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-iteration scanner that closes workspaces and removes worktrees for
|
|
3
|
-
*
|
|
3
|
+
* tasks that have reached a terminal status. One per `orchestrate()`
|
|
4
4
|
* invocation; stateless across iterations. Mirrors `Dispatcher`.
|
|
5
5
|
*/
|
|
6
6
|
import type { ResolvedConfig } from "../lib/config.ts";
|
|
7
|
-
import { type BoardState } from "../lib/
|
|
7
|
+
import { type BoardState } from "../lib/taskSource.ts";
|
|
8
8
|
import { type WorktreeEntry } from "../lib/worktrees.ts";
|
|
9
9
|
interface CleanerDeps {
|
|
10
10
|
config: ResolvedConfig;
|
|
11
11
|
}
|
|
12
12
|
export interface Cleaner {
|
|
13
|
-
runOnce(arguments_: {
|
|
13
|
+
runOnce: (arguments_: {
|
|
14
14
|
state: BoardState;
|
|
15
15
|
worktreeEntries: readonly WorktreeEntry[];
|
|
16
16
|
dryRun: boolean;
|
|
17
17
|
signal?: AbortSignal;
|
|
18
|
-
})
|
|
18
|
+
}) => Promise<void>;
|
|
19
19
|
}
|
|
20
20
|
export declare function createCleaner(deps: CleanerDeps): Cleaner;
|
|
21
21
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleaner.d.ts","sourceRoot":"","sources":["../../src/commands/cleaner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"cleaner.d.ts","sourceRoot":"","sources":["../../src/commands/cleaner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,qBAAqB,CAAC;AAGpE,UAAU,WAAW;IACnB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAoDxD"}
|
package/dist/commands/cleaner.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-iteration scanner that closes workspaces and removes worktrees for
|
|
3
|
-
*
|
|
3
|
+
* tasks that have reached a terminal status. One per `orchestrate()`
|
|
4
4
|
* invocation; stateless across iterations. Mirrors `Dispatcher`.
|
|
5
5
|
*/
|
|
6
6
|
import { recordCleanedUpRuns } from "../lib/runStateCleanup.js";
|
|
7
|
-
import { naturalIdFromCanonical } from "../lib/
|
|
7
|
+
import { naturalIdFromCanonical } from "../lib/taskSource.js";
|
|
8
8
|
import { log, logEvent } from "../lib/util.js";
|
|
9
9
|
import { worktrees } from "../lib/worktrees.js";
|
|
10
10
|
import { logTeardown, recordTeardownEvents } from "./teardownReporter.js";
|
|
@@ -12,24 +12,24 @@ export function createCleaner(deps) {
|
|
|
12
12
|
const { config } = deps;
|
|
13
13
|
async function runOnce(arguments_) {
|
|
14
14
|
const { state, worktreeEntries, dryRun, signal } = arguments_;
|
|
15
|
-
const
|
|
15
|
+
const terminalTasks = new Set(state.issues
|
|
16
16
|
.filter((issue) => issue.status === "done")
|
|
17
17
|
.map((issue) => naturalIdFromCanonical(issue.id)));
|
|
18
|
-
if (
|
|
18
|
+
if (terminalTasks.size === 0) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
const stale = worktreeEntries.filter((entry) =>
|
|
21
|
+
const stale = worktreeEntries.filter((entry) => terminalTasks.has(entry.task));
|
|
22
22
|
if (stale.length === 0) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
if (dryRun) {
|
|
26
26
|
log(`[dry-run] ${stale.length} worktree(s) due for cleanup:`);
|
|
27
27
|
for (const entry of stale) {
|
|
28
|
-
log(` - ${entry.repository}-${entry.
|
|
28
|
+
log(` - ${entry.repository}-${entry.task} (${entry.kind})`);
|
|
29
29
|
logEvent("cleanup", {
|
|
30
30
|
outcome: "skipped",
|
|
31
31
|
reason: "dry_run",
|
|
32
|
-
|
|
32
|
+
task: entry.task,
|
|
33
33
|
repository: entry.repository,
|
|
34
34
|
kind: entry.kind,
|
|
35
35
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/cleanupWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAQnE,MAAM,WAAW,uBAAuB;IACtC,
|
|
1
|
+
{"version":3,"file":"cleanupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/cleanupWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAQnE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAwBD,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvE"}
|
|
@@ -14,35 +14,35 @@ function parseArguments(argv) {
|
|
|
14
14
|
continue;
|
|
15
15
|
}
|
|
16
16
|
if (argument.startsWith("-")) {
|
|
17
|
-
throw new Error(`Unknown option: ${argument}\nUsage: crew cleanup [--force] <
|
|
17
|
+
throw new Error(`Unknown option: ${argument}\nUsage: crew cleanup [--force] <task>\nExample: crew cleanup team-220`);
|
|
18
18
|
}
|
|
19
19
|
positionals.push(argument);
|
|
20
20
|
}
|
|
21
|
-
const [
|
|
22
|
-
if (
|
|
23
|
-
throw new Error("Usage: crew cleanup [--force] <
|
|
21
|
+
const [task, ...extras] = positionals;
|
|
22
|
+
if (task === undefined || task.length === 0 || extras.length > 0) {
|
|
23
|
+
throw new Error("Usage: crew cleanup [--force] <task>\nExample: crew cleanup team-220");
|
|
24
24
|
}
|
|
25
|
-
return {
|
|
25
|
+
return { task: task.toLowerCase(), force };
|
|
26
26
|
}
|
|
27
27
|
export async function cleanupWorkspace(config, options) {
|
|
28
|
-
const {
|
|
29
|
-
const entries = worktrees.
|
|
28
|
+
const { task, force = false } = options;
|
|
29
|
+
const entries = worktrees.findByTask(config, task);
|
|
30
30
|
if (entries.length === 0) {
|
|
31
|
-
if (readRunState(config,
|
|
32
|
-
log(`No worktree found for ${
|
|
31
|
+
if (readRunState(config, task) === undefined) {
|
|
32
|
+
log(`No worktree found for ${task}; nothing to clean up.`);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
const workspaceProbe = await workspaces.probe(config);
|
|
36
36
|
if (workspaceProbe.kind === "unavailable") {
|
|
37
|
-
log(`No worktree found for ${
|
|
37
|
+
log(`No worktree found for ${task}; workspace probe unavailable, leaving run-state intact.`);
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
-
if (workspaceProbe.names.has(
|
|
41
|
-
log(`No worktree found for ${
|
|
40
|
+
if (workspaceProbe.names.has(task)) {
|
|
41
|
+
log(`No worktree found for ${task}; workspace still present; leaving run-state intact.`);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
removeRunState(config,
|
|
45
|
-
log(`No worktree found for ${
|
|
44
|
+
removeRunState(config, task);
|
|
45
|
+
log(`No worktree found for ${task}; cleared stale run-state.`);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
const result = await worktrees.teardown(config, entries, { force });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-iteration decider that picks Todo
|
|
2
|
+
* Per-iteration decider that picks Todo tasks to start and acts on the
|
|
3
3
|
* picks. Stateless across iterations. The Board adapter owns its own writeback
|
|
4
4
|
* caches (e.g., Linear's team-state cache lives in `src/lib/adapters/linear/writeback.ts`).
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Board } from "../lib/board.ts";
|
|
10
10
|
import type { ResolvedConfig } from "../lib/config.ts";
|
|
11
|
-
import { type BoardState, type Issue } from "../lib/
|
|
11
|
+
import { type BoardState, type Issue } from "../lib/taskSource.ts";
|
|
12
12
|
import type { UsageByModel } from "../lib/usage.ts";
|
|
13
13
|
import type { WorktreeEntry } from "../lib/worktrees.ts";
|
|
14
14
|
interface DispatcherDeps {
|
|
@@ -16,7 +16,7 @@ interface DispatcherDeps {
|
|
|
16
16
|
board: Board;
|
|
17
17
|
}
|
|
18
18
|
export interface Dispatcher {
|
|
19
|
-
runOnce(arguments_: {
|
|
19
|
+
runOnce: (arguments_: {
|
|
20
20
|
state: BoardState;
|
|
21
21
|
worktreeEntries: readonly WorktreeEntry[];
|
|
22
22
|
/** Lazy so dispatcher can early-return on idle ticks without paying the codexbar shell-out. */
|
|
@@ -29,7 +29,7 @@ export interface Dispatcher {
|
|
|
29
29
|
* printing a second line per tick.
|
|
30
30
|
*/
|
|
31
31
|
idleSuffix?: string;
|
|
32
|
-
})
|
|
32
|
+
}) => Promise<void>;
|
|
33
33
|
}
|
|
34
34
|
export declare function createDispatcher(deps: DispatcherDeps): Dispatcher;
|
|
35
35
|
export declare function formatActiveSlotList(active: readonly Issue[]): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD,UAAU,cAAc;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,+FAA+F;QAC/F,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAaD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAyNjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
|