@sideboard-ai/core 0.1.149 → 0.1.151
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/dist/{abletime-QEVAE67D.js → abletime-223A3LY6.js} +2 -2
- package/dist/{abletime-DI55AJX3.js → abletime-CLC5UJC2.js} +2 -2
- package/dist/agents/cursor-runner.cjs +40 -35
- package/dist/agents/cursor-runner.js +2 -2
- package/dist/{agents-V4MTDWXS.js → agents-JAOMXDXN.js} +10 -9
- package/dist/{agents-HSVQ2Z6Y.js → agents-U2JCFXW4.js} +10 -9
- package/dist/{app-settings-WJDCJFSY.js → app-settings-3CHCNIMD.js} +5 -1
- package/dist/{app-settings-ZCN632OJ.js → app-settings-J3JBCUOT.js} +5 -1
- package/dist/{chunk-N5PM7HGQ.js → chunk-2SDMENAL.js} +1 -1
- package/dist/{chunk-JTPDWTWJ.js → chunk-3JBMACYX.js} +137 -114
- package/dist/{chunk-I77RYPOH.js → chunk-4XNSGDXE.js} +12 -0
- package/dist/{chunk-JDYKQ6OI.js → chunk-6H4TJ5TA.js} +136 -75
- package/dist/{chunk-EGDRPA4B.js → chunk-77MP647F.js} +6 -6
- package/dist/{chunk-KBWND62T.js → chunk-7F4XE6UG.js} +5 -5
- package/dist/{chunk-R4JP32P5.js → chunk-ADCEGG4A.js} +4 -4
- package/dist/{chunk-BOD5V52W.js → chunk-B7XK37WZ.js} +1 -1
- package/dist/{chunk-BGQBELGJ.js → chunk-CZD4NRDZ.js} +12 -0
- package/dist/{chunk-KPIYENTF.js → chunk-FNQNJOHY.js} +89 -5
- package/dist/{chunk-ED4UPEJX.js → chunk-G7SIQFBQ.js} +1 -1
- package/dist/{chunk-TZWO7J4W.js → chunk-GCWAAPJ5.js} +115 -54
- package/dist/{chunk-YP3CSOZ6.js → chunk-GE7M3QTE.js} +142 -114
- package/dist/{chunk-LAFA56WD.js → chunk-HEII7W3Q.js} +1 -1
- package/dist/{chunk-3QRGCKYL.js → chunk-HWZWBJWL.js} +2 -2
- package/dist/{chunk-FKOIHGKV.js → chunk-JRH62XC4.js} +5 -1
- package/dist/{chunk-Q5DHVMXB.js → chunk-LZAOX76N.js} +18 -7
- package/dist/{chunk-B3SJXYIJ.js → chunk-NVXVEGGM.js} +1 -1
- package/dist/{chunk-ZVYLEF72.js → chunk-OXOU2FRS.js} +6 -6
- package/dist/chunk-QQKDGPUI.js +63 -0
- package/dist/{chunk-QTUESPAW.js → chunk-RFMQY56R.js} +1 -1
- package/dist/chunk-SYFVUF2C.js +65 -0
- package/dist/{chunk-Q62SDB53.js → chunk-TSCWSAQL.js} +2 -2
- package/dist/{chunk-DVM4ID64.js → chunk-UFHNSFAS.js} +89 -5
- package/dist/{chunk-PTI5H3RK.js → chunk-UIS7BVCX.js} +24 -13
- package/dist/{chunk-VZJVKQHH.js → chunk-WLV7TRMR.js} +1 -1
- package/dist/{chunk-N3VNOFXQ.js → chunk-Y2FNDKHF.js} +4 -4
- package/dist/{connected-teams-IJUJQ7R6.js → connected-teams-I2WZZZFA.js} +2 -2
- package/dist/{connected-teams-4A2AXDNN.js → connected-teams-XO766HB4.js} +2 -2
- package/dist/{coordinator-prompt-ZWBMAIWA.js → coordinator-prompt-LWYUZAXR.js} +6 -5
- package/dist/{coordinator-prompt-5GXGBGEZ.js → coordinator-prompt-ZK5MII7W.js} +6 -5
- package/dist/{global-workspace-OJRXWXEA.js → global-workspace-IB2CCEJF.js} +7 -6
- package/dist/{global-workspace-2SIPM4PW.js → global-workspace-T7RL3XEC.js} +7 -6
- package/dist/index.cjs +1010 -577
- package/dist/index.d.cts +96 -11
- package/dist/index.d.ts +96 -11
- package/dist/index.js +272 -93
- package/dist/mcp/run-stdio.cjs +981 -581
- package/dist/mcp/run-stdio.js +196 -43
- package/dist/{orchestrator-Z2O5OVJA.js → orchestrator-DPTRBKEH.js} +16 -13
- package/dist/{orchestrator-6TXQWBXN.js → orchestrator-Y5MWUU7C.js} +14 -11
- package/dist/{plan-file-6O7G4VPQ.js → plan-file-LSSSQGTQ.js} +2 -2
- package/dist/{plan-file-PHVKUAEE.js → plan-file-OZEBFSGQ.js} +2 -2
- package/dist/{run-XQKGHQZ2.js → run-CAGKZLHA.js} +1 -1
- package/dist/{run-USF6FRKV.js → run-EBWL2Q4N.js} +1 -1
- package/dist/{workspaces-UUJNOO4T.js → workspaces-VICQYI2N.js} +8 -7
- package/dist/{workspaces-3NUUZTQK.js → workspaces-ZXCUVCZC.js} +8 -7
- package/dist/{worktree-RZRJNHYL.js → worktree-DPIREDFR.js} +5 -4
- package/dist/{worktree-UCWWTVLS.js → worktree-HTUM35NT.js} +5 -4
- package/dist/worktree-exclude-N5X5I422.js +13 -0
- package/dist/worktree-exclude-X54WSOXM.js +11 -0
- package/package.json +1 -1
|
@@ -2,26 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
isWorkspaceScratchPath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NVXVEGGM.js";
|
|
6
6
|
import {
|
|
7
7
|
listThreads
|
|
8
8
|
} from "./chunk-EUXOHTUK.js";
|
|
9
|
+
import {
|
|
10
|
+
ensureWorktreeSideboardIgnored
|
|
11
|
+
} from "./chunk-SYFVUF2C.js";
|
|
9
12
|
import {
|
|
10
13
|
gh,
|
|
11
14
|
git,
|
|
15
|
+
githubAgentAuthDir,
|
|
16
|
+
githubAgentAuthReady,
|
|
17
|
+
githubCredentialHelperGitConfig,
|
|
18
|
+
githubGhConfigEnv,
|
|
19
|
+
materializeGithubAgentAuth,
|
|
12
20
|
resolveGhAuthToken
|
|
13
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-FNQNJOHY.js";
|
|
14
22
|
import {
|
|
15
23
|
getGithubGitAuthMode,
|
|
16
24
|
getGithubPat
|
|
17
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CZD4NRDZ.js";
|
|
18
26
|
import {
|
|
19
27
|
worktreesRoot
|
|
20
28
|
} from "./chunk-BD2ICC4D.js";
|
|
21
29
|
|
|
22
30
|
// src/git/worktree.ts
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
31
|
+
import { randomBytes } from "crypto";
|
|
32
|
+
import { existsSync, readdirSync, realpathSync, unlinkSync, writeFileSync } from "fs";
|
|
33
|
+
import { tmpdir } from "os";
|
|
34
|
+
import { join } from "path";
|
|
25
35
|
|
|
26
36
|
// src/git/team-meta.ts
|
|
27
37
|
var SOCCER_TEAM_META = {
|
|
@@ -835,9 +845,23 @@ function worktreeDisplayLabelForGroup(threads) {
|
|
|
835
845
|
}
|
|
836
846
|
|
|
837
847
|
// src/git/gh-errors.ts
|
|
848
|
+
var GITHUB_PR_BODY_MAX_CHARS = 65536;
|
|
849
|
+
var GITHUB_PR_BODY_SAFE_CHARS = 6e4;
|
|
838
850
|
function isGhRateLimitError(text) {
|
|
839
851
|
return /API rate limit (already )?exceeded/i.test(text) || /rate limit exceeded/i.test(text);
|
|
840
852
|
}
|
|
853
|
+
function isGhPrBodyTooLongError(text) {
|
|
854
|
+
return /body is too long|body.{0,20}too long|maximum is 65536|exceeds the maximum allowed size|request (entity |is )?too large|payload too large|413 Request Entity Too Large/i.test(
|
|
855
|
+
text
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
function clampGithubPrBody(body, max = GITHUB_PR_BODY_SAFE_CHARS) {
|
|
859
|
+
const t = body.replace(/\s+$/u, "");
|
|
860
|
+
if (t.length <= max) return t;
|
|
861
|
+
const note = "\n\n_(truncated to fit GitHub\u2019s PR body limit)_";
|
|
862
|
+
const keep = Math.max(0, max - note.length);
|
|
863
|
+
return `${t.slice(0, keep).replace(/\s+$/u, "")}${note}`;
|
|
864
|
+
}
|
|
841
865
|
function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
|
|
842
866
|
const ms = resetEpochSec * 1e3 - nowMs;
|
|
843
867
|
if (ms <= 0) return "soon";
|
|
@@ -848,18 +872,32 @@ function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
|
|
|
848
872
|
const hours = Math.ceil(mins / 60);
|
|
849
873
|
return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
850
874
|
}
|
|
875
|
+
function omitGhBodyArg(text) {
|
|
876
|
+
return text.replace(
|
|
877
|
+
/--body(?:-file)?(\s+|=)(?:'[^']*'|"[^"]*"|\S+)/g,
|
|
878
|
+
"--body <omitted>"
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
function capGhErrorText(text, max = 400) {
|
|
882
|
+
const t = text.trim();
|
|
883
|
+
if (t.length <= max) return t;
|
|
884
|
+
return `${t.slice(0, max - 1).trimEnd()}\u2026`;
|
|
885
|
+
}
|
|
851
886
|
function extractGhErrorDetail(text) {
|
|
852
|
-
const trimmed = text.trim();
|
|
887
|
+
const trimmed = omitGhBodyArg(text.trim());
|
|
853
888
|
if (!trimmed) return "";
|
|
889
|
+
if (isGhPrBodyTooLongError(text) || isGhPrBodyTooLongError(trimmed)) {
|
|
890
|
+
return "GraphQL: body is too long (GitHub PR body / request size limit).";
|
|
891
|
+
}
|
|
854
892
|
const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
|
|
855
|
-
if (graphql?.[1]) return `GraphQL: ${graphql[1].trim()}
|
|
893
|
+
if (graphql?.[1]) return capGhErrorText(`GraphQL: ${graphql[1].trim()}`);
|
|
856
894
|
const http = trimmed.match(/\bHTTP\s+\d{3}:\s*(.+)$/im);
|
|
857
|
-
if (http?.[1]) return `HTTP: ${http[1].trim()}
|
|
895
|
+
if (http?.[1]) return capGhErrorText(`HTTP: ${http[1].trim()}`);
|
|
858
896
|
const lines = trimmed.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
859
897
|
if (lines.length > 1 && /^Command failed with exit code/i.test(lines[0])) {
|
|
860
|
-
return lines.slice(1).join(" ").trim() || lines[0];
|
|
898
|
+
return capGhErrorText(lines.slice(1).join(" ").trim() || lines[0]);
|
|
861
899
|
}
|
|
862
|
-
return trimmed;
|
|
900
|
+
return capGhErrorText(trimmed);
|
|
863
901
|
}
|
|
864
902
|
function formatGhLandError(raw, opts) {
|
|
865
903
|
const trimmed = raw.trim();
|
|
@@ -877,6 +915,10 @@ function formatGhLandError(raw, opts) {
|
|
|
877
915
|
const hint = opts?.targetedRepo ? ` Branch was pushed to origin \u2014 confirm it exists on GitHub and differs from the base branch.${target}${head}.` : " Often `gh` targeted upstream instead of origin. Branch was pushed \u2014 retry in the latest Sideboard, or run: gh pr create -R <owner/name> --base main --head <branch>.";
|
|
878
916
|
return `Could not create the pull request.${hint}`;
|
|
879
917
|
}
|
|
918
|
+
if (isGhPrBodyTooLongError(raw) || isGhPrBodyTooLongError(detail)) {
|
|
919
|
+
const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
|
|
920
|
+
return `GitHub rejected the pull request: the GraphQL body is too long (PR description / request size, limit ${GITHUB_PR_BODY_MAX_CHARS} characters).${pushNote} Retry with a short title and body via \`gh pr create --body-file\` (or send_to_thread so the worktree agent writes a brief description). Do not paste a changelog or diff into --body.`;
|
|
921
|
+
}
|
|
880
922
|
if (isGhRateLimitError(raw) || isGhRateLimitError(detail)) {
|
|
881
923
|
const when = opts?.resetAt ? ` Try again ${formatRateLimitResetHint(opts.resetAt, opts.nowMs)}.` : " Wait a few minutes and try again.";
|
|
882
924
|
const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
|
|
@@ -900,88 +942,29 @@ function formatMergePrError(raw) {
|
|
|
900
942
|
|
|
901
943
|
// src/git/git-auth-mode.ts
|
|
902
944
|
import { isAbsolute } from "path";
|
|
903
|
-
|
|
904
|
-
// src/git/github-agent-auth.ts
|
|
905
|
-
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
906
|
-
import { homedir } from "os";
|
|
907
|
-
import { dirname, join } from "path";
|
|
908
|
-
function githubAgentAuthDir() {
|
|
909
|
-
const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
|
|
910
|
-
if (override) return override;
|
|
911
|
-
return join(homedir(), ".sideboard-git-auth");
|
|
912
|
-
}
|
|
913
|
-
function githubCredentialStorePath() {
|
|
914
|
-
return join(githubAgentAuthDir(), "git-credentials");
|
|
915
|
-
}
|
|
916
|
-
function githubGhConfigDir() {
|
|
917
|
-
return join(githubAgentAuthDir(), "gh");
|
|
918
|
-
}
|
|
919
|
-
function writePrivateFile(file, body) {
|
|
920
|
-
mkdirSync(dirname(file), { recursive: true, mode: 448 });
|
|
921
|
-
try {
|
|
922
|
-
chmodSync(dirname(file), 448);
|
|
923
|
-
} catch {
|
|
924
|
-
}
|
|
925
|
-
writeFileSync(file, body, { encoding: "utf8", mode: 384 });
|
|
926
|
-
try {
|
|
927
|
-
chmodSync(file, 384);
|
|
928
|
-
} catch {
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
function gitCredentialStoreContents(token) {
|
|
932
|
-
return `https://x-access-token:${encodeURIComponent(token)}@github.com
|
|
933
|
-
`;
|
|
934
|
-
}
|
|
935
|
-
function ghHostsYml(token, user = "x-access-token") {
|
|
936
|
-
const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
|
|
937
|
-
return [
|
|
938
|
-
"github.com:",
|
|
939
|
-
" git_protocol: https",
|
|
940
|
-
` user: ${u}`,
|
|
941
|
-
` oauth_token: ${token}`,
|
|
942
|
-
" users:",
|
|
943
|
-
` ${u}:`,
|
|
944
|
-
` oauth_token: ${token}`,
|
|
945
|
-
""
|
|
946
|
-
].join("\n");
|
|
947
|
-
}
|
|
948
|
-
function materializeGithubAgentAuth(token, user) {
|
|
949
|
-
const trimmed = token.trim();
|
|
950
|
-
if (!trimmed) return;
|
|
951
|
-
const root = githubAgentAuthDir();
|
|
952
|
-
mkdirSync(root, { recursive: true, mode: 448 });
|
|
953
|
-
try {
|
|
954
|
-
chmodSync(root, 448);
|
|
955
|
-
} catch {
|
|
956
|
-
}
|
|
957
|
-
writePrivateFile(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
|
|
958
|
-
const ghDir = githubGhConfigDir();
|
|
959
|
-
mkdirSync(ghDir, { recursive: true, mode: 448 });
|
|
960
|
-
writePrivateFile(join(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
|
|
961
|
-
writePrivateFile(join(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
|
|
962
|
-
}
|
|
963
|
-
function githubAgentAuthReady() {
|
|
964
|
-
return existsSync(githubCredentialStorePath()) && existsSync(join(githubGhConfigDir(), "hosts.yml"));
|
|
965
|
-
}
|
|
966
|
-
function githubCredentialHelperGitConfig() {
|
|
967
|
-
const file = githubCredentialStorePath();
|
|
968
|
-
return [
|
|
969
|
-
{ key: "credential.helper", value: "" },
|
|
970
|
-
{ key: "credential.helper", value: `store --file=${file}` }
|
|
971
|
-
];
|
|
972
|
-
}
|
|
973
|
-
function githubGhConfigEnv() {
|
|
974
|
-
return {
|
|
975
|
-
GH_CONFIG_DIR: githubGhConfigDir(),
|
|
976
|
-
GH_PROMPT_DISABLED: "1"
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// src/git/git-auth-mode.ts
|
|
981
945
|
var HTTPS_REWRITE = [
|
|
982
946
|
{ key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
|
|
983
947
|
{ key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" }
|
|
984
948
|
];
|
|
949
|
+
function githubHttpsInsteadOfEntries(remoteUrl) {
|
|
950
|
+
const entries = [...HTTPS_REWRITE];
|
|
951
|
+
const url = remoteUrl?.trim() ?? "";
|
|
952
|
+
const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
|
|
953
|
+
if (scpAlias?.[1]) {
|
|
954
|
+
entries.push({
|
|
955
|
+
key: "url.https://github.com/.insteadOf",
|
|
956
|
+
value: `${scpAlias[1]}:`
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
const sshAlias = url.match(/^ssh:\/\/git@(github\.com-[^/]+)\//i);
|
|
960
|
+
if (sshAlias?.[1]) {
|
|
961
|
+
entries.push({
|
|
962
|
+
key: "url.https://github.com/.insteadOf",
|
|
963
|
+
value: `ssh://git@${sshAlias[1]}/`
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
return entries;
|
|
967
|
+
}
|
|
985
968
|
var TOKEN_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
986
969
|
var tokenMemo = null;
|
|
987
970
|
var GITHUB_CHILD_TOKEN_KEYS = [
|
|
@@ -1011,16 +994,19 @@ function appendIndexedGitConfig(existing, entries) {
|
|
|
1011
994
|
out.GIT_CONFIG_COUNT = String(count + entries.length);
|
|
1012
995
|
return out;
|
|
1013
996
|
}
|
|
1014
|
-
function githubAgentGitEnv(existing) {
|
|
997
|
+
function githubAgentGitEnv(existing, opts) {
|
|
1015
998
|
const helpers = githubAgentAuthReady() ? githubCredentialHelperGitConfig() : [{ key: "credential.helper", value: "" }];
|
|
1016
|
-
return appendIndexedGitConfig(existing, [
|
|
999
|
+
return appendIndexedGitConfig(existing, [
|
|
1000
|
+
...githubHttpsInsteadOfEntries(opts?.remoteUrl),
|
|
1001
|
+
...helpers
|
|
1002
|
+
]);
|
|
1017
1003
|
}
|
|
1018
1004
|
function applyGithubGitAuthEnv(existing, opts) {
|
|
1019
1005
|
const out = { ...nonInteractiveGitProcessEnv() };
|
|
1020
1006
|
const token = opts.token?.trim() || existing?.GH_TOKEN?.trim() || "" || "";
|
|
1021
1007
|
if (token) materializeGithubAgentAuth(token);
|
|
1022
1008
|
Object.assign(out, githubGhConfigEnv());
|
|
1023
|
-
if (opts.mode === "ssh") return out;
|
|
1009
|
+
if (opts.mode === "ssh" && !token) return out;
|
|
1024
1010
|
Object.assign(out, githubAgentGitEnv(existing));
|
|
1025
1011
|
return out;
|
|
1026
1012
|
}
|
|
@@ -1133,8 +1119,9 @@ function formatGitAuthModeDirective(mode) {
|
|
|
1133
1119
|
case "ssh":
|
|
1134
1120
|
return [
|
|
1135
1121
|
"Git authentication (Settings \u2192 Git mode: SSH):",
|
|
1136
|
-
"-
|
|
1137
|
-
"-
|
|
1122
|
+
"- Stored remotes stay SSH (`git@github.com:\u2026`). Do not `git remote set-url` or rewrite them to HTTPS.",
|
|
1123
|
+
"- This process may push over HTTPS via `gh` (process-only `insteadOf`) so ask_git / unattended turns work when ssh-agent is missing \u2014 that does not change the stored remote.",
|
|
1124
|
+
"- SSH is batch-mode: it will not prompt for a Keychain password. If both SSH and HTTPS fail, tell the user to unlock ssh-agent or run `gh auth login` (or switch Settings \u2192 Git to Auto).",
|
|
1138
1125
|
"- `gh` already authenticates for PRs/API (no token in the environment). Do not set GitHub token environment variables or run `gh auth login` from this turn.",
|
|
1139
1126
|
"- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
|
|
1140
1127
|
].join("\n");
|
|
@@ -2228,6 +2215,16 @@ function originFetchBranch(sourceRef) {
|
|
|
2228
2215
|
if (ref.startsWith("origin/")) return ref.slice("origin/".length) || null;
|
|
2229
2216
|
return ref;
|
|
2230
2217
|
}
|
|
2218
|
+
async function originRemoteUrl(cwd) {
|
|
2219
|
+
const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
|
|
2220
|
+
return result.exitCode === 0 ? result.stdout.trim() : "";
|
|
2221
|
+
}
|
|
2222
|
+
function githubHttpsRetryEnv(remoteUrl) {
|
|
2223
|
+
return {
|
|
2224
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
2225
|
+
...githubAgentGitEnv(void 0, { remoteUrl })
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2231
2228
|
async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
|
|
2232
2229
|
const first = await git(args, repoPath, { reject: false, timeoutMs });
|
|
2233
2230
|
if (first.exitCode === 0) return true;
|
|
@@ -2237,15 +2234,14 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
|
|
|
2237
2234
|
} catch {
|
|
2238
2235
|
return false;
|
|
2239
2236
|
}
|
|
2240
|
-
if (mode === "ssh") return false;
|
|
2241
2237
|
const token = await resolveGithubAgentToken(mode, repoPath);
|
|
2242
2238
|
if (!token) return false;
|
|
2239
|
+
const remoteUrl = await originRemoteUrl(repoPath);
|
|
2243
2240
|
const tryHttps = async (header) => git(args, repoPath, {
|
|
2244
2241
|
reject: false,
|
|
2245
2242
|
timeoutMs,
|
|
2246
|
-
env:
|
|
2243
|
+
env: githubHttpsRetryEnv(remoteUrl),
|
|
2247
2244
|
config: {
|
|
2248
|
-
"url.https://github.com/.insteadOf": "git@github.com:",
|
|
2249
2245
|
"http.extraHeader": header
|
|
2250
2246
|
}
|
|
2251
2247
|
});
|
|
@@ -2327,8 +2323,8 @@ async function refreshOriginAndMaybeFastForwardMain(repoPath, sourceRef) {
|
|
|
2327
2323
|
}
|
|
2328
2324
|
async function createThreadWorktree(opts) {
|
|
2329
2325
|
let branchName = `thread/${opts.slug}`;
|
|
2330
|
-
const worktreePath =
|
|
2331
|
-
if (
|
|
2326
|
+
const worktreePath = join(worktreesRoot(opts.repoPath), opts.slug);
|
|
2327
|
+
if (existsSync(worktreePath)) {
|
|
2332
2328
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2333
2329
|
}
|
|
2334
2330
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2363,13 +2359,14 @@ ${add.stdout}`;
|
|
|
2363
2359
|
});
|
|
2364
2360
|
branchName = added;
|
|
2365
2361
|
await ensureGhPreferOrigin(worktreePath);
|
|
2362
|
+
await ensureWorktreeSideboardIgnored(worktreePath);
|
|
2366
2363
|
return { branchName, worktreePath };
|
|
2367
2364
|
}
|
|
2368
2365
|
async function createExistingBranchWorktree(opts) {
|
|
2369
2366
|
const branchName = opts.branchName.trim();
|
|
2370
2367
|
if (!branchName) throw new Error("branch name required");
|
|
2371
|
-
const worktreePath =
|
|
2372
|
-
if (
|
|
2368
|
+
const worktreePath = join(worktreesRoot(opts.repoPath), opts.slug);
|
|
2369
|
+
if (existsSync(worktreePath)) {
|
|
2373
2370
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2374
2371
|
}
|
|
2375
2372
|
await ensureGhPreferOrigin(opts.repoPath);
|
|
@@ -2407,6 +2404,7 @@ async function createExistingBranchWorktree(opts) {
|
|
|
2407
2404
|
await git(["checkout", "-B", branchName], worktreePath, { reject: false });
|
|
2408
2405
|
}
|
|
2409
2406
|
await ensureGhPreferOrigin(worktreePath);
|
|
2407
|
+
await ensureWorktreeSideboardIgnored(worktreePath);
|
|
2410
2408
|
return { branchName, worktreePath };
|
|
2411
2409
|
}
|
|
2412
2410
|
async function removeWorktree(repoPath, worktreePath, opts) {
|
|
@@ -2495,7 +2493,9 @@ async function currentBranch(worktreePath) {
|
|
|
2495
2493
|
async function commitAll(worktreePath, message) {
|
|
2496
2494
|
const dirty = await isDirty(worktreePath);
|
|
2497
2495
|
if (!dirty) return false;
|
|
2496
|
+
await ensureWorktreeSideboardIgnored(worktreePath);
|
|
2498
2497
|
await git(["add", "-A"], worktreePath);
|
|
2498
|
+
await git(["reset", "-q", "--", ".sideboard"], worktreePath, { reject: false });
|
|
2499
2499
|
await git(["commit", "-m", message], worktreePath);
|
|
2500
2500
|
return true;
|
|
2501
2501
|
}
|
|
@@ -2509,14 +2509,14 @@ async function pushBranch(worktreePath, branchName) {
|
|
|
2509
2509
|
const token = await resolveGithubAgentToken(getGithubGitAuthMode(), worktreePath);
|
|
2510
2510
|
if (!slug || !token) {
|
|
2511
2511
|
throw new Error(
|
|
2512
|
-
sshErr || `git push origin ${branchName} failed` + (!token ? " (
|
|
2512
|
+
sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
|
|
2513
2513
|
);
|
|
2514
2514
|
}
|
|
2515
|
+
const remoteUrl = await originRemoteUrl(worktreePath);
|
|
2515
2516
|
const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
|
|
2516
2517
|
reject: false,
|
|
2517
|
-
env:
|
|
2518
|
+
env: githubHttpsRetryEnv(remoteUrl),
|
|
2518
2519
|
config: {
|
|
2519
|
-
"url.https://github.com/.insteadOf": "git@github.com:",
|
|
2520
2520
|
"http.extraHeader": header
|
|
2521
2521
|
}
|
|
2522
2522
|
});
|
|
@@ -2646,6 +2646,32 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2646
2646
|
const repoArgs = ghRepoSelectArgs(slug);
|
|
2647
2647
|
const headRef = ghHeadRef(slug, opts.head);
|
|
2648
2648
|
const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
|
|
2649
|
+
const bodyText = clampGithubPrBody(opts.body ?? opts.title);
|
|
2650
|
+
const bodyFile = join(
|
|
2651
|
+
tmpdir(),
|
|
2652
|
+
`sideboard-pr-body-${process.pid}-${randomBytes(6).toString("hex")}.md`
|
|
2653
|
+
);
|
|
2654
|
+
writeFileSync(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
|
|
2655
|
+
`, "utf8");
|
|
2656
|
+
const bodyArgs = ["--body-file", bodyFile];
|
|
2657
|
+
try {
|
|
2658
|
+
return await createOrUpdatePrWithBodyFile(worktreePath, {
|
|
2659
|
+
...opts,
|
|
2660
|
+
slug,
|
|
2661
|
+
repoArgs,
|
|
2662
|
+
headRef,
|
|
2663
|
+
branchOnly,
|
|
2664
|
+
bodyArgs
|
|
2665
|
+
});
|
|
2666
|
+
} finally {
|
|
2667
|
+
try {
|
|
2668
|
+
unlinkSync(bodyFile);
|
|
2669
|
+
} catch {
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
|
|
2674
|
+
const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
|
|
2649
2675
|
const existing = await gh(
|
|
2650
2676
|
[...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
2651
2677
|
worktreePath,
|
|
@@ -2661,8 +2687,7 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2661
2687
|
branchOnly,
|
|
2662
2688
|
"--title",
|
|
2663
2689
|
opts.title,
|
|
2664
|
-
|
|
2665
|
-
opts.body ?? opts.title
|
|
2690
|
+
...bodyArgs
|
|
2666
2691
|
],
|
|
2667
2692
|
worktreePath,
|
|
2668
2693
|
{ reject: false }
|
|
@@ -2677,8 +2702,7 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2677
2702
|
"--web",
|
|
2678
2703
|
"--title",
|
|
2679
2704
|
opts.title,
|
|
2680
|
-
|
|
2681
|
-
opts.body ?? opts.title,
|
|
2705
|
+
...bodyArgs,
|
|
2682
2706
|
"--base",
|
|
2683
2707
|
opts.base,
|
|
2684
2708
|
"--head",
|
|
@@ -2699,8 +2723,7 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2699
2723
|
"create",
|
|
2700
2724
|
"--title",
|
|
2701
2725
|
opts.title,
|
|
2702
|
-
|
|
2703
|
-
opts.body ?? opts.title,
|
|
2726
|
+
...bodyArgs,
|
|
2704
2727
|
"--base",
|
|
2705
2728
|
opts.base,
|
|
2706
2729
|
"--head",
|
|
@@ -2724,8 +2747,8 @@ function sameRepoPath(a, b) {
|
|
|
2724
2747
|
return normalizeWorktreePath(a) === normalizeWorktreePath(b);
|
|
2725
2748
|
}
|
|
2726
2749
|
function listLocalThreadBranchSlugs(repoPath) {
|
|
2727
|
-
const refsDir =
|
|
2728
|
-
if (!
|
|
2750
|
+
const refsDir = join(repoPath, ".git", "refs", "heads", "thread");
|
|
2751
|
+
if (!existsSync(refsDir)) return [];
|
|
2729
2752
|
try {
|
|
2730
2753
|
return readdirSync(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
|
|
2731
2754
|
} catch {
|
|
@@ -2735,7 +2758,7 @@ function listLocalThreadBranchSlugs(repoPath) {
|
|
|
2735
2758
|
function collectTakenTeamSlugs(repoPath) {
|
|
2736
2759
|
const taken = /* @__PURE__ */ new Set();
|
|
2737
2760
|
const root = worktreesRoot(repoPath);
|
|
2738
|
-
if (
|
|
2761
|
+
if (existsSync(root)) {
|
|
2739
2762
|
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
2740
2763
|
if (entry.isDirectory() && entry.name !== ".DS_Store") {
|
|
2741
2764
|
taken.add(normalizeTakenSlug(entry.name));
|
|
@@ -2757,8 +2780,8 @@ function allocateTeamSlug(repoPath) {
|
|
|
2757
2780
|
const taken = collectTakenTeamSlugs(repoPath);
|
|
2758
2781
|
for (let attempt = 0; attempt < 32; attempt++) {
|
|
2759
2782
|
const team = allocateTeamName(taken);
|
|
2760
|
-
const path =
|
|
2761
|
-
if (!
|
|
2783
|
+
const path = join(worktreesRoot(repoPath), team.slug);
|
|
2784
|
+
if (!existsSync(path)) return team;
|
|
2762
2785
|
taken.add(team.slug);
|
|
2763
2786
|
}
|
|
2764
2787
|
throw new Error("No available soccer team worktree directories left");
|
|
@@ -250,6 +250,7 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
|
|
|
250
250
|
"cursor"
|
|
251
251
|
]);
|
|
252
252
|
var GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
|
|
253
|
+
var FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
|
|
253
254
|
function emptyPublicIntegrations() {
|
|
254
255
|
return {
|
|
255
256
|
hasLinearApiKey: false,
|
|
@@ -573,6 +574,9 @@ function normalizeAdvanced(raw) {
|
|
|
573
574
|
if (typeof source.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(source.orchestrationQuotaFallbackAgent) && source.orchestrationQuotaFallbackAgent !== "brightsy") {
|
|
574
575
|
out.orchestrationQuotaFallbackAgent = source.orchestrationQuotaFallbackAgent;
|
|
575
576
|
}
|
|
577
|
+
if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
|
|
578
|
+
out.followUpBehavior = source.followUpBehavior;
|
|
579
|
+
}
|
|
576
580
|
return out;
|
|
577
581
|
}
|
|
578
582
|
function normalizeSettings(raw) {
|
|
@@ -1274,6 +1278,9 @@ function updateAdvancedSettings(patch) {
|
|
|
1274
1278
|
if (typeof patch.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(patch.orchestrationQuotaFallbackAgent) && patch.orchestrationQuotaFallbackAgent !== "brightsy") {
|
|
1275
1279
|
advanced.orchestrationQuotaFallbackAgent = patch.orchestrationQuotaFallbackAgent;
|
|
1276
1280
|
}
|
|
1281
|
+
if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
|
|
1282
|
+
advanced.followUpBehavior = patch.followUpBehavior;
|
|
1283
|
+
}
|
|
1277
1284
|
return saveAppSettings({ ...current, advanced });
|
|
1278
1285
|
}
|
|
1279
1286
|
function autoRenameBranchEnabled(settings = loadAppSettings()) {
|
|
@@ -1319,6 +1326,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
|
|
|
1319
1326
|
}
|
|
1320
1327
|
return "cursor";
|
|
1321
1328
|
}
|
|
1329
|
+
function followUpBehavior(settings = loadAppSettings()) {
|
|
1330
|
+
return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
|
|
1331
|
+
}
|
|
1322
1332
|
function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
1323
1333
|
const n = settings.advanced.maxConcurrent;
|
|
1324
1334
|
if (typeof n === "number" && Number.isFinite(n)) {
|
|
@@ -1426,6 +1436,7 @@ export {
|
|
|
1426
1436
|
issueSourceLabel,
|
|
1427
1437
|
HARNESS_ENV_KEYS,
|
|
1428
1438
|
GITHUB_GIT_AUTH_MODES,
|
|
1439
|
+
FOLLOW_UP_BEHAVIORS,
|
|
1429
1440
|
emptyPublicIntegrations,
|
|
1430
1441
|
toPublicAppSettings,
|
|
1431
1442
|
appSettingsPath,
|
|
@@ -1479,6 +1490,7 @@ export {
|
|
|
1479
1490
|
autoCleanupOrphansEnabled,
|
|
1480
1491
|
orchestrationQuotaOnLimit,
|
|
1481
1492
|
orchestrationQuotaFallbackAgent,
|
|
1493
|
+
followUpBehavior,
|
|
1482
1494
|
maxConcurrentAgents,
|
|
1483
1495
|
resolveClaudeExecutable,
|
|
1484
1496
|
resolveAgentExecutable,
|