@clipboard-health/groundcrew 3.0.0 → 3.0.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/README.md CHANGED
@@ -105,6 +105,25 @@ Installs the `crew` binary. `@clipboard-health/clearance` is pulled in transitiv
105
105
  crew run --watch # poll forever
106
106
  ```
107
107
 
108
+ ## Secrets
109
+
110
+ Groundcrew forwards a small allowlist of build-time secrets from your shell into the setup phase (so `npm install` can authenticate against private registries) and then strips them before the agent runs. The agent process never inherits these values in its environment.
111
+
112
+ **Recognized names.** Defined in [`BUILD_SECRET_NAMES`](./src/lib/buildSecrets.ts):
113
+
114
+ - `NPM_TOKEN`
115
+ - `BUF_TOKEN`
116
+
117
+ Set them in the shell you run `crew` from. Anything not in this list is ignored by the secret-shuttling path.
118
+
119
+ **Flow.** For each ticket:
120
+
121
+ 1. If any recognized var is set and non-empty, groundcrew writes `secrets.env` (mode `0600`) into the ticket's temp prompt dir as `KEY='value'` lines — see `stageBuildSecrets` in [`src/commands/setupWorkspace.ts`](./src/commands/setupWorkspace.ts).
122
+ 2. The launch script sources `secrets.env` with `set -a` so the values are exported into the setup phase only (and under `sdx`, forwarded into the sandbox via `-e KEY` flags).
123
+ 3. After setup completes, the script `unset`s every name in `BUILD_SECRET_NAMES` and then `rm -rf`s the entire prompt dir (including `secrets.env`) before `exec`'ing the agent. See `sourceSecretsLine` / `unsetSecretsLine` and the `rm -rf` / `exec` lines in [`src/lib/launchCommand.ts`](./src/lib/launchCommand.ts). The rollback path on setup failure ([`src/commands/setupWorkspace.ts`](./src/commands/setupWorkspace.ts)) wipes the prompt dir too.
124
+
125
+ Net effect: by the time the agent process exists, the values are gone from the environment and the file is gone from disk.
126
+
108
127
  ## Runners
109
128
 
110
129
  `local.runner` picks the local isolation backend. `auto` resolves per platform.
@@ -270,39 +289,75 @@ To have a coding agent (Claude Code, Cursor, etc.) scaffold `.groundcrew/setup.s
270
289
  ## Commands
271
290
 
272
291
  ```bash
273
- crew doctor # full setup check
274
- crew doctor --ticket <TICKET> # diagnose one ticket
275
- crew run # one-shot dispatch
276
- crew run --watch # poll forever
277
- crew run --ticket <TICKET> # provision one ticket and exit
292
+ crew doctor # full setup check
293
+ crew doctor --ticket <TICKET> [--no-linear] [--no-fetch] # full ticket lifecycle (dispatch + recovery)
294
+ crew run # one-shot dispatch
295
+ crew run --watch # poll forever
296
+ crew run --ticket <TICKET> # provision one ticket and exit
278
297
  crew setup repos [--dry-run] [<repo>...]
279
- crew cleanup <TICKET> # tear down every worktree carrying this ticket
298
+ crew cleanup <TICKET> # tear down every worktree carrying this ticket
280
299
  ```
281
300
 
282
- `--watch` and `--ticket` are mutually exclusive. To inspect codexbar session windows directly, run `codexbar usage`.
301
+ `crew doctor --ticket <TICKET>` covers the full per-ticket lifecycle: pre-dispatch eligibility (Todo status, `agent-*` label, model resolution, repository mention, local clone, blockers, model session usage, in-progress capacity) **and** post-dispatch local-state recovery (host worktree, workspace pane, local branch, remote branch, open PR). Verdict precedence runs from post-dispatch outcomes down: `pr-open` > `pr-merged` > `in-flight` > `recoverable` > `unresolvable` > `ineligible` > `would-dispatch` > `lost`. Exits 0 on `would-dispatch`, `pr-open`, or `pr-merged`; any other verdict exits 1. `--watch` and `--ticket` are mutually exclusive. To inspect codexbar session windows directly, run `codexbar usage`.
283
302
 
284
303
  ### `crew doctor --ticket <ticket>`
285
304
 
286
- Diagnose why a ticket would or wouldn't be dispatched on the next tick. Runs the same resolution and eligibility chain as the dispatcher. Exits 0 if the ticket would dispatch, 1 otherwise. The hero above shows a passing run; here's a failing one:
305
+ Diagnose where a ticket is in its lifecycle and what to do next. Runs the same resolution and eligibility chain as the dispatcher, plus probes the host worktree, workspace pane, local branch, remote branch, and PR; prints a single verdict with a copy-pasteable recovery step when one applies.
306
+
307
+ Flags:
308
+
309
+ - `--no-linear` — skip the Linear GraphQL call. Resolution and Eligibility sections are skipped; verdicts that need only local state (`in-flight`, `recoverable`, `pr-open`, `pr-merged`, `lost`) still fire.
310
+ - `--no-fetch` — skip the upfront `git fetch origin <branch>` before checking remote presence.
311
+
312
+ The Workspace section appends an attach hint to the pane name when the workspace backend exposes one (e.g. `tmux attach -t <session>:<pane>` or `cmux attach <name>`), so the verdict line is immediately actionable. The hero above shows a passing pre-dispatch run; here's the same command on a ticket that's already past dispatch:
287
313
 
288
314
  ```text
289
- groundcrew doctor --ticket HRD-447 (Refactor auth middleware)
290
- ─────────────────────────────────────────────────────────────
315
+ groundcrew doctor --ticket HRD-442 (Multi-event extractor: year inference can produce date_start > date_end)
316
+ ────────────────────────────────────────────────────────────────────────────────────────────────────────────
291
317
 
292
318
  Resolution
293
- [ok] Ticket exists in Linear ("Refactor auth middleware")
294
- [--] Status is Todo (current: In Progress)
295
- [ok] Has agent-* label (agent-claude)
296
- [ok] Model resolves from agent-* label (model "claude")
297
- [ok] Description mentions known repo (owner/repo)
298
- [ok] Resolved repo is cloned locally (/dev/workspaces/owner/repo)
319
+ [ok] Ticket exists in Linear ("Multi-event extractor: year inference can produce date_start > date_end")
320
+ [ok] Status is Todo
321
+ (skipped post-dispatch pre-dispatch checks are irrelevant)
299
322
 
300
323
  Eligibility
301
- (skipped — resolution checks failed)
324
+ (skipped — post-dispatch — pre-dispatch checks are irrelevant)
325
+
326
+ Worktree
327
+ [ok] Host worktree exists (/Users/paul/dev/groundcrew-workspaces/herds-social/herds-hrd-442)
328
+ [--] Working tree clean (0 modified, 1 untracked)
329
+ [ok] Branch checked out (paul-hrd-442)
330
+
331
+ Workspace
332
+ [ok] Workspace pane open (hrd-442 — attach: `tmux attach -t groundcrew:hrd-442`)
302
333
 
303
- ineligible: status is In Progress (need Todo)
334
+ Local branch
335
+ [ok] Local branch exists (paul-hrd-442, 2 ahead / 0 behind origin/main)
336
+
337
+ Remote branch
338
+ [ok] Branch present on origin
339
+
340
+ Pull request
341
+ [ok] Open PR for this branch (#224 https://github.com/herds-social/herds/pull/224)
342
+
343
+ → pr-open: https://github.com/herds-social/herds/pull/224 (#224)
304
344
  ```
305
345
 
346
+ #### Recovering a stranded ticket
347
+
348
+ The verdict on the last line maps to a recovery action:
349
+
350
+ | Verdict | What to do |
351
+ | ---------------- | --------------------------------------------------------------------------------------------- |
352
+ | `pr-open` | Nothing — the PR is the source of truth. |
353
+ | `pr-merged` | Done. |
354
+ | `in-flight` | The ticket is still being worked on; the verdict line names the workspace pane to attach to. |
355
+ | `recoverable` | Run the printed `nextStep` exactly. |
356
+ | `would-dispatch` | Pre-dispatch checks pass; the orchestrator will pick the ticket up on its next tick. |
357
+ | `ineligible` | A resolution or eligibility check failed; the reason after the colon names the failing check. |
358
+ | `unresolvable` | The Linear ticket couldn't be fetched; the reason after the colon names the error. |
359
+ | `lost` | No trace exists. Re-dispatch via `crew run --ticket <ticket>`. |
360
+
306
361
  ## Troubleshooting
307
362
 
308
363
  First stop for "labeled but not on the board": `crew doctor --ticket <ticket>` lists every check the dispatcher runs and flags the failing one.
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAyIA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BvD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAiJA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BvD"}
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { doctor } from "./commands/doctor.js";
4
4
  import { orchestrate } from "./commands/orchestrator.js";
5
5
  import { setupReposCli } from "./commands/setupRepos.js";
6
6
  import { setupWorkspaceCli } from "./commands/setupWorkspace.js";
7
- import { errorMessage, writeError, writeOutput } from "./lib/util.js";
7
+ import { errorMessage, readTicketArgument, writeError, writeOutput } from "./lib/util.js";
8
8
  const requireFromCli = createRequire(import.meta.url);
9
9
  function setupUsage() {
10
10
  return "Usage: crew setup repos [--dry-run] [<repo>...]";
@@ -32,11 +32,7 @@ async function runCli(argv) {
32
32
  continue;
33
33
  }
34
34
  if (argument === "--ticket") {
35
- const value = argv[index + 1];
36
- if (value === undefined || value.length === 0 || value.startsWith("-")) {
37
- throw new Error("crew run --ticket: ticket id is required");
38
- }
39
- ticket = value;
35
+ ticket = readTicketArgument(argv, index, "run");
40
36
  index += 1;
41
37
  continue;
42
38
  }
@@ -53,20 +49,29 @@ async function runCli(argv) {
53
49
  }
54
50
  async function doctorCli(argv) {
55
51
  let ticket;
52
+ const remainingArgs = [];
56
53
  for (let index = 0; index < argv.length; index += 1) {
57
54
  const argument = argv[index];
58
55
  if (argument === "--ticket") {
59
- const value = argv[index + 1];
60
- if (value === undefined || value.length === 0 || value.startsWith("-")) {
61
- throw new Error("crew doctor --ticket: ticket id is required");
62
- }
63
- ticket = value;
56
+ ticket = readTicketArgument(argv, index, "doctor");
64
57
  index += 1;
65
58
  continue;
66
59
  }
60
+ if (argument === "--no-linear" || argument === "--no-fetch") {
61
+ remainingArgs.push(argument);
62
+ continue;
63
+ }
67
64
  throw new Error(`crew doctor: unknown argument: ${argument}`);
68
65
  }
69
- const ok = ticket === undefined ? await doctor() : await doctor({ ticket });
66
+ if (ticket === undefined) {
67
+ if (remainingArgs.length > 0) {
68
+ throw new Error(`crew doctor: ${remainingArgs[0]} requires --ticket (host doctor mode has no flags)`);
69
+ }
70
+ const ok = await doctor();
71
+ process.exitCode = ok ? process.exitCode : 1;
72
+ return;
73
+ }
74
+ const ok = await doctor({ ticket, ticketArgv: remainingArgs });
70
75
  process.exitCode = ok ? process.exitCode : 1;
71
76
  }
72
77
  const SUBCOMMANDS = {
@@ -76,8 +81,8 @@ const SUBCOMMANDS = {
76
81
  invoke: runCli,
77
82
  },
78
83
  doctor: {
79
- summary: "Verify prereqs, or diagnose one ticket with --ticket",
80
- usage: "[--ticket <ticket>]",
84
+ summary: "Verify prereqs, or diagnose one ticket with --ticket (full lifecycle: dispatch eligibility + local-state recovery)",
85
+ usage: "[--ticket <ticket> [--no-linear] [--no-fetch]]",
81
86
  invoke: doctorCli,
82
87
  },
83
88
  cleanup: {
@@ -4,6 +4,8 @@
4
4
  */
5
5
  export interface DoctorOptions {
6
6
  ticket?: string;
7
+ /** Extra flags after `--ticket <id>`; currently `--no-linear` and `--no-fetch`. */
8
+ ticketArgv?: string[];
7
9
  }
8
10
  export declare function doctor(options?: DoctorOptions): Promise<boolean>;
9
11
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2BH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsHD,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAK1E"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2BH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAsHD,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAK1E"}
@@ -8,7 +8,7 @@ import { detectHostCapabilities, which } from "../lib/host.js";
8
8
  import { resolveLocalRunner } from "../lib/localRunner.js";
9
9
  import { errorMessage, resolveLinearApiKey, writeOutput } from "../lib/util.js";
10
10
  import { resolveWorkspaceKind } from "../lib/workspaces.js";
11
- import { runTicketDoctor } from "./ticketDoctor.js";
11
+ import { parseTicketDoctorFlags, runTicketDoctor } from "./ticketDoctor.js";
12
12
  // Tokenization stops after this many non-flag tokens. Two is enough to
13
13
  // catch wrapper + wrapped CLI commands like `safehouse claude --foo`.
14
14
  const MAX_TOKENS_PER_CMD = 2;
@@ -124,13 +124,18 @@ function format(check) {
124
124
  }
125
125
  export async function doctor(options = {}) {
126
126
  if (options.ticket !== undefined) {
127
- return await doctorTicket(options.ticket);
127
+ return await doctorTicket(options.ticket, options.ticketArgv ?? []);
128
128
  }
129
129
  return await doctorHost();
130
130
  }
131
- async function doctorTicket(ticket) {
131
+ async function doctorTicket(ticket, ticketArgv) {
132
132
  try {
133
- return await runTicketDoctor(ticket);
133
+ const flags = parseTicketDoctorFlags(ticketArgv);
134
+ return await runTicketDoctor({
135
+ ticket,
136
+ doLinear: flags.doLinear,
137
+ doFetch: flags.doFetch,
138
+ });
134
139
  }
135
140
  catch (error) {
136
141
  const displayTicket = ticket.toUpperCase();
@@ -0,0 +1,22 @@
1
+ export interface TicketCheck {
2
+ name: string;
3
+ status: "ok" | "fail" | "skipped";
4
+ detail?: string;
5
+ failureSummary?: string;
6
+ }
7
+ export interface Section {
8
+ name: string;
9
+ checks: TicketCheck[];
10
+ /** When present and `checks` is empty, the section renders as `(skipped — <skipReason>)`. */
11
+ skipReason?: string;
12
+ }
13
+ interface RenderInput {
14
+ command: string;
15
+ argument: string;
16
+ title?: string;
17
+ sections: Section[];
18
+ verdict: string;
19
+ }
20
+ export declare function renderTicketCheckResult(input: RenderInput): string[];
21
+ export {};
22
+ //# sourceMappingURL=ticketCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticketCheck.d.ts","sourceRoot":"","sources":["../../src/commands/ticketCheck.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAqBD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,CAMpE"}
@@ -0,0 +1,23 @@
1
+ const STATUS_TAG = {
2
+ ok: "[ok]",
3
+ fail: "[--]",
4
+ skipped: "[? ]",
5
+ };
6
+ function formatCheck(check) {
7
+ const tag = STATUS_TAG[check.status];
8
+ const detail = check.detail === undefined ? "" : ` (${check.detail})`;
9
+ return ` ${tag} ${check.name}${detail}`;
10
+ }
11
+ function sectionLines(section) {
12
+ if (section.checks.length === 0 && section.skipReason !== undefined) {
13
+ return [section.name, ` (skipped — ${section.skipReason})`];
14
+ }
15
+ return [section.name, ...section.checks.map(formatCheck)];
16
+ }
17
+ export function renderTicketCheckResult(input) {
18
+ const titlePart = input.title === undefined ? "" : ` (${input.title})`;
19
+ const header = `groundcrew ${input.command} ${input.argument}${titlePart}`;
20
+ const bar = "─".repeat(header.length);
21
+ const body = input.sections.flatMap((section) => ["", ...sectionLines(section)]);
22
+ return [header, bar, ...body, "", input.verdict];
23
+ }
@@ -1,7 +1,25 @@
1
1
  import { type Blocker, type RawLinearIssue } from "../lib/boardSource.ts";
2
2
  import { type ResolvedConfig } from "../lib/config.ts";
3
3
  import { type UsageByModel } from "../lib/usage.ts";
4
+ import { type WorkspaceAccessHint, type WorkspaceProbe } from "../lib/workspaces.ts";
5
+ import { type WorktreeDirtiness, type WorktreeEntry } from "../lib/worktrees.ts";
6
+ import { type TicketCheck } from "./ticketCheck.ts";
4
7
  export type TicketDoctorVerdict = {
8
+ kind: "pr-open";
9
+ number: number;
10
+ url: string;
11
+ } | {
12
+ kind: "pr-merged";
13
+ number: number;
14
+ url: string;
15
+ } | {
16
+ kind: "in-flight";
17
+ reason: string;
18
+ } | {
19
+ kind: "recoverable";
20
+ reason: string;
21
+ nextStep: string;
22
+ } | {
5
23
  kind: "would-dispatch";
6
24
  } | {
7
25
  kind: "ineligible";
@@ -9,40 +27,170 @@ export type TicketDoctorVerdict = {
9
27
  } | {
10
28
  kind: "unresolvable";
11
29
  reason: string;
30
+ } | {
31
+ kind: "lost";
32
+ reason: string;
12
33
  };
13
- export interface TicketCheck {
14
- name: string;
15
- status: "ok" | "fail" | "skipped";
16
- detail?: string;
17
- failureSummary?: string;
18
- }
19
- export interface TicketDoctorResult {
20
- ticket: string;
21
- title?: string;
22
- resolution: TicketCheck[];
23
- eligibility: TicketCheck[];
24
- verdict: TicketDoctorVerdict;
34
+ export type LinearStatusProbe = {
35
+ kind: "terminal";
36
+ stateName: string;
37
+ } | {
38
+ kind: "non-terminal";
39
+ stateName: string;
40
+ } | {
41
+ kind: "skipped";
42
+ } | {
43
+ kind: "unresolvable";
44
+ reason: string;
45
+ };
46
+ export type WorktreeProbe = {
47
+ kind: "present-clean";
48
+ } | {
49
+ kind: "present-dirty";
50
+ modified: number;
51
+ untracked: number;
52
+ } | {
53
+ kind: "present-unknown-dirtiness";
54
+ reason: string;
55
+ } | {
56
+ kind: "absent";
57
+ };
58
+ export type LocalBranchProbe = {
59
+ kind: "present";
60
+ ahead: number;
61
+ behind: number;
62
+ defaultBranch?: string;
63
+ } | {
64
+ kind: "absent";
65
+ } | {
66
+ kind: "unknown";
67
+ reason: string;
68
+ };
69
+ export type RemoteBranchProbe = {
70
+ kind: "present";
71
+ } | {
72
+ kind: "absent";
73
+ } | {
74
+ kind: "unknown";
75
+ reason: string;
76
+ };
77
+ export type PullRequestProbe = {
78
+ kind: "open";
79
+ number: number;
80
+ url: string;
81
+ } | {
82
+ kind: "merged";
83
+ number: number;
84
+ url: string;
85
+ } | {
86
+ kind: "absent";
87
+ } | {
88
+ kind: "gh-missing";
89
+ } | {
90
+ kind: "unknown";
91
+ reason: string;
92
+ };
93
+ export interface DecideVerdictInput {
94
+ linear: LinearStatusProbe;
95
+ worktree: WorktreeProbe;
96
+ localBranch: LocalBranchProbe;
97
+ remoteBranch: RemoteBranchProbe;
98
+ pullRequest: PullRequestProbe;
99
+ branch: string;
100
+ worktreeDir: string | undefined;
101
+ workspaceName: string | undefined;
25
102
  }
103
+ /**
104
+ * Returns a post-dispatch verdict if the probe bundle matches one of the
105
+ * "ticket has moved past dispatch" cases. Returns `undefined` otherwise,
106
+ * signalling that the caller should fall through to the pre-dispatch path.
107
+ *
108
+ * Precedence: PR > in-flight > recoverable. Inside `recoverable`, dirty
109
+ * worktree beats clean-with-un-pushed-local beats remote-only beats stranded
110
+ * local.
111
+ */
112
+ export declare function decidePostDispatchVerdict(input: DecideVerdictInput): TicketDoctorVerdict | undefined;
26
113
  export interface TicketDoctorDependencies {
27
114
  config: ResolvedConfig;
28
115
  ticket: string;
29
116
  /**
30
- * Injected to keep `ticketDoctor` pure and easy to unit-test. Production
31
- * callers pass a closure that delegates to `fetchRawLinearIssue` with a
32
- * real `LinearClient`; tests pass a `vi.fn()` returning a fixture.
117
+ * Injected to keep `ticketDoctor` pure and easy to unit-test. `undefined`
118
+ * means the caller passed `--no-linear` the Linear-backed pre-dispatch
119
+ * checks (status, label, repo, eligibility) are all skipped.
33
120
  */
34
- fetchRawIssue: (input: {
121
+ fetchRawIssue: ((input: {
35
122
  ticket: string;
36
- }) => Promise<RawLinearIssue>;
123
+ }) => Promise<RawLinearIssue>) | undefined;
37
124
  fetchBlockersFor: (input: {
38
125
  ticket: string;
39
126
  uuid: string;
40
127
  }) => Promise<readonly Blocker[]>;
41
128
  fetchUsage: () => Promise<UsageByModel>;
42
129
  countInProgress: () => Promise<number>;
130
+ findWorktree: (ticket: string) => WorktreeEntry | undefined;
131
+ probeWorkspaces: () => Promise<WorkspaceProbe>;
132
+ workspaceAccessHint: (name: string) => Promise<WorkspaceAccessHint | undefined>;
133
+ probeWorkingTree: (input: {
134
+ worktreeDir: string;
135
+ }) => Promise<WorktreeDirtiness>;
136
+ probeLocalBranch: (input: {
137
+ repoDir: string;
138
+ branch: string;
139
+ defaultBranch: string;
140
+ }) => Promise<LocalBranchProbe>;
141
+ probeRemoteBranch: (input: {
142
+ repoDir: string;
143
+ branch: string;
144
+ doFetch: boolean;
145
+ }) => Promise<RemoteBranchProbe>;
146
+ probePullRequest: (input: {
147
+ repoDir: string;
148
+ branch: string;
149
+ }) => Promise<PullRequestProbe>;
150
+ doFetch: boolean;
43
151
  }
44
- export declare function renderTicketDoctorResult(result: TicketDoctorResult): string[];
152
+ export interface TicketDoctorResult {
153
+ ticket: string;
154
+ title?: string;
155
+ resolution: TicketCheck[];
156
+ eligibility: TicketCheck[];
157
+ worktree: TicketCheck[];
158
+ workspace: TicketCheck[];
159
+ localBranch: TicketCheck[];
160
+ remoteBranch: TicketCheck[];
161
+ pullRequest: TicketCheck[];
162
+ skipReasons: {
163
+ resolution: string;
164
+ eligibility: string;
165
+ worktree: string;
166
+ workspace: string;
167
+ localBranch: string;
168
+ remoteBranch: string;
169
+ pullRequest: string;
170
+ };
171
+ verdict: TicketDoctorVerdict;
172
+ }
173
+ /**
174
+ * Pure-with-async orchestrator that gathers all sections plus the verdict.
175
+ * All I/O happens via injected probes — the function itself does no
176
+ * filesystem, network, or stdout work.
177
+ */
45
178
  export declare function ticketDoctor(dependencies: TicketDoctorDependencies): Promise<TicketDoctorResult>;
46
- export declare function ticketDoctorCli(argv: string[]): Promise<void>;
47
- export declare function runTicketDoctor(ticket: string): Promise<boolean>;
179
+ interface TicketDoctorArguments {
180
+ ticket: string;
181
+ doLinear: boolean;
182
+ doFetch: boolean;
183
+ }
184
+ /**
185
+ * Parses optional `--no-linear` and `--no-fetch` flags that follow
186
+ * `crew doctor --ticket <id>`. The ticket id is consumed by `cli.ts` before
187
+ * this point.
188
+ */
189
+ export declare function parseTicketDoctorFlags(argv: string[]): {
190
+ doLinear: boolean;
191
+ doFetch: boolean;
192
+ };
193
+ export declare function renderTicketDoctorResult(result: TicketDoctorResult): string[];
194
+ export declare function runTicketDoctor(parsed: TicketDoctorArguments): Promise<boolean>;
195
+ export {};
48
196
  //# sourceMappingURL=ticketDoctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ticketDoctor.d.ts","sourceRoot":"","sources":["../../src/commands/ticketDoctor.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,OAAO,EAEZ,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AASrE,MAAM,MAAM,mBAAmB,GAC3B;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,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;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,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACtE,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;CACxC;AA+SD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAmB7E;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,wBAAwB,GACrC,OAAO,CAAC,kBAAkB,CAAC,CA0F7B;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAenE;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBtE"}
1
+ {"version":3,"file":"ticketDoctor.d.ts","sourceRoot":"","sources":["../../src/commands/ticketDoctor.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,OAAO,EAEZ,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEpF,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,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;CACnC;AAqED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,kBAAkB,GACxB,mBAAmB,GAAG,SAAS,CAQjC;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,gBAAgB,EAAE,CAAC,KAAK,EAAE;QACxB,OAAO,EAAE,MAAM,CAAC;QAChB,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,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,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,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,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;AA+mBD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,wBAAwB,GACrC,OAAO,CAAC,kBAAkB,CAAC,CA6I7B;AAkCD,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;AAmCD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAuD7E;AAGD,wBAAsB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmCrF"}