@clipboard-health/groundcrew 4.35.1 → 4.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,10 +69,15 @@ crew run --watch
69
69
 
70
70
  Linear works out of the box: assign tasks to yourself and add an `agent-*` label.
71
71
 
72
- - `agent-claude`, `agent-codex`, or `agent-<name>` routes to that agent.
72
+ - `agent-claude`, `agent-codex`, or `agent-<name>` routes to that enabled launch profile.
73
73
  - `agent-any` routes to the enabled agent with the most session headroom, after skipping agents over their session limit or weekly paced budget.
74
74
  - Tasks without an `agent-*` label are ignored by `crew run`; dispatch one manually with `crew start <TASK>`.
75
75
 
76
+ Agent names are launch profiles, so you can use them to pick model tiers. For example,
77
+ define `claude-fable` with `claude --model claude-fable-5 --permission-mode auto`
78
+ and `claude-opus` with `claude --model claude-opus-4-8 --permission-mode auto`,
79
+ then label tasks with `agent-claude-fable` or `agent-claude-opus`.
80
+
76
81
  Groundcrew scans `workspace.knownRepositories` to infer which repo a task belongs to.
77
82
 
78
83
  A task blocked by non-terminal blockers is skipped until those blockers are done.
@@ -52,13 +52,24 @@ export default {
52
52
  },
53
53
  agents: {
54
54
  default: "claude",
55
- // `definitions` is the enabled agent set. Built-in keys can use `{}` to
56
- // opt into the shipped command/color/usage preset. Add `codex: {}` if you
57
- // want both shipped agents, or add a custom entry and tag tasks with
58
- // `agent-<name>`.
55
+ // `definitions` is the enabled launch profile set. Built-in keys can use
56
+ // `{}` to opt into the shipped command/color/usage preset. Add
57
+ // `codex: {}` if you want both shipped agents. Agent names are launch
58
+ // profiles: add custom entries such as `claude-fable` or `claude-opus` to
59
+ // pin a model per task, then tag tasks with `agent-<name>`.
59
60
  definitions: {
60
61
  claude: {},
61
62
  // codex: {},
63
+ // "claude-fable": {
64
+ // cmd: "claude --model claude-fable-5 --permission-mode auto",
65
+ // color: "#C15F3C",
66
+ // usage: { codexbar: { provider: "claude" } },
67
+ // },
68
+ // "claude-opus": {
69
+ // cmd: "claude --model claude-opus-4-8 --permission-mode auto",
70
+ // color: "#8A4FFF",
71
+ // usage: { codexbar: { provider: "claude" } },
72
+ // },
62
73
  // cursor: {
63
74
  // cmd: "cursor-agent",
64
75
  // color: "#929292",
@@ -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;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"}
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,CA8Ef;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
@@ -119,7 +119,7 @@ export async function resumeWorkspace(config, options) {
119
119
  if (definition === undefined) {
120
120
  throw new Error(`Unknown agent: ${context.agent}`);
121
121
  }
122
- const { runner, sandboxName, ensureReady } = await prepareAgentLaunch({
122
+ const { runner, sandboxName, workspaceKind, ensureReady } = await prepareAgentLaunch({
123
123
  config,
124
124
  agent: context.agent,
125
125
  definition,
@@ -150,6 +150,7 @@ export async function resumeWorkspace(config, options) {
150
150
  workingDir: launchDir,
151
151
  secretsFile,
152
152
  sandboxName,
153
+ workspaceKind,
153
154
  workerEnvironment: workerEnvironmentForTask(context.completionTaskId),
154
155
  }));
155
156
  const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launchCommand);
@@ -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;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"}
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,CA+Hf;AAgJD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
@@ -32,7 +32,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
32
32
  if (!definition) {
33
33
  throw new Error(`Unknown agent: ${agent}`);
34
34
  }
35
- const { runner, sandboxName, ensureReady } = await prepareAgentLaunch({
35
+ const { runner, sandboxName, workspaceKind, ensureReady } = await prepareAgentLaunch({
36
36
  config,
37
37
  agent,
38
38
  definition,
@@ -92,6 +92,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
92
92
  secretsFile,
93
93
  prepareWorktreeCommand,
94
94
  sandboxName,
95
+ workspaceKind,
95
96
  workerEnvironment: workerEnvironmentForTask(completionTaskId),
96
97
  });
97
98
  srtSettingsDir = stagedSrtSettingsDir;
@@ -1,5 +1,6 @@
1
1
  import { type LocalRunner, type AgentDefinition, type ResolvedConfig } from "./config.ts";
2
2
  import { type WorkerEnvironment } from "./launchCommand.ts";
3
+ import type { WorkspaceKind } from "./workspaceAdapter.ts";
3
4
  /**
4
5
  * Stage any srt settings and build the workspace launch command — the assembly
5
6
  * shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
@@ -17,6 +18,7 @@ export declare function composeAgentLaunch(input: {
17
18
  secretsFile?: string | undefined;
18
19
  prepareWorktreeCommand?: string | undefined;
19
20
  sandboxName?: string | undefined;
21
+ workspaceKind: WorkspaceKind;
20
22
  workerEnvironment?: WorkerEnvironment | undefined;
21
23
  }): {
22
24
  launchCommand: string;
@@ -25,6 +27,7 @@ export declare function composeAgentLaunch(input: {
25
27
  interface PreparedAgentLaunch {
26
28
  runner: LocalRunner;
27
29
  sandboxName: string | undefined;
30
+ workspaceKind: WorkspaceKind;
28
31
  ensureReady: () => Promise<void>;
29
32
  }
30
33
  export declare function prepareAgentLaunch(input: {
@@ -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;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"}
1
+ {"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;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,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAgChE;AAmDD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,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,CAsD/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"}
@@ -1,13 +1,13 @@
1
- import { ensureClearance } from "@clipboard-health/clearance";
1
+ import { ensureClearance, resolveSafehouseCmuxIntegration, safehouseCmuxIntegrationWarningLines, } from "@clipboard-health/clearance";
2
2
  import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
3
3
  import { hasPreLaunchEnv, } from "./config.js";
4
4
  import { detectHostCapabilities } from "./host.js";
5
- import { buildLaunchCommand } from "./launchCommand.js";
5
+ import { buildLaunchCommand, inferAgentCommandName, } from "./launchCommand.js";
6
6
  import { assertLocalRunnerRequirements, resolveLocalRunner } from "./localRunner.js";
7
7
  import { sandboxNameFor } from "./sandboxName.js";
8
8
  import { buildAndStageSrtLaunch, resolveGitCommonDir } from "./srtLaunch.js";
9
- import { debug, sleep } from "./util.js";
10
- import { workspaces } from "./workspaces.js";
9
+ import { debug, sleep, writeError } from "./util.js";
10
+ import { resolveWorkspaceKind, workspaces } from "./workspaces.js";
11
11
  /**
12
12
  * Stage any srt settings and build the workspace launch command — the assembly
13
13
  * shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
@@ -23,6 +23,9 @@ export function composeAgentLaunch(input) {
23
23
  definition: input.definition,
24
24
  })
25
25
  : undefined;
26
+ const safehouseAgentIntegration = input.runner === "safehouse"
27
+ ? safehouseAgentIntegrationFor(input.workspaceKind, input.definition)
28
+ : undefined;
26
29
  const launchCommand = buildLaunchCommand({
27
30
  definition: input.definition,
28
31
  promptFile: input.promptFile,
@@ -38,6 +41,7 @@ export function composeAgentLaunch(input) {
38
41
  srtAgentConfigDirEnv: staged?.agentConfigDirEnv,
39
42
  workerEnvironment: input.workerEnvironment,
40
43
  safehouseAddDirs: input.runner === "safehouse" ? resolveSafehouseAddDirs(input.worktreeDir) : undefined,
44
+ safehouseAgentIntegration,
41
45
  });
42
46
  return { launchCommand, srtSettingsDir: staged?.directory };
43
47
  }
@@ -60,9 +64,33 @@ export function composeAgentLaunch(input) {
60
64
  function resolveSafehouseAddDirs(worktreeDir) {
61
65
  return [...new Set([worktreeDir, resolveGitCommonDir(worktreeDir)])];
62
66
  }
67
+ function safehouseAgentIntegrationFor(workspaceKind, definition) {
68
+ if (workspaceKind !== "cmux") {
69
+ return undefined;
70
+ }
71
+ const isClaudeAgent = inferAgentCommandName(definition.cmd) === "claude";
72
+ const cmuxIntegration = resolveSafehouseCmuxIntegration();
73
+ if (isClaudeAgent) {
74
+ warnOnCmuxIntegrationDrift({ unreviewedEnvNames: cmuxIntegration.unreviewedEnvNames });
75
+ }
76
+ return {
77
+ addDirsReadOnly: cmuxIntegration.addDirsReadOnly,
78
+ envPass: cmuxIntegration.envPass,
79
+ commandPreludes: isClaudeAgent ? [cmuxIntegration.claudeCommandPrelude] : [],
80
+ };
81
+ }
82
+ function warnOnCmuxIntegrationDrift(input) {
83
+ for (const warningLine of safehouseCmuxIntegrationWarningLines({
84
+ commandName: "groundcrew",
85
+ unreviewedEnvNames: input.unreviewedEnvNames,
86
+ })) {
87
+ writeError(warningLine);
88
+ }
89
+ }
63
90
  export async function prepareAgentLaunch(input) {
64
91
  const host = await detectHostCapabilities(input.signal);
65
92
  const runner = resolveLocalRunner(input.config.local.runner, host);
93
+ const workspaceKind = resolveWorkspaceKind({ config: input.config, host }).resolved;
66
94
  assertLocalRunnerRequirements(host, runner);
67
95
  const ensureReady = runner === "safehouse"
68
96
  ? async () => {
@@ -96,7 +124,7 @@ export async function prepareAgentLaunch(input) {
96
124
  const sandboxName = runner === "sdx" && input.definition.sandbox !== undefined
97
125
  ? sandboxNameFor({ agent: input.definition.sandbox.agent })
98
126
  : undefined;
99
- return { runner, sandboxName, ensureReady };
127
+ return { runner, sandboxName, workspaceKind, ensureReady };
100
128
  }
101
129
  async function alreadyReady() {
102
130
  await Promise.resolve();
@@ -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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"}
@@ -120,11 +120,19 @@ const ALLOWED_PROMPT_PLACEHOLDERS = new Set([
120
120
  const PROMPT_PLACEHOLDER_RE = /{{[^{}]*}}/g;
121
121
  const PERCENT_MIN_EXCLUSIVE = 0;
122
122
  const PERCENT_MAX = 100;
123
+ const CONFIG_ERROR_PREFIX = "groundcrew config: ";
123
124
  function defaultLogFile() {
124
125
  return xdgStatePath("groundcrew", "groundcrew.log");
125
126
  }
126
127
  function fail(message) {
127
- throw new Error(`groundcrew config: ${message}`);
128
+ throw new Error(`${CONFIG_ERROR_PREFIX}${message}`);
129
+ }
130
+ function rethrowWithConfigPath({ error, filepath }) {
131
+ if (error instanceof Error && error.message.startsWith(CONFIG_ERROR_PREFIX)) {
132
+ const message = error.message.slice(CONFIG_ERROR_PREFIX.length);
133
+ throw new Error(`${CONFIG_ERROR_PREFIX}${filepath}: ${message}`, { cause: error });
134
+ }
135
+ throw error;
128
136
  }
129
137
  function isNonEmptyString(value) {
130
138
  return typeof value === "string" && value.length > 0;
@@ -792,9 +800,15 @@ export async function loadConfigWithSource() {
792
800
  fail(`${filepath} must export a config object (e.g. \`export default { ... } satisfies Config\`)`);
793
801
  }
794
802
  debug(`Loaded config from ${filepath}`);
795
- // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- runtime fields are validated by applyDefaults/validate
796
- const resolved = applyDefaults(userConfig, path.dirname(filepath));
797
- validate(resolved);
803
+ let resolved;
804
+ try {
805
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- runtime fields are validated by applyDefaults/validate
806
+ resolved = applyDefaults(userConfig, path.dirname(filepath));
807
+ validate(resolved);
808
+ }
809
+ catch (error) {
810
+ rethrowWithConfigPath({ error, filepath });
811
+ }
798
812
  setLogFile(resolved.logging.file);
799
813
  cached = Object.freeze({
800
814
  config: Object.freeze(resolved),
@@ -41,6 +41,11 @@ declare const WORKER_ENVIRONMENT_NAMES: readonly ["GROUNDCREW_TASK_ID", "GROUNDC
41
41
  type WorkerEnvironmentName = (typeof WORKER_ENVIRONMENT_NAMES)[number];
42
42
  export type WorkerEnvironment = Readonly<Record<WorkerEnvironmentName, string>>;
43
43
  export declare function workerEnvironmentForTask(taskId: string): WorkerEnvironment;
44
+ export interface SafehouseAgentIntegration {
45
+ addDirsReadOnly: readonly string[];
46
+ envPass: readonly string[];
47
+ commandPreludes: readonly string[];
48
+ }
44
49
  interface LaunchCommandArguments {
45
50
  definition: AgentDefinition;
46
51
  promptFile: string;
@@ -115,6 +120,13 @@ interface LaunchCommandArguments {
115
120
  * pre-existing behavior). Only consumed by the safehouse wrap.
116
121
  */
117
122
  safehouseAddDirs?: readonly string[] | undefined;
123
+ /**
124
+ * Extra host-terminal integration surface granted only to the Safehouse agent
125
+ * wrap. The agent may need to execute host shims and reach their sockets
126
+ * while repo-controlled prepareWorktree hooks should not inherit those paths
127
+ * or env vars.
128
+ */
129
+ safehouseAgentIntegration?: SafehouseAgentIntegration | undefined;
118
130
  /**
119
131
  * Groundcrew-managed task metadata exposed to the launched worker. Forwarded
120
132
  * to the agent process, not the prepareWorktree hook.
@@ -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,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"}
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,MAAM,WAAW,yBAAyB;IACxC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;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;IACjD;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAkC7E"}
@@ -381,7 +381,12 @@ function buildUnwrappedHostLaunchCommand(arguments_) {
381
381
  function buildSafehouseLaunchCommand(arguments_) {
382
382
  const promptDir = path.dirname(arguments_.promptFile);
383
383
  const safehouseCommandName = inferAgentCommandName(arguments_.definition.cmd);
384
- const { agentCommand, prepareWorktreeCommand } = renderPrepareAndAgentCommand(arguments_);
384
+ const { agentCommand: rawAgentCommand, prepareWorktreeCommand } = renderPrepareAndAgentCommand(arguments_);
385
+ const { safehouseAgentIntegration } = arguments_;
386
+ const agentCommand = [
387
+ ...(safehouseAgentIntegration?.commandPreludes ?? []),
388
+ rawAgentCommand,
389
+ ].join("; ");
385
390
  // Split --env-pass per wrap: the prepareWorktree wrap only needs build secrets (so
386
391
  // `npm install` etc. can authenticate); the agent wrap only needs the
387
392
  // user's preLaunchEnv (build secrets are `unset` on the host between the
@@ -392,14 +397,15 @@ function buildSafehouseLaunchCommand(arguments_) {
392
397
  const agentEnvPassFlag = envPassFlag([
393
398
  ...(arguments_.definition.preLaunchEnv ?? []),
394
399
  ...workerEnvironmentNames(arguments_.workerEnvironment),
400
+ ...(safehouseAgentIntegration?.envPass ?? []),
395
401
  ]);
396
402
  // safehouse reads colon-separated paths from `--add-dirs`; both wraps get the
397
403
  // same grant so the prepareWorktree hook and the agent can each reach git.
398
404
  // Quote the whole value so shell-special chars survive; the trailing space
399
405
  // separates it from the next argv token. See `resolveSafehouseAddDirs` for
400
406
  // which paths these are and why.
401
- const addDirs = arguments_.safehouseAddDirs ?? [];
402
- const safehouseAddDirsFlag = addDirs.length === 0 ? "" : `--add-dirs=${shellSingleQuote(addDirs.join(":"))} `;
407
+ const safehouseAddDirsFlag = safehousePathListFlag("--add-dirs", arguments_.safehouseAddDirs ?? []);
408
+ const safehouseAgentAddDirsReadOnlyFlag = safehousePathListFlag("--add-dirs-ro", safehouseAgentIntegration?.addDirsReadOnly ?? []);
403
409
  const safehouseWrapper = safehouseClearanceWrapperCommand();
404
410
  // Defensive shim+promptDir trap: by the time we arm it, `rm -rf <promptDir>`
405
411
  // has already run (line below) so the promptDir wipe is a no-op on the happy
@@ -429,9 +435,12 @@ function buildSafehouseLaunchCommand(arguments_) {
429
435
  // Running the real launch chain as `sh -c` would make it see `sh`, so use
430
436
  // an agent-named symlink to /bin/sh. This preserves per-agent profile
431
437
  // selection without enabling every agent profile.
432
- `{ ${safehouseWrapper} ${safehouseAddDirsFlag}${agentEnvPassFlag}"$_safehouse_shim" -c ${shellSingleQuote(agentCommand)} sh "$_p"; _safehouse_status=$?; rm -rf "$_safehouse_shim_dir"; trap - EXIT; exit "$_safehouse_status"; }`);
438
+ `{ ${safehouseWrapper} ${safehouseAddDirsFlag}${safehouseAgentAddDirsReadOnlyFlag}${agentEnvPassFlag}"$_safehouse_shim" -c ${shellSingleQuote(agentCommand)} sh "$_p"; _safehouse_status=$?; rm -rf "$_safehouse_shim_dir"; trap - EXIT; exit "$_safehouse_status"; }`);
433
439
  return lines.join(" && ");
434
440
  }
441
+ function safehousePathListFlag(flagName, paths) {
442
+ return paths.length === 0 ? "" : `${flagName}=${shellSingleQuote(paths.join(":"))} `;
443
+ }
435
444
  /**
436
445
  * Benign baseline env the srt wraps run under (via `env -i`). This is an
437
446
  * allowlist on purpose: srt's CLI spawns its child with the *inherited* host
@@ -10,8 +10,8 @@ There is a single path from board state to dispatch: `Source[] → Board → Dis
10
10
 
11
11
  ## Consequences
12
12
 
13
- - The canonical seam is the `Issue` contract: a source emits `model` and `repository`, or the task is ignored (`isGroundcrewIssue` keys off exactly that). Consumers branch on the canonical `CanonicalStatus` enum, never on a source's native status names.
13
+ - The canonical seam is the `Issue` contract: a source emits `agent` and `repository`, or the task is ignored (`isGroundcrewIssue` keys off exactly that). Consumers branch on the canonical `CanonicalStatus` enum, never on a source's native status names.
14
14
  - **Linear-specific** concepts live in the adapter: `agent-*` label parsing, `agent-any` routing, sub-issue/parent detection, assigned-to-viewer + label selection policy.
15
- - **Canonical** concepts stay in eligibility so every source benefits: blocker classification (sources populate `blockers[]`) and exhausted-model gating (sources pick a `model`).
15
+ - **Canonical** concepts stay in eligibility so every source benefits: blocker classification (sources populate `blockers[]`) and exhausted-agent gating (sources pick an `agent`).
16
16
  - This was a pure internal refactor with no user-visible change — Linear keeps working identically — so it carried no migration cost and landed before the breaking v5 cuts.
17
17
  - Changing the Linear selection mechanism (assigned + labeled) is now an adapter-local change that does not touch the engine.
package/docs/commands.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ```bash
8
8
  crew task list
9
9
  crew task list --source todo --status todo --unblocked
10
- crew task list --agent codex --repo ClipboardHealth/api --json
10
+ crew task list --agent claude-fable --repo ClipboardHealth/api --json
11
11
  ```
12
12
 
13
13
  `crew task get <task-id>` prints one normalized task. Canonical IDs such as `todo:GC-20260608-001` route directly to the named source. Natural IDs can be resolved with `--source <name>` or, when unique, by searching all configured sources. If more than one source matches, the command fails and asks for a canonical ID or `--source`.
@@ -23,7 +23,7 @@ crew task get todo:GC-20260608-001 --prompt
23
23
  ```bash
24
24
  crew task create "Fix cancellation retry race" \
25
25
  --source todo \
26
- --agent codex \
26
+ --agent claude-fable \
27
27
  --repo ClipboardHealth/api \
28
28
  --project marketplace \
29
29
  --context backend \
@@ -35,7 +35,7 @@ Linear creation creates a Todo issue assigned to the current Linear API viewer,
35
35
  ```bash
36
36
  crew task create "Fix cancellation retry race" \
37
37
  --source linear \
38
- --agent codex \
38
+ --agent claude-fable \
39
39
  --team ENG \
40
40
  --repo ClipboardHealth/api \
41
41
  --description "Investigate retry handling."
@@ -70,7 +70,7 @@ done and schedules the next `status:todo` recurrence itself.
70
70
  ```bash
71
71
  crew task create "Run flaky triage sweep" \
72
72
  --source todo \
73
- --agent codex \
73
+ --agent claude-fable \
74
74
  --repo ClipboardHealth/groundcrew \
75
75
  --id flaky-triage-1 \
76
76
  --rec 2h \
@@ -92,13 +92,40 @@ 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 agent.
95
+ - `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled launch profile.
96
96
  - `agent-any` routes to the agent with the most session headroom, after skipping agents over their session limit or weekly paced budget.
97
97
  - Unknown `agent-<name>` falls back to `agents.default`.
98
98
  - A built-in `agent-<name>` label whose agent is not enabled falls back to `agents.default` with a warning.
99
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
+ Agent names are launch profiles, not just vendor names. To choose a model per
103
+ task, define model-specific profiles and route tasks to them:
104
+
105
+ ```ts
106
+ export default {
107
+ agents: {
108
+ default: "claude-fable",
109
+ definitions: {
110
+ "claude-fable": {
111
+ cmd: "claude --model claude-fable-5 --permission-mode auto",
112
+ color: "#C15F3C",
113
+ usage: { codexbar: { provider: "claude" } },
114
+ },
115
+ "claude-opus": {
116
+ cmd: "claude --model claude-opus-4-8 --permission-mode auto",
117
+ color: "#8A4FFF",
118
+ usage: { codexbar: { provider: "claude" } },
119
+ },
120
+ },
121
+ },
122
+ };
123
+ ```
124
+
125
+ Use the same profile name in every source: Linear label `agent-claude-fable`,
126
+ todo.txt token `agent:claude-fable`, shell JSON `"agent": "claude-fable"`,
127
+ or `crew task create ... --agent claude-fable`.
128
+
102
129
  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.
103
130
 
104
131
  If your Linear workflow uses different names, explicitly declare the built-in Linear source and override only the names you need:
@@ -167,9 +194,9 @@ export default {
167
194
 
168
195
  Rules:
169
196
 
170
- - `agents.definitions` is the enabled agent set; `crew doctor` only probes listed agents.
197
+ - `agents.definitions` is the enabled launch profile set; `crew doctor` only probes listed profiles.
171
198
  - Built-in entries can be `{}` or partial overrides such as `{ cmd: "..." }`.
172
- - Custom agent names must provide `cmd` and `color`.
199
+ - Custom launch profile names must provide `cmd` and `color`.
173
200
  - `agents.default` must point at an enabled agent.
174
201
  - Legacy agent entries like `codex: { disabled: true }` are rejected with migration guidance; remove unwanted entries instead.
175
202
 
@@ -244,9 +271,9 @@ and hook contract.
244
271
  | `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
245
272
  | `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
273
  | `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. |
274
+ | `agents.definitions` | **required** | Enabled launch profile set. Built-in keys (`claude`, `codex`) can use `{}` to opt into the shipped preset. Custom profile names must provide `cmd` and `color`; use custom profiles such as `claude-fable` and `claude-opus` to select model-specific commands per task. |
275
+ | `agents.definitions.<name>.cmd` | preset for built-ins | Shell command launched for the agent. Required for custom profiles. 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. |
276
+ | `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 profiles. |
250
277
  | `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
278
  | `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
279
  | `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. |
@@ -53,7 +53,7 @@ state is expected or explicitly allowed.
53
53
  "description": "Task body",
54
54
  "status": "todo",
55
55
  "repository": "your-org/your-repo",
56
- "model": "claude",
56
+ "agent": "claude-fable",
57
57
  "assignee": "Alice",
58
58
  "updatedAt": "2026-05-22T15:00:00Z",
59
59
  "blockers": [{ "id": "JIRA-122", "title": "Schema migration", "status": "done" }],
@@ -63,7 +63,7 @@ state is expected or explicitly allowed.
63
63
  ]
64
64
  ```
65
65
 
66
- Allowed `status` values are `todo`, `in-progress`, `in-review`, `done`, and `other`. Use `null` for `repository` or `model` when a task should not be groundcrew-eligible. `hasMoreBlockers` is optional and defaults to `false`; `sourceRef` is opaque data that groundcrew passes back to your writeback command.
66
+ Allowed `status` values are `todo`, `in-progress`, `in-review`, `done`, and `other`. Omit `repository` or `agent` when a task should not be groundcrew-eligible. `hasMoreBlockers` is optional and defaults to `false`; `sourceRef` is opaque data that groundcrew passes back to your writeback command.
67
67
 
68
68
  ## Todo.txt
69
69
 
@@ -89,7 +89,7 @@ Creating a todo task appends a line with `status:todo` as the final meaningful t
89
89
  ```bash
90
90
  crew task create "Fix cancellation retry race" \
91
91
  --source todo \
92
- --agent codex \
92
+ --agent claude-fable \
93
93
  --repo ClipboardHealth/api \
94
94
  --project marketplace \
95
95
  --context backend \
@@ -97,7 +97,7 @@ crew task create "Fix cancellation retry race" \
97
97
  ```
98
98
 
99
99
  ```txt
100
- Fix cancellation retry race +marketplace @backend id:GC-20260608-001 repo:ClipboardHealth/api agent:codex status:todo
100
+ Fix cancellation retry race +marketplace @backend id:GC-20260608-001 repo:ClipboardHealth/api agent:claude-fable status:todo
101
101
  ```
102
102
 
103
103
  For hand-written todo lines, a non-empty title is enough prompt text when `.tasks/<id>.md` is absent. Omit `agent:` to default to `agent:any`:
@@ -126,7 +126,7 @@ export default {
126
126
  ```bash
127
127
  crew task create "Fix cancellation retry race" \
128
128
  --source linear \
129
- --agent codex \
129
+ --agent claude-fable \
130
130
  --repo ClipboardHealth/api \
131
131
  --description "Investigate retry handling."
132
132
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.35.1",
3
+ "version": "4.36.1",
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",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "dependencies": {
71
71
  "@anthropic-ai/sandbox-runtime": "0.0.52",
72
- "@clipboard-health/clearance": "1.2.0",
72
+ "@clipboard-health/clearance": "1.3.2",
73
73
  "@linear/sdk": "86.0.0",
74
74
  "cosmiconfig": "9.0.1",
75
75
  "tslib": "2.8.1",
@@ -87,7 +87,7 @@
87
87
  "cspell": "10.0.1",
88
88
  "dependency-cruiser": "17.4.3",
89
89
  "husky": "9.1.7",
90
- "jscpd": "4.2.4",
90
+ "jscpd": "5.0.5",
91
91
  "knip": "6.15.0",
92
92
  "lint-staged": "17.0.7",
93
93
  "markdownlint-cli2": "0.22.1",