@clipboard-health/groundcrew 1.10.1 → 1.10.2
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/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +5 -41
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +6 -0
- package/dist/lib/boardSource.d.ts +2 -0
- package/dist/lib/boardSource.d.ts.map +1 -1
- package/dist/lib/boardSource.js +11 -1
- package/dist/lib/linearIssueStatus.d.ts +17 -0
- package/dist/lib/linearIssueStatus.d.ts.map +1 -0
- package/dist/lib/linearIssueStatus.js +41 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAczD,UAAU,cAAc;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;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;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAwNjE"}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* Pure verdict logic lives in `eligibility.ts`; this module is responsible
|
|
7
7
|
* for telemetry, Linear writes, and side-effecting setupWorkspace calls.
|
|
8
8
|
*/
|
|
9
|
-
import { isGroundcrewIssue
|
|
9
|
+
import { isGroundcrewIssue } from "../lib/boardSource.js";
|
|
10
|
+
import { createLinearIssueStatusUpdater } from "../lib/linearIssueStatus.js";
|
|
10
11
|
import { errorMessage, log, logEvent } from "../lib/util.js";
|
|
11
12
|
import { workspaces } from "../lib/workspaces.js";
|
|
12
13
|
import { classifyBlockers, classifyEligibility, } from "./eligibility.js";
|
|
@@ -17,8 +18,7 @@ const MINUTES_PER_DAY = 24 * 60;
|
|
|
17
18
|
const MINUTES_PER_WEEK = DAYS_PER_WEEK * MINUTES_PER_DAY;
|
|
18
19
|
export function createDispatcher(deps) {
|
|
19
20
|
const { config, client } = deps;
|
|
20
|
-
const
|
|
21
|
-
let teamsMissingInProgress = new Set();
|
|
21
|
+
const issueStatusUpdater = createLinearIssueStatusUpdater({ config, client });
|
|
22
22
|
function buildExhaustedSet(usage) {
|
|
23
23
|
const exhausted = new Set();
|
|
24
24
|
const sessionLimit = config.orchestrator.sessionLimitPercentage;
|
|
@@ -49,42 +49,6 @@ export function createDispatcher(deps) {
|
|
|
49
49
|
}
|
|
50
50
|
return exhausted;
|
|
51
51
|
}
|
|
52
|
-
async function getInProgressStateId(teamId) {
|
|
53
|
-
if (teamId.length === 0) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
const cached = inProgressStateByTeam.get(teamId);
|
|
57
|
-
if (cached !== undefined) {
|
|
58
|
-
return cached;
|
|
59
|
-
}
|
|
60
|
-
// Negative cache is per-iteration so a team that's fixed in Linear during
|
|
61
|
-
// a `crew watch` session auto-recovers next tick. Within one iteration,
|
|
62
|
-
// every eligible ticket in a misconfigured team would otherwise re-fetch.
|
|
63
|
-
if (teamsMissingInProgress.has(teamId)) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
const team = await client.team(teamId);
|
|
67
|
-
const states = await team.states();
|
|
68
|
-
const inProgress = states.nodes.find((state) => state.name === config.linear.statuses.inProgress);
|
|
69
|
-
if (inProgress?.id === undefined) {
|
|
70
|
-
teamsMissingInProgress.add(teamId);
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
inProgressStateByTeam.set(teamId, inProgress.id);
|
|
74
|
-
return inProgress.id;
|
|
75
|
-
}
|
|
76
|
-
async function markInProgress(issue) {
|
|
77
|
-
const stateId = await getInProgressStateId(issue.teamId);
|
|
78
|
-
if (stateId === undefined) {
|
|
79
|
-
// Throw rather than log+return: if we silently swallowed this, the
|
|
80
|
-
// ticket would stay Todo forever while the workspace runs, which means
|
|
81
|
-
// every iteration re-enters the recovery path and the agent never
|
|
82
|
-
// counts toward maximumInProgress.
|
|
83
|
-
throw new Error(`Could not find "${config.linear.statuses.inProgress}" state for ${issue.id} (team ${issue.teamId.length > 0 ? issue.teamId : "?"}). Verify the status name in linear.statuses.inProgress matches the team's workflow.`);
|
|
84
|
-
}
|
|
85
|
-
await client.updateIssue(issue.uuid, { stateId });
|
|
86
|
-
log(`Marked ${issue.id} as ${config.linear.statuses.inProgress}`);
|
|
87
|
-
}
|
|
88
52
|
function logSkip(verdict) {
|
|
89
53
|
log(verdict.message);
|
|
90
54
|
logEvent("dispatch", {
|
|
@@ -130,7 +94,7 @@ export function createDispatcher(deps) {
|
|
|
130
94
|
? setupWorkspace(config, setupOptions)
|
|
131
95
|
: setupWorkspace(config, setupOptions, { signal }));
|
|
132
96
|
}
|
|
133
|
-
await markInProgress(issue);
|
|
97
|
+
await issueStatusUpdater.markInProgress(issue);
|
|
134
98
|
logEvent("dispatch", {
|
|
135
99
|
outcome: recovery ? "resumed" : "started",
|
|
136
100
|
ticket: issue.id,
|
|
@@ -153,7 +117,7 @@ export function createDispatcher(deps) {
|
|
|
153
117
|
}
|
|
154
118
|
async function runOnce(arguments_) {
|
|
155
119
|
const { state, worktreeEntries, usage, dryRun, signal } = arguments_;
|
|
156
|
-
|
|
120
|
+
issueStatusUpdater.resetMissingInProgressCache();
|
|
157
121
|
const activeCount = state.issues.filter((issue) => issue.status === config.linear.statuses.inProgress).length;
|
|
158
122
|
const slots = config.orchestrator.maximumInProgress - activeCount;
|
|
159
123
|
// Narrow Todo to tickets that opted in via an `agent-*` label.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAc1B,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAgBD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAsED,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CA8Ef;AAuHD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -6,6 +6,7 @@ import { fetchResolvedIssue } from "../lib/boardSource.js";
|
|
|
6
6
|
import { BUILD_SECRET_NAMES, loadConfig, } from "../lib/config.js";
|
|
7
7
|
import { detectHostCapabilities } from "../lib/host.js";
|
|
8
8
|
import { buildLaunchCommand, buildRemoteLaunchCommand, shellSingleQuote, } from "../lib/launchCommand.js";
|
|
9
|
+
import { createLinearIssueStatusUpdater } from "../lib/linearIssueStatus.js";
|
|
9
10
|
import { assertLocalRunnerRequirements } from "../lib/localRunner.js";
|
|
10
11
|
import { getRemoteRunnerProvider } from "../lib/spriteRemoteRunnerProvider.js";
|
|
11
12
|
import { errorMessage, getLinearClient, log, readEnvironmentVariable } from "../lib/util.js";
|
|
@@ -245,4 +246,9 @@ export async function setupWorkspaceCli(ticket, options = {}) {
|
|
|
245
246
|
runner: resolved.runner,
|
|
246
247
|
details: { title: resolved.title, description: resolved.description },
|
|
247
248
|
});
|
|
249
|
+
await createLinearIssueStatusUpdater({ config, client }).markInProgress({
|
|
250
|
+
id: ticket.toLowerCase(),
|
|
251
|
+
uuid: resolved.uuid,
|
|
252
|
+
teamId: resolved.teamId,
|
|
253
|
+
});
|
|
248
254
|
}
|
|
@@ -65,11 +65,13 @@ interface BoardSourceDeps {
|
|
|
65
65
|
export declare function createBoardSource(deps: BoardSourceDeps): BoardSource;
|
|
66
66
|
export declare function isTerminalStatus(status: string, config: ResolvedConfig): boolean;
|
|
67
67
|
interface ResolvedIssue {
|
|
68
|
+
uuid: string;
|
|
68
69
|
title: string;
|
|
69
70
|
description: string;
|
|
70
71
|
repository: string;
|
|
71
72
|
model: string;
|
|
72
73
|
runner: WorkspaceRunner;
|
|
74
|
+
teamId: string;
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* `agent-any` collapses to `models.default` here — manual setup doesn't run
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAMrB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,0FAA0F;IAC1F,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,eAAe,CAExE;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAmB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAMjF;CACF;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAUpE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhF;AAgMD,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAMrB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,0FAA0F;IAC1F,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,eAAe,CAExE;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAmB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAMjF;CACF;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAUpE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhF;AAgMD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE;IACnD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,aAAa,CAAC,CAqDzB"}
|
package/dist/lib/boardSource.js
CHANGED
|
@@ -180,8 +180,10 @@ export async function fetchResolvedIssue(arguments_) {
|
|
|
180
180
|
const { client, config, ticket } = arguments_;
|
|
181
181
|
const response = await client.client.rawRequest(`query ResolveIssue($id: String!) {
|
|
182
182
|
issue(id: $id) {
|
|
183
|
+
id
|
|
183
184
|
title
|
|
184
185
|
description
|
|
186
|
+
team { id }
|
|
185
187
|
labels(first: ${ISSUE_LABEL_PAGE_SIZE}) {
|
|
186
188
|
nodes { name }
|
|
187
189
|
}
|
|
@@ -206,7 +208,15 @@ export async function fetchResolvedIssue(arguments_) {
|
|
|
206
208
|
warnIfDisabledFallback(ticket, parsed, config);
|
|
207
209
|
const model = parsed === undefined || parsed.model === AGENT_ANY_MODEL ? config.models.default : parsed.model;
|
|
208
210
|
const runner = parsed?.runner ?? "local";
|
|
209
|
-
return {
|
|
211
|
+
return {
|
|
212
|
+
uuid: issue.id,
|
|
213
|
+
title: issue.title,
|
|
214
|
+
description,
|
|
215
|
+
repository,
|
|
216
|
+
model,
|
|
217
|
+
runner,
|
|
218
|
+
teamId: issue.team?.id ?? "",
|
|
219
|
+
};
|
|
210
220
|
}
|
|
211
221
|
function parseRepository(arguments_) {
|
|
212
222
|
const { description, config, repositoryRegex, ticket } = arguments_;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LinearClient } from "@linear/sdk";
|
|
2
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
3
|
+
interface LinearIssueReference {
|
|
4
|
+
id: string;
|
|
5
|
+
uuid: string;
|
|
6
|
+
teamId: string;
|
|
7
|
+
}
|
|
8
|
+
interface LinearIssueStatusUpdater {
|
|
9
|
+
markInProgress(issue: LinearIssueReference): Promise<void>;
|
|
10
|
+
resetMissingInProgressCache(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function createLinearIssueStatusUpdater(arguments_: {
|
|
13
|
+
config: ResolvedConfig;
|
|
14
|
+
client: LinearClient;
|
|
15
|
+
}): LinearIssueStatusUpdater;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=linearIssueStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linearIssueStatus.d.ts","sourceRoot":"","sources":["../../src/lib/linearIssueStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,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,2BAA2B,IAAI,IAAI,CAAC;CACrC;AAED,wBAAgB,8BAA8B,CAAC,UAAU,EAAE;IACzD,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB,GAAG,wBAAwB,CAgD3B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { log } from "./util.js";
|
|
2
|
+
export function createLinearIssueStatusUpdater(arguments_) {
|
|
3
|
+
const { config, client } = arguments_;
|
|
4
|
+
const inProgressStateByTeam = new Map();
|
|
5
|
+
let teamsMissingInProgress = new Set();
|
|
6
|
+
async function getInProgressStateId(teamId) {
|
|
7
|
+
if (teamId.length === 0) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const cached = inProgressStateByTeam.get(teamId);
|
|
11
|
+
if (cached !== undefined) {
|
|
12
|
+
return cached;
|
|
13
|
+
}
|
|
14
|
+
// Negative cache is reset by dispatcher each iteration so a team that's
|
|
15
|
+
// fixed in Linear during a watch session auto-recovers on the next tick.
|
|
16
|
+
if (teamsMissingInProgress.has(teamId)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const team = await client.team(teamId);
|
|
20
|
+
const states = await team.states();
|
|
21
|
+
const inProgress = states.nodes.find((state) => state.name === config.linear.statuses.inProgress);
|
|
22
|
+
if (inProgress?.id === undefined) {
|
|
23
|
+
teamsMissingInProgress.add(teamId);
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
inProgressStateByTeam.set(teamId, inProgress.id);
|
|
27
|
+
return inProgress.id;
|
|
28
|
+
}
|
|
29
|
+
async function markInProgress(issue) {
|
|
30
|
+
const stateId = await getInProgressStateId(issue.teamId);
|
|
31
|
+
if (stateId === undefined) {
|
|
32
|
+
throw new Error(`Could not find "${config.linear.statuses.inProgress}" state for ${issue.id} (team ${issue.teamId.length > 0 ? issue.teamId : "?"}). Verify the status name in linear.statuses.inProgress matches the team's workflow.`);
|
|
33
|
+
}
|
|
34
|
+
await client.updateIssue(issue.uuid, { stateId });
|
|
35
|
+
log(`Marked ${issue.id} as ${config.linear.statuses.inProgress}`);
|
|
36
|
+
}
|
|
37
|
+
function resetMissingInProgressCache() {
|
|
38
|
+
teamsMissingInProgress = new Set();
|
|
39
|
+
}
|
|
40
|
+
return { markInProgress, resetMissingInProgressCache };
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.2",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle, remote runners, and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"husky": "9.1.7",
|
|
85
85
|
"jscpd": "4.0.9",
|
|
86
86
|
"knip": "6.9.0",
|
|
87
|
-
"lint-staged": "
|
|
87
|
+
"lint-staged": "17.0.4",
|
|
88
88
|
"markdownlint-cli2": "0.22.1",
|
|
89
89
|
"nx": "22.7.1",
|
|
90
90
|
"oxfmt": "0.47.0",
|