@clipboard-health/groundcrew 4.2.4 → 4.3.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 +21 -36
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +5 -1
- package/dist/commands/setupWorkspace.d.ts +2 -0
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +11 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +332 -52
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +2 -0
- package/dist/lib/adapters/linear/fetch.d.ts +5 -0
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/linear/fetch.js +6 -0
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
- package/dist/lib/adapters/shell/factory.js +1 -0
- package/dist/lib/adapters/shell/schema.d.ts +2 -0
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
- package/dist/lib/adapters/shell/schema.js +5 -0
- package/dist/lib/pullRequests.d.ts +23 -0
- package/dist/lib/pullRequests.d.ts.map +1 -0
- package/dist/lib/pullRequests.js +74 -0
- package/dist/lib/runState.d.ts +15 -0
- package/dist/lib/runState.d.ts.map +1 -1
- package/dist/lib/runState.js +11 -0
- package/dist/lib/ticketSource.d.ts +7 -0
- package/dist/lib/ticketSource.d.ts.map +1 -1
- package/dist/lib/workspaces.d.ts +2 -1
- package/dist/lib/workspaces.d.ts.map +1 -1
- package/dist/lib/workspaces.js +5 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,28 +20,17 @@
|
|
|
20
20
|
$ crew status HRD-446
|
|
21
21
|
groundcrew status HRD-446
|
|
22
22
|
========================
|
|
23
|
-
ticket: hrd-446
|
|
23
|
+
ticket: hrd-446 in-progress https://linear.app/example/issue/HRD-446
|
|
24
|
+
title: Add retry logic to the sync job
|
|
25
|
+
run: running; model=claude; updated=2026-05-26T00:01:00.000Z; resumes=0
|
|
26
|
+
workspace: live
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
projectDir: /dev/workspaces
|
|
28
|
-
repositories: owner/repo
|
|
29
|
-
git: remote=origin; defaultBranch=main
|
|
30
|
-
workspaceKind: auto
|
|
31
|
-
|
|
32
|
-
Worktree state
|
|
33
|
-
--------------
|
|
28
|
+
Worktrees
|
|
29
|
+
---------
|
|
34
30
|
- owner/repo host
|
|
35
31
|
branch: rocky-hrd-446
|
|
32
|
+
dir: /dev/workspaces/owner/repo-hrd-446
|
|
36
33
|
git: dirty (2 modified, 1 untracked)
|
|
37
|
-
|
|
38
|
-
Workspace probe
|
|
39
|
-
---------------
|
|
40
|
-
live: yes
|
|
41
|
-
|
|
42
|
-
Last Linear status
|
|
43
|
-
------------------
|
|
44
|
-
In Progress (state.type=started) — Add retry logic to the sync job
|
|
45
34
|
```
|
|
46
35
|
|
|
47
36
|
## Why
|
|
@@ -217,9 +206,9 @@ Replace `claude` with the sbx agent for the model and `<projectDir>` with `works
|
|
|
217
206
|
|
|
218
207
|
## Inspecting status
|
|
219
208
|
|
|
220
|
-
`crew status <TICKET>` prints a read-only snapshot for one ticket:
|
|
209
|
+
`crew status <TICKET>` prints a read-only snapshot for one ticket: cached title/URL when present, recorded run state, live workspace presence, matching worktrees, git dirtiness, PR links for matching branches, recent log lines when present, and the ticket status from the configured ticket source. It does not recover, tear down, resume, or mutate any local/remote state.
|
|
221
210
|
|
|
222
|
-
`crew status` with no ticket prints the current inventory: known worktrees with workspace/run-state
|
|
211
|
+
`crew status` with no ticket prints the current inventory: known worktrees with cached ticket metadata, workspace/run-state agreement, attach hints, worktree paths, PR links, and stray sessions reported by the configured backend. Local worktree/session diagnostics are printed before ticket-source fetches complete; when the source fetch succeeds, status also prints slot usage plus Queue/Blocked sections for eligible Todo tickets. If the source fetch fails, Queue shows `unavailable: <reason>` and the slots line is omitted.
|
|
223
212
|
|
|
224
213
|
Use `crew cleanup <TICKET>` to tear down stale worktrees and `crew resume <TICKET>` to reopen preserved work. Status is intentionally informational only.
|
|
225
214
|
|
|
@@ -233,26 +222,22 @@ Use `crew cleanup <TICKET>` to tear down stale worktrees and `crew resume <TICKE
|
|
|
233
222
|
```text
|
|
234
223
|
groundcrew status HRD-442
|
|
235
224
|
=========================
|
|
236
|
-
ticket: hrd-442
|
|
225
|
+
ticket: hrd-442 in-progress https://linear.app/example/issue/HRD-442
|
|
226
|
+
title: Multi-event extractor: year inference can produce date_start > date_end
|
|
227
|
+
run: running; model=claude; updated=2026-05-26T00:01:00.000Z; resumes=0
|
|
228
|
+
workspace: live
|
|
237
229
|
|
|
238
|
-
|
|
230
|
+
Worktrees
|
|
239
231
|
---------
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
Worktree
|
|
243
|
-
--------
|
|
244
|
-
- herds-social host
|
|
232
|
+
- herds-social/herds host
|
|
245
233
|
branch: paul-hrd-442
|
|
246
|
-
dir: /
|
|
234
|
+
dir: /dev/workspaces/herds-social/herds-hrd-442
|
|
247
235
|
git: dirty (0 modified, 1 untracked)
|
|
236
|
+
pr: https://github.com/herds-social/herds/pull/224 (open)
|
|
248
237
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
Last Linear status
|
|
254
|
-
------------------
|
|
255
|
-
In Progress (state.type=started) — Multi-event extractor: year inference can produce date_start > date_end
|
|
238
|
+
Recent logs
|
|
239
|
+
-----------
|
|
240
|
+
[10:15:30] Workspace "hrd-442" launched
|
|
256
241
|
```
|
|
257
242
|
|
|
258
243
|
</details>
|
|
@@ -463,7 +448,7 @@ op run --env-file .env.1password -- crew doctor
|
|
|
463
448
|
|
|
464
449
|
## Troubleshooting
|
|
465
450
|
|
|
466
|
-
First stop for "what exists locally right now": `crew status <ticket>` shows the ticket's worktrees, workspace presence, run state, logs, and
|
|
451
|
+
First stop for "what exists locally right now": `crew status <ticket>` shows the ticket's worktrees, workspace presence, run state, logs, and ticket-source status. Use `crew doctor` when you need to verify host setup.
|
|
467
452
|
|
|
468
453
|
<details>
|
|
469
454
|
<summary>Safehouse-already-wrapped commands are not re-wrapped</summary>
|
|
@@ -1 +1 @@
|
|
|
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,EAGf,KAAK,KAAK,EAEX,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,
|
|
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,EAGf,KAAK,KAAK,EAEX,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,CAqNjE;AAUD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
|
|
@@ -60,7 +60,11 @@ export function createDispatcher(deps) {
|
|
|
60
60
|
repository: issue.repository,
|
|
61
61
|
ticket: ticketId,
|
|
62
62
|
model: issue.model,
|
|
63
|
-
details: {
|
|
63
|
+
details: {
|
|
64
|
+
title: issue.title,
|
|
65
|
+
description: issue.description,
|
|
66
|
+
...(issue.url === undefined ? {} : { url: issue.url }),
|
|
67
|
+
},
|
|
64
68
|
};
|
|
65
69
|
await (signal === undefined
|
|
66
70
|
? setupWorkspace(config, setupOptions)
|
|
@@ -2,6 +2,8 @@ import { type ResolvedConfig } from "../lib/config.ts";
|
|
|
2
2
|
export interface TicketDetails {
|
|
3
3
|
title: string;
|
|
4
4
|
description: string;
|
|
5
|
+
/** Direct web URL for the ticket; cached into RunState when present. */
|
|
6
|
+
url?: string;
|
|
5
7
|
}
|
|
6
8
|
export interface SetupWorkspaceOptions {
|
|
7
9
|
ticket: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAiBnE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAiBnE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CA6Gf;AAqHD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
|
|
@@ -102,6 +102,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
102
102
|
branchName,
|
|
103
103
|
workspaceName: ticket,
|
|
104
104
|
state: "running",
|
|
105
|
+
title: ticketDetails.title,
|
|
106
|
+
...(ticketDetails.url === undefined ? {} : { url: ticketDetails.url }),
|
|
105
107
|
});
|
|
106
108
|
log(`Workspace "${ticket}" launched (${model})`);
|
|
107
109
|
log(` Worktree: ${launchDir}`);
|
|
@@ -122,6 +124,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
122
124
|
workspaceName: ticket,
|
|
123
125
|
state: "failed-to-launch",
|
|
124
126
|
detail: errorMessage(error),
|
|
127
|
+
title: options.details.title,
|
|
128
|
+
...(options.details.url === undefined ? {} : { url: options.details.url }),
|
|
125
129
|
});
|
|
126
130
|
throw error;
|
|
127
131
|
}
|
|
@@ -167,7 +171,9 @@ function recordRunStateBestEffort(arguments_) {
|
|
|
167
171
|
branchName: arguments_.branchName,
|
|
168
172
|
workspaceName: arguments_.workspaceName,
|
|
169
173
|
state: arguments_.state,
|
|
174
|
+
title: arguments_.title,
|
|
170
175
|
...(arguments_.detail === undefined ? {} : { detail: arguments_.detail }),
|
|
176
|
+
...(arguments_.url === undefined ? {} : { url: arguments_.url }),
|
|
171
177
|
},
|
|
172
178
|
});
|
|
173
179
|
}
|
|
@@ -242,7 +248,11 @@ export async function setupWorkspaceCli(ticket, options = {}) {
|
|
|
242
248
|
ticket: naturalId,
|
|
243
249
|
repository: resolved.repository,
|
|
244
250
|
model: resolved.model,
|
|
245
|
-
details: {
|
|
251
|
+
details: {
|
|
252
|
+
title: resolved.title,
|
|
253
|
+
description: resolved.description,
|
|
254
|
+
...(resolved.url === undefined ? {} : { url: resolved.url }),
|
|
255
|
+
},
|
|
246
256
|
});
|
|
247
257
|
await board.markInProgress(resolved);
|
|
248
258
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAanE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6gBD,wBAAsB,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/F;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createBoard } from "../lib/board.js";
|
|
3
|
+
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
4
4
|
import { loadConfig } from "../lib/config.js";
|
|
5
|
+
import { findPullRequestsForBranch } from "../lib/pullRequests.js";
|
|
5
6
|
import { readRunState } from "../lib/runState.js";
|
|
7
|
+
import { isGroundcrewIssue, naturalIdFromCanonical, } from "../lib/ticketSource.js";
|
|
6
8
|
import { errorMessage, withLogOutputSuppressed, writeOutput } from "../lib/util.js";
|
|
7
9
|
import { workspaces } from "../lib/workspaces.js";
|
|
8
10
|
import { worktrees } from "../lib/worktrees.js";
|
|
@@ -25,16 +27,6 @@ function writeSection(title) {
|
|
|
25
27
|
writeOutput(title);
|
|
26
28
|
writeOutput("-".repeat(title.length));
|
|
27
29
|
}
|
|
28
|
-
function writeConfigSnapshot(config) {
|
|
29
|
-
writeSection("Config snapshot");
|
|
30
|
-
writeOutput(`projectDir: ${config.workspace.projectDir}`);
|
|
31
|
-
writeOutput(`repositories: ${config.workspace.knownRepositories.join(", ")}`);
|
|
32
|
-
writeOutput(`git: remote=${config.git.remote}; defaultBranch=${config.git.defaultBranch}`);
|
|
33
|
-
writeOutput(`workspaceKind: ${config.workspaceKind}`);
|
|
34
|
-
writeOutput(`local.runner: ${config.local.runner}`);
|
|
35
|
-
writeOutput(`models: default=${config.models.default}; enabled=${Object.keys(config.models.definitions).join(", ")}`);
|
|
36
|
-
writeOutput(`logFile: ${config.logging.file}`);
|
|
37
|
-
}
|
|
38
30
|
function formatDirtiness(dirtiness) {
|
|
39
31
|
if (dirtiness.kind === "dirty") {
|
|
40
32
|
return `dirty (${dirtiness.modified} modified, ${dirtiness.untracked} untracked)`;
|
|
@@ -42,7 +34,7 @@ function formatDirtiness(dirtiness) {
|
|
|
42
34
|
return dirtiness.kind;
|
|
43
35
|
}
|
|
44
36
|
async function writeTicketWorktrees(config, ticket) {
|
|
45
|
-
writeSection("
|
|
37
|
+
writeSection("Worktrees");
|
|
46
38
|
const entries = worktrees.findByTicket(config, ticket);
|
|
47
39
|
if (entries.length === 0) {
|
|
48
40
|
writeOutput("(none)");
|
|
@@ -50,12 +42,21 @@ async function writeTicketWorktrees(config, ticket) {
|
|
|
50
42
|
}
|
|
51
43
|
for (const entry of entries) {
|
|
52
44
|
// oxlint-disable-next-line no-await-in-loop -- status output is easier to read in worktree order.
|
|
53
|
-
const dirtiness = await worktrees.probeWorkingTree({
|
|
45
|
+
const dirtiness = await worktrees.probeWorkingTree({
|
|
46
|
+
worktreeDir: entry.dir,
|
|
47
|
+
});
|
|
48
|
+
// oxlint-disable-next-line no-await-in-loop -- one gh lookup per worktree is acceptable; multi-worktree-per-ticket is rare.
|
|
49
|
+
const prs = await findPullRequestsForBranch({
|
|
50
|
+
repository: entry.repository,
|
|
51
|
+
branchName: entry.branchName,
|
|
52
|
+
});
|
|
54
53
|
writeOutput(`- ${entry.repository} ${entry.kind}`);
|
|
55
|
-
writeOutput(` ticket: ${entry.ticket}`);
|
|
56
54
|
writeOutput(` branch: ${entry.branchName}`);
|
|
57
55
|
writeOutput(` dir: ${entry.dir}`);
|
|
58
56
|
writeOutput(` git: ${formatDirtiness(dirtiness)}`);
|
|
57
|
+
if (prs.length > 0) {
|
|
58
|
+
writeOutput(` pr: ${formatPullRequests(prs)}`);
|
|
59
|
+
}
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
function workspaceProbeUnavailableLine(probe) {
|
|
@@ -63,13 +64,11 @@ function workspaceProbeUnavailableLine(probe) {
|
|
|
63
64
|
? "Workspace probe unavailable"
|
|
64
65
|
: `Workspace probe unavailable: ${errorMessage(probe.error)}`;
|
|
65
66
|
}
|
|
66
|
-
function
|
|
67
|
-
writeSection("Workspace probe");
|
|
67
|
+
function ticketWorkspaceText(probe, ticket) {
|
|
68
68
|
if (probe.kind === "unavailable") {
|
|
69
|
-
|
|
70
|
-
return;
|
|
69
|
+
return workspaceProbeUnavailableLine(probe);
|
|
71
70
|
}
|
|
72
|
-
|
|
71
|
+
return probe.names.has(ticket) ? "live" : "not live";
|
|
73
72
|
}
|
|
74
73
|
function formatRunState(state) {
|
|
75
74
|
if (state === undefined) {
|
|
@@ -93,15 +92,56 @@ function recentTicketLogLines(config, ticket) {
|
|
|
93
92
|
.filter((line) => pattern.test(line))
|
|
94
93
|
.slice(-RECENT_LOG_LINE_COUNT);
|
|
95
94
|
}
|
|
96
|
-
async function
|
|
95
|
+
async function resolveTicketSource(config, ticket) {
|
|
96
|
+
const board = await buildBoardForStatus(config);
|
|
97
|
+
return await withLogOutputSuppressed(async () => await board.resolveOne(ticket));
|
|
98
|
+
}
|
|
99
|
+
async function readTicketSourceStatus(config, ticket) {
|
|
97
100
|
try {
|
|
98
|
-
const issue = await
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
const issue = await resolveTicketSource(config, ticket);
|
|
102
|
+
if (issue === undefined) {
|
|
103
|
+
return { kind: "not-found" };
|
|
104
|
+
}
|
|
105
|
+
return { kind: "found", issue };
|
|
102
106
|
}
|
|
103
107
|
catch (error) {
|
|
104
|
-
return
|
|
108
|
+
return { kind: "unavailable", reason: errorMessage(error) };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function writeRecentLogs(config, ticket) {
|
|
112
|
+
const logLines = recentTicketLogLines(config, ticket);
|
|
113
|
+
if (logLines.length === 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
writeSection("Recent logs");
|
|
117
|
+
writeOutput(logLines.join("\n"));
|
|
118
|
+
}
|
|
119
|
+
function formatTicketLine(ticket, runState, sourceStatus) {
|
|
120
|
+
const parts = [`ticket: ${ticket}`];
|
|
121
|
+
if (sourceStatus.kind === "found") {
|
|
122
|
+
parts.push(sourceStatus.issue.status);
|
|
123
|
+
}
|
|
124
|
+
const url = sourceStatus.kind === "found" ? (sourceStatus.issue.url ?? runState?.url) : runState?.url;
|
|
125
|
+
if (url !== undefined) {
|
|
126
|
+
parts.push(url);
|
|
127
|
+
}
|
|
128
|
+
if (sourceStatus.kind === "not-found") {
|
|
129
|
+
parts.push("source not found");
|
|
130
|
+
}
|
|
131
|
+
if (sourceStatus.kind === "unavailable") {
|
|
132
|
+
parts.push(`source unavailable: ${sourceStatus.reason}`);
|
|
133
|
+
}
|
|
134
|
+
return parts.join(" ");
|
|
135
|
+
}
|
|
136
|
+
function writeTicketTitle(runState, sourceStatus) {
|
|
137
|
+
const cachedTitle = runState?.title;
|
|
138
|
+
const sourceTitle = sourceStatus.kind === "found" ? sourceStatus.issue.title : undefined;
|
|
139
|
+
const title = cachedTitle ?? sourceTitle;
|
|
140
|
+
if (title !== undefined) {
|
|
141
|
+
writeOutput(`title: ${title}`);
|
|
142
|
+
}
|
|
143
|
+
if (cachedTitle !== undefined && sourceTitle !== undefined && cachedTitle !== sourceTitle) {
|
|
144
|
+
writeOutput(`source title: ${sourceTitle}`);
|
|
105
145
|
}
|
|
106
146
|
}
|
|
107
147
|
async function writeTicketStatus(config, rawTicket) {
|
|
@@ -109,26 +149,118 @@ async function writeTicketStatus(config, rawTicket) {
|
|
|
109
149
|
const displayTicket = ticket.toUpperCase();
|
|
110
150
|
writeOutput(`groundcrew status ${displayTicket}`);
|
|
111
151
|
writeOutput("=".repeat(`groundcrew status ${displayTicket}`.length));
|
|
112
|
-
|
|
113
|
-
|
|
152
|
+
const runState = readRunState(config, ticket);
|
|
153
|
+
const [workspaceProbe, sourceStatus] = await Promise.all([
|
|
154
|
+
withLogOutputSuppressed(async () => await workspaces.probe(config)),
|
|
155
|
+
readTicketSourceStatus(config, ticket),
|
|
156
|
+
]);
|
|
157
|
+
writeOutput(formatTicketLine(ticket, runState, sourceStatus));
|
|
158
|
+
writeTicketTitle(runState, sourceStatus);
|
|
159
|
+
writeOutput(`run: ${formatRunState(runState)}`);
|
|
160
|
+
writeOutput(`workspace: ${ticketWorkspaceText(workspaceProbe, ticket)}`);
|
|
114
161
|
await writeTicketWorktrees(config, ticket);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
162
|
+
writeRecentLogs(config, ticket);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Wall-clock elapsed time since the run was first recorded (RunState.createdAt
|
|
166
|
+
* is preserved across resume/interrupt). Returns undefined when the row isn't
|
|
167
|
+
* actively running, when no run state exists, or when the timestamp cannot
|
|
168
|
+
* be parsed.
|
|
169
|
+
*/
|
|
170
|
+
function runStateDurationMs(runState, now) {
|
|
171
|
+
if (runState === undefined) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (runState.state !== "running" && runState.state !== "resumed") {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
const created = Date.parse(runState.createdAt);
|
|
178
|
+
if (Number.isNaN(created)) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
return now.getTime() - created;
|
|
182
|
+
}
|
|
183
|
+
const MS_PER_MINUTE = 60_000;
|
|
184
|
+
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
185
|
+
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
186
|
+
function formatDuration(ms) {
|
|
187
|
+
if (ms < MS_PER_MINUTE) {
|
|
188
|
+
return "<1m";
|
|
189
|
+
}
|
|
190
|
+
if (ms < MS_PER_HOUR) {
|
|
191
|
+
return `${Math.floor(ms / MS_PER_MINUTE)}m`;
|
|
192
|
+
}
|
|
193
|
+
if (ms < MS_PER_DAY) {
|
|
194
|
+
const hours = Math.floor(ms / MS_PER_HOUR);
|
|
195
|
+
const minutes = Math.floor((ms - hours * MS_PER_HOUR) / MS_PER_MINUTE);
|
|
196
|
+
return minutes === 0 ? `${hours}h` : `${hours}h ${minutes}m`;
|
|
197
|
+
}
|
|
198
|
+
const days = Math.floor(ms / MS_PER_DAY);
|
|
199
|
+
const hours = Math.floor((ms - days * MS_PER_DAY) / MS_PER_HOUR);
|
|
200
|
+
return hours === 0 ? `${days}d` : `${days}d ${hours}h`;
|
|
124
201
|
}
|
|
125
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Combined human-readable state for the inventory row. Surfaces RunState
|
|
204
|
+
* lifecycle and flags the two interesting disagreements with the workspace
|
|
205
|
+
* probe — `(session dead)` when we recorded a running dispatch but no
|
|
206
|
+
* session is alive, and `(stray session)` when a session is alive without
|
|
207
|
+
* any recorded dispatch. `probe.kind === "unavailable"` is treated as
|
|
208
|
+
* "we don't know" and never produces a suffix. When the row is actively
|
|
209
|
+
* running, appends the elapsed wall-clock time since dispatch.
|
|
210
|
+
*/
|
|
211
|
+
function inventoryStateText(runState, probe, ticket, now) {
|
|
212
|
+
const lifecycle = runState?.state ?? "idle";
|
|
213
|
+
const duration = runStateDurationMs(runState, now);
|
|
214
|
+
const flags = [];
|
|
215
|
+
if (probe.kind === "ok") {
|
|
216
|
+
const sessionLive = probe.names.has(ticket);
|
|
217
|
+
if (lifecycle === "idle" && sessionLive) {
|
|
218
|
+
flags.push("stray session");
|
|
219
|
+
}
|
|
220
|
+
if ((lifecycle === "running" || lifecycle === "resumed") && !sessionLive) {
|
|
221
|
+
flags.push("session dead");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (duration !== undefined) {
|
|
225
|
+
flags.push(formatDuration(duration));
|
|
226
|
+
}
|
|
227
|
+
return flags.length === 0 ? lifecycle : `${lifecycle} (${flags.join(", ")})`;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Hint command for inventory rows where the run-state and the workspace
|
|
231
|
+
* probe disagree. Returned commands are safe defaults; the user is free to
|
|
232
|
+
* ignore them and use `attach:` + `pr:` to investigate first.
|
|
233
|
+
*
|
|
234
|
+
* - Stray session (live session, no run-state record) → `crew cleanup` to
|
|
235
|
+
* tear down the orphaned worktree + close the session.
|
|
236
|
+
* - Session dead (run-state says running/resumed, no live session) →
|
|
237
|
+
* `crew resume` to bring the agent back; the worktree is preserved.
|
|
238
|
+
*
|
|
239
|
+
* No hint when the probe is unavailable (we genuinely don't know whether
|
|
240
|
+
* there's a disagreement) or when the row is healthy.
|
|
241
|
+
*/
|
|
242
|
+
function inventoryHint(runState, probe, ticket) {
|
|
126
243
|
if (probe.kind === "unavailable") {
|
|
127
|
-
return
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
const lifecycle = runState?.state ?? "idle";
|
|
247
|
+
const sessionLive = probe.names.has(ticket);
|
|
248
|
+
if (lifecycle === "idle" && sessionLive) {
|
|
249
|
+
return `run 'crew cleanup ${ticket}' to clear this stray session`;
|
|
128
250
|
}
|
|
129
|
-
|
|
251
|
+
if ((lifecycle === "running" || lifecycle === "resumed") && !sessionLive) {
|
|
252
|
+
return `run 'crew resume ${ticket}' to bring the session back`;
|
|
253
|
+
}
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const INVENTORY_LABEL_WIDTH = "worktree:".length;
|
|
257
|
+
function inventoryField(label, value) {
|
|
258
|
+
return ` ${`${label}:`.padEnd(INVENTORY_LABEL_WIDTH)} ${value}`;
|
|
259
|
+
}
|
|
260
|
+
function formatPullRequests(prs) {
|
|
261
|
+
return prs.map((pr) => `${pr.url} (${pr.state})`).join(", ");
|
|
130
262
|
}
|
|
131
|
-
function writeInventoryWorktrees(config, probe) {
|
|
263
|
+
async function writeInventoryWorktrees(config, probe) {
|
|
132
264
|
writeSection("Worktrees");
|
|
133
265
|
const entries = worktrees
|
|
134
266
|
.list(config)
|
|
@@ -137,31 +269,179 @@ function writeInventoryWorktrees(config, probe) {
|
|
|
137
269
|
writeOutput("(none)");
|
|
138
270
|
return;
|
|
139
271
|
}
|
|
272
|
+
const accessHints = await collectAccessHints(config, entries);
|
|
273
|
+
const pullRequests = await collectPullRequests(entries);
|
|
140
274
|
const runStates = new Map();
|
|
141
|
-
|
|
275
|
+
const now = new Date();
|
|
276
|
+
for (const [index, entry] of entries.entries()) {
|
|
142
277
|
if (!runStates.has(entry.ticket)) {
|
|
143
278
|
runStates.set(entry.ticket, readRunState(config, entry.ticket));
|
|
144
279
|
}
|
|
145
280
|
const runState = runStates.get(entry.ticket);
|
|
146
|
-
|
|
147
|
-
|
|
281
|
+
const accessHint = accessHints.get(entry.ticket);
|
|
282
|
+
// `collectPullRequests` guarantees an entry for every (repo, branch)
|
|
283
|
+
// pair seen in `entries`; the lookup always returns the array.
|
|
284
|
+
/* v8 ignore next @preserve -- defensive fallback for a Map key that collectPullRequests always populates */
|
|
285
|
+
const prs = pullRequests.get(pullRequestKey(entry.repository, entry.branchName)) ?? [];
|
|
286
|
+
if (index > 0) {
|
|
287
|
+
writeOutput();
|
|
288
|
+
}
|
|
289
|
+
writeOutput(runState?.url === undefined ? entry.ticket : `${entry.ticket} ${runState.url}`);
|
|
290
|
+
if (runState?.title !== undefined) {
|
|
291
|
+
writeOutput(inventoryField("title", runState.title));
|
|
292
|
+
}
|
|
293
|
+
writeOutput(inventoryField("state", inventoryStateText(runState, probe, entry.ticket, now)));
|
|
294
|
+
writeOutput(inventoryField("repo", entry.repository));
|
|
295
|
+
writeOutput(inventoryField("worktree", entry.dir));
|
|
296
|
+
if (accessHint !== undefined) {
|
|
297
|
+
writeOutput(inventoryField("attach", accessHint.command));
|
|
298
|
+
}
|
|
299
|
+
if (prs.length > 0) {
|
|
300
|
+
writeOutput(inventoryField("pr", formatPullRequests(prs)));
|
|
301
|
+
}
|
|
302
|
+
const hint = inventoryHint(runState, probe, entry.ticket);
|
|
303
|
+
if (hint !== undefined) {
|
|
304
|
+
writeOutput(inventoryField("hint", hint));
|
|
305
|
+
}
|
|
148
306
|
}
|
|
149
307
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
308
|
+
function pullRequestKey(repository, branchName) {
|
|
309
|
+
return `${repository} ${branchName}`;
|
|
310
|
+
}
|
|
311
|
+
async function collectAccessHints(config, entries) {
|
|
312
|
+
const uniqueTickets = [...new Set(entries.map((entry) => entry.ticket))];
|
|
313
|
+
const results = await Promise.allSettled(uniqueTickets.map(async (ticket) => await workspaces.accessHint(config, ticket)));
|
|
314
|
+
return new Map(uniqueTickets.map((ticket, index) => {
|
|
315
|
+
const result = results[index];
|
|
316
|
+
return [ticket, result?.status === "fulfilled" ? result.value : undefined];
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
async function collectPullRequests(entries) {
|
|
320
|
+
// Same-(repo, branch) entries collapse to one lookup; later inserts
|
|
321
|
+
// overwrite earlier ones with the same identifier, which is fine because
|
|
322
|
+
// gh would return the same PR list for both.
|
|
323
|
+
const uniqueKeys = new Map();
|
|
324
|
+
for (const entry of entries) {
|
|
325
|
+
uniqueKeys.set(pullRequestKey(entry.repository, entry.branchName), {
|
|
326
|
+
repository: entry.repository,
|
|
327
|
+
branchName: entry.branchName,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
const results = await Promise.allSettled([...uniqueKeys.entries()].map(async ([key, { repository, branchName }]) => {
|
|
331
|
+
const prs = await findPullRequestsForBranch({ repository, branchName });
|
|
332
|
+
return [key, prs];
|
|
333
|
+
}));
|
|
334
|
+
return new Map([...uniqueKeys.keys()].map((key, index) => {
|
|
335
|
+
const result = results[index];
|
|
336
|
+
return [key, result?.status === "fulfilled" ? result.value[1] : []];
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
function writeStraySessions(probe, worktreeTickets) {
|
|
152
340
|
if (probe.kind === "unavailable") {
|
|
341
|
+
// Surface probe failures so the user knows we couldn't classify strays
|
|
342
|
+
// (silently dropping the section would hide that diagnostic).
|
|
343
|
+
writeSection("Stray sessions");
|
|
153
344
|
writeOutput(workspaceProbeUnavailableLine(probe));
|
|
154
345
|
return;
|
|
155
346
|
}
|
|
156
|
-
const
|
|
157
|
-
|
|
347
|
+
const strays = [...probe.names].filter((name) => !worktreeTickets.has(name)).toSorted();
|
|
348
|
+
if (strays.length === 0) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
writeSection("Stray sessions");
|
|
352
|
+
writeOutput(strays.join("\n"));
|
|
353
|
+
}
|
|
354
|
+
function isTodoSourceIssue(issue) {
|
|
355
|
+
return issue.status === "todo";
|
|
356
|
+
}
|
|
357
|
+
function hasOpenBlocker(issue) {
|
|
358
|
+
return issue.blockers.some((b) => b.status !== "done");
|
|
359
|
+
}
|
|
360
|
+
function describeOpenBlockers(issue) {
|
|
361
|
+
return issue.blockers
|
|
362
|
+
.filter((b) => b.status !== "done")
|
|
363
|
+
.map((b) => `${naturalIdFromCanonical(b.id)} (${b.nativeStatus ?? b.status})`)
|
|
364
|
+
.join(", ");
|
|
365
|
+
}
|
|
366
|
+
function writeQueueIssue(issue) {
|
|
367
|
+
const naturalId = naturalIdFromCanonical(issue.id);
|
|
368
|
+
writeOutput(issue.url === undefined ? naturalId : `${naturalId} ${issue.url}`);
|
|
369
|
+
writeOutput(inventoryField("title", issue.title));
|
|
370
|
+
writeOutput(inventoryField("repo", issue.repository));
|
|
371
|
+
writeOutput(inventoryField("model", issue.model));
|
|
372
|
+
}
|
|
373
|
+
async function buildBoardForStatus(config) {
|
|
374
|
+
const sources = await buildSources(sourcesFromConfig(config), { globalConfig: config });
|
|
375
|
+
return createBoard(sources);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Single board fetch used by both the slot count header and the
|
|
379
|
+
* Queue/Blocked sections. `sourcesFromConfig` prepends an implicit Linear
|
|
380
|
+
* source when none are configured, so we always attempt; failures
|
|
381
|
+
* (e.g., missing API key) are captured and rendered later as
|
|
382
|
+
* `unavailable: ...` in the Queue section.
|
|
383
|
+
*/
|
|
384
|
+
async function fetchBoardForStatus(config) {
|
|
385
|
+
try {
|
|
386
|
+
const board = await buildBoardForStatus(config);
|
|
387
|
+
const { issues } = await withLogOutputSuppressed(async () => await board.fetch());
|
|
388
|
+
return { kind: "ok", issues };
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
return { kind: "error", error };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function writeQueueSections(boardResult) {
|
|
395
|
+
if (boardResult.kind === "error") {
|
|
396
|
+
writeSection("Queue");
|
|
397
|
+
writeOutput(`unavailable: ${errorMessage(boardResult.error)}`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// Only groundcrew-eligible Todos are dispatchable; non-eligible ones lack
|
|
401
|
+
// a repo or model, so `crew run` would skip them.
|
|
402
|
+
const todos = boardResult.issues.filter(isTodoSourceIssue).filter(isGroundcrewIssue);
|
|
403
|
+
const ready = todos.filter((i) => !hasOpenBlocker(i));
|
|
404
|
+
const blocked = todos.filter(hasOpenBlocker);
|
|
405
|
+
// Hide the section entirely when nothing's queued and nothing's blocked.
|
|
406
|
+
if (ready.length > 0) {
|
|
407
|
+
writeSection("Queue");
|
|
408
|
+
for (const [index, issue] of ready.entries()) {
|
|
409
|
+
if (index > 0) {
|
|
410
|
+
writeOutput();
|
|
411
|
+
}
|
|
412
|
+
writeQueueIssue(issue);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (blocked.length > 0) {
|
|
416
|
+
writeSection("Blocked");
|
|
417
|
+
for (const [index, issue] of blocked.entries()) {
|
|
418
|
+
if (index > 0) {
|
|
419
|
+
writeOutput();
|
|
420
|
+
}
|
|
421
|
+
writeQueueIssue(issue);
|
|
422
|
+
writeOutput(inventoryField("blocked by", describeOpenBlockers(issue)));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function inProgressCount(issues) {
|
|
427
|
+
return issues.filter((issue) => issue.status === "in-progress").length;
|
|
158
428
|
}
|
|
159
429
|
async function writeInventoryStatus(config) {
|
|
160
|
-
|
|
161
|
-
|
|
430
|
+
// Banner ("groundcrew status\n=================") dropped: the command
|
|
431
|
+
// you just ran already tells you what report you're looking at, and the
|
|
432
|
+
// section headers (`Worktrees`, `Queue`, etc.) carry the visual anchors.
|
|
433
|
+
const boardResultPromise = fetchBoardForStatus(config);
|
|
162
434
|
const probe = await withLogOutputSuppressed(async () => await workspaces.probe(config));
|
|
163
|
-
writeInventoryWorktrees(config, probe);
|
|
164
|
-
|
|
435
|
+
await writeInventoryWorktrees(config, probe);
|
|
436
|
+
const worktreeTickets = new Set(worktrees.list(config).map((entry) => entry.ticket));
|
|
437
|
+
writeStraySessions(probe, worktreeTickets);
|
|
438
|
+
const boardResult = await boardResultPromise;
|
|
439
|
+
if (boardResult.kind === "ok") {
|
|
440
|
+
const used = inProgressCount(boardResult.issues);
|
|
441
|
+
writeOutput();
|
|
442
|
+
writeOutput(`slots: ${used}/${config.orchestrator.maximumInProgress} used`);
|
|
443
|
+
}
|
|
444
|
+
writeQueueSections(boardResult);
|
|
165
445
|
}
|
|
166
446
|
export async function status(config, options = {}) {
|
|
167
447
|
const ticket = options.ticket?.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAIL,KAAK,KAAK,IAAI,cAAc,EAE5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAKL,KAAK,KAAK,IAAI,WAAW,EAE1B,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,YAAY,EAAE,MAAM,CAAC;CACtB;AAkFD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAIL,KAAK,KAAK,IAAI,cAAc,EAE5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAKL,KAAK,KAAK,IAAI,WAAW,EAE1B,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,YAAY,EAAE,MAAM,CAAC;CACtB;AAkFD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAuB7F;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,cAAc,GACtB,YAAY,CAgFd"}
|
|
@@ -107,6 +107,7 @@ export function toCanonicalIssue(linearIssue, sourceName) {
|
|
|
107
107
|
updatedAt: linearIssue.updatedAt,
|
|
108
108
|
blockers: linearIssue.blockers.map((b) => toCanonicalBlocker(b, sourceName)),
|
|
109
109
|
hasMoreBlockers: linearIssue.hasMoreBlockers,
|
|
110
|
+
url: linearIssue.url,
|
|
110
111
|
sourceRef,
|
|
111
112
|
};
|
|
112
113
|
}
|
|
@@ -172,6 +173,7 @@ export function createLinearTicketSource(config, context) {
|
|
|
172
173
|
updatedAt: new Date().toISOString(),
|
|
173
174
|
blockers: [],
|
|
174
175
|
hasMoreBlockers: false,
|
|
176
|
+
url: resolved.url,
|
|
175
177
|
sourceRef,
|
|
176
178
|
};
|
|
177
179
|
},
|
|
@@ -46,6 +46,8 @@ export interface Issue {
|
|
|
46
46
|
teamId: string;
|
|
47
47
|
blockers: Blocker[];
|
|
48
48
|
hasMoreBlockers: boolean;
|
|
49
|
+
/** Linear `Issue.url` — direct web link to the ticket. */
|
|
50
|
+
url: string;
|
|
49
51
|
}
|
|
50
52
|
/**
|
|
51
53
|
* `Issue` narrowed to "this ticket is for groundcrew". Consumers operate on
|
|
@@ -113,6 +115,7 @@ interface ResolvedIssue {
|
|
|
113
115
|
stateType: string;
|
|
114
116
|
status: string;
|
|
115
117
|
statusId: string;
|
|
118
|
+
url: string;
|
|
116
119
|
}
|
|
117
120
|
export interface RawLinearIssue {
|
|
118
121
|
uuid: string;
|
|
@@ -135,6 +138,8 @@ export interface RawLinearIssue {
|
|
|
135
138
|
* reporting "would dispatch."
|
|
136
139
|
*/
|
|
137
140
|
hasChildren: boolean;
|
|
141
|
+
/** Linear `Issue.url` — direct web link to the ticket. */
|
|
142
|
+
url: string;
|
|
138
143
|
}
|
|
139
144
|
export declare function fetchBlockersForTicket(arguments_: {
|
|
140
145
|
client: LinearClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAYpC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,8FAA8F;IAC9F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,6 +84,7 @@ async function fetchBoard(client, config) {
|
|
|
84
84
|
title
|
|
85
85
|
description
|
|
86
86
|
updatedAt
|
|
87
|
+
url
|
|
87
88
|
state { id name type }
|
|
88
89
|
team { id key }
|
|
89
90
|
assignee { name }
|
|
@@ -177,6 +178,7 @@ function buildLinearIssue(input) {
|
|
|
177
178
|
repository: input.repository,
|
|
178
179
|
model: input.model,
|
|
179
180
|
teamId: input.teamId,
|
|
181
|
+
url: input.url,
|
|
180
182
|
blockers: blockersFromRelations(input.inverseRelations?.nodes ?? []),
|
|
181
183
|
hasMoreBlockers: input.inverseRelations?.pageInfo.hasNextPage ?? false,
|
|
182
184
|
};
|
|
@@ -209,6 +211,7 @@ function issueFromNode(node, config) {
|
|
|
209
211
|
repository,
|
|
210
212
|
model,
|
|
211
213
|
teamId: node.team?.id ?? "",
|
|
214
|
+
url: node.url,
|
|
212
215
|
inverseRelations: node.inverseRelations,
|
|
213
216
|
});
|
|
214
217
|
}
|
|
@@ -255,6 +258,7 @@ export async function fetchRawLinearIssue(arguments_) {
|
|
|
255
258
|
id
|
|
256
259
|
title
|
|
257
260
|
description
|
|
261
|
+
url
|
|
258
262
|
team { id }
|
|
259
263
|
state { id name type }
|
|
260
264
|
children { nodes { id } }
|
|
@@ -295,6 +299,7 @@ export async function fetchRawLinearIssue(arguments_) {
|
|
|
295
299
|
blockers: blockersFromRelations(issue.inverseRelations?.nodes ?? []),
|
|
296
300
|
hasMoreBlockers: issue.inverseRelations?.pageInfo.hasNextPage ?? false,
|
|
297
301
|
hasChildren: (issue.children?.nodes.length ?? 0) > 0,
|
|
302
|
+
url: issue.url,
|
|
298
303
|
};
|
|
299
304
|
}
|
|
300
305
|
export async function fetchInProgressIssueCount(arguments_) {
|
|
@@ -371,6 +376,7 @@ export async function fetchResolvedIssue(arguments_) {
|
|
|
371
376
|
stateType: raw.stateType,
|
|
372
377
|
status: raw.stateName,
|
|
373
378
|
statusId: raw.stateId,
|
|
379
|
+
url: raw.url,
|
|
374
380
|
};
|
|
375
381
|
}
|
|
376
382
|
export function warnIfDisabledFallback(ticket, modelResolution, config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAGL,KAAK,KAAK,IAAI,cAAc,EAC5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAyBrB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAGL,KAAK,KAAK,IAAI,cAAc,EAC5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAyBrB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAuB3F;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,cAAc,GACvB,YAAY,CAgFd"}
|
|
@@ -50,6 +50,7 @@ export function toCanonicalIssue(shellIssue, sourceName) {
|
|
|
50
50
|
updatedAt: shellIssue.updatedAt,
|
|
51
51
|
blockers,
|
|
52
52
|
hasMoreBlockers: shellIssue.hasMoreBlockers,
|
|
53
|
+
...(shellIssue.url === undefined ? {} : { url: shellIssue.url }),
|
|
53
54
|
sourceRef: shellIssue.sourceRef,
|
|
54
55
|
};
|
|
55
56
|
}
|
|
@@ -41,6 +41,7 @@ export declare const shellIssueSchema: z.ZodObject<{
|
|
|
41
41
|
nativeStatus: z.ZodOptional<z.ZodString>;
|
|
42
42
|
}, z.core.$strip>>;
|
|
43
43
|
hasMoreBlockers: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
44
|
+
url: z.ZodOptional<z.ZodURL>;
|
|
44
45
|
sourceRef: z.ZodUnknown;
|
|
45
46
|
}, z.core.$strip>;
|
|
46
47
|
export type ShellIssue = z.infer<typeof shellIssueSchema>;
|
|
@@ -76,6 +77,7 @@ export declare const shellFetchOutputSchema: z.ZodArray<z.ZodObject<{
|
|
|
76
77
|
nativeStatus: z.ZodOptional<z.ZodString>;
|
|
77
78
|
}, z.core.$strip>>;
|
|
78
79
|
hasMoreBlockers: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
80
|
+
url: z.ZodOptional<z.ZodURL>;
|
|
79
81
|
sourceRef: z.ZodUnknown;
|
|
80
82
|
}, z.core.$strip>>;
|
|
81
83
|
export declare const shellAdapterConfigSchema: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;iBAwBnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
|
@@ -28,6 +28,11 @@ export const shellIssueSchema = z.object({
|
|
|
28
28
|
updatedAt: z.string(),
|
|
29
29
|
blockers: z.array(shellBlockerSchema),
|
|
30
30
|
hasMoreBlockers: z.boolean().optional().default(false),
|
|
31
|
+
/**
|
|
32
|
+
* Direct web URL for the ticket. Optional so scripts can omit it without
|
|
33
|
+
* breaking; `crew status` falls back to displaying just the id.
|
|
34
|
+
*/
|
|
35
|
+
url: z.url().optional(),
|
|
31
36
|
sourceRef: z.unknown(),
|
|
32
37
|
});
|
|
33
38
|
export const shellFetchOutputSchema = z.array(shellIssueSchema);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort lookup of GitHub pull requests for a worktree branch via the
|
|
3
|
+
* `gh` CLI. `crew status` uses this to surface PR links inline; failures
|
|
4
|
+
* (gh not on PATH, not authenticated, non-GitHub remote) are silent — the
|
|
5
|
+
* caller falls back to omitting the row.
|
|
6
|
+
*/
|
|
7
|
+
export interface PullRequestSummary {
|
|
8
|
+
url: string;
|
|
9
|
+
number: number;
|
|
10
|
+
/** Lowercased lifecycle: "open" | "merged" | "closed". */
|
|
11
|
+
state: string;
|
|
12
|
+
title: string;
|
|
13
|
+
}
|
|
14
|
+
interface LookupArgs {
|
|
15
|
+
/** GitHub `owner/repo` slug. */
|
|
16
|
+
repository: string;
|
|
17
|
+
/** Branch name to filter PRs by. */
|
|
18
|
+
branchName: string;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
export declare function findPullRequestsForBranch(arguments_: LookupArgs): Promise<readonly PullRequestSummary[]>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=pullRequests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pullRequests.d.ts","sourceRoot":"","sources":["../../src/lib/pullRequests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AASD,UAAU,UAAU;IAClB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAgDD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CA2BxC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort lookup of GitHub pull requests for a worktree branch via the
|
|
3
|
+
* `gh` CLI. `crew status` uses this to surface PR links inline; failures
|
|
4
|
+
* (gh not on PATH, not authenticated, non-GitHub remote) are silent — the
|
|
5
|
+
* caller falls back to omitting the row.
|
|
6
|
+
*/
|
|
7
|
+
import { runCommandAsync } from "./commandRunner.js";
|
|
8
|
+
const GH_PR_LIST_LIMIT = 5;
|
|
9
|
+
const STATE_MAP = {
|
|
10
|
+
OPEN: "open",
|
|
11
|
+
MERGED: "merged",
|
|
12
|
+
CLOSED: "closed",
|
|
13
|
+
};
|
|
14
|
+
function parsePullRequests(output) {
|
|
15
|
+
let parsed;
|
|
16
|
+
try {
|
|
17
|
+
parsed = JSON.parse(output);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(parsed)) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const summaries = [];
|
|
26
|
+
for (const entry of parsed) {
|
|
27
|
+
if (!isRawPullRequest(entry)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
summaries.push({
|
|
31
|
+
url: entry.url,
|
|
32
|
+
number: entry.number,
|
|
33
|
+
state: STATE_MAP[entry.state] ?? entry.state.toLowerCase(),
|
|
34
|
+
title: entry.title,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return summaries;
|
|
38
|
+
}
|
|
39
|
+
function isRawPullRequest(value) {
|
|
40
|
+
if (typeof value !== "object" || value === null) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- narrowing untyped JSON.parse output to a record so we can probe its keys
|
|
44
|
+
const record = value;
|
|
45
|
+
return (typeof record["url"] === "string" &&
|
|
46
|
+
typeof record["number"] === "number" &&
|
|
47
|
+
typeof record["state"] === "string" &&
|
|
48
|
+
typeof record["title"] === "string");
|
|
49
|
+
}
|
|
50
|
+
export async function findPullRequestsForBranch(arguments_) {
|
|
51
|
+
const { repository, branchName, signal } = arguments_;
|
|
52
|
+
try {
|
|
53
|
+
const output = await runCommandAsync("gh", [
|
|
54
|
+
"pr",
|
|
55
|
+
"list",
|
|
56
|
+
"--repo",
|
|
57
|
+
repository,
|
|
58
|
+
"--head",
|
|
59
|
+
branchName,
|
|
60
|
+
"--state",
|
|
61
|
+
"all",
|
|
62
|
+
"--limit",
|
|
63
|
+
String(GH_PR_LIST_LIMIT),
|
|
64
|
+
"--json",
|
|
65
|
+
"url,number,state,title",
|
|
66
|
+
], signal === undefined ? {} : { signal });
|
|
67
|
+
return parsePullRequests(output);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// gh not installed / not authenticated / non-GitHub remote / network
|
|
71
|
+
// error / etc. All resolve to "no PR info available" for display.
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/lib/runState.d.ts
CHANGED
|
@@ -13,6 +13,19 @@ export interface RunState {
|
|
|
13
13
|
resumeCount: number;
|
|
14
14
|
reason?: string;
|
|
15
15
|
detail?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Ticket title at dispatch time. Cached so `crew status` can render it
|
|
18
|
+
* without re-hitting the ticket source; lifecycle transitions
|
|
19
|
+
* (resume/interrupt) that omit the field preserve the on-disk value.
|
|
20
|
+
*/
|
|
21
|
+
title?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Direct ticket URL at dispatch time. Same caching rationale as `title`;
|
|
24
|
+
* the source adapter populates it when it can (e.g., Linear), otherwise
|
|
25
|
+
* the field stays undefined and `crew status` falls back to displaying
|
|
26
|
+
* just the ticket id.
|
|
27
|
+
*/
|
|
28
|
+
url?: string;
|
|
16
29
|
}
|
|
17
30
|
export interface RunStateDraft {
|
|
18
31
|
ticket: string;
|
|
@@ -25,6 +38,8 @@ export interface RunStateDraft {
|
|
|
25
38
|
reason?: string;
|
|
26
39
|
detail?: string;
|
|
27
40
|
resumeCount?: number;
|
|
41
|
+
title?: string;
|
|
42
|
+
url?: string;
|
|
28
43
|
}
|
|
29
44
|
export interface RecordRunStateInput {
|
|
30
45
|
config: ResolvedConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runState.d.ts","sourceRoot":"","sources":["../../src/lib/runState.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE3F,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"runState.d.ts","sourceRoot":"","sources":["../../src/lib/runState.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE3F,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC,CAAC,GAAG;QACvD,KAAK,EAAE,iBAAiB,CAAC;KAC1B,CAAC;CACH;AAaD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG,MAAM,CAEjF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5F;AAmFD,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAYzF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CA0BnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,GAAG,SAAS,CAc/E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE3E"}
|
package/dist/lib/runState.js
CHANGED
|
@@ -46,6 +46,8 @@ function parseRunState(value) {
|
|
|
46
46
|
const updatedAt = stringField(value, "updatedAt");
|
|
47
47
|
const reason = stringField(value, "reason");
|
|
48
48
|
const detail = stringField(value, "detail");
|
|
49
|
+
const title = stringField(value, "title");
|
|
50
|
+
const url = stringField(value, "url");
|
|
49
51
|
if (ticket === undefined ||
|
|
50
52
|
repository === undefined ||
|
|
51
53
|
model === undefined ||
|
|
@@ -73,6 +75,8 @@ function parseRunState(value) {
|
|
|
73
75
|
resumeCount,
|
|
74
76
|
...(reason === undefined ? {} : { reason }),
|
|
75
77
|
...(detail === undefined ? {} : { detail }),
|
|
78
|
+
...(title === undefined ? {} : { title }),
|
|
79
|
+
...(url === undefined ? {} : { url }),
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
function writeState(config, state) {
|
|
@@ -100,6 +104,11 @@ export function readRunState(config, ticket) {
|
|
|
100
104
|
export function recordRunState(input) {
|
|
101
105
|
const existing = readRunState(input.config, input.state.ticket);
|
|
102
106
|
const timestamp = nowIso();
|
|
107
|
+
// Resume/interrupt callers don't know the title or url, so they omit
|
|
108
|
+
// them. Fall back to the on-disk value so cached display fields survive
|
|
109
|
+
// transitions.
|
|
110
|
+
const title = input.state.title ?? existing?.title;
|
|
111
|
+
const url = input.state.url ?? existing?.url;
|
|
103
112
|
const state = {
|
|
104
113
|
ticket: ticketKey(input.state.ticket),
|
|
105
114
|
repository: input.state.repository,
|
|
@@ -113,6 +122,8 @@ export function recordRunState(input) {
|
|
|
113
122
|
resumeCount: input.state.resumeCount ?? existing?.resumeCount ?? 0,
|
|
114
123
|
...(input.state.reason === undefined ? {} : { reason: input.state.reason }),
|
|
115
124
|
...(input.state.detail === undefined ? {} : { detail: input.state.detail }),
|
|
125
|
+
...(title === undefined ? {} : { title }),
|
|
126
|
+
...(url === undefined ? {} : { url }),
|
|
116
127
|
};
|
|
117
128
|
writeState(input.config, state);
|
|
118
129
|
return state;
|
|
@@ -67,6 +67,13 @@ export interface Issue {
|
|
|
67
67
|
updatedAt: string;
|
|
68
68
|
blockers: Blocker[];
|
|
69
69
|
hasMoreBlockers: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Direct web URL for the ticket on the source system, when the adapter
|
|
72
|
+
* knows one. `undefined` when the source can't produce a public URL (e.g.,
|
|
73
|
+
* a shell script that omits the `url` field). Display-only — never
|
|
74
|
+
* branched on.
|
|
75
|
+
*/
|
|
76
|
+
url?: string;
|
|
70
77
|
/** Adapter-private. Consumers MUST NOT inspect; only the producing adapter reads it. */
|
|
71
78
|
sourceRef: unknown;
|
|
72
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/workspaces.d.ts
CHANGED
|
@@ -22,12 +22,13 @@ interface ResolveArguments {
|
|
|
22
22
|
}
|
|
23
23
|
export declare function resolveWorkspaceKind(arguments_: ResolveArguments): WorkspaceResolution;
|
|
24
24
|
declare function probeWorkspaces(config: ResolvedConfig, signal?: AbortSignal): Promise<WorkspaceProbe>;
|
|
25
|
+
declare function accessHintForWorkspace(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceAccessHint | undefined>;
|
|
25
26
|
declare function interruptWorkspace(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceInterruptResult>;
|
|
26
27
|
export declare const workspaces: {
|
|
27
28
|
open(config: ResolvedConfig, spec: OpenSpec, signal?: AbortSignal): Promise<void>;
|
|
28
29
|
probe: typeof probeWorkspaces;
|
|
29
30
|
close(config: ResolvedConfig, name: string, signal?: AbortSignal): Promise<WorkspaceCloseResult>;
|
|
30
31
|
interrupt: typeof interruptWorkspace;
|
|
31
|
-
accessHint
|
|
32
|
+
accessHint: typeof accessHintForWorkspace;
|
|
32
33
|
};
|
|
33
34
|
//# sourceMappingURL=workspaces.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,EAAE,aAAa,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,mBAAmB,CAUtF;AAsDD,iBAAe,eAAe,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,iBAAe,kBAAkB,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAenC;AAED,eAAO,MAAM,UAAU;IACf,IAAI,SAAS,cAAc,QAAQ,QAAQ,WAAW,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,KAAK;IACC,KAAK,SACD,cAAc,QAChB,MAAM,WACH,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAIhC,SAAS;
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../src/lib/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,EAAE,aAAa,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,mBAAmB,CAUtF;AAsDD,iBAAe,eAAe,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,iBAAe,sBAAsB,CACnC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAG1C;AAED,iBAAe,kBAAkB,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAenC;AAED,eAAO,MAAM,UAAU;IACf,IAAI,SAAS,cAAc,QAAQ,QAAQ,WAAW,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,KAAK;IACC,KAAK,SACD,cAAc,QAChB,MAAM,WACH,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAIhC,SAAS;IACT,UAAU;CACX,CAAC"}
|
package/dist/lib/workspaces.js
CHANGED
|
@@ -76,6 +76,10 @@ async function probeWorkspaces(config, signal) {
|
|
|
76
76
|
}
|
|
77
77
|
return { kind: "ok", names: new Set(raw.map((ws) => ws.name)) };
|
|
78
78
|
}
|
|
79
|
+
async function accessHintForWorkspace(config, name, signal) {
|
|
80
|
+
const adapter = await adapterFor(config, signal);
|
|
81
|
+
return adapter.accessHint(name);
|
|
82
|
+
}
|
|
79
83
|
async function interruptWorkspace(config, name, signal) {
|
|
80
84
|
const probe = await probeWorkspaces(config, signal);
|
|
81
85
|
if (probe.kind === "unavailable") {
|
|
@@ -103,8 +107,5 @@ export const workspaces = {
|
|
|
103
107
|
return await adapter.close(name, signal);
|
|
104
108
|
},
|
|
105
109
|
interrupt: interruptWorkspace,
|
|
106
|
-
|
|
107
|
-
const adapter = await adapterFor(config, signal);
|
|
108
|
-
return adapter.accessHint(name);
|
|
109
|
-
},
|
|
110
|
+
accessHint: accessHintForWorkspace,
|
|
110
111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.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",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"zod": "4.4.3"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@clipboard-health/ai-rules": "2.
|
|
77
|
+
"@clipboard-health/ai-rules": "2.21.0",
|
|
78
78
|
"@clipboard-health/oxlint-config": "1.9.4",
|
|
79
79
|
"@nx/js": "22.7.2",
|
|
80
80
|
"@tsconfig/node24": "24.0.4",
|