@clipboard-health/groundcrew 4.15.0 → 4.17.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 +1 -1
- package/crew.config.example.ts +14 -4
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +8 -1
- package/dist/lib/adapters/linear/factory.d.ts +5 -4
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +33 -58
- package/dist/lib/adapters/linear/fetch.d.ts +10 -9
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/linear/fetch.js +8 -6
- package/dist/lib/adapters/linear/schema.d.ts +6 -1
- package/dist/lib/adapters/linear/schema.d.ts.map +1 -1
- package/dist/lib/adapters/linear/schema.js +9 -1
- package/dist/lib/adapters/linear/statusNames.d.ts +25 -0
- package/dist/lib/adapters/linear/statusNames.d.ts.map +1 -0
- package/dist/lib/adapters/linear/statusNames.js +60 -0
- package/dist/lib/adapters/linear/writeback.d.ts +2 -0
- package/dist/lib/adapters/linear/writeback.d.ts.map +1 -1
- package/dist/lib/adapters/linear/writeback.js +43 -20
- package/dist/lib/config.d.ts +3 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -1
- package/dist/lib/ticketSource.d.ts +14 -8
- package/dist/lib/ticketSource.d.ts.map +1 -1
- package/docs/configuration.md +24 -6
- package/docs/troubleshooting.md +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ crew run --watch
|
|
|
73
73
|
Linear works out of the box: assign tickets to yourself and add an `agent-*` label.
|
|
74
74
|
|
|
75
75
|
- `agent-claude`, `agent-codex`, or `agent-<name>` routes to that model.
|
|
76
|
-
- `agent-any` routes to the enabled model with the most
|
|
76
|
+
- `agent-any` routes to the enabled model with the most session headroom, after skipping models over their session limit or weekly paced budget.
|
|
77
77
|
- Tickets without an `agent-*` label are ignored by `crew run`; dispatch one manually with `crew start <TICKET>`.
|
|
78
78
|
|
|
79
79
|
Groundcrew scans `workspace.knownRepositories` to infer which repo a ticket belongs to.
|
package/crew.config.example.ts
CHANGED
|
@@ -4,10 +4,11 @@ import type { Config } from "@clipboard-health/groundcrew";
|
|
|
4
4
|
export default {
|
|
5
5
|
// Groundcrew's built-in Linear adapter is implicit and needs no config:
|
|
6
6
|
// it picks up every Linear issue assigned to your API key's viewer that
|
|
7
|
-
// carries an `agent-*` label. There is no project / view
|
|
8
|
-
//
|
|
9
|
-
// `started`
|
|
10
|
-
//
|
|
7
|
+
// carries an `agent-*` label. There is no project / view block. The default
|
|
8
|
+
// Linear status names `In Progress` and `In Review` disambiguate Linear's
|
|
9
|
+
// `started` workflow states; other statuses fall back to workflow
|
|
10
|
+
// `state.type` (`unstarted` → todo, `started` → in progress,
|
|
11
|
+
// `completed`/`canceled`/`duplicate` → terminal).
|
|
11
12
|
//
|
|
12
13
|
// Opt a ticket in: assign it to yourself and add an `agent-<model>`
|
|
13
14
|
// label (e.g. `agent-claude`, `agent-any`).
|
|
@@ -53,6 +54,15 @@ export default {
|
|
|
53
54
|
// // See the shell adapter's ShellIssue schema for the JSON contract
|
|
54
55
|
// // `fetch` / `resolveOne` must emit.
|
|
55
56
|
// sources: [
|
|
57
|
+
// // Optional: explicitly declare Linear only when you need custom status
|
|
58
|
+
// // names. Omitted fields keep their defaults.
|
|
59
|
+
// {
|
|
60
|
+
// kind: "linear",
|
|
61
|
+
// statuses: {
|
|
62
|
+
// inProgress: ["Doing"],
|
|
63
|
+
// inReview: ["Code Review"],
|
|
64
|
+
// },
|
|
65
|
+
// },
|
|
56
66
|
// {
|
|
57
67
|
// kind: "shell",
|
|
58
68
|
// name: "jira",
|
|
@@ -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,CA2NjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
|
|
@@ -111,7 +111,10 @@ export function createDispatcher(deps) {
|
|
|
111
111
|
const slots = config.orchestrator.maximumInProgress - activeCount;
|
|
112
112
|
// Narrow Todo to tickets that opted in via an `agent-*` label.
|
|
113
113
|
// Unlabeled tickets are not groundcrew's concern even when in Todo.
|
|
114
|
-
|
|
114
|
+
// Sort by priority so higher-priority tickets fill slots first.
|
|
115
|
+
const todo = state.issues
|
|
116
|
+
.filter((issue) => issue.status === "todo" && isGroundcrewIssue(issue))
|
|
117
|
+
.toSorted((a, b) => prioritySortKey(a.priority) - prioritySortKey(b.priority));
|
|
115
118
|
if (slots <= 0) {
|
|
116
119
|
log(`At capacity (${activeCount}/${config.orchestrator.maximumInProgress})${formatActiveSlotList(active)}, no new work to start${idleSuffix}`);
|
|
117
120
|
return;
|
|
@@ -212,6 +215,10 @@ function formatUsageExhaustion(exhaustion) {
|
|
|
212
215
|
}
|
|
213
216
|
return `${exhaustion.model} weekly at ${exhaustion.usedPercentage.toFixed(1)}% (> ${exhaustion.allowedPercentage.toFixed(1)}% paced budget), resets in ${exhaustion.resetMinutes}m — skipping its tickets`;
|
|
214
217
|
}
|
|
218
|
+
/** Undefined priority sorts last. */
|
|
219
|
+
function prioritySortKey(priority) {
|
|
220
|
+
return priority ?? Number.POSITIVE_INFINITY;
|
|
221
|
+
}
|
|
215
222
|
export function formatActiveSlotList(active) {
|
|
216
223
|
if (active.length === 0) {
|
|
217
224
|
return "";
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* ./client.ts) and converts Linear-specific shapes into the canonical
|
|
6
6
|
* Issue/Blocker types consumers (via Board) speak.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Status names disambiguate Linear's multiple `started` states ("In Progress"
|
|
9
|
+
* vs "In Review"). Unmatched statuses fall back to workflow `state.type` so
|
|
10
|
+
* renamed or custom columns still preserve the broad lifecycle behavior.
|
|
11
11
|
*
|
|
12
12
|
* Description is populated on both `fetch()` Issues and `resolveOne()` Issues.
|
|
13
13
|
*/
|
|
@@ -15,6 +15,7 @@ import type { AdapterContext } from "../../adapterDefinition.ts";
|
|
|
15
15
|
import { type Issue as CanonicalIssue, type TicketSource } from "../../ticketSource.ts";
|
|
16
16
|
import type { LinearAdapterConfig } from "./schema.ts";
|
|
17
17
|
import { type Issue as LinearIssue } from "./fetch.ts";
|
|
18
|
+
import { type LinearStatusNames } from "./statusNames.ts";
|
|
18
19
|
/**
|
|
19
20
|
* Adapter-private payload threaded through `Issue.sourceRef`. Consumers
|
|
20
21
|
* MUST NOT inspect; only the Linear adapter reads it.
|
|
@@ -28,6 +29,6 @@ export interface LinearSourceRef {
|
|
|
28
29
|
/** Human-readable native status name, e.g. "In Progress", "Shipped". Diagnostic display only. */
|
|
29
30
|
nativeStatus: string;
|
|
30
31
|
}
|
|
31
|
-
export declare function toCanonicalIssue(linearIssue: LinearIssue, sourceName: string): CanonicalIssue;
|
|
32
|
+
export declare function toCanonicalIssue(linearIssue: LinearIssue, sourceName: string, statusNames?: LinearStatusNames): CanonicalIssue;
|
|
32
33
|
export declare function createLinearTicketSource(config: LinearAdapterConfig, context: AdapterContext): TicketSource;
|
|
33
34
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -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,EAG5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,
|
|
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,EAG5B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAIL,KAAK,KAAK,IAAI,WAAW,EAE1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAG1B;;;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;AAmDD,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,GAAE,iBAA+C,GAC3D,cAAc,CA4BhB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,cAAc,GACtB,YAAY,CAiGd"}
|
|
@@ -5,73 +5,37 @@
|
|
|
5
5
|
* ./client.ts) and converts Linear-specific shapes into the canonical
|
|
6
6
|
* Issue/Blocker types consumers (via Board) speak.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Status names disambiguate Linear's multiple `started` states ("In Progress"
|
|
9
|
+
* vs "In Review"). Unmatched statuses fall back to workflow `state.type` so
|
|
10
|
+
* renamed or custom columns still preserve the broad lifecycle behavior.
|
|
11
11
|
*
|
|
12
12
|
* Description is populated on both `fetch()` Issues and `resolveOne()` Issues.
|
|
13
13
|
*/
|
|
14
14
|
import { toCanonicalId, } from "../../ticketSource.js";
|
|
15
15
|
import { getLinearClient, lazyLinearClient } from "./client.js";
|
|
16
|
-
import { createBoardSource, fetchResolvedIssue,
|
|
16
|
+
import { createBoardSource, fetchResolvedIssue, } from "./fetch.js";
|
|
17
|
+
import { canonicalStatusFromLinearState, DEFAULT_LINEAR_STATUS_NAMES, resolveLinearStatusNames, } from "./statusNames.js";
|
|
17
18
|
import { createLinearIssueStatusUpdater } from "./writeback.js";
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return "todo";
|
|
26
|
-
}
|
|
27
|
-
case "started": {
|
|
28
|
-
return "in-progress";
|
|
29
|
-
}
|
|
30
|
-
case "completed":
|
|
31
|
-
case "canceled":
|
|
32
|
-
case "duplicate": {
|
|
33
|
-
return "done";
|
|
34
|
-
}
|
|
35
|
-
default: {
|
|
36
|
-
return "other";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function canonicalBlockerStatus(blocker) {
|
|
41
|
-
if (blocker.stateType === undefined) {
|
|
42
|
-
return {
|
|
43
|
-
status: "other",
|
|
44
|
-
statusReason: "missing",
|
|
45
|
-
...(blocker.status !== undefined && { nativeStatus: blocker.status }),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
if (isTerminalStateType(blocker.stateType)) {
|
|
49
|
-
return {
|
|
50
|
-
status: "done",
|
|
51
|
-
...(blocker.status !== undefined && { nativeStatus: blocker.status }),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
if (blocker.stateType === "started") {
|
|
55
|
-
return {
|
|
56
|
-
status: "in-progress",
|
|
57
|
-
...(blocker.status !== undefined && { nativeStatus: blocker.status }),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
if (blocker.stateType === "unstarted") {
|
|
19
|
+
function canonicalBlockerStatus(blocker, statusNames) {
|
|
20
|
+
const status = canonicalStatusFromLinearState({
|
|
21
|
+
nativeStatus: blocker.status,
|
|
22
|
+
stateType: blocker.stateType,
|
|
23
|
+
statusNames,
|
|
24
|
+
});
|
|
25
|
+
if (status !== "other") {
|
|
61
26
|
return {
|
|
62
|
-
status
|
|
27
|
+
status,
|
|
63
28
|
...(blocker.status !== undefined && { nativeStatus: blocker.status }),
|
|
64
29
|
};
|
|
65
30
|
}
|
|
66
|
-
// backlog / triage / anything else falls through as "other"
|
|
67
31
|
return {
|
|
68
|
-
status
|
|
69
|
-
statusReason: "unmapped",
|
|
32
|
+
status,
|
|
33
|
+
statusReason: blocker.stateType === undefined ? "missing" : "unmapped",
|
|
70
34
|
...(blocker.status !== undefined && { nativeStatus: blocker.status }),
|
|
71
35
|
};
|
|
72
36
|
}
|
|
73
|
-
function toCanonicalBlocker(blocker, sourceName) {
|
|
74
|
-
const { status, statusReason, nativeStatus } = canonicalBlockerStatus(blocker);
|
|
37
|
+
function toCanonicalBlocker(blocker, sourceName, statusNames) {
|
|
38
|
+
const { status, statusReason, nativeStatus } = canonicalBlockerStatus(blocker, statusNames);
|
|
75
39
|
return {
|
|
76
40
|
id: toCanonicalId(sourceName, blocker.id),
|
|
77
41
|
title: blocker.title,
|
|
@@ -87,7 +51,7 @@ function toCanonicalParentSkip(skip, sourceName) {
|
|
|
87
51
|
childCount: skip.childCount,
|
|
88
52
|
};
|
|
89
53
|
}
|
|
90
|
-
export function toCanonicalIssue(linearIssue, sourceName) {
|
|
54
|
+
export function toCanonicalIssue(linearIssue, sourceName, statusNames = DEFAULT_LINEAR_STATUS_NAMES) {
|
|
91
55
|
const sourceRef = {
|
|
92
56
|
uuid: linearIssue.uuid,
|
|
93
57
|
statusId: linearIssue.statusId,
|
|
@@ -100,19 +64,25 @@ export function toCanonicalIssue(linearIssue, sourceName) {
|
|
|
100
64
|
source: sourceName,
|
|
101
65
|
title: linearIssue.title,
|
|
102
66
|
description: linearIssue.description,
|
|
103
|
-
status:
|
|
67
|
+
status: canonicalStatusFromLinearState({
|
|
68
|
+
nativeStatus: linearIssue.status,
|
|
69
|
+
stateType: linearIssue.stateType,
|
|
70
|
+
statusNames,
|
|
71
|
+
}),
|
|
104
72
|
repository: linearIssue.repository,
|
|
105
73
|
model: linearIssue.model,
|
|
106
74
|
assignee: linearIssue.assignee,
|
|
107
75
|
updatedAt: linearIssue.updatedAt,
|
|
108
|
-
blockers: linearIssue.blockers.map((b) => toCanonicalBlocker(b, sourceName)),
|
|
76
|
+
blockers: linearIssue.blockers.map((b) => toCanonicalBlocker(b, sourceName, statusNames)),
|
|
109
77
|
hasMoreBlockers: linearIssue.hasMoreBlockers,
|
|
110
78
|
url: linearIssue.url,
|
|
79
|
+
...(linearIssue.priority === 0 ? {} : { priority: linearIssue.priority }),
|
|
111
80
|
sourceRef,
|
|
112
81
|
};
|
|
113
82
|
}
|
|
114
83
|
export function createLinearTicketSource(config, context) {
|
|
115
84
|
const sourceName = config.name ?? "linear";
|
|
85
|
+
const statusNames = resolveLinearStatusNames(config.statuses);
|
|
116
86
|
const { globalConfig } = context;
|
|
117
87
|
// Lazy: deferring `getLinearClient()` (and the sub-modules that depend on
|
|
118
88
|
// it) until first method use means `createLinearTicketSource` can be
|
|
@@ -131,6 +101,7 @@ export function createLinearTicketSource(config, context) {
|
|
|
131
101
|
function getIssueStatusUpdater() {
|
|
132
102
|
cachedIssueStatusUpdater ??= createLinearIssueStatusUpdater({
|
|
133
103
|
client: getClient(),
|
|
104
|
+
statusNames,
|
|
134
105
|
});
|
|
135
106
|
return cachedIssueStatusUpdater;
|
|
136
107
|
}
|
|
@@ -143,7 +114,7 @@ export function createLinearTicketSource(config, context) {
|
|
|
143
114
|
async fetch() {
|
|
144
115
|
const state = await getBoardSource().fetch();
|
|
145
116
|
lastParentSkips = state.parentSkips.map((skip) => toCanonicalParentSkip(skip, sourceName));
|
|
146
|
-
return state.issues.map((linearIssue) => toCanonicalIssue(linearIssue, sourceName));
|
|
117
|
+
return state.issues.map((linearIssue) => toCanonicalIssue(linearIssue, sourceName, statusNames));
|
|
147
118
|
},
|
|
148
119
|
async fetchParentSkips() {
|
|
149
120
|
return lastParentSkips;
|
|
@@ -166,7 +137,11 @@ export function createLinearTicketSource(config, context) {
|
|
|
166
137
|
source: sourceName,
|
|
167
138
|
title: resolved.title,
|
|
168
139
|
description: resolved.description,
|
|
169
|
-
status:
|
|
140
|
+
status: canonicalStatusFromLinearState({
|
|
141
|
+
nativeStatus: resolved.status,
|
|
142
|
+
stateType: resolved.stateType,
|
|
143
|
+
statusNames,
|
|
144
|
+
}),
|
|
170
145
|
repository: resolved.repository,
|
|
171
146
|
model: resolved.model,
|
|
172
147
|
assignee: "Unassigned",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Linear adapter — GraphQL fetch helpers for board/issue data.
|
|
3
3
|
*
|
|
4
|
-
* There is no project
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Done -> Shipped, etc.) Just Work without per-team config.
|
|
4
|
+
* There is no project or view configuration: the only server-side filter is
|
|
5
|
+
* "assigned to the API key's viewer AND carries an `agent-*` label." This
|
|
6
|
+
* module returns Linear's native status name plus workflow `state.type`; the
|
|
7
|
+
* ticket-source factory applies status-name disambiguation and state-type
|
|
8
|
+
* fallback when building canonical issues.
|
|
10
9
|
*/
|
|
11
10
|
import type { LinearClient } from "@linear/sdk";
|
|
12
11
|
import type { ResolvedConfig } from "../../config.ts";
|
|
@@ -18,8 +17,8 @@ export interface Blocker {
|
|
|
18
17
|
status: string | undefined;
|
|
19
18
|
/**
|
|
20
19
|
* Linear workflow `state.type` for the blocker (`unstarted` | `started` |
|
|
21
|
-
* `completed` | `canceled` | `duplicate` | `backlog` | `triage`).
|
|
22
|
-
*
|
|
20
|
+
* `completed` | `canceled` | `duplicate` | `backlog` | `triage`). Canonical
|
|
21
|
+
* classification uses this as a fallback after configured status names.
|
|
23
22
|
*/
|
|
24
23
|
stateType: string | undefined;
|
|
25
24
|
}
|
|
@@ -30,7 +29,7 @@ export interface Issue {
|
|
|
30
29
|
description: string;
|
|
31
30
|
status: string;
|
|
32
31
|
statusId: string;
|
|
33
|
-
/** Linear workflow `state.type` —
|
|
32
|
+
/** Linear workflow `state.type` — canonical classification fallback after status-name matching. */
|
|
34
33
|
stateType: string;
|
|
35
34
|
assignee: string;
|
|
36
35
|
updatedAt: string;
|
|
@@ -48,6 +47,8 @@ export interface Issue {
|
|
|
48
47
|
hasMoreBlockers: boolean;
|
|
49
48
|
/** Linear `Issue.url` — direct web link to the ticket. */
|
|
50
49
|
url: string;
|
|
50
|
+
/** Linear priority: 1=Urgent, 2=High, 3=Medium, 4=Low, 0=No priority. */
|
|
51
|
+
priority: number;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
54
|
* `Issue` narrowed to "this ticket is for groundcrew". Consumers operate on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Linear adapter — GraphQL fetch helpers for board/issue data.
|
|
3
3
|
*
|
|
4
|
-
* There is no project
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Done -> Shipped, etc.) Just Work without per-team config.
|
|
4
|
+
* There is no project or view configuration: the only server-side filter is
|
|
5
|
+
* "assigned to the API key's viewer AND carries an `agent-*` label." This
|
|
6
|
+
* module returns Linear's native status name plus workflow `state.type`; the
|
|
7
|
+
* ticket-source factory applies status-name disambiguation and state-type
|
|
8
|
+
* fallback when building canonical issues.
|
|
10
9
|
*/
|
|
11
10
|
import { RepositoryResolutionError } from "../../ticketSource.js";
|
|
12
11
|
import { log, styleWarning } from "../../util.js";
|
|
@@ -85,6 +84,7 @@ async function fetchBoard(client, config) {
|
|
|
85
84
|
description
|
|
86
85
|
updatedAt
|
|
87
86
|
url
|
|
87
|
+
priority
|
|
88
88
|
state { id name type }
|
|
89
89
|
team { id key }
|
|
90
90
|
assignee { name }
|
|
@@ -179,6 +179,7 @@ function buildLinearIssue(input) {
|
|
|
179
179
|
model: input.model,
|
|
180
180
|
teamId: input.teamId,
|
|
181
181
|
url: input.url,
|
|
182
|
+
priority: input.priority,
|
|
182
183
|
blockers: blockersFromRelations(input.inverseRelations?.nodes ?? []),
|
|
183
184
|
hasMoreBlockers: input.inverseRelations?.pageInfo.hasNextPage ?? false,
|
|
184
185
|
};
|
|
@@ -212,6 +213,7 @@ function issueFromNode(node, config) {
|
|
|
212
213
|
model,
|
|
213
214
|
teamId: node.team?.id ?? "",
|
|
214
215
|
url: node.url,
|
|
216
|
+
priority: node.priority,
|
|
215
217
|
inverseRelations: node.inverseRelations,
|
|
216
218
|
});
|
|
217
219
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Zod schema for the Linear adapter's per-source config block. The built-in
|
|
3
3
|
* Linear adapter is implicit and derives scope from the API key's viewer plus
|
|
4
|
-
* `agent-*` labels
|
|
4
|
+
* `agent-*` labels. Source config is only needed to override display name or
|
|
5
|
+
* Linear status names that disambiguate multiple `started` workflow states.
|
|
5
6
|
*/
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
export declare const linearAdapterConfigSchema: z.ZodObject<{
|
|
8
9
|
kind: z.ZodLiteral<"linear">;
|
|
9
10
|
name: z.ZodOptional<z.ZodString>;
|
|
11
|
+
statuses: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
inProgress: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
inReview: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14
|
+
}, z.core.$strip>>;
|
|
10
15
|
}, z.core.$strip>;
|
|
11
16
|
export type LinearAdapterConfig = z.infer<typeof linearAdapterConfigSchema>;
|
|
12
17
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,yBAAyB;;;;;;;iBAYpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Zod schema for the Linear adapter's per-source config block. The built-in
|
|
3
3
|
* Linear adapter is implicit and derives scope from the API key's viewer plus
|
|
4
|
-
* `agent-*` labels
|
|
4
|
+
* `agent-*` labels. Source config is only needed to override display name or
|
|
5
|
+
* Linear status names that disambiguate multiple `started` workflow states.
|
|
5
6
|
*/
|
|
6
7
|
import { z } from "zod";
|
|
8
|
+
const statusNamesSchema = z.array(z.string().trim().min(1)).min(1);
|
|
7
9
|
export const linearAdapterConfigSchema = z.object({
|
|
8
10
|
kind: z.literal("linear"),
|
|
9
11
|
name: z
|
|
10
12
|
.string()
|
|
11
13
|
.regex(/^[a-z][a-z0-9-]*$/, "name must be kebab-case (lowercase letters, digits, hyphens)")
|
|
12
14
|
.optional(),
|
|
15
|
+
statuses: z
|
|
16
|
+
.object({
|
|
17
|
+
inProgress: statusNamesSchema.optional(),
|
|
18
|
+
inReview: statusNamesSchema.optional(),
|
|
19
|
+
})
|
|
20
|
+
.optional(),
|
|
13
21
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CanonicalStatus } from "../../ticketSource.ts";
|
|
2
|
+
import type { LinearAdapterConfig } from "./schema.ts";
|
|
3
|
+
export interface LinearStatusNames {
|
|
4
|
+
inProgress: readonly string[];
|
|
5
|
+
inReview: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export interface LinearWorkflowState {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
type: string;
|
|
11
|
+
position: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const DEFAULT_LINEAR_STATUS_NAMES: {
|
|
14
|
+
readonly inProgress: readonly ["In Progress"];
|
|
15
|
+
readonly inReview: readonly ["In Review"];
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveLinearStatusNames(config: LinearAdapterConfig["statuses"] | undefined): LinearStatusNames;
|
|
18
|
+
export declare function canonicalStatusFromLinearState(arguments_: {
|
|
19
|
+
nativeStatus: string | undefined;
|
|
20
|
+
stateType: string | undefined;
|
|
21
|
+
statusNames: LinearStatusNames;
|
|
22
|
+
}): CanonicalStatus;
|
|
23
|
+
export declare function findLinearWorkflowStateByName(states: readonly LinearWorkflowState[], names: readonly string[]): LinearWorkflowState | undefined;
|
|
24
|
+
export declare function formatLinearStatusNames(names: readonly string[]): string;
|
|
25
|
+
//# sourceMappingURL=statusNames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statusNames.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/statusNames.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B;;;CAGF,CAAC;AAEvC,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,SAAS,GAClD,iBAAiB,CAKnB;AAED,wBAAgB,8BAA8B,CAAC,UAAU,EAAE;IACzD,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,iBAAiB,CAAC;CAChC,GAAG,eAAe,CAWlB;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,SAAS,mBAAmB,EAAE,EACtC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,mBAAmB,GAAG,SAAS,CAEjC;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAExE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const DEFAULT_LINEAR_STATUS_NAMES = {
|
|
2
|
+
inProgress: ["In Progress"],
|
|
3
|
+
inReview: ["In Review"],
|
|
4
|
+
};
|
|
5
|
+
export function resolveLinearStatusNames(config) {
|
|
6
|
+
return {
|
|
7
|
+
inProgress: config?.inProgress ?? DEFAULT_LINEAR_STATUS_NAMES.inProgress,
|
|
8
|
+
inReview: config?.inReview ?? DEFAULT_LINEAR_STATUS_NAMES.inReview,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function canonicalStatusFromLinearState(arguments_) {
|
|
12
|
+
const { nativeStatus, stateType, statusNames } = arguments_;
|
|
13
|
+
if (stateType === "started") {
|
|
14
|
+
if (matchesLinearStatusName(nativeStatus, statusNames.inReview)) {
|
|
15
|
+
return "in-review";
|
|
16
|
+
}
|
|
17
|
+
if (matchesLinearStatusName(nativeStatus, statusNames.inProgress)) {
|
|
18
|
+
return "in-progress";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return canonicalStatusFromStateType(stateType);
|
|
22
|
+
}
|
|
23
|
+
export function findLinearWorkflowStateByName(states, names) {
|
|
24
|
+
return states.find((state) => matchesLinearStatusName(state.name, names));
|
|
25
|
+
}
|
|
26
|
+
export function formatLinearStatusNames(names) {
|
|
27
|
+
return names.map((name) => `"${name}"`).join(" or ");
|
|
28
|
+
}
|
|
29
|
+
function canonicalStatusFromStateType(stateType) {
|
|
30
|
+
/* v8 ignore next 3 @preserve -- LinearIssue.stateType is non-optional; this guard is defensive for the resolveOne path */
|
|
31
|
+
if (stateType === undefined) {
|
|
32
|
+
return "other";
|
|
33
|
+
}
|
|
34
|
+
switch (stateType) {
|
|
35
|
+
case "unstarted": {
|
|
36
|
+
return "todo";
|
|
37
|
+
}
|
|
38
|
+
case "started": {
|
|
39
|
+
return "in-progress";
|
|
40
|
+
}
|
|
41
|
+
case "completed":
|
|
42
|
+
case "canceled":
|
|
43
|
+
case "duplicate": {
|
|
44
|
+
return "done";
|
|
45
|
+
}
|
|
46
|
+
default: {
|
|
47
|
+
return "other";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function matchesLinearStatusName(nativeStatus, configuredNames) {
|
|
52
|
+
if (nativeStatus === undefined) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const normalizedStatus = normalizeStatusName(nativeStatus);
|
|
56
|
+
return configuredNames.some((name) => normalizeStatusName(name) === normalizedStatus);
|
|
57
|
+
}
|
|
58
|
+
function normalizeStatusName(name) {
|
|
59
|
+
return name.trim().toLowerCase();
|
|
60
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LinearClient } from "@linear/sdk";
|
|
2
2
|
import type { MarkInReviewResult } from "../../ticketSource.ts";
|
|
3
|
+
import { type LinearStatusNames } from "./statusNames.ts";
|
|
3
4
|
interface LinearIssueReference {
|
|
4
5
|
id: string;
|
|
5
6
|
uuid: string;
|
|
@@ -11,6 +12,7 @@ interface LinearIssueStatusUpdater {
|
|
|
11
12
|
}
|
|
12
13
|
export declare function createLinearIssueStatusUpdater(arguments_: {
|
|
13
14
|
client: LinearClient;
|
|
15
|
+
statusNames?: LinearStatusNames;
|
|
14
16
|
}): LinearIssueStatusUpdater;
|
|
15
17
|
export {};
|
|
16
18
|
//# sourceMappingURL=writeback.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/writeback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"writeback.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/writeback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,EAIL,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAC;AAE1B,UAAU,oBAAoB;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,wBAAwB;IAChC,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,YAAY,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxE;AAED,wBAAgB,8BAA8B,CAAC,UAAU,EAAE;IACzD,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC,GAAG,wBAAwB,CA0F3B"}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { debug } from "../../util.js";
|
|
2
|
-
|
|
3
|
-
// so it cannot prove a successful in-review transition. Report unsupported
|
|
4
|
-
// until read/write sides can distinguish "In Review" from generic started.
|
|
5
|
-
async function markInReviewUnsupported(issue) {
|
|
6
|
-
debug(`markInReview is unsupported for ${issue.id} (Linear in-review not yet implemented)`);
|
|
7
|
-
return {
|
|
8
|
-
outcome: "unsupported",
|
|
9
|
-
reason: "Linear in-review writeback is not implemented",
|
|
10
|
-
};
|
|
11
|
-
}
|
|
2
|
+
import { DEFAULT_LINEAR_STATUS_NAMES, findLinearWorkflowStateByName, formatLinearStatusNames, } from "./statusNames.js";
|
|
12
3
|
export function createLinearIssueStatusUpdater(arguments_) {
|
|
13
|
-
const { client } = arguments_;
|
|
4
|
+
const { client, statusNames = DEFAULT_LINEAR_STATUS_NAMES } = arguments_;
|
|
14
5
|
// Positive cache only. Keyed by teamId because the in-progress-state
|
|
15
6
|
// resolution yields a single stateId per team — independent of which
|
|
16
7
|
// project the ticket belongs to. State ids don't change for misconfig
|
|
@@ -23,6 +14,12 @@ export function createLinearIssueStatusUpdater(arguments_) {
|
|
|
23
14
|
// every failing attempt costs at most a handful of extra Linear API calls
|
|
24
15
|
// per tick.
|
|
25
16
|
const inProgressStateByTeam = new Map();
|
|
17
|
+
const inReviewStateByTeam = new Map();
|
|
18
|
+
async function fetchWorkflowStates(teamId) {
|
|
19
|
+
const team = await client.team(teamId);
|
|
20
|
+
const states = await team.states();
|
|
21
|
+
return states.nodes;
|
|
22
|
+
}
|
|
26
23
|
async function getInProgressStateId(teamId) {
|
|
27
24
|
if (teamId.length === 0) {
|
|
28
25
|
return undefined;
|
|
@@ -31,17 +28,14 @@ export function createLinearIssueStatusUpdater(arguments_) {
|
|
|
31
28
|
if (cached !== undefined) {
|
|
32
29
|
return cached;
|
|
33
30
|
}
|
|
34
|
-
const team = await client.team(teamId);
|
|
35
|
-
const states = await team.states();
|
|
36
31
|
// Linear's default workflow has MULTIPLE `started`-type states — both
|
|
37
32
|
// "In Progress" and "In Review" are `started`. `team.states()` orders by
|
|
38
33
|
// updatedAt (the connection has no position ordering), so array order
|
|
39
|
-
// can't disambiguate them. Prefer
|
|
40
|
-
// otherwise fall back to the lowest-position (leftmost) `started` column
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const inProgress = startedStates.find((state) => state.name.trim().toLowerCase() === "in progress") ??
|
|
34
|
+
// can't disambiguate them. Prefer configured/default in-progress names;
|
|
35
|
+
// otherwise fall back to the lowest-position (leftmost) `started` column.
|
|
36
|
+
const states = await fetchWorkflowStates(teamId);
|
|
37
|
+
const startedStates = states.filter((state) => state.type === "started");
|
|
38
|
+
const inProgress = findLinearWorkflowStateByName(startedStates, statusNames.inProgress) ??
|
|
45
39
|
startedStates.toSorted((a, b) => a.position - b.position).at(0);
|
|
46
40
|
if (inProgress?.id === undefined) {
|
|
47
41
|
return undefined;
|
|
@@ -49,6 +43,23 @@ export function createLinearIssueStatusUpdater(arguments_) {
|
|
|
49
43
|
inProgressStateByTeam.set(teamId, inProgress.id);
|
|
50
44
|
return inProgress.id;
|
|
51
45
|
}
|
|
46
|
+
async function getInReviewStateId(teamId) {
|
|
47
|
+
if (teamId.length === 0) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const cached = inReviewStateByTeam.get(teamId);
|
|
51
|
+
if (cached !== undefined) {
|
|
52
|
+
return cached;
|
|
53
|
+
}
|
|
54
|
+
const states = await fetchWorkflowStates(teamId);
|
|
55
|
+
const startedStates = states.filter((state) => state.type === "started");
|
|
56
|
+
const inReview = findLinearWorkflowStateByName(startedStates, statusNames.inReview);
|
|
57
|
+
if (inReview?.id === undefined) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
inReviewStateByTeam.set(teamId, inReview.id);
|
|
61
|
+
return inReview.id;
|
|
62
|
+
}
|
|
52
63
|
async function markInProgress(issue) {
|
|
53
64
|
const stateId = await getInProgressStateId(issue.teamId);
|
|
54
65
|
if (stateId === undefined) {
|
|
@@ -57,5 +68,17 @@ export function createLinearIssueStatusUpdater(arguments_) {
|
|
|
57
68
|
await client.updateIssue(issue.uuid, { stateId });
|
|
58
69
|
debug(`Marked ${issue.id} as in progress`);
|
|
59
70
|
}
|
|
60
|
-
|
|
71
|
+
async function markInReview(issue) {
|
|
72
|
+
const stateId = await getInReviewStateId(issue.teamId);
|
|
73
|
+
if (stateId === undefined) {
|
|
74
|
+
return {
|
|
75
|
+
outcome: "unsupported",
|
|
76
|
+
reason: `Could not find a Linear workflow state named ${formatLinearStatusNames(statusNames.inReview)} for team ${issue.teamId.length > 0 ? issue.teamId : "?"}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
await client.updateIssue(issue.uuid, { stateId });
|
|
80
|
+
debug(`Marked ${issue.id} as in review`);
|
|
81
|
+
return { outcome: "applied" };
|
|
82
|
+
}
|
|
83
|
+
return { markInProgress, markInReview };
|
|
61
84
|
}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -123,9 +123,9 @@ type UserModelDefinition = EnabledUserModelDefinition;
|
|
|
123
123
|
*
|
|
124
124
|
* Groundcrew's built-in Linear adapter is implicit and needs no config:
|
|
125
125
|
* it picks up every Linear issue assigned to the API key's viewer that
|
|
126
|
-
* carries an `agent-*` label. There is no project
|
|
127
|
-
*
|
|
128
|
-
*
|
|
126
|
+
* carries an `agent-*` label. There is no project or view configuration.
|
|
127
|
+
* Linear's default "In Progress" / "In Review" status names disambiguate
|
|
128
|
+
* `started` workflow states; unmatched statuses fall back to `state.type`.
|
|
129
129
|
*/
|
|
130
130
|
export interface Config {
|
|
131
131
|
/**
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -372,7 +372,8 @@ function failOnLegacyLinearShape(user) {
|
|
|
372
372
|
fail([
|
|
373
373
|
"The `linear` config block is no longer supported.",
|
|
374
374
|
"Groundcrew now picks up every Linear issue assigned to your API key's viewer that carries an `agent-*` label —",
|
|
375
|
-
"
|
|
375
|
+
"remove the `linear: { ... }` block from your config.",
|
|
376
|
+
'To customize Linear status names, declare `sources: [{ kind: "linear", statuses: { ... } }]` instead.',
|
|
376
377
|
"If you only want a subset of your Linear tickets to be picked up, leave the unwanted tickets unassigned or remove their `agent-*` label.",
|
|
377
378
|
].join("\n"));
|
|
378
379
|
}
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* Source-neutral status enum every adapter normalises its native vocabulary
|
|
12
12
|
* to. Consumers branch on these values, never on a source's native names.
|
|
13
13
|
*
|
|
14
|
-
* - `todo` / `in-progress` / `done`:
|
|
15
|
-
*
|
|
16
|
-
* - `in-review`:
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* - `todo` / `in-progress` / `done`: broad lifecycle states mapped from each
|
|
15
|
+
* source's native vocabulary.
|
|
16
|
+
* - `in-review`: review-stage work that should no longer consume a dispatch
|
|
17
|
+
* slot but should not be cleaned up as terminal. The built-in Linear adapter
|
|
18
|
+
* maps default/configured review status names here; the shell adapter's JSON
|
|
19
|
+
* contract accepts it directly.
|
|
20
20
|
* - `other`: anything an adapter sees but can't classify (Linear tickets in
|
|
21
21
|
* `backlog`/`triage`, blockers with no resolvable state).
|
|
22
22
|
*/
|
|
@@ -35,8 +35,8 @@ export interface Blocker {
|
|
|
35
35
|
* (e.g., Linear had no state on the blocker; shell script omitted
|
|
36
36
|
* the field).
|
|
37
37
|
* - `"unmapped"`: the source returned a status that isn't in the
|
|
38
|
-
* source's known mapping (e.g., a Linear column not
|
|
39
|
-
* `
|
|
38
|
+
* source's known mapping (e.g., a Linear column not covered by
|
|
39
|
+
* `sources[*].statuses`, or an unrecognized shell value).
|
|
40
40
|
*
|
|
41
41
|
* MUST be undefined when `status !== "other"`.
|
|
42
42
|
*/
|
|
@@ -74,6 +74,12 @@ export interface Issue {
|
|
|
74
74
|
* branched on.
|
|
75
75
|
*/
|
|
76
76
|
url?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Source-native priority. Lower values sort first; `undefined` means no
|
|
79
|
+
* priority and sorts last. Dispatcher uses this to order Todo tickets before
|
|
80
|
+
* slot assignment.
|
|
81
|
+
*/
|
|
82
|
+
priority?: number;
|
|
77
83
|
/** Adapter-private. Consumers MUST NOT inspect; only the producing adapter reads it. */
|
|
78
84
|
sourceRef: unknown;
|
|
79
85
|
}
|
|
@@ -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/docs/configuration.md
CHANGED
|
@@ -8,7 +8,7 @@ Workspace settings and at least one enabled model are required; everything else
|
|
|
8
8
|
| `workspace.knownRepositories` | Repos searched for in ticket descriptions to infer where work belongs. |
|
|
9
9
|
| `models.definitions` | Enabled model set. Built-in presets can be enabled with `{}`. |
|
|
10
10
|
|
|
11
|
-
The branch prefix (`<prefix>-<TICKET>`) defaults to `os.userInfo().username`; override it with `git.branchPrefix` (see the full reference below). Changing it only affects newly created worktrees; existing local branches keep their original names until cleaned up.
|
|
11
|
+
The branch prefix (`<prefix>-<TICKET>`) defaults to `os.userInfo().username`; override it with `git.branchPrefix` (see the full reference below). Changing it only affects newly created worktrees; existing local branches keep their original names until cleaned up. Groundcrew picks up every issue assigned to your API key's viewer that carries an `agent-*` label across every visible team and project, governed by a single `orchestrator.maximumInProgress` budget.
|
|
12
12
|
|
|
13
13
|
## Repository Layout
|
|
14
14
|
|
|
@@ -42,13 +42,31 @@ The "Loaded config from ..." line at startup tells you which config won.
|
|
|
42
42
|
## Agent Label Routing
|
|
43
43
|
|
|
44
44
|
- `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled model.
|
|
45
|
-
- `agent-any` routes to the model with the most
|
|
45
|
+
- `agent-any` routes to the model with the most session headroom, after skipping models over their session limit or weekly paced budget.
|
|
46
46
|
- Unknown `agent-<name>` falls back to `models.default`.
|
|
47
47
|
- A built-in `agent-<name>` label whose model is not enabled falls back to `models.default` with a warning.
|
|
48
48
|
- No `agent-*` label is ignored by `crew run`. Dispatch on demand with `crew start <TICKET>`, which falls back to `models.default`.
|
|
49
49
|
- Todo tickets blocked by non-terminal blockers are skipped until their blockers reach a terminal status.
|
|
50
50
|
|
|
51
|
-
Status classification uses Linear's workflow `state.type` (`unstarted`, `started`, `completed`, `canceled`, `duplicate`), so
|
|
51
|
+
Status classification uses Linear's default status names `In Progress` and `In Review` to disambiguate multiple `started` workflow states. Statuses that do not match those names fall back to Linear's workflow `state.type` (`unstarted`, `started`, `completed`, `canceled`, `duplicate`), so broad lifecycle classification still works without configuration. Parent issues with children are ignored; sub-issues are the work items.
|
|
52
|
+
|
|
53
|
+
If your Linear workflow uses different names, explicitly declare the built-in Linear source and override only the names you need:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
export default {
|
|
57
|
+
sources: [
|
|
58
|
+
{
|
|
59
|
+
kind: "linear",
|
|
60
|
+
statuses: {
|
|
61
|
+
inProgress: ["Doing"],
|
|
62
|
+
inReview: ["Code Review"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Configured names replace the default for that status; omitted fields keep their defaults. Matching is case-insensitive and trims surrounding whitespace.
|
|
52
70
|
|
|
53
71
|
## Enabling Model Presets
|
|
54
72
|
|
|
@@ -130,7 +148,7 @@ and hook contract.
|
|
|
130
148
|
|
|
131
149
|
| Key | Default | What it does |
|
|
132
150
|
| ---------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
|
-
| `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`.
|
|
151
|
+
| `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`. Declare `{ kind: "linear", statuses: { ... } }` only to override Linear status names used for `in-progress` / `in-review` disambiguation. |
|
|
134
152
|
| `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
|
|
135
153
|
| `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
|
|
136
154
|
| `git.branchPrefix` | OS username | Prefix groundcrew puts before the ticket id when naming a worktree branch (`<branchPrefix>-<ticket>`). Must be a slash-free slug of letters, digits, `.`, `_`, or `-`. Defaults to the OS account username. Changing it only affects newly created worktrees; existing local branches keep their original names until cleaned up. Prefer a per-user config for personal prefixes — a committed `git.branchPrefix` gives every contributor the same branch prefix. |
|
|
@@ -139,12 +157,12 @@ and hook contract.
|
|
|
139
157
|
| `defaults.hooks.prepareWorktree` | optional | Fallback repo-preparation command used only when the worktree does not define `.groundcrew/config.json` `hooks.prepareWorktree`. The hook runs after worktree creation and before the agent starts. Repo-local config wins. |
|
|
140
158
|
| `orchestrator.maximumInProgress` | `4` | Cap on in-progress tickets at once for this `crew` instance. |
|
|
141
159
|
| `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
|
|
142
|
-
| `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick.
|
|
160
|
+
| `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick. Models are also skipped when codexbar reports weekly usage over the current weekly paced budget. |
|
|
143
161
|
| `models.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TICKET>` for unlabeled tickets. `crew run` ignores unlabeled tickets and does not apply this default. Must exist in `models.definitions`. If you enable only `codex`, set `default: "codex"`. |
|
|
144
162
|
| `models.definitions` | **required** | Enabled model set. Built-in keys (`claude`, `codex`) can use `{}` to opt into the shipped preset. Custom model names must provide `cmd` and `color`. |
|
|
145
163
|
| `models.definitions.<name>.cmd` | preset for built-ins | Shell command launched for the model. Required for custom models. Runs in the worktree through the resolved `local.runner`. `{{worktree}}` is replaced before launch; `{{sandbox}}` expands to the sbx sandbox name under the sdx runner and an empty string otherwise. |
|
|
146
164
|
| `models.definitions.<name>.color` | preset for built-ins | Color for the workspace status pill (cmux only; tmux silently drops it). Required for custom models. |
|
|
147
|
-
| `models.definitions.<name>.usage` | preset for built-ins | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage
|
|
165
|
+
| `models.definitions.<name>.usage` | preset for built-ins | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage` plus the weekly paced budget when codexbar exposes a weekly window. When `usage.codexbar.source` is omitted, groundcrew uses `oauth` for Codex/Claude on macOS, `auto` for other macOS providers, and `cli` elsewhere. Set to `{ disabled: true }` to disable usage gating while keeping the model enabled. |
|
|
148
166
|
| `models.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the model. Required at launch when `local.runner` resolves to `sdx`. Field: `agent` (required sbx agent name). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
|
|
149
167
|
| `models.definitions.<name>.preLaunch` | optional | Host-only shell snippet run before the agent exec and outside Safehouse/sdx. Exports survive into the launch shell; under the default `safehouse` runner they are only forwarded to the agent when listed via `preLaunchEnv` or when `cmd` includes its own `safehouse --env-pass=NAMES`. `{{worktree}}` is substituted. A non-zero exit aborts launch. Not supported when `local.runner` resolves to `sdx` in v1. |
|
|
150
168
|
| `models.definitions.<name>.preLaunchEnv` | optional | Companion to `preLaunch`: list of env var names to append to groundcrew's `safehouse-clearance` `--env-pass=` flag, so `preLaunch` exports reach the agent without overriding `cmd` and losing the project's egress allowlist. Each entry must match `[A-Za-z_][A-Za-z0-9_]*`. Under `runner: "none"` exports already inherit and `preLaunchEnv` is a no-op. An empty array is a uniform no-op in every runner; a non-empty list is rejected when `cmd` already starts with `safehouse` or when `runner` resolves to `sdx`. |
|
package/docs/troubleshooting.md
CHANGED
|
@@ -29,7 +29,7 @@ This applies to the tmux backend only.
|
|
|
29
29
|
|
|
30
30
|
## Tickets Stay In-Progress
|
|
31
31
|
|
|
32
|
-
Groundcrew
|
|
32
|
+
Groundcrew marks a ticket `In Progress` when it provisions a workspace. When a PR opens on that worktree branch, the reviewer pass attempts to mark the ticket `In Review`. Linear's default `In Review` status works out of the box; if your team renamed it, configure `sources: [{ kind: "linear", statuses: { inReview: ["Code Review"] } }]`.
|
|
33
33
|
|
|
34
34
|
## Claude Launches In Auto Mode By Default
|
|
35
35
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.17.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",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@tsconfig/node24": "24.0.4",
|
|
83
83
|
"@tsconfig/strictest": "2.0.8",
|
|
84
84
|
"@types/node": "25.9.1",
|
|
85
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
85
|
+
"@typescript/native-preview": "7.0.0-dev.20260602.1",
|
|
86
86
|
"@vitest/coverage-v8": "4.1.7",
|
|
87
87
|
"cspell": "10.0.1",
|
|
88
88
|
"dependency-cruiser": "17.4.3",
|