@oh-my-pi/pi-coding-agent 13.18.0 → 14.0.2
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 +316 -1
- package/package.json +86 -24
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +116 -30
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +123 -178
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -8
- package/src/commit/agentic/index.ts +22 -26
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- 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/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/changelog/index.ts +5 -9
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +13 -16
- package/src/config/keybindings.ts +7 -6
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +98 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +6 -18
- package/src/extensibility/custom-commands/bundled/review/index.ts +45 -43
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +4 -2
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
- package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +125 -47
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- 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/read-tool-group.ts +6 -12
- package/src/modes/components/session-observer-overlay.ts +472 -0
- package/src/modes/components/settings-defs.ts +24 -0
- package/src/modes/components/status-line.ts +15 -61
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +4 -2
- 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 +15 -8
- package/src/modes/controllers/selector-controller.ts +26 -0
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +278 -69
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +0 -42
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +4 -2
- 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 +8 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +216 -165
- package/src/secrets/index.ts +1 -1
- package/src/secrets/obfuscator.ts +25 -17
- package/src/session/agent-session.ts +381 -286
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +15 -5
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +25 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +32 -4
- package/src/task/index.ts +91 -82
- package/src/task/template.ts +2 -2
- package/src/task/types.ts +25 -0
- package/src/task/worktree.ts +131 -149
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +3 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +130 -308
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +211 -146
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/external-editor.ts +11 -5
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +1400 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/web/search/render.ts +6 -4
- package/src/autoresearch/command-initialize.md +0 -34
- 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/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/gh-cli.ts +0 -125
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
package/src/tools/gh.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
|
-
import { abortableSleep, isEnoent, untilAborted } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { abortableSleep, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { type Static, Type } from "@sinclair/typebox";
|
|
6
|
-
import { $ } from "bun";
|
|
7
|
-
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
8
6
|
import ghIssueViewDescription from "../prompts/tools/gh-issue-view.md" with { type: "text" };
|
|
9
7
|
import ghPrCheckoutDescription from "../prompts/tools/gh-pr-checkout.md" with { type: "text" };
|
|
10
8
|
import ghPrDiffDescription from "../prompts/tools/gh-pr-diff.md" with { type: "text" };
|
|
@@ -14,8 +12,8 @@ import ghRepoViewDescription from "../prompts/tools/gh-repo-view.md" with { type
|
|
|
14
12
|
import ghRunWatchDescription from "../prompts/tools/gh-run-watch.md" with { type: "text" };
|
|
15
13
|
import ghSearchIssuesDescription from "../prompts/tools/gh-search-issues.md" with { type: "text" };
|
|
16
14
|
import ghSearchPrsDescription from "../prompts/tools/gh-search-prs.md" with { type: "text" };
|
|
15
|
+
import * as git from "../utils/git";
|
|
17
16
|
import type { ToolSession } from ".";
|
|
18
|
-
import { isGhAvailable, runGhCommand, runGhJson, runGhText } from "./gh-cli";
|
|
19
17
|
import type { OutputMeta } from "./output-meta";
|
|
20
18
|
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
21
19
|
import { toolResult } from "./tool-result";
|
|
@@ -401,19 +399,6 @@ interface GhPrViewData extends GhIssueViewData {
|
|
|
401
399
|
reviewDecision?: string;
|
|
402
400
|
}
|
|
403
401
|
|
|
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
402
|
interface GhPrReviewCommit {
|
|
418
403
|
oid?: string | null;
|
|
419
404
|
}
|
|
@@ -641,142 +626,45 @@ function stripHeadsRef(value: string | undefined): string | undefined {
|
|
|
641
626
|
return value.startsWith("refs/heads/") ? value.slice("refs/heads/".length) : value;
|
|
642
627
|
}
|
|
643
628
|
|
|
644
|
-
function
|
|
645
|
-
const
|
|
646
|
-
if (
|
|
647
|
-
|
|
629
|
+
async function requireGitRepoRoot(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
630
|
+
const repoRoot = await git.repo.root(cwd, signal);
|
|
631
|
+
if (!repoRoot) {
|
|
632
|
+
throw new ToolError("Current git repository is unavailable.");
|
|
648
633
|
}
|
|
649
634
|
|
|
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
|
-
});
|
|
635
|
+
return repoRoot;
|
|
683
636
|
}
|
|
684
637
|
|
|
685
|
-
async function
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (!text) {
|
|
690
|
-
throw new ToolError(`git ${args.join(" ")} returned empty output.`);
|
|
638
|
+
async function requirePrimaryGitRepoRoot(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
639
|
+
const primaryRepoRoot = await git.repo.primaryRoot(cwd, signal);
|
|
640
|
+
if (!primaryRepoRoot) {
|
|
641
|
+
throw new ToolError("Current git repository is unavailable.");
|
|
691
642
|
}
|
|
692
643
|
|
|
693
|
-
return
|
|
644
|
+
return primaryRepoRoot;
|
|
694
645
|
}
|
|
695
646
|
|
|
696
|
-
async function
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
699
|
-
throw new ToolError(
|
|
647
|
+
async function requireCurrentGitBranch(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
648
|
+
const branch = await git.branch.current(cwd, signal);
|
|
649
|
+
if (!branch) {
|
|
650
|
+
throw new ToolError("Current git branch is unavailable. Pass `branch` or `run` explicitly.");
|
|
700
651
|
}
|
|
701
652
|
|
|
702
|
-
return
|
|
653
|
+
return branch;
|
|
703
654
|
}
|
|
704
655
|
|
|
705
|
-
async function
|
|
706
|
-
const
|
|
707
|
-
if (
|
|
708
|
-
|
|
656
|
+
async function requireCurrentGitHead(cwd: string, signal?: AbortSignal): Promise<string> {
|
|
657
|
+
const headSha = await git.head.sha(cwd, signal);
|
|
658
|
+
if (!headSha) {
|
|
659
|
+
throw new ToolError("Current git HEAD is unavailable. Pass `run` explicitly.");
|
|
709
660
|
}
|
|
710
661
|
|
|
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;
|
|
662
|
+
return headSha;
|
|
775
663
|
}
|
|
776
664
|
|
|
777
665
|
async function ensureGitWorktreePathAvailable(
|
|
778
666
|
worktreePath: string,
|
|
779
|
-
existingWorktrees: GitWorktreeEntry[],
|
|
667
|
+
existingWorktrees: git.GitWorktreeEntry[],
|
|
780
668
|
): Promise<void> {
|
|
781
669
|
const normalizedTarget = path.resolve(worktreePath);
|
|
782
670
|
const conflictingWorktree = existingWorktrees.find(entry => path.resolve(entry.path) === normalizedTarget);
|
|
@@ -804,15 +692,10 @@ function selectPrCloneUrl(originUrl: string | undefined, repo: Pick<GhRepoViewDa
|
|
|
804
692
|
}
|
|
805
693
|
|
|
806
694
|
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) ?? [];
|
|
695
|
+
const remotes = await git.remote.list(repoRoot, signal);
|
|
813
696
|
const urls = new Map<string, string>();
|
|
814
697
|
for (const remoteName of remotes) {
|
|
815
|
-
const remoteUrl = await
|
|
698
|
+
const remoteUrl = await git.remote.url(repoRoot, remoteName, signal);
|
|
816
699
|
if (remoteUrl) {
|
|
817
700
|
urls.set(remoteName, remoteUrl);
|
|
818
701
|
}
|
|
@@ -826,7 +709,7 @@ async function ensurePrRemote(
|
|
|
826
709
|
signal?: AbortSignal,
|
|
827
710
|
): Promise<{ name: string; url: string }> {
|
|
828
711
|
if (!data.isCrossRepository) {
|
|
829
|
-
const originUrl =
|
|
712
|
+
const originUrl = await git.remote.url(repoRoot, "origin", signal);
|
|
830
713
|
if (!originUrl) {
|
|
831
714
|
throw new ToolError("origin remote is unavailable for this repository.");
|
|
832
715
|
}
|
|
@@ -838,13 +721,13 @@ async function ensurePrRemote(
|
|
|
838
721
|
}
|
|
839
722
|
|
|
840
723
|
const headRepository = requireNonEmpty(data.headRepository?.nameWithOwner, "head repository");
|
|
841
|
-
const repoSummary = await
|
|
724
|
+
const repoSummary = await git.github.json<GhRepoViewData>(
|
|
842
725
|
repoRoot,
|
|
843
726
|
["repo", "view", headRepository, "--json", GH_REPO_CLONE_FIELDS.join(",")],
|
|
844
727
|
signal,
|
|
845
728
|
{ repoProvided: true },
|
|
846
729
|
);
|
|
847
|
-
const originUrl = await
|
|
730
|
+
const originUrl = await git.remote.url(repoRoot, "origin", signal);
|
|
848
731
|
const remoteUrl = selectPrCloneUrl(originUrl, repoSummary);
|
|
849
732
|
if (!remoteUrl) {
|
|
850
733
|
throw new ToolError(`Could not determine a clone URL for ${headRepository}.`);
|
|
@@ -867,10 +750,7 @@ async function ensurePrRemote(
|
|
|
867
750
|
suffix += 1;
|
|
868
751
|
}
|
|
869
752
|
|
|
870
|
-
|
|
871
|
-
if (result.exitCode !== 0) {
|
|
872
|
-
throw new ToolError(formatGitFailure(["remote", "add", remoteName, remoteUrl], result));
|
|
873
|
-
}
|
|
753
|
+
await git.remote.add(repoRoot, remoteName, remoteUrl, signal);
|
|
874
754
|
|
|
875
755
|
return {
|
|
876
756
|
name: remoteName,
|
|
@@ -878,28 +758,6 @@ async function ensurePrRemote(
|
|
|
878
758
|
};
|
|
879
759
|
}
|
|
880
760
|
|
|
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
761
|
async function resolvePrBranchPushTarget(
|
|
904
762
|
repoRoot: string,
|
|
905
763
|
localBranch: string,
|
|
@@ -912,13 +770,18 @@ async function resolvePrBranchPushTarget(
|
|
|
912
770
|
maintainerCanModify?: boolean;
|
|
913
771
|
isCrossRepository: boolean;
|
|
914
772
|
}> {
|
|
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
|
-
|
|
773
|
+
const pushRemote = await git.config.getBranch(repoRoot, localBranch, "pushRemote", signal);
|
|
774
|
+
const remote = await git.config.getBranch(repoRoot, localBranch, "remote", signal);
|
|
775
|
+
const mergeRef = await git.config.getBranch(repoRoot, localBranch, "merge", signal);
|
|
776
|
+
const headRef = await git.config.getBranch(repoRoot, localBranch, "ompPrHeadRef", signal);
|
|
777
|
+
const prUrl = await git.config.getBranch(repoRoot, localBranch, "ompPrUrl", signal);
|
|
778
|
+
const maintainerCanModifyValue = await git.config.getBranch(
|
|
779
|
+
repoRoot,
|
|
780
|
+
localBranch,
|
|
781
|
+
"ompPrMaintainerCanModify",
|
|
782
|
+
signal,
|
|
783
|
+
);
|
|
784
|
+
const isCrossRepositoryValue = await git.config.getBranch(repoRoot, localBranch, "ompPrIsCrossRepository", signal);
|
|
922
785
|
|
|
923
786
|
const remoteName = pushRemote ?? remote;
|
|
924
787
|
if (!remoteName) {
|
|
@@ -933,7 +796,7 @@ async function resolvePrBranchPushTarget(
|
|
|
933
796
|
return {
|
|
934
797
|
remoteName,
|
|
935
798
|
remoteBranch,
|
|
936
|
-
remoteUrl: await
|
|
799
|
+
remoteUrl: await git.remote.url(repoRoot, remoteName, signal),
|
|
937
800
|
prUrl,
|
|
938
801
|
maintainerCanModify:
|
|
939
802
|
maintainerCanModifyValue === undefined
|
|
@@ -1085,6 +948,10 @@ function getRunCollectionOutcome(runs: GhRunSnapshot[]): "success" | "failure" |
|
|
|
1085
948
|
|
|
1086
949
|
let pending = false;
|
|
1087
950
|
for (const run of runs) {
|
|
951
|
+
if (run.jobs.some(isFailedJob)) {
|
|
952
|
+
return "failure";
|
|
953
|
+
}
|
|
954
|
+
|
|
1088
955
|
const outcome = getRunSnapshotOutcome(run);
|
|
1089
956
|
if (outcome === "failure") {
|
|
1090
957
|
return "failure";
|
|
@@ -1483,44 +1350,6 @@ function buildCommitRunWatchDetails(
|
|
|
1483
1350
|
};
|
|
1484
1351
|
}
|
|
1485
1352
|
|
|
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
1353
|
async function resolveGitHubRepo(
|
|
1525
1354
|
cwd: string,
|
|
1526
1355
|
repo: string | undefined,
|
|
@@ -1539,7 +1368,11 @@ async function resolveGitHubRepo(
|
|
|
1539
1368
|
return runRepo;
|
|
1540
1369
|
}
|
|
1541
1370
|
|
|
1542
|
-
const resolved = await
|
|
1371
|
+
const resolved = await git.github.text(
|
|
1372
|
+
cwd,
|
|
1373
|
+
["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"],
|
|
1374
|
+
signal,
|
|
1375
|
+
);
|
|
1543
1376
|
return requireNonEmpty(resolved, "repo");
|
|
1544
1377
|
}
|
|
1545
1378
|
|
|
@@ -1549,7 +1382,7 @@ async function resolveGitHubBranchHead(
|
|
|
1549
1382
|
branch: string,
|
|
1550
1383
|
signal?: AbortSignal,
|
|
1551
1384
|
): Promise<string> {
|
|
1552
|
-
const response = await
|
|
1385
|
+
const response = await git.github.json<GhBranchApiResponse>(
|
|
1553
1386
|
cwd,
|
|
1554
1387
|
["api", "--method", "GET", `/repos/${repo}/branches/${encodeURIComponent(branch)}`],
|
|
1555
1388
|
signal,
|
|
@@ -1565,7 +1398,7 @@ async function fetchRunsForCommit(
|
|
|
1565
1398
|
branch: string | undefined,
|
|
1566
1399
|
signal?: AbortSignal,
|
|
1567
1400
|
): Promise<GhRunSnapshot[]> {
|
|
1568
|
-
const response = await
|
|
1401
|
+
const response = await git.github.json<GhActionsRunListResponse>(
|
|
1569
1402
|
cwd,
|
|
1570
1403
|
[
|
|
1571
1404
|
"api",
|
|
@@ -1602,7 +1435,7 @@ async function fetchRunJobs(
|
|
|
1602
1435
|
let page = 1;
|
|
1603
1436
|
|
|
1604
1437
|
while (true) {
|
|
1605
|
-
const response = await
|
|
1438
|
+
const response = await git.github.json<GhActionsJobsResponse>(
|
|
1606
1439
|
cwd,
|
|
1607
1440
|
[
|
|
1608
1441
|
"api",
|
|
@@ -1646,7 +1479,7 @@ async function fetchPrReviewComments(
|
|
|
1646
1479
|
let page = 1;
|
|
1647
1480
|
|
|
1648
1481
|
while (true) {
|
|
1649
|
-
const response = await
|
|
1482
|
+
const response = await git.github.json<GhPrReviewCommentApi[]>(
|
|
1650
1483
|
cwd,
|
|
1651
1484
|
[
|
|
1652
1485
|
"api",
|
|
@@ -1684,9 +1517,14 @@ async function fetchRunSnapshot(
|
|
|
1684
1517
|
signal?: AbortSignal,
|
|
1685
1518
|
): Promise<GhRunSnapshot> {
|
|
1686
1519
|
const [run, jobs] = await Promise.all([
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1520
|
+
git.github.json<GhActionsRunApi>(
|
|
1521
|
+
cwd,
|
|
1522
|
+
["api", "--method", "GET", `/repos/${repo}/actions/runs/${runId}`],
|
|
1523
|
+
signal,
|
|
1524
|
+
{
|
|
1525
|
+
repoProvided: true,
|
|
1526
|
+
},
|
|
1527
|
+
),
|
|
1690
1528
|
fetchRunJobs(cwd, repo, runId, signal),
|
|
1691
1529
|
]);
|
|
1692
1530
|
|
|
@@ -1712,7 +1550,7 @@ async function fetchFailedJobLogs(
|
|
|
1712
1550
|
): Promise<GhFailedJobLog[]> {
|
|
1713
1551
|
return Promise.all(
|
|
1714
1552
|
failedJobs.map(async entry => {
|
|
1715
|
-
const result = await
|
|
1553
|
+
const result = await git.github.run(cwd, ["api", `/repos/${repo}/actions/jobs/${entry.job.id}/logs`], signal);
|
|
1716
1554
|
const fullLog = result.exitCode === 0 ? normalizeBlock(result.stdout) : undefined;
|
|
1717
1555
|
const logTail = fullLog ? tailLogLines(fullLog, tail) : undefined;
|
|
1718
1556
|
return {
|
|
@@ -2063,14 +1901,14 @@ function buildTextResult(
|
|
|
2063
1901
|
export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhToolDetails> {
|
|
2064
1902
|
readonly name = "gh_repo_view";
|
|
2065
1903
|
readonly label = "GitHub Repo";
|
|
2066
|
-
readonly description =
|
|
1904
|
+
readonly description = prompt.render(ghRepoViewDescription);
|
|
2067
1905
|
readonly parameters = ghRepoViewSchema;
|
|
2068
1906
|
readonly strict = true;
|
|
2069
1907
|
|
|
2070
1908
|
constructor(private readonly session: ToolSession) {}
|
|
2071
1909
|
|
|
2072
1910
|
static createIf(session: ToolSession): GhRepoViewTool | null {
|
|
2073
|
-
if (!
|
|
1911
|
+
if (!git.github.available()) return null;
|
|
2074
1912
|
return new GhRepoViewTool(session);
|
|
2075
1913
|
}
|
|
2076
1914
|
|
|
@@ -2093,7 +1931,9 @@ export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhTool
|
|
|
2093
1931
|
}
|
|
2094
1932
|
args.push("--json", GH_REPO_FIELDS.join(","));
|
|
2095
1933
|
|
|
2096
|
-
const data = await
|
|
1934
|
+
const data = await git.github.json<GhRepoViewData>(this.session.cwd, args, signal, {
|
|
1935
|
+
repoProvided: Boolean(repo),
|
|
1936
|
+
});
|
|
2097
1937
|
return buildTextResult(formatRepoView(data, { repo, branch }), data.url);
|
|
2098
1938
|
});
|
|
2099
1939
|
}
|
|
@@ -2102,14 +1942,14 @@ export class GhRepoViewTool implements AgentTool<typeof ghRepoViewSchema, GhTool
|
|
|
2102
1942
|
export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhToolDetails> {
|
|
2103
1943
|
readonly name = "gh_issue_view";
|
|
2104
1944
|
readonly label = "GitHub Issue";
|
|
2105
|
-
readonly description =
|
|
1945
|
+
readonly description = prompt.render(ghIssueViewDescription);
|
|
2106
1946
|
readonly parameters = ghIssueViewSchema;
|
|
2107
1947
|
readonly strict = true;
|
|
2108
1948
|
|
|
2109
1949
|
constructor(private readonly session: ToolSession) {}
|
|
2110
1950
|
|
|
2111
1951
|
static createIf(session: ToolSession): GhIssueViewTool | null {
|
|
2112
|
-
if (!
|
|
1952
|
+
if (!git.github.available()) return null;
|
|
2113
1953
|
return new GhIssueViewTool(session);
|
|
2114
1954
|
}
|
|
2115
1955
|
|
|
@@ -2128,7 +1968,9 @@ export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhTo
|
|
|
2128
1968
|
appendRepoFlag(args, repo, issue);
|
|
2129
1969
|
args.push("--json", (includeComments ? GH_ISSUE_FIELDS : GH_ISSUE_FIELDS_NO_COMMENTS).join(","));
|
|
2130
1970
|
|
|
2131
|
-
const data = await
|
|
1971
|
+
const data = await git.github.json<GhIssueViewData>(this.session.cwd, args, signal, {
|
|
1972
|
+
repoProvided: Boolean(repo),
|
|
1973
|
+
});
|
|
2132
1974
|
return buildTextResult(formatIssueView(data, { issue, repo, comments: includeComments }), data.url);
|
|
2133
1975
|
});
|
|
2134
1976
|
}
|
|
@@ -2137,14 +1979,14 @@ export class GhIssueViewTool implements AgentTool<typeof ghIssueViewSchema, GhTo
|
|
|
2137
1979
|
export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDetails> {
|
|
2138
1980
|
readonly name = "gh_pr_view";
|
|
2139
1981
|
readonly label = "GitHub PR";
|
|
2140
|
-
readonly description =
|
|
1982
|
+
readonly description = prompt.render(ghPrViewDescription);
|
|
2141
1983
|
readonly parameters = ghPrViewSchema;
|
|
2142
1984
|
readonly strict = true;
|
|
2143
1985
|
|
|
2144
1986
|
constructor(private readonly session: ToolSession) {}
|
|
2145
1987
|
|
|
2146
1988
|
static createIf(session: ToolSession): GhPrViewTool | null {
|
|
2147
|
-
if (!
|
|
1989
|
+
if (!git.github.available()) return null;
|
|
2148
1990
|
return new GhPrViewTool(session);
|
|
2149
1991
|
}
|
|
2150
1992
|
|
|
@@ -2166,7 +2008,9 @@ export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDeta
|
|
|
2166
2008
|
appendRepoFlag(args, repo, pr);
|
|
2167
2009
|
args.push("--json", (includeComments ? GH_PR_FIELDS : GH_PR_FIELDS_NO_COMMENTS).join(","));
|
|
2168
2010
|
|
|
2169
|
-
const data = await
|
|
2011
|
+
const data = await git.github.json<GhPrViewData>(this.session.cwd, args, signal, {
|
|
2012
|
+
repoProvided: Boolean(repo),
|
|
2013
|
+
});
|
|
2170
2014
|
const resolvedRepo = repo ?? parsePullRequestUrl(data.url).repo;
|
|
2171
2015
|
if (includeComments && resolvedRepo && typeof data.number === "number") {
|
|
2172
2016
|
data.reviewComments = await fetchPrReviewComments(this.session.cwd, resolvedRepo, data.number, signal);
|
|
@@ -2179,14 +2023,14 @@ export class GhPrViewTool implements AgentTool<typeof ghPrViewSchema, GhToolDeta
|
|
|
2179
2023
|
export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDetails> {
|
|
2180
2024
|
readonly name = "gh_pr_diff";
|
|
2181
2025
|
readonly label = "GitHub PR Diff";
|
|
2182
|
-
readonly description =
|
|
2026
|
+
readonly description = prompt.render(ghPrDiffDescription);
|
|
2183
2027
|
readonly parameters = ghPrDiffSchema;
|
|
2184
2028
|
readonly strict = true;
|
|
2185
2029
|
|
|
2186
2030
|
constructor(private readonly session: ToolSession) {}
|
|
2187
2031
|
|
|
2188
2032
|
static createIf(session: ToolSession): GhPrDiffTool | null {
|
|
2189
|
-
if (!
|
|
2033
|
+
if (!git.github.available()) return null;
|
|
2190
2034
|
return new GhPrDiffTool(session);
|
|
2191
2035
|
}
|
|
2192
2036
|
|
|
@@ -2214,7 +2058,7 @@ export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDeta
|
|
|
2214
2058
|
args.push("--exclude", normalizedPattern);
|
|
2215
2059
|
}
|
|
2216
2060
|
|
|
2217
|
-
const output = await
|
|
2061
|
+
const output = await git.github.text(this.session.cwd, args, signal, {
|
|
2218
2062
|
repoProvided: Boolean(repo),
|
|
2219
2063
|
trimOutput: false,
|
|
2220
2064
|
});
|
|
@@ -2228,14 +2072,14 @@ export class GhPrDiffTool implements AgentTool<typeof ghPrDiffSchema, GhToolDeta
|
|
|
2228
2072
|
export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, GhToolDetails> {
|
|
2229
2073
|
readonly name = "gh_pr_checkout";
|
|
2230
2074
|
readonly label = "GitHub PR Checkout";
|
|
2231
|
-
readonly description =
|
|
2075
|
+
readonly description = prompt.render(ghPrCheckoutDescription);
|
|
2232
2076
|
readonly parameters = ghPrCheckoutSchema;
|
|
2233
2077
|
readonly strict = true;
|
|
2234
2078
|
|
|
2235
2079
|
constructor(private readonly session: ToolSession) {}
|
|
2236
2080
|
|
|
2237
2081
|
static createIf(session: ToolSession): GhPrCheckoutTool | null {
|
|
2238
|
-
if (!
|
|
2082
|
+
if (!git.github.available()) return null;
|
|
2239
2083
|
return new GhPrCheckoutTool(session);
|
|
2240
2084
|
}
|
|
2241
2085
|
|
|
@@ -2259,7 +2103,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2259
2103
|
appendRepoFlag(args, repo, pr);
|
|
2260
2104
|
args.push("--json", GH_PR_CHECKOUT_FIELDS.join(","));
|
|
2261
2105
|
|
|
2262
|
-
const data = await
|
|
2106
|
+
const data = await git.github.json<GhPrViewData>(this.session.cwd, args, signal, {
|
|
2263
2107
|
repoProvided: Boolean(repo),
|
|
2264
2108
|
});
|
|
2265
2109
|
const prNumber = data.number;
|
|
@@ -2269,68 +2113,56 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2269
2113
|
|
|
2270
2114
|
const headRefName = requireNonEmpty(data.headRefName, "head branch");
|
|
2271
2115
|
const headRefOid = requireNonEmpty(data.headRefOid, "head commit");
|
|
2272
|
-
const repoRoot = await
|
|
2273
|
-
const primaryRepoRoot = await
|
|
2116
|
+
const repoRoot = await requireGitRepoRoot(this.session.cwd, signal);
|
|
2117
|
+
const primaryRepoRoot = await requirePrimaryGitRepoRoot(repoRoot, signal);
|
|
2274
2118
|
const localBranch = requestedBranch ?? `pr-${prNumber}`;
|
|
2275
2119
|
const worktreePath = requestedWorktree
|
|
2276
2120
|
? path.resolve(this.session.cwd, requestedWorktree)
|
|
2277
2121
|
: path.join(primaryRepoRoot, ".worktrees", localBranch);
|
|
2278
|
-
const existingWorktrees = await
|
|
2122
|
+
const existingWorktrees = await git.worktree.list(repoRoot, signal);
|
|
2279
2123
|
const existingWorktree = existingWorktrees.find(entry => entry.branch === toLocalBranchRef(localBranch));
|
|
2280
2124
|
|
|
2281
2125
|
const remote = await ensurePrRemote(repoRoot, data, signal);
|
|
2282
|
-
await
|
|
2126
|
+
await git.fetch(
|
|
2283
2127
|
repoRoot,
|
|
2284
|
-
|
|
2128
|
+
remote.name,
|
|
2129
|
+
`refs/heads/${headRefName}`,
|
|
2130
|
+
`refs/remotes/${remote.name}/${headRefName}`,
|
|
2285
2131
|
signal,
|
|
2286
2132
|
);
|
|
2287
2133
|
|
|
2288
2134
|
if (!existingWorktree) {
|
|
2289
2135
|
const localBranchRef = toLocalBranchRef(localBranch);
|
|
2290
|
-
const localBranchExists = await
|
|
2136
|
+
const localBranchExists = await git.ref.exists(repoRoot, localBranchRef, signal);
|
|
2291
2137
|
if (localBranchExists) {
|
|
2292
|
-
const existingOid = await
|
|
2138
|
+
const existingOid = await git.ref.resolve(repoRoot, localBranchRef, signal);
|
|
2293
2139
|
if (existingOid !== headRefOid) {
|
|
2294
2140
|
if (!force) {
|
|
2295
2141
|
throw new ToolError(
|
|
2296
|
-
`local branch ${localBranch} already exists at ${formatShortSha(existingOid) ?? existingOid}; pass force=true to reset it`,
|
|
2142
|
+
`local branch ${localBranch} already exists at ${formatShortSha(existingOid ?? undefined) ?? existingOid ?? "unknown commit"}; pass force=true to reset it`,
|
|
2297
2143
|
);
|
|
2298
2144
|
}
|
|
2299
2145
|
|
|
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
|
-
}
|
|
2146
|
+
await git.branch.force(repoRoot, localBranch, `refs/remotes/${remote.name}/${headRefName}`, signal);
|
|
2308
2147
|
}
|
|
2309
2148
|
} 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
|
-
}
|
|
2149
|
+
await git.branch.create(repoRoot, localBranch, `refs/remotes/${remote.name}/${headRefName}`, signal);
|
|
2318
2150
|
}
|
|
2319
2151
|
}
|
|
2320
2152
|
|
|
2321
|
-
await
|
|
2322
|
-
await
|
|
2323
|
-
await
|
|
2324
|
-
await
|
|
2325
|
-
await
|
|
2326
|
-
await
|
|
2153
|
+
await git.config.setBranch(repoRoot, localBranch, "remote", remote.name, signal);
|
|
2154
|
+
await git.config.setBranch(repoRoot, localBranch, "merge", `refs/heads/${headRefName}`, signal);
|
|
2155
|
+
await git.config.setBranch(repoRoot, localBranch, "pushRemote", remote.name, signal);
|
|
2156
|
+
await git.config.setBranch(repoRoot, localBranch, "ompPrHeadRef", headRefName, signal);
|
|
2157
|
+
await git.config.setBranch(repoRoot, localBranch, "ompPrUrl", data.url ?? "", signal);
|
|
2158
|
+
await git.config.setBranch(
|
|
2327
2159
|
repoRoot,
|
|
2328
2160
|
localBranch,
|
|
2329
2161
|
"ompPrIsCrossRepository",
|
|
2330
2162
|
String(Boolean(data.isCrossRepository)),
|
|
2331
2163
|
signal,
|
|
2332
2164
|
);
|
|
2333
|
-
await
|
|
2165
|
+
await git.config.setBranch(
|
|
2334
2166
|
repoRoot,
|
|
2335
2167
|
localBranch,
|
|
2336
2168
|
"ompPrMaintainerCanModify",
|
|
@@ -2342,21 +2174,15 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2342
2174
|
if (!existingWorktree) {
|
|
2343
2175
|
await ensureGitWorktreePathAvailable(finalWorktreePath, existingWorktrees);
|
|
2344
2176
|
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
|
-
}
|
|
2177
|
+
await git.worktree.add(repoRoot, finalWorktreePath, localBranch, { signal });
|
|
2353
2178
|
}
|
|
2179
|
+
const resolvedWorktreePath = await fs.realpath(finalWorktreePath);
|
|
2354
2180
|
|
|
2355
2181
|
return buildTextResult(
|
|
2356
2182
|
formatPrCheckoutResult({
|
|
2357
2183
|
data,
|
|
2358
2184
|
localBranch,
|
|
2359
|
-
worktreePath:
|
|
2185
|
+
worktreePath: resolvedWorktreePath,
|
|
2360
2186
|
remoteName: remote.name,
|
|
2361
2187
|
remoteUrl: remote.url,
|
|
2362
2188
|
reused: Boolean(existingWorktree),
|
|
@@ -2365,7 +2191,7 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2365
2191
|
{
|
|
2366
2192
|
repo: repo ?? data.headRepository?.nameWithOwner,
|
|
2367
2193
|
branch: localBranch,
|
|
2368
|
-
worktreePath:
|
|
2194
|
+
worktreePath: resolvedWorktreePath,
|
|
2369
2195
|
remote: remote.name,
|
|
2370
2196
|
remoteBranch: headRefName,
|
|
2371
2197
|
},
|
|
@@ -2377,14 +2203,14 @@ export class GhPrCheckoutTool implements AgentTool<typeof ghPrCheckoutSchema, Gh
|
|
|
2377
2203
|
export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDetails> {
|
|
2378
2204
|
readonly name = "gh_pr_push";
|
|
2379
2205
|
readonly label = "GitHub PR Push";
|
|
2380
|
-
readonly description =
|
|
2206
|
+
readonly description = prompt.render(ghPrPushDescription);
|
|
2381
2207
|
readonly parameters = ghPrPushSchema;
|
|
2382
2208
|
readonly strict = true;
|
|
2383
2209
|
|
|
2384
2210
|
constructor(private readonly session: ToolSession) {}
|
|
2385
2211
|
|
|
2386
2212
|
static createIf(session: ToolSession): GhPrPushTool | null {
|
|
2387
|
-
if (!
|
|
2213
|
+
if (!git.github.available()) return null;
|
|
2388
2214
|
return new GhPrPushTool(session);
|
|
2389
2215
|
}
|
|
2390
2216
|
|
|
@@ -2396,28 +2222,24 @@ export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDeta
|
|
|
2396
2222
|
_context?: AgentToolContext,
|
|
2397
2223
|
): Promise<AgentToolResult<GhToolDetails>> {
|
|
2398
2224
|
return untilAborted(signal, async () => {
|
|
2399
|
-
const repoRoot = await
|
|
2225
|
+
const repoRoot = await requireGitRepoRoot(this.session.cwd, signal);
|
|
2400
2226
|
const localBranch =
|
|
2401
|
-
normalizeOptionalString(params.branch) ?? (await
|
|
2402
|
-
const refExists = await
|
|
2227
|
+
normalizeOptionalString(params.branch) ?? (await requireCurrentGitBranch(repoRoot, signal));
|
|
2228
|
+
const refExists = await git.ref.exists(repoRoot, toLocalBranchRef(localBranch), signal);
|
|
2403
2229
|
if (!refExists) {
|
|
2404
2230
|
throw new ToolError(`local branch ${localBranch} does not exist`);
|
|
2405
2231
|
}
|
|
2406
2232
|
|
|
2407
2233
|
const target = await resolvePrBranchPushTarget(repoRoot, localBranch, signal);
|
|
2408
|
-
const currentBranch = await
|
|
2234
|
+
const currentBranch = await git.branch.current(repoRoot, signal);
|
|
2409
2235
|
const sourceRef = currentBranch === localBranch ? "HEAD" : toLocalBranchRef(localBranch);
|
|
2410
2236
|
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
|
-
}
|
|
2237
|
+
await git.push(repoRoot, {
|
|
2238
|
+
forceWithLease: params.forceWithLease,
|
|
2239
|
+
refspec,
|
|
2240
|
+
remote: target.remoteName,
|
|
2241
|
+
signal,
|
|
2242
|
+
});
|
|
2421
2243
|
|
|
2422
2244
|
return buildTextResult(
|
|
2423
2245
|
formatPrPushResult({
|
|
@@ -2442,14 +2264,14 @@ export class GhPrPushTool implements AgentTool<typeof ghPrPushSchema, GhToolDeta
|
|
|
2442
2264
|
export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema, GhToolDetails> {
|
|
2443
2265
|
readonly name = "gh_search_issues";
|
|
2444
2266
|
readonly label = "GitHub Issue Search";
|
|
2445
|
-
readonly description =
|
|
2267
|
+
readonly description = prompt.render(ghSearchIssuesDescription);
|
|
2446
2268
|
readonly parameters = ghSearchIssuesSchema;
|
|
2447
2269
|
readonly strict = true;
|
|
2448
2270
|
|
|
2449
2271
|
constructor(private readonly session: ToolSession) {}
|
|
2450
2272
|
|
|
2451
2273
|
static createIf(session: ToolSession): GhSearchIssuesTool | null {
|
|
2452
|
-
if (!
|
|
2274
|
+
if (!git.github.available()) return null;
|
|
2453
2275
|
return new GhSearchIssuesTool(session);
|
|
2454
2276
|
}
|
|
2455
2277
|
|
|
@@ -2466,7 +2288,7 @@ export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema
|
|
|
2466
2288
|
const limit = resolveSearchLimit(params.limit);
|
|
2467
2289
|
const args = buildGhSearchArgs("issues", query, limit, repo);
|
|
2468
2290
|
|
|
2469
|
-
const items = await
|
|
2291
|
+
const items = await git.github.json<GhSearchResult[]>(this.session.cwd, args, signal, {
|
|
2470
2292
|
repoProvided: Boolean(repo),
|
|
2471
2293
|
});
|
|
2472
2294
|
return buildTextResult(formatSearchResults("issues", query, repo, items));
|
|
@@ -2477,14 +2299,14 @@ export class GhSearchIssuesTool implements AgentTool<typeof ghSearchIssuesSchema
|
|
|
2477
2299
|
export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhToolDetails> {
|
|
2478
2300
|
readonly name = "gh_search_prs";
|
|
2479
2301
|
readonly label = "GitHub PR Search";
|
|
2480
|
-
readonly description =
|
|
2302
|
+
readonly description = prompt.render(ghSearchPrsDescription);
|
|
2481
2303
|
readonly parameters = ghSearchPrsSchema;
|
|
2482
2304
|
readonly strict = true;
|
|
2483
2305
|
|
|
2484
2306
|
constructor(private readonly session: ToolSession) {}
|
|
2485
2307
|
|
|
2486
2308
|
static createIf(session: ToolSession): GhSearchPrsTool | null {
|
|
2487
|
-
if (!
|
|
2309
|
+
if (!git.github.available()) return null;
|
|
2488
2310
|
return new GhSearchPrsTool(session);
|
|
2489
2311
|
}
|
|
2490
2312
|
|
|
@@ -2501,7 +2323,7 @@ export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhTo
|
|
|
2501
2323
|
const limit = resolveSearchLimit(params.limit);
|
|
2502
2324
|
const args = buildGhSearchArgs("prs", query, limit, repo);
|
|
2503
2325
|
|
|
2504
|
-
const items = await
|
|
2326
|
+
const items = await git.github.json<GhSearchResult[]>(this.session.cwd, args, signal, {
|
|
2505
2327
|
repoProvided: Boolean(repo),
|
|
2506
2328
|
});
|
|
2507
2329
|
return buildTextResult(formatSearchResults("pull requests", query, repo, items));
|
|
@@ -2512,14 +2334,14 @@ export class GhSearchPrsTool implements AgentTool<typeof ghSearchPrsSchema, GhTo
|
|
|
2512
2334
|
export class GhRunWatchTool implements AgentTool<typeof ghRunWatchSchema, GhToolDetails> {
|
|
2513
2335
|
readonly name = "gh_run_watch";
|
|
2514
2336
|
readonly label = "GitHub Run Watch";
|
|
2515
|
-
readonly description =
|
|
2337
|
+
readonly description = prompt.render(ghRunWatchDescription);
|
|
2516
2338
|
readonly parameters = ghRunWatchSchema;
|
|
2517
2339
|
readonly strict = true;
|
|
2518
2340
|
|
|
2519
2341
|
constructor(private readonly session: ToolSession) {}
|
|
2520
2342
|
|
|
2521
2343
|
static createIf(session: ToolSession): GhRunWatchTool | null {
|
|
2522
|
-
if (!
|
|
2344
|
+
if (!git.github.available()) return null;
|
|
2523
2345
|
return new GhRunWatchTool(session);
|
|
2524
2346
|
}
|
|
2525
2347
|
|
|
@@ -2613,10 +2435,10 @@ export class GhRunWatchTool implements AgentTool<typeof ghRunWatchSchema, GhTool
|
|
|
2613
2435
|
}
|
|
2614
2436
|
}
|
|
2615
2437
|
|
|
2616
|
-
const branch = branchInput ?? (await
|
|
2438
|
+
const branch = branchInput ?? (await requireCurrentGitBranch(this.session.cwd, signal));
|
|
2617
2439
|
const headSha = branchInput
|
|
2618
2440
|
? await resolveGitHubBranchHead(this.session.cwd, repo, branch, signal)
|
|
2619
|
-
: await
|
|
2441
|
+
: await requireCurrentGitHead(this.session.cwd, signal);
|
|
2620
2442
|
let pollCount = 0;
|
|
2621
2443
|
let settledSuccessSignature: string | undefined;
|
|
2622
2444
|
|