@clipboard-health/groundcrew 4.7.3 → 4.8.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 CHANGED
@@ -27,7 +27,7 @@ Groundcrew watches assigned tickets, creates isolated worktrees, launches agent
27
27
  - **One worktree per ticket.** Agents work in parallel without stepping on each other.
28
28
  - **Pluggable ticket sources.** Linear by default; Jira and local files via [ticket sources](./docs/ticket-sources.md).
29
29
  - **Local-first isolation.** Safehouse, Docker Sandboxes, or an explicit `none` escape hatch.
30
- - **Multi-agent routing.** Ships with `claude` and `codex`; bring your own CLI in config.
30
+ - **Multi-agent routing.** Ships `claude` and `codex` presets; bring your own CLI in config.
31
31
 
32
32
  ## Prerequisites
33
33
 
@@ -59,7 +59,7 @@ crew doctor
59
59
  crew run --watch
60
60
  ```
61
61
 
62
- `crew init --global` writes config to `${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/`. Pass `--repo` more than once for multiple repos. If you only have one CLI installed, pass `--model claude` (or `--model codex`) so Groundcrew disables the other model and `doctor` won't flag it as missing.
62
+ `crew init --global` writes config to `${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/`. Pass `--repo` more than once for multiple repos. `--model claude` or `--model codex` chooses the single built-in model preset to enable in the generated config.
63
63
 
64
64
  ## Ticket Pickup
65
65
 
@@ -96,7 +96,7 @@ See [command details](./docs/commands.md) for status output, doctor behavior, an
96
96
 
97
97
  ## Configuration
98
98
 
99
- Two keys are required; everything else has a default.
99
+ Workspace settings and at least one enabled model are required; everything else has a default.
100
100
 
101
101
  ```ts
102
102
  import type { Config } from "@clipboard-health/groundcrew";
@@ -112,7 +112,7 @@ export default {
112
112
  models: {
113
113
  default: "claude",
114
114
  definitions: {
115
- codex: { disabled: true },
115
+ claude: {},
116
116
  },
117
117
  },
118
118
  } satisfies Config;
@@ -20,6 +20,21 @@ export default {
20
20
  // tickets to these and refuses unknown repos by default.
21
21
  knownRepositories: ["your-org/your-repo"],
22
22
  },
23
+ models: {
24
+ default: "claude",
25
+ // `definitions` is the enabled model set. Built-in keys can use `{}` to
26
+ // opt into the shipped command/color/usage preset. Add `codex: {}` if you
27
+ // want both shipped agents, or add a custom entry and tag tickets with
28
+ // `agent-<name>`.
29
+ definitions: {
30
+ claude: {},
31
+ // codex: {},
32
+ // cursor: {
33
+ // cmd: "cursor-agent",
34
+ // color: "#929292",
35
+ // },
36
+ },
37
+ },
23
38
  // Everything below is optional — defaults shown for reference. Uncomment
24
39
  // and edit to override.
25
40
  //
@@ -50,31 +65,14 @@ export default {
50
65
  // sessionLimitPercentage: 85,
51
66
  // },
52
67
  //
53
- // models: {
54
- // default: "claude",
55
- // // Additive: defaults for `claude` and `codex` are merged in unless you
56
- // // re-declare those keys here. Add a third agent (e.g. `cursor`) by
57
- // // dropping it in this map and tagging tickets with `agent-cursor`.
58
- // // Groundcrew runs agent commands through Safehouse/clearance unless already Safehouse-wrapped.
59
- // definitions: {
60
- // cursor: {
61
- // cmd: "cursor-agent",
62
- // color: "#929292",
63
- // },
64
- // // Optional: mint a short-lived credential outside Safehouse and
65
- // // forward it into the agent. `preLaunch` runs in the launch shell
66
- // // before the agent exec; `preLaunchEnv` lists the names to add to
67
- // // groundcrew's `safehouse-clearance --env-pass=` flag so the wrap's
68
- // // egress allowlist stays intact. Chain with `&&` so a failed mint
69
- // // aborts launch before `export`.
70
- // // claude: {
71
- // // preLaunch: "SESSION_TOKEN=$(your-mint-command) && export SESSION_TOKEN",
72
- // // preLaunchEnv: ["SESSION_TOKEN"],
73
- // // },
74
- // // To run a model under the sdx (Docker Sandboxes) runner, bind it to
75
- // // an sbx agent. Required when `local.runner` resolves to `sdx`.
76
- // // claude: { sandbox: { agent: "claude" } },
77
- // },
68
+ // To customize an enabled built-in, replace `claude: {}` above with:
69
+ // claude: {
70
+ // // Optional: mint a short-lived credential outside Safehouse and forward
71
+ // // it into the agent. Chain with `&&` so a failed mint aborts launch.
72
+ // preLaunch: "SESSION_TOKEN=$(your-mint-command) && export SESSION_TOKEN",
73
+ // preLaunchEnv: ["SESSION_TOKEN"],
74
+ // // Required for this model when `local.runner` resolves to `sdx`.
75
+ // sandbox: { agent: "claude" },
78
76
  // },
79
77
  //
80
78
  // // Local isolation backend. Defaults to `"auto"` — macOS → safehouse,
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyKH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CA8E/C"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuKH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CA8E/C"}
@@ -14,7 +14,7 @@ import { resolveWorkspaceKind } from "../lib/workspaces.js";
14
14
  // Tokenization stops after this many non-flag tokens. Two is enough to
15
15
  // catch wrapper + wrapped CLI commands like `safehouse claude --foo`.
16
16
  const MAX_TOKENS_PER_CMD = 2;
17
- const SHIPPED_DEFAULT_MODEL_NAMES = ["claude", "codex"];
17
+ const BUILT_IN_MODEL_NAMES = ["claude", "codex"];
18
18
  async function checkCmd(cmd, required, hint) {
19
19
  const path = await which(cmd);
20
20
  const resolvedHint = path ?? hint;
@@ -120,12 +120,12 @@ function modelCliHint(modelName, token) {
120
120
  if (token !== modelName) {
121
121
  return undefined;
122
122
  }
123
- if (!isShippedDefaultModelName(modelName)) {
123
+ if (!isBuiltInModelName(modelName)) {
124
124
  return undefined;
125
125
  }
126
- return `install ${token} or disable it in crew.config.ts: \`models.definitions.${modelName} = { disabled: true }\``;
126
+ return `install ${token} or remove \`models.definitions.${modelName}\` from crew.config.ts`;
127
127
  }
128
- function isShippedDefaultModelName(value) {
128
+ function isBuiltInModelName(value) {
129
129
  return value === "claude" || value === "codex";
130
130
  }
131
131
  function format(check) {
@@ -23,7 +23,7 @@ interface InitConfigOptions {
23
23
  repositories?: string[];
24
24
  /** Pre-fill local.runner in the generated config. */
25
25
  runner?: LocalRunnerSetting;
26
- /** Keep one shipped default model enabled and disable the other. */
26
+ /** Choose the single built-in model preset enabled by the generated config. */
27
27
  model?: InitModel;
28
28
  /** Override the source template path. */
29
29
  examplePath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAUlF,QAAA,MAAM,WAAW,YAAI,QAAQ,EAAE,OAAO,CAAU,CAAC;AAEjD,KAAK,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC1C,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,UAAU,iBAAiB;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qDAAqD;IACrD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,iBAAiB,GAAG,qBAAqB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpE,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAoB5E;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAUlF,QAAA,MAAM,WAAW,YAAI,QAAQ,EAAE,OAAO,CAAU,CAAC;AAEjD,KAAK,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC1C,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,UAAU,iBAAiB;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qDAAqD;IACrD,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,iBAAiB,GAAG,qBAAqB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpE,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAoB5E;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjE"}
@@ -164,28 +164,39 @@ function renderConfig(source, options) {
164
164
  contents = replaceRequired(contents, ` // local: { runner: "auto" },`, ` local: { runner: ${tsString(options.runner)} },`, "--runner");
165
165
  }
166
166
  if (options.model !== undefined) {
167
- contents = replaceRequired(contents, " // prompts: {", `${modelBlock(options.model)}\n // prompts: {`, "--model");
167
+ contents = replaceRequired(contents, ` default: "claude",`, ` default: ${tsString(options.model)},`, "--model");
168
+ contents = replaceRequired(contents, " claude: {},", ` ${options.model}: {},`, "--model");
169
+ contents = removeDuplicateModelDefinitionLines(contents, options.model);
168
170
  }
169
171
  return contents;
170
172
  }
173
+ function removeDuplicateModelDefinitionLines(contents, model) {
174
+ const linePattern = new RegExp(`^\\s*(?://\\s*)?${escapeRegExp(model)}:\\s*\\{\\},\\s*$`);
175
+ let hasActiveEntry = false;
176
+ return contents
177
+ .split("\n")
178
+ .filter((line) => {
179
+ if (!linePattern.test(line)) {
180
+ return true;
181
+ }
182
+ const isCommented = line.trimStart().startsWith("//");
183
+ if (!isCommented && !hasActiveEntry) {
184
+ hasActiveEntry = true;
185
+ return true;
186
+ }
187
+ return false;
188
+ })
189
+ .join("\n");
190
+ }
191
+ function escapeRegExp(value) {
192
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
193
+ }
171
194
  function replaceRequired(contents, search, replacement, flag) {
172
195
  if (!contents.includes(search)) {
173
196
  throw new Error(`crew init ${flag}: template anchor not found in ${EXAMPLE_FILE_NAME}`);
174
197
  }
175
198
  return contents.replace(search, replacement);
176
199
  }
177
- function modelBlock(model) {
178
- const disabled = model === "claude" ? "codex" : "claude";
179
- return [
180
- " models: {",
181
- ` default: ${tsString(model)},`,
182
- " definitions: {",
183
- ` ${disabled}: { disabled: true },`,
184
- " },",
185
- " },",
186
- "",
187
- ].join("\n");
188
- }
189
200
  function writeInitGuidance(destination, options) {
190
201
  writeOutput("");
191
202
  writeOutput("Next steps:");
@@ -158,7 +158,7 @@ export declare function fetchResolvedIssue(arguments_: {
158
158
  config: ResolvedConfig;
159
159
  ticket: string;
160
160
  }): Promise<ResolvedIssue>;
161
- export declare function warnIfDisabledFallback(ticket: string, modelResolution: ModelResolution, config: ResolvedConfig): void;
161
+ export declare function warnIfNotEnabledFallback(ticket: string, modelResolution: ModelResolution, config: ResolvedConfig): void;
162
162
  export declare function blockersFromRelations(relations: IssueRelationNode[]): Blocker[];
163
163
  export {};
164
164
  //# sourceMappingURL=fetch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"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"}
@@ -138,7 +138,7 @@ export function modelForResolution(resolution) {
138
138
  if (resolution.kind === "matched") {
139
139
  return resolution.model;
140
140
  }
141
- if (resolution.kind === "disabled-fallback") {
141
+ if (resolution.kind === "not-enabled-fallback") {
142
142
  return resolution.fallbackModel;
143
143
  }
144
144
  return "any";
@@ -185,7 +185,7 @@ function buildLinearIssue(input) {
185
185
  }
186
186
  function issueFromNode(node, config) {
187
187
  const modelResolution = resolveModelFor({ labels: node.labels.nodes, config });
188
- warnIfDisabledFallback(node.identifier, modelResolution, config);
188
+ warnIfNotEnabledFallback(node.identifier, modelResolution, config);
189
189
  const { repository, model } = resolveAgentMetadata({
190
190
  ticket: node.identifier,
191
191
  /* v8 ignore next @preserve -- BoardIssues query selects description; the ?? guard normalises a null vs undefined edge */
@@ -358,12 +358,12 @@ export async function fetchResolvedIssue(arguments_) {
358
358
  });
359
359
  }
360
360
  const modelResolution = resolveModelFor({ labels: raw.labels, config });
361
- warnIfDisabledFallback(ticket, modelResolution, config);
361
+ warnIfNotEnabledFallback(ticket, modelResolution, config);
362
362
  let model = config.models.default;
363
363
  if (modelResolution.kind === "matched") {
364
364
  ({ model } = modelResolution);
365
365
  }
366
- else if (modelResolution.kind === "disabled-fallback") {
366
+ else if (modelResolution.kind === "not-enabled-fallback") {
367
367
  model = modelResolution.fallbackModel;
368
368
  }
369
369
  return {
@@ -379,11 +379,11 @@ export async function fetchResolvedIssue(arguments_) {
379
379
  url: raw.url,
380
380
  };
381
381
  }
382
- export function warnIfDisabledFallback(ticket, modelResolution, config) {
383
- if (modelResolution.kind !== "disabled-fallback") {
382
+ export function warnIfNotEnabledFallback(ticket, modelResolution, config) {
383
+ if (modelResolution.kind !== "not-enabled-fallback") {
384
384
  return;
385
385
  }
386
- log(`${ticket.toLowerCase()}: agent-${modelResolution.requestedModel} label refers to a disabled model; falling back to models.default (${config.models.default})`);
386
+ log(`${ticket.toLowerCase()}: agent-${modelResolution.requestedModel} label refers to a model that is not enabled; falling back to models.default (${config.models.default})`);
387
387
  }
388
388
  export function blockersFromRelations(relations) {
389
389
  return relations
@@ -18,7 +18,7 @@ export type ModelResolution = {
18
18
  } | {
19
19
  kind: "agent-any";
20
20
  } | {
21
- kind: "disabled-fallback";
21
+ kind: "not-enabled-fallback";
22
22
  requestedModel: string;
23
23
  fallbackModel: string;
24
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/parsing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjG,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5F,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AASjF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAUnE;AAiDD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE;IAC/C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,oBAAoB,CAuBvB;AAED,UAAU,wBAAwB;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,wBAAwB,GAAG,MAAM,CA2B5E;AAkDD,wBAAgB,eAAe,CAAC,UAAU,EAAE;IAC1C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,eAAe,CAiBlB"}
1
+ {"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/parsing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjG,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5F,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AASpF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAUnE;AAiDD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE;IAC/C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,oBAAoB,CAuBvB;AAED,UAAU,wBAAwB;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,wBAAwB,GAAG,MAAM,CA2B5E;AAkDD,wBAAgB,eAAe,CAAC,UAAU,EAAE;IAC1C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,eAAe,CAiBlB"}
@@ -2,7 +2,7 @@
2
2
  * Linear adapter — parsing helpers for model/repository resolution from
3
3
  * issue labels and descriptions. Extracted from boardSource.ts (Task 10).
4
4
  */
5
- import { AGENT_ANY_MODEL, isShippedDefaultDisabled } from "../../config.js";
5
+ import { AGENT_ANY_MODEL, isBuiltInModelNotEnabled } from "../../config.js";
6
6
  import { RepositoryResolutionError } from "../../ticketSource.js";
7
7
  export const AGENT_LABEL_PREFIX = "agent-";
8
8
  function escapeRegex(value) {
@@ -102,7 +102,7 @@ function parseAgentLabels(labels, config) {
102
102
  if (agentLabels.length === 0) {
103
103
  return undefined;
104
104
  }
105
- let disabledFallback;
105
+ let notEnabledFallback;
106
106
  for (const label of agentLabels) {
107
107
  const name = label.name.slice(AGENT_LABEL_PREFIX.length);
108
108
  if (name === AGENT_ANY_MODEL) {
@@ -114,13 +114,13 @@ function parseAgentLabels(labels, config) {
114
114
  if (Object.hasOwn(config.models.definitions, name)) {
115
115
  return { model: name };
116
116
  }
117
- if (disabledFallback === undefined && isShippedDefaultDisabled(config, name)) {
118
- disabledFallback = name;
117
+ if (notEnabledFallback === undefined && isBuiltInModelNotEnabled(config, name)) {
118
+ notEnabledFallback = name;
119
119
  }
120
120
  }
121
121
  const fallback = { model: config.models.default };
122
- if (disabledFallback !== undefined) {
123
- fallback.disabledFallback = disabledFallback;
122
+ if (notEnabledFallback !== undefined) {
123
+ fallback.notEnabledFallback = notEnabledFallback;
124
124
  }
125
125
  return fallback;
126
126
  }
@@ -133,10 +133,10 @@ export function resolveModelFor(arguments_) {
133
133
  if (parsed.model === AGENT_ANY_MODEL) {
134
134
  return { kind: "agent-any" };
135
135
  }
136
- if (parsed.disabledFallback !== undefined) {
136
+ if (parsed.notEnabledFallback !== undefined) {
137
137
  return {
138
- kind: "disabled-fallback",
139
- requestedModel: parsed.disabledFallback,
138
+ kind: "not-enabled-fallback",
139
+ requestedModel: parsed.notEnabledFallback,
140
140
  fallbackModel: parsed.model,
141
141
  };
142
142
  }
@@ -103,26 +103,21 @@ export interface ModelDefinition {
103
103
  sandbox?: SandboxDefinition;
104
104
  }
105
105
  /**
106
- * User-facing model entry shape. Discriminated union so the type system
107
- * mirrors the runtime contract: an entry is either a pure overlay
108
- * (every concrete field optional, no `disabled` key) or a pure
109
- * disable directive (`{ disabled: true }` and nothing else).
106
+ * User-facing model entry shape. Built-in model names (`claude`, `codex`)
107
+ * accept empty or partial entries because they merge over built-in presets.
108
+ * Brand-new model names must supply enough fields to satisfy `validate()`.
110
109
  *
111
110
  * `usage` accepts an extra `{ disabled: true }` sentinel that strips the
112
111
  * usage block from the merged definition — the only way to opt a shipped
113
- * default out of codexbar gating without disabling the model entirely.
112
+ * preset out of codexbar gating without removing the model entirely.
114
113
  */
115
114
  type UserUsage = ModelDefinition["usage"] | {
116
115
  disabled: true;
117
116
  };
118
117
  type EnabledUserModelDefinition = Partial<Omit<ModelDefinition, "usage">> & {
119
118
  usage?: UserUsage;
120
- disabled?: never;
121
119
  };
122
- interface DisabledUserModelDefinition {
123
- disabled: true;
124
- }
125
- type UserModelDefinition = EnabledUserModelDefinition | DisabledUserModelDefinition;
120
+ type UserModelDefinition = EnabledUserModelDefinition;
126
121
  /**
127
122
  * Loose user-facing shape — what a `config.ts` file declares.
128
123
  * Fields with defaults are optional; only `workspace.*` is required.
@@ -161,10 +156,10 @@ export interface Config {
161
156
  models?: {
162
157
  default?: string;
163
158
  /**
164
- * Additive: each entry merges over the shipped default for that key.
165
- * Override `claude.cmd` only by declaring `{ claude: { cmd: "..." } }`
166
- * the other fields stay at their default values. Brand-new model
167
- * names must supply enough fields to satisfy `validate()`.
159
+ * Explicit enabled model set. Built-in keys (`claude`, `codex`) merge over
160
+ * their presets, so `{ claude: {} }` enables Claude with the shipped
161
+ * command/color/usage. Brand-new model names must supply enough fields to
162
+ * satisfy `validate()`.
168
163
  */
169
164
  definitions?: Record<string, UserModelDefinition>;
170
165
  };
@@ -255,11 +250,10 @@ export interface ResolvedConfig {
255
250
  */
256
251
  export declare function hasPreLaunchEnv(definition: Pick<ModelDefinition, "preLaunchEnv">): boolean;
257
252
  /**
258
- * True when `name` is a shipped default the user removed via `disabled: true`.
259
- * Derived from absence in `definitions` that's the only path that removes a
260
- * shipped default, codified in `failIfLegacyModelKeys` + `mergeDefinitions`.
261
- * Consumers needing to distinguish disabled-by-user from unknown-label use this.
253
+ * True when `name` is a built-in preset but not present in the enabled
254
+ * definitions. Consumers use this to distinguish `agent-codex` when codex is
255
+ * not enabled from an arbitrary unknown label like `agent-typo`.
262
256
  */
263
- export declare function isShippedDefaultDisabled(config: Pick<ResolvedConfig, "models">, name: string): boolean;
257
+ export declare function isBuiltInModelNotEnabled(config: Pick<ResolvedConfig, "models">, name: string): boolean;
264
258
  export declare function loadConfig(): Promise<Readonly<ResolvedConfig>>;
265
259
  //# sourceMappingURL=config.d.ts.map
@@ -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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"}
@@ -35,7 +35,7 @@ const DEFAULT_ORCHESTRATOR = {
35
35
  pollIntervalMilliseconds: 120_000,
36
36
  sessionLimitPercentage: 85,
37
37
  };
38
- const DEFAULT_MODEL_DEFINITIONS = {
38
+ const BUILT_IN_MODEL_DEFINITIONS = {
39
39
  claude: {
40
40
  cmd: "claude --permission-mode auto",
41
41
  color: "#C15F3C",
@@ -47,6 +47,30 @@ const DEFAULT_MODEL_DEFINITIONS = {
47
47
  usage: { codexbar: { provider: "codex" } },
48
48
  },
49
49
  };
50
+ const MODEL_DEFINITIONS_MIGRATION_MESSAGE = [
51
+ "configuration migration required: models are no longer enabled by default.",
52
+ "",
53
+ "Add the models you want to use:",
54
+ "",
55
+ "models: {",
56
+ ' default: "claude",',
57
+ " definitions: {",
58
+ " claude: {},",
59
+ " },",
60
+ "},",
61
+ "",
62
+ "To keep the previous claude+codex behavior:",
63
+ "",
64
+ "models: {",
65
+ ' default: "claude",',
66
+ " definitions: {",
67
+ " claude: {},",
68
+ " codex: {},",
69
+ " },",
70
+ "},",
71
+ "",
72
+ "`disabled: true` is no longer supported; remove disabled model entries instead.",
73
+ ].join("\n");
50
74
  const DEFAULT_PROMPT_INITIAL = [
51
75
  "You are working on Linear ticket {{ticket}} ({{title}}) in the {{worktree}} worktree subdirectory.",
52
76
  "",
@@ -221,23 +245,16 @@ function failIfLegacyModelKeys(name, override) {
221
245
  fail(`models.definitions.${name}.isolation is no longer supported: per-model isolation is no longer supported`);
222
246
  }
223
247
  if (Object.hasOwn(override, "disabled")) {
224
- if (override["disabled"] !== true) {
225
- fail(`models.definitions.${name}.disabled must be exactly \`true\` when set (got ${JSON.stringify(override["disabled"])})`);
226
- }
227
- const conflicting = ["cmd", "color", "usage", "sandbox", "preLaunch", "preLaunchEnv"].filter((key) => Object.hasOwn(override, key));
228
- if (conflicting.length > 0) {
229
- fail(`models.definitions.${name}: cannot combine \`disabled: true\` with other fields (${conflicting.join(", ")}). Either disable the model or override its fields, not both.`);
230
- }
248
+ fail(MODEL_DEFINITIONS_MIGRATION_MESSAGE);
231
249
  }
232
250
  }
233
251
  /**
234
- * True when `name` is a shipped default the user removed via `disabled: true`.
235
- * Derived from absence in `definitions` that's the only path that removes a
236
- * shipped default, codified in `failIfLegacyModelKeys` + `mergeDefinitions`.
237
- * Consumers needing to distinguish disabled-by-user from unknown-label use this.
252
+ * True when `name` is a built-in preset but not present in the enabled
253
+ * definitions. Consumers use this to distinguish `agent-codex` when codex is
254
+ * not enabled from an arbitrary unknown label like `agent-typo`.
238
255
  */
239
- export function isShippedDefaultDisabled(config, name) {
240
- return (Object.hasOwn(DEFAULT_MODEL_DEFINITIONS, name) &&
256
+ export function isBuiltInModelNotEnabled(config, name) {
257
+ return (Object.hasOwn(BUILT_IN_MODEL_DEFINITIONS, name) &&
241
258
  !Object.hasOwn(config.models.definitions, name));
242
259
  }
243
260
  function isUsageDisableSentinel(usage) {
@@ -274,27 +291,17 @@ function buildOverrideCandidate(name, override, existing) {
274
291
  return candidate;
275
292
  }
276
293
  function mergeDefinitions(user) {
277
- if (user !== undefined && !isPlainObject(user)) {
294
+ if (user === undefined) {
295
+ fail(MODEL_DEFINITIONS_MIGRATION_MESSAGE);
296
+ }
297
+ if (!isPlainObject(user)) {
278
298
  fail("models.definitions must be an object");
279
299
  }
280
- const merged = Object.fromEntries(Object.entries(DEFAULT_MODEL_DEFINITIONS).map(([name, definition]) => [
281
- name,
282
- cloneModelDefinition(definition),
283
- ]));
284
- for (const [name, override] of Object.entries(user ?? {})) {
300
+ const merged = {};
301
+ for (const [name, override] of Object.entries(user)) {
285
302
  failIfLegacyModelKeys(name, override);
286
- if (override.disabled === true) {
287
- if (!Object.hasOwn(DEFAULT_MODEL_DEFINITIONS, name)) {
288
- fail(`models.definitions.${name}: \`disabled: true\` is only valid for shipped defaults (${Object.keys(DEFAULT_MODEL_DEFINITIONS).join(", ")}). Remove the entry instead.`);
289
- }
290
- // Drop the key so downstream iterators (doctor, eligibility, usage) ignore
291
- // the model automatically; `isShippedDefaultDisabled` lets the few consumers
292
- // that need to distinguish disabled from unknown re-derive the set.
293
- // oxlint-disable-next-line typescript/no-dynamic-delete -- `merged` is a fresh function-local clone of DEFAULT_MODEL_DEFINITIONS; no V8 dictionary-mode/pollution concerns
294
- delete merged[name];
295
- continue;
296
- }
297
- const candidate = buildOverrideCandidate(name, override, merged[name]);
303
+ const builtIn = BUILT_IN_MODEL_DEFINITIONS[name];
304
+ const candidate = buildOverrideCandidate(name, override, builtIn);
298
305
  const { cmd, color, usage, sandbox, preLaunch, preLaunchEnv } = candidate;
299
306
  if (typeof cmd !== "string" || cmd.length === 0) {
300
307
  fail(`models.definitions.${name}.cmd must be a non-empty string`);
@@ -456,7 +463,6 @@ function validate(config) {
456
463
  requirePositiveInt(config.orchestrator.pollIntervalMilliseconds, "orchestrator.pollIntervalMilliseconds");
457
464
  requirePercent(config.orchestrator.sessionLimitPercentage, "orchestrator.sessionLimitPercentage");
458
465
  const { definitions } = config.models;
459
- /* v8 ignore next 3 @preserve -- mergeDefinitions seeds claude+codex defaults, so an empty map is unreachable */
460
466
  if (Object.keys(definitions).length === 0) {
461
467
  fail("models.definitions must contain at least one model");
462
468
  }
@@ -468,12 +474,12 @@ function validate(config) {
468
474
  requireString(definition.color, `models.definitions.${name}.color`);
469
475
  if (definition.usage !== undefined) {
470
476
  const usagePath = `models.definitions.${name}.usage`;
471
- /* v8 ignore next 3 @preserve -- mergeDefinitions only assigns usage from validated overrides or shipped defaults; reaching this guard requires hand-mutating the resolved config */
477
+ /* v8 ignore next 3 @preserve -- mergeDefinitions only assigns usage from validated overrides or built-in presets; reaching this guard requires hand-mutating the resolved config */
472
478
  if (typeof definition.usage !== "object" || definition.usage === null) {
473
479
  fail(`${usagePath} must be an object`);
474
480
  }
475
481
  const { codexbar } = definition.usage;
476
- /* v8 ignore next 3 @preserve -- mergeDefinitions only assigns usage from validated overrides or shipped defaults; reaching this guard requires hand-mutating the resolved config */
482
+ /* v8 ignore next 3 @preserve -- mergeDefinitions only assigns usage from validated overrides or built-in presets; reaching this guard requires hand-mutating the resolved config */
477
483
  if (typeof codexbar !== "object" || codexbar === null) {
478
484
  fail(`${usagePath}.codexbar must be an object`);
479
485
  }
@@ -496,11 +502,11 @@ function validate(config) {
496
502
  if (!LOCAL_RUNNER_SETTINGS.includes(config.local.runner)) {
497
503
  fail(`local.runner must be one of ${LOCAL_RUNNER_SETTINGS.join(", ")} (got ${JSON.stringify(config.local.runner)})`);
498
504
  }
499
- // Disabled-default check must run before the generic "not a key" check so
500
- // the user gets the specific "is disabled" message instead of a stale-list
501
- // message they can't act on without realizing they need to re-enable.
502
- if (isShippedDefaultDisabled(config, config.models.default)) {
503
- fail(`models.default ("${config.models.default}") is disabled. Either re-enable it or set models.default to an enabled model.`);
505
+ // Built-in-not-enabled check must run before the generic "not a key" check
506
+ // so the user gets the specific migration-oriented message for `codex`
507
+ // instead of a stale-list message.
508
+ if (isBuiltInModelNotEnabled(config, config.models.default)) {
509
+ fail(`models.default ("${config.models.default}") is not enabled. Add \`models.definitions.${config.models.default}: {}\` or set models.default to an enabled model.`);
504
510
  }
505
511
  if (!(config.models.default in definitions)) {
506
512
  fail(`models.default ("${config.models.default}") is not a key in models.definitions (have: ${Object.keys(definitions).join(", ")})`);
@@ -1,11 +1,12 @@
1
1
  # Configuration
2
2
 
3
- Two keys are required; everything else has a default.
3
+ Workspace settings and at least one enabled model 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 sibling ticket worktrees. |
8
8
  | `workspace.knownRepositories` | Repos searched for in ticket descriptions to infer where work belongs. |
9
+ | `models.definitions` | Enabled model set. Built-in presets can be enabled with `{}`. |
9
10
 
10
11
  The branch prefix (`<prefix>-<TICKET>`) is derived from `os.userInfo().username` and is not configurable. There is no `linear` config block. 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.
11
12
 
@@ -40,41 +41,51 @@ The "Loaded config from ..." line at startup tells you which config won.
40
41
 
41
42
  ## Agent Label Routing
42
43
 
43
- - `agent-claude`, `agent-codex`, `agent-<name>` routes to that model.
44
+ - `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled model.
44
45
  - `agent-any` routes to the model with the most available session capacity.
45
- - Unknown `agent-<name>` falls back to `models.default` with a warning.
46
+ - Unknown `agent-<name>` falls back to `models.default`.
47
+ - A built-in `agent-<name>` label whose model is not enabled falls back to `models.default` with a warning.
46
48
  - No `agent-*` label is ignored by `crew run`. Dispatch on demand with `crew start <TICKET>`, which falls back to `models.default`.
47
49
  - Todo tickets blocked by non-terminal blockers are skipped until their blockers reach a terminal status.
48
50
 
49
51
  Status classification uses Linear's workflow `state.type` (`unstarted`, `started`, `completed`, `canceled`, `duplicate`), so renamed status columns work without configuration. Parent issues with children are ignored; sub-issues are the work items.
50
52
 
51
- ## Disabling A Shipped Default Model
53
+ ## Enabling Model Presets
52
54
 
53
- Groundcrew ships `claude` and `codex` as default model definitions, additively merged into every resolved config. To stop probing one:
55
+ Groundcrew ships built-in presets for `claude` and `codex`, but models are not enabled by default. List the models you want in `models.definitions`:
54
56
 
55
57
  ```ts
56
58
  export default {
57
59
  models: {
58
60
  default: "claude",
59
61
  definitions: {
60
- codex: { disabled: true },
62
+ claude: {},
61
63
  },
62
64
  },
63
65
  };
64
66
  ```
65
67
 
66
- Effects:
68
+ To keep both shipped presets enabled:
67
69
 
68
- - `crew doctor` does not probe the disabled model's CLI.
69
- - `agent-any` only resolves to enabled models.
70
- - An `agent-<disabled>` label on a ticket falls back to `models.default` with a warning in the log.
70
+ ```ts
71
+ export default {
72
+ models: {
73
+ default: "claude",
74
+ definitions: {
75
+ claude: {},
76
+ codex: {},
77
+ },
78
+ },
79
+ };
80
+ ```
71
81
 
72
82
  Rules:
73
83
 
74
- - `disabled` only accepts shipped-default keys (`claude`, `codex`). A typo fails loudly at config load.
75
- - `disabled` must be exactly the boolean `true`.
76
- - It cannot be combined with `cmd`, `color`, or `usage` in the same entry.
84
+ - `models.definitions` is the enabled model set; `crew doctor` only probes listed models.
85
+ - Built-in entries can be `{}` or partial overrides such as `{ cmd: "..." }`.
86
+ - Custom model names must provide `cmd` and `color`.
77
87
  - `models.default` must point at an enabled model.
88
+ - Legacy model entries like `codex: { disabled: true }` are rejected with migration guidance; remove unwanted entries instead.
78
89
 
79
90
  ## Prompt Customization
80
91
 
@@ -96,26 +107,25 @@ This keeps package defaults portable while letting your private config reference
96
107
 
97
108
  ## Full Reference
98
109
 
99
- | Key | Default | What it does |
100
- | ---------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
- | `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`. |
102
- | `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
103
- | `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
104
- | `workspace.projectDir` | **required** | Parent dir for cloned repos and sibling ticket worktrees. |
105
- | `workspace.knownRepositories` | **required** | Repos searched for in ticket descriptions to infer where work belongs. A ticket labeled for groundcrew (`agent-*`) fails fast when no known repo appears; unlabeled tickets are ignored. |
106
- | `orchestrator.maximumInProgress` | `4` | Cap on in-progress tickets at once for this `crew` instance. |
107
- | `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
108
- | `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick. |
109
- | `models.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TICKET>` for unlabeled tickets. `crew run` ignores unlabeled tickets and does not apply this default. Must exist in `models.definitions`. |
110
- | `models.definitions` | `{ claude, codex }` | Agent definitions. Additive merge with shipped defaults. |
111
- | `models.definitions.<name>.cmd` | | Shell command launched for the model. 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. |
112
- | `models.definitions.<name>.color` | | Color for the workspace status pill (cmux only; tmux silently drops it). |
113
- | `models.definitions.<name>.usage` | optional | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage`. Falls back to default when unset, with gating enabled for known models. 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. |
114
- | `models.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the model. Required at launch when `local.runner` resolves to `sdx`. Fields: `agent` (required sbx agent name) and `setupCommand` (override for the inside-sandbox setup script). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
115
- | `models.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. |
116
- | `models.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`. |
117
- | `models.definitions.<name>.disabled` | optional | When set to exactly `true`, drops the named shipped default (`claude` or `codex`). Doctor skips probing it; `agent-<name>` labels fall back to `models.default` with a warning. |
118
- | `prompts.initial` | unattended template | First message sent to the agent. Placeholders: `{{ticket}}`, `{{worktree}}`, `{{title}}`, `{{description}}`. Override this from `crew.config.ts` for team-specific statuses, tools, plugins, or review loops. |
119
- | `workspaceKind` | `"auto"` | Terminal session manager. `"auto"` picks `cmux` when on PATH, else `tmux`. Set to `"cmux"` or `"tmux"` to fail loudly when the chosen backend is missing. |
120
- | `local.runner` | `"auto"` | Local isolation backend. `"auto"` uses `safehouse` on macOS and `sdx` on Linux/WSL. Explicit: `"safehouse"`, `"sdx"`, `"none"`. `"none"` is never picked implicitly. |
121
- | `logging.file` | XDG state path | Append-mode log file. `log()` / `logEvent()` tee here in addition to stdout. Defaults to `${XDG_STATE_HOME:-$HOME/.local/state}/groundcrew/groundcrew.log`. |
110
+ | Key | Default | What it does |
111
+ | ---------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
112
+ | `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`. |
113
+ | `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
114
+ | `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
115
+ | `workspace.projectDir` | **required** | Parent dir for cloned repos and sibling ticket worktrees. |
116
+ | `workspace.knownRepositories` | **required** | Repos searched for in ticket descriptions to infer where work belongs. A ticket labeled for groundcrew (`agent-*`) fails fast when no known repo appears; unlabeled tickets are ignored. |
117
+ | `orchestrator.maximumInProgress` | `4` | Cap on in-progress tickets at once for this `crew` instance. |
118
+ | `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
119
+ | `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick. |
120
+ | `models.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TICKET>` for unlabeled tickets. `crew run` ignores unlabeled tickets and does not apply this default. Must exist in `models.definitions`. If you enable only `codex`, set `default: "codex"`. |
121
+ | `models.definitions` | **required** | Enabled model set. Built-in keys (`claude`, `codex`) can use `{}` to opt into the shipped preset. Custom model names must provide `cmd` and `color`. |
122
+ | `models.definitions.<name>.cmd` | preset for built-ins | Shell command launched for the model. Required for custom models. 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. |
123
+ | `models.definitions.<name>.color` | preset for built-ins | Color for the workspace status pill (cmux only; tmux silently drops it). Required for custom models. |
124
+ | `models.definitions.<name>.usage` | preset for built-ins | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage`. 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 model enabled. |
125
+ | `models.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the model. Required at launch when `local.runner` resolves to `sdx`. Fields: `agent` (required sbx agent name) and `setupCommand` (override for the inside-sandbox setup script). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
126
+ | `models.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. |
127
+ | `models.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`. |
128
+ | `prompts.initial` | unattended template | First message sent to the agent. Placeholders: `{{ticket}}`, `{{worktree}}`, `{{title}}`, `{{description}}`. Override this from `crew.config.ts` for team-specific statuses, tools, plugins, or review loops. |
129
+ | `workspaceKind` | `"auto"` | Terminal session manager. `"auto"` picks `cmux` when on PATH, else `tmux`. Set to `"cmux"` or `"tmux"` to fail loudly when the chosen backend is missing. |
130
+ | `local.runner` | `"auto"` | Local isolation backend. `"auto"` uses `safehouse` on macOS and `sdx` on Linux/WSL. Explicit: `"safehouse"`, `"sdx"`, `"none"`. `"none"` is never picked implicitly. |
131
+ | `logging.file` | XDG state path | Append-mode log file. `log()` / `logEvent()` tee here in addition to stdout. Defaults to `${XDG_STATE_HOME:-$HOME/.local/state}/groundcrew/groundcrew.log`. |
@@ -4,18 +4,18 @@ First stop for "what exists locally right now": `crew status <ticket>` shows the
4
4
 
5
5
  ## Missing Model CLI
6
6
 
7
- `models.definitions` includes both shipped defaults (`claude`, `codex`) by default via additive merge. If you only intend to label tickets `agent-claude` and do not have `codex` installed, initialize with `crew init --model claude` or set:
7
+ `crew doctor` probes every model listed in `models.definitions`. If you do not have `codex` installed, initialize with `crew init --model claude` or leave `codex` out of the enabled model set:
8
8
 
9
9
  ```ts
10
10
  models: {
11
11
  default: "claude",
12
12
  definitions: {
13
- codex: { disabled: true },
13
+ claude: {},
14
14
  },
15
15
  },
16
16
  ```
17
17
 
18
- Without that, doctor exits non-zero on a missing `codex` binary even though `crew run` would never route to it.
18
+ If `codex: {}` is listed, doctor expects the `codex` CLI to be installed because tickets can route to `agent-codex` and `agent-any` can select it.
19
19
 
20
20
  ## Safehouse-Wrapped Commands Are Not Re-Wrapped
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.7.3",
3
+ "version": "4.8.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",
@@ -68,7 +68,7 @@
68
68
  "verify": "node scripts/verifyAll.mts"
69
69
  },
70
70
  "dependencies": {
71
- "@clipboard-health/clearance": "1.0.8",
71
+ "@clipboard-health/clearance": "1.1.7",
72
72
  "@linear/sdk": "86.0.0",
73
73
  "cosmiconfig": "9.0.1",
74
74
  "tslib": "2.8.1",
@@ -77,10 +77,10 @@
77
77
  "devDependencies": {
78
78
  "@clipboard-health/ai-rules": "2.21.0",
79
79
  "@clipboard-health/oxlint-config": "1.9.4",
80
- "@nx/js": "22.7.2",
80
+ "@nx/js": "22.7.3",
81
81
  "@tsconfig/node24": "24.0.4",
82
82
  "@tsconfig/strictest": "2.0.8",
83
- "@types/node": "24.12.4",
83
+ "@types/node": "25.9.1",
84
84
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
85
85
  "@vitest/coverage-v8": "4.1.7",
86
86
  "cspell": "10.0.0",
@@ -90,11 +90,11 @@
90
90
  "knip": "6.14.2",
91
91
  "lint-staged": "17.0.5",
92
92
  "markdownlint-cli2": "0.22.1",
93
- "nx": "22.7.2",
93
+ "nx": "22.7.3",
94
94
  "oxfmt": "0.52.0",
95
- "oxlint": "1.65.0",
95
+ "oxlint": "1.66.0",
96
96
  "oxlint-tsgolint": "0.23.0",
97
- "syncpack": "15.2.0",
97
+ "syncpack": "15.3.1",
98
98
  "vite": "8.0.14",
99
99
  "vitest": "4.1.7"
100
100
  },