@clipboard-health/groundcrew 4.48.2 → 4.48.3

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;AAiRH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAuF/C"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0SH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAyF/C"}
@@ -210,6 +210,29 @@ function gatherToolTargets(config) {
210
210
  }
211
211
  return [...all].map(([token, hint]) => (hint === undefined ? { token } : { token, hint }));
212
212
  }
213
+ function preLaunchEnvAdvisories(config) {
214
+ const advisories = [];
215
+ for (const [agentName, definition] of Object.entries(config.agents.definitions)) {
216
+ const names = definition.preLaunchEnv ?? [];
217
+ if (names.length === 0) {
218
+ continue;
219
+ }
220
+ // The runtime empty-check is spliced only when `preLaunch` is present
221
+ // (see `hostPreLaunchSourceAndReadPrompt` / `preLaunchPromptAndExec` in
222
+ // `src/lib/launchCommand.ts`). Without `preLaunch`, forwarded values come
223
+ // straight from the parent shell and no launch-time guard runs.
224
+ const warnClause = definition.preLaunch === undefined
225
+ ? "no preLaunch is set, so empty values are NOT checked at launch"
226
+ : "empty values are warned at launch";
227
+ advisories.push({
228
+ name: `preLaunchEnv: ${agentName}`,
229
+ ok: true,
230
+ required: false,
231
+ hint: `forwards ${names.length} var(s): ${names.join(", ")}. ${warnClause}.`,
232
+ });
233
+ }
234
+ return advisories;
235
+ }
213
236
  function agentCliHint(agentName, token) {
214
237
  if (!isBuiltInAgentName(agentName)) {
215
238
  return undefined;
@@ -289,6 +312,7 @@ export async function doctor() {
289
312
  const check = await checkCmd(token, required, required ? hint : "required for local runs");
290
313
  checks.push(check);
291
314
  }
315
+ checks.push(...preLaunchEnvAdvisories(config));
292
316
  const usageGatedAgents = gatedAgents(config);
293
317
  if (usageGatedAgents.length > 0) {
294
318
  const codexbarPath = await which("codexbar");
@@ -1 +1 @@
1
- {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"launchCommand.d.ts","sourceRoot":"","sources":["../../src/lib/launchCommand.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
2
2
  import path from "node:path";
3
3
  import { BUILD_SECRET_NAMES, hasPreLaunchEnv, } from "./config.js";
4
4
  import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
5
+ import { buildPreLaunchEmptyCheckLines } from "./preLaunchEmptyCheck.js";
5
6
  import { shellSingleQuote } from "./shell.js";
6
7
  export { shellSingleQuote } from "./shell.js";
7
8
  /**
@@ -116,7 +117,7 @@ function hostSourceSecrets(secretsFile) {
116
117
  function preLaunchPromptAndExec(arguments_) {
117
118
  const lines = [];
118
119
  if (arguments_.definition.preLaunch !== undefined) {
119
- lines.push(renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir));
120
+ lines.push(renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir), ...buildPreLaunchEmptyCheckLines(arguments_.definition.preLaunchEnv ?? []));
120
121
  }
121
122
  lines.push(`_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(arguments_.promptDir)}`, arguments_.execLine);
122
123
  return lines;
@@ -150,7 +151,10 @@ function renderPrepareAndAgentCommand(arguments_) {
150
151
  function hostPreLaunchSourceAndReadPrompt(arguments_) {
151
152
  const lines = [];
152
153
  if (arguments_.definition.preLaunch !== undefined) {
153
- lines.push(unsetEnvironmentLine([...BUILD_SECRET_NAMES, ...(arguments_.definition.preLaunchEnv ?? [])]), renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir));
154
+ const preLaunchEnv = arguments_.definition.preLaunchEnv ?? [];
155
+ // Order is load-bearing: unset scrubs inherited values so the empty-check
156
+ // reads what preLaunch actually produced, not what the parent shell leaked.
157
+ lines.push(unsetEnvironmentLine([...BUILD_SECRET_NAMES, ...preLaunchEnv]), renderPreLaunch(arguments_.definition.preLaunch, arguments_.worktreeDir), ...buildPreLaunchEmptyCheckLines(preLaunchEnv));
154
158
  }
155
159
  lines.push(...hostSourceSecrets(arguments_.secretsFile), `_p=$(cat ${shellSingleQuote(arguments_.promptFile)})`, `rm -rf ${shellSingleQuote(arguments_.promptDir)}`);
156
160
  return lines;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Emit POSIX-shell lines that WARN (to stderr, never abort) when a
3
+ * `preLaunchEnv` name resolves to an empty string after `preLaunch` runs.
4
+ * Length-0 is the reliable tell for the `export VAR="$(cat missing-file)"`
5
+ * failure mode: `export` masks the substitution's non-zero exit status, so
6
+ * `set -e` and exit-code checks do not catch it.
7
+ *
8
+ * Warn-only is deliberate — a hard-abort mode is a separate opt-in, so the
9
+ * chain (prompt read, prompt-dir cleanup, exec) must not be short-circuited
10
+ * by an empty value here.
11
+ *
12
+ * Returns `[]` for an empty input list so callers can splat unconditionally
13
+ * into their `&&` chain.
14
+ *
15
+ * Caveat: the check reads the shell's post-preLaunch view of the var. On the
16
+ * safehouse chain (`hostPreLaunchSourceAndReadPrompt`) the caller unsets each
17
+ * `preLaunchEnv` name before running `preLaunch`, so the check truly reflects
18
+ * what `preLaunch` produced. On the unwrapped-host chain
19
+ * (`preLaunchPromptAndExec`) no unset happens first, so a value inherited
20
+ * from the parent shell can survive `preLaunch` and mask a failed `export`.
21
+ * That asymmetry is a property of the runners; closing it requires an env
22
+ * scrub on the runner=none path, tracked as a follow-up.
23
+ *
24
+ * Names must be POSIX identifiers (`[A-Za-z_][A-Za-z0-9_]*`); groundcrew's
25
+ * `validatePreLaunchEnv` in `src/lib/config.ts` enforces that, so no shell
26
+ * escaping is done here.
27
+ */
28
+ export declare function buildPreLaunchEmptyCheckLines(names: readonly string[]): string[];
29
+ //# sourceMappingURL=preLaunchEmptyCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preLaunchEmptyCheck.d.ts","sourceRoot":"","sources":["../../src/lib/preLaunchEmptyCheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Emit POSIX-shell lines that WARN (to stderr, never abort) when a
3
+ * `preLaunchEnv` name resolves to an empty string after `preLaunch` runs.
4
+ * Length-0 is the reliable tell for the `export VAR="$(cat missing-file)"`
5
+ * failure mode: `export` masks the substitution's non-zero exit status, so
6
+ * `set -e` and exit-code checks do not catch it.
7
+ *
8
+ * Warn-only is deliberate — a hard-abort mode is a separate opt-in, so the
9
+ * chain (prompt read, prompt-dir cleanup, exec) must not be short-circuited
10
+ * by an empty value here.
11
+ *
12
+ * Returns `[]` for an empty input list so callers can splat unconditionally
13
+ * into their `&&` chain.
14
+ *
15
+ * Caveat: the check reads the shell's post-preLaunch view of the var. On the
16
+ * safehouse chain (`hostPreLaunchSourceAndReadPrompt`) the caller unsets each
17
+ * `preLaunchEnv` name before running `preLaunch`, so the check truly reflects
18
+ * what `preLaunch` produced. On the unwrapped-host chain
19
+ * (`preLaunchPromptAndExec`) no unset happens first, so a value inherited
20
+ * from the parent shell can survive `preLaunch` and mask a failed `export`.
21
+ * That asymmetry is a property of the runners; closing it requires an env
22
+ * scrub on the runner=none path, tracked as a follow-up.
23
+ *
24
+ * Names must be POSIX identifiers (`[A-Za-z_][A-Za-z0-9_]*`); groundcrew's
25
+ * `validatePreLaunchEnv` in `src/lib/config.ts` enforces that, so no shell
26
+ * escaping is done here.
27
+ */
28
+ export function buildPreLaunchEmptyCheckLines(names) {
29
+ if (names.length === 0) {
30
+ return [];
31
+ }
32
+ return [...new Set(names)].map((name) => `if [ -z "\${${name}-}" ]; then echo "preLaunchEnv: ${name} is empty after preLaunch (value length 0)" >&2; fi`);
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.48.2",
3
+ "version": "4.48.3",
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",
@@ -90,7 +90,7 @@
90
90
  "jscpd": "5.0.12",
91
91
  "knip": "6.27.0",
92
92
  "lint-staged": "17.0.8",
93
- "markdownlint-cli2": "0.23.0",
93
+ "markdownlint-cli2": "0.23.1",
94
94
  "nx": "22.7.7",
95
95
  "oxfmt": "0.59.0",
96
96
  "oxlint": "1.73.0",