@clipboard-health/groundcrew 4.34.3 → 4.35.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 -0
- package/dist/cli.js +2 -2
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +1 -0
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +6 -1
- package/dist/commands/setupWorkspace.d.ts +2 -0
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +7 -0
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +87 -0
- package/dist/lib/agentLaunch.d.ts +2 -0
- package/dist/lib/agentLaunch.d.ts.map +1 -1
- package/dist/lib/agentLaunch.js +5 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -0
- package/dist/lib/launchCommand.d.ts +9 -0
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +38 -7
- package/dist/lib/runState.d.ts +6 -0
- package/dist/lib/runState.d.ts.map +1 -1
- package/dist/lib/runState.js +4 -0
- package/docs/commands.md +42 -4
- package/docs/configuration.md +25 -25
- package/docs/credentials.md +2 -2
- package/docs/runners.md +3 -3
- package/docs/task-sources.md +9 -3
- package/docs/troubleshooting.md +8 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -94,6 +94,7 @@ crew source list|verify [<source>] # inspect configured ta
|
|
|
94
94
|
crew task list [--source <name>] # list tasks across sources
|
|
95
95
|
crew task get <TASK> [--source <name>] [--prompt] # inspect one task or its prompt
|
|
96
96
|
crew task create "Title" --source <name> [--agent <name>] # create a source task
|
|
97
|
+
crew task done <TASK> [--allow-dirty] # mark a no-PR task done
|
|
97
98
|
crew task validate [<source>] # validate task content
|
|
98
99
|
crew status [<TASK>] # inspect current state or one task
|
|
99
100
|
crew run [--watch] # one-shot or --watch forever
|
package/dist/cli.js
CHANGED
|
@@ -136,8 +136,8 @@ const SUBCOMMANDS = {
|
|
|
136
136
|
invoke: sourceCli,
|
|
137
137
|
},
|
|
138
138
|
task: {
|
|
139
|
-
summary: "List, get, and
|
|
140
|
-
usage: "<list|get|create> [...]",
|
|
139
|
+
summary: "List, get, create, and complete tasks across configured sources",
|
|
140
|
+
usage: "<list|get|create|done|validate> [...]",
|
|
141
141
|
invoke: taskCli,
|
|
142
142
|
},
|
|
143
143
|
status: {
|
|
@@ -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,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;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,
|
|
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;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAoOjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAenE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AA0ID,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA6Ef;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
|
|
@@ -3,9 +3,10 @@ import { getLinearClient } from "../lib/adapters/linear/client.js";
|
|
|
3
3
|
import { isLinearEnabled } from "../lib/buildSources.js";
|
|
4
4
|
import { loadConfig } from "../lib/config.js";
|
|
5
5
|
import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
|
|
6
|
+
import { workerEnvironmentForTask } from "../lib/launchCommand.js";
|
|
6
7
|
import { readRunState, recordRunState } from "../lib/runState.js";
|
|
7
8
|
import { removeStagedPrompt, stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
8
|
-
import { naturalIdFromCanonical } from "../lib/taskSource.js";
|
|
9
|
+
import { naturalIdFromCanonical, toCanonicalId } from "../lib/taskSource.js";
|
|
9
10
|
import { errorMessage, log } from "../lib/util.js";
|
|
10
11
|
import { workspaces } from "../lib/workspaces.js";
|
|
11
12
|
import { resolveLaunchDir, worktrees } from "../lib/worktrees.js";
|
|
@@ -38,6 +39,7 @@ async function contextFromLinear(config, task, worktree) {
|
|
|
38
39
|
worktree,
|
|
39
40
|
title: resolved.title,
|
|
40
41
|
description: resolved.description,
|
|
42
|
+
completionTaskId: toCanonicalId("linear", task),
|
|
41
43
|
resumeCount: 0,
|
|
42
44
|
};
|
|
43
45
|
}
|
|
@@ -53,6 +55,7 @@ async function contextFromState(config, task, state, worktree) {
|
|
|
53
55
|
worktree,
|
|
54
56
|
title: details?.title ?? task.toUpperCase(),
|
|
55
57
|
description: details?.description ?? "",
|
|
58
|
+
completionTaskId: state.completionTaskId ?? task,
|
|
56
59
|
...(state.reason === undefined ? {} : { reason: state.reason }),
|
|
57
60
|
resumeCount: state.resumeCount,
|
|
58
61
|
};
|
|
@@ -147,6 +150,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
147
150
|
workingDir: launchDir,
|
|
148
151
|
secretsFile,
|
|
149
152
|
sandboxName,
|
|
153
|
+
workerEnvironment: workerEnvironmentForTask(context.completionTaskId),
|
|
150
154
|
}));
|
|
151
155
|
const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launchCommand);
|
|
152
156
|
await openAgentWorkspace({
|
|
@@ -178,6 +182,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
178
182
|
workspaceName: task,
|
|
179
183
|
state: "resumed",
|
|
180
184
|
resumeCount: context.resumeCount + 1,
|
|
185
|
+
completionTaskId: context.completionTaskId,
|
|
181
186
|
...(context.reason === undefined ? {} : { reason: context.reason }),
|
|
182
187
|
},
|
|
183
188
|
});
|
|
@@ -7,6 +7,8 @@ export interface TaskDetails {
|
|
|
7
7
|
}
|
|
8
8
|
export interface SetupWorkspaceOptions {
|
|
9
9
|
task: string;
|
|
10
|
+
/** Canonical source id for worker self-completion; falls back to `task`. */
|
|
11
|
+
completionTaskId?: string;
|
|
10
12
|
repository: string;
|
|
11
13
|
agent: string;
|
|
12
14
|
details: TaskDetails;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAuBnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CA8Hf;AAgJD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -3,6 +3,7 @@ import { loadConfig } from "../lib/config.js";
|
|
|
3
3
|
import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
|
|
4
4
|
import { createBoard } from "../lib/board.js";
|
|
5
5
|
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
6
|
+
import { workerEnvironmentForTask } from "../lib/launchCommand.js";
|
|
6
7
|
import { resolvePrepareWorktreeCommand } from "../lib/repositoryHooks.js";
|
|
7
8
|
import { recordRunState } from "../lib/runState.js";
|
|
8
9
|
import { stageBuildSecrets, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
@@ -80,6 +81,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
80
81
|
defaultHooks: config.defaults.hooks,
|
|
81
82
|
});
|
|
82
83
|
const secretsFile = prepareWorktreeCommand === undefined ? undefined : stageBuildSecrets(promptDir);
|
|
84
|
+
const completionTaskId = options.completionTaskId ?? task;
|
|
83
85
|
const { launchCommand, srtSettingsDir: stagedSrtSettingsDir } = composeAgentLaunch({
|
|
84
86
|
runner,
|
|
85
87
|
task,
|
|
@@ -90,6 +92,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
90
92
|
secretsFile,
|
|
91
93
|
prepareWorktreeCommand,
|
|
92
94
|
sandboxName,
|
|
95
|
+
workerEnvironment: workerEnvironmentForTask(completionTaskId),
|
|
93
96
|
});
|
|
94
97
|
srtSettingsDir = stagedSrtSettingsDir;
|
|
95
98
|
const launchCmd = stageWorkspaceLaunchCommand(promptDir, launchCommand);
|
|
@@ -113,6 +116,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
113
116
|
workspaceName: task,
|
|
114
117
|
state: "running",
|
|
115
118
|
title: taskDetails.title,
|
|
119
|
+
completionTaskId,
|
|
116
120
|
...(taskDetails.url === undefined ? {} : { url: taskDetails.url }),
|
|
117
121
|
});
|
|
118
122
|
log(`${okMark()} "${task}" launched (${agent}) worktree ${worktreeName}`);
|
|
@@ -135,6 +139,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
135
139
|
state: "failed-to-launch",
|
|
136
140
|
detail: errorMessage(error),
|
|
137
141
|
title: options.details.title,
|
|
142
|
+
completionTaskId: options.completionTaskId ?? task,
|
|
138
143
|
...(options.details.url === undefined ? {} : { url: options.details.url }),
|
|
139
144
|
});
|
|
140
145
|
throw error;
|
|
@@ -197,6 +202,7 @@ function recordRunStateBestEffort(arguments_) {
|
|
|
197
202
|
workspaceName: arguments_.workspaceName,
|
|
198
203
|
state: arguments_.state,
|
|
199
204
|
title: arguments_.title,
|
|
205
|
+
completionTaskId: arguments_.completionTaskId,
|
|
200
206
|
...(arguments_.detail === undefined ? {} : { detail: arguments_.detail }),
|
|
201
207
|
...(arguments_.url === undefined ? {} : { url: arguments_.url }),
|
|
202
208
|
},
|
|
@@ -275,6 +281,7 @@ export async function setupWorkspaceCli(task, options = {}) {
|
|
|
275
281
|
const naturalId = naturalIdFromCanonical(resolved.id);
|
|
276
282
|
await setupWorkspace(config, {
|
|
277
283
|
task: naturalId,
|
|
284
|
+
completionTaskId: resolved.id,
|
|
278
285
|
repository: resolved.repository,
|
|
279
286
|
agent: resolved.agent,
|
|
280
287
|
details: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAoyBA,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3D"}
|
package/dist/commands/task.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { createBoard } from "../lib/board.js";
|
|
1
2
|
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
2
3
|
import { AGENT_ANY, loadConfig } from "../lib/config.js";
|
|
4
|
+
import { findPullRequestsForBranch } from "../lib/pullRequests.js";
|
|
3
5
|
import { naturalIdFromCanonical, } from "../lib/taskSource.js";
|
|
4
6
|
import { parseSourceFilterArgs, writeOutput } from "../lib/util.js";
|
|
7
|
+
import { worktrees } from "../lib/worktrees.js";
|
|
5
8
|
const TASK_USAGE = `Usage: crew task <subcommand>
|
|
6
9
|
|
|
7
10
|
Subcommands:
|
|
8
11
|
list [options] List tasks across configured sources
|
|
9
12
|
get <task-id> [options] Get one task
|
|
10
13
|
create "Short title" [options] Create one task
|
|
14
|
+
done <task-id> [options] Mark one task done
|
|
11
15
|
validate [source] [options] Validate task content`;
|
|
12
16
|
const LIST_USAGE = `Usage: crew task list [options]
|
|
13
17
|
|
|
@@ -27,6 +31,7 @@ Options:
|
|
|
27
31
|
--json Print normalized task JSON.
|
|
28
32
|
--prompt Print only the task description/prompt.`;
|
|
29
33
|
const CREATE_USAGE = `Usage: crew task create "Short title" --source <source> [--agent <agent>] [options]`;
|
|
34
|
+
const DONE_USAGE = `Usage: crew task done <task-id> [--allow-dirty]`;
|
|
30
35
|
const CANONICAL_STATUSES = [
|
|
31
36
|
"todo",
|
|
32
37
|
"in-progress",
|
|
@@ -251,10 +256,34 @@ function parseCreateOptions(argv) {
|
|
|
251
256
|
};
|
|
252
257
|
return { title, sourceName: state.sourceName, input, json: state.json };
|
|
253
258
|
}
|
|
259
|
+
function parseDoneOptions(argv) {
|
|
260
|
+
const positionals = [];
|
|
261
|
+
let allowDirty = false;
|
|
262
|
+
for (const argument of argv) {
|
|
263
|
+
if (argument === "--allow-dirty") {
|
|
264
|
+
allowDirty = true;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (argument.startsWith("-")) {
|
|
268
|
+
throw new Error(`crew task done: unknown option: ${argument}\n${DONE_USAGE}`);
|
|
269
|
+
}
|
|
270
|
+
positionals.push(argument);
|
|
271
|
+
}
|
|
272
|
+
const [taskId, ...extras] = positionals;
|
|
273
|
+
if (taskId === undefined || taskId.length === 0 || extras.length > 0) {
|
|
274
|
+
throw new Error(DONE_USAGE);
|
|
275
|
+
}
|
|
276
|
+
return { taskId, allowDirty };
|
|
277
|
+
}
|
|
254
278
|
async function loadTaskSources() {
|
|
255
279
|
const config = await loadConfig();
|
|
256
280
|
return await buildSources(sourcesFromConfig(config), { globalConfig: config });
|
|
257
281
|
}
|
|
282
|
+
async function loadTaskBoard() {
|
|
283
|
+
const config = await loadConfig();
|
|
284
|
+
const sources = await buildSources(sourcesFromConfig(config), { globalConfig: config });
|
|
285
|
+
return { config, board: createBoard(sources) };
|
|
286
|
+
}
|
|
258
287
|
function findSource(sources, sourceName) {
|
|
259
288
|
const source = sources.find((candidate) => candidate.name === sourceName);
|
|
260
289
|
if (source === undefined) {
|
|
@@ -472,6 +501,60 @@ async function taskCreateCli(argv) {
|
|
|
472
501
|
}
|
|
473
502
|
writeOutput(created.id);
|
|
474
503
|
}
|
|
504
|
+
function matchingWorktreeEntries(arguments_) {
|
|
505
|
+
const task = naturalIdFromCanonical(arguments_.issue.id);
|
|
506
|
+
return worktrees
|
|
507
|
+
.findByTask(arguments_.config, task)
|
|
508
|
+
.filter((entry) => arguments_.issue.repository === undefined ||
|
|
509
|
+
entry.repository === arguments_.issue.repository);
|
|
510
|
+
}
|
|
511
|
+
function describeDirtiness(dirtiness) {
|
|
512
|
+
if (dirtiness.kind === "dirty") {
|
|
513
|
+
return `${dirtiness.modified} modified, ${dirtiness.untracked} untracked`;
|
|
514
|
+
}
|
|
515
|
+
return "unknown git status";
|
|
516
|
+
}
|
|
517
|
+
async function worktreeHasPullRequest(entry) {
|
|
518
|
+
const pullRequests = await findPullRequestsForBranch({
|
|
519
|
+
cwd: entry.dir,
|
|
520
|
+
branchName: entry.branchName,
|
|
521
|
+
});
|
|
522
|
+
return pullRequests.length > 0;
|
|
523
|
+
}
|
|
524
|
+
async function assertCanMarkDone(arguments_) {
|
|
525
|
+
if (arguments_.allowDirty) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
for (const entry of matchingWorktreeEntries({
|
|
529
|
+
config: arguments_.config,
|
|
530
|
+
issue: arguments_.issue,
|
|
531
|
+
})) {
|
|
532
|
+
// oxlint-disable-next-line no-await-in-loop -- one git status per matching worktree keeps diagnostics deterministic.
|
|
533
|
+
const dirtiness = await worktrees.probeWorkingTree({ worktreeDir: entry.dir });
|
|
534
|
+
if (dirtiness.kind === "clean") {
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
// oxlint-disable-next-line no-await-in-loop -- only dirty/unknown worktrees need the PR lookup.
|
|
538
|
+
if (dirtiness.kind === "dirty" && (await worktreeHasPullRequest(entry))) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
throw new Error(`crew task done: refusing to mark ${arguments_.issue.id} done because ${entry.dir} has ${describeDirtiness(dirtiness)} and no matching PR. Commit or stash the work, open a PR, or rerun with --allow-dirty.`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async function taskDoneCli(argv) {
|
|
545
|
+
const options = parseDoneOptions(argv);
|
|
546
|
+
const { config, board } = await loadTaskBoard();
|
|
547
|
+
const issue = await board.resolveOne(options.taskId);
|
|
548
|
+
if (issue === undefined) {
|
|
549
|
+
throw new Error(`Task ${options.taskId} not found across configured sources.`);
|
|
550
|
+
}
|
|
551
|
+
await assertCanMarkDone({ config, issue, allowDirty: options.allowDirty });
|
|
552
|
+
const result = await board.markDone(issue);
|
|
553
|
+
if (result.outcome === "unsupported") {
|
|
554
|
+
throw new Error(`crew task done: ${result.reason}`);
|
|
555
|
+
}
|
|
556
|
+
writeOutput(`Marked ${issue.id} done.`);
|
|
557
|
+
}
|
|
475
558
|
const VALIDATE_USAGE = `Usage: crew task validate [source]
|
|
476
559
|
|
|
477
560
|
Options:
|
|
@@ -536,6 +619,10 @@ export async function taskCli(argv) {
|
|
|
536
619
|
await taskCreateCli(rest);
|
|
537
620
|
return;
|
|
538
621
|
}
|
|
622
|
+
if (verb === "done") {
|
|
623
|
+
await taskDoneCli(rest);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
539
626
|
if (verb === "validate") {
|
|
540
627
|
await taskValidateCli(rest);
|
|
541
628
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LocalRunner, type AgentDefinition, type ResolvedConfig } from "./config.ts";
|
|
2
|
+
import { type WorkerEnvironment } from "./launchCommand.ts";
|
|
2
3
|
/**
|
|
3
4
|
* Stage any srt settings and build the workspace launch command — the assembly
|
|
4
5
|
* shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
|
|
@@ -16,6 +17,7 @@ export declare function composeAgentLaunch(input: {
|
|
|
16
17
|
secretsFile?: string | undefined;
|
|
17
18
|
prepareWorktreeCommand?: string | undefined;
|
|
18
19
|
sandboxName?: string | undefined;
|
|
20
|
+
workerEnvironment?: WorkerEnvironment | undefined;
|
|
19
21
|
}): {
|
|
20
22
|
launchCommand: string;
|
|
21
23
|
srtSettingsDir: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOhF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CA2BhE;AAsBD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqD/B;AAwBD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB"}
|
package/dist/lib/agentLaunch.js
CHANGED
|
@@ -36,6 +36,7 @@ export function composeAgentLaunch(input) {
|
|
|
36
36
|
srtAgentSettingsFile: staged?.agentFile,
|
|
37
37
|
srtSettingsDir: staged?.directory,
|
|
38
38
|
srtAgentConfigDirEnv: staged?.agentConfigDirEnv,
|
|
39
|
+
workerEnvironment: input.workerEnvironment,
|
|
39
40
|
safehouseAddDirs: input.runner === "safehouse" ? resolveSafehouseAddDirs(input.worktreeDir) : undefined,
|
|
40
41
|
});
|
|
41
42
|
return { launchCommand, srtSettingsDir: staged?.directory };
|
|
@@ -88,6 +89,10 @@ export async function prepareAgentLaunch(input) {
|
|
|
88
89
|
throw new Error(`Local groundcrew ${input.purpose} on agent '${input.agent}' cannot inject preLaunchEnv when 'cmd' already starts with 'safehouse'. ` +
|
|
89
90
|
"Your cmd owns the wrap, so add the names to its own '--env-pass=' flag, or drop the 'safehouse' prefix from 'cmd' to let groundcrew compose the flag for you.");
|
|
90
91
|
}
|
|
92
|
+
if (runner === "safehouse" && /^safehouse(?:\s|$)/.test(input.definition.cmd)) {
|
|
93
|
+
throw new Error(`Local groundcrew ${input.purpose} on agent '${input.agent}' cannot inject worker self-completion env when 'cmd' already starts with 'safehouse'. ` +
|
|
94
|
+
"Your cmd owns the wrap, so add GROUNDCREW_TASK_ID,GROUNDCREW_COMPLETE to its own '--env-pass=' flag, or drop the 'safehouse' prefix from 'cmd' to let groundcrew compose the flag for you.");
|
|
95
|
+
}
|
|
91
96
|
const sandboxName = runner === "sdx" && input.definition.sandbox !== undefined
|
|
92
97
|
? sandboxNameFor({ agent: input.definition.sandbox.agent })
|
|
93
98
|
: undefined;
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -108,6 +108,7 @@ const DEFAULT_PROMPT_INITIAL = [
|
|
|
108
108
|
"2. Implement the smallest sensible change that completes the task.",
|
|
109
109
|
"3. Run the repo's documented verification command. If no documented command exists, run the smallest relevant test suite you can find and fix failures you introduced before continuing.",
|
|
110
110
|
"4. Follow the task description for output. If no output instructions exist, open a PR with `Closes {{task}}` in the description. If you cannot open one, leave the branch ready and record the blocker.",
|
|
111
|
+
"5. If the requested work is complete, no PR is needed, and any dirty worktree state is expected or explicitly allowed, run the command in `GROUNDCREW_COMPLETE` to mark the task done.",
|
|
111
112
|
].join("\n");
|
|
112
113
|
const ALLOWED_PROMPT_PLACEHOLDERS = new Set([
|
|
113
114
|
"{{task}}",
|
|
@@ -37,6 +37,10 @@ export declare function isEnvironmentAssignment(token: string): boolean;
|
|
|
37
37
|
* profile; srt uses it to pick the agent's credential profile in `srtPolicy`.
|
|
38
38
|
*/
|
|
39
39
|
export declare function inferAgentCommandName(agentCmd: string): string;
|
|
40
|
+
declare const WORKER_ENVIRONMENT_NAMES: readonly ["GROUNDCREW_TASK_ID", "GROUNDCREW_COMPLETE"];
|
|
41
|
+
type WorkerEnvironmentName = (typeof WORKER_ENVIRONMENT_NAMES)[number];
|
|
42
|
+
export type WorkerEnvironment = Readonly<Record<WorkerEnvironmentName, string>>;
|
|
43
|
+
export declare function workerEnvironmentForTask(taskId: string): WorkerEnvironment;
|
|
40
44
|
interface LaunchCommandArguments {
|
|
41
45
|
definition: AgentDefinition;
|
|
42
46
|
promptFile: string;
|
|
@@ -111,6 +115,11 @@ interface LaunchCommandArguments {
|
|
|
111
115
|
* pre-existing behavior). Only consumed by the safehouse wrap.
|
|
112
116
|
*/
|
|
113
117
|
safehouseAddDirs?: readonly string[] | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Groundcrew-managed task metadata exposed to the launched worker. Forwarded
|
|
120
|
+
* to the agent process, not the prepareWorktree hook.
|
|
121
|
+
*/
|
|
122
|
+
workerEnvironment?: WorkerEnvironment | undefined;
|
|
114
123
|
}
|
|
115
124
|
/**
|
|
116
125
|
* Build the shell command that runs inside the workspace. The prompt is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAMvF;AAsMD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACnE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAMvF;AAsMD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAED,QAAA,MAAM,wBAAwB,YAAI,oBAAoB,EAAE,qBAAqB,CAAU,CAAC;AAExF,KAAK,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC;AAEhF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAK1E;AAsBD,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACnE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACjD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAkC7E"}
|
|
@@ -247,6 +247,26 @@ export function inferAgentCommandName(agentCmd) {
|
|
|
247
247
|
}
|
|
248
248
|
return commandName;
|
|
249
249
|
}
|
|
250
|
+
const WORKER_ENVIRONMENT_NAMES = ["GROUNDCREW_TASK_ID", "GROUNDCREW_COMPLETE"];
|
|
251
|
+
export function workerEnvironmentForTask(taskId) {
|
|
252
|
+
return {
|
|
253
|
+
GROUNDCREW_TASK_ID: taskId,
|
|
254
|
+
GROUNDCREW_COMPLETE: `crew task done ${taskId}`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function workerEnvironmentNames(workerEnvironment) {
|
|
258
|
+
return workerEnvironment === undefined ? [] : WORKER_ENVIRONMENT_NAMES;
|
|
259
|
+
}
|
|
260
|
+
function workerEnvironmentExports(workerEnvironment) {
|
|
261
|
+
if (workerEnvironment === undefined) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
return WORKER_ENVIRONMENT_NAMES.map((name) => `export ${name}=${shellSingleQuote(workerEnvironment[name])}`);
|
|
265
|
+
}
|
|
266
|
+
function envPassFlag(names) {
|
|
267
|
+
const uniqueNames = [...new Set(names)];
|
|
268
|
+
return uniqueNames.length === 0 ? "" : `--env-pass=${uniqueNames.join(",")} `;
|
|
269
|
+
}
|
|
250
270
|
/**
|
|
251
271
|
* Build the shell command that runs inside the workspace. The prompt is
|
|
252
272
|
* staged in a temp file (so backticks/quotes/$ in the description survive),
|
|
@@ -277,6 +297,9 @@ export function buildLaunchCommand(arguments_) {
|
|
|
277
297
|
// inject into. Fail loudly instead of silently dropping the contract.
|
|
278
298
|
throw new Error("preLaunchEnv cannot be injected when `cmd` starts with `safehouse` — your cmd owns the wrap, so add the names to its own `--env-pass=` flag, or drop the `safehouse` prefix from `cmd` to let groundcrew compose the flag for you.");
|
|
279
299
|
}
|
|
300
|
+
if (arguments_.workerEnvironment !== undefined && arguments_.runner === "safehouse") {
|
|
301
|
+
throw new Error("workerEnvironment cannot be injected when `cmd` starts with `safehouse` — your cmd owns the wrap, so add GROUNDCREW_TASK_ID,GROUNDCREW_COMPLETE to its own `--env-pass=` flag, or drop the `safehouse` prefix from `cmd` to let groundcrew compose the flag for you.");
|
|
302
|
+
}
|
|
280
303
|
return buildUnwrappedHostLaunchCommand(arguments_);
|
|
281
304
|
}
|
|
282
305
|
/**
|
|
@@ -315,6 +338,7 @@ function buildUnwrappedHostLaunchCommand(arguments_) {
|
|
|
315
338
|
if (arguments_.secretsFile !== undefined) {
|
|
316
339
|
lines.push(unsetSecretsLine());
|
|
317
340
|
}
|
|
341
|
+
lines.push(...workerEnvironmentExports(arguments_.workerEnvironment));
|
|
318
342
|
lines.push(...preLaunchPromptAndExec({
|
|
319
343
|
definition: arguments_.definition,
|
|
320
344
|
worktreeDir: arguments_.worktreeDir,
|
|
@@ -365,8 +389,10 @@ function buildSafehouseLaunchCommand(arguments_) {
|
|
|
365
389
|
// credentials out of the profile-neutral prepare phase — see PR #128.
|
|
366
390
|
// Trailing space keeps each flag separated from the next argv token.
|
|
367
391
|
const prepareWorktreeEnvPassFlag = arguments_.secretsFile === undefined ? "" : `--env-pass=${BUILD_SECRET_NAMES.join(",")} `;
|
|
368
|
-
const
|
|
369
|
-
|
|
392
|
+
const agentEnvPassFlag = envPassFlag([
|
|
393
|
+
...(arguments_.definition.preLaunchEnv ?? []),
|
|
394
|
+
...workerEnvironmentNames(arguments_.workerEnvironment),
|
|
395
|
+
]);
|
|
370
396
|
// safehouse reads colon-separated paths from `--add-dirs`; both wraps get the
|
|
371
397
|
// same grant so the prepareWorktree hook and the agent can each reach git.
|
|
372
398
|
// Quote the whole value so shell-special chars survive; the trailing space
|
|
@@ -398,7 +424,7 @@ function buildSafehouseLaunchCommand(arguments_) {
|
|
|
398
424
|
if (arguments_.secretsFile !== undefined) {
|
|
399
425
|
lines.push(unsetSecretsLine());
|
|
400
426
|
}
|
|
401
|
-
lines.push(`_safehouse_shim_dir=$(mktemp -d "\${TMPDIR:-/tmp}/groundcrew-safehouse-XXXXXX")`, shimAndPromptTrap, `_safehouse_shim="$_safehouse_shim_dir/${safehouseCommandName}"`, `ln -s /bin/sh "$_safehouse_shim"`,
|
|
427
|
+
lines.push(...workerEnvironmentExports(arguments_.workerEnvironment), `_safehouse_shim_dir=$(mktemp -d "\${TMPDIR:-/tmp}/groundcrew-safehouse-XXXXXX")`, shimAndPromptTrap, `_safehouse_shim="$_safehouse_shim_dir/${safehouseCommandName}"`, `ln -s /bin/sh "$_safehouse_shim"`,
|
|
402
428
|
// Safehouse selects an agent profile from the wrapped command's basename.
|
|
403
429
|
// Running the real launch chain as `sh -c` would make it see `sh`, so use
|
|
404
430
|
// an agent-named symlink to /bin/sh. This preserves per-agent profile
|
|
@@ -492,7 +518,10 @@ function buildSrtLaunchCommand(arguments_) {
|
|
|
492
518
|
const agentConfigDirAssignment = arguments_.srtAgentConfigDirEnv === undefined
|
|
493
519
|
? ""
|
|
494
520
|
: ` ${arguments_.srtAgentConfigDirEnv.name}=${shellSingleQuote(arguments_.srtAgentConfigDirEnv.value)}`;
|
|
495
|
-
const agentWrap = `env -i ${baseline}${agentConfigDirAssignment}${srtForwardedEnv(
|
|
521
|
+
const agentWrap = `env -i ${baseline}${agentConfigDirAssignment}${srtForwardedEnv([
|
|
522
|
+
...(arguments_.definition.preLaunchEnv ?? []),
|
|
523
|
+
...workerEnvironmentNames(arguments_.workerEnvironment),
|
|
524
|
+
])} ${agentTarget}`;
|
|
496
525
|
// One EXIT trap wipes both the settings dir and the prompt dir, covering
|
|
497
526
|
// every failure window between here and the post-wrap cleanup.
|
|
498
527
|
const cleanup = `rm -rf ${shellSingleQuote(arguments_.srtSettingsDir)}; rm -rf ${shellSingleQuote(promptDir)}`;
|
|
@@ -510,7 +539,7 @@ function buildSrtLaunchCommand(arguments_) {
|
|
|
510
539
|
if (prepareWorktreeCommand !== undefined) {
|
|
511
540
|
lines.push(`${prepareWrap} sh -c ${shellSingleQuote(prepareWorktreeCommand)}`);
|
|
512
541
|
}
|
|
513
|
-
lines.push(`{ ${agentWrap} sh -c ${shellSingleQuote(agentCommand)} sh "$_p"; _srt_status=$?; rm -rf ${shellSingleQuote(arguments_.srtSettingsDir)}; trap - EXIT; exit "$_srt_status"; }`);
|
|
542
|
+
lines.push(...workerEnvironmentExports(arguments_.workerEnvironment), `{ ${agentWrap} sh -c ${shellSingleQuote(agentCommand)} sh "$_p"; _srt_status=$?; rm -rf ${shellSingleQuote(arguments_.srtSettingsDir)}; trap - EXIT; exit "$_srt_status"; }`);
|
|
514
543
|
return lines.join(" && ");
|
|
515
544
|
}
|
|
516
545
|
function buildSdxLaunchCommand(arguments_) {
|
|
@@ -527,15 +556,17 @@ function buildSdxLaunchCommand(arguments_) {
|
|
|
527
556
|
if (arguments_.secretsFile !== undefined) {
|
|
528
557
|
innerParts.push(unsetSecretsLine());
|
|
529
558
|
}
|
|
559
|
+
innerParts.push(...workerEnvironmentExports(arguments_.workerEnvironment));
|
|
530
560
|
innerParts.push(agentCommand);
|
|
531
561
|
const innerCommand = innerParts.join("; ");
|
|
532
562
|
// Passthrough form (`-e KEY` without `=VALUE`): sbx reads each value
|
|
533
563
|
// from its own env at invocation time — populated by sourceSecretsLine
|
|
534
564
|
// a few lines up. Avoids `-e KEY="$KEY"`, which would embed the value
|
|
535
565
|
// in argv and break on `"`, `$`, or backticks in the token.
|
|
536
|
-
const
|
|
566
|
+
const sbxEnvironmentNames = arguments_.secretsFile === undefined ? [] : BUILD_SECRET_NAMES;
|
|
567
|
+
const sbxEnvironmentFlags = sbxEnvironmentNames.length === 0
|
|
537
568
|
? ""
|
|
538
|
-
: `${
|
|
569
|
+
: `${sbxEnvironmentNames.map((name) => `-e ${name}`).join(" ")} `;
|
|
539
570
|
const lines = [trapCleanupLine(promptDir)];
|
|
540
571
|
if (arguments_.secretsFile !== undefined) {
|
|
541
572
|
lines.push(sourceSecretsLine(arguments_.secretsFile));
|
package/dist/lib/runState.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface RunState {
|
|
|
26
26
|
* just the task id.
|
|
27
27
|
*/
|
|
28
28
|
url?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Canonical source-prefixed id used for no-PR self-completion. Cached so
|
|
31
|
+
* resumed workers keep the same completion target as the original launch.
|
|
32
|
+
*/
|
|
33
|
+
completionTaskId?: string;
|
|
29
34
|
}
|
|
30
35
|
export interface RunStateDraft {
|
|
31
36
|
task: string;
|
|
@@ -40,6 +45,7 @@ export interface RunStateDraft {
|
|
|
40
45
|
resumeCount?: number;
|
|
41
46
|
title?: string;
|
|
42
47
|
url?: string;
|
|
48
|
+
completionTaskId?: string;
|
|
43
49
|
}
|
|
44
50
|
export interface RecordRunStateInput {
|
|
45
51
|
config: ResolvedConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runState.d.ts","sourceRoot":"","sources":["../../src/lib/runState.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE3F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"runState.d.ts","sourceRoot":"","sources":["../../src/lib/runState.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE3F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC,GAAG;QACrD,KAAK,EAAE,iBAAiB,CAAC;KAC1B,CAAC;CACH;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG,MAAM,CAEjF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1F;AAqFD,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAYvF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CA4BnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,GAAG,SAAS,CAc/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEzE"}
|
package/dist/lib/runState.js
CHANGED
|
@@ -44,6 +44,7 @@ function parseRunState(value) {
|
|
|
44
44
|
const detail = stringField(value, "detail");
|
|
45
45
|
const title = stringField(value, "title");
|
|
46
46
|
const url = stringField(value, "url");
|
|
47
|
+
const completionTaskId = stringField(value, "completionTaskId");
|
|
47
48
|
if (task === undefined ||
|
|
48
49
|
repository === undefined ||
|
|
49
50
|
agent === undefined ||
|
|
@@ -73,6 +74,7 @@ function parseRunState(value) {
|
|
|
73
74
|
...(detail === undefined ? {} : { detail }),
|
|
74
75
|
...(title === undefined ? {} : { title }),
|
|
75
76
|
...(url === undefined ? {} : { url }),
|
|
77
|
+
...(completionTaskId === undefined ? {} : { completionTaskId }),
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
80
|
function writeState(config, state) {
|
|
@@ -105,6 +107,7 @@ export function recordRunState(input) {
|
|
|
105
107
|
// transitions.
|
|
106
108
|
const title = input.state.title ?? existing?.title;
|
|
107
109
|
const url = input.state.url ?? existing?.url;
|
|
110
|
+
const completionTaskId = input.state.completionTaskId ?? existing?.completionTaskId;
|
|
108
111
|
const state = {
|
|
109
112
|
task: taskKey(input.state.task),
|
|
110
113
|
repository: input.state.repository,
|
|
@@ -120,6 +123,7 @@ export function recordRunState(input) {
|
|
|
120
123
|
...(input.state.detail === undefined ? {} : { detail: input.state.detail }),
|
|
121
124
|
...(title === undefined ? {} : { title }),
|
|
122
125
|
...(url === undefined ? {} : { url }),
|
|
126
|
+
...(completionTaskId === undefined ? {} : { completionTaskId }),
|
|
123
127
|
};
|
|
124
128
|
writeState(input.config, state);
|
|
125
129
|
return state;
|
package/docs/commands.md
CHANGED
|
@@ -41,6 +41,44 @@ crew task create "Fix cancellation retry race" \
|
|
|
41
41
|
--description "Investigate retry handling."
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
`crew task done <task-id>` marks one task done through its source adapter. Use
|
|
45
|
+
it for completed work that intentionally does not produce a PR. The command
|
|
46
|
+
resolves canonical IDs such as `todo:flaky-triage-1` directly, or natural IDs
|
|
47
|
+
when they match exactly one configured source. Sources without a done writeback
|
|
48
|
+
return an unsupported error.
|
|
49
|
+
|
|
50
|
+
Groundcrew checks matching local worktrees before marking a task done. Clean
|
|
51
|
+
worktrees, and tasks with no local worktree, are allowed. A dirty worktree with
|
|
52
|
+
no matching PR is refused by default so later cleanup does not discard
|
|
53
|
+
uncommitted work; pass `--allow-dirty` only when that dirty state is expected.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# PR-producing tasks usually complete automatically:
|
|
57
|
+
# open PR -> in-review, merged PR -> done.
|
|
58
|
+
|
|
59
|
+
# Manual completion for no-PR operational work:
|
|
60
|
+
crew task done todo:flaky-triage-1
|
|
61
|
+
|
|
62
|
+
# Explicit override when a no-PR task intentionally leaves local changes:
|
|
63
|
+
crew task done todo:docs-refresh-1 --allow-dirty
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For recurring no-PR tasks, keep recurrence in the source and complete the
|
|
67
|
+
current task with `crew task done`. The todo-txt source marks the current line
|
|
68
|
+
done and schedules the next `status:todo` recurrence itself.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
crew task create "Run flaky triage sweep" \
|
|
72
|
+
--source todo \
|
|
73
|
+
--agent codex \
|
|
74
|
+
--repo ClipboardHealth/groundcrew \
|
|
75
|
+
--id flaky-triage-1 \
|
|
76
|
+
--rec 2h \
|
|
77
|
+
--description "Triage the flaky queue. No PR is needed when the queue is updated."
|
|
78
|
+
|
|
79
|
+
crew task done todo:flaky-triage-1
|
|
80
|
+
```
|
|
81
|
+
|
|
44
82
|
## Status
|
|
45
83
|
|
|
46
84
|
`crew status <TASK>` prints a read-only snapshot for one task: cached title and URL when present, recorded run state, live workspace presence, matching worktrees, git dirtiness, PR links for matching branches, recent log lines when present, and the task status from the configured task source.
|
|
@@ -57,7 +95,7 @@ crew status ENG-123
|
|
|
57
95
|
===================
|
|
58
96
|
task: eng-123 in-progress https://linear.app/example/issue/ENG-123
|
|
59
97
|
title: Multi-event extractor: year inference can produce date_start > date_end
|
|
60
|
-
run: running;
|
|
98
|
+
run: running; agent=claude; updated=2026-05-26T00:01:00.000Z; resumes=0
|
|
61
99
|
workspace: live
|
|
62
100
|
|
|
63
101
|
Worktrees
|
|
@@ -77,9 +115,9 @@ Recent logs
|
|
|
77
115
|
|
|
78
116
|
## Doctor
|
|
79
117
|
|
|
80
|
-
`crew doctor` checks host prerequisites only: config validity, task-source reachability, required binaries on PATH, workspace backend availability, `workspace.projectDir`, local runner capability, and enabled
|
|
118
|
+
`crew doctor` checks host prerequisites only: config validity, task-source reachability, required binaries on PATH, workspace backend availability, `workspace.projectDir`, local runner capability, and enabled agent commands.
|
|
81
119
|
|
|
82
|
-
Doctor's command introspection is intentionally shallow. It reports the resolved local runner and tokenizes each
|
|
120
|
+
Doctor's command introspection is intentionally shallow. It reports the resolved local runner and tokenizes each agent `cmd`, then checks the first two non-flag tokens against PATH. Boolean flags without values, env-var assignments, shell pipelines, and subshells are not parsed.
|
|
83
121
|
|
|
84
122
|
## Start
|
|
85
123
|
|
|
@@ -106,4 +144,4 @@ The command closes the cmux/tmux/zellij workspace if present, records local run
|
|
|
106
144
|
|
|
107
145
|
`crew resume <TASK>` reopens an existing task worktree with a continuation prompt. Resume never creates a new worktree; if none exists it fails and leaves re-dispatch to `crew start <task>`.
|
|
108
146
|
|
|
109
|
-
The resume prompt tells the agent to inspect git status and diff before editing, includes the previous interrupt reason when recorded, and reuses the recorded
|
|
147
|
+
The resume prompt tells the agent to inspect git status and diff before editing, includes the previous interrupt reason when recorded, and reuses the recorded agent, repository, branch, runner, sandbox, and workspace backend. When no run-state file exists but a worktree does, resume falls back to Linear resolution for the agent and task context.
|
package/docs/configuration.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
Workspace settings and at least one enabled
|
|
3
|
+
Workspace settings and at least one enabled agent are required; everything else has a default.
|
|
4
4
|
|
|
5
5
|
| Key | What |
|
|
6
6
|
| ----------------------------- | -------------------------------------------------------------------- |
|
|
7
7
|
| `workspace.projectDir` | Parent dir for cloned repos and the default task worktree root. |
|
|
8
8
|
| `workspace.worktreeDir` | Optional parent dir for task worktrees. |
|
|
9
9
|
| `workspace.knownRepositories` | Repos searched for in task descriptions to infer where work belongs. |
|
|
10
|
-
| `
|
|
10
|
+
| `agents.definitions` | Enabled agent set. Built-in presets can be enabled with `{}`. |
|
|
11
11
|
|
|
12
12
|
The branch prefix (`<prefix>-<TASK>`) defaults to `os.userInfo().username`; override it with `git.branchPrefix` (see the full reference below). Changing it only affects newly created worktrees; existing local branches keep their original names until cleaned up. Groundcrew picks up every issue assigned to your API key's viewer that carries an `agent-*` label across every visible team and project, governed by a single `orchestrator.maximumInProgress` budget.
|
|
13
13
|
|
|
@@ -92,11 +92,11 @@ The "Loaded config from ..." line at startup tells you which config won.
|
|
|
92
92
|
|
|
93
93
|
## Agent Label Routing
|
|
94
94
|
|
|
95
|
-
- `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled
|
|
96
|
-
- `agent-any` routes to the
|
|
97
|
-
- Unknown `agent-<name>` falls back to `
|
|
98
|
-
- A built-in `agent-<name>` label whose
|
|
99
|
-
- No `agent-*` label is ignored by `crew run`. Dispatch on demand with `crew start <TASK>`, which falls back to `
|
|
95
|
+
- `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled agent.
|
|
96
|
+
- `agent-any` routes to the agent with the most session headroom, after skipping agents over their session limit or weekly paced budget.
|
|
97
|
+
- Unknown `agent-<name>` falls back to `agents.default`.
|
|
98
|
+
- A built-in `agent-<name>` label whose agent is not enabled falls back to `agents.default` with a warning.
|
|
99
|
+
- No `agent-*` label is ignored by `crew run`. Dispatch on demand with `crew start <TASK>`, which falls back to `agents.default`.
|
|
100
100
|
- Todo tasks blocked by non-terminal blockers are skipped until their blockers reach a terminal status.
|
|
101
101
|
|
|
102
102
|
Status classification uses Linear's default status names `In Progress` and `In Review` to disambiguate multiple `started` workflow states. Statuses that do not match those names fall back to Linear's workflow `state.type` (`unstarted`, `started`, `completed`, `canceled`, `duplicate`), so broad lifecycle classification still works without configuration. Parent issues with children are ignored; sub-issues are the work items.
|
|
@@ -136,13 +136,13 @@ export default {
|
|
|
136
136
|
};
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
## Enabling
|
|
139
|
+
## Enabling Agent Presets
|
|
140
140
|
|
|
141
|
-
Groundcrew ships built-in presets for `claude` and `codex`, but
|
|
141
|
+
Groundcrew ships built-in presets for `claude` and `codex`, but agents are not enabled by default. List the agents you want in `agents.definitions`:
|
|
142
142
|
|
|
143
143
|
```ts
|
|
144
144
|
export default {
|
|
145
|
-
|
|
145
|
+
agents: {
|
|
146
146
|
default: "claude",
|
|
147
147
|
definitions: {
|
|
148
148
|
claude: {},
|
|
@@ -155,7 +155,7 @@ To keep both shipped presets enabled:
|
|
|
155
155
|
|
|
156
156
|
```ts
|
|
157
157
|
export default {
|
|
158
|
-
|
|
158
|
+
agents: {
|
|
159
159
|
default: "claude",
|
|
160
160
|
definitions: {
|
|
161
161
|
claude: {},
|
|
@@ -167,15 +167,15 @@ export default {
|
|
|
167
167
|
|
|
168
168
|
Rules:
|
|
169
169
|
|
|
170
|
-
- `
|
|
170
|
+
- `agents.definitions` is the enabled agent set; `crew doctor` only probes listed agents.
|
|
171
171
|
- Built-in entries can be `{}` or partial overrides such as `{ cmd: "..." }`.
|
|
172
|
-
- Custom
|
|
173
|
-
- `
|
|
174
|
-
- Legacy
|
|
172
|
+
- Custom agent names must provide `cmd` and `color`.
|
|
173
|
+
- `agents.default` must point at an enabled agent.
|
|
174
|
+
- Legacy agent entries like `codex: { disabled: true }` are rejected with migration guidance; remove unwanted entries instead.
|
|
175
175
|
|
|
176
176
|
## Prompt Customization
|
|
177
177
|
|
|
178
|
-
Groundcrew ships one
|
|
178
|
+
Groundcrew ships one agent-agnostic unattended prompt by default. It tells the agent to make reasonable assumptions, follow repository instructions, run documented verification, review its diff, open a PR when GitHub/`gh` is available, and include a workspace continuation hint when known.
|
|
179
179
|
|
|
180
180
|
This prompt describes how the agent works, not what it does. The task is the task description, which groundcrew passes through unchanged. Keep source-specific instructions, acceptance criteria, links, and output requirements in the task body. Override `prompts.initial` only to change the execution contract for every dispatched task — team-wide review rules, required verification, local tool conventions — not to encode behavior for a single task type.
|
|
181
181
|
|
|
@@ -242,15 +242,15 @@ and hook contract.
|
|
|
242
242
|
| `defaults.hooks.prepareWorktree` | optional | Fallback repo-preparation command used only when the worktree does not define `.groundcrew/config.json` `hooks.prepareWorktree`. The hook runs after worktree creation and before the agent starts. Repo-local config wins. |
|
|
243
243
|
| `orchestrator.maximumInProgress` | `4` | Cap on in-progress tasks at once for this `crew` instance. |
|
|
244
244
|
| `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
|
|
245
|
-
| `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`.
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
| `
|
|
245
|
+
| `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. An agent whose codexbar session window exceeds this percentage is skipped that tick. Agents are also skipped when codexbar reports weekly usage over the current weekly paced budget. |
|
|
246
|
+
| `agents.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TASK>` for unlabeled tasks. `crew run` ignores unlabeled tasks and does not apply this default. Must exist in `agents.definitions`. If you enable only `codex`, set `default: "codex"`. |
|
|
247
|
+
| `agents.definitions` | **required** | Enabled agent set. Built-in keys (`claude`, `codex`) can use `{}` to opt into the shipped preset. Custom agent names must provide `cmd` and `color`. |
|
|
248
|
+
| `agents.definitions.<name>.cmd` | preset for built-ins | Shell command launched for the agent. Required for custom agents. Runs in the worktree through the resolved `local.runner`. `{{worktree}}` is replaced before launch; `{{sandbox}}` expands to the sbx sandbox name under the sdx runner and an empty string otherwise. |
|
|
249
|
+
| `agents.definitions.<name>.color` | preset for built-ins | Color for the workspace status pill (cmux only; tmux and zellij silently drop it). Required for custom agents. |
|
|
250
|
+
| `agents.definitions.<name>.usage` | preset for built-ins | If set, codexbar usage is fetched for this agent and gated by `sessionLimitPercentage` plus the weekly paced budget when codexbar exposes a weekly window. When `usage.codexbar.source` is omitted, groundcrew uses `oauth` for Codex/Claude on macOS, `auto` for other macOS providers, and `cli` elsewhere. Set to `{ disabled: true }` to disable usage gating while keeping the agent enabled. |
|
|
251
|
+
| `agents.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the agent. Required at launch when `local.runner` resolves to `sdx`. Field: `agent` (required sbx agent name). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
|
|
252
|
+
| `agents.definitions.<name>.preLaunch` | optional | Host-only shell snippet run before the agent exec and outside Safehouse/sdx. Exports survive into the launch shell; under the default `safehouse` runner they are only forwarded to the agent when listed via `preLaunchEnv` or when `cmd` includes its own `safehouse --env-pass=NAMES`. `{{worktree}}` is substituted. A non-zero exit aborts launch. Not supported when `local.runner` resolves to `sdx` in v1. |
|
|
253
|
+
| `agents.definitions.<name>.preLaunchEnv` | optional | Companion to `preLaunch`: list of env var names to append to groundcrew's `safehouse-clearance` `--env-pass=` flag, so `preLaunch` exports reach the agent without overriding `cmd` and losing the project's egress allowlist. Each entry must match `[A-Za-z_][A-Za-z0-9_]*`. Under `runner: "none"` exports already inherit and `preLaunchEnv` is a no-op. An empty array is a uniform no-op in every runner; a non-empty list is rejected when `cmd` already starts with `safehouse` or when `runner` resolves to `sdx`. |
|
|
254
254
|
| `prompts.initial` | unattended template | First message sent to the agent: the execution wrapper around each task. The task description is the task-specific prompt. Placeholders: `{{task}}`, `{{worktree}}`, `{{title}}`, `{{description}}`. Override only to change the execution contract for every task, such as team-wide review rules or tool conventions. Mutually exclusive with `prompts.promptFile`. |
|
|
255
255
|
| `prompts.promptFile` | optional | Path to a UTF-8 file whose contents become `prompts.initial`, read at load time. Resolved relative to the config file's directory; `~` is expanded and absolute paths are used as-is. The JSON-friendly alternative to inlining a large prompt or `readFileSync`. Mutually exclusive with `prompts.initial`. |
|
|
256
256
|
| `workspaceKind` | `"auto"` | Terminal session manager. `"auto"` picks `cmux` when on PATH, else `tmux`. Set to `"cmux"`, `"tmux"`, or `"zellij"` to fail loudly when the chosen backend is missing. |
|
package/docs/credentials.md
CHANGED
|
@@ -52,7 +52,7 @@ The "preLaunch never sees build secrets" contract is enforced differently per ru
|
|
|
52
52
|
Under the default `safehouse` runner, the agent runs under a sanitized env allowlist. Exports from `preLaunch` land in the launch shell but are stripped before reaching the agent unless they are forwarded. `preLaunchEnv` is the supported way to forward them:
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
|
-
|
|
55
|
+
agents: {
|
|
56
56
|
definitions: {
|
|
57
57
|
claude: {
|
|
58
58
|
preLaunch: "SESSION_TOKEN=$(your-mint-command) && export SESSION_TOKEN",
|
|
@@ -69,7 +69,7 @@ Under `runner: "none"`, exports flow through unchanged and `preLaunchEnv` is a n
|
|
|
69
69
|
<details>
|
|
70
70
|
<summary>Manual fallback when <code>cmd</code> brings its own <code>safehouse</code> wrap</summary>
|
|
71
71
|
|
|
72
|
-
If your `cmd` already starts with `safehouse`, groundcrew will not auto-compose `--env-pass=` for you and a non-empty `preLaunchEnv` is rejected at launch. Add the names to your own `cmd` instead. This opts the
|
|
72
|
+
If your `cmd` already starts with `safehouse`, groundcrew will not auto-compose `--env-pass=` for you and a non-empty `preLaunchEnv` is rejected at launch. Add the names to your own `cmd` instead. This opts the agent out of groundcrew's default `safehouse-clearance` wrap, so re-supply `--append-profile` / `--env` yourself if you need it:
|
|
73
73
|
|
|
74
74
|
```ts
|
|
75
75
|
claude: {
|
package/docs/runners.md
CHANGED
|
@@ -49,7 +49,7 @@ Groundcrew generates a per-launch policy itself (Safehouse's `.sb` profiles have
|
|
|
49
49
|
|
|
50
50
|
- **Reads**: the home region (`/Users` on macOS, `/home`+`/root`+`/mnt` on Linux — `/mnt` covers WSL's Windows drive mounts) is denied, then the worktree, the repo's git metadata, the language toolchains needed to run the agent, and the agent's own config dirs (`~/.claude`, `~/.codex`, …) are re-opened. On macOS the user keychain dir (`~/Library/Keychains`) is also re-opened read-only so keychain-authenticated agents (claude) can sign in under the home mask. The agent cannot read `~/.ssh`, `~/.aws`, shell history, or unrelated repos.
|
|
51
51
|
- **Writes**: allow-only, and the host-CLI persistence vector (planting hooks, `mcpServers`, `commands/`, `plugins/`, … that run on the user's next host invocation) is closed per agent. **claude** keeps a writable `~/.claude` (its Bash tool needs scratch/session state there) but every fixed-path executable/instruction surface — `~/.claude.json` (`mcpServers`), `settings.json` and its hooks, `commands/`, `agents/`, `plugins/`, `skills/`, `statusline.sh`, `CLAUDE.md`, the bundled `chrome` binary, `.git/{hooks,config}` — is denied; claude tolerates those write denials. **codex** hard-fails with a read-only home, so it is pointed at a per-launch relocated config dir (`CODEX_HOME`) seeded with its credentials, leaving the real `~/.codex` entirely unwritten. The git common dir is granted as a **narrow allowlist** of only what `status/diff/add/commit/push/gc` write (`objects`, `refs`, `logs`, `packed-refs`, this worktree's gitdir, …) — never wholesale, so the repo `config`/`hooks`, the per-worktree gitdir redirection files, and **sibling worktree gitdirs** stay unwritable. Global toolchain bins (`~/.cargo/bin`, global `node_modules`, the npx cache, …) are never writable either.
|
|
52
|
-
- **Environment**: each `srt` invocation runs under a sanitized env (`env -i` + a benign baseline). Unlike safehouse and sdx, the `srt` CLI inherits the host env, so without this an ambient `AWS_*`, `GITHUB_TOKEN`, etc. would reach the agent and bypass the read mask. Credentials the agent legitimately needs from the environment must be forwarded explicitly via the
|
|
52
|
+
- **Environment**: each `srt` invocation runs under a sanitized env (`env -i` + a benign baseline). Unlike safehouse and sdx, the `srt` CLI inherits the host env, so without this an ambient `AWS_*`, `GITHUB_TOKEN`, etc. would reach the agent and bypass the read mask. Credentials the agent legitimately needs from the environment must be forwarded explicitly via the agent's `preLaunchEnv` (the same opt-in pass-list safehouse uses).
|
|
53
53
|
- **Network**: allow-only, **reused from the same Clearance allowlist** (`CLEARANCE_ALLOW_HOSTS` / `CLEARANCE_ALLOW_HOSTS_FILES`, including the shipped `clearance-allow-hosts`) so there is one source of truth. Local binding and unix sockets stay off (never the Docker socket).
|
|
54
54
|
|
|
55
55
|
### Linux / WSL prerequisites
|
|
@@ -76,7 +76,7 @@ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
|
|
76
76
|
|
|
77
77
|
## Docker Sandboxes Setup
|
|
78
78
|
|
|
79
|
-
Each
|
|
79
|
+
Each agent that runs under `sdx` needs a `sandbox: { agent: "<sbx-agent>" }` block in `crew.config.ts`. Groundcrew addresses the sandbox as `groundcrew-<agent>` and reuses one existing sandbox per agent across repos and tasks.
|
|
80
80
|
|
|
81
81
|
First-time setup is manual:
|
|
82
82
|
|
|
@@ -86,4 +86,4 @@ sbx exec -it groundcrew-claude claude auth login
|
|
|
86
86
|
sbx exec -it groundcrew-claude gh auth login
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Replace `claude` with the sbx agent for
|
|
89
|
+
Replace `claude` with the sbx agent name for your agent and `<projectDir>` with `workspace.projectDir` from `crew.config.ts`. Manage lifecycle and auth with `sbx` directly (`sbx ls`, `sbx exec`, `sbx rm`). Groundcrew does not create, authenticate, regenerate, list, or remove sandboxes.
|
package/docs/task-sources.md
CHANGED
|
@@ -35,9 +35,15 @@ tasks only). If omitted, groundcrew treats in-review advancement as unsupported
|
|
|
35
35
|
for that source and does not claim the transition succeeded. `commands.markDone`,
|
|
36
36
|
when set, receives the same `sourceRef` and is run after groundcrew sees a
|
|
37
37
|
**merged** PR on the task's worktree branch (a merged PR never advances to
|
|
38
|
-
in-review)
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
in-review), or when a human or worker runs `crew task done <task-id>` for
|
|
39
|
+
completed no-PR work. If omitted, groundcrew treats done advancement as
|
|
40
|
+
unsupported and leaves the task for the source's own integration to close out.
|
|
41
|
+
`${id}`, `${canonicalId}`, and `${name}` placeholders are shell-quoted before substitution.
|
|
42
|
+
|
|
43
|
+
Workers receive `GROUNDCREW_TASK_ID` and `GROUNDCREW_COMPLETE` in their launch
|
|
44
|
+
environment. The default prompt tells them to run `GROUNDCREW_COMPLETE` only
|
|
45
|
+
when the requested work is complete, no PR is needed, and any dirty worktree
|
|
46
|
+
state is expected or explicitly allowed.
|
|
41
47
|
|
|
42
48
|
```json
|
|
43
49
|
[
|
package/docs/troubleshooting.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
First stop for "what exists locally right now": `crew status <task>` shows the task's worktrees, workspace presence, run state, logs, and task-source status. Use `crew doctor` when you need to verify host setup.
|
|
4
4
|
|
|
5
|
-
## Missing
|
|
5
|
+
## Missing Agent CLI
|
|
6
6
|
|
|
7
|
-
`crew doctor` probes every
|
|
7
|
+
`crew doctor` probes every agent listed in `agents.definitions`. If you do not have `codex` installed, initialize with `crew init --agent claude` or leave `codex` out of the enabled agent set:
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
|
|
10
|
+
agents: {
|
|
11
11
|
default: "claude",
|
|
12
12
|
definitions: {
|
|
13
13
|
claude: {},
|
|
@@ -19,7 +19,7 @@ If `codex: {}` is listed, doctor expects the `codex` CLI to be installed because
|
|
|
19
19
|
|
|
20
20
|
## Safehouse-Wrapped Commands Are Not Re-Wrapped
|
|
21
21
|
|
|
22
|
-
If a `
|
|
22
|
+
If a `agents.definitions.<name>.cmd` already starts with `safehouse`, groundcrew assumes that command owns its Safehouse flags and does not add the `safehouse-clearance` wrapper a second time. Changing the proxy's allowlist after it is running requires killing the PID in `${XDG_CACHE_HOME:-$HOME/.cache}/clearance/clearance.pid` so the next launch picks up the new env.
|
|
23
23
|
|
|
24
24
|
## Dead Tmux Windows Vanish By Default
|
|
25
25
|
|
|
@@ -31,13 +31,15 @@ This applies to the tmux backend only.
|
|
|
31
31
|
|
|
32
32
|
Groundcrew marks a task `In Progress` when it provisions a workspace. When a PR opens on that worktree branch, the reviewer pass attempts to mark the task `In Review`. Linear's default `In Review` status works out of the box; if your team renamed it, configure `sources: [{ kind: "linear", statuses: { inReview: ["Code Review"] } }]`.
|
|
33
33
|
|
|
34
|
+
If the task intentionally has no PR, mark it complete with `crew task done <task-id>`. Groundcrew refuses dirty matching worktrees with no PR unless you pass `--allow-dirty`, so inspect or commit/stash unexpected changes first. For todo-txt tasks with `rec:`, this completion path also lets the source schedule the next recurrence.
|
|
35
|
+
|
|
34
36
|
## Claude Launches In Auto Mode By Default
|
|
35
37
|
|
|
36
|
-
Groundcrew creates isolated per-task worktrees for unattended runs, so the shipped `claude` command is `claude --permission-mode auto` to let Claude proceed without stopping for clarifying questions while keeping its built-in safety prompts intact. Override `
|
|
38
|
+
Groundcrew creates isolated per-task worktrees for unattended runs, so the shipped `claude` command is `claude --permission-mode auto` to let Claude proceed without stopping for clarifying questions while keeping its built-in safety prompts intact. Override `agents.definitions.claude.cmd` for `bypassPermissions` if you need to suppress tool-permission prompts entirely, or for a stricter mode.
|
|
37
39
|
|
|
38
40
|
## Doctor's Command Introspection Is Shallow
|
|
39
41
|
|
|
40
|
-
Doctor reports the resolved local runner and whether its prerequisites are met, then tokenizes
|
|
42
|
+
Doctor reports the resolved local runner and whether its prerequisites are met, then tokenizes agent `cmd` and checks the first two non-flag tokens against PATH. Boolean flags without values, env-var assignments (`FOO=1`), shell pipelines, and subshells are not parsed. When `local.runner` is `"none"`, doctor surfaces a single WARNING line.
|
|
41
43
|
|
|
42
44
|
## Switch To Tmux If Cmux Is Misbehaving
|
|
43
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.35.0",
|
|
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",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@clipboard-health/ai-rules": "2.27.0",
|
|
80
|
-
"@clipboard-health/oxlint-config": "1.10.
|
|
80
|
+
"@clipboard-health/oxlint-config": "1.10.21",
|
|
81
81
|
"@nx/js": "22.7.5",
|
|
82
82
|
"@tsconfig/node24": "24.0.4",
|
|
83
83
|
"@tsconfig/strictest": "2.0.8",
|