@oh-my-pi/pi-coding-agent 13.18.0 → 13.19.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/CHANGELOG.md +50 -0
- package/package.json +7 -11
- package/src/autoresearch/git.ts +25 -30
- package/src/autoresearch/tools/log-experiment.ts +61 -74
- package/src/commit/agentic/agent.ts +0 -3
- package/src/commit/agentic/index.ts +19 -22
- package/src/commit/agentic/tools/git-file-diff.ts +3 -6
- package/src/commit/agentic/tools/git-hunk.ts +3 -3
- package/src/commit/agentic/tools/git-overview.ts +6 -9
- package/src/commit/agentic/tools/index.ts +6 -8
- package/src/commit/agentic/tools/propose-commit.ts +4 -7
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/split-commit.ts +4 -4
- package/src/commit/changelog/index.ts +5 -9
- package/src/commit/pipeline.ts +10 -12
- package/src/config/keybindings.ts +7 -6
- package/src/config/settings-schema.ts +44 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +4 -16
- package/src/extensibility/custom-commands/bundled/review/index.ts +43 -41
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
- package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
- package/src/index.ts +1 -0
- package/src/main.ts +24 -2
- package/src/modes/components/footer.ts +9 -29
- package/src/modes/components/hook-editor.ts +3 -3
- package/src/modes/components/hook-selector.ts +6 -1
- package/src/modes/components/session-observer-overlay.ts +472 -0
- package/src/modes/components/settings-defs.ts +19 -0
- package/src/modes/components/status-line.ts +15 -61
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +59 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +26 -0
- package/src/modes/interactive-mode.ts +195 -43
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +0 -42
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/keybinding-matchers.ts +9 -0
- package/src/prompts/system/custom-system-prompt.md +5 -0
- package/src/prompts/system/system-prompt.md +6 -0
- package/src/sdk.ts +28 -13
- package/src/secrets/index.ts +1 -1
- package/src/secrets/obfuscator.ts +24 -16
- package/src/session/agent-session.ts +75 -30
- package/src/session/session-manager.ts +15 -5
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +28 -0
- package/src/task/index.ts +88 -78
- package/src/task/types.ts +25 -0
- package/src/task/worktree.ts +127 -145
- package/src/tools/exit-plan-mode.ts +1 -0
- package/src/tools/gh.ts +120 -297
- package/src/tools/read.ts +13 -79
- package/src/utils/external-editor.ts +11 -5
- package/src/utils/git.ts +1400 -0
- package/src/web/search/render.ts +6 -4
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -210
- package/src/commit/git/operations.ts +0 -54
- package/src/tools/gh-cli.ts +0 -125
package/src/tools/gh.ts
CHANGED
|
@@ -3,7 +3,6 @@ import * as path from "node:path";
|
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import { abortableSleep, isEnoent, untilAborted } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { type Static, Type } from "@sinclair/typebox";
|
|
6
|
-
import { $ } from "bun";
|
|
7
6
|
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
8
7
|
import ghIssueViewDescription from "../prompts/tools/gh-issue-view.md" with { type: "text" };
|
|
9
8
|
import ghPrCheckoutDescription from "../prompts/tools/gh-pr-checkout.md" with { type: "text" };
|
|
@@ -14,8 +13,8 @@ import ghRepoViewDescription from "../prompts/tools/gh-repo-view.md" with { type
|
|
|
14
13
|
import ghRunWatchDescription from "../prompts/tools/gh-run-watch.md" with { type: "text" };
|
|
15
14
|
import ghSearchIssuesDescription from "../prompts/tools/gh-search-issues.md" with { type: "text" };
|
|
16
15
|
import ghSearchPrsDescription from "../prompts/tools/gh-search-prs.md" with { type: "text" };
|
|
16
|
+
import * as git from "../utils/git";
|
|
17
17
|
import type { ToolSession } from ".";
|
|
18
|
-
import { isGhAvailable, runGhCommand, runGhJson, runGhText } from "./gh-cli";
|
|
19
18
|
import type { OutputMeta } from "./output-meta";
|
|
20
19
|
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
21
20
|
import { toolResult } from "./tool-result";
|
|
@@ -401,19 +400,6 @@ interface GhPrViewData extends GhIssueViewData {
|
|
|
401
400
|
reviewDecision?: string;
|
|
402
401
|
}
|
|
403
402
|
|
|
404
|
-
interface GitCommandResult {
|
|
405
|
-
exitCode: number;
|
|
406
|
-
stdout: string;
|
|
407
|
-
stderr: string;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
interface GitWorktreeEntry {
|
|
411
|
-
path: string;
|
|
412
|
-
head?: string;
|
|
413
|
-
branch?: string;
|
|
414
|
-
detached: boolean;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
403
|
interface GhPrReviewCommit {
|
|
418
404
|
oid?: string | null;
|
|
419
405
|
}
|
|
@@ -641,142 +627,45 @@ function stripHeadsRef(value: string | undefined): string | undefined {
|
|
|
641
627
|
return value.startsWith("refs/heads/") ? value.slice("refs/heads/".length) : value;
|
|
642
628
|
}
|
|
643
629
|
|
|
644
|
-
function
|
|
645
|
-
const
|
|
646
|
-
if (
|
|
647
|
-
|
|
630
|
+
async function requireGitRepoRoot(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
631
|
+
const repoRoot = await git.repo.root(cwd, signal);
|
|
632
|
+
if (!repoRoot) {
|
|
633
|
+
throw new ToolError("Current git repository is unavailable.");
|
|
648
634
|
}
|
|
649
635
|
|
|
650
|
-
return
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
async function runGitCommand(cwd: string, args: string[], signal?: AbortSignal): Promise<GitCommandResult> {
|
|
654
|
-
return untilAborted(signal, async () => {
|
|
655
|
-
throwIfAborted(signal);
|
|
656
|
-
const child = Bun.spawn(["git", ...args], {
|
|
657
|
-
cwd,
|
|
658
|
-
stdin: "ignore",
|
|
659
|
-
stdout: "pipe",
|
|
660
|
-
stderr: "pipe",
|
|
661
|
-
windowsHide: true,
|
|
662
|
-
signal,
|
|
663
|
-
});
|
|
664
|
-
throwIfAborted(signal);
|
|
665
|
-
|
|
666
|
-
if (!child.stdout || !child.stderr) {
|
|
667
|
-
throw new ToolError("Failed to capture git command output.");
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
671
|
-
new Response(child.stdout).text(),
|
|
672
|
-
new Response(child.stderr).text(),
|
|
673
|
-
child.exited,
|
|
674
|
-
]);
|
|
675
|
-
throwIfAborted(signal);
|
|
676
|
-
|
|
677
|
-
return {
|
|
678
|
-
exitCode: exitCode ?? 0,
|
|
679
|
-
stdout: normalizeBlock(stdout),
|
|
680
|
-
stderr: normalizeBlock(stderr),
|
|
681
|
-
};
|
|
682
|
-
});
|
|
636
|
+
return repoRoot;
|
|
683
637
|
}
|
|
684
638
|
|
|
685
|
-
async function
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (!text) {
|
|
690
|
-
throw new ToolError(`git ${args.join(" ")} returned empty output.`);
|
|
639
|
+
async function requirePrimaryGitRepoRoot(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
640
|
+
const primaryRepoRoot = await git.repo.primaryRoot(cwd, signal);
|
|
641
|
+
if (!primaryRepoRoot) {
|
|
642
|
+
throw new ToolError("Current git repository is unavailable.");
|
|
691
643
|
}
|
|
692
644
|
|
|
693
|
-
return
|
|
645
|
+
return primaryRepoRoot;
|
|
694
646
|
}
|
|
695
647
|
|
|
696
|
-
async function
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
699
|
-
throw new ToolError(
|
|
648
|
+
async function requireCurrentGitBranch(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
649
|
+
const branch = await git.branch.current(cwd, signal);
|
|
650
|
+
if (!branch) {
|
|
651
|
+
throw new ToolError("Current git branch is unavailable. Pass `branch` or `run` explicitly.");
|
|
700
652
|
}
|
|
701
653
|
|
|
702
|
-
return
|
|
654
|
+
return branch;
|
|
703
655
|
}
|
|
704
656
|
|
|
705
|
-
async function
|
|
706
|
-
const
|
|
707
|
-
if (
|
|
708
|
-
|
|
657
|
+
async function requireCurrentGitHead(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
658
|
+
const headSha = await git.head.sha(cwd, signal);
|
|
659
|
+
if (!headSha) {
|
|
660
|
+
throw new ToolError("Current git HEAD is unavailable. Pass `run` explicitly.");
|
|
709
661
|
}
|
|
710
662
|
|
|
711
|
-
return
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
async function resolveGitRepoRoot(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
715
|
-
return runGitTextChecked(cwd, ["rev-parse", "--show-toplevel"], signal);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
async function resolvePrimaryGitRepoRoot(repoRoot: string, signal?: AbortSignal): Promise<string> {
|
|
719
|
-
const commonDir = await runGitTextChecked(
|
|
720
|
-
repoRoot,
|
|
721
|
-
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
722
|
-
signal,
|
|
723
|
-
);
|
|
724
|
-
if (path.basename(commonDir) === ".git") {
|
|
725
|
-
return path.dirname(commonDir);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
return repoRoot;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
function parseGitWorktreeList(text: string): GitWorktreeEntry[] {
|
|
732
|
-
const trimmed = text.trim();
|
|
733
|
-
if (!trimmed) {
|
|
734
|
-
return [];
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
return trimmed
|
|
738
|
-
.split(/\n\s*\n/)
|
|
739
|
-
.map(block => block.trim())
|
|
740
|
-
.filter(Boolean)
|
|
741
|
-
.map(block => {
|
|
742
|
-
const entry: GitWorktreeEntry = {
|
|
743
|
-
path: "",
|
|
744
|
-
detached: false,
|
|
745
|
-
};
|
|
746
|
-
for (const line of block.split("\n")) {
|
|
747
|
-
if (line.startsWith("worktree ")) {
|
|
748
|
-
entry.path = line.slice("worktree ".length);
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
if (line.startsWith("HEAD ")) {
|
|
752
|
-
entry.head = line.slice("HEAD ".length);
|
|
753
|
-
continue;
|
|
754
|
-
}
|
|
755
|
-
if (line.startsWith("branch ")) {
|
|
756
|
-
entry.branch = line.slice("branch ".length);
|
|
757
|
-
continue;
|
|
758
|
-
}
|
|
759
|
-
if (line === "detached") {
|
|
760
|
-
entry.detached = true;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
return entry;
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
async function listGitWorktrees(repoRoot: string, signal?: AbortSignal): Promise<GitWorktreeEntry[]> {
|
|
768
|
-
const output = await runGitTextChecked(repoRoot, ["worktree", "list", "--porcelain"], signal);
|
|
769
|
-
return parseGitWorktreeList(output);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
async function gitRefExists(repoRoot: string, ref: string, signal?: AbortSignal): Promise<boolean> {
|
|
773
|
-
const result = await runGitCommand(repoRoot, ["show-ref", "--verify", "--quiet", ref], signal);
|
|
774
|
-
return result.exitCode === 0;
|
|
663
|
+
return headSha;
|
|
775
664
|
}
|
|
776
665
|
|
|
777
666
|
async function ensureGitWorktreePathAvailable(
|
|
778
667
|
worktreePath: string,
|
|
779
|
-
existingWorktrees: GitWorktreeEntry[],
|
|
668
|
+
existingWorktrees: git.GitWorktreeEntry[],
|
|
780
669
|
): Promise<void> {
|
|
781
670
|
const normalizedTarget = path.resolve(worktreePath);
|
|
782
671
|
const conflictingWorktree = existingWorktrees.find(entry => path.resolve(entry.path) === normalizedTarget);
|
|
@@ -804,15 +693,10 @@ function selectPrCloneUrl(originUrl: string | undefined, repo: Pick<GhRepoViewDa
|
|
|
804
693
|
}
|
|
805
694
|
|
|
806
695
|
async function getRemoteUrls(repoRoot: string, signal?: AbortSignal): Promise<Map<string, string>> {
|
|
807
|
-
const
|
|
808
|
-
const remotes =
|
|
809
|
-
remoteList
|
|
810
|
-
?.split("\n")
|
|
811
|
-
.map(value => value.trim())
|
|
812
|
-
.filter(Boolean) ?? [];
|
|
696
|
+
const remotes = await git.remote.list(repoRoot, signal);
|
|
813
697
|
const urls = new Map<string, string>();
|
|
814
698
|
for (const remoteName of remotes) {
|
|
815
|
-
const remoteUrl = await
|
|
699
|
+
const remoteUrl = await git.remote.url(repoRoot, remoteName, signal);
|
|
816
700
|
if (remoteUrl) {
|
|
817
701
|
urls.set(remoteName, remoteUrl);
|
|
818
702
|
}
|
|
@@ -826,7 +710,7 @@ async function ensurePrRemote(
|
|
|
826
710
|
signal?: AbortSignal,
|
|
827
711
|
): Promise<{ name: string; url: string }> {
|
|
828
712
|
if (!data.isCrossRepository) {
|
|
829
|
-
const originUrl =
|
|
713
|
+
const originUrl = await git.remote.url(repoRoot, "origin", signal);
|
|
830
714
|
if (!originUrl) {
|
|
831
715
|
throw new ToolError("origin remote is unavailable for this repository.");
|
|
832
716
|
}
|
|
@@ -838,13 +722,13 @@ async function ensurePrRemote(
|
|
|
838
722
|
}
|
|
839
723
|
|
|
840
724
|
const headRepository = requireNonEmpty(data.headRepository?.nameWithOwner, "head repository");
|
|
841
|
-
const repoSummary = await
|
|
725
|
+
const repoSummary = await git.github.json<GhRepoViewData>(
|
|
842
726
|
repoRoot,
|
|
843
727
|
["repo", "view", headRepository, "--json", GH_REPO_CLONE_FIELDS.join(",")],
|
|
844
728
|
signal,
|
|
845
729
|
{ repoProvided: true },
|
|
846
730
|
);
|
|
847
|
-
const originUrl = await
|
|
731
|
+
const originUrl = await git.remote.url(repoRoot, "origin", signal);
|
|
848
732
|
const remoteUrl = selectPrCloneUrl(originUrl, repoSummary);
|
|
849
733
|
if (!remoteUrl) {
|
|
850
734
|
throw new ToolError(`Could not determine a clone URL for ${headRepository}.`);
|
|
@@ -867,10 +751,7 @@ async function ensurePrRemote(
|
|
|
867
751
|
suffix += 1;
|
|
868
752
|
}
|
|
869
753
|
|
|
870
|
-
|
|
871
|
-
if (result.exitCode !== 0) {
|
|
872
|
-
throw new ToolError(formatGitFailure(["remote", "add", remoteName, remoteUrl], result));
|
|
873
|
-
}
|
|
754
|
+
await git.remote.add(repoRoot, remoteName, remoteUrl, signal);
|
|
874
755
|
|
|
875
756
|
return {
|
|
876
757
|
name: remoteName,
|
|
@@ -878,28 +759,6 @@ async function ensurePrRemote(
|
|
|
878
759
|
};
|
|
879
760
|
}
|
|
880
761
|
|
|
881
|
-
async function setBranchConfig(
|
|
882
|
-
repoRoot: string,
|
|
883
|
-
localBranch: string,
|
|
884
|
-
key: string,
|
|
885
|
-
value: string,
|
|
886
|
-
signal?: AbortSignal,
|
|
887
|
-
): Promise<void> {
|
|
888
|
-
const result = await runGitCommand(repoRoot, ["config", `branch.${localBranch}.${key}`, value], signal);
|
|
889
|
-
if (result.exitCode !== 0) {
|
|
890
|
-
throw new ToolError(formatGitFailure(["config", `branch.${localBranch}.${key}`, value], result));
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
async function getBranchConfig(
|
|
895
|
-
repoRoot: string,
|
|
896
|
-
localBranch: string,
|
|
897
|
-
key: string,
|
|
898
|
-
signal?: AbortSignal,
|
|
899
|
-
): Promise<string | undefined> {
|
|
900
|
-
return tryRunGitText(repoRoot, ["config", "--get", `branch.${localBranch}.${key}`], signal);
|
|
901
|
-
}
|
|
902
|
-
|
|
903
762
|
async function resolvePrBranchPushTarget(
|
|
904
763
|
repoRoot: string,
|
|
905
764
|
localBranch: string,
|
|
@@ -912,13 +771,18 @@ async function resolvePrBranchPushTarget(
|
|
|
912
771
|
maintainerCanModify?: boolean;
|
|
913
772
|
isCrossRepository: boolean;
|
|
914
773
|
}> {
|
|
915
|
-
const pushRemote = await
|
|
916
|
-
const remote = await
|
|
917
|
-
const mergeRef = await
|
|
918
|
-
const headRef = await
|
|
919
|
-
const prUrl = await
|
|
920
|
-
const maintainerCanModifyValue = await
|
|
921
|
-
|
|
774
|
+
const pushRemote = await git.config.getBranch(repoRoot, localBranch, "pushRemote", signal);
|
|
775
|
+
const remote = await git.config.getBranch(repoRoot, localBranch, "remote", signal);
|
|
776
|
+
const mergeRef = await git.config.getBranch(repoRoot, localBranch, "merge", signal);
|
|
777
|
+
const headRef = await git.config.getBranch(repoRoot, localBranch, "ompPrHeadRef", signal);
|
|
778
|
+
const prUrl = await git.config.getBranch(repoRoot, localBranch, "ompPrUrl", signal);
|
|
779
|
+
const maintainerCanModifyValue = await git.config.getBranch(
|
|
780
|
+
repoRoot,
|
|
781
|
+
localBranch,
|
|
782
|
+
"ompPrMaintainerCanModify",
|
|
783
|
+
signal,
|
|
784
|
+
);
|
|
785
|
+
const isCrossRepositoryValue = await git.config.getBranch(repoRoot, localBranch, "ompPrIsCrossRepository", signal);
|
|
922
786
|
|
|
923
787
|
const remoteName = pushRemote ?? remote;
|
|
924
788
|
if (!remoteName) {
|
|
@@ -933,7 +797,7 @@ async function resolvePrBranchPushTarget(
|
|
|
933
797
|
return {
|
|
934
798
|
remoteName,
|
|
935
799
|
remoteBranch,
|
|
936
|
-
remoteUrl: await
|
|
800
|
+
remoteUrl: await git.remote.url(repoRoot, remoteName, signal),
|
|
937
801
|
prUrl,
|
|
938
802
|
maintainerCanModify:
|
|
939
803
|
maintainerCanModifyValue === undefined
|
|
@@ -1085,6 +949,10 @@ function getRunCollectionOutcome(runs: GhRunSnapshot[]): "success" | "failure" |
|
|
|
1085
949
|
|
|
1086
950
|
let pending = false;
|
|
1087
951
|
for (const run of runs) {
|
|
952
|
+
if (run.jobs.some(isFailedJob)) {
|
|
953
|
+
return "failure";
|
|
954
|
+
}
|
|
955
|
+
|
|
1088
956
|
const outcome = getRunSnapshotOutcome(run);
|
|
1089
957
|
if (outcome === "failure") {
|
|
1090
958
|
return "failure";
|
|
@@ -1483,44 +1351,6 @@ function buildCommitRunWatchDetails(
|
|
|
1483
1351
|
};
|
|
1484
1352
|
}
|
|
1485
1353
|
|
|
1486
|
-
async function resolveCurrentGitBranch(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
1487
|
-
return untilAborted(signal, async () => {
|
|
1488
|
-
throwIfAborted(signal);
|
|
1489
|
-
const result = await $`git symbolic-ref --short HEAD`.cwd(cwd).quiet().nothrow();
|
|
1490
|
-
throwIfAborted(signal);
|
|
1491
|
-
|
|
1492
|
-
if (result.exitCode !== 0) {
|
|
1493
|
-
throw new ToolError("Current git branch is unavailable. Pass `branch` or `run` explicitly.");
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
const branch = normalizeOptionalString(result.text());
|
|
1497
|
-
if (!branch) {
|
|
1498
|
-
throw new ToolError("Current git branch is unavailable. Pass `branch` or `run` explicitly.");
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
return branch;
|
|
1502
|
-
});
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
async function resolveCurrentGitHead(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
1506
|
-
return untilAborted(signal, async () => {
|
|
1507
|
-
throwIfAborted(signal);
|
|
1508
|
-
const result = await $`git rev-parse HEAD`.cwd(cwd).quiet().nothrow();
|
|
1509
|
-
throwIfAborted(signal);
|
|
1510
|
-
|
|
1511
|
-
if (result.exitCode !== 0) {
|
|
1512
|
-
throw new ToolError("Current git HEAD is unavailable. Pass `run` explicitly.");
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
const headSha = normalizeOptionalString(result.text());
|
|
1516
|
-
if (!headSha) {
|
|
1517
|
-
throw new ToolError("Current git HEAD is unavailable. Pass `run` explicitly.");
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
return headSha;
|
|
1521
|
-
});
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
1354
|
async function resolveGitHubRepo(
|
|
1525
1355
|
cwd: string,
|
|
1526
1356
|
repo: string | undefined,
|
|
@@ -1539,7 +1369,11 @@ async function resolveGitHubRepo(
|
|
|
1539
1369
|
return runRepo;
|
|
1540
1370
|
}
|
|
1541
1371
|
|
|
1542
|
-
const resolved = await
|
|
1372
|
+
const resolved = await git.github.text(
|
|
1373
|
+
cwd,
|
|
1374
|
+
["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"],
|
|
1375
|
+
signal,
|
|
1376
|
+
);
|
|
1543
1377
|
return requireNonEmpty(resolved, "repo");
|
|
1544
1378
|
}
|
|
1545
1379
|
|
|
@@ -1549,7 +1383,7 @@ async function resolveGitHubBranchHead(
|
|
|
1549
1383
|
branch: string,
|
|
1550
1384
|
signal?: AbortSignal,
|
|
1551
1385
|
): Promise<string> {
|
|
1552
|
-
const response = await
|
|
1386
|
+
const response = await git.github.json<GhBranchApiResponse>(
|
|
1553
1387
|
cwd,
|
|
1554
1388
|
["api", "--method", "GET", `/repos/${repo}/branches/${encodeURIComponent(branch)}`],
|
|
1555
1389
|
signal,
|
|
@@ -1565,7 +1399,7 @@ async function fetchRunsForCommit(
|
|
|
1565
1399
|
branch: string | undefined,
|
|
1566
1400
|
signal?: AbortSignal,
|
|
1567
1401
|
): Promise<GhRunSnapshot[]> {
|
|
1568
|
-
const response = await
|
|
1402
|
+
const response = await git.github.json<GhActionsRunListResponse>(
|
|
1569
1403
|
cwd,
|
|
1570
1404
|
[
|
|
1571
1405
|
"api",
|
|
@@ -1602,7 +1436,7 @@ async function fetchRunJobs(
|
|
|
1602
1436
|
let page = 1;
|
|
1603
1437
|
|
|
1604
1438
|
while (true) {
|
|
1605
|
-
const response = await
|
|
1439
|
+
const response = await git.github.json<GhActionsJobsResponse>(
|
|
1606
1440
|
cwd,
|
|
1607
1441
|
[
|
|
1608
1442
|
"api",
|
|
@@ -1646,7 +1480,7 @@ async function fetchPrReviewComments(
|
|
|
1646
1480
|
let page = 1;
|
|
1647
1481
|
|
|
1648
1482
|
while (true) {
|
|
1649
|
-
const response = await
|
|
1483
|
+
const response = await git.github.json<GhPrReviewCommentApi[]>(
|
|
1650
1484
|
cwd,
|
|
1651
1485
|
[
|
|
1652
1486
|
"api",
|
|
@@ -1684,9 +1518,14 @@ async function fetchRunSnapshot(
|
|
|
1684
1518
|
signal?: AbortSignal,
|
|
1685
1519
|
): Promise<GhRunSnapshot> {
|
|
1686
1520
|
const [run, jobs] = await Promise.all([
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1521
|
+
git.github.json<GhActionsRunApi>(
|
|
1522
|
+
cwd,
|
|
1523
|
+
["api", "--method", "GET", `/repos/${repo}/actions/runs/${runId}`],
|
|
1524
|
+
signal,
|
|
1525
|
+
{
|
|
1526
|
+
repoProvided: true,
|
|
1527
|
+
},
|
|
1528
|
+
),
|
|
1690
1529
|
fetchRunJobs(cwd, repo, runId, signal),
|
|
1691
1530
|
]);
|
|
1692
1531
|
|
|
@@ -1712,7 +1551,7 @@ async function fetchFailedJobLogs(
|
|
|
1712
1551
|
): Promise<GhFailedJobLog[]> {
|
|
1713
1552
|
return Promise.all(
|
|
1714
1553
|
failedJobs.map(async entry => {
|
|
1715
|
-
const result = await
|
|
1554
|
+
const result = await git.github.run(cwd, ["api", `/repos/${repo}/actions/jobs/${entry.job.id}/logs`], signal);
|
|
1716
1555
|
const fullLog = result.exitCode === 0 ? normalizeBlock(result.stdout) : undefined;
|
|
1717
1556
|
const logTail = fullLog ? tailLogLines(fullLog, tail) : undefined;
|
|
1718
1557
|
return {
|
|
@@ -2070,7 +1909,7 @@ export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhTool
|
|
|
2070
1909
|
constructor(private readonly session: ToolSession) {}
|
|
2071
1910
|
|
|
2072
1911
|
static createIf(session: ToolSession): GhRepoViewTool | null {
|
|
2073
|
-
if (!
|
|
1912
|
+
if (!git.github.available()) return null;
|
|
2074
1913
|
return new GhRepoViewTool(session);
|
|
2075
1914
|
}
|
|
2076
1915
|
|
|
@@ -2093,7 +1932,9 @@ export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhTool
|
|
|
2093
1932
|
}
|
|
2094
1933
|
args.push("--json", GH_REPO_FIELDS.join(","));
|
|
2095
1934
|
|
|
2096
|
-
const data = await
|
|
1935
|
+
const data = await git.github.json<GhRepoViewData>(this.session.cwd, args, signal, {
|
|
1936
|
+
repoProvided: Boolean(repo),
|
|
1937
|
+
});
|
|
2097
1938
|
return buildTextResult(formatRepoView(data, { repo, branch }), data.url);
|
|
2098
1939
|
});
|
|
2099
1940
|
}
|
|
@@ -2109,7 +1950,7 @@ export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhTo
|
|
|
2109
1950
|
constructor(private readonly session: ToolSession) {}
|
|
2110
1951
|
|
|
2111
1952
|
static createIf(session: ToolSession): GhIssueViewTool | null {
|
|
2112
|
-
if (!
|
|
1953
|
+
if (!git.github.available()) return null;
|
|
2113
1954
|
return new GhIssueViewTool(session);
|
|
2114
1955
|
}
|
|
2115
1956
|
|
|
@@ -2128,7 +1969,9 @@ export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhTo
|
|
|
2128
1969
|
appendRepoFlag(args, repo, issue);
|
|
2129
1970
|
args.push("--json", (includeComments ? GH_ISSUE_FIELDS : GH_ISSUE_FIELDS_NO_COMMENTS).join(","));
|
|
2130
1971
|
|
|
2131
|
-
const data = await
|
|
1972
|
+
const data = await git.github.json<GhIssueViewData>(this.session.cwd, args, signal, {
|
|
1973
|
+
repoProvided: Boolean(repo),
|
|
1974
|
+
});
|
|
2132
1975
|
return buildTextResult(formatIssueView(data, { issue, repo, comments: includeComments }), data.url);
|
|
2133
1976
|
});
|
|
2134
1977
|
}
|
|
@@ -2144,7 +1987,7 @@ export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDeta
|
|
|
2144
1987
|
constructor(private readonly session: ToolSession) {}
|
|
2145
1988
|
|
|
2146
1989
|
static createIf(session: ToolSession): GhPrViewTool | null {
|
|
2147
|
-
if (!
|
|
1990
|
+
if (!git.github.available()) return null;
|
|
2148
1991
|
return new GhPrViewTool(session);
|
|
2149
1992
|
}
|
|
2150
1993
|
|
|
@@ -2166,7 +2009,9 @@ export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDeta
|
|
|
2166
2009
|
appendRepoFlag(args, repo, pr);
|
|
2167
2010
|
args.push("--json", (includeComments ? GH_PR_FIELDS : GH_PR_FIELDS_NO_COMMENTS).join(","));
|
|
2168
2011
|
|
|
2169
|
-
const data = await
|
|
2012
|
+
const data = await git.github.json<GhPrViewData>(this.session.cwd, args, signal, {
|
|
2013
|
+
repoProvided: Boolean(repo),
|
|
2014
|
+
});
|
|
2170
2015
|
const resolvedRepo = repo ?? parsePullRequestUrl(data.url).repo;
|
|
2171
2016
|
if (includeComments && resolvedRepo && typeof data.number === "number") {
|
|
2172
2017
|
data.reviewComments = await fetchPrReviewComments(this.session.cwd, resolvedRepo, data.number, signal);
|
|
@@ -2186,7 +2031,7 @@ export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDeta
|
|
|
2186
2031
|
constructor(private readonly session: ToolSession) {}
|
|
2187
2032
|
|
|
2188
2033
|
static createIf(session: ToolSession): GhPrDiffTool | null {
|
|
2189
|
-
if (!
|
|
2034
|
+
if (!git.github.available()) return null;
|
|
2190
2035
|
return new GhPrDiffTool(session);
|
|
2191
2036
|
}
|
|
2192
2037
|
|
|
@@ -2214,7 +2059,7 @@ export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDeta
|
|
|
2214
2059
|
args.push("--exclude", normalizedPattern);
|
|
2215
2060
|
}
|
|
2216
2061
|
|
|
2217
|
-
const output = await
|
|
2062
|
+
const output = await git.github.text(this.session.cwd, args, signal, {
|
|
2218
2063
|
repoProvided: Boolean(repo),
|
|
2219
2064
|
trimOutput: false,
|
|
2220
2065
|
});
|
|
@@ -2235,7 +2080,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2235
2080
|
constructor(private readonly session: ToolSession) {}
|
|
2236
2081
|
|
|
2237
2082
|
static createIf(session: ToolSession): GhPrCheckoutTool | null {
|
|
2238
|
-
if (!
|
|
2083
|
+
if (!git.github.available()) return null;
|
|
2239
2084
|
return new GhPrCheckoutTool(session);
|
|
2240
2085
|
}
|
|
2241
2086
|
|
|
@@ -2259,7 +2104,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2259
2104
|
appendRepoFlag(args, repo, pr);
|
|
2260
2105
|
args.push("--json", GH_PR_CHECKOUT_FIELDS.join(","));
|
|
2261
2106
|
|
|
2262
|
-
const data = await
|
|
2107
|
+
const data = await git.github.json<GhPrViewData>(this.session.cwd, args, signal, {
|
|
2263
2108
|
repoProvided: Boolean(repo),
|
|
2264
2109
|
});
|
|
2265
2110
|
const prNumber = data.number;
|
|
@@ -2269,68 +2114,56 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2269
2114
|
|
|
2270
2115
|
const headRefName = requireNonEmpty(data.headRefName, "head branch");
|
|
2271
2116
|
const headRefOid = requireNonEmpty(data.headRefOid, "head commit");
|
|
2272
|
-
const repoRoot = await
|
|
2273
|
-
const primaryRepoRoot = await
|
|
2117
|
+
const repoRoot = await requireGitRepoRoot(this.session.cwd, signal);
|
|
2118
|
+
const primaryRepoRoot = await requirePrimaryGitRepoRoot(repoRoot, signal);
|
|
2274
2119
|
const localBranch = requestedBranch ?? `pr-${prNumber}`;
|
|
2275
2120
|
const worktreePath = requestedWorktree
|
|
2276
2121
|
? path.resolve(this.session.cwd, requestedWorktree)
|
|
2277
2122
|
: path.join(primaryRepoRoot, ".worktrees", localBranch);
|
|
2278
|
-
const existingWorktrees = await
|
|
2123
|
+
const existingWorktrees = await git.worktree.list(repoRoot, signal);
|
|
2279
2124
|
const existingWorktree = existingWorktrees.find(entry => entry.branch === toLocalBranchRef(localBranch));
|
|
2280
2125
|
|
|
2281
2126
|
const remote = await ensurePrRemote(repoRoot, data, signal);
|
|
2282
|
-
await
|
|
2127
|
+
await git.fetch(
|
|
2283
2128
|
repoRoot,
|
|
2284
|
-
|
|
2129
|
+
remote.name,
|
|
2130
|
+
`refs/heads/${headRefName}`,
|
|
2131
|
+
`refs/remotes/${remote.name}/${headRefName}`,
|
|
2285
2132
|
signal,
|
|
2286
2133
|
);
|
|
2287
2134
|
|
|
2288
2135
|
if (!existingWorktree) {
|
|
2289
2136
|
const localBranchRef = toLocalBranchRef(localBranch);
|
|
2290
|
-
const localBranchExists = await
|
|
2137
|
+
const localBranchExists = await git.ref.exists(repoRoot, localBranchRef, signal);
|
|
2291
2138
|
if (localBranchExists) {
|
|
2292
|
-
const existingOid = await
|
|
2139
|
+
const existingOid = await git.ref.resolve(repoRoot, localBranchRef, signal);
|
|
2293
2140
|
if (existingOid !== headRefOid) {
|
|
2294
2141
|
if (!force) {
|
|
2295
2142
|
throw new ToolError(
|
|
2296
|
-
`local branch ${localBranch} already exists at ${formatShortSha(existingOid) ?? existingOid}; pass force=true to reset it`,
|
|
2143
|
+
`local branch ${localBranch} already exists at ${formatShortSha(existingOid ?? undefined) ?? existingOid ?? "unknown commit"}; pass force=true to reset it`,
|
|
2297
2144
|
);
|
|
2298
2145
|
}
|
|
2299
2146
|
|
|
2300
|
-
|
|
2301
|
-
repoRoot,
|
|
2302
|
-
["branch", "--force", localBranch, `refs/remotes/${remote.name}/${headRefName}`],
|
|
2303
|
-
signal,
|
|
2304
|
-
);
|
|
2305
|
-
if (resetResult.exitCode !== 0) {
|
|
2306
|
-
throw new ToolError(formatGitFailure(["branch", "--force", localBranch], resetResult));
|
|
2307
|
-
}
|
|
2147
|
+
await git.branch.force(repoRoot, localBranch, `refs/remotes/${remote.name}/${headRefName}`, signal);
|
|
2308
2148
|
}
|
|
2309
2149
|
} else {
|
|
2310
|
-
|
|
2311
|
-
repoRoot,
|
|
2312
|
-
["branch", localBranch, `refs/remotes/${remote.name}/${headRefName}`],
|
|
2313
|
-
signal,
|
|
2314
|
-
);
|
|
2315
|
-
if (createResult.exitCode !== 0) {
|
|
2316
|
-
throw new ToolError(formatGitFailure(["branch", localBranch], createResult));
|
|
2317
|
-
}
|
|
2150
|
+
await git.branch.create(repoRoot, localBranch, `refs/remotes/${remote.name}/${headRefName}`, signal);
|
|
2318
2151
|
}
|
|
2319
2152
|
}
|
|
2320
2153
|
|
|
2321
|
-
await
|
|
2322
|
-
await
|
|
2323
|
-
await
|
|
2324
|
-
await
|
|
2325
|
-
await
|
|
2326
|
-
await
|
|
2154
|
+
await git.config.setBranch(repoRoot, localBranch, "remote", remote.name, signal);
|
|
2155
|
+
await git.config.setBranch(repoRoot, localBranch, "merge", `refs/heads/${headRefName}`, signal);
|
|
2156
|
+
await git.config.setBranch(repoRoot, localBranch, "pushRemote", remote.name, signal);
|
|
2157
|
+
await git.config.setBranch(repoRoot, localBranch, "ompPrHeadRef", headRefName, signal);
|
|
2158
|
+
await git.config.setBranch(repoRoot, localBranch, "ompPrUrl", data.url ?? "", signal);
|
|
2159
|
+
await git.config.setBranch(
|
|
2327
2160
|
repoRoot,
|
|
2328
2161
|
localBranch,
|
|
2329
2162
|
"ompPrIsCrossRepository",
|
|
2330
2163
|
String(Boolean(data.isCrossRepository)),
|
|
2331
2164
|
signal,
|
|
2332
2165
|
);
|
|
2333
|
-
await
|
|
2166
|
+
await git.config.setBranch(
|
|
2334
2167
|
repoRoot,
|
|
2335
2168
|
localBranch,
|
|
2336
2169
|
"ompPrMaintainerCanModify",
|
|
@@ -2342,21 +2175,15 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2342
2175
|
if (!existingWorktree) {
|
|
2343
2176
|
await ensureGitWorktreePathAvailable(finalWorktreePath, existingWorktrees);
|
|
2344
2177
|
await fs.mkdir(path.dirname(finalWorktreePath), { recursive: true });
|
|
2345
|
-
|
|
2346
|
-
repoRoot,
|
|
2347
|
-
["worktree", "add", finalWorktreePath, localBranch],
|
|
2348
|
-
signal,
|
|
2349
|
-
);
|
|
2350
|
-
if (addResult.exitCode !== 0) {
|
|
2351
|
-
throw new ToolError(formatGitFailure(["worktree", "add", finalWorktreePath, localBranch], addResult));
|
|
2352
|
-
}
|
|
2178
|
+
await git.worktree.add(repoRoot, finalWorktreePath, localBranch, { signal });
|
|
2353
2179
|
}
|
|
2180
|
+
const resolvedWorktreePath = await fs.realpath(finalWorktreePath);
|
|
2354
2181
|
|
|
2355
2182
|
return buildTextResult(
|
|
2356
2183
|
formatPrCheckoutResult({
|
|
2357
2184
|
data,
|
|
2358
2185
|
localBranch,
|
|
2359
|
-
worktreePath:
|
|
2186
|
+
worktreePath: resolvedWorktreePath,
|
|
2360
2187
|
remoteName: remote.name,
|
|
2361
2188
|
remoteUrl: remote.url,
|
|
2362
2189
|
reused: Boolean(existingWorktree),
|
|
@@ -2365,7 +2192,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2365
2192
|
{
|
|
2366
2193
|
repo: repo ?? data.headRepository?.nameWithOwner,
|
|
2367
2194
|
branch: localBranch,
|
|
2368
|
-
worktreePath:
|
|
2195
|
+
worktreePath: resolvedWorktreePath,
|
|
2369
2196
|
remote: remote.name,
|
|
2370
2197
|
remoteBranch: headRefName,
|
|
2371
2198
|
},
|
|
@@ -2384,7 +2211,7 @@ export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDeta
|
|
|
2384
2211
|
constructor(private readonly session: ToolSession) {}
|
|
2385
2212
|
|
|
2386
2213
|
static createIf(session: ToolSession): GhPrPushTool | null {
|
|
2387
|
-
if (!
|
|
2214
|
+
if (!git.github.available()) return null;
|
|
2388
2215
|
return new GhPrPushTool(session);
|
|
2389
2216
|
}
|
|
2390
2217
|
|
|
@@ -2396,28 +2223,24 @@ export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDeta
|
|
|
2396
2223
|
_context?: AgentToolContext,
|
|
2397
2224
|
): Promise<AgentToolResult<GhToolDetails>> {
|
|
2398
2225
|
return untilAborted(signal, async () => {
|
|
2399
|
-
const repoRoot = await
|
|
2226
|
+
const repoRoot = await requireGitRepoRoot(this.session.cwd, signal);
|
|
2400
2227
|
const localBranch =
|
|
2401
|
-
normalizeOptionalString(params.branch) ?? (await
|
|
2402
|
-
const refExists = await
|
|
2228
|
+
normalizeOptionalString(params.branch) ?? (await requireCurrentGitBranch(repoRoot, signal));
|
|
2229
|
+
const refExists = await git.ref.exists(repoRoot, toLocalBranchRef(localBranch), signal);
|
|
2403
2230
|
if (!refExists) {
|
|
2404
2231
|
throw new ToolError(`local branch ${localBranch} does not exist`);
|
|
2405
2232
|
}
|
|
2406
2233
|
|
|
2407
2234
|
const target = await resolvePrBranchPushTarget(repoRoot, localBranch, signal);
|
|
2408
|
-
const currentBranch = await
|
|
2235
|
+
const currentBranch = await git.branch.current(repoRoot, signal);
|
|
2409
2236
|
const sourceRef = currentBranch === localBranch ? "HEAD" : toLocalBranchRef(localBranch);
|
|
2410
2237
|
const refspec = `${sourceRef}:refs/heads/${target.remoteBranch}`;
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
const pushResult = await runGitCommand(repoRoot, pushArgs, signal);
|
|
2418
|
-
if (pushResult.exitCode !== 0) {
|
|
2419
|
-
throw new ToolError(formatGitFailure(pushArgs, pushResult));
|
|
2420
|
-
}
|
|
2238
|
+
await git.push(repoRoot, {
|
|
2239
|
+
forceWithLease: params.forceWithLease,
|
|
2240
|
+
refspec,
|
|
2241
|
+
remote: target.remoteName,
|
|
2242
|
+
signal,
|
|
2243
|
+
});
|
|
2421
2244
|
|
|
2422
2245
|
return buildTextResult(
|
|
2423
2246
|
formatPrPushResult({
|
|
@@ -2449,7 +2272,7 @@ export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema
|
|
|
2449
2272
|
constructor(private readonly session: ToolSession) {}
|
|
2450
2273
|
|
|
2451
2274
|
static createIf(session: ToolSession): GhSearchIssuesTool | null {
|
|
2452
|
-
if (!
|
|
2275
|
+
if (!git.github.available()) return null;
|
|
2453
2276
|
return new GhSearchIssuesTool(session);
|
|
2454
2277
|
}
|
|
2455
2278
|
|
|
@@ -2466,7 +2289,7 @@ export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema
|
|
|
2466
2289
|
const limit = resolveSearchLimit(params.limit);
|
|
2467
2290
|
const args = buildGhSearchArgs("issues", query, limit, repo);
|
|
2468
2291
|
|
|
2469
|
-
const items = await
|
|
2292
|
+
const items = await git.github.json<GhSearchResult[]>(this.session.cwd, args, signal, {
|
|
2470
2293
|
repoProvided: Boolean(repo),
|
|
2471
2294
|
});
|
|
2472
2295
|
return buildTextResult(formatSearchResults("issues", query, repo, items));
|
|
@@ -2484,7 +2307,7 @@ export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhTo
|
|
|
2484
2307
|
constructor(private readonly session: ToolSession) {}
|
|
2485
2308
|
|
|
2486
2309
|
static createIf(session: ToolSession): GhSearchPrsTool | null {
|
|
2487
|
-
if (!
|
|
2310
|
+
if (!git.github.available()) return null;
|
|
2488
2311
|
return new GhSearchPrsTool(session);
|
|
2489
2312
|
}
|
|
2490
2313
|
|
|
@@ -2501,7 +2324,7 @@ export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhTo
|
|
|
2501
2324
|
const limit = resolveSearchLimit(params.limit);
|
|
2502
2325
|
const args = buildGhSearchArgs("prs", query, limit, repo);
|
|
2503
2326
|
|
|
2504
|
-
const items = await
|
|
2327
|
+
const items = await git.github.json<GhSearchResult[]>(this.session.cwd, args, signal, {
|
|
2505
2328
|
repoProvided: Boolean(repo),
|
|
2506
2329
|
});
|
|
2507
2330
|
return buildTextResult(formatSearchResults("pull requests", query, repo, items));
|
|
@@ -2519,7 +2342,7 @@ export class GhRunWatchTool implements AgentTool<typeof ghRunWatchSchema, GhTool
|
|
|
2519
2342
|
constructor(private readonly session: ToolSession) {}
|
|
2520
2343
|
|
|
2521
2344
|
static createIf(session: ToolSession): GhRunWatchTool | null {
|
|
2522
|
-
if (!
|
|
2345
|
+
if (!git.github.available()) return null;
|
|
2523
2346
|
return new GhRunWatchTool(session);
|
|
2524
2347
|
}
|
|
2525
2348
|
|
|
@@ -2613,10 +2436,10 @@ export class GhRunWatchTool implements AgentTool<typeof ghRunWatchSchema, GhTool
|
|
|
2613
2436
|
}
|
|
2614
2437
|
}
|
|
2615
2438
|
|
|
2616
|
-
const branch = branchInput ?? (await
|
|
2439
|
+
const branch = branchInput ?? (await requireCurrentGitBranch(this.session.cwd, signal));
|
|
2617
2440
|
const headSha = branchInput
|
|
2618
2441
|
? await resolveGitHubBranchHead(this.session.cwd, repo, branch, signal)
|
|
2619
|
-
: await
|
|
2442
|
+
: await requireCurrentGitHead(this.session.cwd, signal);
|
|
2620
2443
|
let pollCount = 0;
|
|
2621
2444
|
let settledSuccessSignature: string | undefined;
|
|
2622
2445
|
|