@clipboard-health/groundcrew 4.52.12 → 4.53.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/crew.config.example.ts +9 -0
- package/dist/commands/cleanupWorkspace.js +1 -1
- package/dist/commands/dispatcher.d.ts +3 -0
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +15 -4
- package/dist/commands/eligibility.d.ts +59 -8
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +193 -8
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +3 -0
- package/dist/commands/reviewer.d.ts.map +1 -1
- package/dist/commands/reviewer.js +14 -0
- package/dist/commands/setupWorkspace.d.ts +4 -0
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +60 -18
- package/dist/commands/stackRetarget.d.ts +64 -0
- package/dist/commands/stackRetarget.d.ts.map +1 -0
- package/dist/commands/stackRetarget.js +627 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +10 -0
- package/dist/commands/statusCollect.d.ts.map +1 -1
- package/dist/commands/statusCollect.js +3 -0
- package/dist/commands/teardownReporter.js +1 -1
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +2 -0
- package/dist/lib/adapters/linear/fetch.d.ts +4 -1
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/linear/fetch.js +6 -2
- package/dist/lib/adapters/linear/parsing.d.ts +6 -1
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.js +5 -1
- package/dist/lib/config.d.ts +12 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -2
- package/dist/lib/githubStacks.d.ts +51 -0
- package/dist/lib/githubStacks.d.ts.map +1 -0
- package/dist/lib/githubStacks.js +116 -0
- package/dist/lib/launchCommand.d.ts +6 -0
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +4 -1
- package/dist/lib/pullRequests.d.ts +16 -0
- package/dist/lib/pullRequests.d.ts.map +1 -1
- package/dist/lib/pullRequests.js +31 -2
- package/dist/lib/runState.d.ts +53 -0
- package/dist/lib/runState.d.ts.map +1 -1
- package/dist/lib/runState.js +113 -24
- package/dist/lib/runStateCleanup.d.ts +8 -2
- package/dist/lib/runStateCleanup.d.ts.map +1 -1
- package/dist/lib/runStateCleanup.js +18 -2
- package/dist/lib/statusSnapshot.d.ts +6 -0
- package/dist/lib/statusSnapshot.d.ts.map +1 -1
- package/dist/lib/taskSource.d.ts +4 -0
- package/dist/lib/taskSource.d.ts.map +1 -1
- package/dist/lib/util.d.ts +6 -0
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +18 -0
- package/dist/lib/worktreeRunState.d.ts +19 -0
- package/dist/lib/worktreeRunState.d.ts.map +1 -1
- package/dist/lib/worktreeRunState.js +20 -1
- package/dist/lib/worktrees.d.ts +44 -0
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +95 -18
- package/docs/configuration.md +1 -0
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { inferAgentCommandName, workerEnvironmentForTask } from "../lib/launchCo
|
|
|
4
4
|
import { createBoard } from "../lib/board.js";
|
|
5
5
|
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
6
6
|
import { resolveRepositoryPreparationCommands } from "../lib/repositoryHooks.js";
|
|
7
|
-
import { recordRunState } from "../lib/runState.js";
|
|
7
|
+
import { readRunState, recordRunState } from "../lib/runState.js";
|
|
8
8
|
import { seedLaunchWorkspaceTrust } from "../lib/seedLaunchWorkspaceTrust.js";
|
|
9
9
|
import { sourceSupportsMarkDone } from "../lib/sourceCapabilities.js";
|
|
10
10
|
import { removeStagedPrompt, stageBuildSecretsForLaunch, stagePromptFromTemplate, stageWorkspaceLaunchCommand, } from "../lib/stagedLaunch.js";
|
|
@@ -42,25 +42,16 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
42
42
|
purpose: "runs",
|
|
43
43
|
...(signal === undefined ? {} : { signal }),
|
|
44
44
|
});
|
|
45
|
+
// Captured before preflightProvisioningGate stamps its own "provisioning"
|
|
46
|
+
// row (which carries options.baseBranch forward) — otherwise the reattach
|
|
47
|
+
// guard inside worktrees.create() would compare the just-requested
|
|
48
|
+
// baseBranch against itself and never fire. See
|
|
49
|
+
// StackedBaseBranchMismatchError's doc comment in worktrees.ts.
|
|
50
|
+
const recordedBaseBranch = readRunState(config, task)?.baseBranch;
|
|
45
51
|
await preflightProvisioningGate({ config, options, signal });
|
|
46
|
-
const
|
|
47
|
-
const createdPromise = signal === undefined ? worktrees.create(config, spec) : worktrees.create(config, spec, signal);
|
|
52
|
+
const createdPromise = beginWorktreeCreate({ config, options, signal, recordedBaseBranch });
|
|
48
53
|
const readinessPromise = startLaunchReadiness(ensureReady);
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
created = await createdPromise;
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
// Roll the pre-flight `provisioning` row forward; the outer catch only
|
|
55
|
-
// fires post-create and the dispatcher just logs and moves on.
|
|
56
|
-
recordFailedToLaunch({
|
|
57
|
-
config,
|
|
58
|
-
options,
|
|
59
|
-
paths: worktrees.predictedEntry(config, repository, task),
|
|
60
|
-
error,
|
|
61
|
-
});
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
54
|
+
const created = await createdPromise;
|
|
64
55
|
const { branchName, dir: worktreeDir } = created;
|
|
65
56
|
const launchDir = resolveLaunchDir(config, repository, worktreeDir);
|
|
66
57
|
const worktreeName = `${repository}-${task}`;
|
|
@@ -130,6 +121,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
130
121
|
workerEnvironment: workerEnvironmentForTask({
|
|
131
122
|
taskId: completionTaskId,
|
|
132
123
|
markDoneSupported: completionMarkDoneSupported,
|
|
124
|
+
remote: config.git.remote,
|
|
125
|
+
...(options.baseBranch === undefined ? {} : { baseBranch: options.baseBranch }),
|
|
133
126
|
}),
|
|
134
127
|
taskSourceWritePaths,
|
|
135
128
|
safehouse: config.local.safehouse,
|
|
@@ -161,6 +154,8 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
161
154
|
title: taskDetails.title,
|
|
162
155
|
completionTaskId,
|
|
163
156
|
...(taskDetails.url === undefined ? {} : { url: taskDetails.url }),
|
|
157
|
+
...(options.baseBranch === undefined ? {} : { baseBranch: options.baseBranch }),
|
|
158
|
+
...(options.parentTask === undefined ? {} : { parentTask: options.parentTask }),
|
|
164
159
|
});
|
|
165
160
|
log(`${okMark()} "${task}" launched (${agent}) worktree ${worktreeName}`);
|
|
166
161
|
debug(` Worktree: ${launchDir}`);
|
|
@@ -176,6 +171,34 @@ export async function setupWorkspace(config, options, runOptions = {}) {
|
|
|
176
171
|
throw error;
|
|
177
172
|
}
|
|
178
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Starts the task's worktree create call, synchronously, so it kicks off
|
|
176
|
+
* before the concurrent launch-readiness check (some tests assert
|
|
177
|
+
* `worktrees.create` is invoked ahead of the safehouse clearance probe).
|
|
178
|
+
* Rolls the pre-flight "provisioning" run state row forward into
|
|
179
|
+
* "failed-to-launch" on failure — the outer catch in `setupWorkspace` only
|
|
180
|
+
* fires post-create, and the dispatcher just logs and moves on otherwise.
|
|
181
|
+
*/
|
|
182
|
+
async function beginWorktreeCreate(arguments_) {
|
|
183
|
+
const { config, options, signal, recordedBaseBranch } = arguments_;
|
|
184
|
+
const { task, repository } = options;
|
|
185
|
+
const spec = {
|
|
186
|
+
repository,
|
|
187
|
+
task,
|
|
188
|
+
...(options.baseBranch === undefined ? {} : { baseBranch: options.baseBranch }),
|
|
189
|
+
...(recordedBaseBranch === undefined ? {} : { recordedBaseBranch }),
|
|
190
|
+
};
|
|
191
|
+
const createdPromise = signal === undefined ? worktrees.create(config, spec) : worktrees.create(config, spec, signal);
|
|
192
|
+
return await createdPromise.catch((error) => {
|
|
193
|
+
recordFailedToLaunch({
|
|
194
|
+
config,
|
|
195
|
+
options,
|
|
196
|
+
paths: worktrees.predictedEntry(config, repository, task),
|
|
197
|
+
error,
|
|
198
|
+
});
|
|
199
|
+
throw error;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
179
202
|
function resolveTaskPreparationCommands(arguments_) {
|
|
180
203
|
if (!arguments_.shouldPrepareWorktree) {
|
|
181
204
|
return {
|
|
@@ -220,6 +243,8 @@ async function preflightProvisioningGate(arguments_) {
|
|
|
220
243
|
title: options.details.title,
|
|
221
244
|
completionTaskId: options.completionTaskId ?? task,
|
|
222
245
|
...(options.details.url === undefined ? {} : { url: options.details.url }),
|
|
246
|
+
...(options.baseBranch === undefined ? {} : { baseBranch: options.baseBranch }),
|
|
247
|
+
...(options.parentTask === undefined ? {} : { parentTask: options.parentTask }),
|
|
223
248
|
});
|
|
224
249
|
}
|
|
225
250
|
function recordFailedToLaunch(arguments_) {
|
|
@@ -238,6 +263,12 @@ function recordFailedToLaunch(arguments_) {
|
|
|
238
263
|
title: options.details.title,
|
|
239
264
|
completionTaskId: options.completionTaskId ?? task,
|
|
240
265
|
...(options.details.url === undefined ? {} : { url: options.details.url }),
|
|
266
|
+
// A failed dispatch never got a worktree, so any baseBranch/parentTask
|
|
267
|
+
// recorded moments earlier by preflightProvisioningGate's "provisioning"
|
|
268
|
+
// row must not survive into this terminal state — otherwise the parent's
|
|
269
|
+
// branch would look permanently referenced (worktreeRunState.ts's
|
|
270
|
+
// isReferencedAsStackParent) even though no child ever came to exist.
|
|
271
|
+
clearStackingFields: true,
|
|
241
272
|
});
|
|
242
273
|
}
|
|
243
274
|
async function startLaunchReadiness(ensureReady) {
|
|
@@ -284,6 +315,12 @@ function renderWorkspaceContinuationInstruction(accessHint) {
|
|
|
284
315
|
}
|
|
285
316
|
return `Include this workspace continuation note in the output: Workspace attach: \`${accessHint.command}\`.`;
|
|
286
317
|
}
|
|
318
|
+
const STACKING_RUN_STATE_FIELDS = [
|
|
319
|
+
"baseBranch",
|
|
320
|
+
"parentTask",
|
|
321
|
+
"needsRebase",
|
|
322
|
+
"stackRegistered",
|
|
323
|
+
];
|
|
287
324
|
function recordRunStateBestEffort(arguments_) {
|
|
288
325
|
try {
|
|
289
326
|
recordRunState({
|
|
@@ -300,6 +337,11 @@ function recordRunStateBestEffort(arguments_) {
|
|
|
300
337
|
completionTaskId: arguments_.completionTaskId,
|
|
301
338
|
...(arguments_.detail === undefined ? {} : { detail: arguments_.detail }),
|
|
302
339
|
...(arguments_.url === undefined ? {} : { url: arguments_.url }),
|
|
340
|
+
...(arguments_.baseBranch === undefined ? {} : { baseBranch: arguments_.baseBranch }),
|
|
341
|
+
...(arguments_.parentTask === undefined ? {} : { parentTask: arguments_.parentTask }),
|
|
342
|
+
...(arguments_.clearStackingFields === true
|
|
343
|
+
? { clearFields: STACKING_RUN_STATE_FIELDS }
|
|
344
|
+
: {}),
|
|
303
345
|
},
|
|
304
346
|
});
|
|
305
347
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-tick pass that keeps a stacked child's PR base and branch in sync with
|
|
3
|
+
* its parent, per the stacked-PRs design (spec section 5). Runs from within
|
|
4
|
+
* the reviewer, once per task whose run state carries `baseBranch`:
|
|
5
|
+
*
|
|
6
|
+
* - Parent still open, child PR based on something other than the parent →
|
|
7
|
+
* `gh pr edit --base <parent>`.
|
|
8
|
+
* - Parent merged (a PR on the parent's branch has state `merged` — the
|
|
9
|
+
* parent issue's canonical `done` status is never on its own proof of a
|
|
10
|
+
* merge: a ticket closed as duplicate, or completed via `crew task done`
|
|
11
|
+
* with an unmerged/closed PR, is `done` but not merged) and the child PR
|
|
12
|
+
* is still based on the parent (or GitHub already moved it to the default
|
|
13
|
+
* branch itself) → `gh pr edit --base <default>`, then rebase onto the
|
|
14
|
+
* default branch and force-push when the worktree is clean, or flag
|
|
15
|
+
* `needsRebase` when it isn't. A successful rebase also tries to reclaim
|
|
16
|
+
* the parent's now-unreferenced branch (spec section 6) — `worktrees.teardown`
|
|
17
|
+
* only checks this at the moment the parent's own worktree is torn down,
|
|
18
|
+
* which is usually before this rebase runs.
|
|
19
|
+
* - Parent not merged, and the parent issue is done (or its worktree is
|
|
20
|
+
* gone) — the parent will never merge, so retargeting onto or rebasing
|
|
21
|
+
* onto a branch that is about to vanish would be wrong. Leave the child
|
|
22
|
+
* untouched (`parent_done_unmerged`).
|
|
23
|
+
*
|
|
24
|
+
* A task with no `baseBranch` in run state, or no open PR yet, is left
|
|
25
|
+
* alone — there is nothing to correct. Every git/gh failure is caught,
|
|
26
|
+
* logged, and left for the next tick; one task's failure never blocks
|
|
27
|
+
* another's.
|
|
28
|
+
*/
|
|
29
|
+
import type { ResolvedConfig } from "../lib/config.ts";
|
|
30
|
+
import { type StacksClient } from "../lib/githubStacks.ts";
|
|
31
|
+
import { type CountMergeCommits } from "../lib/pullRequests.ts";
|
|
32
|
+
import { type BoardState } from "../lib/taskSource.ts";
|
|
33
|
+
import { type WorktreeEntry } from "../lib/worktrees.ts";
|
|
34
|
+
import type { FindPullRequests } from "./reviewer.ts";
|
|
35
|
+
export type RunGitCommand = (arguments_: {
|
|
36
|
+
cwd: string;
|
|
37
|
+
args: readonly string[];
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}) => Promise<string>;
|
|
40
|
+
export type RunGhCommand = (arguments_: {
|
|
41
|
+
cwd: string;
|
|
42
|
+
args: readonly string[];
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}) => Promise<string>;
|
|
45
|
+
export interface StackRetargetDeps {
|
|
46
|
+
findPullRequests: FindPullRequests;
|
|
47
|
+
runGit?: RunGitCommand;
|
|
48
|
+
runGh?: RunGhCommand;
|
|
49
|
+
stacks?: StacksClient;
|
|
50
|
+
countMergeCommits?: CountMergeCommits;
|
|
51
|
+
}
|
|
52
|
+
interface StackRetargetArguments {
|
|
53
|
+
config: ResolvedConfig;
|
|
54
|
+
state: BoardState;
|
|
55
|
+
worktreeEntries: readonly WorktreeEntry[];
|
|
56
|
+
dryRun: boolean;
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
export interface StackRetarget {
|
|
60
|
+
runOnce: (arguments_: StackRetargetArguments) => Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
export declare function createStackRetarget(deps: StackRetargetDeps): StackRetarget;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=stackRetarget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stackRetarget.d.ts","sourceRoot":"","sources":["../../src/commands/stackRetarget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA8CtD,MAAM,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEtB,MAAM,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAQtB,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAgyBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAuD1E"}
|