@clipboard-health/groundcrew 4.7.2 → 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/bin/run.js +2 -2
- package/bin/runCli.js +3 -3
- 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 +28 -17
- package/dist/commands/upgrade.js +2 -2
- 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/adapters/registry.js +2 -2
- package/dist/lib/config.d.ts +13 -19
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +82 -76
- package/dist/lib/launchCommand.js +7 -7
- package/dist/lib/npmGlobal.d.ts +1 -1
- package/dist/lib/npmGlobal.d.ts.map +1 -1
- package/dist/lib/npmGlobal.js +7 -7
- package/dist/lib/runState.js +7 -7
- package/dist/lib/stagedLaunch.js +5 -5
- package/dist/lib/util.d.ts +1 -1
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +4 -4
- package/dist/lib/worktrees.js +18 -18
- package/dist/lib/xdg.js +5 -5
- package/docs/configuration.md +46 -36
- package/docs/troubleshooting.md +3 -3
- package/package.json +7 -7
package/dist/lib/worktrees.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
11
11
|
import { userInfo } from "node:os";
|
|
12
|
-
import
|
|
12
|
+
import path from "node:path";
|
|
13
13
|
import { runCommandAsync } from "./commandRunner.js";
|
|
14
14
|
import { resolveDefaultBranch } from "./defaultBranch.js";
|
|
15
15
|
import { debug, errorMessage, isVerbose } from "./util.js";
|
|
@@ -42,7 +42,7 @@ function repoDirFor(config, repository) {
|
|
|
42
42
|
if (!config.workspace.knownRepositories.includes(repository)) {
|
|
43
43
|
throw new Error(`Repository "${repository}" is not in workspace.knownRepositories: ${config.workspace.knownRepositories.join(", ")}`);
|
|
44
44
|
}
|
|
45
|
-
const repoDir = resolve(config.workspace.projectDir, repository);
|
|
45
|
+
const repoDir = path.resolve(config.workspace.projectDir, repository);
|
|
46
46
|
if (!existsSync(repoDir)) {
|
|
47
47
|
throw new Error(`Repository not found: ${repoDir}`);
|
|
48
48
|
}
|
|
@@ -51,14 +51,14 @@ function repoDirFor(config, repository) {
|
|
|
51
51
|
function basePaths(config, repository, ticket) {
|
|
52
52
|
// Tickets must match the same shape the worktree discovery regexes use,
|
|
53
53
|
// so create()/list()/findByTicket() agree on what's a valid worktree.
|
|
54
|
-
// This also rejects traversal tokens before they reach resolve().
|
|
54
|
+
// This also rejects traversal tokens before they reach path.resolve().
|
|
55
55
|
if (!TICKET_RE.test(ticket)) {
|
|
56
56
|
throw new Error(`Invalid ticket "${ticket}": must be a plain ticket id`);
|
|
57
57
|
}
|
|
58
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
58
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
59
59
|
const repoDir = repoDirFor(config, repository);
|
|
60
60
|
const hostWorktreeName = `${repository}-${ticket}`;
|
|
61
|
-
const hostWorktreeDir = resolve(projectDir, hostWorktreeName);
|
|
61
|
+
const hostWorktreeDir = path.resolve(projectDir, hostWorktreeName);
|
|
62
62
|
return {
|
|
63
63
|
projectDir,
|
|
64
64
|
repoDir,
|
|
@@ -122,17 +122,17 @@ async function createWorktree(config, spec, signal) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
function listWorktrees(config) {
|
|
125
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
125
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
126
126
|
const entries = [];
|
|
127
127
|
// Worktrees live at `projectDir/<repository>-<ticket>`. When `repository`
|
|
128
|
-
// contains a slash (e.g. "owner/repo"), `resolve()` nests one level
|
|
128
|
+
// contains a slash (e.g. "owner/repo"), `path.resolve()` nests one level
|
|
129
129
|
// deeper, so the worktree path is `projectDir/owner/repo-<ticket>`.
|
|
130
130
|
// Scan each known repository's parent directory rather than the project
|
|
131
131
|
// root, so nested worktrees are discovered alongside bare ones.
|
|
132
132
|
const reposByParent = new Map();
|
|
133
133
|
for (const repository of config.workspace.knownRepositories) {
|
|
134
134
|
const lastSlash = repository.lastIndexOf("/");
|
|
135
|
-
const parentDir = lastSlash === -1 ? projectDir : resolve(projectDir, repository.slice(0, lastSlash));
|
|
135
|
+
const parentDir = lastSlash === -1 ? projectDir : path.resolve(projectDir, repository.slice(0, lastSlash));
|
|
136
136
|
const basename = lastSlash === -1 ? repository : repository.slice(lastSlash + 1);
|
|
137
137
|
let repoByBasename = reposByParent.get(parentDir);
|
|
138
138
|
if (repoByBasename === undefined) {
|
|
@@ -170,7 +170,7 @@ function listWorktrees(config) {
|
|
|
170
170
|
repository,
|
|
171
171
|
ticket,
|
|
172
172
|
branchName: branchNameForTicket(ticket),
|
|
173
|
-
dir: resolve(parentDir, entry.name),
|
|
173
|
+
dir: path.resolve(parentDir, entry.name),
|
|
174
174
|
kind: "host",
|
|
175
175
|
});
|
|
176
176
|
}
|
|
@@ -178,8 +178,8 @@ function listWorktrees(config) {
|
|
|
178
178
|
return entries;
|
|
179
179
|
}
|
|
180
180
|
async function removeWorktree(config, entry, options) {
|
|
181
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
182
|
-
const repoDir = resolve(projectDir, entry.repository);
|
|
181
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
182
|
+
const repoDir = path.resolve(projectDir, entry.repository);
|
|
183
183
|
if (existsSync(entry.dir)) {
|
|
184
184
|
debug(`Removing worktree ${entry.dir}${options.force ? " (--force)" : ""}...`);
|
|
185
185
|
const removeArguments = ["-C", repoDir, "worktree", "remove"];
|
|
@@ -297,12 +297,12 @@ async function probeWorktreeRegistration(arguments_) {
|
|
|
297
297
|
catch {
|
|
298
298
|
return "unknown";
|
|
299
299
|
}
|
|
300
|
-
const resolvedWorktreeDir = resolve(arguments_.worktreeDir);
|
|
300
|
+
const resolvedWorktreeDir = path.resolve(arguments_.worktreeDir);
|
|
301
301
|
for (const line of output.split("\n")) {
|
|
302
302
|
if (!line.startsWith(WORKTREE_LIST_PREFIX)) {
|
|
303
303
|
continue;
|
|
304
304
|
}
|
|
305
|
-
if (resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
|
|
305
|
+
if (path.resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
|
|
306
306
|
return "registered";
|
|
307
307
|
}
|
|
308
308
|
}
|
|
@@ -313,18 +313,18 @@ function describeOrphanWorktree(arguments_) {
|
|
|
313
313
|
return `directory exists but is not a registered git worktree. Run \`crew cleanup --force ${ticket}\` to remove ${dir}, or inspect it first if it may contain valuable files.`;
|
|
314
314
|
}
|
|
315
315
|
function expectedHostWorktreeDir(config, entry) {
|
|
316
|
-
return resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
|
|
316
|
+
return path.resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
|
|
317
317
|
}
|
|
318
318
|
function isInsideDirectory(parentDir, childDir) {
|
|
319
|
-
const childRelativePath = relative(parentDir, childDir);
|
|
319
|
+
const childRelativePath = path.relative(parentDir, childDir);
|
|
320
320
|
return (childRelativePath.length > 0 &&
|
|
321
321
|
!childRelativePath.startsWith("..") &&
|
|
322
|
-
!isAbsolute(childRelativePath));
|
|
322
|
+
!path.isAbsolute(childRelativePath));
|
|
323
323
|
}
|
|
324
324
|
function removeOrphanWorktreeDirectory(config, entry) {
|
|
325
|
-
const projectDir = resolve(config.workspace.projectDir);
|
|
325
|
+
const projectDir = path.resolve(config.workspace.projectDir);
|
|
326
326
|
const expectedDir = expectedHostWorktreeDir(config, entry);
|
|
327
|
-
const targetDir = resolve(entry.dir);
|
|
327
|
+
const targetDir = path.resolve(entry.dir);
|
|
328
328
|
if (targetDir !== expectedDir || !isInsideDirectory(projectDir, targetDir)) {
|
|
329
329
|
throw new Error(`Refusing to force-delete ${entry.dir}: expected groundcrew worktree path ${expectedDir}.`);
|
|
330
330
|
}
|
package/dist/lib/xdg.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { readEnvironmentVariable } from "./util.js";
|
|
4
4
|
// Per the XDG Base Directory spec, relative override paths are invalid and
|
|
5
5
|
// must be ignored — without this guard, a relative override would anchor to
|
|
6
6
|
// the cwd via `resolve` instead of falling back to $HOME.
|
|
7
7
|
function xdgBase(envName, fallbackSegments) {
|
|
8
8
|
const override = readEnvironmentVariable(envName);
|
|
9
|
-
if (override !== undefined && override.length > 0 && isAbsolute(override)) {
|
|
9
|
+
if (override !== undefined && override.length > 0 && path.isAbsolute(override)) {
|
|
10
10
|
return override;
|
|
11
11
|
}
|
|
12
|
-
return resolve(homedir(), ...fallbackSegments);
|
|
12
|
+
return path.resolve(homedir(), ...fallbackSegments);
|
|
13
13
|
}
|
|
14
14
|
export function xdgConfigPath(...segments) {
|
|
15
|
-
return resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
15
|
+
return path.resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
|
|
16
16
|
}
|
|
17
17
|
export function xdgStatePath(...segments) {
|
|
18
|
-
return resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
18
|
+
return path.resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
|
|
19
19
|
}
|
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
|
},
|