@clipboard-health/groundcrew 4.31.1 → 4.32.0

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":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+KH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAmF/C"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyLH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAmF/C"}
@@ -7,6 +7,7 @@ import { createBoard } from "../lib/board.js";
7
7
  import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
8
8
  import { loadConfigWithSource, worktreeBaseDir, } from "../lib/config.js";
9
9
  import { detectHostCapabilities, which } from "../lib/host.js";
10
+ import { isEnvironmentAssignment } from "../lib/launchCommand.js";
10
11
  import { resolveLocalRunner } from "../lib/localRunner.js";
11
12
  import { gatedAgents } from "../lib/usage.js";
12
13
  import { errorMessage, writeOutput } from "../lib/util.js";
@@ -78,12 +79,14 @@ function checkDir(path, label) {
78
79
  * the executable name (first non-flag token), and any subsequent
79
80
  * non-flag, non-flag-value token until a flag is hit. Flag tokens are
80
81
  * dropped along with the token immediately following them (treated as
81
- * the flag's value).
82
+ * the flag's value). `env VAR=val` assignments are skipped (they are not
83
+ * binaries), so the wrapped command is still found.
82
84
  *
83
85
  * Examples:
84
86
  * "safehouse claude --permission-mode auto" → ["safehouse", "claude"]
85
87
  * "claude" → ["claude"]
86
88
  * "node --inspect script.ts" → ["node"] (script.ts skipped — flag value)
89
+ * "env GIT_CONFIG_COUNT=1 claude --foo" → ["env", "claude"] (assignment skipped)
87
90
  */
88
91
  function commandTokensToCheck(cmd) {
89
92
  const parts = cmd.trim().split(/\s+/);
@@ -101,6 +104,13 @@ function commandTokensToCheck(cmd) {
101
104
  index += 2;
102
105
  continue;
103
106
  }
107
+ if (isEnvironmentAssignment(token)) {
108
+ // An `env VAR=val …` prefix: the assignment is not a binary, so don't
109
+ // probe it on PATH. Mirrors the launch path's inferAgentCommandName,
110
+ // which skips assignments to find the real agent command.
111
+ index += 1;
112
+ continue;
113
+ }
104
114
  result.push(token);
105
115
  if (result.length >= MAX_TOKENS_PER_CMD) {
106
116
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AASrB;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAwBhE;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+C/B;AAwBD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB"}
1
+ {"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AASrB;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CA0BhE;AAsBD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+C/B;AAwBD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB"}
@@ -5,7 +5,7 @@ import { detectHostCapabilities } from "./host.js";
5
5
  import { buildLaunchCommand } from "./launchCommand.js";
6
6
  import { assertLocalRunnerRequirements, resolveLocalRunner } from "./localRunner.js";
7
7
  import { sandboxNameFor } from "./sandboxName.js";
8
- import { buildAndStageSrtLaunch } from "./srtLaunch.js";
8
+ import { buildAndStageSrtLaunch, resolveGitCommonDir } from "./srtLaunch.js";
9
9
  import { debug, sleep } from "./util.js";
10
10
  import { workspaces } from "./workspaces.js";
11
11
  /**
@@ -36,9 +36,29 @@ export function composeAgentLaunch(input) {
36
36
  srtAgentSettingsFile: staged?.agentFile,
37
37
  srtSettingsDir: staged?.directory,
38
38
  srtAgentConfigDirEnv: staged?.agentConfigDirEnv,
39
+ safehouseAddDirs: input.runner === "safehouse" ? resolveSafehouseAddDirs(input.worktreeDir) : undefined,
39
40
  });
40
41
  return { launchCommand, srtSettingsDir: staged?.directory };
41
42
  }
43
+ /**
44
+ * Filesystem paths the safehouse sandbox must be granted (read/write) beyond
45
+ * its automatic cwd grant, so git works for every worktree shape:
46
+ *
47
+ * - `worktreeDir` — the checkout root. A `workdir` subproject cwd's into a
48
+ * subdir, so without this the worktree-root `.git` gitfile is unreachable.
49
+ * - the **git common dir** — resolved from the worktree itself (not assumed to
50
+ * be `<projectDir>/<repo>/.git`), so a scripted/sparse-checkout worktree
51
+ * whose store lives outside the worktree tree (e.g. graft's `~/carrot/.git`)
52
+ * gets git access. This is the path the bare cwd grant fundamentally cannot
53
+ * cover, and the reason this resolution exists.
54
+ * Gated to the safehouse runner at the call site (srt fences its own equivalent
55
+ * surface — worktree root + git common dir — through its settings file; sdx/none
56
+ * don't use it). Deduped defensively in case git resolves either path to the
57
+ * same directory in an unusual checkout shape.
58
+ */
59
+ function resolveSafehouseAddDirs(worktreeDir) {
60
+ return [...new Set([worktreeDir, resolveGitCommonDir(worktreeDir)])];
61
+ }
42
62
  export async function prepareAgentLaunch(input) {
43
63
  const host = await detectHostCapabilities(input.signal);
44
64
  const runner = resolveLocalRunner(input.config.local.runner, host);
@@ -30,6 +30,7 @@ export declare function resolveSrtBinPath(baseUrl?: string): string;
30
30
  export declare function srtBinEntry(manifest: {
31
31
  bin?: string | Record<string, string>;
32
32
  }): string;
33
+ export declare function isEnvironmentAssignment(token: string): boolean;
33
34
  /**
34
35
  * Infer the agent's command basename from a agent `cmd` (skipping a leading
35
36
  * `env`/`KEY=val` prefix). Safehouse uses it to pick the matching `.sb`
@@ -102,6 +103,14 @@ interface LaunchCommandArguments {
102
103
  name: string;
103
104
  value: string;
104
105
  } | undefined;
106
+ /**
107
+ * Extra filesystem paths granted read/write to the safehouse sandbox via
108
+ * `--add-dirs`, beyond safehouse's automatic cwd grant. Resolved (and deduped)
109
+ * by `composeAgentLaunch`'s `resolveSafehouseAddDirs` — see there for which
110
+ * paths and why git needs them. Empty/undefined → no `--add-dirs` flag (the
111
+ * pre-existing behavior). Only consumed by the safehouse wrap.
112
+ */
113
+ safehouseAddDirs?: readonly string[] | undefined;
105
114
  }
106
115
  /**
107
116
  * Build the shell command that runs inside the workspace. The prompt is
@@ -1 +1 @@
1
- {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAMvF;AA0MD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACpE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CA6B7E"}
1
+ {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAcvF;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,MAAM,CAMvF;AAsMD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACnE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAClD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CA6B7E"}
@@ -213,7 +213,7 @@ function tokenizeShellPrefix(command) {
213
213
  }
214
214
  return tokens;
215
215
  }
216
- function isEnvironmentAssignment(token) {
216
+ export function isEnvironmentAssignment(token) {
217
217
  return /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
218
218
  }
219
219
  /**
@@ -367,6 +367,13 @@ function buildSafehouseLaunchCommand(arguments_) {
367
367
  const prepareWorktreeEnvPassFlag = arguments_.secretsFile === undefined ? "" : `--env-pass=${BUILD_SECRET_NAMES.join(",")} `;
368
368
  const preLaunchEnvNames = arguments_.definition.preLaunchEnv ?? [];
369
369
  const agentEnvPassFlag = preLaunchEnvNames.length === 0 ? "" : `--env-pass=${preLaunchEnvNames.join(",")} `;
370
+ // safehouse reads colon-separated paths from `--add-dirs`; both wraps get the
371
+ // same grant so the prepareWorktree hook and the agent can each reach git.
372
+ // Quote the whole value so shell-special chars survive; the trailing space
373
+ // separates it from the next argv token. See `resolveSafehouseAddDirs` for
374
+ // which paths these are and why.
375
+ const addDirs = arguments_.safehouseAddDirs ?? [];
376
+ const safehouseAddDirsFlag = addDirs.length === 0 ? "" : `--add-dirs=${shellSingleQuote(addDirs.join(":"))} `;
370
377
  const safehouseWrapper = safehouseClearanceWrapperCommand();
371
378
  // Defensive shim+promptDir trap: by the time we arm it, `rm -rf <promptDir>`
372
379
  // has already run (line below) so the promptDir wipe is a no-op on the happy
@@ -386,7 +393,7 @@ function buildSafehouseLaunchCommand(arguments_) {
386
393
  secretsFile: arguments_.secretsFile,
387
394
  }));
388
395
  if (prepareWorktreeCommand !== undefined) {
389
- lines.push(`${safehouseWrapper} ${prepareWorktreeEnvPassFlag}sh -c ${shellSingleQuote(prepareWorktreeCommand)}`);
396
+ lines.push(`${safehouseWrapper} ${safehouseAddDirsFlag}${prepareWorktreeEnvPassFlag}sh -c ${shellSingleQuote(prepareWorktreeCommand)}`);
390
397
  }
391
398
  if (arguments_.secretsFile !== undefined) {
392
399
  lines.push(unsetSecretsLine());
@@ -396,7 +403,7 @@ function buildSafehouseLaunchCommand(arguments_) {
396
403
  // Running the real launch chain as `sh -c` would make it see `sh`, so use
397
404
  // an agent-named symlink to /bin/sh. This preserves per-agent profile
398
405
  // selection without enabling every agent profile.
399
- `{ ${safehouseWrapper} ${agentEnvPassFlag}"$_safehouse_shim" -c ${shellSingleQuote(agentCommand)} sh "$_p"; _safehouse_status=$?; rm -rf "$_safehouse_shim_dir"; trap - EXIT; exit "$_safehouse_status"; }`);
406
+ `{ ${safehouseWrapper} ${safehouseAddDirsFlag}${agentEnvPassFlag}"$_safehouse_shim" -c ${shellSingleQuote(agentCommand)} sh "$_p"; _safehouse_status=$?; rm -rf "$_safehouse_shim_dir"; trap - EXIT; exit "$_safehouse_status"; }`);
400
407
  return lines.join(" && ");
401
408
  }
402
409
  /**
@@ -16,6 +16,18 @@ export interface StagedSrtLaunch {
16
16
  value: string;
17
17
  };
18
18
  }
19
+ /**
20
+ * Resolve the worktree's real git common dir — the shared `.git` the srt policy
21
+ * fences off (read-grant + narrow write-allow + write-denies). Derived from the
22
+ * worktree itself rather than assuming a `<projectDir>/<repo>/.git` clone, so
23
+ * scripted/sparse-checkout worktrees — whose checkout is owned by an external
24
+ * provisioner and whose `repo` is just an alias with no clone on disk — get the
25
+ * correct dir instead of a phantom path that breaks git access and leaves the
26
+ * real common dir unfenced. For native worktrees this returns the same
27
+ * `<projectDir>/<repo>/.git` as before. `--path-format=absolute` keeps the path
28
+ * absolute regardless of git version or cwd.
29
+ */
30
+ export declare function resolveGitCommonDir(worktreeDir: string): string;
19
31
  /**
20
32
  * Generate the srt policies for a launch and stage them, plus — for agents that
21
33
  * cannot run with a read-only config home (codex) — a relocated, writable
@@ -1 +1 @@
1
- {"version":3,"file":"srtLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/srtLaunch.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAuBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,CAAC;IAC5B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CA6ClB"}
1
+ {"version":3,"file":"srtLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/srtLaunch.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,CAAC;IAC5B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CA6ClB"}
@@ -18,7 +18,7 @@ import { readEnvironmentVariable } from "./util.js";
18
18
  * `<projectDir>/<repo>/.git` as before. `--path-format=absolute` keeps the path
19
19
  * absolute regardless of git version or cwd.
20
20
  */
21
- function resolveGitCommonDir(worktreeDir) {
21
+ export function resolveGitCommonDir(worktreeDir) {
22
22
  return runCommand("git", [
23
23
  "-C",
24
24
  worktreeDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.31.1",
3
+ "version": "4.32.0",
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",