@clipboard-health/groundcrew 4.45.1 → 4.45.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/crew.config.example.ts +5 -0
- package/dist/cli.js +2 -2
- package/dist/commands/resumeWorkspace.d.ts +6 -0
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +36 -6
- package/dist/lib/config.d.ts +11 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +18 -1
- package/dist/lib/launchCommand.d.ts +9 -0
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +11 -0
- package/docs/commands.md +3 -1
- package/docs/configuration.md +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ crew status [<TASK>] # inspect current state o
|
|
|
105
105
|
crew run [--watch] # one-shot or --watch forever
|
|
106
106
|
crew start <TASK> # provision + launch one task now
|
|
107
107
|
crew stop <TASK> [--reason <text>] # stop workspace, keep worktree
|
|
108
|
-
crew resume <TASK>
|
|
108
|
+
crew resume [--new] <TASK> # reopen a paused task (--new: fresh conversation)
|
|
109
109
|
crew open <pr> | --branch <name> [--repo <owner/repo>] # iterate on an existing PR or branch
|
|
110
110
|
[--prompt <text> | --prompt-file <path>] [--task <id>] [--dry-run]
|
|
111
111
|
crew cleanup [--force] <TASK> # tear down every worktree for a task
|
package/crew.config.example.ts
CHANGED
|
@@ -153,6 +153,11 @@ export default {
|
|
|
153
153
|
// preLaunchEnv: ["SESSION_TOKEN"],
|
|
154
154
|
// // Required for this agent when `local.runner` resolves to `sdx`.
|
|
155
155
|
// sandbox: { agent: "claude" },
|
|
156
|
+
// // Args appended on `crew resume` so the agent reopens its previous
|
|
157
|
+
// // conversation in the worktree (`crew resume --new` starts fresh). The
|
|
158
|
+
// // built-in claude/codex presets default this ("--continue" / "resume
|
|
159
|
+
// // --last"); set it for custom agents or to override the preset default.
|
|
160
|
+
// resumeArgs: "--continue",
|
|
156
161
|
// },
|
|
157
162
|
//
|
|
158
163
|
// // Local isolation backend. Defaults to `"auto"` — macOS → safehouse,
|
package/dist/cli.js
CHANGED
|
@@ -166,8 +166,8 @@ const SUBCOMMANDS = {
|
|
|
166
166
|
},
|
|
167
167
|
},
|
|
168
168
|
resume: {
|
|
169
|
-
summary: "Reopen an existing task worktree
|
|
170
|
-
usage: "<task>",
|
|
169
|
+
summary: "Reopen an existing task worktree, resuming the agent's chat session",
|
|
170
|
+
usage: "[--new] <task>",
|
|
171
171
|
invoke: resumeWorkspaceCli,
|
|
172
172
|
},
|
|
173
173
|
open: {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type ResolvedConfig } from "../lib/config.ts";
|
|
2
2
|
export interface ResumeWorkspaceOptions {
|
|
3
3
|
task: string;
|
|
4
|
+
/**
|
|
5
|
+
* Force a fresh conversation: cold-start the agent (the historic behavior),
|
|
6
|
+
* ignoring the agent's `resumeArgs`. Defaults to false, which reopens the
|
|
7
|
+
* agent's previous conversation when `resumeArgs` is configured.
|
|
8
|
+
*/
|
|
9
|
+
fresh?: boolean;
|
|
4
10
|
}
|
|
5
11
|
export declare function resumeWorkspace(config: ResolvedConfig, options: ResumeWorkspaceOptions): Promise<void>;
|
|
6
12
|
export declare function resumeWorkspaceCli(argv: string[]): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAiBzF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAyKD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAkGf;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
|
|
@@ -3,7 +3,7 @@ import { getLinearClient } from "../lib/adapters/linear/client.js";
|
|
|
3
3
|
import { isLinearEnabled, sourcesFromConfig } 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
|
+
import { withResumeArgs, workerEnvironmentForTask } from "../lib/launchCommand.js";
|
|
7
7
|
import { readRunState, recordRunState } from "../lib/runState.js";
|
|
8
8
|
import { taskSupportsCompletionCommand } from "../lib/sourceCapabilities.js";
|
|
9
9
|
import { removeStagedPrompt, stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
@@ -13,11 +13,20 @@ import { errorMessage, log } from "../lib/util.js";
|
|
|
13
13
|
import { failIfWorkspaceAlreadyLive } from "../lib/workspaceLiveness.js";
|
|
14
14
|
import { resolveLaunchDir, worktrees } from "../lib/worktrees.js";
|
|
15
15
|
function parseArguments(argv) {
|
|
16
|
-
|
|
16
|
+
let fresh = false;
|
|
17
|
+
const positionals = [];
|
|
18
|
+
for (const argument of argv) {
|
|
19
|
+
if (argument === "--new") {
|
|
20
|
+
fresh = true;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
positionals.push(argument);
|
|
24
|
+
}
|
|
25
|
+
const [task, ...extras] = positionals;
|
|
17
26
|
if (task === undefined || task.length === 0 || extras.length > 0 || task.startsWith("-")) {
|
|
18
|
-
throw new Error("Usage: crew resume <task>");
|
|
27
|
+
throw new Error("Usage: crew resume [--new] <task>");
|
|
19
28
|
}
|
|
20
|
-
return { task: naturalIdFromCanonical(task).toLowerCase() };
|
|
29
|
+
return { task: naturalIdFromCanonical(task).toLowerCase(), fresh };
|
|
21
30
|
}
|
|
22
31
|
async function fetchTaskDetails(task) {
|
|
23
32
|
try {
|
|
@@ -116,6 +125,22 @@ function renderResumePrompt(context) {
|
|
|
116
125
|
"Run the repository's documented verification before stopping, then leave the branch ready or open a PR when possible.",
|
|
117
126
|
].join("\n");
|
|
118
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Decide the definition to launch on resume: when the agent has `resumeArgs`
|
|
130
|
+
* and `--new` was not passed, append them so the agent reopens its previous
|
|
131
|
+
* conversation; otherwise cold-start (the historic behavior).
|
|
132
|
+
*/
|
|
133
|
+
function resolveResumeLaunch(input) {
|
|
134
|
+
const { task, definition, fresh } = input;
|
|
135
|
+
if (fresh || definition.resumeArgs === undefined) {
|
|
136
|
+
if (fresh && definition.resumeArgs !== undefined) {
|
|
137
|
+
log(`Starting a fresh conversation for ${task}`);
|
|
138
|
+
}
|
|
139
|
+
return definition;
|
|
140
|
+
}
|
|
141
|
+
log(`Reopening the previous conversation for ${task}`);
|
|
142
|
+
return withResumeArgs(definition, definition.resumeArgs);
|
|
143
|
+
}
|
|
119
144
|
export async function resumeWorkspace(config, options) {
|
|
120
145
|
const task = options.task.toLowerCase();
|
|
121
146
|
await failIfWorkspaceAlreadyLive(config, task, "resuming");
|
|
@@ -124,10 +149,15 @@ export async function resumeWorkspace(config, options) {
|
|
|
124
149
|
if (definition === undefined) {
|
|
125
150
|
throw new Error(`Unknown agent: ${context.agent}`);
|
|
126
151
|
}
|
|
152
|
+
const launchDefinition = resolveResumeLaunch({
|
|
153
|
+
task,
|
|
154
|
+
definition,
|
|
155
|
+
fresh: options.fresh === true,
|
|
156
|
+
});
|
|
127
157
|
const { runner, networkEgress, sandboxName, workspaceKind, ensureReady } = await prepareAgentLaunch({
|
|
128
158
|
config,
|
|
129
159
|
agent: context.agent,
|
|
130
|
-
definition,
|
|
160
|
+
definition: launchDefinition,
|
|
131
161
|
purpose: "resumes",
|
|
132
162
|
});
|
|
133
163
|
await ensureReady();
|
|
@@ -157,7 +187,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
157
187
|
runner,
|
|
158
188
|
networkEgress,
|
|
159
189
|
task,
|
|
160
|
-
definition,
|
|
190
|
+
definition: launchDefinition,
|
|
161
191
|
promptFile: stagedPrompt.file,
|
|
162
192
|
worktreeDir,
|
|
163
193
|
workingDir: launchDir,
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -107,6 +107,17 @@ export interface AgentDefinition {
|
|
|
107
107
|
* or `none` without surprise.
|
|
108
108
|
*/
|
|
109
109
|
sandbox?: SandboxDefinition;
|
|
110
|
+
/**
|
|
111
|
+
* Opt-in: shell args appended to `cmd` on `crew resume` so the agent reopens
|
|
112
|
+
* its previous conversation in the worktree instead of cold-starting. groundcrew
|
|
113
|
+
* stores no session id — it relies on one conversation per worktree, so the
|
|
114
|
+
* agent's own "resume latest in this directory" primitive is enough.
|
|
115
|
+
*
|
|
116
|
+
* Examples: `"--continue"` (Claude Code), `"resume --last"` (Codex).
|
|
117
|
+
*
|
|
118
|
+
* `crew resume --new` ignores this and forces a fresh conversation.
|
|
119
|
+
*/
|
|
120
|
+
resumeArgs?: string;
|
|
110
121
|
}
|
|
111
122
|
/**
|
|
112
123
|
* User-facing agent entry shape. Built-in agent names (`claude`, `codex`)
|
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":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAO1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;AAE3F,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,QAAQ,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAKzD,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE/D;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,MAAM,CAAC;AAEtD,eAAO,MAAM,qBAAqB,EAAE,SAAS,kBAAkB,EAMrD,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG,MAAM,CAAC;AAE1D,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAGzD,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,QAAQ,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACjD,CAAC;IACF;;;;OAIG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;
|
|
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
|
@@ -61,11 +61,14 @@ const BUILT_IN_AGENT_DEFINITIONS = {
|
|
|
61
61
|
cmd: "claude --permission-mode auto",
|
|
62
62
|
color: "#C15F3C",
|
|
63
63
|
usage: { codexbar: { provider: "claude" } },
|
|
64
|
+
// `crew resume` reopens the worktree's latest conversation by default.
|
|
65
|
+
resumeArgs: "--continue",
|
|
64
66
|
},
|
|
65
67
|
codex: {
|
|
66
68
|
cmd: "codex --dangerously-bypass-approvals-and-sandbox",
|
|
67
69
|
color: "#3267e3",
|
|
68
70
|
usage: { codexbar: { provider: "codex" } },
|
|
71
|
+
resumeArgs: "resume --last",
|
|
69
72
|
},
|
|
70
73
|
};
|
|
71
74
|
const MODEL_DEFINITIONS_MIGRATION_MESSAGE = [
|
|
@@ -280,6 +283,14 @@ function normalizeNetworkEgress(value, configKey) {
|
|
|
280
283
|
}
|
|
281
284
|
return value;
|
|
282
285
|
}
|
|
286
|
+
function normalizeResumeArgs(value, configKey) {
|
|
287
|
+
requireString(value, configKey);
|
|
288
|
+
const trimmed = value.trim();
|
|
289
|
+
if (trimmed.length === 0) {
|
|
290
|
+
fail(`${configKey} must be a non-empty string (got ${JSON.stringify(value)})`);
|
|
291
|
+
}
|
|
292
|
+
return trimmed;
|
|
293
|
+
}
|
|
283
294
|
function normalizeSandbox(value, configKey) {
|
|
284
295
|
if (!isPlainObject(value)) {
|
|
285
296
|
fail(`${configKey} must be an object`);
|
|
@@ -350,6 +361,9 @@ function buildOverrideCandidate(name, override, existing) {
|
|
|
350
361
|
if (override.sandbox !== undefined) {
|
|
351
362
|
candidate.sandbox = normalizeSandbox(override.sandbox, `agents.definitions.${name}.sandbox`);
|
|
352
363
|
}
|
|
364
|
+
if (override.resumeArgs !== undefined) {
|
|
365
|
+
candidate.resumeArgs = normalizeResumeArgs(override.resumeArgs, `agents.definitions.${name}.resumeArgs`);
|
|
366
|
+
}
|
|
353
367
|
if (override.preLaunch !== undefined) {
|
|
354
368
|
candidate.preLaunch = override.preLaunch;
|
|
355
369
|
}
|
|
@@ -370,7 +384,7 @@ function mergeDefinitions(user) {
|
|
|
370
384
|
failIfLegacyModelKeys(name, override);
|
|
371
385
|
const builtIn = BUILT_IN_AGENT_DEFINITIONS[name];
|
|
372
386
|
const candidate = buildOverrideCandidate(name, override, builtIn);
|
|
373
|
-
const { cmd, color, usage, sandbox, preLaunch, preLaunchEnv } = candidate;
|
|
387
|
+
const { cmd, color, usage, sandbox, resumeArgs, preLaunch, preLaunchEnv } = candidate;
|
|
374
388
|
if (typeof cmd !== "string" || cmd.length === 0) {
|
|
375
389
|
fail(`agents.definitions.${name}.cmd must be a non-empty string`);
|
|
376
390
|
}
|
|
@@ -384,6 +398,9 @@ function mergeDefinitions(user) {
|
|
|
384
398
|
if (sandbox !== undefined) {
|
|
385
399
|
definition.sandbox = sandbox;
|
|
386
400
|
}
|
|
401
|
+
if (resumeArgs !== undefined) {
|
|
402
|
+
definition.resumeArgs = resumeArgs;
|
|
403
|
+
}
|
|
387
404
|
if (preLaunch !== undefined) {
|
|
388
405
|
definition.preLaunch = preLaunch;
|
|
389
406
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { type LocalRunner, type AgentDefinition, type NetworkEgressSetting } from "./config.ts";
|
|
2
2
|
export { shellSingleQuote } from "./shell.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Return a copy of `definition` whose `cmd` has the agent's `resumeArgs`
|
|
5
|
+
* appended, so `crew resume` relaunches the agent into its previous
|
|
6
|
+
* conversation (e.g. `--continue` for Claude, `resume --last` for Codex).
|
|
7
|
+
* Appending after the base `cmd` keeps the first token — which
|
|
8
|
+
* `inferAgentCommandName` reads for the safehouse profile — and the trailing
|
|
9
|
+
* `"$_p"` prompt positional the launch builders add both intact.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withResumeArgs(definition: AgentDefinition, resumeArgs: string): AgentDefinition;
|
|
3
12
|
/**
|
|
4
13
|
* Resolve the shipped Safehouse proxy wrapper inside `@clipboard-health/clearance`
|
|
5
14
|
* via Node's module-resolution algorithm so the path works whether npm hoists
|
|
@@ -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,EACpB,KAAK,oBAAoB,EAC1B,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;AAkND,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAMD,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,CAMpB;AAwCD,MAAM,WAAW,yBAAyB;IACxC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;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;;;;OAIG;IACH,aAAa,EAAE,oBAAoB,CAAC;IACpC;;;;;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,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACtD;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAClD;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAkC7E"}
|
|
1
|
+
{"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,17 @@ import { BUILD_SECRET_NAMES, hasPreLaunchEnv, } from "./config.js";
|
|
|
5
5
|
import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
|
|
6
6
|
import { shellSingleQuote } from "./shell.js";
|
|
7
7
|
export { shellSingleQuote } from "./shell.js";
|
|
8
|
+
/**
|
|
9
|
+
* Return a copy of `definition` whose `cmd` has the agent's `resumeArgs`
|
|
10
|
+
* appended, so `crew resume` relaunches the agent into its previous
|
|
11
|
+
* conversation (e.g. `--continue` for Claude, `resume --last` for Codex).
|
|
12
|
+
* Appending after the base `cmd` keeps the first token — which
|
|
13
|
+
* `inferAgentCommandName` reads for the safehouse profile — and the trailing
|
|
14
|
+
* `"$_p"` prompt positional the launch builders add both intact.
|
|
15
|
+
*/
|
|
16
|
+
export function withResumeArgs(definition, resumeArgs) {
|
|
17
|
+
return { ...definition, cmd: `${definition.cmd} ${resumeArgs}` };
|
|
18
|
+
}
|
|
8
19
|
/**
|
|
9
20
|
* Resolve the shipped Safehouse proxy wrapper inside `@clipboard-health/clearance`
|
|
10
21
|
* via Node's module-resolution algorithm so the path works whether npm hoists
|
package/docs/commands.md
CHANGED
|
@@ -143,10 +143,12 @@ The command closes the cmux/tmux/zellij workspace if present, records local run
|
|
|
143
143
|
|
|
144
144
|
## Resume
|
|
145
145
|
|
|
146
|
-
`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>`.
|
|
146
|
+
`crew resume [--new] <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>`.
|
|
147
147
|
|
|
148
148
|
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.
|
|
149
149
|
|
|
150
|
+
`crew resume <TASK>` reopens the agent's previous conversation in the worktree by default — the built-in `claude` and `codex` presets ship a [`resumeArgs`](./configuration.md#resuming-the-agents-conversation) default (`--continue`, `resume --last`) that groundcrew appends to the agent's command. `crew resume --new <TASK>` ignores `resumeArgs` and forces a fresh conversation. Custom agents cold-start unless they set `resumeArgs`. groundcrew stores no session id — it relies on one conversation per worktree.
|
|
151
|
+
|
|
150
152
|
## Open
|
|
151
153
|
|
|
152
154
|
`crew open` provisions a worktree for an existing pull request or branch — work that groundcrew did not create — and launches a session in it. Use it to iterate on a PR opened by hand, by a teammate, or by another tool.
|
package/docs/configuration.md
CHANGED
|
@@ -201,6 +201,27 @@ Rules:
|
|
|
201
201
|
- `agents.default` must point at an enabled agent.
|
|
202
202
|
- Legacy agent entries like `codex: { disabled: true }` are rejected with migration guidance; remove unwanted entries instead.
|
|
203
203
|
|
|
204
|
+
## Resuming the agent's conversation
|
|
205
|
+
|
|
206
|
+
`crew resume` reopens the agent's previous conversation in the worktree — **no config required** for the built-in agents. The shipped `claude` and `codex` presets carry a `resumeArgs` default (`--continue` and `resume --last`), which groundcrew appends to the agent's command on resume.
|
|
207
|
+
|
|
208
|
+
How it works:
|
|
209
|
+
|
|
210
|
+
- The first launch is unchanged — the agent creates its own conversation in the worktree.
|
|
211
|
+
- `crew resume <TASK>` appends `resumeArgs` to the agent's `cmd`, so the agent reopens that conversation (`claude --continue`, `codex … resume --last`). groundcrew stores no session id — each task has its own worktree, so the agent's "resume the latest conversation in this directory" primitive is enough.
|
|
212
|
+
- `crew resume --new <TASK>` ignores `resumeArgs` and cold-starts a fresh conversation.
|
|
213
|
+
- `resumeArgs` is appended after `cmd`, so the agent binary (and its safehouse profile) and the trailing prompt argument are unaffected. It assumes one conversation per worktree — if you manually open extra sessions in that directory, "latest" may not be the one you expect.
|
|
214
|
+
|
|
215
|
+
Set `resumeArgs` yourself to enable this for a **custom** agent, or to override/replace the preset default:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
agents: {
|
|
219
|
+
definitions: {
|
|
220
|
+
"claude-opus": { cmd: "claude --model claude-opus-4-8 --permission-mode auto", color: "#8A4FFF", resumeArgs: "--continue" },
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
```
|
|
224
|
+
|
|
204
225
|
## Prompt Customization
|
|
205
226
|
|
|
206
227
|
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.
|
|
@@ -312,6 +333,7 @@ and hook contract.
|
|
|
312
333
|
| `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. |
|
|
313
334
|
| `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. |
|
|
314
335
|
| `agents.definitions.<name>.preLaunchEnv` | optional | Companion to `preLaunch`: list of env var names to append to groundcrew's Safehouse `--env-pass=` flag, so `preLaunch` exports reach the agent without overriding `cmd`. 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`. |
|
|
336
|
+
| `agents.definitions.<name>.resumeArgs` | preset default | Shell args appended to `cmd` on `crew resume` so the agent [reopens its previous conversation](#resuming-the-agents-conversation) in the worktree. Defaults to `"--continue"` (claude) and `"resume --last"` (codex) on the built-in presets; set it for custom agents or to override. `crew resume --new` ignores it and cold-starts. No session id is stored. |
|
|
315
337
|
| `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`. |
|
|
316
338
|
| `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`. |
|
|
317
339
|
| `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/package.json
CHANGED