@clipboard-health/groundcrew 3.1.6 → 3.1.8

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.
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/commands/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqDH,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"}
@@ -4,7 +4,7 @@
4
4
  * the cleaner, and runs the dispatcher; logging from those modules is the
5
5
  * orchestrator's user-facing output.
6
6
  */
7
- import { createBoardSource, RepositoryResolutionError, } from "../lib/boardSource.js";
7
+ import { createBoardSource } from "../lib/boardSource.js";
8
8
  import { createBoard } from "../lib/board.js";
9
9
  import { buildSources } from "../lib/buildSources.js";
10
10
  import { loadConfig } from "../lib/config.js";
@@ -24,9 +24,6 @@ async function withRetry(function_, signal, maxRetries = RETRY_MAX_ATTEMPTS, bas
24
24
  return await function_();
25
25
  }
26
26
  catch (error) {
27
- if (error instanceof RepositoryResolutionError) {
28
- throw error;
29
- }
30
27
  if (attempt === maxRetries) {
31
28
  throw error;
32
29
  }
@@ -150,9 +147,6 @@ async function runWatchLoop(tick, config) {
150
147
  if (error instanceof WatchLoopShutdownError) {
151
148
  break;
152
149
  }
153
- if (error instanceof RepositoryResolutionError) {
154
- throw error;
155
- }
156
150
  const message = errorMessage(error);
157
151
  if (message.includes("Signal: SIGINT")) {
158
152
  if (!shutdown.signal.aborted) {
@@ -27,9 +27,14 @@ export interface Issue {
27
27
  statusId: string;
28
28
  assignee: string;
29
29
  updatedAt: string;
30
- /** `undefined` when the ticket has no `agent-*` label — i.e. not groundcrew's concern. */
30
+ /**
31
+ * `undefined` unless the ticket is in Todo with a parseable `agent-*` label
32
+ * and a known-repo reference in its description — i.e. the dispatcher would
33
+ * actually pick it up. Resolving on non-Todo statuses would just invite
34
+ * tick-spam warnings on already-finished work.
35
+ */
31
36
  repository: string | undefined;
32
- /** `undefined` when the ticket has no `agent-*` labeli.e. not groundcrew's concern. */
37
+ /** `undefined` whenever `repository` isthe two are populated together. */
33
38
  model: string | undefined;
34
39
  teamId: string;
35
40
  /** SlugId of the Linear project the issue belongs to — always one of `linear.projects[*].slugId`. */
@@ -1 +1 @@
1
- {"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"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"}
@@ -177,17 +177,16 @@ async function fetchBoard(client, config) {
177
177
  }
178
178
  after = page.pageInfo.endCursor;
179
179
  }
180
- const repositoryRegex = buildRepositoryRegex(config);
181
180
  // Only parse `repository` for tickets that opted in via an `agent-*` label.
182
- // Without this gate, a single human-owned ticket without a parseable repo
183
- // would abort the whole `crew run` before the Todo filter ever runs.
181
+ // Unlabeled tickets are not groundcrew's concern even when they share a
182
+ // configured state name with one that is.
184
183
  const issues = nodes
185
184
  .filter((node) => node.children.nodes.length === 0)
186
185
  .filter((node) => issueStatusBelongsToOwnProject(node, config))
187
- .map((node) => issueFromNode(node, config, repositoryRegex));
186
+ .map((node) => issueFromNode(node, config));
188
187
  const parentSkips = nodes
189
188
  .filter((node) => node.children.nodes.length > 0)
190
- .filter((node) => isTodoParentForOwnProject(node, config))
189
+ .filter((node) => isTodoStatusForOwnProject(node, config))
191
190
  .map((node) => ({
192
191
  id: node.identifier.toLowerCase(),
193
192
  title: node.title,
@@ -196,11 +195,15 @@ async function fetchBoard(client, config) {
196
195
  return { timestamp: new Date().toISOString(), issues, parentSkips };
197
196
  }
198
197
  /**
199
- * Narrows parent tickets to those the dispatcher would otherwise pick up
200
- * Todo status under their own project's configured status names. Done /
201
- * In Progress parents aren't surprising drops, so we don't log them.
198
+ * Checks whether the node sits in Todo under its own project's configured
199
+ * status names. Used to narrow parent skips (Done / In Progress parents
200
+ * aren't surprising drops, so we don't log them) and to gate the repository
201
+ * parse on the only status the dispatcher acts on — In Progress / Done
202
+ * tickets never read `Issue.repository` so parsing it for them just creates
203
+ * tick-spamming warnings when an operator already-finished ticket can't be
204
+ * re-parsed.
202
205
  */
203
- function isTodoParentForOwnProject(node, config) {
206
+ function isTodoStatusForOwnProject(node, config) {
204
207
  const slugId = node.project?.slugId?.toLowerCase();
205
208
  /* v8 ignore next 3 @preserve -- GraphQL slugId filter scopes results to configured projects */
206
209
  if (slugId === undefined) {
@@ -220,22 +223,36 @@ function modelForResolution(resolution) {
220
223
  if (resolution.kind === "disabled-fallback") {
221
224
  return resolution.fallbackModel;
222
225
  }
223
- if (resolution.kind === "agent-any") {
224
- return AGENT_ANY_MODEL;
225
- }
226
- return undefined;
226
+ return AGENT_ANY_MODEL;
227
227
  }
228
- function issueFromNode(node, config, repositoryRegex) {
228
+ function issueFromNode(node, config) {
229
229
  const modelResolution = resolveModelFor({ labels: node.labels.nodes, config });
230
230
  warnIfDisabledFallback(node.identifier, modelResolution, config);
231
- const repository = modelResolution.kind === "no-label"
232
- ? undefined
233
- : parseRepository({
231
+ let repository;
232
+ let model;
233
+ // Only the dispatcher reads `Issue.repository` / `Issue.model`, and only on
234
+ // tickets in the Todo column it's about to pick up. Resolving them for In
235
+ // Progress (already running) or Done (cleaner only needs the id) would just
236
+ // invite tick-spam warnings on already-finished tickets — e.g. when a
237
+ // description was edited or knownRepositories changed after dispatch. A
238
+ // ticket sitting in Todo with no parseable repo is still the operator's
239
+ // bug, so surface it loudly there and treat it as not-eligible so the rest
240
+ // of the board still ticks instead of the whole watch loop crashing on
241
+ // one bad ticket.
242
+ if (modelResolution.kind !== "no-label" && isTodoStatusForOwnProject(node, config)) {
243
+ const resolution = resolveRepositoryFor({
234
244
  description: node.description ?? undefined,
235
245
  config,
236
- repositoryRegex,
237
246
  ticket: node.identifier,
238
247
  });
248
+ if (resolution.kind === "ok") {
249
+ ({ repository } = resolution);
250
+ model = modelForResolution(modelResolution);
251
+ }
252
+ else {
253
+ log(`WARNING: ${node.identifier} has an ${AGENT_LABEL_PREFIX}* label but no known repository in its description; skipping dispatch. Add one of workspace.knownRepositories to the description, or remove the ${AGENT_LABEL_PREFIX}* label: ${config.workspace.knownRepositories.join(", ")}`);
254
+ }
255
+ }
239
256
  // `issueStatusBelongsToOwnProject` drops nodes whose `state` or `project`
240
257
  // is missing, so by the time we land here both are defined. The nullish
241
258
  // coalescing on those fields is belt-and-suspenders for type narrowing.
@@ -250,7 +267,7 @@ function issueFromNode(node, config, repositoryRegex) {
250
267
  assignee: node.assignee?.name ?? "Unassigned",
251
268
  updatedAt: node.updatedAt,
252
269
  repository,
253
- model: modelForResolution(modelResolution),
270
+ model,
254
271
  teamId: node.team?.id ?? "",
255
272
  /* v8 ignore next @preserve -- post-filter guarantees `project` is defined */
256
273
  projectSlugId: node.project?.slugId?.toLowerCase() ?? "",
@@ -544,38 +561,6 @@ export async function fetchResolvedIssue(arguments_) {
544
561
  projectSlugId: project.slugId,
545
562
  };
546
563
  }
547
- function parseRepository(arguments_) {
548
- const { description, config, repositoryRegex, ticket } = arguments_;
549
- if (description === undefined || description.length === 0) {
550
- throw new RepositoryResolutionError({
551
- ticket,
552
- repositories: config.workspace.knownRepositories,
553
- });
554
- }
555
- const matched = repositoryRegex.exec(description)?.[1];
556
- if (matched === undefined) {
557
- throw new RepositoryResolutionError({
558
- ticket,
559
- repositories: config.workspace.knownRepositories,
560
- });
561
- }
562
- // Resolve the match to a known repo. The regex may capture a bare repo name
563
- // (no org prefix) when only that appears in the description; the filter
564
- // handles both full "owner/repo" and bare "repo" matches. Reject if
565
- // ambiguous (same bare name under multiple orgs).
566
- const candidates = config.workspace.knownRepositories.filter((r) => r === matched || r.endsWith(`/${matched}`));
567
- if (candidates.length !== 1) {
568
- throw new RepositoryResolutionError({
569
- ticket,
570
- repositories: config.workspace.knownRepositories,
571
- });
572
- }
573
- /* v8 ignore next 3 @preserve -- candidates.length===1 guarantees [0] is defined */
574
- if (candidates[0] === undefined) {
575
- throw new Error("unreachable");
576
- }
577
- return candidates[0];
578
- }
579
564
  function parseAgentLabels(labels, config) {
580
565
  const agentLabels = labels.filter((label) => label.name.startsWith(AGENT_LABEL_PREFIX));
581
566
  if (agentLabels.length === 0) {
@@ -15,7 +15,7 @@ export declare function resolveSafehouseClearancePath(baseUrl?: string): string;
15
15
  * Per-repo setup hook: if `.groundcrew/setup.sh` exists, run it with
16
16
  * `--deps-only`; otherwise no-op.
17
17
  */
18
- export declare const SETUP_COMMAND = "[ -f .groundcrew/setup.sh ] && bash .groundcrew/setup.sh --deps-only";
18
+ export declare const SETUP_COMMAND = "if [ -f .groundcrew/setup.sh ]; then bash .groundcrew/setup.sh --deps-only; fi";
19
19
  interface LaunchCommandArguments {
20
20
  definition: ModelDefinition;
21
21
  promptFile: string;
@@ -1 +1 @@
1
- {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;GAGG;AACH,eAAO,MAAM,aAAa,yEAAyE,CAAC;AAiCpG,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAK7E"}
1
+ {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;GAGG;AACH,eAAO,MAAM,aAAa,mFACwD,CAAC;AAiCnF,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAK7E"}
@@ -29,7 +29,7 @@ const SAFEHOUSE_CLEARANCE_WRAPPER_PATH = resolveSafehouseClearancePath();
29
29
  * Per-repo setup hook: if `.groundcrew/setup.sh` exists, run it with
30
30
  * `--deps-only`; otherwise no-op.
31
31
  */
32
- export const SETUP_COMMAND = "[ -f .groundcrew/setup.sh ] && bash .groundcrew/setup.sh --deps-only";
32
+ export const SETUP_COMMAND = "if [ -f .groundcrew/setup.sh ]; then bash .groundcrew/setup.sh --deps-only; fi";
33
33
  function renderAgentCommand(arguments_) {
34
34
  return arguments_.agentCmd
35
35
  .replaceAll("{{worktree}}", shellSingleQuote(arguments_.worktreeDir))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "3.1.6",
3
+ "version": "3.1.8",
4
4
  "description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
5
5
  "keywords": [
6
6
  "agent",