@getpaseo/server 0.1.58 → 0.1.60
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/scripts/dev-runner.js +26 -7
- package/dist/scripts/dev-runner.js.map +1 -1
- package/dist/server/client/daemon-client-runtime-metrics.d.ts +39 -0
- package/dist/server/client/daemon-client-runtime-metrics.d.ts.map +1 -0
- package/dist/server/client/daemon-client-runtime-metrics.js +173 -0
- package/dist/server/client/daemon-client-runtime-metrics.js.map +1 -0
- package/dist/server/client/daemon-client.d.ts +58 -9
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +151 -10
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +55 -48
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +541 -331
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts +3 -2
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +31 -16
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-projections.d.ts +2 -1
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +29 -1
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -5
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +76 -69
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +13 -6
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
- package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -0
- package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
- package/dist/server/server/agent/agent-stream-coalescer.js.map +1 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts +54 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -0
- package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
- package/dist/server/server/agent/agent-timeline-store-types.js.map +1 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts +32 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -0
- package/dist/server/server/agent/agent-timeline-store.js +245 -0
- package/dist/server/server/agent/agent-timeline-store.js.map +1 -0
- package/dist/server/server/agent/mcp-server.d.ts +12 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +276 -65
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/mcp-shared.d.ts +196 -152
- package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-shared.js +40 -42
- package/dist/server/server/agent/mcp-shared.js.map +1 -1
- package/dist/server/server/agent/model-resolver.d.ts.map +1 -1
- package/dist/server/server/agent/model-resolver.js +3 -1
- package/dist/server/server/agent/model-resolver.js.map +1 -1
- package/dist/server/server/agent/prompt-attachments.d.ts +6 -0
- package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -0
- package/dist/server/server/agent/prompt-attachments.js +31 -0
- package/dist/server/server/agent/prompt-attachments.js.map +1 -0
- package/dist/server/server/agent/provider-launch-config.d.ts +12 -10
- package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
- package/dist/server/server/agent/provider-launch-config.js +34 -0
- package/dist/server/server/agent/provider-launch-config.js.map +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts +1 -0
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
- package/dist/server/server/agent/provider-manifest.js +22 -1
- package/dist/server/server/agent/provider-manifest.js.map +1 -1
- package/dist/server/server/agent/provider-registry.d.ts +5 -2
- package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
- package/dist/server/server/agent/provider-registry.js +20 -9
- package/dist/server/server/agent/provider-registry.js.map +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +17 -5
- package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +150 -61
- package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.d.ts +8 -4
- package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/acp-agent.js +73 -8
- package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/claude/claude-models.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/claude-models.js +21 -0
- package/dist/server/server/agent/providers/claude/claude-models.js.map +1 -1
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
- package/dist/server/server/agent/providers/claude-agent.d.ts +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +15 -8
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +37 -4
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +61 -31
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +3 -2
- package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +64 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +585 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +19 -4
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +227 -118
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts +69 -0
- package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/pi-direct-agent.js +1177 -0
- package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +7 -4
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent-attention-policy.d.ts +13 -13
- package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
- package/dist/server/server/agent-attention-policy.js +20 -36
- package/dist/server/server/agent-attention-policy.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +6 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +113 -11
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/chat/chat-rpc-schemas.d.ts +44 -44
- package/dist/server/server/chat/chat-types.d.ts +6 -6
- package/dist/server/server/checkout-diff-manager.d.ts +0 -1
- package/dist/server/server/checkout-diff-manager.d.ts.map +1 -1
- package/dist/server/server/checkout-diff-manager.js +6 -4
- package/dist/server/server/checkout-diff-manager.js.map +1 -1
- package/dist/server/server/config.d.ts.map +1 -1
- package/dist/server/server/config.js +1 -0
- package/dist/server/server/config.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +2 -1
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/loop/rpc-schemas.d.ts +392 -392
- package/dist/server/server/loop-service.d.ts +52 -52
- package/dist/server/server/paseo-worktree-archive-service.d.ts +41 -0
- package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -0
- package/dist/server/server/paseo-worktree-archive-service.js +137 -0
- package/dist/server/server/paseo-worktree-archive-service.js.map +1 -0
- package/dist/server/server/paseo-worktree-service.d.ts +24 -0
- package/dist/server/server/paseo-worktree-service.d.ts.map +1 -0
- package/dist/server/server/paseo-worktree-service.js +94 -0
- package/dist/server/server/paseo-worktree-service.js.map +1 -0
- package/dist/server/server/path-utils.d.ts +1 -0
- package/dist/server/server/path-utils.d.ts.map +1 -1
- package/dist/server/server/path-utils.js +9 -0
- package/dist/server/server/path-utils.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +73 -73
- package/dist/server/server/persistence-hooks.d.ts.map +1 -1
- package/dist/server/server/persistence-hooks.js +3 -0
- package/dist/server/server/persistence-hooks.js.map +1 -1
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +30 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts.map +1 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +163 -0
- package/dist/server/server/resolve-worktree-creation-intent.js.map +1 -0
- package/dist/server/server/schedule/rpc-schemas.d.ts +192 -192
- package/dist/server/server/schedule/service.d.ts +1 -1
- package/dist/server/server/schedule/service.d.ts.map +1 -1
- package/dist/server/server/schedule/types.d.ts +44 -44
- package/dist/server/server/script-health-monitor.d.ts +39 -0
- package/dist/server/server/script-health-monitor.d.ts.map +1 -0
- package/dist/server/server/script-health-monitor.js +158 -0
- package/dist/server/server/script-health-monitor.js.map +1 -0
- package/dist/server/server/script-proxy.d.ts +40 -0
- package/dist/server/server/script-proxy.d.ts.map +1 -0
- package/dist/server/server/script-proxy.js +245 -0
- package/dist/server/server/script-proxy.js.map +1 -0
- package/dist/server/server/script-route-branch-handler.d.ts +10 -0
- package/dist/server/server/script-route-branch-handler.d.ts.map +1 -0
- package/dist/server/server/script-route-branch-handler.js +45 -0
- package/dist/server/server/script-route-branch-handler.js.map +1 -0
- package/dist/server/server/script-status-projection.d.ts +29 -0
- package/dist/server/server/script-status-projection.d.ts.map +1 -0
- package/dist/server/server/script-status-projection.js +133 -0
- package/dist/server/server/script-status-projection.js.map +1 -0
- package/dist/server/server/session.d.ts +77 -13
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +1290 -548
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +27 -3
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +112 -29
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/workspace-archive-service.d.ts +8 -0
- package/dist/server/server/workspace-archive-service.d.ts.map +1 -0
- package/dist/server/server/workspace-archive-service.js +17 -0
- package/dist/server/server/workspace-archive-service.js.map +1 -0
- package/dist/server/server/workspace-git-metadata.d.ts +24 -0
- package/dist/server/server/workspace-git-metadata.d.ts.map +1 -0
- package/dist/server/server/workspace-git-metadata.js +78 -0
- package/dist/server/server/workspace-git-metadata.js.map +1 -0
- package/dist/server/server/workspace-git-service.d.ts +104 -5
- package/dist/server/server/workspace-git-service.d.ts.map +1 -1
- package/dist/server/server/workspace-git-service.js +442 -56
- package/dist/server/server/workspace-git-service.js.map +1 -1
- package/dist/server/server/workspace-reconciliation-service.d.ts +54 -0
- package/dist/server/server/workspace-reconciliation-service.d.ts.map +1 -0
- package/dist/server/server/workspace-reconciliation-service.js +176 -0
- package/dist/server/server/workspace-reconciliation-service.js.map +1 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
- package/dist/server/server/workspace-registry-bootstrap.js +4 -3
- package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
- package/dist/server/server/workspace-registry.d.ts +8 -8
- package/dist/server/server/workspace-registry.test-helpers.d.ts +37 -0
- package/dist/server/server/workspace-registry.test-helpers.d.ts.map +1 -0
- package/dist/server/server/workspace-registry.test-helpers.js +121 -0
- package/dist/server/server/workspace-registry.test-helpers.js.map +1 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts.map +1 -0
- package/dist/server/server/workspace-script-runtime-store.js +78 -0
- package/dist/server/server/workspace-script-runtime-store.js.map +1 -0
- package/dist/server/server/workspace-service-env.d.ts +17 -0
- package/dist/server/server/workspace-service-env.d.ts.map +1 -0
- package/dist/server/server/workspace-service-env.js +80 -0
- package/dist/server/server/workspace-service-env.js.map +1 -0
- package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
- package/dist/server/server/workspace-service-port-registry.d.ts.map +1 -0
- package/dist/server/server/workspace-service-port-registry.js +59 -0
- package/dist/server/server/workspace-service-port-registry.js.map +1 -0
- package/dist/server/server/worktree-bootstrap.d.ts +55 -10
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +290 -112
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/server/worktree-core.d.ts +25 -0
- package/dist/server/server/worktree-core.d.ts.map +1 -0
- package/dist/server/server/worktree-core.js +75 -0
- package/dist/server/server/worktree-core.js.map +1 -0
- package/dist/server/server/worktree-errors.d.ts +12 -0
- package/dist/server/server/worktree-errors.d.ts.map +1 -0
- package/dist/server/server/worktree-errors.js +31 -0
- package/dist/server/server/worktree-errors.js.map +1 -0
- package/dist/server/server/worktree-session.d.ts +56 -70
- package/dist/server/server/worktree-session.d.ts.map +1 -1
- package/dist/server/server/worktree-session.js +176 -251
- package/dist/server/server/worktree-session.js.map +1 -1
- package/dist/server/services/github-service.d.ts +225 -0
- package/dist/server/services/github-service.d.ts.map +1 -0
- package/dist/server/services/github-service.js +1381 -0
- package/dist/server/services/github-service.js.map +1 -0
- package/dist/server/shared/messages.d.ts +29408 -12268
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +391 -65
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/server/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/dist/server/terminal/terminal-manager.d.ts +9 -0
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +27 -0
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal-output-coalescer.d.ts +30 -0
- package/dist/server/terminal/terminal-output-coalescer.d.ts.map +1 -0
- package/dist/server/terminal/terminal-output-coalescer.js +55 -0
- package/dist/server/terminal/terminal-output-coalescer.js.map +1 -0
- package/dist/server/terminal/terminal.d.ts +32 -1
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +397 -17
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +63 -10
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +321 -229
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/promise-timeout.d.ts +9 -0
- package/dist/server/utils/promise-timeout.d.ts.map +1 -0
- package/dist/server/utils/promise-timeout.js +25 -0
- package/dist/server/utils/promise-timeout.js.map +1 -0
- package/dist/server/utils/script-hostname.d.ts +8 -0
- package/dist/server/utils/script-hostname.d.ts.map +1 -0
- package/dist/server/utils/script-hostname.js +14 -0
- package/dist/server/utils/script-hostname.js.map +1 -0
- package/dist/server/utils/string-command-shell.d.ts +10 -0
- package/dist/server/utils/string-command-shell.d.ts.map +1 -0
- package/dist/server/utils/string-command-shell.js +21 -0
- package/dist/server/utils/string-command-shell.js.map +1 -0
- package/dist/server/utils/worktree.d.ts +54 -7
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +434 -129
- package/dist/server/utils/worktree.js.map +1 -1
- package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/src/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/package.json +11 -14
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts +0 -28
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts.map +0 -1
- package/dist/server/server/agent/providers/pi-acp-agent.js +0 -302
- package/dist/server/server/agent/providers/pi-acp-agent.js.map +0 -1
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { resolve, dirname, basename } from "path";
|
|
2
2
|
import { existsSync, realpathSync } from "fs";
|
|
3
|
-
import { open as openFile, stat as statFile } from "fs/promises";
|
|
3
|
+
import { open as openFile, readFile, stat as statFile } from "fs/promises";
|
|
4
4
|
import { TTLCache } from "@isaacs/ttlcache";
|
|
5
5
|
import { parseAndHighlightDiff } from "../server/utils/diff-highlighter.js";
|
|
6
|
-
import {
|
|
6
|
+
import { GitHubAuthenticationError, GitHubCliMissingError, createGitHubService, resolveGitHubRepo, } from "../services/github-service.js";
|
|
7
7
|
import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
|
|
8
8
|
import { runGitCommand } from "./run-git-command.js";
|
|
9
|
-
import { execCommand } from "./spawn.js";
|
|
10
9
|
import { isPaseoOwnedWorktreeCwd } from "./worktree.js";
|
|
11
10
|
import { requirePaseoWorktreeBaseRefName } from "./worktree-metadata.js";
|
|
12
11
|
const READ_ONLY_GIT_ENV = {
|
|
@@ -15,10 +14,14 @@ const READ_ONLY_GIT_ENV = {
|
|
|
15
14
|
};
|
|
16
15
|
const DEFAULT_PULL_REQUEST_STATUS_CACHE_TTL_MS = 30000;
|
|
17
16
|
const PULL_REQUEST_STATUS_CACHE_MAX = 1000;
|
|
17
|
+
const DEFAULT_SHORTSTAT_CACHE_TTL_MS = 15000;
|
|
18
|
+
const SHORTSTAT_CACHE_MAX = 1000;
|
|
18
19
|
let pullRequestStatusCacheTtlMs = DEFAULT_PULL_REQUEST_STATUS_CACHE_TTL_MS;
|
|
19
20
|
let pullRequestStatusCache = createPullRequestStatusCache(pullRequestStatusCacheTtlMs);
|
|
20
21
|
const pullRequestStatusInFlight = new Map();
|
|
21
|
-
let
|
|
22
|
+
let shortstatCacheTtlMs = DEFAULT_SHORTSTAT_CACHE_TTL_MS;
|
|
23
|
+
let shortstatCache = createShortstatCache(shortstatCacheTtlMs);
|
|
24
|
+
const shortstatInFlight = new Map();
|
|
22
25
|
function createPullRequestStatusCache(ttlMs) {
|
|
23
26
|
return new TTLCache({
|
|
24
27
|
ttl: ttlMs,
|
|
@@ -26,9 +29,19 @@ function createPullRequestStatusCache(ttlMs) {
|
|
|
26
29
|
checkAgeOnGet: true,
|
|
27
30
|
});
|
|
28
31
|
}
|
|
32
|
+
function createShortstatCache(ttlMs) {
|
|
33
|
+
return new TTLCache({
|
|
34
|
+
ttl: ttlMs,
|
|
35
|
+
max: SHORTSTAT_CACHE_MAX,
|
|
36
|
+
checkAgeOnGet: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
29
39
|
function getPullRequestStatusCacheKey(cwd) {
|
|
30
40
|
return resolve(cwd);
|
|
31
41
|
}
|
|
42
|
+
function getShortstatCacheKey(cwd) {
|
|
43
|
+
return resolve(cwd);
|
|
44
|
+
}
|
|
32
45
|
export function __resetPullRequestStatusCacheForTests() {
|
|
33
46
|
pullRequestStatusCache.clear();
|
|
34
47
|
pullRequestStatusCache.cancelTimer();
|
|
@@ -43,11 +56,22 @@ export function __setPullRequestStatusCacheTtlForTests(ttlMs) {
|
|
|
43
56
|
pullRequestStatusCache = createPullRequestStatusCache(ttlMs);
|
|
44
57
|
pullRequestStatusInFlight.clear();
|
|
45
58
|
}
|
|
46
|
-
export function
|
|
47
|
-
|
|
59
|
+
export function __resetCheckoutShortstatCacheForTests() {
|
|
60
|
+
shortstatCache.clear();
|
|
61
|
+
shortstatCache.cancelTimer();
|
|
62
|
+
shortstatCacheTtlMs = DEFAULT_SHORTSTAT_CACHE_TTL_MS;
|
|
63
|
+
shortstatCache = createShortstatCache(shortstatCacheTtlMs);
|
|
64
|
+
shortstatInFlight.clear();
|
|
65
|
+
}
|
|
66
|
+
export function __setCheckoutShortstatCacheTtlForTests(ttlMs) {
|
|
67
|
+
shortstatCache.clear();
|
|
68
|
+
shortstatCache.cancelTimer();
|
|
69
|
+
shortstatCacheTtlMs = ttlMs;
|
|
70
|
+
shortstatCache = createShortstatCache(ttlMs);
|
|
71
|
+
shortstatInFlight.clear();
|
|
48
72
|
}
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
function getCheckoutDiffRefArgs(refs) {
|
|
74
|
+
return [refs.baseRef, ...(refs.targetRef ? [refs.targetRef] : [])];
|
|
51
75
|
}
|
|
52
76
|
function normalizeBranchSuggestionName(raw) {
|
|
53
77
|
const trimmed = raw.trim();
|
|
@@ -126,7 +150,8 @@ export async function listBranchSuggestions(cwd, options) {
|
|
|
126
150
|
continue;
|
|
127
151
|
const existing = branchMeta.get(normalized);
|
|
128
152
|
branchMeta.set(normalized, {
|
|
129
|
-
|
|
153
|
+
hasLocal: true,
|
|
154
|
+
hasRemote: existing?.hasRemote ?? false,
|
|
130
155
|
committerDate: Math.max(ref.committerDate, existing?.committerDate ?? 0),
|
|
131
156
|
});
|
|
132
157
|
}
|
|
@@ -136,11 +161,16 @@ export async function listBranchSuggestions(cwd, options) {
|
|
|
136
161
|
continue;
|
|
137
162
|
const existing = branchMeta.get(normalized);
|
|
138
163
|
if (!existing) {
|
|
139
|
-
branchMeta.set(normalized, {
|
|
164
|
+
branchMeta.set(normalized, {
|
|
165
|
+
hasLocal: false,
|
|
166
|
+
hasRemote: true,
|
|
167
|
+
committerDate: ref.committerDate,
|
|
168
|
+
});
|
|
140
169
|
}
|
|
141
170
|
else {
|
|
142
171
|
branchMeta.set(normalized, {
|
|
143
172
|
...existing,
|
|
173
|
+
hasRemote: true,
|
|
144
174
|
committerDate: Math.max(ref.committerDate, existing.committerDate),
|
|
145
175
|
});
|
|
146
176
|
}
|
|
@@ -150,13 +180,71 @@ export async function listBranchSuggestions(cwd, options) {
|
|
|
150
180
|
return [];
|
|
151
181
|
}
|
|
152
182
|
const ordered = sortBranchSuggestions(filteredNames, branchMeta, query);
|
|
153
|
-
return ordered.slice(0, limit)
|
|
183
|
+
return ordered.slice(0, limit).map((name) => {
|
|
184
|
+
const meta = branchMeta.get(name);
|
|
185
|
+
return {
|
|
186
|
+
name,
|
|
187
|
+
committerDate: meta?.committerDate ?? 0,
|
|
188
|
+
hasLocal: meta?.hasLocal ?? false,
|
|
189
|
+
hasRemote: meta?.hasRemote ?? false,
|
|
190
|
+
};
|
|
191
|
+
});
|
|
154
192
|
}
|
|
155
|
-
async function
|
|
193
|
+
export async function resolveBranchCheckout(cwd, name) {
|
|
194
|
+
await requireGitRepo(cwd);
|
|
195
|
+
const normalized = normalizeBranchSuggestionName(name);
|
|
196
|
+
if (!normalized) {
|
|
197
|
+
return { kind: "not-found" };
|
|
198
|
+
}
|
|
199
|
+
const localRef = `refs/heads/${normalized}`;
|
|
200
|
+
const localResult = await runGitCommand(["rev-parse", "--verify", "--quiet", localRef], {
|
|
201
|
+
cwd,
|
|
202
|
+
env: READ_ONLY_GIT_ENV,
|
|
203
|
+
acceptExitCodes: [0, 1],
|
|
204
|
+
});
|
|
205
|
+
const hasLocal = localResult.exitCode === 0;
|
|
206
|
+
if (hasLocal) {
|
|
207
|
+
return { kind: "local", name: normalized };
|
|
208
|
+
}
|
|
209
|
+
const remoteRef = `origin/${normalized}`;
|
|
210
|
+
const remoteRefPath = `refs/remotes/${remoteRef}`;
|
|
211
|
+
const remoteResult = await runGitCommand(["rev-parse", "--verify", "--quiet", remoteRefPath], {
|
|
212
|
+
cwd,
|
|
213
|
+
env: READ_ONLY_GIT_ENV,
|
|
214
|
+
acceptExitCodes: [0, 1],
|
|
215
|
+
});
|
|
216
|
+
const hasRemote = remoteResult.exitCode === 0;
|
|
217
|
+
if (hasRemote) {
|
|
218
|
+
return { kind: "remote-only", name: normalized, remoteRef };
|
|
219
|
+
}
|
|
220
|
+
return { kind: "not-found" };
|
|
221
|
+
}
|
|
222
|
+
export async function checkoutResolvedBranch(input) {
|
|
223
|
+
const { cwd, resolution } = input;
|
|
224
|
+
switch (resolution.kind) {
|
|
225
|
+
case "local": {
|
|
226
|
+
const { stdout } = await runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
|
|
227
|
+
const current = stdout.trim();
|
|
228
|
+
if (current === resolution.name) {
|
|
229
|
+
return { source: "local" };
|
|
230
|
+
}
|
|
231
|
+
await runGitCommand(["checkout", resolution.name], { cwd });
|
|
232
|
+
return { source: "local" };
|
|
233
|
+
}
|
|
234
|
+
case "remote-only":
|
|
235
|
+
await runGitCommand(["checkout", "-b", resolution.name, "--track", resolution.remoteRef], {
|
|
236
|
+
cwd,
|
|
237
|
+
});
|
|
238
|
+
return { source: "remote" };
|
|
239
|
+
case "not-found":
|
|
240
|
+
throw new Error(`Branch not found: ${input.requestedBranch ?? "unknown"}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function listCheckoutFileChanges(cwd, refs, ignoreWhitespace = false) {
|
|
156
244
|
const changes = [];
|
|
157
245
|
const { stdout: nameStatusOut } = await runGitCommand(buildGitDiffArgs({
|
|
158
246
|
ignoreWhitespace,
|
|
159
|
-
extra: ["--name-status",
|
|
247
|
+
extra: ["--name-status", ...getCheckoutDiffRefArgs(refs)],
|
|
160
248
|
}), { cwd, env: READ_ONLY_GIT_ENV });
|
|
161
249
|
for (const line of nameStatusOut
|
|
162
250
|
.split("\n")
|
|
@@ -192,21 +280,23 @@ async function listCheckoutFileChanges(cwd, ref, ignoreWhitespace = false) {
|
|
|
192
280
|
isDeleted: code === "D",
|
|
193
281
|
});
|
|
194
282
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
for (const file of untrackedOut
|
|
200
|
-
.split("\n")
|
|
201
|
-
.map((l) => l.trim())
|
|
202
|
-
.filter(Boolean)) {
|
|
203
|
-
changes.push({
|
|
204
|
-
path: file,
|
|
205
|
-
status: "U",
|
|
206
|
-
isNew: true,
|
|
207
|
-
isDeleted: false,
|
|
208
|
-
isUntracked: true,
|
|
283
|
+
if (refs.includeUntracked) {
|
|
284
|
+
const { stdout: untrackedOut } = await runGitCommand(["ls-files", "--others", "--exclude-standard"], {
|
|
285
|
+
cwd,
|
|
286
|
+
env: READ_ONLY_GIT_ENV,
|
|
209
287
|
});
|
|
288
|
+
for (const file of untrackedOut
|
|
289
|
+
.split("\n")
|
|
290
|
+
.map((l) => l.trim())
|
|
291
|
+
.filter(Boolean)) {
|
|
292
|
+
changes.push({
|
|
293
|
+
path: file,
|
|
294
|
+
status: "U",
|
|
295
|
+
isNew: true,
|
|
296
|
+
isDeleted: false,
|
|
297
|
+
isUntracked: true,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
210
300
|
}
|
|
211
301
|
// Deduplicate by path (prefer tracked status over untracked marker if both appear).
|
|
212
302
|
const byPath = new Map();
|
|
@@ -264,10 +354,10 @@ function buildGitDiffArgs(args) {
|
|
|
264
354
|
}
|
|
265
355
|
const TRACKED_DIFF_NUMSTAT_MAX_BYTES = 2 * 1024 * 1024; // 2MB
|
|
266
356
|
const TRACKED_MAX_CHANGED_LINES = 40000;
|
|
267
|
-
async function getTrackedNumstatByPath(cwd,
|
|
357
|
+
async function getTrackedNumstatByPath(cwd, refs, ignoreWhitespace = false) {
|
|
268
358
|
const result = await runGitCommand(buildGitDiffArgs({
|
|
269
359
|
ignoreWhitespace,
|
|
270
|
-
extra: ["--numstat",
|
|
360
|
+
extra: ["--numstat", ...getCheckoutDiffRefArgs(refs)],
|
|
271
361
|
}), {
|
|
272
362
|
cwd,
|
|
273
363
|
env: READ_ONLY_GIT_ENV,
|
|
@@ -358,12 +448,34 @@ export async function getCurrentBranch(cwd) {
|
|
|
358
448
|
env: READ_ONLY_GIT_ENV,
|
|
359
449
|
});
|
|
360
450
|
const branch = stdout.trim();
|
|
451
|
+
if (branch === "HEAD") {
|
|
452
|
+
return await getRebaseHeadBranch(cwd);
|
|
453
|
+
}
|
|
361
454
|
return branch.length > 0 ? branch : null;
|
|
362
455
|
}
|
|
363
456
|
catch {
|
|
364
457
|
return null;
|
|
365
458
|
}
|
|
366
459
|
}
|
|
460
|
+
async function getRebaseHeadBranch(cwd) {
|
|
461
|
+
for (const path of ["rebase-merge/head-name", "rebase-apply/head-name"]) {
|
|
462
|
+
try {
|
|
463
|
+
const { stdout } = await runGitCommand(["rev-parse", "--git-path", path], {
|
|
464
|
+
cwd,
|
|
465
|
+
env: READ_ONLY_GIT_ENV,
|
|
466
|
+
});
|
|
467
|
+
const headName = (await readFile(resolve(cwd, stdout.trim()), "utf8")).trim();
|
|
468
|
+
if (headName.startsWith("refs/heads/")) {
|
|
469
|
+
return headName.slice("refs/heads/".length) || null;
|
|
470
|
+
}
|
|
471
|
+
return headName || null;
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
// Try the other rebase backend.
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
367
479
|
async function getWorktreeRoot(cwd) {
|
|
368
480
|
try {
|
|
369
481
|
const { stdout } = await runGitCommand(["rev-parse", "--show-toplevel"], {
|
|
@@ -598,21 +710,47 @@ async function resolveBaseRef(repoRoot) {
|
|
|
598
710
|
return resolveRepositoryDefaultBranch(repoRoot);
|
|
599
711
|
}
|
|
600
712
|
function normalizeLocalBranchRefName(input) {
|
|
601
|
-
|
|
713
|
+
if (input.startsWith("refs/remotes/origin/")) {
|
|
714
|
+
return input.slice("refs/remotes/origin/".length);
|
|
715
|
+
}
|
|
716
|
+
if (input.startsWith("refs/heads/")) {
|
|
717
|
+
return input.slice("refs/heads/".length);
|
|
718
|
+
}
|
|
719
|
+
if (input.startsWith("origin/")) {
|
|
720
|
+
return input.slice("origin/".length);
|
|
721
|
+
}
|
|
722
|
+
return input;
|
|
723
|
+
}
|
|
724
|
+
function normalizeComparisonBaseRefName(input) {
|
|
725
|
+
const localName = normalizeLocalBranchRefName(input);
|
|
726
|
+
return { localName, originRef: `origin/${localName}` };
|
|
602
727
|
}
|
|
603
728
|
async function doesGitRefExist(cwd, fullRef) {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
729
|
+
const result = await runGitCommand(["show-ref", "--verify", "--quiet", fullRef], {
|
|
730
|
+
cwd,
|
|
731
|
+
env: READ_ONLY_GIT_ENV,
|
|
732
|
+
acceptExitCodes: [0, 1],
|
|
733
|
+
});
|
|
734
|
+
return result.exitCode === 0;
|
|
735
|
+
}
|
|
736
|
+
async function resolveBestComparisonBaseRef(cwd, baseRef) {
|
|
737
|
+
const normalized = normalizeComparisonBaseRefName(baseRef);
|
|
738
|
+
const [hasLocal, hasOrigin] = await Promise.all([
|
|
739
|
+
doesGitRefExist(cwd, `refs/heads/${normalized.localName}`),
|
|
740
|
+
doesGitRefExist(cwd, `refs/remotes/origin/${normalized.localName}`),
|
|
741
|
+
]);
|
|
742
|
+
if (hasOrigin) {
|
|
743
|
+
return normalized.originRef;
|
|
610
744
|
}
|
|
611
|
-
|
|
612
|
-
return
|
|
745
|
+
if (hasLocal) {
|
|
746
|
+
return normalized.localName;
|
|
613
747
|
}
|
|
748
|
+
const refName = baseRef.startsWith("origin/") || baseRef.startsWith("refs/remotes/origin/")
|
|
749
|
+
? normalized.originRef
|
|
750
|
+
: normalized.localName;
|
|
751
|
+
throw new Error(`Base branch not found locally or on origin: ${refName}`);
|
|
614
752
|
}
|
|
615
|
-
async function
|
|
753
|
+
async function resolveMostAheadBaseRef(cwd, normalizedBaseRef) {
|
|
616
754
|
const [hasLocal, hasOrigin] = await Promise.all([
|
|
617
755
|
doesGitRefExist(cwd, `refs/heads/${normalizedBaseRef}`),
|
|
618
756
|
doesGitRefExist(cwd, `refs/remotes/origin/${normalizedBaseRef}`),
|
|
@@ -626,18 +764,15 @@ async function resolveBestComparisonBaseRef(cwd, normalizedBaseRef) {
|
|
|
626
764
|
if (!hasLocal && !hasOrigin) {
|
|
627
765
|
throw new Error(`Base branch not found locally or on origin: ${normalizedBaseRef}`);
|
|
628
766
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (!Number.isNaN(localOnly) && !Number.isNaN(originOnly) && originOnly > localOnly) {
|
|
636
|
-
return `origin/${normalizedBaseRef}`;
|
|
637
|
-
}
|
|
767
|
+
const { stdout } = await runGitCommand(["rev-list", "--left-right", "--count", `${normalizedBaseRef}...origin/${normalizedBaseRef}`], { cwd, env: READ_ONLY_GIT_ENV });
|
|
768
|
+
const [localOnlyRaw, originOnlyRaw] = stdout.trim().split(/\s+/);
|
|
769
|
+
const localOnly = Number.parseInt(localOnlyRaw ?? "0", 10);
|
|
770
|
+
const originOnly = Number.parseInt(originOnlyRaw ?? "0", 10);
|
|
771
|
+
if (Number.isNaN(localOnly) || Number.isNaN(originOnly)) {
|
|
772
|
+
return normalizedBaseRef;
|
|
638
773
|
}
|
|
639
|
-
|
|
640
|
-
|
|
774
|
+
if (originOnly > localOnly) {
|
|
775
|
+
return `origin/${normalizedBaseRef}`;
|
|
641
776
|
}
|
|
642
777
|
return normalizedBaseRef;
|
|
643
778
|
}
|
|
@@ -646,7 +781,7 @@ async function getAheadBehind(cwd, baseRef, currentBranch) {
|
|
|
646
781
|
if (!normalizedBaseRef || !currentBranch || normalizedBaseRef === currentBranch) {
|
|
647
782
|
return null;
|
|
648
783
|
}
|
|
649
|
-
const comparisonBaseRef = await resolveBestComparisonBaseRef(cwd,
|
|
784
|
+
const comparisonBaseRef = await resolveBestComparisonBaseRef(cwd, baseRef);
|
|
650
785
|
const { stdout } = await runGitCommand(["rev-list", "--left-right", "--count", `${comparisonBaseRef}...${currentBranch}`], { cwd, env: READ_ONLY_GIT_ENV });
|
|
651
786
|
const [behindRaw, aheadRaw] = stdout.trim().split(/\s+/);
|
|
652
787
|
const behind = Number.parseInt(behindRaw ?? "0", 10);
|
|
@@ -852,7 +987,27 @@ export async function getCheckoutStatus(cwd, context) {
|
|
|
852
987
|
isPaseoOwnedWorktree: false,
|
|
853
988
|
};
|
|
854
989
|
}
|
|
855
|
-
|
|
990
|
+
function parseCheckoutShortstat(text) {
|
|
991
|
+
const trimmed = text.trim();
|
|
992
|
+
if (!trimmed) {
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
let additions = 0;
|
|
996
|
+
let deletions = 0;
|
|
997
|
+
const addMatch = trimmed.match(/(\d+)\s+insertion/);
|
|
998
|
+
if (addMatch) {
|
|
999
|
+
additions = Number.parseInt(addMatch[1], 10);
|
|
1000
|
+
}
|
|
1001
|
+
const delMatch = trimmed.match(/(\d+)\s+deletion/);
|
|
1002
|
+
if (delMatch) {
|
|
1003
|
+
deletions = Number.parseInt(delMatch[1], 10);
|
|
1004
|
+
}
|
|
1005
|
+
if (additions === 0 && deletions === 0) {
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
return { additions, deletions };
|
|
1009
|
+
}
|
|
1010
|
+
async function getCheckoutShortstatUncached(cwd, context) {
|
|
856
1011
|
try {
|
|
857
1012
|
await requireGitRepo(cwd);
|
|
858
1013
|
}
|
|
@@ -862,70 +1017,91 @@ export async function getCheckoutShortstat(cwd, context) {
|
|
|
862
1017
|
const configured = await getConfiguredBaseRefForCwd(cwd, context);
|
|
863
1018
|
const localBaseRef = configured.baseRef ?? (await resolveBaseRef(cwd));
|
|
864
1019
|
const currentBranch = await getCurrentBranch(cwd);
|
|
865
|
-
let
|
|
1020
|
+
let comparisonRef;
|
|
866
1021
|
if (currentBranch && localBaseRef && currentBranch !== localBaseRef) {
|
|
867
|
-
// Feature branch: diff against the merge-base with the base branch
|
|
868
|
-
const comparisonBaseRef = await resolveBestComparisonBaseRef(cwd, normalizeLocalBranchRefName(localBaseRef));
|
|
869
1022
|
try {
|
|
870
|
-
|
|
871
|
-
cwd,
|
|
872
|
-
env: READ_ONLY_GIT_ENV,
|
|
873
|
-
});
|
|
874
|
-
const mergeBase = stdout.trim();
|
|
875
|
-
if (!mergeBase) {
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
diffTarget = mergeBase;
|
|
1023
|
+
comparisonRef = await resolveBestComparisonBaseRef(cwd, localBaseRef);
|
|
879
1024
|
}
|
|
880
1025
|
catch {
|
|
881
1026
|
return null;
|
|
882
1027
|
}
|
|
883
1028
|
}
|
|
884
1029
|
else if (currentBranch) {
|
|
885
|
-
// On the base branch (or no base ref configured): diff against remote tracking branch
|
|
886
1030
|
const hasOrigin = await doesGitRefExist(cwd, `refs/remotes/origin/${currentBranch}`);
|
|
887
1031
|
if (!hasOrigin) {
|
|
888
1032
|
return null;
|
|
889
1033
|
}
|
|
890
|
-
|
|
1034
|
+
comparisonRef = `origin/${currentBranch}`;
|
|
891
1035
|
}
|
|
892
1036
|
else {
|
|
893
1037
|
return null;
|
|
894
1038
|
}
|
|
895
1039
|
try {
|
|
896
|
-
|
|
897
|
-
const { stdout } = await runGitCommand(["diff", "--shortstat", diffTarget], {
|
|
1040
|
+
const { stdout: mergeBaseOut } = await runGitCommand(["merge-base", "HEAD", comparisonRef], {
|
|
898
1041
|
cwd,
|
|
899
1042
|
env: READ_ONLY_GIT_ENV,
|
|
900
1043
|
});
|
|
901
|
-
const
|
|
902
|
-
if (!
|
|
1044
|
+
const mergeBase = mergeBaseOut.trim();
|
|
1045
|
+
if (!mergeBase) {
|
|
903
1046
|
return null;
|
|
904
1047
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}
|
|
911
|
-
const delMatch = text.match(/(\d+)\s+deletion/);
|
|
912
|
-
if (delMatch) {
|
|
913
|
-
deletions = Number.parseInt(delMatch[1], 10);
|
|
914
|
-
}
|
|
915
|
-
if (additions === 0 && deletions === 0) {
|
|
916
|
-
return null;
|
|
917
|
-
}
|
|
918
|
-
return { additions, deletions };
|
|
1048
|
+
const { stdout } = await runGitCommand(["diff", "--shortstat", mergeBase], {
|
|
1049
|
+
cwd,
|
|
1050
|
+
env: READ_ONLY_GIT_ENV,
|
|
1051
|
+
});
|
|
1052
|
+
return parseCheckoutShortstat(stdout);
|
|
919
1053
|
}
|
|
920
1054
|
catch {
|
|
921
1055
|
return null;
|
|
922
1056
|
}
|
|
923
1057
|
}
|
|
1058
|
+
function getOrLoadCheckoutShortstat(cwd, context, options) {
|
|
1059
|
+
const cacheKey = getShortstatCacheKey(cwd);
|
|
1060
|
+
if (!options?.force) {
|
|
1061
|
+
const cached = shortstatCache.get(cacheKey);
|
|
1062
|
+
if (cached !== undefined) {
|
|
1063
|
+
return Promise.resolve(cached);
|
|
1064
|
+
}
|
|
1065
|
+
const existing = shortstatInFlight.get(cacheKey);
|
|
1066
|
+
if (existing) {
|
|
1067
|
+
return existing;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
const load = getCheckoutShortstatUncached(cwd, context)
|
|
1071
|
+
.then((shortstat) => {
|
|
1072
|
+
shortstatCache.set(cacheKey, shortstat);
|
|
1073
|
+
return shortstat;
|
|
1074
|
+
})
|
|
1075
|
+
.finally(() => {
|
|
1076
|
+
shortstatInFlight.delete(cacheKey);
|
|
1077
|
+
});
|
|
1078
|
+
shortstatInFlight.set(cacheKey, load);
|
|
1079
|
+
return load;
|
|
1080
|
+
}
|
|
1081
|
+
export async function getCheckoutShortstat(cwd, context, options) {
|
|
1082
|
+
return getOrLoadCheckoutShortstat(cwd, context, options);
|
|
1083
|
+
}
|
|
1084
|
+
export function getCachedCheckoutShortstat(cwd) {
|
|
1085
|
+
return shortstatCache.get(getShortstatCacheKey(cwd));
|
|
1086
|
+
}
|
|
1087
|
+
export function warmCheckoutShortstatInBackground(cwd, context, onComplete) {
|
|
1088
|
+
const cacheKey = getShortstatCacheKey(cwd);
|
|
1089
|
+
if (shortstatCache.get(cacheKey) !== undefined || shortstatInFlight.has(cacheKey)) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
void getOrLoadCheckoutShortstat(cwd, context)
|
|
1093
|
+
.then(() => {
|
|
1094
|
+
onComplete?.();
|
|
1095
|
+
})
|
|
1096
|
+
.catch(() => {
|
|
1097
|
+
// Non-critical: keep listing path resilient even if git commands fail.
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
924
1100
|
export async function getCheckoutDiff(cwd, compare, context) {
|
|
925
1101
|
await requireGitRepo(cwd);
|
|
926
|
-
let
|
|
1102
|
+
let refsForDiff;
|
|
927
1103
|
if (compare.mode === "uncommitted") {
|
|
928
|
-
|
|
1104
|
+
refsForDiff = { baseRef: "HEAD", includeUntracked: true };
|
|
929
1105
|
}
|
|
930
1106
|
else {
|
|
931
1107
|
const configured = await getConfiguredBaseRefForCwd(cwd, context);
|
|
@@ -936,12 +1112,15 @@ export async function getCheckoutDiff(cwd, compare, context) {
|
|
|
936
1112
|
if (configured.isPaseoOwnedWorktree && compare.baseRef && compare.baseRef !== baseRef) {
|
|
937
1113
|
throw new Error(`Base ref mismatch: expected ${baseRef}, got ${compare.baseRef}`);
|
|
938
1114
|
}
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
|
|
1115
|
+
const bestBaseRef = await resolveBestComparisonBaseRef(cwd, baseRef);
|
|
1116
|
+
refsForDiff = {
|
|
1117
|
+
baseRef: (await tryResolveMergeBase(cwd, bestBaseRef)) ?? bestBaseRef,
|
|
1118
|
+
targetRef: "HEAD",
|
|
1119
|
+
includeUntracked: false,
|
|
1120
|
+
};
|
|
942
1121
|
}
|
|
943
1122
|
const ignoreWhitespace = compare.ignoreWhitespace === true;
|
|
944
|
-
const changes = await listCheckoutFileChanges(cwd,
|
|
1123
|
+
const changes = await listCheckoutFileChanges(cwd, refsForDiff, ignoreWhitespace);
|
|
945
1124
|
changes.sort((a, b) => {
|
|
946
1125
|
if (a.path === b.path)
|
|
947
1126
|
return 0;
|
|
@@ -971,7 +1150,7 @@ export async function getCheckoutDiff(cwd, compare, context) {
|
|
|
971
1150
|
const untrackedChanges = changes.filter((change) => change.isUntracked === true);
|
|
972
1151
|
const trackedChangeByPath = new Map(trackedChanges.map((change) => [change.path, change]));
|
|
973
1152
|
const trackedNumstatByPath = trackedChanges.length > 0
|
|
974
|
-
? await getTrackedNumstatByPath(cwd,
|
|
1153
|
+
? await getTrackedNumstatByPath(cwd, refsForDiff, ignoreWhitespace)
|
|
975
1154
|
: new Map();
|
|
976
1155
|
const trackedDiffPaths = [];
|
|
977
1156
|
const trackedPlaceholderByPath = new Map();
|
|
@@ -992,7 +1171,7 @@ export async function getCheckoutDiff(cwd, compare, context) {
|
|
|
992
1171
|
if (trackedDiffPaths.length > 0) {
|
|
993
1172
|
const trackedDiffResult = await runGitCommand(buildGitDiffArgs({
|
|
994
1173
|
ignoreWhitespace,
|
|
995
|
-
extra: [
|
|
1174
|
+
extra: [...getCheckoutDiffRefArgs(refsForDiff), "--", ...trackedDiffPaths],
|
|
996
1175
|
}), {
|
|
997
1176
|
cwd,
|
|
998
1177
|
env: READ_ONLY_GIT_ENV,
|
|
@@ -1021,7 +1200,13 @@ export async function getCheckoutDiff(cwd, compare, context) {
|
|
|
1021
1200
|
return null;
|
|
1022
1201
|
}
|
|
1023
1202
|
const refPath = change.oldPath ?? change.path;
|
|
1024
|
-
return readGitFileContentAtRef(cwd,
|
|
1203
|
+
return readGitFileContentAtRef(cwd, refsForDiff.baseRef, refPath);
|
|
1204
|
+
},
|
|
1205
|
+
getNewFileContent: async (file) => {
|
|
1206
|
+
if (!refsForDiff.targetRef) {
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
return readGitFileContentAtRef(cwd, refsForDiff.targetRef, file.path);
|
|
1025
1210
|
},
|
|
1026
1211
|
})
|
|
1027
1212
|
: [];
|
|
@@ -1051,7 +1236,10 @@ export async function getCheckoutDiff(cwd, compare, context) {
|
|
|
1051
1236
|
// `git diff -w --name-status` can still report a modified path even when the
|
|
1052
1237
|
// whitespace-filtered patch and numstat are both empty. Skip emitting a
|
|
1053
1238
|
// structured placeholder in that case so whitespace-only edits truly disappear.
|
|
1054
|
-
if (ignoreWhitespace &&
|
|
1239
|
+
if (ignoreWhitespace &&
|
|
1240
|
+
!trackedDiffTruncated &&
|
|
1241
|
+
change.status.startsWith("M") &&
|
|
1242
|
+
(!stat || (!stat.isBinary && stat.additions === 0 && stat.deletions === 0))) {
|
|
1055
1243
|
continue;
|
|
1056
1244
|
}
|
|
1057
1245
|
structured.push({
|
|
@@ -1153,10 +1341,10 @@ export async function mergeToBase(cwd, options = {}, context) {
|
|
|
1153
1341
|
}
|
|
1154
1342
|
let normalizedBaseRef = baseRef;
|
|
1155
1343
|
normalizedBaseRef = normalizeLocalBranchRefName(normalizedBaseRef);
|
|
1344
|
+
const currentWorktreeRoot = (await getWorktreeRoot(cwd)) ?? cwd;
|
|
1156
1345
|
if (normalizedBaseRef === currentBranch) {
|
|
1157
|
-
return;
|
|
1346
|
+
return currentWorktreeRoot;
|
|
1158
1347
|
}
|
|
1159
|
-
const currentWorktreeRoot = (await getWorktreeRoot(cwd)) ?? cwd;
|
|
1160
1348
|
const baseWorktree = await getWorktreePathForBranch(cwd, normalizedBaseRef);
|
|
1161
1349
|
const operationCwd = baseWorktree ?? currentWorktreeRoot;
|
|
1162
1350
|
const isSameCheckout = resolve(operationCwd) === resolve(currentWorktreeRoot);
|
|
@@ -1246,6 +1434,7 @@ export async function mergeToBase(cwd, options = {}, context) {
|
|
|
1246
1434
|
}
|
|
1247
1435
|
}
|
|
1248
1436
|
}
|
|
1437
|
+
return operationCwd;
|
|
1249
1438
|
}
|
|
1250
1439
|
export async function mergeFromBase(cwd, options = {}, context) {
|
|
1251
1440
|
await requireGitRepo(cwd);
|
|
@@ -1272,7 +1461,7 @@ export async function mergeFromBase(cwd, options = {}, context) {
|
|
|
1272
1461
|
}
|
|
1273
1462
|
}
|
|
1274
1463
|
const normalizedBaseRef = normalizeLocalBranchRefName(baseRef);
|
|
1275
|
-
const bestBaseRef = await
|
|
1464
|
+
const bestBaseRef = await resolveMostAheadBaseRef(cwd, normalizedBaseRef);
|
|
1276
1465
|
if (bestBaseRef === currentBranch) {
|
|
1277
1466
|
return;
|
|
1278
1467
|
}
|
|
@@ -1331,7 +1520,7 @@ export async function mergeFromBase(cwd, options = {}, context) {
|
|
|
1331
1520
|
throw error;
|
|
1332
1521
|
}
|
|
1333
1522
|
}
|
|
1334
|
-
export async function pullCurrentBranch(cwd) {
|
|
1523
|
+
export async function pullCurrentBranch(cwd, github) {
|
|
1335
1524
|
await requireGitRepo(cwd);
|
|
1336
1525
|
const currentBranch = await getCurrentBranch(cwd);
|
|
1337
1526
|
if (!currentBranch || currentBranch === "HEAD") {
|
|
@@ -1343,13 +1532,14 @@ export async function pullCurrentBranch(cwd) {
|
|
|
1343
1532
|
}
|
|
1344
1533
|
try {
|
|
1345
1534
|
await runGitCommand(["pull"], { cwd, timeout: 120000 });
|
|
1535
|
+
github?.invalidate({ cwd });
|
|
1346
1536
|
}
|
|
1347
1537
|
catch (error) {
|
|
1348
1538
|
await abortGitPullConflictState(cwd);
|
|
1349
1539
|
throw error;
|
|
1350
1540
|
}
|
|
1351
1541
|
}
|
|
1352
|
-
export async function pushCurrentBranch(cwd) {
|
|
1542
|
+
export async function pushCurrentBranch(cwd, github) {
|
|
1353
1543
|
await requireGitRepo(cwd);
|
|
1354
1544
|
const currentBranch = await getCurrentBranch(cwd);
|
|
1355
1545
|
if (!currentBranch || currentBranch === "HEAD") {
|
|
@@ -1360,80 +1550,11 @@ export async function pushCurrentBranch(cwd) {
|
|
|
1360
1550
|
throw new Error("Remote 'origin' is not configured.");
|
|
1361
1551
|
}
|
|
1362
1552
|
await runGitCommand(["push", "-u", "origin", currentBranch], { cwd, timeout: 120000 });
|
|
1553
|
+
github?.invalidate({ cwd });
|
|
1363
1554
|
}
|
|
1364
|
-
export async function
|
|
1365
|
-
if (cachedGhPath === undefined) {
|
|
1366
|
-
cachedGhPath = await findExecutable("gh");
|
|
1367
|
-
}
|
|
1368
|
-
if (cachedGhPath === null) {
|
|
1369
|
-
throw new Error("GitHub CLI (gh) is not installed or not in PATH");
|
|
1370
|
-
}
|
|
1371
|
-
return cachedGhPath;
|
|
1372
|
-
}
|
|
1373
|
-
function getCommandErrorText(error) {
|
|
1374
|
-
if (!(error instanceof Error)) {
|
|
1375
|
-
return String(error);
|
|
1376
|
-
}
|
|
1377
|
-
const stderr = typeof error?.stderr === "string" ? error.stderr : "";
|
|
1378
|
-
const stdout = typeof error?.stdout === "string" ? error.stdout : "";
|
|
1379
|
-
return `${error.message}\n${stderr}\n${stdout}`.toLowerCase();
|
|
1380
|
-
}
|
|
1381
|
-
function isGhAuthError(error) {
|
|
1382
|
-
const text = getCommandErrorText(error);
|
|
1383
|
-
return (text.includes("gh auth login") ||
|
|
1384
|
-
text.includes("not logged into any github hosts") ||
|
|
1385
|
-
text.includes("authentication failed") ||
|
|
1386
|
-
text.includes("authentication required") ||
|
|
1387
|
-
text.includes("bad credentials") ||
|
|
1388
|
-
text.includes("http 401"));
|
|
1389
|
-
}
|
|
1390
|
-
async function resolveGitHubRepo(cwd) {
|
|
1391
|
-
try {
|
|
1392
|
-
const { stdout } = await runGitCommand(["config", "--get", "remote.origin.url"], {
|
|
1393
|
-
cwd,
|
|
1394
|
-
env: READ_ONLY_GIT_ENV,
|
|
1395
|
-
});
|
|
1396
|
-
const url = stdout.trim();
|
|
1397
|
-
if (!url) {
|
|
1398
|
-
return null;
|
|
1399
|
-
}
|
|
1400
|
-
let cleaned = url;
|
|
1401
|
-
if (cleaned.startsWith("git@github.com:")) {
|
|
1402
|
-
cleaned = cleaned.slice("git@github.com:".length);
|
|
1403
|
-
}
|
|
1404
|
-
else if (cleaned.startsWith("https://github.com/")) {
|
|
1405
|
-
cleaned = cleaned.slice("https://github.com/".length);
|
|
1406
|
-
}
|
|
1407
|
-
else if (cleaned.startsWith("http://github.com/")) {
|
|
1408
|
-
cleaned = cleaned.slice("http://github.com/".length);
|
|
1409
|
-
}
|
|
1410
|
-
else {
|
|
1411
|
-
const marker = "github.com/";
|
|
1412
|
-
const index = cleaned.indexOf(marker);
|
|
1413
|
-
if (index !== -1) {
|
|
1414
|
-
cleaned = cleaned.slice(index + marker.length);
|
|
1415
|
-
}
|
|
1416
|
-
else {
|
|
1417
|
-
return null;
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
if (cleaned.endsWith(".git")) {
|
|
1421
|
-
cleaned = cleaned.slice(0, -".git".length);
|
|
1422
|
-
}
|
|
1423
|
-
if (!cleaned.includes("/")) {
|
|
1424
|
-
return null;
|
|
1425
|
-
}
|
|
1426
|
-
return cleaned;
|
|
1427
|
-
}
|
|
1428
|
-
catch {
|
|
1429
|
-
// ignore
|
|
1430
|
-
}
|
|
1431
|
-
return null;
|
|
1432
|
-
}
|
|
1433
|
-
export async function createPullRequest(cwd, options) {
|
|
1555
|
+
export async function createPullRequest(cwd, options, github = createGitHubService(), workspaceGitService) {
|
|
1434
1556
|
await requireGitRepo(cwd);
|
|
1435
|
-
const
|
|
1436
|
-
const repo = await resolveGitHubRepo(cwd);
|
|
1557
|
+
const repo = await resolveGitHubRepo(cwd, { workspaceGitService });
|
|
1437
1558
|
if (!repo) {
|
|
1438
1559
|
throw new Error("Unable to determine GitHub repo from git remote");
|
|
1439
1560
|
}
|
|
@@ -1451,31 +1572,30 @@ export async function createPullRequest(cwd, options) {
|
|
|
1451
1572
|
throw new Error(`Base ref mismatch: expected ${base}, got ${options.base}`);
|
|
1452
1573
|
}
|
|
1453
1574
|
await runGitCommand(["push", "-u", "origin", head], { cwd, timeout: 120000 });
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
throw new Error("GitHub CLI did not return PR url/number");
|
|
1465
|
-
}
|
|
1466
|
-
return { url: parsed.url, number: parsed.number };
|
|
1575
|
+
const result = await github.createPullRequest({
|
|
1576
|
+
cwd,
|
|
1577
|
+
repo,
|
|
1578
|
+
title: options.title,
|
|
1579
|
+
body: options.body,
|
|
1580
|
+
head,
|
|
1581
|
+
base: normalizedBase,
|
|
1582
|
+
});
|
|
1583
|
+
github.invalidate({ cwd });
|
|
1584
|
+
return result;
|
|
1467
1585
|
}
|
|
1468
|
-
export async function getPullRequestStatus(cwd) {
|
|
1586
|
+
export async function getPullRequestStatus(cwd, github = createGitHubService(), options) {
|
|
1469
1587
|
const cacheKey = getPullRequestStatusCacheKey(cwd);
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1588
|
+
if (!options?.force) {
|
|
1589
|
+
const cached = pullRequestStatusCache.get(cacheKey);
|
|
1590
|
+
if (cached) {
|
|
1591
|
+
return cached;
|
|
1592
|
+
}
|
|
1593
|
+
const existing = pullRequestStatusInFlight.get(cacheKey);
|
|
1594
|
+
if (existing) {
|
|
1595
|
+
return existing;
|
|
1596
|
+
}
|
|
1477
1597
|
}
|
|
1478
|
-
const lookup = getPullRequestStatusUncached(cwd)
|
|
1598
|
+
const lookup = getPullRequestStatusUncached(cwd, github, options)
|
|
1479
1599
|
.then((status) => {
|
|
1480
1600
|
pullRequestStatusCache.set(cacheKey, status);
|
|
1481
1601
|
return status;
|
|
@@ -1486,7 +1606,7 @@ export async function getPullRequestStatus(cwd) {
|
|
|
1486
1606
|
pullRequestStatusInFlight.set(cacheKey, lookup);
|
|
1487
1607
|
return lookup;
|
|
1488
1608
|
}
|
|
1489
|
-
async function getPullRequestStatusUncached(cwd) {
|
|
1609
|
+
async function getPullRequestStatusUncached(cwd, github, options) {
|
|
1490
1610
|
await requireGitRepo(cwd);
|
|
1491
1611
|
const head = await getCurrentBranch(cwd);
|
|
1492
1612
|
if (!head) {
|
|
@@ -1495,49 +1615,21 @@ async function getPullRequestStatusUncached(cwd) {
|
|
|
1495
1615
|
githubFeaturesEnabled: false,
|
|
1496
1616
|
};
|
|
1497
1617
|
}
|
|
1498
|
-
let ghPath;
|
|
1499
|
-
try {
|
|
1500
|
-
ghPath = await resolveGhPath();
|
|
1501
|
-
}
|
|
1502
|
-
catch {
|
|
1503
|
-
return {
|
|
1504
|
-
status: null,
|
|
1505
|
-
githubFeaturesEnabled: false,
|
|
1506
|
-
};
|
|
1507
|
-
}
|
|
1508
1618
|
try {
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
}
|
|
1514
|
-
const mergedAt = typeof pr.mergedAt === "string" && pr.mergedAt.trim().length > 0 ? pr.mergedAt : null;
|
|
1515
|
-
const state = mergedAt !== null
|
|
1516
|
-
? "merged"
|
|
1517
|
-
: typeof pr.state === "string" && pr.state.trim().length > 0
|
|
1518
|
-
? pr.state.toLowerCase()
|
|
1519
|
-
: "";
|
|
1619
|
+
const status = await github.getCurrentPullRequestStatus({
|
|
1620
|
+
cwd,
|
|
1621
|
+
headRef: head,
|
|
1622
|
+
reason: options?.reason,
|
|
1623
|
+
});
|
|
1520
1624
|
return {
|
|
1521
|
-
status
|
|
1522
|
-
url: pr.url,
|
|
1523
|
-
title: pr.title,
|
|
1524
|
-
state,
|
|
1525
|
-
baseRefName: pr.baseRefName ?? "",
|
|
1526
|
-
headRefName: pr.headRefName ?? head,
|
|
1527
|
-
isMerged: mergedAt !== null,
|
|
1528
|
-
},
|
|
1625
|
+
status,
|
|
1529
1626
|
githubFeaturesEnabled: true,
|
|
1530
1627
|
};
|
|
1531
1628
|
}
|
|
1532
1629
|
catch (error) {
|
|
1533
|
-
if (
|
|
1630
|
+
if (error instanceof GitHubCliMissingError || error instanceof GitHubAuthenticationError) {
|
|
1534
1631
|
return { status: null, githubFeaturesEnabled: false };
|
|
1535
1632
|
}
|
|
1536
|
-
// gh pr view exits non-zero when no PR exists for the branch
|
|
1537
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1538
|
-
if (message.includes("no pull requests found") || message.includes("Could not resolve")) {
|
|
1539
|
-
return { status: null, githubFeaturesEnabled: true };
|
|
1540
|
-
}
|
|
1541
1633
|
throw error;
|
|
1542
1634
|
}
|
|
1543
1635
|
}
|