@clipboard-health/groundcrew 3.1.5 → 3.1.7

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.
Files changed (63) hide show
  1. package/README.md +48 -0
  2. package/crew.config.example.ts +20 -0
  3. package/dist/commands/dispatcher.d.ts.map +1 -1
  4. package/dist/commands/dispatcher.js +12 -0
  5. package/dist/commands/orchestrator.d.ts.map +1 -1
  6. package/dist/commands/orchestrator.js +9 -0
  7. package/dist/commands/ticketDoctor.d.ts +12 -0
  8. package/dist/commands/ticketDoctor.d.ts.map +1 -1
  9. package/dist/commands/ticketDoctor.js +35 -8
  10. package/dist/index.d.ts +6 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/lib/adapterDefinition.d.ts +27 -0
  14. package/dist/lib/adapterDefinition.d.ts.map +1 -0
  15. package/dist/lib/adapterDefinition.js +7 -0
  16. package/dist/lib/adapters/linear/factory.d.ts +27 -0
  17. package/dist/lib/adapters/linear/factory.d.ts.map +1 -0
  18. package/dist/lib/adapters/linear/factory.js +161 -0
  19. package/dist/lib/adapters/linear/index.d.ts +5 -0
  20. package/dist/lib/adapters/linear/index.d.ts.map +1 -0
  21. package/dist/lib/adapters/linear/index.js +8 -0
  22. package/dist/lib/adapters/linear/schema.d.ts +14 -0
  23. package/dist/lib/adapters/linear/schema.d.ts.map +1 -0
  24. package/dist/lib/adapters/linear/schema.js +15 -0
  25. package/dist/lib/adapters/registry.d.ts +38 -0
  26. package/dist/lib/adapters/registry.d.ts.map +1 -0
  27. package/dist/lib/adapters/registry.js +82 -0
  28. package/dist/lib/adapters/shell/factory.d.ts +21 -0
  29. package/dist/lib/adapters/shell/factory.d.ts.map +1 -0
  30. package/dist/lib/adapters/shell/factory.js +130 -0
  31. package/dist/lib/adapters/shell/index.d.ts +5 -0
  32. package/dist/lib/adapters/shell/index.d.ts.map +1 -0
  33. package/dist/lib/adapters/shell/index.js +8 -0
  34. package/dist/lib/adapters/shell/invoke.d.ts +45 -0
  35. package/dist/lib/adapters/shell/invoke.d.ts.map +1 -0
  36. package/dist/lib/adapters/shell/invoke.js +153 -0
  37. package/dist/lib/adapters/shell/schema.d.ts +90 -0
  38. package/dist/lib/adapters/shell/schema.d.ts.map +1 -0
  39. package/dist/lib/adapters/shell/schema.js +54 -0
  40. package/dist/lib/board.d.ts +20 -0
  41. package/dist/lib/board.d.ts.map +1 -0
  42. package/dist/lib/board.js +77 -0
  43. package/dist/lib/boardSource.d.ts +23 -7
  44. package/dist/lib/boardSource.d.ts.map +1 -1
  45. package/dist/lib/boardSource.js +35 -8
  46. package/dist/lib/buildSources.d.ts +19 -0
  47. package/dist/lib/buildSources.d.ts.map +1 -0
  48. package/dist/lib/buildSources.js +34 -0
  49. package/dist/lib/config.d.ts +27 -0
  50. package/dist/lib/config.d.ts.map +1 -1
  51. package/dist/lib/config.js +36 -0
  52. package/dist/lib/defaultBranch.d.ts +24 -0
  53. package/dist/lib/defaultBranch.d.ts.map +1 -0
  54. package/dist/lib/defaultBranch.js +39 -0
  55. package/dist/lib/launchCommand.d.ts +1 -1
  56. package/dist/lib/launchCommand.d.ts.map +1 -1
  57. package/dist/lib/launchCommand.js +1 -1
  58. package/dist/lib/ticketSource.d.ts +85 -0
  59. package/dist/lib/ticketSource.d.ts.map +1 -0
  60. package/dist/lib/ticketSource.js +26 -0
  61. package/dist/lib/worktrees.d.ts.map +1 -1
  62. package/dist/lib/worktrees.js +9 -2
  63. 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. |
@@ -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: {
@@ -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;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,CAwLjE"}
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"}
@@ -87,6 +87,18 @@ export function createDispatcher(deps) {
87
87
  async function runOnce(arguments_) {
88
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.
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2DH,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,CAoC7E"}
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,6 +67,13 @@ 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 });
70
79
  // Folded into the dispatcher's idle log lines in watch mode so each idle
@@ -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":"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"}
1
+ {"version":3,"file":"ticketDoctor.d.ts","sourceRoot":"","sources":["../../src/commands/ticketDoctor.ts"],"names":[],"mappings":"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"}
@@ -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
- defaultBranch: deps.config.git.defaultBranch,
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 origin/${defaultBranchName}`,
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: "Branch present on origin", status: "ok" }], skipReason: "", probe };
597
+ return { checks: [{ name: checkName, status: "ok" }], skipReason: "", probe };
582
598
  }
583
599
  if (probe.kind === "absent") {
584
600
  return {
585
- checks: [{ name: "Branch present on origin", status: "fail", detail: "not pushed" }],
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: "Branch present on origin", status: "skipped", detail: probe.reason }],
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}...origin/${input.defaultBranch}`,
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
- "origin",
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
- "origin",
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
@@ -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,GACtB,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,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,YAAY,EACZ,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC"}
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"}
@@ -0,0 +1,161 @@
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 { createBoardSource, fetchResolvedIssue, isTerminalStatusForBlocker, } from "../../boardSource.js";
19
+ import { findProjectBySlugId } from "../../config.js";
20
+ import { createLinearIssueStatusUpdater } from "../../linearIssueStatus.js";
21
+ import { getLinearClient } from "../../util.js";
22
+ export function canonicalStatusForProject(nativeStatus, project) {
23
+ if (project.statuses.todo === nativeStatus) {
24
+ return "todo";
25
+ }
26
+ if (project.statuses.inProgress === nativeStatus) {
27
+ return "in-progress";
28
+ }
29
+ if (project.statuses.done === nativeStatus) {
30
+ return "done";
31
+ }
32
+ if (project.statuses.terminal.includes(nativeStatus)) {
33
+ return "done";
34
+ }
35
+ return "other";
36
+ }
37
+ export function canonicalBlockerStatus(blocker, globalConfig) {
38
+ if (blocker.status === undefined) {
39
+ return "other";
40
+ }
41
+ // Terminal first — handles off-config blockers via the union fallback that
42
+ // isTerminalStatusForBlocker already implements.
43
+ if (isTerminalStatusForBlocker(blocker, globalConfig)) {
44
+ return "done";
45
+ }
46
+ // Non-terminal: if the blocker's project is configured, use its statuses to
47
+ // distinguish todo vs in-progress. For off-config blockers we collapse to
48
+ // "other" — eligibility only cares whether the blocker is terminal, so the
49
+ // distinction is informational at most.
50
+ if (blocker.projectSlugId !== undefined) {
51
+ const project = findProjectBySlugId(globalConfig, blocker.projectSlugId);
52
+ if (project !== undefined) {
53
+ return canonicalStatusForProject(blocker.status, project);
54
+ }
55
+ }
56
+ return "other";
57
+ }
58
+ function toCanonicalBlocker(blocker, globalConfig, sourceName) {
59
+ return {
60
+ id: `${sourceName}:${blocker.id}`,
61
+ title: blocker.title,
62
+ status: canonicalBlockerStatus(blocker, globalConfig),
63
+ };
64
+ }
65
+ export function toCanonicalIssue(linearIssue, globalConfig, sourceName) {
66
+ const project = findProjectBySlugId(globalConfig, linearIssue.projectSlugId);
67
+ /* v8 ignore next 5 @preserve -- fetchBoard's slugId filter and issueStatusBelongsToOwnProject guarantee project is configured by the time we get here */
68
+ if (project === undefined) {
69
+ throw new Error(`Linear adapter: issue ${linearIssue.id} carries unknown projectSlugId "${linearIssue.projectSlugId}"`);
70
+ }
71
+ const sourceRef = {
72
+ uuid: linearIssue.uuid,
73
+ statusId: linearIssue.statusId,
74
+ teamId: linearIssue.teamId,
75
+ projectSlugId: linearIssue.projectSlugId,
76
+ nativeStatus: linearIssue.status,
77
+ };
78
+ return {
79
+ id: `${sourceName}:${linearIssue.id}`,
80
+ source: sourceName,
81
+ title: linearIssue.title,
82
+ // Board snapshot doesn't carry description; resolveOne() populates it.
83
+ description: "",
84
+ status: canonicalStatusForProject(linearIssue.status, project),
85
+ repository: linearIssue.repository,
86
+ model: linearIssue.model,
87
+ assignee: linearIssue.assignee,
88
+ updatedAt: linearIssue.updatedAt,
89
+ blockers: linearIssue.blockers.map((b) => toCanonicalBlocker(b, globalConfig, sourceName)),
90
+ hasMoreBlockers: linearIssue.hasMoreBlockers,
91
+ sourceRef,
92
+ };
93
+ }
94
+ export function createLinearTicketSource(config, context) {
95
+ const sourceName = config.name ?? "linear";
96
+ const { globalConfig } = context;
97
+ const client = getLinearClient();
98
+ const boardSource = createBoardSource({ config: globalConfig, client });
99
+ const issueStatusUpdater = createLinearIssueStatusUpdater({ config: globalConfig, client });
100
+ return {
101
+ name: sourceName,
102
+ async verify() {
103
+ await boardSource.verify();
104
+ },
105
+ async fetch() {
106
+ const state = await boardSource.fetch();
107
+ return state.issues.map((linearIssue) => toCanonicalIssue(linearIssue, globalConfig, sourceName));
108
+ },
109
+ async resolveOne(naturalId) {
110
+ // fetchResolvedIssue throws on unknown project / missing repo; we let
111
+ // those propagate. Returning `undefined` is reserved for "ticket genuinely
112
+ // doesn't exist," which fetchResolvedIssue surfaces as an Error too —
113
+ // for now we let any error bubble up rather than swallow.
114
+ const resolved = await fetchResolvedIssue({
115
+ client,
116
+ config: globalConfig,
117
+ ticket: naturalId,
118
+ });
119
+ const project = findProjectBySlugId(globalConfig, resolved.projectSlugId);
120
+ /* v8 ignore next 5 @preserve -- fetchResolvedIssue already throws UnknownProjectError before reaching this guard */
121
+ if (project === undefined) {
122
+ throw new Error(`Linear adapter: resolved issue ${naturalId} carries unknown projectSlugId "${resolved.projectSlugId}"`);
123
+ }
124
+ // fetchResolvedIssue doesn't return the native status name (it's
125
+ // already been resolved through workflow state lookup). We surface
126
+ // "other" until the consumer needs the canonical status, which is fine
127
+ // because `crew setup` doesn't branch on it.
128
+ const sourceRef = {
129
+ uuid: resolved.uuid,
130
+ statusId: "",
131
+ teamId: resolved.teamId,
132
+ projectSlugId: resolved.projectSlugId,
133
+ nativeStatus: "",
134
+ };
135
+ return {
136
+ id: `${sourceName}:${naturalId.toLowerCase()}`,
137
+ source: sourceName,
138
+ title: resolved.title,
139
+ description: resolved.description,
140
+ status: "other",
141
+ repository: resolved.repository,
142
+ model: resolved.model,
143
+ assignee: "Unassigned",
144
+ updatedAt: new Date().toISOString(),
145
+ blockers: [],
146
+ hasMoreBlockers: false,
147
+ sourceRef,
148
+ };
149
+ },
150
+ async markInProgress(issue) {
151
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- by the Linear adapter's contract, every Issue it produces carries a LinearSourceRef in sourceRef
152
+ const ref = issue.sourceRef;
153
+ await issueStatusUpdater.markInProgress({
154
+ id: issue.id,
155
+ uuid: ref.uuid,
156
+ teamId: ref.teamId,
157
+ projectSlugId: ref.projectSlugId,
158
+ });
159
+ },
160
+ };
161
+ }
@@ -0,0 +1,5 @@
1
+ import type { AdapterDefinition } from "../../adapterDefinition.ts";
2
+ import { linearAdapterConfigSchema } from "./schema.ts";
3
+ declare const definition: AdapterDefinition<typeof linearAdapterConfigSchema>;
4
+ export default definition;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/linear/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD,QAAA,MAAM,UAAU,EAAE,iBAAiB,CAAC,OAAO,yBAAyB,CAInE,CAAC;eAEa,UAAU"}
@@ -0,0 +1,8 @@
1
+ import { createLinearTicketSource } from "./factory.js";
2
+ import { linearAdapterConfigSchema } from "./schema.js";
3
+ const definition = {
4
+ kind: "linear",
5
+ configSchema: linearAdapterConfigSchema,
6
+ create: createLinearTicketSource,
7
+ };
8
+ export default definition;