@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 +4 -4
- package/crew.config.example.ts +23 -25
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +4 -4
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +24 -13
- package/dist/lib/adapters/linear/fetch.d.ts +1 -1
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/linear/fetch.js +7 -7
- package/dist/lib/adapters/linear/parsing.d.ts +1 -1
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.js +9 -9
- package/dist/lib/config.d.ts +13 -19
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +46 -40
- package/docs/configuration.md +46 -36
- package/docs/troubleshooting.md +3 -3
- package/package.json +7 -7
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
115
|
+
claude: {},
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
118
|
} satisfies Config;
|
package/crew.config.example.ts
CHANGED
|
@@ -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
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// //
|
|
56
|
-
// //
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
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;
|
|
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"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
|
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 (!
|
|
123
|
+
if (!isBuiltInModelName(modelName)) {
|
|
124
124
|
return undefined;
|
|
125
125
|
}
|
|
126
|
-
return `install ${token} or
|
|
126
|
+
return `install ${token} or remove \`models.definitions.${modelName}\` from crew.config.ts`;
|
|
127
127
|
}
|
|
128
|
-
function
|
|
128
|
+
function isBuiltInModelName(value) {
|
|
129
129
|
return value === "claude" || value === "codex";
|
|
130
130
|
}
|
|
131
131
|
function format(check) {
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -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, "
|
|
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
|
|
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 === "
|
|
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
|
-
|
|
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
|
-
|
|
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 === "
|
|
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
|
|
383
|
-
if (modelResolution.kind !== "
|
|
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
|
|
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
|
|
@@ -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,
|
|
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,
|
|
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
|
|
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 (
|
|
118
|
-
|
|
117
|
+
if (notEnabledFallback === undefined && isBuiltInModelNotEnabled(config, name)) {
|
|
118
|
+
notEnabledFallback = name;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
const fallback = { model: config.models.default };
|
|
122
|
-
if (
|
|
123
|
-
fallback.
|
|
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.
|
|
136
|
+
if (parsed.notEnabledFallback !== undefined) {
|
|
137
137
|
return {
|
|
138
|
-
kind: "
|
|
139
|
-
requestedModel: parsed.
|
|
138
|
+
kind: "not-enabled-fallback",
|
|
139
|
+
requestedModel: parsed.notEnabledFallback,
|
|
140
140
|
fallbackModel: parsed.model,
|
|
141
141
|
};
|
|
142
142
|
}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -103,26 +103,21 @@ export interface ModelDefinition {
|
|
|
103
103
|
sandbox?: SandboxDefinition;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
|
-
* User-facing model entry shape.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
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
|
|
259
|
-
*
|
|
260
|
-
*
|
|
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
|
|
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
|
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":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAMrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAIzD,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,MAAM,CAAC;AAEtD,eAAO,MAAM,qBAAqB,EAAE,SAAS,kBAAkB,EAKrD,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;;OAaG;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;CAC7B;AAED
|
|
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
|
@@ -35,7 +35,7 @@ const DEFAULT_ORCHESTRATOR = {
|
|
|
35
35
|
pollIntervalMilliseconds: 120_000,
|
|
36
36
|
sessionLimitPercentage: 85,
|
|
37
37
|
};
|
|
38
|
-
const
|
|
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
|
-
|
|
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
|
|
235
|
-
*
|
|
236
|
-
*
|
|
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
|
|
240
|
-
return (Object.hasOwn(
|
|
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
|
|
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 =
|
|
281
|
-
|
|
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
|
-
|
|
287
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
500
|
-
// the user gets the specific
|
|
501
|
-
//
|
|
502
|
-
if (
|
|
503
|
-
fail(`models.default ("${config.models.default}") is
|
|
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(", ")})`);
|
package/docs/configuration.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
##
|
|
53
|
+
## Enabling Model Presets
|
|
52
54
|
|
|
53
|
-
Groundcrew ships `claude` and `codex
|
|
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
|
-
|
|
62
|
+
claude: {},
|
|
61
63
|
},
|
|
62
64
|
},
|
|
63
65
|
};
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
To keep both shipped presets enabled:
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
-
|
|
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
|
|
100
|
-
| ---------------------------------------- |
|
|
101
|
-
| `sources` | `[]`
|
|
102
|
-
| `git.remote` | `"origin"`
|
|
103
|
-
| `git.defaultBranch` | `"main"`
|
|
104
|
-
| `workspace.projectDir` | **required**
|
|
105
|
-
| `workspace.knownRepositories` | **required**
|
|
106
|
-
| `orchestrator.maximumInProgress` | `4`
|
|
107
|
-
| `orchestrator.pollIntervalMilliseconds` | `120_000`
|
|
108
|
-
| `orchestrator.sessionLimitPercentage` | `85`
|
|
109
|
-
| `models.default` | `"claude"`
|
|
110
|
-
| `models.definitions` | `
|
|
111
|
-
| `models.definitions.<name>.cmd` |
|
|
112
|
-
| `models.definitions.<name>.color` |
|
|
113
|
-
| `models.definitions.<name>.usage` |
|
|
114
|
-
| `models.definitions.<name>.sandbox` | optional
|
|
115
|
-
| `models.definitions.<name>.preLaunch` | optional
|
|
116
|
-
| `models.definitions.<name>.preLaunchEnv` | optional
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
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`. |
|
package/docs/troubleshooting.md
CHANGED
|
@@ -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
|
-
`
|
|
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
|
-
|
|
13
|
+
claude: {},
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
80
|
+
"@nx/js": "22.7.3",
|
|
81
81
|
"@tsconfig/node24": "24.0.4",
|
|
82
82
|
"@tsconfig/strictest": "2.0.8",
|
|
83
|
-
"@types/node": "
|
|
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.
|
|
93
|
+
"nx": "22.7.3",
|
|
94
94
|
"oxfmt": "0.52.0",
|
|
95
|
-
"oxlint": "1.
|
|
95
|
+
"oxlint": "1.66.0",
|
|
96
96
|
"oxlint-tsgolint": "0.23.0",
|
|
97
|
-
"syncpack": "15.
|
|
97
|
+
"syncpack": "15.3.1",
|
|
98
98
|
"vite": "8.0.14",
|
|
99
99
|
"vitest": "4.1.7"
|
|
100
100
|
},
|