@clipboard-health/groundcrew 4.1.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +16 -42
- package/dist/commands/cleaner.d.ts +1 -1
- package/dist/commands/cleaner.d.ts.map +1 -1
- package/dist/commands/cleaner.js +4 -2
- package/dist/commands/dispatcher.d.ts +6 -6
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +43 -27
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -22
- package/dist/commands/eligibility.d.ts +1 -1
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +7 -6
- package/dist/commands/orchestrator.d.ts.map +1 -1
- package/dist/commands/orchestrator.js +18 -14
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +3 -2
- package/dist/commands/setupWorkspace.d.ts +2 -4
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +27 -27
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +6 -3
- package/dist/commands/upgrade.d.ts +0 -11
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +14 -100
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/adapters/linear/client.d.ts +22 -0
- package/dist/lib/adapters/linear/client.d.ts.map +1 -0
- package/dist/lib/adapters/linear/client.js +36 -0
- package/dist/lib/adapters/linear/factory.d.ts +24 -14
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +113 -46
- package/dist/lib/{boardSource.d.ts → adapters/linear/fetch.d.ts} +19 -71
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -0
- package/dist/lib/{boardSource.js → adapters/linear/fetch.js} +21 -133
- package/dist/lib/adapters/linear/index.d.ts +1 -0
- package/dist/lib/adapters/linear/index.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.d.ts +44 -0
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -0
- package/dist/lib/adapters/linear/parsing.js +144 -0
- package/dist/lib/{linearIssueStatus.d.ts → adapters/linear/writeback.d.ts} +1 -2
- package/dist/lib/adapters/linear/writeback.d.ts.map +1 -0
- package/dist/lib/{linearIssueStatus.js → adapters/linear/writeback.js} +16 -17
- package/dist/lib/adapters/shell/factory.d.ts +1 -1
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
- package/dist/lib/adapters/shell/factory.js +8 -4
- package/dist/lib/adapters/shell/invoke.d.ts +4 -7
- package/dist/lib/adapters/shell/invoke.d.ts.map +1 -1
- package/dist/lib/adapters/shell/invoke.js +46 -75
- package/dist/lib/adapters/shell/schema.d.ts +10 -0
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
- package/dist/lib/adapters/shell/schema.js +9 -5
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +43 -4
- package/dist/lib/buildSources.d.ts +11 -0
- package/dist/lib/buildSources.d.ts.map +1 -1
- package/dist/lib/buildSources.js +41 -0
- package/dist/lib/repositoryValidation.d.ts +13 -0
- package/dist/lib/repositoryValidation.d.ts.map +1 -0
- package/dist/lib/repositoryValidation.js +20 -0
- package/dist/lib/testing/canonicalFixtures.d.ts +19 -0
- package/dist/lib/testing/canonicalFixtures.d.ts.map +1 -0
- package/dist/lib/testing/canonicalFixtures.js +62 -0
- package/dist/lib/ticketSource.d.ts +71 -1
- package/dist/lib/ticketSource.d.ts.map +1 -1
- package/dist/lib/ticketSource.js +31 -0
- package/dist/lib/util.d.ts +0 -20
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +0 -35
- package/package.json +1 -1
- package/dist/commands/setupRepos.d.ts +0 -44
- package/dist/commands/setupRepos.d.ts.map +0 -1
- package/dist/commands/setupRepos.js +0 -212
- package/dist/lib/boardSource.d.ts.map +0 -1
- package/dist/lib/linearIssueStatus.d.ts.map +0 -1
- package/dist/lib/upgrade.d.ts +0 -66
- package/dist/lib/upgrade.d.ts.map +0 -1
- package/dist/lib/upgrade.js +0 -178
package/README.md
CHANGED
|
@@ -63,7 +63,9 @@ npm install -g @clipboard-health/groundcrew
|
|
|
63
63
|
crew init && $EDITOR crew.config.ts
|
|
64
64
|
|
|
65
65
|
# 4. Clone the repos referenced in your config
|
|
66
|
-
|
|
66
|
+
PROJECT_DIR="$HOME/dev/c"
|
|
67
|
+
mkdir -p "$PROJECT_DIR/OWNER"
|
|
68
|
+
git clone git@github.com:OWNER/REPO.git "$PROJECT_DIR/OWNER/REPO"
|
|
67
69
|
|
|
68
70
|
# 5. Export your Linear API key
|
|
69
71
|
export GROUNDCREW_LINEAR_API_KEY="lin_api_..."
|
|
@@ -86,14 +88,35 @@ crew status [<TICKET>] # inspect current state
|
|
|
86
88
|
crew run # one-shot orchestration
|
|
87
89
|
crew run --watch # poll forever
|
|
88
90
|
crew start <TICKET> # provision + launch one ticket now
|
|
89
|
-
crew setup repos [<repo>...] [--dry-run] # clone known repos via gh
|
|
90
91
|
crew stop <TICKET> [--reason <text>] # stop workspace, keep worktree
|
|
91
92
|
crew resume <TICKET> # reopen a paused ticket
|
|
92
93
|
crew cleanup <TICKET> # tear down every worktree for a ticket
|
|
94
|
+
crew upgrade [<version>] # reinstall crew globally through npm
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
Deprecated aliases still work but print a warning and will be removed in the next major version: `crew interrupt` → `crew stop`, `crew run --ticket <TICKET>` → `crew start <TICKET>`, `crew doctor --ticket <TICKET>` → `crew status <TICKET>`.
|
|
96
98
|
|
|
99
|
+
## Manual Repository Bootstrap
|
|
100
|
+
|
|
101
|
+
Groundcrew no longer clones repositories for you. For each `workspace.knownRepositories` entry,
|
|
102
|
+
clone the repository into `workspace.projectDir` using the same relative path that appears in the
|
|
103
|
+
config. For an `OWNER/REPO` entry:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
PROJECT_DIR="$HOME/dev/c"
|
|
107
|
+
mkdir -p "$PROJECT_DIR/OWNER"
|
|
108
|
+
git clone git@github.com:OWNER/REPO.git "$PROJECT_DIR/OWNER/REPO"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
HTTPS works the same way if you do not use SSH:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git clone https://github.com/OWNER/REPO.git "$PROJECT_DIR/OWNER/REPO"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Bare-name entries do not include an owner, so choose the correct remote URL yourself and clone it to
|
|
118
|
+
`$PROJECT_DIR/<name>`. `crew setup repos` now exits non-zero and points back to this section.
|
|
119
|
+
|
|
97
120
|
## Configuration
|
|
98
121
|
|
|
99
122
|
Two keys are required; everything else has a default.
|
|
@@ -508,13 +531,6 @@ The handoff is `<your cmd> "<prompt>"`. `claude`, `codex`, and `cursor-agent` al
|
|
|
508
531
|
|
|
509
532
|
</details>
|
|
510
533
|
|
|
511
|
-
<details>
|
|
512
|
-
<summary><code>crew setup repos</code> only auto-clones <code>owner/repo</code> entries</summary>
|
|
513
|
-
|
|
514
|
-
Bare-name entries in `workspace.knownRepositories` (e.g. `"api"` rather than `"clipboardhealth/api"`) are skipped with a hint to clone manually — the command refuses to guess the owner. After a partial setup, the exit code is non-zero so CI gates notice; rerun is idempotent once you clone the bare ones into `<projectDir>/<name>` yourself.
|
|
515
|
-
|
|
516
|
-
</details>
|
|
517
|
-
|
|
518
534
|
## Development
|
|
519
535
|
|
|
520
536
|
Clone the repo and the `crew` / `crew:op` scripts execute straight from TypeScript source — no build step needed.
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAmPA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCvD"}
|
package/dist/cli.js
CHANGED
|
@@ -5,20 +5,21 @@ import { initConfigCli } from "./commands/init.js";
|
|
|
5
5
|
import { interruptWorkspaceCli } from "./commands/interruptWorkspace.js";
|
|
6
6
|
import { orchestrate } from "./commands/orchestrator.js";
|
|
7
7
|
import { resumeWorkspaceCli } from "./commands/resumeWorkspace.js";
|
|
8
|
-
import { setupReposCli } from "./commands/setupRepos.js";
|
|
9
8
|
import { setupWorkspaceCli } from "./commands/setupWorkspace.js";
|
|
10
9
|
import { statusCli } from "./commands/status.js";
|
|
11
10
|
import { createDefaultUpgradeCliOptions, upgradeCli } from "./commands/upgrade.js";
|
|
12
|
-
import {
|
|
13
|
-
import { errorMessage, parseDryRunPositionals, readEnvironmentVariable, readTicketArgument, writeError, writeOutput, } from "./lib/util.js";
|
|
14
|
-
const NUDGE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
15
|
-
const NUDGE_FETCH_TIMEOUT_MS = 1000;
|
|
11
|
+
import { errorMessage, parseDryRunPositionals, readTicketArgument, writeError, writeOutput, } from "./lib/util.js";
|
|
16
12
|
const REMOVED_SANDBOX_COMMAND_MESSAGE = [
|
|
17
13
|
"`crew sandbox` is no longer supported.",
|
|
18
14
|
"Groundcrew now launches agents inside existing sbx sandboxes but does not list, create, regenerate, authenticate, or remove them.",
|
|
19
15
|
"Use the manual `sbx` workflow in README.md#docker-sandboxes-sdx-setup, then keep `models.definitions.<model>.sandbox.agent` in crew.config.ts so launches can address the existing sandbox.",
|
|
20
16
|
].join("\n");
|
|
21
17
|
const requireFromCli = createRequire(import.meta.url);
|
|
18
|
+
const SETUP_REPOS_REMOVED_MESSAGE = [
|
|
19
|
+
"crew setup repos was removed.",
|
|
20
|
+
"Clone repositories manually with git clone into workspace.projectDir.",
|
|
21
|
+
"See README.md#manual-repository-bootstrap for the replacement workflow.",
|
|
22
|
+
].join(" ");
|
|
22
23
|
/**
|
|
23
24
|
* Prints a deprecation warning to stderr naming the canonical command and that
|
|
24
25
|
* the old form is removed in the next major, then lets the caller proceed.
|
|
@@ -27,13 +28,12 @@ function warnDeprecated(forms) {
|
|
|
27
28
|
writeError(`crew ${forms.oldForm} is deprecated and will be removed in the next major version; use crew ${forms.newForm} instead.`);
|
|
28
29
|
}
|
|
29
30
|
function setupUsage() {
|
|
30
|
-
return
|
|
31
|
+
return `Usage: crew setup repos\n\n${SETUP_REPOS_REMOVED_MESSAGE}`;
|
|
31
32
|
}
|
|
32
33
|
async function setupCli(argv) {
|
|
33
|
-
const [verb
|
|
34
|
+
const [verb] = argv;
|
|
34
35
|
if (verb === "repos") {
|
|
35
|
-
|
|
36
|
-
return;
|
|
36
|
+
throw new Error(SETUP_REPOS_REMOVED_MESSAGE);
|
|
37
37
|
}
|
|
38
38
|
throw new Error(setupUsage());
|
|
39
39
|
}
|
|
@@ -80,27 +80,10 @@ async function startCli(argv) {
|
|
|
80
80
|
async function upgradeCliInvoke(argv) {
|
|
81
81
|
const metadata = packageMetadata();
|
|
82
82
|
await upgradeCli(argv, async () => await createDefaultUpgradeCliOptions({
|
|
83
|
-
currentVersion: metadata.version,
|
|
84
83
|
packageName: metadata.name,
|
|
85
84
|
cliMetaUrl: import.meta.url,
|
|
86
85
|
}));
|
|
87
86
|
}
|
|
88
|
-
async function maybeRunUpgradeNudge(metadata) {
|
|
89
|
-
const message = await computeUpgradeNudge({
|
|
90
|
-
currentVersion: metadata.version,
|
|
91
|
-
packageName: metadata.name,
|
|
92
|
-
cachePath: defaultUpgradeCheckCachePath(),
|
|
93
|
-
ttlMs: NUDGE_TTL_MS,
|
|
94
|
-
fetchTimeoutMs: NUDGE_FETCH_TIMEOUT_MS,
|
|
95
|
-
registry: readEnvironmentVariable("npm_config_registry"),
|
|
96
|
-
noUpgradeCheck: readEnvironmentVariable("GROUNDCREW_NO_UPGRADE_CHECK") === "1",
|
|
97
|
-
now: Date.now,
|
|
98
|
-
fetcher: fetchLatestVersion,
|
|
99
|
-
});
|
|
100
|
-
if (message !== undefined) {
|
|
101
|
-
writeError(message);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
87
|
function doctorTicketAlias(argv) {
|
|
105
88
|
if (argv[0] !== "--ticket") {
|
|
106
89
|
return undefined;
|
|
@@ -175,28 +158,27 @@ const SUBCOMMANDS = {
|
|
|
175
158
|
invoke: resumeWorkspaceCli,
|
|
176
159
|
},
|
|
177
160
|
setup: {
|
|
178
|
-
summary: "
|
|
179
|
-
usage: "repos
|
|
161
|
+
summary: "Removed repository bootstrap command",
|
|
162
|
+
usage: "repos",
|
|
163
|
+
hidden: true,
|
|
180
164
|
invoke: setupCli,
|
|
181
165
|
},
|
|
182
166
|
upgrade: {
|
|
183
167
|
summary: "Install the latest version of crew (or pin to a specific version)",
|
|
184
|
-
usage: "[<version>]
|
|
168
|
+
usage: "[<version>]",
|
|
185
169
|
invoke: upgradeCliInvoke,
|
|
186
170
|
},
|
|
187
171
|
};
|
|
188
172
|
function printHelp() {
|
|
189
|
-
const
|
|
173
|
+
const visibleCommands = Object.entries(SUBCOMMANDS).filter(([, command]) => command.hidden !== true && command.deprecated !== true);
|
|
174
|
+
const width = Math.max(...visibleCommands.map(([key]) => key.length));
|
|
190
175
|
writeOutput("Usage: crew <command> [...args]\n");
|
|
191
176
|
writeOutput("Options:");
|
|
192
177
|
writeOutput(" -h, --help Show help");
|
|
193
178
|
writeOutput(" -v, --version Print version");
|
|
194
179
|
writeOutput("");
|
|
195
180
|
writeOutput("Commands:");
|
|
196
|
-
for (const [name, command] of
|
|
197
|
-
if (command.deprecated === true) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
181
|
+
for (const [name, command] of visibleCommands) {
|
|
200
182
|
writeOutput(` ${name.padEnd(width)} ${command.summary}`);
|
|
201
183
|
writeOutput(` ${" ".repeat(width)} → crew ${name} ${command.usage}`);
|
|
202
184
|
}
|
|
@@ -235,14 +217,6 @@ export async function run(argv) {
|
|
|
235
217
|
process.exitCode = 1;
|
|
236
218
|
return;
|
|
237
219
|
}
|
|
238
|
-
if (subcommand !== "upgrade") {
|
|
239
|
-
try {
|
|
240
|
-
await maybeRunUpgradeNudge(packageMetadata());
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
// Passive nudge is never load-bearing; never block the user's command.
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
220
|
try {
|
|
247
221
|
await command.invoke(rest);
|
|
248
222
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* tickets that have reached a terminal status. One per `orchestrate()`
|
|
4
4
|
* invocation; stateless across iterations. Mirrors `Dispatcher`.
|
|
5
5
|
*/
|
|
6
|
-
import { type BoardState } from "../lib/boardSource.ts";
|
|
7
6
|
import type { ResolvedConfig } from "../lib/config.ts";
|
|
7
|
+
import { type BoardState } from "../lib/ticketSource.ts";
|
|
8
8
|
import { type WorktreeEntry } from "../lib/worktrees.ts";
|
|
9
9
|
interface CleanerDeps {
|
|
10
10
|
config: ResolvedConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleaner.d.ts","sourceRoot":"","sources":["../../src/commands/cleaner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cleaner.d.ts","sourceRoot":"","sources":["../../src/commands/cleaner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,qBAAqB,CAAC;AAGpE,UAAU,WAAW;IACnB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,CAAC,UAAU,EAAE;QAClB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAoDxD"}
|
package/dist/commands/cleaner.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* tickets that have reached a terminal status. One per `orchestrate()`
|
|
4
4
|
* invocation; stateless across iterations. Mirrors `Dispatcher`.
|
|
5
5
|
*/
|
|
6
|
-
import { isTerminalStatusForIssue } from "../lib/boardSource.js";
|
|
7
6
|
import { recordCleanedUpRuns } from "../lib/runStateCleanup.js";
|
|
7
|
+
import { naturalIdFromCanonical } from "../lib/ticketSource.js";
|
|
8
8
|
import { log, logEvent } from "../lib/util.js";
|
|
9
9
|
import { worktrees } from "../lib/worktrees.js";
|
|
10
10
|
import { logTeardown, recordTeardownEvents } from "./teardownReporter.js";
|
|
@@ -12,7 +12,9 @@ export function createCleaner(deps) {
|
|
|
12
12
|
const { config } = deps;
|
|
13
13
|
async function runOnce(arguments_) {
|
|
14
14
|
const { state, worktreeEntries, dryRun, signal } = arguments_;
|
|
15
|
-
const terminalTickets = new Set(state.issues
|
|
15
|
+
const terminalTickets = new Set(state.issues
|
|
16
|
+
.filter((issue) => issue.status === "done")
|
|
17
|
+
.map((issue) => naturalIdFromCanonical(issue.id)));
|
|
16
18
|
if (terminalTickets.size === 0) {
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-iteration decider that picks Todo tickets to start and acts on the
|
|
3
|
-
* picks.
|
|
4
|
-
*
|
|
3
|
+
* picks. Stateless across iterations. The Board adapter owns its own writeback
|
|
4
|
+
* caches (e.g., Linear's team-state cache lives in `src/lib/adapters/linear/writeback.ts`).
|
|
5
5
|
*
|
|
6
6
|
* Pure verdict logic lives in `eligibility.ts`; this module is responsible
|
|
7
|
-
* for telemetry,
|
|
7
|
+
* for telemetry, writeback via Board, and side-effecting setupWorkspace calls.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
10
|
-
import { type BoardState } from "../lib/boardSource.ts";
|
|
9
|
+
import type { Board } from "../lib/board.ts";
|
|
11
10
|
import type { ResolvedConfig } from "../lib/config.ts";
|
|
11
|
+
import { type BoardState } from "../lib/ticketSource.ts";
|
|
12
12
|
import type { UsageByModel } from "../lib/usage.ts";
|
|
13
13
|
import type { WorktreeEntry } from "../lib/worktrees.ts";
|
|
14
14
|
interface DispatcherDeps {
|
|
15
15
|
config: ResolvedConfig;
|
|
16
|
-
|
|
16
|
+
board: Board;
|
|
17
17
|
}
|
|
18
18
|
export interface Dispatcher {
|
|
19
19
|
runOnce(arguments_: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD,UAAU,cAAc;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,UAAU,EAAE;QAClB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,+FAA+F;QAC/F,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAaD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CA8MjE"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-iteration decider that picks Todo tickets to start and acts on the
|
|
3
|
-
* picks.
|
|
4
|
-
*
|
|
3
|
+
* picks. Stateless across iterations. The Board adapter owns its own writeback
|
|
4
|
+
* caches (e.g., Linear's team-state cache lives in `src/lib/adapters/linear/writeback.ts`).
|
|
5
5
|
*
|
|
6
6
|
* Pure verdict logic lives in `eligibility.ts`; this module is responsible
|
|
7
|
-
* for telemetry,
|
|
7
|
+
* for telemetry, writeback via Board, and side-effecting setupWorkspace calls.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { dispatchableRepository } from "../lib/repositoryValidation.js";
|
|
10
|
+
import { isGroundcrewIssue, naturalIdFromCanonical, } from "../lib/ticketSource.js";
|
|
11
11
|
import { errorMessage, log, logEvent } from "../lib/util.js";
|
|
12
12
|
import { workspaces } from "../lib/workspaces.js";
|
|
13
13
|
import { classifyBlockers, classifyEligibility, classifyUsageExhaustion, } from "./eligibility.js";
|
|
@@ -17,14 +17,13 @@ function logSkip(verdict) {
|
|
|
17
17
|
logEvent("dispatch", {
|
|
18
18
|
outcome: "skipped",
|
|
19
19
|
reason: verdict.eventReason,
|
|
20
|
-
ticket: verdict.issue.id,
|
|
20
|
+
ticket: naturalIdFromCanonical(verdict.issue.id),
|
|
21
21
|
blockers: verdict.blockers,
|
|
22
22
|
model: verdict.model,
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
export function createDispatcher(deps) {
|
|
26
|
-
const { config,
|
|
27
|
-
const issueStatusUpdater = createLinearIssueStatusUpdater({ client });
|
|
26
|
+
const { config, board } = deps;
|
|
28
27
|
function buildExhaustedSet(usage) {
|
|
29
28
|
const exhausted = new Set();
|
|
30
29
|
for (const exhaustion of classifyUsageExhaustion(config, usage)) {
|
|
@@ -35,17 +34,18 @@ export function createDispatcher(deps) {
|
|
|
35
34
|
}
|
|
36
35
|
async function startEligibleIssue(start, dryRun, signal) {
|
|
37
36
|
const { issue, recovery } = start;
|
|
37
|
+
const ticketId = naturalIdFromCanonical(issue.id);
|
|
38
38
|
if (start.resolvedFromAny) {
|
|
39
|
-
log(`Resolved agent-any for ${
|
|
39
|
+
log(`Resolved agent-any for ${ticketId} → ${issue.model}`);
|
|
40
40
|
}
|
|
41
41
|
if (dryRun) {
|
|
42
42
|
log(
|
|
43
43
|
/* v8 ignore next @preserve -- classifyTodo forces recovery=false in dry-run, so the resume branch can't fire here */
|
|
44
|
-
`[dry-run] Would ${recovery ? "resume" : "start"} ${
|
|
44
|
+
`[dry-run] Would ${recovery ? "resume" : "start"} ${ticketId} in ${issue.repository} (${issue.model})`);
|
|
45
45
|
logEvent("dispatch", {
|
|
46
46
|
outcome: "skipped",
|
|
47
47
|
reason: "dry_run",
|
|
48
|
-
ticket:
|
|
48
|
+
ticket: ticketId,
|
|
49
49
|
model: issue.model,
|
|
50
50
|
repository: issue.repository,
|
|
51
51
|
});
|
|
@@ -53,31 +53,32 @@ export function createDispatcher(deps) {
|
|
|
53
53
|
}
|
|
54
54
|
try {
|
|
55
55
|
if (recovery) {
|
|
56
|
-
log(`Worktree and workspace already exist for ${
|
|
56
|
+
log(`Worktree and workspace already exist for ${ticketId}; resuming with markInProgress`);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
59
|
const setupOptions = {
|
|
60
60
|
repository: issue.repository,
|
|
61
|
-
ticket:
|
|
61
|
+
ticket: ticketId,
|
|
62
62
|
model: issue.model,
|
|
63
|
+
details: { title: issue.title, description: issue.description },
|
|
63
64
|
};
|
|
64
65
|
await (signal === undefined
|
|
65
66
|
? setupWorkspace(config, setupOptions)
|
|
66
67
|
: setupWorkspace(config, setupOptions, { signal }));
|
|
67
68
|
}
|
|
68
|
-
await
|
|
69
|
+
await board.markInProgress(issue);
|
|
69
70
|
logEvent("dispatch", {
|
|
70
71
|
outcome: recovery ? "resumed" : "started",
|
|
71
|
-
ticket:
|
|
72
|
+
ticket: ticketId,
|
|
72
73
|
model: issue.model,
|
|
73
74
|
repository: issue.repository,
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
catch (error) {
|
|
77
|
-
log(`Failed to start ${
|
|
78
|
+
log(`Failed to start ${ticketId}: ${errorMessage(error)}`);
|
|
78
79
|
logEvent("dispatch", {
|
|
79
80
|
outcome: "failed",
|
|
80
|
-
ticket:
|
|
81
|
+
ticket: ticketId,
|
|
81
82
|
model: issue.model,
|
|
82
83
|
repository: issue.repository,
|
|
83
84
|
error: errorMessage(error),
|
|
@@ -86,24 +87,24 @@ export function createDispatcher(deps) {
|
|
|
86
87
|
}
|
|
87
88
|
async function runOnce(arguments_) {
|
|
88
89
|
const { state, worktreeEntries, usage, dryRun, signal, idleSuffix = "" } = arguments_;
|
|
89
|
-
|
|
90
|
-
// Surface parent tickets that fetchBoard silently dropped. Without this
|
|
90
|
+
// Surface parent tickets that fetch silently dropped. Without this
|
|
91
91
|
// an operator sees "No Todo tickets to pick up" with no signal that an
|
|
92
92
|
// expected Todo+labelled ticket was skipped because it has sub-issues.
|
|
93
93
|
for (const skip of state.parentSkips) {
|
|
94
|
-
|
|
94
|
+
const ticket = naturalIdFromCanonical(skip.id);
|
|
95
|
+
log(`Skipping ${ticket}: parent ticket with ${skip.childCount} sub-issue(s) — groundcrew works sub-issues, not parents`);
|
|
95
96
|
logEvent("dispatch", {
|
|
96
97
|
outcome: "skipped",
|
|
97
98
|
reason: "parent_with_children",
|
|
98
|
-
ticket
|
|
99
|
+
ticket,
|
|
99
100
|
children: skip.childCount,
|
|
100
101
|
});
|
|
101
102
|
}
|
|
102
|
-
const activeCount = state.issues.filter((issue) =>
|
|
103
|
+
const activeCount = state.issues.filter((issue) => issue.status === "in-progress").length;
|
|
103
104
|
const slots = config.orchestrator.maximumInProgress - activeCount;
|
|
104
105
|
// Narrow Todo to tickets that opted in via an `agent-*` label.
|
|
105
106
|
// Unlabeled tickets are not groundcrew's concern even when in Todo.
|
|
106
|
-
const todo = state.issues.filter((issue) =>
|
|
107
|
+
const todo = state.issues.filter((issue) => issue.status === "todo" && isGroundcrewIssue(issue));
|
|
107
108
|
if (slots <= 0) {
|
|
108
109
|
log(`At capacity (${activeCount}/${config.orchestrator.maximumInProgress}), no new work to start${idleSuffix}`);
|
|
109
110
|
return;
|
|
@@ -122,6 +123,20 @@ export function createDispatcher(deps) {
|
|
|
122
123
|
log(`No eligible Todo tickets after blocker filtering${idleSuffix}`);
|
|
123
124
|
return;
|
|
124
125
|
}
|
|
126
|
+
// Validate repositories BEFORE the expensive probes so a tick whose only
|
|
127
|
+
// candidates have unknown repos short-circuits without paying for the
|
|
128
|
+
// usage() HTTP call or the workspaces.probe shell-out. Doing this filter
|
|
129
|
+
// here also keeps an unknown-repo ticket at the head of the queue from
|
|
130
|
+
// consuming a slot in classifyEligibility and starving later valid
|
|
131
|
+
// tickets. Each unknown repo still emits a WARN via dispatchableRepository.
|
|
132
|
+
const dispatchableUnblocked = unblocked.filter((issue) => {
|
|
133
|
+
const repository = dispatchableRepository(issue, config.workspace.knownRepositories, log);
|
|
134
|
+
return repository !== undefined;
|
|
135
|
+
});
|
|
136
|
+
if (dispatchableUnblocked.length === 0) {
|
|
137
|
+
log(`No eligible Todo tickets after repository validation${idleSuffix}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
125
140
|
// usage() is an HTTP call; workspaces.probe shells tmux/cmux. Kick off
|
|
126
141
|
// usage first so the workspace probe can overlap with the in-flight request.
|
|
127
142
|
const usagePromise = usage(signal);
|
|
@@ -144,7 +159,7 @@ export function createDispatcher(deps) {
|
|
|
144
159
|
const exhausted = buildExhaustedSet(fetchedUsage);
|
|
145
160
|
const verdicts = classifyEligibility({
|
|
146
161
|
config,
|
|
147
|
-
unblocked,
|
|
162
|
+
unblocked: dispatchableUnblocked,
|
|
148
163
|
worktreeEntries,
|
|
149
164
|
workspaceProbe,
|
|
150
165
|
usage: fetchedUsage,
|
|
@@ -161,12 +176,13 @@ export function createDispatcher(deps) {
|
|
|
161
176
|
log(`No eligible Todo tickets after eligibility filtering${idleSuffix}`);
|
|
162
177
|
return;
|
|
163
178
|
}
|
|
164
|
-
|
|
179
|
+
const dispatchable = starts;
|
|
180
|
+
log(`${slots} slot(s) available, starting ${dispatchable.length} ticket(s): ${dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}(${issue.model})`).join(", ")}`);
|
|
165
181
|
logEvent("dispatch", {
|
|
166
182
|
outcome: "starting",
|
|
167
|
-
tickets:
|
|
183
|
+
tickets: dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}:${issue.model}`),
|
|
168
184
|
});
|
|
169
|
-
for (const start of
|
|
185
|
+
for (const start of dispatchable) {
|
|
170
186
|
// oxlint-disable-next-line no-await-in-loop -- one workspace at a time avoids racing on git
|
|
171
187
|
await startEligibleIssue(start, dryRun, signal);
|
|
172
188
|
}
|
|
@@ -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;AAgJH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CA8E/C"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* Returns true if every required check passes; false otherwise.
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, statSync } from "node:fs";
|
|
6
|
+
import { createBoard } from "../lib/board.js";
|
|
7
|
+
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
6
8
|
import { loadConfig, } from "../lib/config.js";
|
|
7
|
-
import { createBoardSource } from "../lib/boardSource.js";
|
|
8
9
|
import { detectHostCapabilities, which } from "../lib/host.js";
|
|
9
10
|
import { resolveLocalRunner } from "../lib/localRunner.js";
|
|
10
11
|
import { gatedModels } from "../lib/usage.js";
|
|
11
|
-
import { errorMessage,
|
|
12
|
+
import { errorMessage, writeOutput } from "../lib/util.js";
|
|
12
13
|
import { resolveWorkspaceKind } from "../lib/workspaces.js";
|
|
13
14
|
// Tokenization stops after this many non-flag tokens. Two is enough to
|
|
14
15
|
// catch wrapper + wrapped CLI commands like `safehouse claude --foo`.
|
|
@@ -26,32 +27,27 @@ async function checkCmd(cmd, required, hint) {
|
|
|
26
27
|
}
|
|
27
28
|
return result;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
}
|
|
30
|
+
/**
|
|
31
|
+
* Source-agnostic reachability check: build every configured ticket source
|
|
32
|
+
* and run the Board's `verify()` fan-out. Replaces the old Linear-only
|
|
33
|
+
* "api key + reachability" probe so a misconfigured shell (or future Jira)
|
|
34
|
+
* source surfaces here too. A missing Linear API key still fails verify with
|
|
35
|
+
* its own user-facing message, so the prior behavior is preserved.
|
|
36
|
+
*/
|
|
37
|
+
async function checkSourceProbe(config) {
|
|
39
38
|
try {
|
|
40
|
-
await
|
|
39
|
+
const sources = await buildSources(sourcesFromConfig(config), { globalConfig: config });
|
|
40
|
+
const board = createBoard(sources);
|
|
41
|
+
await board.verify();
|
|
41
42
|
return {
|
|
42
|
-
name: "
|
|
43
|
+
name: "source probe",
|
|
43
44
|
ok: true,
|
|
44
45
|
required: true,
|
|
45
|
-
hint:
|
|
46
|
+
hint: `${sources.length} source(s) verified`,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
catch (error) {
|
|
49
|
-
return {
|
|
50
|
-
name: "linear reachability",
|
|
51
|
-
ok: false,
|
|
52
|
-
required: true,
|
|
53
|
-
hint: errorMessage(error),
|
|
54
|
-
};
|
|
50
|
+
return { name: "source probe", ok: false, required: true, hint: errorMessage(error) };
|
|
55
51
|
}
|
|
56
52
|
}
|
|
57
53
|
function checkDir(path, label) {
|
|
@@ -161,7 +157,7 @@ export async function doctor() {
|
|
|
161
157
|
const workspaceOutcome = resolveWorkspaceOutcome(config, host);
|
|
162
158
|
reportWorkspaceKind(config, workspaceOutcome);
|
|
163
159
|
const checks = [
|
|
164
|
-
await
|
|
160
|
+
await checkSourceProbe(config),
|
|
165
161
|
await checkCmd("git", true, "https://git-scm.com/"),
|
|
166
162
|
...(await workspaceChecks(workspaceOutcome)),
|
|
167
163
|
checkDir(config.workspace.projectDir, "workspace.projectDir"),
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* The Dispatcher consumes the verdict list to drive logging and side
|
|
7
7
|
* effects.
|
|
8
8
|
*/
|
|
9
|
-
import { type GroundcrewIssue } from "../lib/boardSource.ts";
|
|
10
9
|
import { type ResolvedConfig } from "../lib/config.ts";
|
|
10
|
+
import { type GroundcrewIssue } from "../lib/ticketSource.ts";
|
|
11
11
|
import type { UsageByModel } from "../lib/usage.ts";
|
|
12
12
|
import type { WorkspaceProbe } from "../lib/workspaces.ts";
|
|
13
13
|
import type { WorktreeEntry } from "../lib/worktrees.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,KAAK,UAAU,GACX,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,4BAA4B,GAC5B,mBAAmB,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,EAAE,UAAU,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1C,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,MAAM,GAAG,SAAS,CAepB;AAaD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,GAClB,oBAAoB,EAAE,CAmCxB;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,qBAAqB,CAYxF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAgE5E"}
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* The Dispatcher consumes the verdict list to drive logging and side
|
|
7
7
|
* effects.
|
|
8
8
|
*/
|
|
9
|
-
import { isTerminalStatusForBlocker, } from "../lib/boardSource.js";
|
|
10
9
|
import { AGENT_ANY_MODEL } from "../lib/config.js";
|
|
10
|
+
import { naturalIdFromCanonical } from "../lib/ticketSource.js";
|
|
11
11
|
const PERCENT_FRACTION_DIVISOR = 100;
|
|
12
12
|
const DAYS_PER_WEEK = 7;
|
|
13
13
|
const MINUTES_PER_DAY = 24 * 60;
|
|
14
14
|
const MINUTES_PER_WEEK = DAYS_PER_WEEK * MINUTES_PER_DAY;
|
|
15
15
|
function blockerSummary(blocker) {
|
|
16
|
-
return `${blocker.id}:${blocker.status
|
|
16
|
+
return `${blocker.id}:${blocker.status}`;
|
|
17
17
|
}
|
|
18
18
|
function blockerVerdictFor(issue) {
|
|
19
19
|
if (issue.hasMoreBlockers) {
|
|
@@ -26,7 +26,7 @@ function blockerVerdictFor(issue) {
|
|
|
26
26
|
blockers,
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
const unresolved = issue.blockers.filter((blocker) =>
|
|
29
|
+
const unresolved = issue.blockers.filter((blocker) => blocker.status !== "done");
|
|
30
30
|
if (unresolved.length === 0) {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
@@ -110,7 +110,8 @@ function classifyRecovery(arguments_) {
|
|
|
110
110
|
if (dryRun) {
|
|
111
111
|
return { kind: "go", recovery: false };
|
|
112
112
|
}
|
|
113
|
-
const
|
|
113
|
+
const naturalId = naturalIdFromCanonical(issue.id);
|
|
114
|
+
const exists = worktreeEntries.some((entry) => entry.repository === issue.repository && entry.ticket === naturalId);
|
|
114
115
|
if (!exists) {
|
|
115
116
|
return { kind: "go", recovery: false };
|
|
116
117
|
}
|
|
@@ -122,11 +123,11 @@ function classifyRecovery(arguments_) {
|
|
|
122
123
|
eventReason: "workspace_list_unavailable",
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
|
-
if (!workspaceProbe.names.has(
|
|
126
|
+
if (!workspaceProbe.names.has(naturalId)) {
|
|
126
127
|
return {
|
|
127
128
|
kind: "skip",
|
|
128
129
|
issue,
|
|
129
|
-
message: `Skipping ${issue.id}: worktree exists but no live workspace. Run \`crew cleanup ${
|
|
130
|
+
message: `Skipping ${issue.id}: worktree exists but no live workspace. Run \`crew cleanup ${naturalId}\` to allow re-provisioning.`,
|
|
130
131
|
eventReason: "workspace_missing",
|
|
131
132
|
};
|
|
132
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyDH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAiBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C7E"}
|