@clipboard-health/groundcrew 4.26.0 → 4.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2 -2
- package/dist/commands/dispatcher.d.ts +2 -2
- package/dist/commands/dispatcher.js +19 -19
- package/dist/commands/doctor.js +14 -14
- package/dist/commands/eligibility.d.ts +19 -19
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +21 -21
- package/dist/commands/init.d.ts +4 -4
- package/dist/commands/init.js +17 -17
- package/dist/commands/interruptWorkspace.js +3 -3
- package/dist/commands/orchestrator.js +2 -2
- package/dist/commands/resumeWorkspace.js +9 -9
- package/dist/commands/setupWorkspace.d.ts +1 -1
- package/dist/commands/setupWorkspace.js +14 -14
- package/dist/commands/status.js +4 -4
- package/dist/commands/task.js +8 -8
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/lib/adapterDefinition.d.ts +1 -1
- package/dist/lib/adapters/linear/create.js +1 -1
- package/dist/lib/adapters/linear/factory.js +2 -2
- package/dist/lib/adapters/linear/fetch.d.ts +6 -6
- package/dist/lib/adapters/linear/fetch.js +28 -28
- package/dist/lib/adapters/linear/parsing.d.ts +7 -7
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.js +14 -14
- package/dist/lib/adapters/shell/factory.js +1 -1
- package/dist/lib/adapters/shell/schema.d.ts +3 -3
- package/dist/lib/adapters/shell/schema.js +2 -2
- package/dist/lib/adapters/todo-txt/normalizer.js +3 -3
- package/dist/lib/adapters/todo-txt/source.d.ts.map +1 -1
- package/dist/lib/adapters/todo-txt/source.js +5 -7
- package/dist/lib/agentLaunch.d.ts +4 -4
- package/dist/lib/agentLaunch.js +7 -7
- package/dist/lib/cmuxAdapter.js +1 -1
- package/dist/lib/config.d.ts +22 -22
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +47 -44
- package/dist/lib/launchCommand.d.ts +4 -4
- package/dist/lib/launchCommand.js +4 -4
- package/dist/lib/runState.d.ts +2 -2
- package/dist/lib/runState.js +4 -4
- package/dist/lib/srtLaunch.d.ts +2 -2
- package/dist/lib/taskSource.d.ts +4 -4
- package/dist/lib/taskSource.d.ts.map +1 -1
- package/dist/lib/taskSource.js +1 -1
- package/dist/lib/testing/canonicalFixtures.js +2 -2
- package/dist/lib/usage.d.ts +7 -7
- package/dist/lib/usage.js +20 -20
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ async function contextFromLinear(config, task, worktree) {
|
|
|
35
35
|
return {
|
|
36
36
|
task,
|
|
37
37
|
repository: resolved.repository,
|
|
38
|
-
|
|
38
|
+
agent: resolved.agent,
|
|
39
39
|
worktree,
|
|
40
40
|
title: resolved.title,
|
|
41
41
|
description: resolved.description,
|
|
@@ -50,7 +50,7 @@ async function contextFromState(config, task, state, worktree) {
|
|
|
50
50
|
return {
|
|
51
51
|
task,
|
|
52
52
|
repository: state.repository,
|
|
53
|
-
|
|
53
|
+
agent: state.agent,
|
|
54
54
|
worktree,
|
|
55
55
|
title: details?.title ?? task.toUpperCase(),
|
|
56
56
|
description: details?.description ?? "",
|
|
@@ -70,7 +70,7 @@ async function buildResumeContext(config, task) {
|
|
|
70
70
|
if (state !== undefined) {
|
|
71
71
|
return await contextFromState(config, task, state, worktree);
|
|
72
72
|
}
|
|
73
|
-
// The cold-resume path resolves repository +
|
|
73
|
+
// The cold-resume path resolves repository + agent from Linear alone, so it
|
|
74
74
|
// can't proceed when Linear is disabled. Fail with a clear reason instead of
|
|
75
75
|
// the cryptic missing-API-key error getLinearClient() would otherwise raise.
|
|
76
76
|
if (!isLinearEnabled(config)) {
|
|
@@ -113,13 +113,13 @@ export async function resumeWorkspace(config, options) {
|
|
|
113
113
|
const task = options.task.toLowerCase();
|
|
114
114
|
await failIfWorkspaceAlreadyLive(config, task);
|
|
115
115
|
const context = await buildResumeContext(config, task);
|
|
116
|
-
const definition = config.
|
|
116
|
+
const definition = config.agents.definitions[context.agent];
|
|
117
117
|
if (definition === undefined) {
|
|
118
|
-
throw new Error(`Unknown
|
|
118
|
+
throw new Error(`Unknown agent: ${context.agent}`);
|
|
119
119
|
}
|
|
120
120
|
const { runner, sandboxName, ensureReady } = await prepareAgentLaunch({
|
|
121
121
|
config,
|
|
122
|
-
|
|
122
|
+
agent: context.agent,
|
|
123
123
|
definition,
|
|
124
124
|
purpose: "resumes",
|
|
125
125
|
});
|
|
@@ -169,7 +169,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
169
169
|
name: task,
|
|
170
170
|
cwd: context.worktree.dir,
|
|
171
171
|
command: launchCmd,
|
|
172
|
-
|
|
172
|
+
agent: context.agent,
|
|
173
173
|
color: definition.color,
|
|
174
174
|
});
|
|
175
175
|
}
|
|
@@ -187,7 +187,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
187
187
|
state: {
|
|
188
188
|
task,
|
|
189
189
|
repository: context.repository,
|
|
190
|
-
|
|
190
|
+
agent: context.agent,
|
|
191
191
|
worktreeDir: context.worktree.dir,
|
|
192
192
|
branchName: context.worktree.branchName,
|
|
193
193
|
workspaceName: task,
|
|
@@ -196,7 +196,7 @@ export async function resumeWorkspace(config, options) {
|
|
|
196
196
|
...(context.reason === undefined ? {} : { reason: context.reason }),
|
|
197
197
|
},
|
|
198
198
|
});
|
|
199
|
-
log(`Resumed ${task} in ${context.worktree.dir} (${context.
|
|
199
|
+
log(`Resumed ${task} in ${context.worktree.dir} (${context.agent})`);
|
|
200
200
|
}
|
|
201
201
|
export async function resumeWorkspaceCli(argv) {
|
|
202
202
|
const config = await loadConfig();
|
|
@@ -27,15 +27,15 @@ function stagePrompt(input) {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
export async function setupWorkspace(config, options, runOptions = {}) {
|
|
30
|
-
const { task, repository,
|
|
30
|
+
const { task, repository, agent } = options;
|
|
31
31
|
const { signal } = runOptions;
|
|
32
|
-
const definition = config.
|
|
32
|
+
const definition = config.agents.definitions[agent];
|
|
33
33
|
if (!definition) {
|
|
34
|
-
throw new Error(`Unknown
|
|
34
|
+
throw new Error(`Unknown agent: ${agent}`);
|
|
35
35
|
}
|
|
36
36
|
const { runner, sandboxName, ensureReady } = await prepareAgentLaunch({
|
|
37
37
|
config,
|
|
38
|
-
|
|
38
|
+
agent,
|
|
39
39
|
definition,
|
|
40
40
|
purpose: "runs",
|
|
41
41
|
...(signal === undefined ? {} : { signal }),
|
|
@@ -117,7 +117,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
117
117
|
name: task,
|
|
118
118
|
cwd: launchDir,
|
|
119
119
|
command: launchCmd,
|
|
120
|
-
|
|
120
|
+
agent,
|
|
121
121
|
color: definition.color,
|
|
122
122
|
...(signal === undefined ? {} : { signal }),
|
|
123
123
|
});
|
|
@@ -125,7 +125,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
125
125
|
config,
|
|
126
126
|
task,
|
|
127
127
|
repository,
|
|
128
|
-
|
|
128
|
+
agent,
|
|
129
129
|
worktreeDir: launchDir,
|
|
130
130
|
branchName,
|
|
131
131
|
workspaceName: task,
|
|
@@ -133,7 +133,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
133
133
|
title: taskDetails.title,
|
|
134
134
|
...(taskDetails.url === undefined ? {} : { url: taskDetails.url }),
|
|
135
135
|
});
|
|
136
|
-
log(`${okMark()} "${task}" launched (${
|
|
136
|
+
log(`${okMark()} "${task}" launched (${agent}) worktree ${worktreeName}`);
|
|
137
137
|
debug(` Worktree: ${launchDir}`);
|
|
138
138
|
debug(` Branch: ${branchName}`);
|
|
139
139
|
if (accessHint !== undefined) {
|
|
@@ -146,7 +146,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
146
146
|
config,
|
|
147
147
|
task,
|
|
148
148
|
repository,
|
|
149
|
-
|
|
149
|
+
agent,
|
|
150
150
|
worktreeDir: launchDir,
|
|
151
151
|
branchName,
|
|
152
152
|
workspaceName: task,
|
|
@@ -209,7 +209,7 @@ function recordRunStateBestEffort(arguments_) {
|
|
|
209
209
|
state: {
|
|
210
210
|
task: arguments_.task,
|
|
211
211
|
repository: arguments_.repository,
|
|
212
|
-
|
|
212
|
+
agent: arguments_.agent,
|
|
213
213
|
worktreeDir: arguments_.worktreeDir,
|
|
214
214
|
branchName: arguments_.branchName,
|
|
215
215
|
workspaceName: arguments_.workspaceName,
|
|
@@ -282,19 +282,19 @@ export async function setupWorkspaceCli(task, options = {}) {
|
|
|
282
282
|
if (resolved === undefined) {
|
|
283
283
|
throw new Error(`Task ${task} not found across configured sources.`);
|
|
284
284
|
}
|
|
285
|
-
if (resolved.repository === undefined || resolved.
|
|
286
|
-
throw new Error(`Task ${task} resolved but isn't groundcrew-eligible (missing agent-* label or repository/
|
|
285
|
+
if (resolved.repository === undefined || resolved.agent === undefined) {
|
|
286
|
+
throw new Error(`Task ${task} resolved but isn't groundcrew-eligible (missing agent-* label or repository/agent).`);
|
|
287
287
|
}
|
|
288
|
-
log(`Resolved ${task}: repository=${resolved.repository},
|
|
288
|
+
log(`Resolved ${task}: repository=${resolved.repository}, agent=${resolved.agent}`);
|
|
289
289
|
if (options.dryRun === true) {
|
|
290
|
-
log(`[dry-run] Would launch ${task} in ${resolved.repository} (${resolved.
|
|
290
|
+
log(`[dry-run] Would launch ${task} in ${resolved.repository} (${resolved.agent})`);
|
|
291
291
|
return;
|
|
292
292
|
}
|
|
293
293
|
const naturalId = naturalIdFromCanonical(resolved.id);
|
|
294
294
|
await setupWorkspace(config, {
|
|
295
295
|
task: naturalId,
|
|
296
296
|
repository: resolved.repository,
|
|
297
|
-
|
|
297
|
+
agent: resolved.agent,
|
|
298
298
|
details: {
|
|
299
299
|
title: resolved.title,
|
|
300
300
|
description: resolved.description,
|
package/dist/commands/status.js
CHANGED
|
@@ -81,9 +81,9 @@ function formatRunState(state, flags = []) {
|
|
|
81
81
|
return "(none)";
|
|
82
82
|
}
|
|
83
83
|
// Only the leading lifecycle token gains the reconciliation flags; the
|
|
84
|
-
// `;`-separated detail (
|
|
84
|
+
// `;`-separated detail (agent/updated/resumes/reason) is preserved verbatim.
|
|
85
85
|
const lifecycle = flags.length === 0 ? state.state : `${state.state} (${flags.join(", ")})`;
|
|
86
|
-
const summary = `${lifecycle};
|
|
86
|
+
const summary = `${lifecycle}; agent=${state.agent}; updated=${state.updatedAt}; resumes=${state.resumeCount}`;
|
|
87
87
|
const detail = state.reason ?? state.detail;
|
|
88
88
|
return detail === undefined ? summary : `${summary}; ${detail}`;
|
|
89
89
|
}
|
|
@@ -426,7 +426,7 @@ function writeQueueIssue(issue) {
|
|
|
426
426
|
writeOutput(issue.url === undefined ? naturalId : `${naturalId} ${issue.url}`);
|
|
427
427
|
writeOutput(inventoryField("title", issue.title));
|
|
428
428
|
writeOutput(inventoryField("repo", issue.repository));
|
|
429
|
-
writeOutput(inventoryField("
|
|
429
|
+
writeOutput(inventoryField("agent", issue.agent));
|
|
430
430
|
}
|
|
431
431
|
async function buildBoardForStatus(config) {
|
|
432
432
|
const sources = await buildSources(sourcesFromConfig(config), { globalConfig: config });
|
|
@@ -480,7 +480,7 @@ function writeQueueSections(boardResult) {
|
|
|
480
480
|
return;
|
|
481
481
|
}
|
|
482
482
|
// Only groundcrew-eligible Todos are dispatchable; non-eligible ones lack
|
|
483
|
-
// a repo or
|
|
483
|
+
// a repo or agent, so `crew run` would skip them.
|
|
484
484
|
const todos = boardResult.issues.filter(isTodoSourceIssue).filter(isGroundcrewIssue);
|
|
485
485
|
const ready = todos.filter((i) => !hasOpenBlocker(i));
|
|
486
486
|
const blocked = todos.filter(hasOpenBlocker);
|
package/dist/commands/task.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
2
|
-
import {
|
|
2
|
+
import { AGENT_ANY, loadConfig } from "../lib/config.js";
|
|
3
3
|
import { naturalIdFromCanonical, } from "../lib/taskSource.js";
|
|
4
4
|
import { parseSourceFilterArgs, writeOutput } from "../lib/util.js";
|
|
5
5
|
const TASK_USAGE = `Usage: crew task <subcommand>
|
|
@@ -14,7 +14,7 @@ const LIST_USAGE = `Usage: crew task list [options]
|
|
|
14
14
|
Options:
|
|
15
15
|
--source <name> Limit to one source.
|
|
16
16
|
--status <status> Filter by status. Repeatable.
|
|
17
|
-
--agent <name> Filter by agent
|
|
17
|
+
--agent <name> Filter by agent name.
|
|
18
18
|
--repo <owner/repo> Filter by repository.
|
|
19
19
|
--blocked Show only blocked tasks.
|
|
20
20
|
--unblocked Show only unblocked tasks.
|
|
@@ -235,7 +235,7 @@ function parseCreateOptions(argv) {
|
|
|
235
235
|
}
|
|
236
236
|
const input = {
|
|
237
237
|
title,
|
|
238
|
-
agent: state.agent ??
|
|
238
|
+
agent: state.agent ?? AGENT_ANY,
|
|
239
239
|
projects: state.projects,
|
|
240
240
|
contexts: state.contexts,
|
|
241
241
|
dependencies: state.dependencies,
|
|
@@ -271,7 +271,7 @@ function filterTasks(tasks, options) {
|
|
|
271
271
|
filtered = filtered.filter((task) => options.statuses.includes(task.status));
|
|
272
272
|
}
|
|
273
273
|
if (options.agent !== undefined) {
|
|
274
|
-
filtered = filtered.filter((task) => task.
|
|
274
|
+
filtered = filtered.filter((task) => task.agent === options.agent);
|
|
275
275
|
}
|
|
276
276
|
if (options.repository !== undefined) {
|
|
277
277
|
filtered = filtered.filter((task) => task.repository === options.repository);
|
|
@@ -295,7 +295,7 @@ function printableTask(task) {
|
|
|
295
295
|
description: task.description,
|
|
296
296
|
status: task.status,
|
|
297
297
|
repository: task.repository,
|
|
298
|
-
|
|
298
|
+
agent: task.agent,
|
|
299
299
|
assignee: task.assignee,
|
|
300
300
|
updatedAt: task.updatedAt,
|
|
301
301
|
blockers: task.blockers,
|
|
@@ -318,7 +318,7 @@ function writeTaskTable(tasks) {
|
|
|
318
318
|
const rows = tasks.map((task) => ({
|
|
319
319
|
id: task.id,
|
|
320
320
|
status: task.status,
|
|
321
|
-
agent: task.
|
|
321
|
+
agent: task.agent ?? "-",
|
|
322
322
|
repository: task.repository ?? "-",
|
|
323
323
|
blocked: taskIsBlocked(task) ? "yes" : "no",
|
|
324
324
|
title: task.title,
|
|
@@ -418,8 +418,8 @@ function writeTaskDetails(task) {
|
|
|
418
418
|
if (task.repository !== undefined) {
|
|
419
419
|
writeOutput(`repo: ${task.repository}`);
|
|
420
420
|
}
|
|
421
|
-
if (task.
|
|
422
|
-
writeOutput(`agent: ${task.
|
|
421
|
+
if (task.agent !== undefined) {
|
|
422
|
+
writeOutput(`agent: ${task.agent}`);
|
|
423
423
|
}
|
|
424
424
|
if (task.url !== undefined) {
|
|
425
425
|
writeOutput(`url: ${task.url}`);
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,12 @@ export { orchestrate, type OrchestratorOptions } from "./commands/orchestrator.t
|
|
|
6
6
|
export { resumeWorkspace, type ResumeWorkspaceOptions } from "./commands/resumeWorkspace.ts";
|
|
7
7
|
export { setupWorkspace, type SetupWorkspaceOptions } from "./commands/setupWorkspace.ts";
|
|
8
8
|
export { status, type StatusOptions } from "./commands/status.ts";
|
|
9
|
-
export type { Config, HookCommands,
|
|
9
|
+
export type { Config, HookCommands, AgentDefinition, ResolvedConfig, SourceConfig, } from "./lib/config.ts";
|
|
10
10
|
export { loadConfig } from "./lib/config.ts";
|
|
11
11
|
export { readRunState, recordRunState, removeRunState, runStateDirectory, runStatePath, updateRunState, type RunLifecycleState, type RunState, } from "./lib/runState.ts";
|
|
12
12
|
export { fetchBlockersForTask, fetchInProgressIssueCount, fetchRawLinearIssue, fetchResolvedIssue, isIssueInProgress, isIssueTodo, isTerminalStateType, isTerminalStatusForBlocker, isTerminalStatusForIssue, type RawLinearIssue, } from "./lib/adapters/linear/fetch.ts";
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
13
|
+
export { resolveAgentFor, resolveRepositoryFor, type AgentResolution, type RepositoryResolution, } from "./lib/adapters/linear/parsing.ts";
|
|
14
|
+
export { getUsageByAgent, type UsageByAgent } from "./lib/usage.ts";
|
|
15
15
|
export { type Board, createBoard } from "./lib/board.ts";
|
|
16
16
|
export { buildSources, buildSourcesWith } from "./lib/buildSources.ts";
|
|
17
17
|
export type { AdapterContext, AdapterDefinition } from "./lib/adapterDefinition.ts";
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ export { status } from "./commands/status.js";
|
|
|
9
9
|
export { loadConfig } from "./lib/config.js";
|
|
10
10
|
export { readRunState, recordRunState, removeRunState, runStateDirectory, runStatePath, updateRunState, } from "./lib/runState.js";
|
|
11
11
|
export { fetchBlockersForTask, fetchInProgressIssueCount, fetchRawLinearIssue, fetchResolvedIssue, isIssueInProgress, isIssueTodo, isTerminalStateType, isTerminalStatusForBlocker, isTerminalStatusForIssue, } from "./lib/adapters/linear/fetch.js";
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
12
|
+
export { resolveAgentFor, resolveRepositoryFor, } from "./lib/adapters/linear/parsing.js";
|
|
13
|
+
export { getUsageByAgent } from "./lib/usage.js";
|
|
14
14
|
export { createBoard } from "./lib/board.js";
|
|
15
15
|
export { buildSources, buildSourcesWith } from "./lib/buildSources.js";
|
|
16
16
|
export { adapterRegistry, buildRegistry, buildSourceConfigSchema, listAdapterDirectories, } from "./lib/adapters/registry.js";
|
|
@@ -10,7 +10,7 @@ import type { TaskSource } from "./taskSource.ts";
|
|
|
10
10
|
/**
|
|
11
11
|
* Cross-cutting context every adapter receives at construction time. Holds
|
|
12
12
|
* the global resolved config so adapters can read shared concerns (the
|
|
13
|
-
* `workspace.knownRepositories` list, `
|
|
13
|
+
* `workspace.knownRepositories` list, `agents.*` definitions, etc.) without
|
|
14
14
|
* each one duplicating them in its per-source config block.
|
|
15
15
|
*/
|
|
16
16
|
export interface AdapterContext {
|
|
@@ -51,7 +51,7 @@ export async function createLinearIssue(arguments_) {
|
|
|
51
51
|
assignee: resolved.assignee,
|
|
52
52
|
updatedAt: resolved.updatedAt,
|
|
53
53
|
repository: resolved.repository,
|
|
54
|
-
|
|
54
|
+
agent: resolved.agent,
|
|
55
55
|
teamId: resolved.teamId,
|
|
56
56
|
blockers: resolved.blockers,
|
|
57
57
|
hasMoreBlockers: resolved.hasMoreBlockers,
|
|
@@ -76,7 +76,7 @@ export function toCanonicalIssue(linearIssue, sourceName, statusNames = DEFAULT_
|
|
|
76
76
|
statusNames,
|
|
77
77
|
}),
|
|
78
78
|
repository: linearIssue.repository,
|
|
79
|
-
|
|
79
|
+
agent: linearIssue.agent,
|
|
80
80
|
assignee: linearIssue.assignee,
|
|
81
81
|
updatedAt: linearIssue.updatedAt,
|
|
82
82
|
blockers: linearIssue.blockers.map((b) => toCanonicalBlocker(b, sourceName, statusNames)),
|
|
@@ -150,7 +150,7 @@ export function createLinearTaskSource(config, context) {
|
|
|
150
150
|
statusNames,
|
|
151
151
|
}),
|
|
152
152
|
repository: resolved.repository,
|
|
153
|
-
|
|
153
|
+
agent: resolved.agent,
|
|
154
154
|
assignee: resolved.assignee,
|
|
155
155
|
updatedAt: resolved.updatedAt,
|
|
156
156
|
blockers: resolved.blockers.map((b) => toCanonicalBlocker(b, sourceName, statusNames)),
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { LinearClient } from "@linear/sdk";
|
|
11
11
|
import type { ResolvedConfig } from "../../config.ts";
|
|
12
|
-
import { type
|
|
12
|
+
import { type AgentResolution } from "./parsing.ts";
|
|
13
13
|
export declare const ISSUES_PAGE_SIZE = 250;
|
|
14
14
|
export interface Blocker {
|
|
15
15
|
id: string;
|
|
@@ -41,7 +41,7 @@ export interface Issue {
|
|
|
41
41
|
*/
|
|
42
42
|
repository: string | undefined;
|
|
43
43
|
/** Parsed from the `agent-*` label when present, including non-Todo tasks for slot logs. */
|
|
44
|
-
|
|
44
|
+
agent: string | undefined;
|
|
45
45
|
teamId: string;
|
|
46
46
|
blockers: Blocker[];
|
|
47
47
|
hasMoreBlockers: boolean;
|
|
@@ -56,7 +56,7 @@ export interface Issue {
|
|
|
56
56
|
* variant just shapes the adapter's local Linear type.
|
|
57
57
|
*/
|
|
58
58
|
export type GroundcrewIssue = Issue & {
|
|
59
|
-
|
|
59
|
+
agent: string;
|
|
60
60
|
repository: string;
|
|
61
61
|
};
|
|
62
62
|
/**
|
|
@@ -103,7 +103,7 @@ export interface IssueRelationNode {
|
|
|
103
103
|
} | null;
|
|
104
104
|
} | null;
|
|
105
105
|
}
|
|
106
|
-
export declare function
|
|
106
|
+
export declare function agentForResolution(resolution: Exclude<AgentResolution, {
|
|
107
107
|
kind: "no-label";
|
|
108
108
|
}>): string;
|
|
109
109
|
interface ResolvedIssue {
|
|
@@ -111,7 +111,7 @@ interface ResolvedIssue {
|
|
|
111
111
|
title: string;
|
|
112
112
|
description: string;
|
|
113
113
|
repository: string;
|
|
114
|
-
|
|
114
|
+
agent: string;
|
|
115
115
|
teamId: string;
|
|
116
116
|
stateType: string;
|
|
117
117
|
status: string;
|
|
@@ -168,7 +168,7 @@ export declare function fetchResolvedIssue(arguments_: {
|
|
|
168
168
|
config: ResolvedConfig;
|
|
169
169
|
task: string;
|
|
170
170
|
}): Promise<ResolvedIssue>;
|
|
171
|
-
export declare function warnIfNotEnabledFallback(task: string,
|
|
171
|
+
export declare function warnIfNotEnabledFallback(task: string, agentResolution: AgentResolution, config: ResolvedConfig): void;
|
|
172
172
|
export declare function blockersFromRelations(relations: IssueRelationNode[]): Blocker[];
|
|
173
173
|
export {};
|
|
174
174
|
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { RepositoryResolutionError } from "../../taskSource.js";
|
|
11
11
|
import { log, styleWarning } from "../../util.js";
|
|
12
|
-
import { AGENT_LABEL_PREFIX,
|
|
12
|
+
import { AGENT_LABEL_PREFIX, resolveAgentFor, resolveRepositoryFor, } from "./parsing.js";
|
|
13
13
|
export const ISSUES_PAGE_SIZE = 250;
|
|
14
14
|
// `state.type` values surfaced by `fetch()`. `backlog` / `triage` are dropped
|
|
15
15
|
// at the GraphQL filter; everything else is post-classified by these names.
|
|
@@ -134,34 +134,34 @@ async function fetchBoard(client, config) {
|
|
|
134
134
|
}));
|
|
135
135
|
return { timestamp: new Date().toISOString(), issues, parentSkips };
|
|
136
136
|
}
|
|
137
|
-
export function
|
|
137
|
+
export function agentForResolution(resolution) {
|
|
138
138
|
if (resolution.kind === "matched") {
|
|
139
|
-
return resolution.
|
|
139
|
+
return resolution.agent;
|
|
140
140
|
}
|
|
141
141
|
if (resolution.kind === "not-enabled-fallback") {
|
|
142
|
-
return resolution.
|
|
142
|
+
return resolution.fallbackAgent;
|
|
143
143
|
}
|
|
144
144
|
return "any";
|
|
145
145
|
}
|
|
146
146
|
function resolveAgentMetadata(arguments_) {
|
|
147
|
-
const { task, description,
|
|
147
|
+
const { task, description, agentResolution, config, isTodo } = arguments_;
|
|
148
148
|
let repository;
|
|
149
|
-
let
|
|
150
|
-
if (
|
|
151
|
-
return { repository,
|
|
149
|
+
let agent;
|
|
150
|
+
if (agentResolution.kind === "no-label") {
|
|
151
|
+
return { repository, agent };
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
agent = agentForResolution(agentResolution);
|
|
154
154
|
if (isTodo) {
|
|
155
155
|
const resolution = resolveRepositoryFor({ description, config });
|
|
156
156
|
if (resolution.kind === "ok") {
|
|
157
157
|
({ repository } = resolution);
|
|
158
158
|
}
|
|
159
159
|
else {
|
|
160
|
-
|
|
160
|
+
agent = undefined;
|
|
161
161
|
log(styleWarning(`WARNING: ${task} has an ${AGENT_LABEL_PREFIX}* label but no known repository in its description; skipping dispatch. Add one of workspace.knownRepositories to the description, or remove the ${AGENT_LABEL_PREFIX}* label: ${config.workspace.knownRepositories.join(", ")}`));
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
return { repository,
|
|
164
|
+
return { repository, agent };
|
|
165
165
|
}
|
|
166
166
|
function buildLinearIssue(input) {
|
|
167
167
|
return {
|
|
@@ -176,7 +176,7 @@ function buildLinearIssue(input) {
|
|
|
176
176
|
assignee: input.assigneeName ?? "Unassigned",
|
|
177
177
|
updatedAt: input.updatedAt,
|
|
178
178
|
repository: input.repository,
|
|
179
|
-
|
|
179
|
+
agent: input.agent,
|
|
180
180
|
teamId: input.teamId,
|
|
181
181
|
url: input.url,
|
|
182
182
|
priority: input.priority,
|
|
@@ -185,13 +185,13 @@ function buildLinearIssue(input) {
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
function issueFromNode(node, config) {
|
|
188
|
-
const
|
|
189
|
-
warnIfNotEnabledFallback(node.identifier,
|
|
190
|
-
const { repository,
|
|
188
|
+
const agentResolution = resolveAgentFor({ labels: node.labels.nodes, config });
|
|
189
|
+
warnIfNotEnabledFallback(node.identifier, agentResolution, config);
|
|
190
|
+
const { repository, agent } = resolveAgentMetadata({
|
|
191
191
|
task: node.identifier,
|
|
192
192
|
/* v8 ignore next @preserve -- BoardIssues query selects description; the ?? guard normalises a null vs undefined edge */
|
|
193
193
|
description: node.description ?? undefined,
|
|
194
|
-
|
|
194
|
+
agentResolution,
|
|
195
195
|
config,
|
|
196
196
|
isTodo: node.state?.type === "unstarted",
|
|
197
197
|
});
|
|
@@ -210,7 +210,7 @@ function issueFromNode(node, config) {
|
|
|
210
210
|
assigneeName: node.assignee?.name,
|
|
211
211
|
updatedAt: node.updatedAt,
|
|
212
212
|
repository,
|
|
213
|
-
|
|
213
|
+
agent,
|
|
214
214
|
teamId: node.team?.id ?? "",
|
|
215
215
|
url: node.url,
|
|
216
216
|
priority: node.priority,
|
|
@@ -365,21 +365,21 @@ export async function fetchResolvedIssue(arguments_) {
|
|
|
365
365
|
repositories: config.workspace.knownRepositories,
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
|
-
const
|
|
369
|
-
warnIfNotEnabledFallback(task,
|
|
370
|
-
let
|
|
371
|
-
if (
|
|
372
|
-
({
|
|
368
|
+
const agentResolution = resolveAgentFor({ labels: raw.labels, config });
|
|
369
|
+
warnIfNotEnabledFallback(task, agentResolution, config);
|
|
370
|
+
let agent = config.agents.default;
|
|
371
|
+
if (agentResolution.kind === "matched") {
|
|
372
|
+
({ agent } = agentResolution);
|
|
373
373
|
}
|
|
374
|
-
else if (
|
|
375
|
-
|
|
374
|
+
else if (agentResolution.kind === "not-enabled-fallback") {
|
|
375
|
+
agent = agentResolution.fallbackAgent;
|
|
376
376
|
}
|
|
377
377
|
return {
|
|
378
378
|
uuid: raw.uuid,
|
|
379
379
|
title: raw.title,
|
|
380
380
|
description: raw.description,
|
|
381
381
|
repository: repositoryResolution.repository,
|
|
382
|
-
|
|
382
|
+
agent,
|
|
383
383
|
teamId: raw.teamId,
|
|
384
384
|
stateType: raw.stateType,
|
|
385
385
|
status: raw.stateName,
|
|
@@ -392,11 +392,11 @@ export async function fetchResolvedIssue(arguments_) {
|
|
|
392
392
|
priority: raw.priority,
|
|
393
393
|
};
|
|
394
394
|
}
|
|
395
|
-
export function warnIfNotEnabledFallback(task,
|
|
396
|
-
if (
|
|
395
|
+
export function warnIfNotEnabledFallback(task, agentResolution, config) {
|
|
396
|
+
if (agentResolution.kind !== "not-enabled-fallback") {
|
|
397
397
|
return;
|
|
398
398
|
}
|
|
399
|
-
log(`${task.toLowerCase()}: agent-${
|
|
399
|
+
log(`${task.toLowerCase()}: agent-${agentResolution.requestedAgent} label refers to an agent that is not enabled; falling back to agents.default (${config.agents.default})`);
|
|
400
400
|
}
|
|
401
401
|
export function blockersFromRelations(relations) {
|
|
402
402
|
return relations
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Linear adapter — parsing helpers for
|
|
2
|
+
* Linear adapter — parsing helpers for agent/repository resolution from
|
|
3
3
|
* issue labels and descriptions. Extracted from boardSource.ts (Task 10).
|
|
4
4
|
*/
|
|
5
5
|
import { type ResolvedConfig } from "../../config.ts";
|
|
@@ -10,17 +10,17 @@ export type RepositoryResolution = {
|
|
|
10
10
|
} | {
|
|
11
11
|
kind: "missing";
|
|
12
12
|
};
|
|
13
|
-
export type
|
|
13
|
+
export type AgentResolution = {
|
|
14
14
|
kind: "matched";
|
|
15
|
-
|
|
15
|
+
agent: string;
|
|
16
16
|
} | {
|
|
17
17
|
kind: "no-label";
|
|
18
18
|
} | {
|
|
19
19
|
kind: "agent-any";
|
|
20
20
|
} | {
|
|
21
21
|
kind: "not-enabled-fallback";
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
requestedAgent: string;
|
|
23
|
+
fallbackAgent: string;
|
|
24
24
|
};
|
|
25
25
|
export declare function buildRepositoryRegex(config: ResolvedConfig): RegExp;
|
|
26
26
|
export declare function resolveRepositoryFor(arguments_: {
|
|
@@ -34,11 +34,11 @@ interface ParseRepositoryArguments {
|
|
|
34
34
|
task: string;
|
|
35
35
|
}
|
|
36
36
|
export declare function parseRepository(arguments_: ParseRepositoryArguments): string;
|
|
37
|
-
export declare function
|
|
37
|
+
export declare function resolveAgentFor(arguments_: {
|
|
38
38
|
labels: {
|
|
39
39
|
name: string;
|
|
40
40
|
}[];
|
|
41
41
|
config: ResolvedConfig;
|
|
42
|
-
}):
|
|
42
|
+
}): AgentResolution;
|
|
43
43
|
export {};
|
|
44
44
|
//# sourceMappingURL=parsing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/parsing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/parsing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuC,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG3F,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5F,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AASpF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAUnE;AAiDD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE;IAC/C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,oBAAoB,CAuBvB;AAED,UAAU,wBAAwB;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,wBAAwB,GAAG,MAAM,CA2B5E;AAkDD,wBAAgB,eAAe,CAAC,UAAU,EAAE;IAC1C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,eAAe,CAiBlB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Linear adapter — parsing helpers for
|
|
2
|
+
* Linear adapter — parsing helpers for agent/repository resolution from
|
|
3
3
|
* issue labels and descriptions. Extracted from boardSource.ts (Task 10).
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { AGENT_ANY, isBuiltInAgentNotEnabled } from "../../config.js";
|
|
6
6
|
import { RepositoryResolutionError } from "../../taskSource.js";
|
|
7
7
|
export const AGENT_LABEL_PREFIX = "agent-";
|
|
8
8
|
function escapeRegex(value) {
|
|
@@ -105,40 +105,40 @@ function parseAgentLabels(labels, config) {
|
|
|
105
105
|
let notEnabledFallback;
|
|
106
106
|
for (const label of agentLabels) {
|
|
107
107
|
const name = label.name.slice(AGENT_LABEL_PREFIX.length);
|
|
108
|
-
if (name ===
|
|
109
|
-
return {
|
|
108
|
+
if (name === AGENT_ANY) {
|
|
109
|
+
return { agent: AGENT_ANY };
|
|
110
110
|
}
|
|
111
111
|
// Own-property check, not `in`: a label like `agent-toString` or
|
|
112
112
|
// `agent-__proto__` would otherwise resolve through the prototype chain
|
|
113
|
-
// instead of falling back to `
|
|
114
|
-
if (Object.hasOwn(config.
|
|
115
|
-
return {
|
|
113
|
+
// instead of falling back to `agents.default`.
|
|
114
|
+
if (Object.hasOwn(config.agents.definitions, name)) {
|
|
115
|
+
return { agent: name };
|
|
116
116
|
}
|
|
117
|
-
if (notEnabledFallback === undefined &&
|
|
117
|
+
if (notEnabledFallback === undefined && isBuiltInAgentNotEnabled(config, name)) {
|
|
118
118
|
notEnabledFallback = name;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
const fallback = {
|
|
121
|
+
const fallback = { agent: config.agents.default };
|
|
122
122
|
if (notEnabledFallback !== undefined) {
|
|
123
123
|
fallback.notEnabledFallback = notEnabledFallback;
|
|
124
124
|
}
|
|
125
125
|
return fallback;
|
|
126
126
|
}
|
|
127
|
-
export function
|
|
127
|
+
export function resolveAgentFor(arguments_) {
|
|
128
128
|
const { labels, config } = arguments_;
|
|
129
129
|
const parsed = parseAgentLabels(labels, config);
|
|
130
130
|
if (parsed === undefined) {
|
|
131
131
|
return { kind: "no-label" };
|
|
132
132
|
}
|
|
133
|
-
if (parsed.
|
|
133
|
+
if (parsed.agent === AGENT_ANY) {
|
|
134
134
|
return { kind: "agent-any" };
|
|
135
135
|
}
|
|
136
136
|
if (parsed.notEnabledFallback !== undefined) {
|
|
137
137
|
return {
|
|
138
138
|
kind: "not-enabled-fallback",
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
requestedAgent: parsed.notEnabledFallback,
|
|
140
|
+
fallbackAgent: parsed.agent,
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
|
-
return { kind: "matched",
|
|
143
|
+
return { kind: "matched", agent: parsed.agent };
|
|
144
144
|
}
|
|
@@ -76,7 +76,7 @@ export function toCanonicalIssue(shellIssue, sourceName) {
|
|
|
76
76
|
description: shellIssue.description,
|
|
77
77
|
status: shellIssue.status,
|
|
78
78
|
repository: shellIssue.repository ?? undefined,
|
|
79
|
-
|
|
79
|
+
agent: shellIssue.agent ?? undefined,
|
|
80
80
|
assignee: shellIssue.assignee,
|
|
81
81
|
updatedAt: shellIssue.updatedAt,
|
|
82
82
|
blockers,
|