@clipboard-health/groundcrew 1.12.1 → 1.12.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.
- package/README.md +1 -1
- package/dist/commands/remoteSetup.d.ts +1 -0
- package/dist/commands/remoteSetup.d.ts.map +1 -1
- package/dist/commands/remoteSetup.js +45 -16
- package/dist/commands/setupRepos.d.ts.map +1 -1
- package/dist/commands/setupRepos.js +3 -2
- package/dist/lib/spriteRemoteRunnerProvider.d.ts +1 -0
- package/dist/lib/spriteRemoteRunnerProvider.d.ts.map +1 -1
- package/dist/lib/spriteRemoteRunnerProvider.js +17 -6
- package/dist/lib/worktrees.d.ts.map +1 -1
- package/dist/lib/worktrees.js +65 -11
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ This installs the `crew` binary. `@clipboard-health/clearance` is pulled in tran
|
|
|
102
102
|
|
|
103
103
|
Known MCP aliases are `linear`, `slack`, and `notion`. For another HTTP MCP server, pass `--mcp name=https://example.com/mcp`. The command creates the remote runner if needed, prepares `~/dev`, configures Git, runs selected auth flows, adds selected MCP servers to Claude Code, and then opens Claude so you can run `/mcp` and authenticate only those selected servers. With the Sprite provider, `--copy-local-codex-auth` copies `${CODEX_HOME:-$HOME/.codex}/auth.json` into `/home/sprite/.codex/auth.json` and then verifies `codex login status`; it never prints the file contents. `--datadog` installs a pinned `pup` release in the remote runner, verifies its checksum, installs the `dd-pup` guidance for Claude and Codex, verifies `pup auth status`, and when auth is missing temporarily runs `sprite proxy` for the OAuth callback while `pup auth login --read-only` runs in the remote runner. Open the Datadog URL printed by `pup` if your terminal does not open it automatically. Use `--skip-mcp-auth` when you only want to add MCP definitions, and run the `/mcp` step later.
|
|
104
104
|
|
|
105
|
-
Repo setup is separate from runner setup and should run after the ticket branch exists, immediately before launching an agent. It clones/fetches the repo in the remote runner
|
|
105
|
+
Repo setup is separate from runner setup and should run after the ticket branch exists, immediately before launching an agent. It clones/fetches the repo in the remote runner using `git.remote`, checks out the requested branch (creating it from the base branch when it does not exist on that remote), forwards only build-time secrets for the dependency install, removes the temporary secret file, clears those env vars, and then exits. It uses groundcrew's remote setup command.
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
op run --env-file "${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/op.env" -- \
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteSetup.d.ts","sourceRoot":"","sources":["../../src/commands/remoteSetup.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,OAAO,CAAC;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4BAA4B,EAAE,OAAO,CAAC;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"remoteSetup.d.ts","sourceRoot":"","sources":["../../src/commands/remoteSetup.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB,EAAE,OAAO,CAAC;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4BAA4B,EAAE,OAAO,CAAC;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAq2BD,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkC9F;AAUD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AAYD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgClF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B7D"}
|
|
@@ -14,7 +14,7 @@ const KNOWN_MCP_SERVER_URLS = {
|
|
|
14
14
|
const DEFAULT_CHECKPOINT_COMMENT = "groundcrew remote runner baseline: selected agent auth, git identity, and MCP config";
|
|
15
15
|
const CLAUDE_SUBSCRIPTION_LOGIN_FLAG = ["--claude", "ai"].join("");
|
|
16
16
|
const DEFAULT_REPOSITORY_OWNER = "ClipboardHealth";
|
|
17
|
-
const
|
|
17
|
+
const DEFAULT_GIT_REMOTE = "origin";
|
|
18
18
|
const DATADOG_PUP_VERSION = "0.63.0";
|
|
19
19
|
const DATADOG_OAUTH_CALLBACK_PORT = 8000;
|
|
20
20
|
const DATADOG_AUTH_STATUS_RETRY_ATTEMPTS = 5;
|
|
@@ -52,7 +52,7 @@ function usage() {
|
|
|
52
52
|
"",
|
|
53
53
|
"Bootstrap options:",
|
|
54
54
|
" --branch <branch> Checkout/create a ticket branch before installing deps",
|
|
55
|
-
" --base <branch> Base branch used when creating a missing branch (default:
|
|
55
|
+
" --base <branch> Base branch used when creating a missing branch (default: git.defaultBranch)",
|
|
56
56
|
" --owner <owner> GitHub owner for bare repo names (default: ClipboardHealth)",
|
|
57
57
|
" --secret <env-name> Forward one required build secret; repeat for multiple",
|
|
58
58
|
" --no-secrets Do not forward NPM_TOKEN/BUF_TOKEN even if present",
|
|
@@ -233,7 +233,7 @@ function parseBootstrapArguments(argv) {
|
|
|
233
233
|
}
|
|
234
234
|
validateRepository(repository);
|
|
235
235
|
let owner = DEFAULT_REPOSITORY_OWNER;
|
|
236
|
-
let baseBranch
|
|
236
|
+
let baseBranch;
|
|
237
237
|
let branchName;
|
|
238
238
|
let shouldUseSecrets = true;
|
|
239
239
|
let shouldRequireSelectedSecrets = false;
|
|
@@ -276,10 +276,10 @@ function parseBootstrapArguments(argv) {
|
|
|
276
276
|
runnerName,
|
|
277
277
|
repository,
|
|
278
278
|
owner,
|
|
279
|
-
baseBranch,
|
|
280
279
|
secretNames: selectedSecretNames.length > 0 ? selectedSecretNames : [...BUILD_SECRET_NAMES],
|
|
281
280
|
shouldRequireSelectedSecrets,
|
|
282
281
|
shouldUseSecrets,
|
|
282
|
+
...(baseBranch === undefined ? {} : { baseBranch }),
|
|
283
283
|
...(branchName === undefined ? {} : { branchName }),
|
|
284
284
|
};
|
|
285
285
|
}
|
|
@@ -631,37 +631,61 @@ function repositoryDirectoryName(repository) {
|
|
|
631
631
|
: repository;
|
|
632
632
|
return name.endsWith(".git") ? name.slice(0, -4) : name;
|
|
633
633
|
}
|
|
634
|
+
function validateRemoteBootstrapOptions(options) {
|
|
635
|
+
validateRepository(options.repository);
|
|
636
|
+
validateRepositoryOwner(options.owner);
|
|
637
|
+
validateGitRef(options.baseBranch, "base branch");
|
|
638
|
+
validateGitRef(options.gitRemote, "git remote");
|
|
639
|
+
if (options.branchName !== undefined) {
|
|
640
|
+
validateGitRef(options.branchName, "branch");
|
|
641
|
+
}
|
|
642
|
+
for (const name of options.secretNames) {
|
|
643
|
+
validateSecretName(name);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
634
646
|
function remoteBootstrapCommand(options) {
|
|
635
647
|
const slug = repositorySlug(options);
|
|
636
648
|
const directoryName = repositoryDirectoryName(options.repository);
|
|
649
|
+
const baseRef = `${options.gitRemote}/${options.baseBranch}`;
|
|
637
650
|
const unsetSecretsLine = options.secretNames.length === 0 ? ":" : `unset ${options.secretNames.join(" ")}`;
|
|
638
651
|
const checkoutLines = options.branchName === undefined
|
|
639
|
-
? [
|
|
640
|
-
`git checkout -B ${shellSingleQuote(options.baseBranch)} ${shellSingleQuote(`origin/${options.baseBranch}`)}`,
|
|
641
|
-
]
|
|
652
|
+
? [`git checkout -B ${shellSingleQuote(options.baseBranch)} ${shellSingleQuote(baseRef)}`]
|
|
642
653
|
: [
|
|
643
|
-
`if git show-ref --verify --quiet ${shellSingleQuote(`refs/remotes
|
|
644
|
-
` git checkout -B ${shellSingleQuote(options.branchName)} ${shellSingleQuote(
|
|
654
|
+
`if git show-ref --verify --quiet ${shellSingleQuote(`refs/remotes/${options.gitRemote}/${options.branchName}`)}; then`,
|
|
655
|
+
` git checkout -B ${shellSingleQuote(options.branchName)} ${shellSingleQuote(`${options.gitRemote}/${options.branchName}`)}`,
|
|
645
656
|
"else",
|
|
646
|
-
` git checkout -B ${shellSingleQuote(options.branchName)} ${shellSingleQuote(
|
|
657
|
+
` git checkout -B ${shellSingleQuote(options.branchName)} ${shellSingleQuote(baseRef)}`,
|
|
647
658
|
"fi",
|
|
648
659
|
];
|
|
649
660
|
return [
|
|
650
661
|
"set -euo pipefail",
|
|
651
662
|
`cleanup() { rm -f ${shellSingleQuote(REMOTE_SECRETS_FILE)}; ${unsetSecretsLine}; }`,
|
|
652
663
|
"trap cleanup EXIT",
|
|
664
|
+
`git_remote=${shellSingleQuote(options.gitRemote)}`,
|
|
653
665
|
'mkdir -p "$HOME/dev"',
|
|
654
|
-
`repo_dir="$HOME/dev/${directoryName}
|
|
666
|
+
`repo_dir="$HOME/dev"/${shellSingleQuote(directoryName)}`,
|
|
655
667
|
'if [ ! -d "$repo_dir/.git" ]; then',
|
|
656
668
|
` gh repo clone ${shellSingleQuote(slug)} "$repo_dir"`,
|
|
657
669
|
"fi",
|
|
658
670
|
'cd "$repo_dir"',
|
|
659
|
-
|
|
671
|
+
'if ! git remote get-url "$git_remote" >/dev/null 2>&1; then',
|
|
672
|
+
' origin_url="$(git remote get-url origin)"',
|
|
673
|
+
' git remote add "$git_remote" "$origin_url"',
|
|
674
|
+
"fi",
|
|
675
|
+
'git fetch "$git_remote" --prune',
|
|
660
676
|
...checkoutLines,
|
|
661
677
|
`if [ -f ${shellSingleQuote(REMOTE_SECRETS_FILE)} ]; then set -a && . ${shellSingleQuote(REMOTE_SECRETS_FILE)} && set +a; fi`,
|
|
662
678
|
DEFAULT_REMOTE_SETUP_COMMAND,
|
|
663
679
|
].join("\n");
|
|
664
680
|
}
|
|
681
|
+
async function resolveBootstrapOptions(options) {
|
|
682
|
+
const config = await loadConfig();
|
|
683
|
+
return {
|
|
684
|
+
...options,
|
|
685
|
+
baseBranch: options.baseBranch ?? config.git.defaultBranch,
|
|
686
|
+
gitRemote: config.git.remote,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
665
689
|
function noSecretCleanup() {
|
|
666
690
|
// No staged secrets file was created.
|
|
667
691
|
}
|
|
@@ -697,12 +721,17 @@ function stageBuildSecrets(options) {
|
|
|
697
721
|
};
|
|
698
722
|
}
|
|
699
723
|
export async function bootstrapRemoteRepository(options) {
|
|
724
|
+
const bootstrapOptions = {
|
|
725
|
+
...options,
|
|
726
|
+
gitRemote: options.gitRemote ?? DEFAULT_GIT_REMOTE,
|
|
727
|
+
};
|
|
728
|
+
validateRemoteBootstrapOptions(bootstrapOptions);
|
|
700
729
|
const config = remoteConfigWithRunnerName(options.runnerName);
|
|
701
730
|
const provider = providerFor(config);
|
|
702
731
|
if (!(await provider.runnerExists(config))) {
|
|
703
732
|
throw new Error(`Remote runner ${options.runnerName} does not exist. Run crew remote setup first.`);
|
|
704
733
|
}
|
|
705
|
-
const stagedSecrets = stageBuildSecrets(
|
|
734
|
+
const stagedSecrets = stageBuildSecrets(bootstrapOptions);
|
|
706
735
|
try {
|
|
707
736
|
if (stagedSecrets.names.length > 0) {
|
|
708
737
|
log(`Forwarding build secret names for setup only: ${stagedSecrets.names.join(", ")}`);
|
|
@@ -710,11 +739,11 @@ export async function bootstrapRemoteRepository(options) {
|
|
|
710
739
|
const files = stagedSecrets.filePath === undefined
|
|
711
740
|
? []
|
|
712
741
|
: [{ localPath: stagedSecrets.filePath, remotePath: REMOTE_SECRETS_FILE }];
|
|
713
|
-
log(`Bootstrapping ${repositorySlug(
|
|
742
|
+
log(`Bootstrapping ${repositorySlug(bootstrapOptions)} in ${options.runnerName}`);
|
|
714
743
|
await provider.runCommand({
|
|
715
744
|
config,
|
|
716
745
|
files,
|
|
717
|
-
remoteArguments: ["bash", "-lc", remoteBootstrapCommand(
|
|
746
|
+
remoteArguments: ["bash", "-lc", remoteBootstrapCommand(bootstrapOptions)],
|
|
718
747
|
options: { stdio: "inherit", timeoutMs: 0 },
|
|
719
748
|
});
|
|
720
749
|
}
|
|
@@ -790,7 +819,7 @@ export async function remoteCli(argv) {
|
|
|
790
819
|
return;
|
|
791
820
|
}
|
|
792
821
|
if (action === "bootstrap") {
|
|
793
|
-
await bootstrapRemoteRepository(parseBootstrapArguments(rest));
|
|
822
|
+
await bootstrapRemoteRepository(await resolveBootstrapOptions(parseBootstrapArguments(rest)));
|
|
794
823
|
return;
|
|
795
824
|
}
|
|
796
825
|
if (action === "sessions") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupRepos.d.ts","sourceRoot":"","sources":["../../src/commands/setupRepos.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAInE,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;AAEvF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,wCAAwC;IACxC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,EAAE,CAAC;IACzC,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;CACpB;AA6JD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"setupRepos.d.ts","sourceRoot":"","sources":["../../src/commands/setupRepos.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAInE,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;AAEvF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,wCAAwC;IACxC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,EAAE,CAAC;IACzC,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;CACpB;AA6JD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA0D3B;AAwBD,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAajE"}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* entries are skipped with a hint, because they have no canonical URL
|
|
6
6
|
* we can guess at without involving the user's gh login. Idempotent.
|
|
7
7
|
*/
|
|
8
|
-
import { opendirSync, statSync } from "node:fs";
|
|
9
|
-
import { isAbsolute, relative, resolve } from "node:path";
|
|
8
|
+
import { mkdirSync, opendirSync, statSync } from "node:fs";
|
|
9
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
10
10
|
import { runCommandAsync } from "../lib/commandRunner.js";
|
|
11
11
|
import { loadConfig } from "../lib/config.js";
|
|
12
12
|
import { which } from "../lib/host.js";
|
|
@@ -173,6 +173,7 @@ export async function setupRepos(config, options) {
|
|
|
173
173
|
const target = resolve(projectDir, entry);
|
|
174
174
|
log(`[clone] ${entry} → ${target}`);
|
|
175
175
|
try {
|
|
176
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
176
177
|
// oxlint-disable-next-line no-await-in-loop -- see comment above
|
|
177
178
|
await runCommandAsync("gh", ["repo", "clone", entry, target], {
|
|
178
179
|
stdio: "inherit",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spriteRemoteRunnerProvider.d.ts","sourceRoot":"","sources":["../../src/lib/spriteRemoteRunnerProvider.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"spriteRemoteRunnerProvider.d.ts","sourceRoot":"","sources":["../../src/lib/spriteRemoteRunnerProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -105,6 +105,9 @@ function worktreeTicketComponent(ticket) {
|
|
|
105
105
|
}
|
|
106
106
|
function spriteCreateWorktreeCommand(arguments_) {
|
|
107
107
|
const slug = repositorySlug(arguments_.owner, arguments_.repository);
|
|
108
|
+
const branchRemoteRef = `refs/remotes/${arguments_.gitRemote}/${arguments_.branchName}`;
|
|
109
|
+
const branchRef = `${arguments_.gitRemote}/${arguments_.branchName}`;
|
|
110
|
+
const baseRef = `${arguments_.gitRemote}/${arguments_.baseBranch}`;
|
|
108
111
|
return [
|
|
109
112
|
"set -euo pipefail",
|
|
110
113
|
`repo_root=${shellSingleQuote(arguments_.repoRoot)}`,
|
|
@@ -112,20 +115,27 @@ function spriteCreateWorktreeCommand(arguments_) {
|
|
|
112
115
|
`repo_dir=${shellSingleQuote(arguments_.repoDir)}`,
|
|
113
116
|
`worktree_dir=${shellSingleQuote(arguments_.worktreeDir)}`,
|
|
114
117
|
`branch=${shellSingleQuote(arguments_.branchName)}`,
|
|
115
|
-
`
|
|
118
|
+
`git_remote=${shellSingleQuote(arguments_.gitRemote)}`,
|
|
119
|
+
`branch_remote_ref=${shellSingleQuote(branchRemoteRef)}`,
|
|
120
|
+
`branch_ref=${shellSingleQuote(branchRef)}`,
|
|
121
|
+
`base_ref=${shellSingleQuote(baseRef)}`,
|
|
116
122
|
'mkdir -p "$repo_root" "$worktree_root"',
|
|
117
123
|
'if [ ! -d "$repo_dir/.git" ]; then',
|
|
118
124
|
` gh repo clone ${shellSingleQuote(slug)} "$repo_dir"`,
|
|
119
125
|
"fi",
|
|
120
|
-
'git -C "$repo_dir"
|
|
126
|
+
'if ! git -C "$repo_dir" remote get-url "$git_remote" >/dev/null 2>&1; then',
|
|
127
|
+
' origin_url="$(git -C "$repo_dir" remote get-url origin)"',
|
|
128
|
+
' git -C "$repo_dir" remote add "$git_remote" "$origin_url"',
|
|
129
|
+
"fi",
|
|
130
|
+
'git -C "$repo_dir" fetch "$git_remote" --prune',
|
|
121
131
|
'if [ -e "$worktree_dir" ]; then',
|
|
122
132
|
' echo "Remote worktree already exists: $worktree_dir" >&2',
|
|
123
133
|
" exit 1",
|
|
124
134
|
"fi",
|
|
125
|
-
'if git -C "$repo_dir" show-ref --verify --quiet "
|
|
126
|
-
' git -C "$repo_dir" worktree add -B "$branch" "$worktree_dir" "
|
|
135
|
+
'if git -C "$repo_dir" show-ref --verify --quiet "$branch_remote_ref"; then',
|
|
136
|
+
' git -C "$repo_dir" worktree add -B "$branch" "$worktree_dir" "$branch_ref"',
|
|
127
137
|
"else",
|
|
128
|
-
' git -C "$repo_dir" worktree add -b "$branch" "$worktree_dir" "
|
|
138
|
+
' git -C "$repo_dir" worktree add -b "$branch" "$worktree_dir" "$base_ref"',
|
|
129
139
|
"fi",
|
|
130
140
|
].join("\n");
|
|
131
141
|
}
|
|
@@ -306,7 +316,7 @@ export const spriteRemoteRunnerProvider = {
|
|
|
306
316
|
});
|
|
307
317
|
},
|
|
308
318
|
async createWorktree(arguments_) {
|
|
309
|
-
const { config, repository, ticket, branchName, baseBranch, signal } = arguments_;
|
|
319
|
+
const { config, repository, ticket, branchName, baseBranch, gitRemote, signal } = arguments_;
|
|
310
320
|
const remoteRepositoryName = repositoryDirectoryName(config.owner, repository);
|
|
311
321
|
const remoteRepoDir = remotePathJoin(config.repoRoot, remoteRepositoryName);
|
|
312
322
|
const remoteWorktreeDir = remotePathJoin(config.worktreeRoot, `${remoteRepositoryName}-${worktreeTicketComponent(ticket)}`);
|
|
@@ -324,6 +334,7 @@ export const spriteRemoteRunnerProvider = {
|
|
|
324
334
|
worktreeDir: remoteWorktreeDir,
|
|
325
335
|
branchName,
|
|
326
336
|
baseBranch,
|
|
337
|
+
gitRemote,
|
|
327
338
|
repoRoot: config.repoRoot,
|
|
328
339
|
worktreeRoot: config.worktreeRoot,
|
|
329
340
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAMpG,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,mCAAmC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IACtD,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAW7E;
|
|
1
|
+
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAMpG,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,mCAAmC;IACnC,cAAc,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IACtD,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAW7E;AAgdD,4FAA4F;AAC5F,iBAAS,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,EAAE,CAErD;AAED,iBAAS,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAE7E;AAED,iBAAS,YAAY,CACnB,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,aAAa,GAAG,SAAS,CAI3B;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,sCAAsC;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,wDAAwD;IACxD,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;CAChC;AAKD,iBAAe,QAAQ,CACrB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,cAAc,CAAC,CAkDzB;AAED,eAAO,MAAM,SAAS;;;;;;;CAOrB,CAAC"}
|
package/dist/lib/worktrees.js
CHANGED
|
@@ -198,6 +198,23 @@ function stateBaseDir() {
|
|
|
198
198
|
function remoteStateFilePath() {
|
|
199
199
|
return resolve(stateBaseDir(), "groundcrew", "remote-worktrees.json");
|
|
200
200
|
}
|
|
201
|
+
function normalizeRemoteStateNamespacePath(path) {
|
|
202
|
+
const normalized = path.replace(/\/+$/u, "");
|
|
203
|
+
return normalized.length === 0 ? "/" : normalized;
|
|
204
|
+
}
|
|
205
|
+
function remoteStateNamespaceFor(config) {
|
|
206
|
+
return JSON.stringify({
|
|
207
|
+
version: 1,
|
|
208
|
+
projectDir: resolve(config.workspace.projectDir),
|
|
209
|
+
remote: {
|
|
210
|
+
provider: config.remote.provider,
|
|
211
|
+
runnerName: config.remote.runnerName,
|
|
212
|
+
owner: config.remote.owner,
|
|
213
|
+
repoRoot: normalizeRemoteStateNamespacePath(config.remote.repoRoot),
|
|
214
|
+
worktreeRoot: normalizeRemoteStateNamespacePath(config.remote.worktreeRoot),
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
}
|
|
201
218
|
function isRemoteEntry(value) {
|
|
202
219
|
if (typeof value !== "object" || value === null) {
|
|
203
220
|
return false;
|
|
@@ -210,7 +227,8 @@ function isRemoteEntry(value) {
|
|
|
210
227
|
entry.kind === "remote" &&
|
|
211
228
|
isRemoteRunnerProviderName(entry.remoteProvider) &&
|
|
212
229
|
typeof entry.remoteRunnerName === "string" &&
|
|
213
|
-
typeof entry.remoteRepoDir === "string"
|
|
230
|
+
typeof entry.remoteRepoDir === "string" &&
|
|
231
|
+
(entry.remoteStateNamespace === undefined || typeof entry.remoteStateNamespace === "string"));
|
|
214
232
|
}
|
|
215
233
|
function readRemoteEntries() {
|
|
216
234
|
try {
|
|
@@ -238,8 +256,41 @@ function writeRemoteEntries(entries) {
|
|
|
238
256
|
}
|
|
239
257
|
}
|
|
240
258
|
}
|
|
241
|
-
function
|
|
242
|
-
|
|
259
|
+
function remoteStateEntryFor(namespace, entry) {
|
|
260
|
+
return { ...entry, remoteStateNamespace: namespace };
|
|
261
|
+
}
|
|
262
|
+
function remoteStateEntryMatchesNamespace(namespace, entry) {
|
|
263
|
+
// Legacy records have no project/config identity. Keep them on disk, but do
|
|
264
|
+
// not expose them to cleanup paths that could remove another config's remote
|
|
265
|
+
// worktree.
|
|
266
|
+
return entry.remoteStateNamespace === namespace;
|
|
267
|
+
}
|
|
268
|
+
function remoteStateEntryMatchesWorktree(namespace, candidate, entry) {
|
|
269
|
+
return (candidate.remoteStateNamespace === namespace &&
|
|
270
|
+
candidate.repository === entry.repository &&
|
|
271
|
+
candidate.ticket === entry.ticket &&
|
|
272
|
+
candidate.dir === entry.dir &&
|
|
273
|
+
candidate.kind === "remote");
|
|
274
|
+
}
|
|
275
|
+
function worktreeEntryFromRemoteState(entry) {
|
|
276
|
+
return {
|
|
277
|
+
repository: entry.repository,
|
|
278
|
+
ticket: entry.ticket,
|
|
279
|
+
branchName: entry.branchName,
|
|
280
|
+
dir: entry.dir,
|
|
281
|
+
kind: entry.kind,
|
|
282
|
+
remoteProvider: entry.remoteProvider,
|
|
283
|
+
remoteRunnerName: entry.remoteRunnerName,
|
|
284
|
+
remoteRepoDir: entry.remoteRepoDir,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function upsertRemoteEntry(config, entry) {
|
|
288
|
+
const namespace = remoteStateNamespaceFor(config);
|
|
289
|
+
const stateEntry = remoteStateEntryFor(namespace, entry);
|
|
290
|
+
writeRemoteEntries([
|
|
291
|
+
...readRemoteEntries().filter((candidate) => !remoteStateEntryMatchesWorktree(namespace, candidate, entry)),
|
|
292
|
+
stateEntry,
|
|
293
|
+
]);
|
|
243
294
|
}
|
|
244
295
|
function removeTemporaryRemoteStateFileBestEffort(path) {
|
|
245
296
|
try {
|
|
@@ -250,11 +301,9 @@ function removeTemporaryRemoteStateFileBestEffort(path) {
|
|
|
250
301
|
log(`Temporary remote state file cleanup skipped: ${errorMessage(error)}`);
|
|
251
302
|
}
|
|
252
303
|
}
|
|
253
|
-
function deleteRemoteEntry(entry) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
candidate.dir === entry.dir &&
|
|
257
|
-
candidate.kind === "remote")));
|
|
304
|
+
function deleteRemoteEntry(config, entry) {
|
|
305
|
+
const namespace = remoteStateNamespaceFor(config);
|
|
306
|
+
writeRemoteEntries(readRemoteEntries().filter((candidate) => !remoteStateEntryMatchesWorktree(namespace, candidate, entry)));
|
|
258
307
|
}
|
|
259
308
|
function remoteProviderFor(config, entry) {
|
|
260
309
|
return getRemoteRunnerProvider(entry?.remoteProvider ?? config.remote.provider);
|
|
@@ -284,6 +333,7 @@ const remoteWorktreeAdapter = {
|
|
|
284
333
|
ticket: spec.ticket,
|
|
285
334
|
branchName: base.branchName,
|
|
286
335
|
baseBranch: config.git.defaultBranch,
|
|
336
|
+
gitRemote: config.git.remote,
|
|
287
337
|
...signalProperty(signal),
|
|
288
338
|
});
|
|
289
339
|
const entry = {
|
|
@@ -297,7 +347,7 @@ const remoteWorktreeAdapter = {
|
|
|
297
347
|
remoteRepoDir,
|
|
298
348
|
};
|
|
299
349
|
try {
|
|
300
|
-
upsertRemoteEntry(entry);
|
|
350
|
+
upsertRemoteEntry(config, entry);
|
|
301
351
|
}
|
|
302
352
|
catch (error) {
|
|
303
353
|
await removeCreatedRemoteWorktreeBestEffort({
|
|
@@ -311,7 +361,11 @@ const remoteWorktreeAdapter = {
|
|
|
311
361
|
return entry;
|
|
312
362
|
},
|
|
313
363
|
list(config) {
|
|
314
|
-
|
|
364
|
+
const namespace = remoteStateNamespaceFor(config);
|
|
365
|
+
return readRemoteEntries()
|
|
366
|
+
.filter((entry) => remoteStateEntryMatchesNamespace(namespace, entry))
|
|
367
|
+
.filter((entry) => config.workspace.knownRepositories.includes(entry.repository))
|
|
368
|
+
.map(worktreeEntryFromRemoteState);
|
|
315
369
|
},
|
|
316
370
|
async remove(config, entry, options) {
|
|
317
371
|
log(`Removing remote worktree ${entry.dir}${options.force ? " (--force)" : ""}...`);
|
|
@@ -326,7 +380,7 @@ const remoteWorktreeAdapter = {
|
|
|
326
380
|
force: options.force,
|
|
327
381
|
...signalProperty(options.signal),
|
|
328
382
|
});
|
|
329
|
-
deleteRemoteEntry(entry);
|
|
383
|
+
deleteRemoteEntry(config, entry);
|
|
330
384
|
},
|
|
331
385
|
};
|
|
332
386
|
function adapterForEntry(entry) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.3",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle, remote runners, and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@clipboard-health/ai-rules": "2.18.4",
|
|
75
|
-
"@clipboard-health/oxlint-config": "1.
|
|
75
|
+
"@clipboard-health/oxlint-config": "1.9.4",
|
|
76
76
|
"@nx/js": "22.7.1",
|
|
77
77
|
"@tsconfig/node24": "24.0.4",
|
|
78
78
|
"@tsconfig/strictest": "2.0.8",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"markdownlint-cli2": "0.22.1",
|
|
89
89
|
"nx": "22.7.1",
|
|
90
90
|
"oxfmt": "0.47.0",
|
|
91
|
-
"oxlint": "1.
|
|
92
|
-
"oxlint-tsgolint": "0.
|
|
91
|
+
"oxlint": "1.64.0",
|
|
92
|
+
"oxlint-tsgolint": "0.22.1",
|
|
93
93
|
"syncpack": "15.0.0",
|
|
94
94
|
"vite": "8.0.10",
|
|
95
95
|
"vitest": "4.1.5"
|