@clipboard-health/groundcrew 4.37.0 → 4.38.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.
@@ -73,6 +73,12 @@ conda.anaconda.org
73
73
  registry.npmjs.org
74
74
  www.npmjs.com
75
75
 
76
+ # PyPI registry + package CDN (pip / uv). pypi.org serves the Simple index;
77
+ # files.pythonhosted.org serves the wheel/sdist artifacts. uv's managed-Python
78
+ # downloads come from github.com (already allowed above).
79
+ files.pythonhosted.org
80
+ pypi.org
81
+
76
82
  # Google APIs
77
83
  developers.google.com
78
84
  www.googleapis.com
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AAC9B,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,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,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,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAoOjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AAC9B,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,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,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,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAyOjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
@@ -6,7 +6,9 @@
6
6
  * Pure verdict logic lives in `eligibility.ts`; this module is responsible
7
7
  * for telemetry, writeback via Board, and side-effecting setupWorkspace calls.
8
8
  */
9
+ import { sourcesFromConfig } from "../lib/buildSources.js";
9
10
  import { dispatchableRepository, formatKnownRepositories } from "../lib/repositoryValidation.js";
11
+ import { sourceSupportsMarkDone } from "../lib/sourceCapabilities.js";
10
12
  import { isGroundcrewIssue, naturalIdFromCanonical, } from "../lib/taskSource.js";
11
13
  import { errorMessage, failMark, log, logEvent, styleWarning } from "../lib/util.js";
12
14
  import { workspaces } from "../lib/workspaces.js";
@@ -35,6 +37,7 @@ function logMissingRepositorySkip(issue, agent, knownRepositories) {
35
37
  }
36
38
  export function createDispatcher(deps) {
37
39
  const { config, board } = deps;
40
+ const rawSources = sourcesFromConfig(config);
38
41
  function buildExhaustedSet(usage) {
39
42
  const exhausted = new Set();
40
43
  for (const exhaustion of classifyUsageExhaustion(config, usage)) {
@@ -71,6 +74,10 @@ export function createDispatcher(deps) {
71
74
  repository: issue.repository,
72
75
  task: taskId,
73
76
  completionTaskId: issue.id,
77
+ completionMarkDoneSupported: sourceSupportsMarkDone({
78
+ rawSources,
79
+ sourceName: issue.source,
80
+ }),
74
81
  agent: issue.agent,
75
82
  details: {
76
83
  title: issue.title,
@@ -1 +1 @@
1
- {"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAgBnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AA0ID,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAuFf;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
1
+ {"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAiBnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAqJD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA0Ff;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
@@ -1,10 +1,11 @@
1
1
  import { fetchResolvedIssue } from "../lib/adapters/linear/fetch.js";
2
2
  import { getLinearClient } from "../lib/adapters/linear/client.js";
3
- import { isLinearEnabled } from "../lib/buildSources.js";
3
+ import { isLinearEnabled, sourcesFromConfig } from "../lib/buildSources.js";
4
4
  import { loadConfig } from "../lib/config.js";
5
5
  import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
6
6
  import { workerEnvironmentForTask } from "../lib/launchCommand.js";
7
7
  import { readRunState, recordRunState } from "../lib/runState.js";
8
+ import { taskSupportsCompletionCommand } from "../lib/sourceCapabilities.js";
8
9
  import { removeStagedPrompt, stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
9
10
  import { taskSourceWritePathsForCompletion } from "../lib/taskSourceFilesystem.js";
10
11
  import { naturalIdFromCanonical, toCanonicalId } from "../lib/taskSource.js";
@@ -33,6 +34,7 @@ async function fetchTaskDetails(task) {
33
34
  }
34
35
  async function contextFromLinear(config, task, worktree) {
35
36
  const resolved = await fetchResolvedIssue({ client: getLinearClient(), config, task });
37
+ const completionTaskId = toCanonicalId("linear", task);
36
38
  return {
37
39
  task,
38
40
  repository: resolved.repository,
@@ -40,7 +42,11 @@ async function contextFromLinear(config, task, worktree) {
40
42
  worktree,
41
43
  title: resolved.title,
42
44
  description: resolved.description,
43
- completionTaskId: toCanonicalId("linear", task),
45
+ completionTaskId,
46
+ completionMarkDoneSupported: taskSupportsCompletionCommand({
47
+ rawSources: sourcesFromConfig(config),
48
+ taskId: completionTaskId,
49
+ }),
44
50
  resumeCount: 0,
45
51
  };
46
52
  }
@@ -49,6 +55,7 @@ async function contextFromState(config, task, state, worktree) {
49
55
  // missing-API-key error logs noisily even though resume only needs it to
50
56
  // enrich the prompt title/description (which falls back to the task id).
51
57
  const details = isLinearEnabled(config) ? await fetchTaskDetails(task) : undefined;
58
+ const completionTaskId = state.completionTaskId ?? task;
52
59
  return {
53
60
  task,
54
61
  repository: state.repository,
@@ -56,7 +63,11 @@ async function contextFromState(config, task, state, worktree) {
56
63
  worktree,
57
64
  title: details?.title ?? task.toUpperCase(),
58
65
  description: details?.description ?? "",
59
- completionTaskId: state.completionTaskId ?? task,
66
+ completionTaskId,
67
+ completionMarkDoneSupported: taskSupportsCompletionCommand({
68
+ rawSources: sourcesFromConfig(config),
69
+ taskId: completionTaskId,
70
+ }),
60
71
  ...(state.reason === undefined ? {} : { reason: state.reason }),
61
72
  resumeCount: state.resumeCount,
62
73
  };
@@ -159,7 +170,10 @@ export async function resumeWorkspace(config, options) {
159
170
  secretsFile,
160
171
  sandboxName,
161
172
  workspaceKind,
162
- workerEnvironment: workerEnvironmentForTask(context.completionTaskId),
173
+ workerEnvironment: workerEnvironmentForTask({
174
+ taskId: context.completionTaskId,
175
+ markDoneSupported: context.completionMarkDoneSupported,
176
+ }),
163
177
  taskSourceWritePaths,
164
178
  }));
165
179
  const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launchCommand);
@@ -9,6 +9,8 @@ export interface SetupWorkspaceOptions {
9
9
  task: string;
10
10
  /** Canonical source id for worker self-completion; falls back to `task`. */
11
11
  completionTaskId?: string;
12
+ /** Whether the task source can apply `crew task done`; defaults to true for direct calls. */
13
+ completionMarkDoneSupported?: boolean;
12
14
  repository: string;
13
15
  agent: string;
14
16
  details: TaskDetails;
@@ -1 +1 @@
1
- {"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAwBnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CAwIf;AAgJD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
1
+ {"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAyBnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,EAC9B,UAAU,GAAE,wBAA6B,GACxC,OAAO,CAAC,IAAI,CAAC,CA4If;AAgJD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CAkDf"}
@@ -6,6 +6,7 @@ import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
6
6
  import { workerEnvironmentForTask } from "../lib/launchCommand.js";
7
7
  import { resolvePrepareWorktreeCommand } from "../lib/repositoryHooks.js";
8
8
  import { recordRunState } from "../lib/runState.js";
9
+ import { sourceSupportsMarkDone } from "../lib/sourceCapabilities.js";
9
10
  import { stageBuildSecrets, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
10
11
  import { taskSourceWritePathsForCompletion } from "../lib/taskSourceFilesystem.js";
11
12
  import { naturalIdFromCanonical } from "../lib/taskSource.js";
@@ -83,6 +84,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
83
84
  });
84
85
  const secretsFile = prepareWorktreeCommand === undefined ? undefined : stageBuildSecrets(promptDir);
85
86
  const completionTaskId = options.completionTaskId ?? task;
87
+ const completionMarkDoneSupported = options.completionMarkDoneSupported ?? true;
86
88
  const taskSourceWritePaths = runner === "safehouse" || runner === "srt"
87
89
  ? taskSourceWritePathsForCompletion({
88
90
  config,
@@ -101,7 +103,10 @@ export async function setupWorkspace(config, options, runOptions = {}) {
101
103
  prepareWorktreeCommand,
102
104
  sandboxName,
103
105
  workspaceKind,
104
- workerEnvironment: workerEnvironmentForTask(completionTaskId),
106
+ workerEnvironment: workerEnvironmentForTask({
107
+ taskId: completionTaskId,
108
+ markDoneSupported: completionMarkDoneSupported,
109
+ }),
105
110
  taskSourceWritePaths,
106
111
  });
107
112
  srtSettingsDir = stagedSrtSettingsDir;
@@ -264,9 +269,10 @@ async function rollbackWorktree(arguments_) {
264
269
  }
265
270
  export async function setupWorkspaceCli(task, options = {}) {
266
271
  const config = await loadConfig();
272
+ const rawSources = sourcesFromConfig(config);
267
273
  let sources;
268
274
  try {
269
- sources = await buildSources(sourcesFromConfig(config), { globalConfig: config });
275
+ sources = await buildSources(rawSources, { globalConfig: config });
270
276
  }
271
277
  catch (error) {
272
278
  /* v8 ignore next @preserve -- catch re-throw always receives an Error; String(error) is an unreachable fallback */
@@ -292,6 +298,10 @@ export async function setupWorkspaceCli(task, options = {}) {
292
298
  await setupWorkspace(config, {
293
299
  task: naturalId,
294
300
  completionTaskId: resolved.id,
301
+ completionMarkDoneSupported: sourceSupportsMarkDone({
302
+ rawSources,
303
+ sourceName: resolved.source,
304
+ }),
295
305
  repository: resolved.repository,
296
306
  agent: resolved.agent,
297
307
  details: {
@@ -108,7 +108,7 @@ const DEFAULT_PROMPT_INITIAL = [
108
108
  "2. Implement the smallest sensible change that completes the task.",
109
109
  "3. Run the repo's documented verification command. If no documented command exists, run the smallest relevant test suite you can find and fix failures you introduced before continuing.",
110
110
  "4. Follow the task description for output. If no output instructions exist, open a PR with `Closes {{task}}` in the description. If you cannot open one, leave the branch ready and record the blocker.",
111
- "5. If the requested work is complete, no PR is needed, and any dirty worktree state is expected or explicitly allowed, run the command in `GROUNDCREW_COMPLETE` to mark the task done.",
111
+ "5. If the requested work is complete, no PR is needed, `GROUNDCREW_COMPLETE` is set, and any dirty worktree state is expected or explicitly allowed, run the command in `GROUNDCREW_COMPLETE` to mark the task done.",
112
112
  ].join("\n");
113
113
  const ALLOWED_PROMPT_PLACEHOLDERS = new Set([
114
114
  "{{task}}",
@@ -37,10 +37,14 @@ export declare function isEnvironmentAssignment(token: string): boolean;
37
37
  * profile; srt uses it to pick the agent's credential profile in `srtPolicy`.
38
38
  */
39
39
  export declare function inferAgentCommandName(agentCmd: string): string;
40
- declare const WORKER_ENVIRONMENT_NAMES: readonly ["GROUNDCREW_TASK_ID", "GROUNDCREW_COMPLETE"];
41
- type WorkerEnvironmentName = (typeof WORKER_ENVIRONMENT_NAMES)[number];
42
- export type WorkerEnvironment = Readonly<Record<WorkerEnvironmentName, string>>;
43
- export declare function workerEnvironmentForTask(taskId: string): WorkerEnvironment;
40
+ export type WorkerEnvironment = Readonly<{
41
+ GROUNDCREW_TASK_ID: string;
42
+ GROUNDCREW_COMPLETE?: string;
43
+ }>;
44
+ export declare function workerEnvironmentForTask(arguments_: {
45
+ taskId: string;
46
+ markDoneSupported: boolean;
47
+ }): WorkerEnvironment;
44
48
  export interface SafehouseAgentIntegration {
45
49
  addDirsReadOnly: readonly string[];
46
50
  envPass: readonly string[];
@@ -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;AAsMD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B9D;AAED,QAAA,MAAM,wBAAwB,YAAI,oBAAoB,EAAE,qBAAqB,CAAU,CAAC;AAExF,KAAK,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC;AAEhF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAK1E;AAsBD,MAAM,WAAW,yBAAyB;IACxC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;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;IACjD;;;OAGG;IACH,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACtD;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAkC7E"}
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;AAMD,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,iBAAiB,CAMpB;AA8BD,MAAM,WAAW,yBAAyB;IACxC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;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;IACjD;;;OAGG;IACH,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACtD;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClE;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAkC7E"}
@@ -248,20 +248,31 @@ export function inferAgentCommandName(agentCmd) {
248
248
  return commandName;
249
249
  }
250
250
  const WORKER_ENVIRONMENT_NAMES = ["GROUNDCREW_TASK_ID", "GROUNDCREW_COMPLETE"];
251
- export function workerEnvironmentForTask(taskId) {
251
+ export function workerEnvironmentForTask(arguments_) {
252
+ const { taskId, markDoneSupported } = arguments_;
252
253
  return {
253
254
  GROUNDCREW_TASK_ID: taskId,
254
- GROUNDCREW_COMPLETE: `crew task done ${taskId}`,
255
+ ...(markDoneSupported ? { GROUNDCREW_COMPLETE: `crew task done ${taskId}` } : {}),
255
256
  };
256
257
  }
257
258
  function workerEnvironmentNames(workerEnvironment) {
258
- return workerEnvironment === undefined ? [] : WORKER_ENVIRONMENT_NAMES;
259
+ if (workerEnvironment === undefined) {
260
+ return [];
261
+ }
262
+ return WORKER_ENVIRONMENT_NAMES.filter((name) => workerEnvironment[name] !== undefined);
259
263
  }
260
264
  function workerEnvironmentExports(workerEnvironment) {
261
265
  if (workerEnvironment === undefined) {
262
266
  return [];
263
267
  }
264
- return WORKER_ENVIRONMENT_NAMES.map((name) => `export ${name}=${shellSingleQuote(workerEnvironment[name])}`);
268
+ const exports = [];
269
+ for (const name of WORKER_ENVIRONMENT_NAMES) {
270
+ const value = workerEnvironment[name];
271
+ if (value !== undefined) {
272
+ exports.push(`export ${name}=${shellSingleQuote(value)}`);
273
+ }
274
+ }
275
+ return exports;
265
276
  }
266
277
  function envPassFlag(names) {
267
278
  const uniqueNames = [...new Set(names)];
@@ -14,5 +14,13 @@ export interface SourceSummary {
14
14
  capabilities: SourceCapabilities;
15
15
  }
16
16
  export declare function summarizeSource(raw: unknown): SourceSummary;
17
+ export declare function sourceSupportsMarkDone(arguments_: {
18
+ rawSources: readonly unknown[];
19
+ sourceName: string;
20
+ }): boolean;
21
+ export declare function taskSupportsCompletionCommand(arguments_: {
22
+ rawSources: readonly unknown[];
23
+ taskId: string;
24
+ }): boolean;
17
25
  export {};
18
26
  //# sourceMappingURL=sourceCapabilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sourceCapabilities.d.ts","sourceRoot":"","sources":["../../src/lib/sourceCapabilities.ts"],"names":[],"mappings":"AAKA,UAAU,kBAAkB;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,kBAAkB,CAAC;CAClC;AAoDD,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAiB3D"}
1
+ {"version":3,"file":"sourceCapabilities.d.ts","sourceRoot":"","sources":["../../src/lib/sourceCapabilities.ts"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,kBAAkB,CAAC;CAClC;AAqDD,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAiB3D;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE;IACjD,UAAU,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CASV;AAED,wBAAgB,6BAA6B,CAAC,UAAU,EAAE;IACxD,UAAU,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAcV"}
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { shellAdapterConfigSchema } from "./adapters/shell/schema.js";
3
- import { kindShape } from "./buildSources.js";
4
3
  const nameShape = z.looseObject({ name: z.string().optional() });
4
+ const kindShape = z.object({ kind: z.string() });
5
5
  const LINEAR_CAPABILITIES = {
6
6
  verify: true,
7
7
  listTasks: true,
@@ -65,3 +65,28 @@ export function summarizeSource(raw) {
65
65
  }
66
66
  return { name: sourceName, kind, capabilities };
67
67
  }
68
+ export function sourceSupportsMarkDone(arguments_) {
69
+ const { rawSources, sourceName } = arguments_;
70
+ for (const rawSource of rawSources) {
71
+ const source = summarizeSource(rawSource);
72
+ if (source.name === sourceName) {
73
+ return source.capabilities.markDone;
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ export function taskSupportsCompletionCommand(arguments_) {
79
+ const { rawSources, taskId } = arguments_;
80
+ const colonIndex = taskId.indexOf(":");
81
+ if (colonIndex === -1) {
82
+ const [singleSource] = rawSources;
83
+ if (rawSources.length === 1 && singleSource !== undefined) {
84
+ return summarizeSource(singleSource).capabilities.markDone;
85
+ }
86
+ return false;
87
+ }
88
+ return sourceSupportsMarkDone({
89
+ rawSources,
90
+ sourceName: taskId.slice(0, colonIndex),
91
+ });
92
+ }
package/docs/runners.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Only applies when `local.runner` resolves to `safehouse`. Groundcrew starts `clearance` on `http://127.0.0.1:19999` and runs the agent through the bundled `safehouse-clearance` wrapper. Groundcrew automatically points clearance at its shipped starter allowlist, so a fresh install does not need a `CLEARANCE_ALLOW_HOSTS_FILES` export.
17
17
 
18
- Groundcrew ships that starter file at `$(npm root -g)/@clipboard-health/groundcrew/clearance-allow-hosts`, covering model APIs, Linear, Notion, Slack, Datadog, GitHub, npm, and common dev tooling.
18
+ Groundcrew ships that starter file at `$(npm root -g)/@clipboard-health/groundcrew/clearance-allow-hosts`, covering model APIs, Linear, Notion, Slack, Datadog, GitHub, npm, PyPI, and common dev tooling.
19
19
 
20
20
  To add ad hoc hosts for one run, use `CLEARANCE_ALLOW_HOSTS`:
21
21
 
@@ -40,10 +40,11 @@ completed no-PR work. If omitted, groundcrew treats done advancement as
40
40
  unsupported and leaves the task for the source's own integration to close out.
41
41
  `${id}`, `${canonicalId}`, and `${name}` placeholders are shell-quoted before substitution.
42
42
 
43
- Workers receive `GROUNDCREW_TASK_ID` and `GROUNDCREW_COMPLETE` in their launch
44
- environment. The default prompt tells them to run `GROUNDCREW_COMPLETE` only
45
- when the requested work is complete, no PR is needed, and any dirty worktree
46
- state is expected or explicitly allowed.
43
+ Workers receive `GROUNDCREW_TASK_ID` in their launch environment. They receive
44
+ `GROUNDCREW_COMPLETE` only when the source supports done writeback. The default
45
+ prompt tells them to run `GROUNDCREW_COMPLETE` only when it is set, the
46
+ requested work is complete, no PR is needed, and any dirty worktree state is
47
+ expected or explicitly allowed.
47
48
 
48
49
  ```json
49
50
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.37.0",
3
+ "version": "4.38.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",