@clipboard-health/groundcrew 4.29.0 → 4.31.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.
package/README.md CHANGED
@@ -145,7 +145,7 @@ There is no `linear` config block. Groundcrew reads `GROUNDCREW_LINEAR_API_KEY`
145
145
 
146
146
  ## Reference
147
147
 
148
- - [Configuration](./docs/configuration.md): discovery order, repo layout, full config table, prompt customization.
148
+ - [Configuration](./docs/configuration.md): discovery order, repo layout, scripted/sparse-checkout (graft) worktrees, full config table, prompt customization.
149
149
  - [Runners](./docs/runners.md): Safehouse, Docker Sandboxes, and the `none` escape hatch.
150
150
  - [Credentials](./docs/credentials.md): Linear API keys, 1Password, build secrets, and `preLaunch`.
151
151
  - [Prepare worktree hooks](./docs/setup-hooks.md): `.groundcrew/config.json` `hooks.prepareWorktree` for per-repo dependency setup.
@@ -26,6 +26,29 @@ export default {
26
26
  // form to point a repo at a different parent directory:
27
27
  // { name: "other-org/other-repo", projectDirOverride: "~/work" }
28
28
  knownRepositories: ["your-org/your-repo"],
29
+ // A knownRepositories entry can also be an object that provisions the
30
+ // worktree with a custom command instead of `git worktree add` — e.g. a
31
+ // sparse checkout via `graft`. `repo` is a logical name (task token +
32
+ // worktree dir basename); the physical clone is the command's concern.
33
+ // Templates interpolate ${branch} ${dir} ${baseRef} ${repo} ${task}.
34
+ //
35
+ // {
36
+ // name: "billing",
37
+ // provision: {
38
+ // create: "graft new ${branch} billing --from ${baseRef} --dir ${dir}",
39
+ // remove: "graft rm ${branch} -f",
40
+ // },
41
+ // // Optional: run the agent, prepareWorktree hook, and
42
+ // // .groundcrew/config.json lookup in this subdirectory of the
43
+ // // checkout (relative, no ".."). Use it when the checkout is a
44
+ // // monorepo whose project lives in a subdir.
45
+ // workdir: "services/billing",
46
+ // },
47
+ //
48
+ // Set up graft once outside groundcrew:
49
+ // graft repo add ~/dev/owner/monorepo
50
+ // graft alias add billing services/billing libs/common
51
+ // `crew doctor` does not parse or validate these shell templates.
29
52
  },
30
53
  agents: {
31
54
  default: "claude",
@@ -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;AAenE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAuID,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
+ {"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAanE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAuID,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA2Ef;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
@@ -2,14 +2,12 @@ import { fetchResolvedIssue } from "../lib/adapters/linear/fetch.js";
2
2
  import { getLinearClient } from "../lib/adapters/linear/client.js";
3
3
  import { isLinearEnabled } from "../lib/buildSources.js";
4
4
  import { loadConfig } from "../lib/config.js";
5
- import { openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
6
- import { buildLaunchCommand } from "../lib/launchCommand.js";
5
+ import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
7
6
  import { readRunState, recordRunState } from "../lib/runState.js";
8
- import { buildAndStageSrtLaunch } from "../lib/srtLaunch.js";
9
7
  import { removeStagedPrompt, stageBuildSecrets, stagePromptText, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
10
8
  import { errorMessage, log } from "../lib/util.js";
11
9
  import { workspaces } from "../lib/workspaces.js";
12
- import { worktrees } from "../lib/worktrees.js";
10
+ import { resolveLaunchDir, worktrees } from "../lib/worktrees.js";
13
11
  function parseArguments(argv) {
14
12
  const [task, ...extras] = argv;
15
13
  if (task === undefined || task.length === 0 || extras.length > 0 || task.startsWith("-")) {
@@ -124,50 +122,36 @@ export async function resumeWorkspace(config, options) {
124
122
  purpose: "resumes",
125
123
  });
126
124
  await ensureReady();
125
+ const worktreeDir = context.worktree.dir;
126
+ const launchDir = resolveLaunchDir(config, context.repository, worktreeDir);
127
127
  const stagedPrompt = stagePromptText({
128
128
  prefix: "groundcrew-resume",
129
129
  task,
130
130
  text: renderResumePrompt(context),
131
131
  });
132
132
  const secretsFile = stageBuildSecrets(stagedPrompt.directory);
133
- // Resume must stage srt settings exactly like setup, or `buildLaunchCommand`
134
- // throws under the srt runner and a relocating agent (codex) needs its
135
- // config home re-seeded so it authenticates on the resumed launch.
136
- let srtPrepareSettingsFile;
137
- let srtAgentSettingsFile;
133
+ // Resume stages srt settings exactly like setup (a relocating agent such as
134
+ // codex needs its config home re-seeded to authenticate on the resumed launch).
135
+ // Composition runs inside the try so a pre-launch failure still cleans up the
136
+ // staged prompt (and any srt settings) dir.
138
137
  let srtSettingsDir;
139
- let srtAgentConfigDirEnv;
140
- if (runner === "srt") {
141
- const staged = buildAndStageSrtLaunch({
142
- config,
143
- repository: context.repository,
138
+ try {
139
+ let launchCommand;
140
+ ({ launchCommand, srtSettingsDir } = composeAgentLaunch({
141
+ runner,
144
142
  task,
145
- worktreeDir: context.worktree.dir,
146
143
  definition,
147
- });
148
- srtPrepareSettingsFile = staged.prepareFile;
149
- srtAgentSettingsFile = staged.agentFile;
150
- srtSettingsDir = staged.directory;
151
- srtAgentConfigDirEnv = staged.agentConfigDirEnv;
152
- }
153
- const launchCommand = buildLaunchCommand({
154
- definition,
155
- promptFile: stagedPrompt.file,
156
- worktreeDir: context.worktree.dir,
157
- secretsFile,
158
- runner,
159
- sandboxName,
160
- srtPrepareSettingsFile,
161
- srtAgentSettingsFile,
162
- srtSettingsDir,
163
- srtAgentConfigDirEnv,
164
- });
165
- const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launchCommand);
166
- try {
144
+ promptFile: stagedPrompt.file,
145
+ worktreeDir,
146
+ workingDir: launchDir,
147
+ secretsFile,
148
+ sandboxName,
149
+ }));
150
+ const launchCmd = stageWorkspaceLaunchCommand(stagedPrompt.directory, launchCommand);
167
151
  await openAgentWorkspace({
168
152
  config,
169
153
  name: task,
170
- cwd: context.worktree.dir,
154
+ cwd: launchDir,
171
155
  command: launchCmd,
172
156
  agent: context.agent,
173
157
  color: definition.color,
@@ -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;AAmBnE,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,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,CA0If;AA8ID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
1
+ {"version":3,"file":"setupWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/setupWorkspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAsBnE,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,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,CA0Hf;AA8ID,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
@@ -1,17 +1,15 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { loadConfig } from "../lib/config.js";
3
- import { openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
3
+ import { composeAgentLaunch, openAgentWorkspace, prepareAgentLaunch } from "../lib/agentLaunch.js";
4
4
  import { createBoard } from "../lib/board.js";
5
5
  import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
6
- import { buildLaunchCommand } from "../lib/launchCommand.js";
7
6
  import { resolvePrepareWorktreeCommand } from "../lib/repositoryHooks.js";
8
7
  import { recordRunState } from "../lib/runState.js";
9
- import { buildAndStageSrtLaunch } from "../lib/srtLaunch.js";
10
8
  import { stageBuildSecrets, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
11
9
  import { naturalIdFromCanonical } from "../lib/taskSource.js";
12
10
  import { debug, errorMessage, log, okMark } from "../lib/util.js";
13
11
  import { workspaces } from "../lib/workspaces.js";
14
- import { isWorktreeAlreadyExistsError, worktrees } from "../lib/worktrees.js";
12
+ import { isWorktreeAlreadyExistsError, resolveLaunchDir, worktrees, } from "../lib/worktrees.js";
15
13
  function stagePrompt(input) {
16
14
  return stagePromptFromTemplate({
17
15
  config: input.config,
@@ -53,7 +51,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
53
51
  }
54
52
  throw error;
55
53
  }
56
- const { branchName, dir: launchDir } = created;
54
+ const { branchName, dir: worktreeDir } = created;
55
+ const launchDir = resolveLaunchDir(config, repository, worktreeDir);
57
56
  const worktreeName = `${repository}-${task}`;
58
57
  // Anything that fails after the worktree is on disk must roll it back
59
58
  // (the worktree and the just-created branch). `workspaces.open` cleans
@@ -81,35 +80,18 @@ export async function setupWorkspace(config, options, runOptions = {}) {
81
80
  defaultHooks: config.defaults.hooks,
82
81
  });
83
82
  const secretsFile = prepareWorktreeCommand === undefined ? undefined : stageBuildSecrets(promptDir);
84
- let srtPrepareSettingsFile;
85
- let srtAgentSettingsFile;
86
- let srtAgentConfigDirEnv;
87
- if (runner === "srt") {
88
- const staged = buildAndStageSrtLaunch({
89
- config,
90
- repository,
91
- task,
92
- worktreeDir: launchDir,
93
- definition,
94
- });
95
- srtPrepareSettingsFile = staged.prepareFile;
96
- srtAgentSettingsFile = staged.agentFile;
97
- srtSettingsDir = staged.directory;
98
- srtAgentConfigDirEnv = staged.agentConfigDirEnv;
99
- }
100
- const launchCommand = buildLaunchCommand({
83
+ const { launchCommand, srtSettingsDir: stagedSrtSettingsDir } = composeAgentLaunch({
84
+ runner,
85
+ task,
101
86
  definition,
102
87
  promptFile: stagedPrompt.file,
103
- worktreeDir: launchDir,
88
+ worktreeDir,
89
+ workingDir: launchDir,
104
90
  secretsFile,
105
91
  prepareWorktreeCommand,
106
- runner,
107
92
  sandboxName,
108
- srtPrepareSettingsFile,
109
- srtAgentSettingsFile,
110
- srtSettingsDir,
111
- srtAgentConfigDirEnv,
112
93
  });
94
+ srtSettingsDir = stagedSrtSettingsDir;
113
95
  const launchCmd = stageWorkspaceLaunchCommand(promptDir, launchCommand);
114
96
  debug("Opening workspace...");
115
97
  await openAgentWorkspace({
@@ -126,7 +108,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
126
108
  task,
127
109
  repository,
128
110
  agent,
129
- worktreeDir: launchDir,
111
+ worktreeDir,
130
112
  branchName,
131
113
  workspaceName: task,
132
114
  state: "running",
@@ -147,7 +129,7 @@ export async function setupWorkspace(config, options, runOptions = {}) {
147
129
  task,
148
130
  repository,
149
131
  agent,
150
- worktreeDir: launchDir,
132
+ worktreeDir,
151
133
  branchName,
152
134
  workspaceName: task,
153
135
  state: "failed-to-launch",
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { orchestrate, type OrchestratorOptions } from "./commands/orchestrator.t
6
6
  export { resumeWorkspace, type ResumeWorkspaceOptions } from "./commands/resumeWorkspace.ts";
7
7
  export { setupWorkspace, type SetupWorkspaceOptions } from "./commands/setupWorkspace.ts";
8
8
  export { status, type StatusOptions } from "./commands/status.ts";
9
- export type { Config, HookCommands, AgentDefinition, ResolvedConfig, SourceConfig, } from "./lib/config.ts";
9
+ export type { Config, HookCommands, AgentDefinition, KnownRepository, ResolvedConfig, SourceConfig, } from "./lib/config.ts";
10
10
  export { loadConfig } from "./lib/config.ts";
11
11
  export { readRunState, recordRunState, removeRunState, runStateDirectory, runStatePath, updateRunState, type RunLifecycleState, type RunState, } from "./lib/runState.ts";
12
12
  export { fetchBlockersForTask, fetchInProgressIssueCount, fetchRawLinearIssue, fetchResolvedIssue, isIssueInProgress, isIssueTodo, isTerminalStateType, isTerminalStatusForBlocker, isTerminalStatusForIssue, type RawLinearIssue, } from "./lib/adapters/linear/fetch.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EACV,MAAM,EACN,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,cAAc,GACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,KAAK,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kBAAkB,EAClB,KAAK,OAAO,IAAI,gBAAgB,EAChC,KAAK,UAAU,IAAI,mBAAmB,EACtC,KAAK,eAAe,EACpB,KAAK,eAAe,IAAI,wBAAwB,EAChD,KAAK,KAAK,IAAI,cAAc,EAC5B,iBAAiB,IAAI,0BAA0B,EAC/C,KAAK,UAAU,IAAI,mBAAmB,EACtC,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EACV,MAAM,EACN,YAAY,EACZ,eAAe,EACf,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,cAAc,GACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,KAAK,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kBAAkB,EAClB,KAAK,OAAO,IAAI,gBAAgB,EAChC,KAAK,UAAU,IAAI,mBAAmB,EACtC,KAAK,eAAe,EACpB,KAAK,eAAe,IAAI,wBAAwB,EAChD,KAAK,KAAK,IAAI,cAAc,EAC5B,iBAAiB,IAAI,0BAA0B,EAC/C,KAAK,UAAU,IAAI,mBAAmB,EACtC,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shell-adapter `TaskSource` factory. Wires `invokeShellCommand` to the
3
- * four TaskSource operations and applies the ShellIssue Zod schema for
4
- * runtime validation of script stdout.
3
+ * TaskSource operations and applies the ShellIssue Zod schema for runtime
4
+ * validation of script stdout.
5
5
  *
6
6
  * Fallback behavior for omitted commands:
7
7
  * - `verify` absent → no-op (always succeeds).
@@ -13,6 +13,10 @@
13
13
  * - `markInProgress` absent → silent no-op.
14
14
  * - `markInReview` absent → reports unsupported.
15
15
  * - `markDone` absent → reports unsupported.
16
+ * - `createTask` absent → method omitted entirely (source reports it cannot
17
+ * create tasks; the optional method is attached only when configured).
18
+ * - `validate` absent → method omitted entirely (same capability-detection
19
+ * contract as createTask).
16
20
  * - `fetch` is required by the Zod schema.
17
21
  */
18
22
  import type { AdapterContext } from "../../adapterDefinition.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAGL,KAAK,KAAK,IAAI,cAAc,EAG5B,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAkErB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAuB3F;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,cAAc,GACvB,UAAU,CAyHZ"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAIL,KAAK,KAAK,IAAI,cAAc,EAG5B,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAyErB,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAuB3F;AA8BD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,cAAc,GACvB,UAAU,CA6LZ"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shell-adapter `TaskSource` factory. Wires `invokeShellCommand` to the
3
- * four TaskSource operations and applies the ShellIssue Zod schema for
4
- * runtime validation of script stdout.
3
+ * TaskSource operations and applies the ShellIssue Zod schema for runtime
4
+ * validation of script stdout.
5
5
  *
6
6
  * Fallback behavior for omitted commands:
7
7
  * - `verify` absent → no-op (always succeeds).
@@ -13,12 +13,16 @@
13
13
  * - `markInProgress` absent → silent no-op.
14
14
  * - `markInReview` absent → reports unsupported.
15
15
  * - `markDone` absent → reports unsupported.
16
+ * - `createTask` absent → method omitted entirely (source reports it cannot
17
+ * create tasks; the optional method is attached only when configured).
18
+ * - `validate` absent → method omitted entirely (same capability-detection
19
+ * contract as createTask).
16
20
  * - `fetch` is required by the Zod schema.
17
21
  */
18
22
  import { toCanonicalId, } from "../../taskSource.js";
19
- import { writeError } from "../../util.js";
23
+ import { errorMessage, writeError } from "../../util.js";
20
24
  import { invokeShellCommand } from "./invoke.js";
21
- import { shellFetchOutputSchema, shellIssueSchema, } from "./schema.js";
25
+ import { shellFetchOutputSchema, shellIssueSchema, shellValidateOutputSchema, } from "./schema.js";
22
26
  const DEFAULT_TIMEOUTS = {
23
27
  verify: 10_000,
24
28
  listTasks: 30_000,
@@ -26,15 +30,20 @@ const DEFAULT_TIMEOUTS = {
26
30
  markInProgress: 10_000,
27
31
  markInReview: 10_000,
28
32
  markDone: 10_000,
33
+ createTask: 30_000,
34
+ validate: 30_000,
29
35
  };
30
36
  function mergeTimeouts(overrides) {
37
+ const o = overrides ?? {};
31
38
  return {
32
- verify: overrides?.verify ?? DEFAULT_TIMEOUTS.verify,
33
- listTasks: overrides?.listTasks ?? overrides?.fetch ?? DEFAULT_TIMEOUTS.listTasks,
34
- getTask: overrides?.getTask ?? overrides?.resolveOne ?? DEFAULT_TIMEOUTS.getTask,
35
- markInProgress: overrides?.markInProgress ?? DEFAULT_TIMEOUTS.markInProgress,
36
- markInReview: overrides?.markInReview ?? DEFAULT_TIMEOUTS.markInReview,
37
- markDone: overrides?.markDone ?? DEFAULT_TIMEOUTS.markDone,
39
+ verify: o.verify ?? DEFAULT_TIMEOUTS.verify,
40
+ listTasks: o.listTasks ?? o.fetch ?? DEFAULT_TIMEOUTS.listTasks,
41
+ getTask: o.getTask ?? o.resolveOne ?? DEFAULT_TIMEOUTS.getTask,
42
+ markInProgress: o.markInProgress ?? DEFAULT_TIMEOUTS.markInProgress,
43
+ markInReview: o.markInReview ?? DEFAULT_TIMEOUTS.markInReview,
44
+ markDone: o.markDone ?? DEFAULT_TIMEOUTS.markDone,
45
+ createTask: o.createTask ?? DEFAULT_TIMEOUTS.createTask,
46
+ validate: o.validate ?? DEFAULT_TIMEOUTS.validate,
38
47
  };
39
48
  }
40
49
  function warnDuplicate(sourceName, preferred, legacy) {
@@ -85,6 +94,33 @@ export function toCanonicalIssue(shellIssue, sourceName) {
85
94
  sourceRef: shellIssue.sourceRef,
86
95
  };
87
96
  }
97
+ /**
98
+ * Flatten a CreateTaskInput into the `${...}` substitution map the createTask
99
+ * script receives. Every key is always present — absent optionals become an
100
+ * empty string — so no placeholder is ever left literally in the command.
101
+ * List fields are comma-joined into a single value.
102
+ */
103
+ function createTaskSubstitutions(input) {
104
+ return {
105
+ title: input.title,
106
+ agent: input.agent,
107
+ // Exposed under both `repo` (short form) and `repository` (matches the
108
+ // CreateTaskInput field name) so either placeholder resolves.
109
+ repo: input.repository ?? "",
110
+ repository: input.repository ?? "",
111
+ team: input.team ?? "",
112
+ id: input.id ?? "",
113
+ priority: input.priority ?? "",
114
+ due: input.due ?? "",
115
+ recurrence: input.recurrence ?? "",
116
+ promptFile: input.promptFile ?? "",
117
+ description: input.description ?? "",
118
+ edit: input.edit ? "true" : "",
119
+ projects: input.projects.join(","),
120
+ contexts: input.contexts.join(","),
121
+ dependencies: input.dependencies.join(","),
122
+ };
123
+ }
88
124
  export function createShellTaskSource(config, _context) {
89
125
  const sourceName = config.name;
90
126
  const timeouts = mergeTimeouts(config.timeouts);
@@ -149,7 +185,7 @@ export function createShellTaskSource(config, _context) {
149
185
  sourceName,
150
186
  });
151
187
  }
152
- return {
188
+ const source = {
153
189
  name: sourceName,
154
190
  async verify() {
155
191
  const verifyCommand = config.commands.verify;
@@ -198,4 +234,66 @@ export function createShellTaskSource(config, _context) {
198
234
  return { outcome: "applied" };
199
235
  },
200
236
  };
237
+ // createTask / validate are attached only when their command is configured.
238
+ // Capability detection keys off `source.createTask === undefined` /
239
+ // `source.validate === undefined`, so a slot left unset must leave the
240
+ // method genuinely absent rather than present-but-failing.
241
+ const createTaskCommand = config.commands.createTask;
242
+ if (createTaskCommand !== undefined) {
243
+ source.createTask = async (input) => {
244
+ const { stdout, exitCode } = await invokeShellCommand({
245
+ command: createTaskCommand,
246
+ timeoutMs: timeouts.createTask,
247
+ cwd: config.cwd,
248
+ env: config.env,
249
+ substitutions: createTaskSubstitutions(input),
250
+ sourceName,
251
+ });
252
+ // invokeShellCommand resolves (does not throw) on exit 3 — its "not
253
+ // found" sentinel for lookups. Creation has no not-found concept, so any
254
+ // nonzero exit is a failure: surface it rather than parse partial output.
255
+ if (exitCode === 3) {
256
+ throw new Error(`shell source "${sourceName}" createTask command exited 3 (not-found); task creation cannot signal not-found`);
257
+ }
258
+ const trimmed = stdout.trim();
259
+ if (trimmed.length === 0) {
260
+ throw new Error(`shell source "${sourceName}" createTask command produced no output (expected one ShellIssue JSON)`);
261
+ }
262
+ const parsed = shellIssueSchema.parse(JSON.parse(trimmed));
263
+ return toCanonicalIssue(parsed, sourceName);
264
+ };
265
+ }
266
+ const validateCommand = config.commands.validate;
267
+ if (validateCommand !== undefined) {
268
+ source.validate = async () => {
269
+ try {
270
+ const { stdout, exitCode } = await invokeShellCommand({
271
+ command: validateCommand,
272
+ timeoutMs: timeouts.validate,
273
+ cwd: config.cwd,
274
+ env: config.env,
275
+ sourceName,
276
+ });
277
+ // exit 3 is invoke's lookup "not found" sentinel; for validation it is
278
+ // not a meaningful success, so surface it as a failure (and validate
279
+ // never throws — return the failure as an error string).
280
+ if (exitCode === 3) {
281
+ return [
282
+ `shell source "${sourceName}" validate command exited 3 (not-found); treating as a validation failure`,
283
+ ];
284
+ }
285
+ const trimmed = stdout.trim();
286
+ if (trimmed.length === 0) {
287
+ return [];
288
+ }
289
+ return shellValidateOutputSchema.parse(JSON.parse(trimmed));
290
+ }
291
+ catch (error) {
292
+ // Contract: validate() never throws — fold a nonzero exit, timeout,
293
+ // malformed JSON, or wrong-shape payload into a single error string.
294
+ return [`shell source "${sourceName}" validate command failed: ${errorMessage(error)}`];
295
+ }
296
+ };
297
+ }
298
+ return source;
201
299
  }
@@ -80,6 +80,12 @@ export declare const shellFetchOutputSchema: z.ZodArray<z.ZodObject<{
80
80
  url: z.ZodOptional<z.ZodURL>;
81
81
  sourceRef: z.ZodUnknown;
82
82
  }, z.core.$strip>>;
83
+ /**
84
+ * Shape a `commands.validate` script must emit on stdout: a JSON array of
85
+ * human-readable error strings. An empty array (or empty stdout, handled by
86
+ * the factory) means "no problems found".
87
+ */
88
+ export declare const shellValidateOutputSchema: z.ZodArray<z.ZodString>;
83
89
  export declare const shellAdapterConfigSchema: z.ZodObject<{
84
90
  kind: z.ZodLiteral<"shell">;
85
91
  name: z.ZodString;
@@ -92,6 +98,8 @@ export declare const shellAdapterConfigSchema: z.ZodObject<{
92
98
  markInProgress: z.ZodOptional<z.ZodString>;
93
99
  markInReview: z.ZodOptional<z.ZodString>;
94
100
  markDone: z.ZodOptional<z.ZodString>;
101
+ createTask: z.ZodOptional<z.ZodString>;
102
+ validate: z.ZodOptional<z.ZodString>;
95
103
  }, z.core.$strip>;
96
104
  cwd: z.ZodOptional<z.ZodString>;
97
105
  timeouts: z.ZodOptional<z.ZodObject<{
@@ -103,6 +111,8 @@ export declare const shellAdapterConfigSchema: z.ZodObject<{
103
111
  markInProgress: z.ZodOptional<z.ZodNumber>;
104
112
  markInReview: z.ZodOptional<z.ZodNumber>;
105
113
  markDone: z.ZodOptional<z.ZodNumber>;
114
+ createTask: z.ZodOptional<z.ZodNumber>;
115
+ validate: z.ZodOptional<z.ZodNumber>;
106
116
  }, z.core.$strip>>;
107
117
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
108
118
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;iBAiDnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,yBAAsB,CAAC;AAE7D,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkEnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
@@ -36,6 +36,12 @@ export const shellIssueSchema = z.object({
36
36
  sourceRef: z.unknown(),
37
37
  });
38
38
  export const shellFetchOutputSchema = z.array(shellIssueSchema);
39
+ /**
40
+ * Shape a `commands.validate` script must emit on stdout: a JSON array of
41
+ * human-readable error strings. An empty array (or empty stdout, handled by
42
+ * the factory) means "no problems found".
43
+ */
44
+ export const shellValidateOutputSchema = z.array(z.string());
39
45
  export const shellAdapterConfigSchema = z.object({
40
46
  kind: z.literal("shell"),
41
47
  name: z
@@ -55,6 +61,19 @@ export const shellAdapterConfigSchema = z.object({
55
61
  markInProgress: z.string().optional(),
56
62
  markInReview: z.string().optional(),
57
63
  markDone: z.string().optional(),
64
+ /**
65
+ * Create a task from a `CreateTaskInput`. Receives the input fields as
66
+ * shell-quoted `${...}` placeholders (see factory) and must print one
67
+ * ShellIssue JSON on stdout. Omitting it leaves the source unable to
68
+ * create tasks.
69
+ */
70
+ createTask: z.string().optional(),
71
+ /**
72
+ * Validate task content. Must print a JSON array of error strings on
73
+ * stdout (empty array / empty stdout = no problems). Omitting it leaves
74
+ * the source unable to validate.
75
+ */
76
+ validate: z.string().optional(),
58
77
  })
59
78
  .superRefine((commands, ctx) => {
60
79
  if (commands.listTasks === undefined && commands.fetch === undefined) {
@@ -82,6 +101,10 @@ export const shellAdapterConfigSchema = z.object({
82
101
  markInProgress: z.number().int().positive().optional(),
83
102
  markInReview: z.number().int().positive().optional(),
84
103
  markDone: z.number().int().positive().optional(),
104
+ /** Timeout for the createTask command. */
105
+ createTask: z.number().int().positive().optional(),
106
+ /** Timeout for the validate command. */
107
+ validate: z.number().int().positive().optional(),
85
108
  })
86
109
  .optional(),
87
110
  env: z.record(z.string(), z.string()).optional(),
@@ -1,4 +1,25 @@
1
1
  import { type LocalRunner, type AgentDefinition, type ResolvedConfig } from "./config.ts";
2
+ /**
3
+ * Stage any srt settings and build the workspace launch command — the assembly
4
+ * shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
5
+ * (resumes). `worktreeDir` is the checkout root (srt grants + `{{worktree}}`);
6
+ * `workingDir` is the agent cwd (the worktree root, or its `workdir` subproject).
7
+ * Returns `srtSettingsDir` so callers can tear it down on a pre-launch failure.
8
+ */
9
+ export declare function composeAgentLaunch(input: {
10
+ runner: LocalRunner;
11
+ task: string;
12
+ definition: AgentDefinition;
13
+ promptFile: string;
14
+ worktreeDir: string;
15
+ workingDir: string;
16
+ secretsFile?: string | undefined;
17
+ prepareWorktreeCommand?: string | undefined;
18
+ sandboxName?: string | undefined;
19
+ }): {
20
+ launchCommand: string;
21
+ srtSettingsDir: string | undefined;
22
+ };
2
23
  interface PreparedAgentLaunch {
3
24
  runner: LocalRunner;
4
25
  sandboxName: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"agentLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/agentLaunch.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAOrB,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;AAqBD,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":"AAEA,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;AAqBD,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,10 +1,43 @@
1
1
  import { ensureClearance } from "@clipboard-health/clearance";
2
2
  import { hasPreLaunchEnv, } from "./config.js";
3
3
  import { detectHostCapabilities } from "./host.js";
4
+ import { buildLaunchCommand } from "./launchCommand.js";
4
5
  import { assertLocalRunnerRequirements, resolveLocalRunner } from "./localRunner.js";
5
6
  import { sandboxNameFor } from "./sandboxName.js";
7
+ import { buildAndStageSrtLaunch } from "./srtLaunch.js";
6
8
  import { debug, sleep } from "./util.js";
7
9
  import { workspaces } from "./workspaces.js";
10
+ /**
11
+ * Stage any srt settings and build the workspace launch command — the assembly
12
+ * shared verbatim by `setupWorkspace` (fresh runs) and `resumeWorkspace`
13
+ * (resumes). `worktreeDir` is the checkout root (srt grants + `{{worktree}}`);
14
+ * `workingDir` is the agent cwd (the worktree root, or its `workdir` subproject).
15
+ * Returns `srtSettingsDir` so callers can tear it down on a pre-launch failure.
16
+ */
17
+ export function composeAgentLaunch(input) {
18
+ const staged = input.runner === "srt"
19
+ ? buildAndStageSrtLaunch({
20
+ task: input.task,
21
+ worktreeDir: input.worktreeDir,
22
+ definition: input.definition,
23
+ })
24
+ : undefined;
25
+ const launchCommand = buildLaunchCommand({
26
+ definition: input.definition,
27
+ promptFile: input.promptFile,
28
+ worktreeDir: input.worktreeDir,
29
+ workingDir: input.workingDir,
30
+ secretsFile: input.secretsFile,
31
+ prepareWorktreeCommand: input.prepareWorktreeCommand,
32
+ runner: input.runner,
33
+ sandboxName: input.sandboxName,
34
+ srtPrepareSettingsFile: staged?.prepareFile,
35
+ srtAgentSettingsFile: staged?.agentFile,
36
+ srtSettingsDir: staged?.directory,
37
+ srtAgentConfigDirEnv: staged?.agentConfigDirEnv,
38
+ });
39
+ return { launchCommand, srtSettingsDir: staged?.directory };
40
+ }
8
41
  export async function prepareAgentLaunch(input) {
9
42
  const host = await detectHostCapabilities(input.signal);
10
43
  const runner = resolveLocalRunner(input.config.local.runner, host);