@clipboard-health/groundcrew 3.1.4 → 3.1.6
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 +48 -0
- package/clearance-allow-hosts +1 -0
- package/crew.config.example.ts +20 -0
- package/dist/commands/dispatcher.d.ts +6 -0
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +17 -5
- package/dist/commands/orchestrator.d.ts.map +1 -1
- package/dist/commands/orchestrator.js +15 -1
- package/dist/commands/ticketDoctor.d.ts +12 -0
- package/dist/commands/ticketDoctor.d.ts.map +1 -1
- package/dist/commands/ticketDoctor.js +35 -8
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/lib/adapterDefinition.d.ts +27 -0
- package/dist/lib/adapterDefinition.d.ts.map +1 -0
- package/dist/lib/adapterDefinition.js +7 -0
- package/dist/lib/adapters/linear/factory.d.ts +27 -0
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -0
- package/dist/lib/adapters/linear/factory.js +161 -0
- package/dist/lib/adapters/linear/index.d.ts +5 -0
- package/dist/lib/adapters/linear/index.d.ts.map +1 -0
- package/dist/lib/adapters/linear/index.js +8 -0
- package/dist/lib/adapters/linear/schema.d.ts +14 -0
- package/dist/lib/adapters/linear/schema.d.ts.map +1 -0
- package/dist/lib/adapters/linear/schema.js +15 -0
- package/dist/lib/adapters/registry.d.ts +38 -0
- package/dist/lib/adapters/registry.d.ts.map +1 -0
- package/dist/lib/adapters/registry.js +82 -0
- package/dist/lib/adapters/shell/factory.d.ts +21 -0
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -0
- package/dist/lib/adapters/shell/factory.js +130 -0
- package/dist/lib/adapters/shell/index.d.ts +5 -0
- package/dist/lib/adapters/shell/index.d.ts.map +1 -0
- package/dist/lib/adapters/shell/index.js +8 -0
- package/dist/lib/adapters/shell/invoke.d.ts +45 -0
- package/dist/lib/adapters/shell/invoke.d.ts.map +1 -0
- package/dist/lib/adapters/shell/invoke.js +153 -0
- package/dist/lib/adapters/shell/schema.d.ts +90 -0
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -0
- package/dist/lib/adapters/shell/schema.js +54 -0
- package/dist/lib/board.d.ts +20 -0
- package/dist/lib/board.d.ts.map +1 -0
- package/dist/lib/board.js +77 -0
- package/dist/lib/boardSource.d.ts +23 -7
- package/dist/lib/boardSource.d.ts.map +1 -1
- package/dist/lib/boardSource.js +51 -11
- package/dist/lib/buildSources.d.ts +19 -0
- package/dist/lib/buildSources.d.ts.map +1 -0
- package/dist/lib/buildSources.js +34 -0
- package/dist/lib/config.d.ts +27 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +36 -0
- package/dist/lib/defaultBranch.d.ts +24 -0
- package/dist/lib/defaultBranch.d.ts.map +1 -0
- package/dist/lib/defaultBranch.js +39 -0
- package/dist/lib/ticketSource.d.ts +85 -0
- package/dist/lib/ticketSource.d.ts.map +1 -0
- package/dist/lib/ticketSource.js +26 -0
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +9 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -173,6 +173,53 @@ Three keys are required; everything else has a default.
|
|
|
173
173
|
|
|
174
174
|
Agent selection uses Linear labels: `agent-claude`, `agent-codex`, `agent-<name>`. `crew run` without `--ticket` only fetches tickets carrying an `agent-*` label — the GraphQL query filters server-side, so unlabeled tickets are never returned by Linear and do not appear on the board. Use `crew run --ticket <TICKET>` to provision an unlabeled ticket on demand (falls back to `models.default`). `agent-any` routes to the model with the most available session capacity. Todo tickets blocked by non-terminal blockers are skipped until their blockers reach a terminal status.
|
|
175
175
|
|
|
176
|
+
### Pluggable ticket sources
|
|
177
|
+
|
|
178
|
+
`sources` declares extra ticket-system adapters. The current release verifies configured extra sources during `crew run` startup; the dispatch loop still reads Linear through `linear.projects` until the consumer refactor lands. This lets you validate shell/Jira/local-plan integrations without changing existing Linear behavior.
|
|
179
|
+
|
|
180
|
+
The built-in `shell` adapter runs command templates and reads JSON from stdout:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
export default {
|
|
184
|
+
// ...
|
|
185
|
+
sources: [
|
|
186
|
+
{
|
|
187
|
+
kind: "shell",
|
|
188
|
+
name: "jira",
|
|
189
|
+
commands: {
|
|
190
|
+
verify: "jira me",
|
|
191
|
+
fetch: "~/.config/groundcrew/jira-fetch.sh",
|
|
192
|
+
resolveOne: "~/.config/groundcrew/jira-resolve.sh ${id}",
|
|
193
|
+
markInProgress: "jira issue move ${id} 'In Progress'",
|
|
194
|
+
},
|
|
195
|
+
timeouts: { fetch: 60_000 },
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`commands.fetch` must print a JSON array of issues. `commands.resolveOne`, when set, must print one issue, print nothing for "not found", or exit `3` for "not found". `commands.markInProgress`, when set, receives the issue's `sourceRef` as JSON on stdin. `${id}`, `${canonicalId}`, and `${name}` placeholders are shell-quoted before substitution.
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
[
|
|
205
|
+
{
|
|
206
|
+
"id": "JIRA-123",
|
|
207
|
+
"title": "Add retry logic",
|
|
208
|
+
"description": "Ticket body",
|
|
209
|
+
"status": "todo",
|
|
210
|
+
"repository": "your-org/your-repo",
|
|
211
|
+
"model": "claude",
|
|
212
|
+
"assignee": "Alice",
|
|
213
|
+
"updatedAt": "2026-05-22T15:00:00Z",
|
|
214
|
+
"blockers": [{ "id": "JIRA-122", "title": "Schema migration", "status": "done" }],
|
|
215
|
+
"hasMoreBlockers": false,
|
|
216
|
+
"sourceRef": { "nativeId": "10042" }
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Allowed `status` values are `todo`, `in-progress`, `in-review`, `done`, and `other`. Use `null` for `repository` or `model` when a ticket should not be groundcrew-eligible. `hasMoreBlockers` is optional and defaults to `false`; `sourceRef` is opaque data that groundcrew passes back to your writeback command.
|
|
222
|
+
|
|
176
223
|
### Prompt customization
|
|
177
224
|
|
|
178
225
|
Groundcrew ships one model-agnostic unattended prompt by default. It tells the agent to make reasonable assumptions, follow repository instructions, run documented verification, review its diff, open a PR when GitHub/`gh` is available, and include a workspace continuation hint when known.
|
|
@@ -203,6 +250,7 @@ This keeps package defaults portable while letting your private config reference
|
|
|
203
250
|
| `linear.projects[].statuses.inProgress` | `"In Progress"` | Status set after a workspace is provisioned; counts toward the shared `maximumInProgress`. |
|
|
204
251
|
| `linear.projects[].statuses.done` | `"Done"` | Status that triggers worktree cleanup for this project. |
|
|
205
252
|
| `linear.projects[].statuses.terminal` | `["Done"]` | Additional status names treated as terminal for cleanup and blocker checks. The project's `done` status is always included. |
|
|
253
|
+
| `sources` | `[]` | Additional pluggable ticket sources. Extra sources are verified at startup; Linear remains the dispatch read path until the consumer refactor. Built-in kinds: `shell`, `linear`. |
|
|
206
254
|
| `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
|
|
207
255
|
| `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
|
|
208
256
|
| `workspace.projectDir` | **required** | Parent dir for cloned repos and sibling ticket worktrees. |
|
package/clearance-allow-hosts
CHANGED
package/crew.config.example.ts
CHANGED
|
@@ -40,6 +40,26 @@ export default {
|
|
|
40
40
|
// Everything below is optional — defaults shown for reference. Uncomment
|
|
41
41
|
// and edit to override.
|
|
42
42
|
//
|
|
43
|
+
// // Additional pluggable ticket sources beyond the implicit built-in
|
|
44
|
+
// // Linear adapter (configured via `linear.projects` above). The most
|
|
45
|
+
// // common use is `kind: "shell"`, which wires any external system via
|
|
46
|
+
// // command templates that emit/consume JSON. See the shell adapter's
|
|
47
|
+
// // ShellIssue schema for the JSON contract `fetch` / `resolveOne` must
|
|
48
|
+
// // emit.
|
|
49
|
+
// sources: [
|
|
50
|
+
// {
|
|
51
|
+
// kind: "shell",
|
|
52
|
+
// name: "jira",
|
|
53
|
+
// commands: {
|
|
54
|
+
// verify: "jira me",
|
|
55
|
+
// fetch: "~/.config/groundcrew/jira-fetch.sh",
|
|
56
|
+
// resolveOne: "~/.config/groundcrew/jira-resolve.sh ${id}",
|
|
57
|
+
// markInProgress: "jira issue move ${id} 'In Progress'",
|
|
58
|
+
// },
|
|
59
|
+
// timeouts: { fetch: 60_000 },
|
|
60
|
+
// },
|
|
61
|
+
// ],
|
|
62
|
+
//
|
|
43
63
|
// git: { remote: "origin", defaultBranch: "main" },
|
|
44
64
|
//
|
|
45
65
|
// orchestrator: {
|
|
@@ -23,6 +23,12 @@ export interface Dispatcher {
|
|
|
23
23
|
usage: (signal?: AbortSignal) => Promise<UsageByModel>;
|
|
24
24
|
dryRun: boolean;
|
|
25
25
|
signal?: AbortSignal;
|
|
26
|
+
/**
|
|
27
|
+
* Appended to the dispatcher's idle-branch log lines so the watch loop
|
|
28
|
+
* can fold its `next poll in Xs` heartbeat into the same line instead of
|
|
29
|
+
* printing a second line per tick.
|
|
30
|
+
*/
|
|
31
|
+
idleSuffix?: string;
|
|
26
32
|
}): Promise<void>;
|
|
27
33
|
}
|
|
28
34
|
export declare function createDispatcher(deps: DispatcherDeps): Dispatcher;
|
|
@@ -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,EACL,KAAK,UAAU,EAIhB,MAAM,uBAAuB,CAAC;AAC/B,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;AAWzD,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;
|
|
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,EACL,KAAK,UAAU,EAIhB,MAAM,uBAAuB,CAAC;AAC/B,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;AAWzD,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;QACrB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAuMjE"}
|
|
@@ -85,19 +85,31 @@ export function createDispatcher(deps) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
async function runOnce(arguments_) {
|
|
88
|
-
const { state, worktreeEntries, usage, dryRun, signal } = arguments_;
|
|
88
|
+
const { state, worktreeEntries, usage, dryRun, signal, idleSuffix = "" } = arguments_;
|
|
89
89
|
issueStatusUpdater.resetMissingInProgressCache();
|
|
90
|
+
// Surface parent tickets that fetchBoard silently dropped. Without this
|
|
91
|
+
// an operator sees "No Todo tickets to pick up" with no signal that an
|
|
92
|
+
// expected Todo+labelled ticket was skipped because it has sub-issues.
|
|
93
|
+
for (const skip of state.parentSkips) {
|
|
94
|
+
log(`Skipping ${skip.id}: parent ticket with ${skip.childCount} sub-issue(s) — groundcrew works sub-issues, not parents`);
|
|
95
|
+
logEvent("dispatch", {
|
|
96
|
+
outcome: "skipped",
|
|
97
|
+
reason: "parent_with_children",
|
|
98
|
+
ticket: skip.id,
|
|
99
|
+
children: skip.childCount,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
90
102
|
const activeCount = state.issues.filter((issue) => issue.status === projectFor(issue, config).statuses.inProgress).length;
|
|
91
103
|
const slots = config.orchestrator.maximumInProgress - activeCount;
|
|
92
104
|
// Narrow Todo to tickets that opted in via an `agent-*` label.
|
|
93
105
|
// Unlabeled tickets are not groundcrew's concern even when in Todo.
|
|
94
106
|
const todo = state.issues.filter((issue) => issue.status === projectFor(issue, config).statuses.todo && isGroundcrewIssue(issue));
|
|
95
107
|
if (slots <= 0) {
|
|
96
|
-
log(`At capacity (${activeCount}/${config.orchestrator.maximumInProgress}), no new work to start`);
|
|
108
|
+
log(`At capacity (${activeCount}/${config.orchestrator.maximumInProgress}), no new work to start${idleSuffix}`);
|
|
97
109
|
return;
|
|
98
110
|
}
|
|
99
111
|
if (todo.length === 0) {
|
|
100
|
-
log(`No Todo tickets to pick up`);
|
|
112
|
+
log(`No Todo tickets to pick up${idleSuffix}`);
|
|
101
113
|
return;
|
|
102
114
|
}
|
|
103
115
|
// Run the blocker pre-pass first so an all-blocked board short-circuits
|
|
@@ -107,7 +119,7 @@ export function createDispatcher(deps) {
|
|
|
107
119
|
logSkip(skip);
|
|
108
120
|
}
|
|
109
121
|
if (unblocked.length === 0) {
|
|
110
|
-
log(`No eligible Todo tickets after blocker filtering`);
|
|
122
|
+
log(`No eligible Todo tickets after blocker filtering${idleSuffix}`);
|
|
111
123
|
return;
|
|
112
124
|
}
|
|
113
125
|
// usage() is an HTTP call; workspaces.probe shells tmux/cmux. Kick off
|
|
@@ -146,7 +158,7 @@ export function createDispatcher(deps) {
|
|
|
146
158
|
logSkip(skip);
|
|
147
159
|
}
|
|
148
160
|
if (starts.length === 0) {
|
|
149
|
-
log(`No eligible Todo tickets after eligibility filtering`);
|
|
161
|
+
log(`No eligible Todo tickets after eligibility filtering${idleSuffix}`);
|
|
150
162
|
return;
|
|
151
163
|
}
|
|
152
164
|
log(`${slots} slot(s) available, starting ${starts.length} ticket(s): ${starts.map(({ issue }) => `${issue.id}(${issue.model})`).join(", ")}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6DH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAiBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4C7E"}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* orchestrator's user-facing output.
|
|
6
6
|
*/
|
|
7
7
|
import { createBoardSource, RepositoryResolutionError, } from "../lib/boardSource.js";
|
|
8
|
+
import { createBoard } from "../lib/board.js";
|
|
9
|
+
import { buildSources } from "../lib/buildSources.js";
|
|
8
10
|
import { loadConfig } from "../lib/config.js";
|
|
9
11
|
import { getUsageByModel } from "../lib/usage.js";
|
|
10
12
|
import { errorMessage, getLinearClient, log, sleep } from "../lib/util.js";
|
|
@@ -65,8 +67,20 @@ export async function orchestrate(options) {
|
|
|
65
67
|
const client = getLinearClient();
|
|
66
68
|
const boardSource = createBoardSource({ config, client });
|
|
67
69
|
await boardSource.verify();
|
|
70
|
+
// Verify any pluggable sources declared in config.sources (shell adapters,
|
|
71
|
+
// future built-in adapters) at startup. The Linear path still runs through
|
|
72
|
+
// boardSource.fetch in the main loop; shell-source dispatch is a follow-up.
|
|
73
|
+
// An empty config.sources resolves to an empty Board and verify() is a no-op.
|
|
74
|
+
const extraSources = await buildSources(config.sources, { globalConfig: config });
|
|
75
|
+
const board = createBoard(extraSources);
|
|
76
|
+
await board.verify();
|
|
68
77
|
const cleaner = createCleaner({ config });
|
|
69
78
|
const dispatcher = createDispatcher({ config, client });
|
|
79
|
+
// Folded into the dispatcher's idle log lines in watch mode so each idle
|
|
80
|
+
// tick prints one combined line instead of "<reason>" + "Next poll in Xs".
|
|
81
|
+
const idleSuffix = options.watch
|
|
82
|
+
? `; next poll in ${config.orchestrator.pollIntervalMilliseconds / MS_PER_SECOND}s`
|
|
83
|
+
: undefined;
|
|
70
84
|
const tick = async (signal) => {
|
|
71
85
|
const state = await withRetry(async () => await boardSource.fetch(), signal);
|
|
72
86
|
const worktreeEntries = worktrees.list(config);
|
|
@@ -82,6 +96,7 @@ export async function orchestrate(options) {
|
|
|
82
96
|
// Lazy: dispatcher only invokes this after its own early-returns, so
|
|
83
97
|
// an idle board doesn't burn a codexbar shell-out per tick.
|
|
84
98
|
usage: async (usageSignal) => await fetchUsageOrEmpty(config, usageSignal),
|
|
99
|
+
...(idleSuffix === undefined ? {} : { idleSuffix }),
|
|
85
100
|
});
|
|
86
101
|
};
|
|
87
102
|
await (options.watch ? runWatchLoop(tick, config) : tick());
|
|
@@ -150,7 +165,6 @@ async function runWatchLoop(tick, config) {
|
|
|
150
165
|
if (shutdown.signal.aborted) {
|
|
151
166
|
break;
|
|
152
167
|
}
|
|
153
|
-
log(`Next poll in ${config.orchestrator.pollIntervalMilliseconds / MS_PER_SECOND}s...`);
|
|
154
168
|
// oxlint-disable-next-line no-await-in-loop -- watch loop is intentionally serial
|
|
155
169
|
await sleep(config.orchestrator.pollIntervalMilliseconds, shutdown.signal);
|
|
156
170
|
}
|
|
@@ -145,14 +145,26 @@ export interface TicketDoctorDependencies {
|
|
|
145
145
|
probeWorkingTree: (input: {
|
|
146
146
|
worktreeDir: string;
|
|
147
147
|
}) => Promise<WorktreeDirtiness>;
|
|
148
|
+
/**
|
|
149
|
+
* Resolves the default branch for `repoDir` (e.g. "master" vs "main") from
|
|
150
|
+
* the local clone's `refs/remotes/<remote>/HEAD`, falling back to
|
|
151
|
+
* `config.git.defaultBranch`. Injected so probeLocalBranchSection can pass a
|
|
152
|
+
* per-repo branch into `probeLocalBranch` without each probe needing to
|
|
153
|
+
* shell out to git itself.
|
|
154
|
+
*/
|
|
155
|
+
resolveDefaultBranch: (input: {
|
|
156
|
+
repoDir: string;
|
|
157
|
+
}) => Promise<string>;
|
|
148
158
|
probeLocalBranch: (input: {
|
|
149
159
|
repoDir: string;
|
|
150
160
|
branch: string;
|
|
161
|
+
remote: string;
|
|
151
162
|
defaultBranch: string;
|
|
152
163
|
}) => Promise<LocalBranchProbe>;
|
|
153
164
|
probeRemoteBranch: (input: {
|
|
154
165
|
repoDir: string;
|
|
155
166
|
branch: string;
|
|
167
|
+
remote: string;
|
|
156
168
|
doFetch: boolean;
|
|
157
169
|
}) => Promise<RemoteBranchProbe>;
|
|
158
170
|
probePullRequest: (input: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticketDoctor.d.ts","sourceRoot":"","sources":["../../src/commands/ticketDoctor.ts"],"names":[],"mappings":"AAoBA,OAAO,EAML,KAAK,OAAO,EAEZ,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"ticketDoctor.d.ts","sourceRoot":"","sources":["../../src/commands/ticketDoctor.ts"],"names":[],"mappings":"AAoBA,OAAO,EAML,KAAK,OAAO,EAEZ,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAc,KAAK,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAa,KAAK,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAO5F,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAW3F,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,2BAA2B,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,YAAY,EAAE,iBAAiB,CAAC;IAChC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;CAChC;AAqED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,kBAAkB,GACxB,mBAAmB,GAAG,SAAS,CA4BjC;AAID,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,aAAa,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,SAAS,CAAC;IACpF,gBAAgB,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;IAC3F,UAAU,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACxC,eAAe,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;IAC5D,eAAe,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/C,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAAC;IAChF,gBAAgB,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACjF;;;;;;OAMG;IACH,oBAAoB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,KAAK,EAAE;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;KACvB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChC,iBAAiB,EAAE,CAAC,KAAK,EAAE;QACzB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACjC,gBAAgB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC5F,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAC;IACvD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAmsBD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,wBAAwB,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAmJ7B;AAoCD,UAAU,qBAAqB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAe9F;AAyCD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CA4D7E;AAGD,wBAAsB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA0CrF"}
|
|
@@ -18,6 +18,7 @@ import { existsSync } from "node:fs";
|
|
|
18
18
|
import { join } from "node:path";
|
|
19
19
|
import { fetchBlockersForTicket, fetchInProgressIssueCount, fetchRawLinearIssue, resolveModelFor, resolveRepositoryFor, } from "../lib/boardSource.js";
|
|
20
20
|
import { runCommandAsync } from "../lib/commandRunner.js";
|
|
21
|
+
import { resolveDefaultBranch } from "../lib/defaultBranch.js";
|
|
21
22
|
import { AGENT_ANY_MODEL, findProjectBySlugId, loadConfig, } from "../lib/config.js";
|
|
22
23
|
import { which } from "../lib/host.js";
|
|
23
24
|
import { readRunState } from "../lib/runState.js";
|
|
@@ -258,6 +259,17 @@ function buildRepoChecks(raw, config, ticket) {
|
|
|
258
259
|
const resolvedRepository = repositoryResolution.kind === "ok" ? repositoryResolution.repository : "";
|
|
259
260
|
return { resolvedRepository, checks };
|
|
260
261
|
}
|
|
262
|
+
function buildChildrenCheck(raw) {
|
|
263
|
+
if (raw.hasChildren) {
|
|
264
|
+
return {
|
|
265
|
+
name: "Has no sub-issues",
|
|
266
|
+
status: "fail",
|
|
267
|
+
detail: "parent ticket with sub-issues — groundcrew works sub-issues, not parents; label a sub-issue or detach the children",
|
|
268
|
+
failureSummary: "parent ticket with sub-issues — groundcrew works sub-issues, not parents",
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return { name: "Has no sub-issues", status: "ok" };
|
|
272
|
+
}
|
|
261
273
|
async function runEligibilityChecks(arguments_) {
|
|
262
274
|
const { ticket, raw, config, resolvedRepository, resolvedModel, dependencies, eligibility } = arguments_;
|
|
263
275
|
const blockers = await dependencies.fetchBlockersFor({ ticket, uuid: raw.uuid });
|
|
@@ -531,10 +543,12 @@ async function probeLocalBranchSection(deps, entry) {
|
|
|
531
543
|
};
|
|
532
544
|
}
|
|
533
545
|
const repoDir = repoDirFromEntry(entry, deps);
|
|
546
|
+
const defaultBranch = await deps.resolveDefaultBranch({ repoDir });
|
|
534
547
|
const probe = await deps.probeLocalBranch({
|
|
535
548
|
repoDir,
|
|
536
549
|
branch: entry.branchName,
|
|
537
|
-
|
|
550
|
+
remote: deps.config.git.remote,
|
|
551
|
+
defaultBranch,
|
|
538
552
|
});
|
|
539
553
|
if (probe.kind === "present") {
|
|
540
554
|
const defaultBranchName = probe.defaultBranch ?? deps.config.git.defaultBranch;
|
|
@@ -543,7 +557,7 @@ async function probeLocalBranchSection(deps, entry) {
|
|
|
543
557
|
{
|
|
544
558
|
name: "Local branch exists",
|
|
545
559
|
status: "ok",
|
|
546
|
-
detail: `${entry.branchName}, ${probe.ahead} ahead / ${probe.behind} behind
|
|
560
|
+
detail: `${entry.branchName}, ${probe.ahead} ahead / ${probe.behind} behind ${deps.config.git.remote}/${defaultBranchName}`,
|
|
547
561
|
},
|
|
548
562
|
],
|
|
549
563
|
skipReason: "",
|
|
@@ -572,24 +586,26 @@ async function probeRemoteBranchSection(deps, entry) {
|
|
|
572
586
|
return { checks: [], skipReason: "repo dir unresolved", probe: { kind: "absent" } };
|
|
573
587
|
}
|
|
574
588
|
const repoDir = repoDirFromEntry(entry, deps);
|
|
589
|
+
const checkName = `Branch present on ${deps.config.git.remote}`;
|
|
575
590
|
const probe = await deps.probeRemoteBranch({
|
|
576
591
|
repoDir,
|
|
577
592
|
branch: entry.branchName,
|
|
593
|
+
remote: deps.config.git.remote,
|
|
578
594
|
doFetch: deps.doFetch,
|
|
579
595
|
});
|
|
580
596
|
if (probe.kind === "present") {
|
|
581
|
-
return { checks: [{ name:
|
|
597
|
+
return { checks: [{ name: checkName, status: "ok" }], skipReason: "", probe };
|
|
582
598
|
}
|
|
583
599
|
if (probe.kind === "absent") {
|
|
584
600
|
return {
|
|
585
|
-
checks: [{ name:
|
|
601
|
+
checks: [{ name: checkName, status: "fail", detail: "not pushed" }],
|
|
586
602
|
skipReason: "",
|
|
587
603
|
probe,
|
|
588
604
|
};
|
|
589
605
|
}
|
|
590
606
|
// probe.kind === "unknown"
|
|
591
607
|
return {
|
|
592
|
-
checks: [{ name:
|
|
608
|
+
checks: [{ name: checkName, status: "skipped", detail: probe.reason }],
|
|
593
609
|
skipReason: "",
|
|
594
610
|
probe,
|
|
595
611
|
};
|
|
@@ -642,6 +658,12 @@ async function runPreDispatch(input) {
|
|
|
642
658
|
const eligibility = [];
|
|
643
659
|
const { resolvedModel, checks: modelChecks } = buildModelChecks(raw, config);
|
|
644
660
|
resolutionExtra.push(...modelChecks);
|
|
661
|
+
// Children check comes before repo checks: a parent ticket is a
|
|
662
|
+
// structural property of the issue itself (filtered out by `fetchBoard`,
|
|
663
|
+
// so the dispatcher will never act on it). Reporting that first beats
|
|
664
|
+
// surfacing "your repo isn't cloned" for a ticket groundcrew won't pick
|
|
665
|
+
// up either way.
|
|
666
|
+
resolutionExtra.push(buildChildrenCheck(raw));
|
|
645
667
|
const { resolvedRepository, checks: repoChecks } = buildRepoChecks(raw, config, ticket);
|
|
646
668
|
resolutionExtra.push(...repoChecks);
|
|
647
669
|
if (input.statusCheckFailed) {
|
|
@@ -969,6 +991,11 @@ export async function runTicketDoctor(parsed) {
|
|
|
969
991
|
probeWorkspaces: async () => await workspaces.probe(config),
|
|
970
992
|
workspaceAccessHint: async (name) => await workspaces.accessHint(config, name),
|
|
971
993
|
probeWorkingTree: async ({ worktreeDir }) => await worktrees.probeWorkingTree({ worktreeDir }),
|
|
994
|
+
resolveDefaultBranch: async ({ repoDir }) => await resolveDefaultBranch({
|
|
995
|
+
repoDir,
|
|
996
|
+
remote: config.git.remote,
|
|
997
|
+
fallback: config.git.defaultBranch,
|
|
998
|
+
}),
|
|
972
999
|
probeLocalBranch: probeLocalBranchImpl,
|
|
973
1000
|
probeRemoteBranch: probeRemoteBranchImpl,
|
|
974
1001
|
probePullRequest: probePullRequestImpl,
|
|
@@ -1028,7 +1055,7 @@ async function probeLocalBranchImpl(input) {
|
|
|
1028
1055
|
"rev-list",
|
|
1029
1056
|
"--left-right",
|
|
1030
1057
|
"--count",
|
|
1031
|
-
`${input.branch}
|
|
1058
|
+
`${input.branch}...${input.remote}/${input.defaultBranch}`,
|
|
1032
1059
|
]);
|
|
1033
1060
|
const [aheadString, behindString] = output.trim().split(/\s+/);
|
|
1034
1061
|
const ahead = Number.parseInt(aheadString ?? "0", 10);
|
|
@@ -1050,7 +1077,7 @@ async function probeRemoteBranchImpl(input) {
|
|
|
1050
1077
|
input.repoDir,
|
|
1051
1078
|
"fetch",
|
|
1052
1079
|
"--quiet",
|
|
1053
|
-
|
|
1080
|
+
input.remote,
|
|
1054
1081
|
input.branch,
|
|
1055
1082
|
]);
|
|
1056
1083
|
}
|
|
@@ -1064,7 +1091,7 @@ async function probeRemoteBranchImpl(input) {
|
|
|
1064
1091
|
input.repoDir,
|
|
1065
1092
|
"ls-remote",
|
|
1066
1093
|
"--exit-code",
|
|
1067
|
-
|
|
1094
|
+
input.remote,
|
|
1068
1095
|
`refs/heads/${input.branch}`,
|
|
1069
1096
|
]);
|
|
1070
1097
|
return { kind: "present" };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,11 +5,16 @@ export { interruptWorkspace, type InterruptWorkspaceOptions, } from "./commands/
|
|
|
5
5
|
export { orchestrate, type OrchestratorOptions } from "./commands/orchestrator.ts";
|
|
6
6
|
export { resumeWorkspace, type ResumeWorkspaceOptions } from "./commands/resumeWorkspace.ts";
|
|
7
7
|
export { setupWorkspace, type SetupWorkspaceOptions } from "./commands/setupWorkspace.ts";
|
|
8
|
-
export type { Config, ModelDefinition, ProjectConfig, ResolvedConfig, ResolvedProjectConfig, } from "./lib/config.ts";
|
|
8
|
+
export type { Config, ModelDefinition, ProjectConfig, ResolvedConfig, ResolvedProjectConfig, SourceConfig, } from "./lib/config.ts";
|
|
9
9
|
export { findProjectBySlugId, loadConfig, unionTerminalStatuses } from "./lib/config.ts";
|
|
10
10
|
export { readRunState, recordRunState, removeRunState, runStateDirectory, runStatePath, updateRunState, type RunLifecycleState, type RunState, } from "./lib/runState.ts";
|
|
11
11
|
export { fetchBlockersForTicket, fetchInProgressIssueCount, fetchRawLinearIssue, fetchResolvedIssue, isTerminalStatusForBlocker, isTerminalStatusForIssue, projectFor, resolveModelFor, resolveRepositoryFor, UnknownProjectError, type ModelResolution, type RawLinearIssue, type RepositoryResolution, } from "./lib/boardSource.ts";
|
|
12
12
|
export { getUsageByModel, type UsageByModel } from "./lib/usage.ts";
|
|
13
|
+
export { type Board, createBoard } from "./lib/board.ts";
|
|
14
|
+
export { buildSources, buildSourcesWith } from "./lib/buildSources.ts";
|
|
15
|
+
export type { AdapterContext, AdapterDefinition } from "./lib/adapterDefinition.ts";
|
|
16
|
+
export { adapterRegistry, type AdapterLoader, buildRegistry, buildSourceConfigSchema, listAdapterDirectories, } from "./lib/adapters/registry.ts";
|
|
17
|
+
export { AmbiguousTicketError, type Blocker as CanonicalBlocker, type BoardState as CanonicalBoardState, type CanonicalStatus, type GroundcrewIssue as CanonicalGroundcrewIssue, type Issue as CanonicalIssue, isGroundcrewIssue as isCanonicalGroundcrewIssue, type TicketSource, } from "./lib/ticketSource.ts";
|
|
13
18
|
export type { TicketCheck } from "./commands/ticketCheck.ts";
|
|
14
19
|
export { ticketDoctor, type TicketDoctorDependencies, type TicketDoctorResult, type TicketDoctorVerdict, } from "./commands/ticketDoctor.ts";
|
|
15
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,YAAY,EACV,MAAM,EACN,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,YAAY,EACV,MAAM,EACN,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACzF,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,KAAK,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,oBAAoB,EACpB,KAAK,OAAO,IAAI,gBAAgB,EAChC,KAAK,UAAU,IAAI,mBAAmB,EACtC,KAAK,eAAe,EACpB,KAAK,eAAe,IAAI,wBAAwB,EAChD,KAAK,KAAK,IAAI,cAAc,EAC5B,iBAAiB,IAAI,0BAA0B,EAC/C,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,YAAY,EACZ,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,4 +9,8 @@ export { findProjectBySlugId, loadConfig, unionTerminalStatuses } from "./lib/co
|
|
|
9
9
|
export { readRunState, recordRunState, removeRunState, runStateDirectory, runStatePath, updateRunState, } from "./lib/runState.js";
|
|
10
10
|
export { fetchBlockersForTicket, fetchInProgressIssueCount, fetchRawLinearIssue, fetchResolvedIssue, isTerminalStatusForBlocker, isTerminalStatusForIssue, projectFor, resolveModelFor, resolveRepositoryFor, UnknownProjectError, } from "./lib/boardSource.js";
|
|
11
11
|
export { getUsageByModel } from "./lib/usage.js";
|
|
12
|
+
export { createBoard } from "./lib/board.js";
|
|
13
|
+
export { buildSources, buildSourcesWith } from "./lib/buildSources.js";
|
|
14
|
+
export { adapterRegistry, buildRegistry, buildSourceConfigSchema, listAdapterDirectories, } from "./lib/adapters/registry.js";
|
|
15
|
+
export { AmbiguousTicketError, isGroundcrewIssue as isCanonicalGroundcrewIssue, } from "./lib/ticketSource.js";
|
|
12
16
|
export { ticketDoctor, } from "./commands/ticketDoctor.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `AdapterDefinition` shape that every built-in adapter
|
|
3
|
+
* (`src/lib/adapters/<kind>/index.ts`) default-exports. The runtime registry
|
|
4
|
+
* (`./adapters/registry.ts`) discovers adapters by enumerating that
|
|
5
|
+
* directory and reading each module's default export.
|
|
6
|
+
*/
|
|
7
|
+
import type { z } from "zod";
|
|
8
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
9
|
+
import type { TicketSource } from "./ticketSource.ts";
|
|
10
|
+
/**
|
|
11
|
+
* Cross-cutting context every adapter receives at construction time. Holds
|
|
12
|
+
* the global resolved config so adapters can read shared concerns (the
|
|
13
|
+
* `workspace.knownRepositories` list, `models.*` definitions, etc.) without
|
|
14
|
+
* each one duplicating them in its per-source config block.
|
|
15
|
+
*/
|
|
16
|
+
export interface AdapterContext {
|
|
17
|
+
readonly globalConfig: ResolvedConfig;
|
|
18
|
+
}
|
|
19
|
+
export interface AdapterDefinition<TSchema extends z.ZodType = z.ZodType> {
|
|
20
|
+
/** Discriminator value used in `SourceConfig.kind`. Must equal the directory name. */
|
|
21
|
+
readonly kind: string;
|
|
22
|
+
/** Zod schema for this adapter's config block. The `kind` field must be `z.literal(kind)`. */
|
|
23
|
+
readonly configSchema: TSchema;
|
|
24
|
+
/** Builds a TicketSource from a validated config and the shared adapter context. */
|
|
25
|
+
readonly create: (config: z.infer<TSchema>, context: AdapterContext) => TicketSource;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=adapterDefinition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapterDefinition.d.ts","sourceRoot":"","sources":["../../src/lib/adapterDefinition.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IACtE,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,cAAc,KAAK,YAAY,CAAC;CACtF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `AdapterDefinition` shape that every built-in adapter
|
|
3
|
+
* (`src/lib/adapters/<kind>/index.ts`) default-exports. The runtime registry
|
|
4
|
+
* (`./adapters/registry.ts`) discovers adapters by enumerating that
|
|
5
|
+
* directory and reading each module's default export.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear `TicketSource` factory. Wraps the existing boardSource.ts machinery
|
|
3
|
+
* (createBoardSource, fetchResolvedIssue, createLinearIssueStatusUpdater) and
|
|
4
|
+
* converts the legacy Linear-specific `Issue`/`Blocker` shapes into the
|
|
5
|
+
* canonical `Issue`/`Blocker` shapes consumers (via `Board`) speak.
|
|
6
|
+
*
|
|
7
|
+
* Per-project canonical-status mapping lives here: each `Issue` is mapped
|
|
8
|
+
* against its own project's `statuses` block (the multi-project semantics
|
|
9
|
+
* shipped in PR #75). Off-config blockers fall back to the union of all
|
|
10
|
+
* configured projects' status sets — preserving today's
|
|
11
|
+
* `isTerminalStatusForBlocker` behavior.
|
|
12
|
+
*
|
|
13
|
+
* Description is not populated on `fetch()` Issues (boardSource's snapshot
|
|
14
|
+
* doesn't include it); `resolveOne()` Issues carry the full description
|
|
15
|
+
* because `fetchResolvedIssue` fetches it explicitly. Phase 6 can lift
|
|
16
|
+
* description onto the board snapshot when it refactors setupWorkspace.
|
|
17
|
+
*/
|
|
18
|
+
import type { AdapterContext } from "../../adapterDefinition.ts";
|
|
19
|
+
import { type Blocker as LinearBlocker, type Issue as LinearIssue } from "../../boardSource.ts";
|
|
20
|
+
import { type ResolvedProjectConfig } from "../../config.ts";
|
|
21
|
+
import type { CanonicalStatus, Issue as CanonicalIssue, TicketSource } from "../../ticketSource.ts";
|
|
22
|
+
import type { LinearAdapterConfig } from "./schema.ts";
|
|
23
|
+
export declare function canonicalStatusForProject(nativeStatus: string, project: ResolvedProjectConfig): CanonicalStatus;
|
|
24
|
+
export declare function canonicalBlockerStatus(blocker: LinearBlocker, globalConfig: AdapterContext["globalConfig"]): CanonicalStatus;
|
|
25
|
+
export declare function toCanonicalIssue(linearIssue: LinearIssue, globalConfig: AdapterContext["globalConfig"], sourceName: string): CanonicalIssue;
|
|
26
|
+
export declare function createLinearTicketSource(config: LinearAdapterConfig, context: AdapterContext): TicketSource;
|
|
27
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EACL,KAAK,OAAO,IAAI,aAAa,EAG7B,KAAK,KAAK,IAAI,WAAW,EAE1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAElF,OAAO,KAAK,EAEV,eAAe,EACf,KAAK,IAAI,cAAc,EACvB,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAUvD,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,qBAAqB,GAC7B,eAAe,CAcjB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,EACtB,YAAY,EAAE,cAAc,CAAC,cAAc,CAAC,GAC3C,eAAe,CAoBjB;AAcD,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,cAAc,CAAC,cAAc,CAAC,EAC5C,UAAU,EAAE,MAAM,GACjB,cAAc,CA8BhB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,cAAc,GACtB,YAAY,CAwEd"}
|