@jmoyers/harness 0.1.10 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -35
- package/package.json +31 -11
- package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
- package/packages/harness-ai/src/stream-text.ts +13 -91
- package/packages/harness-ui/src/frame-primitives.ts +158 -0
- package/packages/harness-ui/src/index.ts +18 -0
- package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
- package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
- package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
- package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
- package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
- package/packages/harness-ui/src/interaction/input.ts +420 -0
- package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
- package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
- package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
- package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
- package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
- package/packages/harness-ui/src/kit.ts +476 -0
- package/packages/harness-ui/src/layout.ts +238 -0
- package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
- package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
- package/packages/harness-ui/src/surface.ts +252 -0
- package/packages/harness-ui/src/text-layout.ts +210 -0
- package/packages/nim-core/src/contracts.ts +239 -0
- package/packages/nim-core/src/event-store.ts +299 -0
- package/packages/nim-core/src/events.ts +53 -0
- package/packages/nim-core/src/index.ts +9 -0
- package/packages/nim-core/src/provider-router.ts +129 -0
- package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
- package/packages/nim-core/src/runtime-factory.ts +49 -0
- package/packages/nim-core/src/runtime.ts +1797 -0
- package/packages/nim-core/src/session-store.ts +516 -0
- package/packages/nim-core/src/telemetry.ts +48 -0
- package/packages/nim-test-tui/src/index.ts +150 -0
- package/packages/nim-ui-core/src/index.ts +1 -0
- package/packages/nim-ui-core/src/projection.ts +87 -0
- package/scripts/codex-live-mux-runtime.ts +2 -3721
- package/scripts/control-plane-daemon.ts +24 -2
- package/scripts/harness-bin.js +5 -0
- package/scripts/harness-commands.ts +300 -0
- package/scripts/harness-runtime.ts +82 -0
- package/scripts/harness.ts +33 -3007
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -0
- package/src/cli/default-gateway-pointer.ts +193 -0
- package/src/cli/gateway/runtime.ts +1872 -0
- package/src/cli/parsing/flags.ts +23 -0
- package/src/cli/parsing/session.ts +42 -0
- package/src/cli/runtime/context.ts +193 -0
- package/src/cli/runtime-app/application.ts +392 -0
- package/src/cli/runtime-infra/gateway-control.ts +729 -0
- package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
- package/src/cli/workflows/runtime.ts +965 -0
- package/src/clients/tui/left-rail-interactions.ts +519 -0
- package/src/clients/tui/main-pane-interactions.ts +509 -0
- package/src/clients/tui/modal-input-routing.ts +71 -0
- package/src/clients/tui/render-snapshot-adapter.ts +88 -0
- package/src/clients/web/synced-selectors.ts +132 -0
- package/src/codex/live-session.ts +82 -29
- package/src/config/config-core.ts +361 -10
- package/src/config/harness-paths.ts +4 -7
- package/src/config/harness-runtime-migration.ts +142 -19
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/config/secrets-core.ts +92 -4
- package/src/control-plane/agent-realtime-api.ts +82 -427
- package/src/control-plane/prompt/thread-title-namer.ts +49 -23
- package/src/control-plane/session-summary.ts +10 -81
- package/src/control-plane/status/reducer-base.ts +12 -12
- package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
- package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
- package/src/control-plane/stream-client.ts +12 -2
- package/src/control-plane/stream-command-parser.ts +83 -143
- package/src/control-plane/stream-protocol.ts +53 -37
- package/src/control-plane/stream-server-background.ts +18 -2
- package/src/control-plane/stream-server-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +943 -80
- package/src/control-plane/stream-session-runtime-types.ts +41 -0
- package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
- package/src/core/state/observed-stream-cursor.ts +43 -0
- package/src/core/state/synced-observed-state.ts +273 -0
- package/src/core/store/harness-synced-store.ts +81 -0
- package/src/diff/budget.ts +136 -0
- package/src/diff/build.ts +289 -0
- package/src/diff/chunker.ts +146 -0
- package/src/diff/git-invoke.ts +315 -0
- package/src/diff/git-parse.ts +472 -0
- package/src/diff/hash.ts +70 -0
- package/src/diff/index.ts +24 -0
- package/src/diff/normalize.ts +134 -0
- package/src/diff/types.ts +178 -0
- package/src/diff-ui/args.ts +346 -0
- package/src/diff-ui/commands.ts +123 -0
- package/src/diff-ui/finder.ts +94 -0
- package/src/diff-ui/highlight.ts +127 -0
- package/src/diff-ui/index.ts +2 -0
- package/src/diff-ui/model.ts +141 -0
- package/src/diff-ui/pager.ts +412 -0
- package/src/diff-ui/render.ts +337 -0
- package/src/diff-ui/runtime.ts +379 -0
- package/src/diff-ui/state.ts +224 -0
- package/src/diff-ui/types.ts +236 -0
- package/src/domain/conversations.ts +11 -7
- package/src/domain/workspace.ts +76 -4
- package/src/mux/control-plane-op-queue.ts +93 -7
- package/src/mux/conversation-rail.ts +28 -71
- package/src/mux/dual-pane-core.ts +13 -13
- package/src/mux/harness-core-ui.ts +313 -42
- package/src/mux/input-shortcuts.ts +22 -112
- package/src/mux/keybinding-catalog.ts +340 -0
- package/src/mux/keybinding-registry.ts +103 -0
- package/src/mux/live-mux/command-menu-open-in.ts +280 -0
- package/src/mux/live-mux/command-menu.ts +167 -4
- package/src/mux/live-mux/conversation-state.ts +13 -0
- package/src/mux/live-mux/directory-resolution.ts +1 -1
- package/src/mux/live-mux/git-parsing.ts +16 -0
- package/src/mux/live-mux/git-snapshot.ts +33 -2
- package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
- package/src/mux/live-mux/home-pane-drop.ts +1 -1
- package/src/mux/live-mux/home-pane-pointer.ts +10 -0
- package/src/mux/live-mux/input-forwarding.ts +59 -2
- package/src/mux/live-mux/left-nav-activation.ts +124 -7
- package/src/mux/live-mux/left-nav.ts +35 -0
- package/src/mux/live-mux/link-click.ts +292 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
- package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
- package/src/mux/live-mux/modal-input-reducers.ts +106 -8
- package/src/mux/live-mux/modal-overlays.ts +210 -31
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
- package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
- package/src/mux/live-mux/pointer-routing.ts +5 -2
- package/src/mux/live-mux/project-pane-pointer.ts +8 -0
- package/src/mux/live-mux/rail-layout.ts +33 -30
- package/src/mux/live-mux/release-notes.ts +383 -0
- package/src/mux/live-mux/render-trace-analysis.ts +52 -7
- package/src/mux/live-mux/repository-folding.ts +3 -0
- package/src/mux/live-mux/selection.ts +0 -4
- package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
- package/src/mux/project-pane-github-review.ts +271 -0
- package/src/mux/render-frame.ts +4 -0
- package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
- package/src/mux/task-composer.ts +21 -14
- package/src/mux/task-focused-pane.ts +118 -117
- package/src/mux/task-screen-keybindings.ts +19 -82
- package/src/mux/workspace-rail-model.ts +270 -104
- package/src/mux/workspace-rail.ts +45 -22
- package/src/pty/session-broker.ts +1 -1
- package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
- package/src/services/control-plane.ts +50 -32
- package/src/services/conversation-lifecycle.ts +118 -87
- package/src/services/conversation-startup-hydration.ts +20 -12
- package/src/services/directory-hydration.ts +21 -16
- package/src/services/event-persistence.ts +7 -0
- package/src/services/left-rail-pointer-handler.ts +329 -0
- package/src/services/mux-ui-state-persistence.ts +5 -1
- package/src/services/recording.ts +34 -26
- package/src/services/runtime-command-menu-agent-tools.ts +1 -1
- package/src/services/runtime-control-actions.ts +79 -61
- package/src/services/runtime-control-plane-ops.ts +122 -83
- package/src/services/runtime-conversation-actions.ts +40 -26
- package/src/services/runtime-conversation-activation.ts +82 -30
- package/src/services/runtime-conversation-starter.ts +80 -48
- package/src/services/runtime-conversation-title-edit.ts +91 -80
- package/src/services/runtime-envelope-handler.ts +107 -105
- package/src/services/runtime-git-state.ts +42 -29
- package/src/services/runtime-layout-resize.ts +3 -1
- package/src/services/runtime-left-rail-render.ts +99 -63
- package/src/services/runtime-nim-cli-session.ts +438 -0
- package/src/services/runtime-nim-session.ts +705 -0
- package/src/services/runtime-nim-tool-bridge.ts +141 -0
- package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
- package/src/services/runtime-process-wiring.ts +29 -36
- package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
- package/src/services/runtime-render-flush.ts +63 -70
- package/src/services/runtime-render-lifecycle.ts +65 -64
- package/src/services/runtime-render-orchestrator.ts +55 -45
- package/src/services/runtime-render-pipeline.ts +106 -103
- package/src/services/runtime-render-state.ts +62 -49
- package/src/services/runtime-repository-actions.ts +97 -70
- package/src/services/runtime-right-pane-render.ts +80 -53
- package/src/services/runtime-shutdown.ts +38 -35
- package/src/services/runtime-stream-subscriptions.ts +35 -27
- package/src/services/runtime-task-composer-persistence.ts +71 -59
- package/src/services/runtime-task-composer-snapshot.ts +14 -0
- package/src/services/runtime-task-editor-actions.ts +46 -29
- package/src/services/runtime-task-pane-actions.ts +220 -134
- package/src/services/runtime-task-pane-shortcuts.ts +323 -123
- package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
- package/src/services/runtime-workspace-observed-events.ts +33 -184
- package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
- package/src/services/session-diagnostics-store.ts +217 -0
- package/src/services/startup-background-resume.ts +26 -21
- package/src/services/startup-orchestrator.ts +16 -13
- package/src/services/startup-paint-tracker.ts +29 -21
- package/src/services/startup-persisted-conversation-queue.ts +19 -13
- package/src/services/startup-settled-gate.ts +25 -15
- package/src/services/startup-shutdown.ts +18 -22
- package/src/services/startup-state-hydration.ts +44 -34
- package/src/services/startup-visibility.ts +12 -4
- package/src/services/task-pane-selection-actions.ts +89 -72
- package/src/services/task-planning-hydration.ts +24 -18
- package/src/services/task-planning-observed-events.ts +50 -52
- package/src/services/workspace-observed-events.ts +66 -63
- package/src/storage/storage-lifecycle-core.ts +438 -0
- package/src/store/control-plane-store-normalize.ts +33 -242
- package/src/store/control-plane-store-types.ts +1 -35
- package/src/store/control-plane-store.ts +396 -56
- package/src/store/event-store.ts +397 -3
- package/src/terminal/snapshot-oracle.ts +207 -94
- package/src/ui/mux-theme.ts +112 -8
- package/src/ui/panes/home-gridfire.ts +40 -31
- package/src/ui/panes/home.ts +10 -2
- package/src/ui/panes/nim.ts +315 -0
- package/src/mux/live-mux/actions-task.ts +0 -115
- package/src/mux/live-mux/left-rail-actions.ts +0 -118
- package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
- package/src/mux/live-mux/left-rail-pointer.ts +0 -74
- package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
- package/src/services/runtime-directory-actions.ts +0 -164
- package/src/services/runtime-input-pipeline.ts +0 -50
- package/src/services/runtime-input-router.ts +0 -189
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -119
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -278
- package/src/services/runtime-task-pane.ts +0 -62
- package/src/services/runtime-workspace-actions.ts +0 -158
- package/src/ui/conversation-input-forwarder.ts +0 -114
- package/src/ui/conversation-selection-input.ts +0 -103
- package/src/ui/global-shortcut-input.ts +0 -89
- package/src/ui/input.ts +0 -238
- package/src/ui/kit.ts +0 -509
- package/src/ui/left-nav-input.ts +0 -80
- package/src/ui/left-rail-pointer-input.ts +0 -148
- package/src/ui/repository-fold-input.ts +0 -91
- package/src/ui/surface.ts +0 -224
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import type { DiffMode } from './types.ts';
|
|
3
|
+
|
|
4
|
+
type GitDiffCommandKind = 'patch' | 'name-status' | 'numstat';
|
|
5
|
+
|
|
6
|
+
export interface GitDiffInvocationOptions {
|
|
7
|
+
readonly cwd: string;
|
|
8
|
+
readonly mode: DiffMode;
|
|
9
|
+
readonly baseRef: string | null;
|
|
10
|
+
readonly headRef: string | null;
|
|
11
|
+
readonly noRenames: boolean;
|
|
12
|
+
readonly renameLimit: number | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface GitDiffPreflight {
|
|
16
|
+
readonly filesChanged: number;
|
|
17
|
+
readonly additions: number;
|
|
18
|
+
readonly deletions: number;
|
|
19
|
+
readonly binaryFiles: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ResolveRangeBaseRefOptions {
|
|
23
|
+
readonly cwd: string;
|
|
24
|
+
readonly headRef: string;
|
|
25
|
+
readonly timeoutMs: number;
|
|
26
|
+
readonly runCommand?: ResolveRangeBaseRefCommandRunner;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface StreamGitLinesInput {
|
|
30
|
+
readonly cwd: string;
|
|
31
|
+
readonly args: readonly string[];
|
|
32
|
+
readonly timeoutMs: number;
|
|
33
|
+
readonly onLine?: (line: string) => boolean | void;
|
|
34
|
+
readonly onBytes?: (bytes: number) => boolean | void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface StreamGitLinesResult {
|
|
38
|
+
readonly exitCode: number;
|
|
39
|
+
readonly signal: NodeJS.Signals | null;
|
|
40
|
+
readonly aborted: boolean;
|
|
41
|
+
readonly timedOut: boolean;
|
|
42
|
+
readonly bytesRead: number;
|
|
43
|
+
readonly peakLineBufferBytes: number;
|
|
44
|
+
readonly stderr: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface GitCommandOutput {
|
|
48
|
+
readonly exitCode: number;
|
|
49
|
+
readonly aborted: boolean;
|
|
50
|
+
readonly timedOut: boolean;
|
|
51
|
+
readonly stdout: string;
|
|
52
|
+
readonly stderr: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type ResolveRangeBaseRefCommandRunner = (
|
|
56
|
+
cwd: string,
|
|
57
|
+
args: readonly string[],
|
|
58
|
+
timeoutMs: number,
|
|
59
|
+
) => Promise<GitCommandOutput>;
|
|
60
|
+
|
|
61
|
+
function trimLineEnding(line: string): string {
|
|
62
|
+
return line.endsWith('\r') ? line.slice(0, -1) : line;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseFiniteInteger(value: string): number {
|
|
66
|
+
const parsed = Number.parseInt(value, 10);
|
|
67
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function firstNonEmptyLine(text: string): string | null {
|
|
71
|
+
for (const line of text.split('\n')) {
|
|
72
|
+
const trimmed = line.trim();
|
|
73
|
+
if (trimmed.length > 0) {
|
|
74
|
+
return trimmed;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function buildGitDiffArgs(
|
|
81
|
+
options: GitDiffInvocationOptions,
|
|
82
|
+
kind: GitDiffCommandKind,
|
|
83
|
+
): readonly string[] {
|
|
84
|
+
const args: string[] = ['diff', '--no-ext-diff', '--no-color'];
|
|
85
|
+
if (options.noRenames) {
|
|
86
|
+
args.push('--no-renames');
|
|
87
|
+
} else if (options.renameLimit !== null) {
|
|
88
|
+
args.push(`-l${String(options.renameLimit)}`);
|
|
89
|
+
}
|
|
90
|
+
if (kind === 'patch') {
|
|
91
|
+
args.push('--patch', '--binary');
|
|
92
|
+
} else if (kind === 'name-status') {
|
|
93
|
+
args.push('--name-status');
|
|
94
|
+
} else {
|
|
95
|
+
args.push('--numstat');
|
|
96
|
+
}
|
|
97
|
+
if (options.mode === 'staged') {
|
|
98
|
+
args.push('--cached');
|
|
99
|
+
} else if (options.mode === 'range') {
|
|
100
|
+
if (options.baseRef === null || options.headRef === null) {
|
|
101
|
+
throw new Error('range diff requires baseRef and headRef');
|
|
102
|
+
}
|
|
103
|
+
args.push(options.baseRef, options.headRef);
|
|
104
|
+
}
|
|
105
|
+
return args;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function streamGitLines(input: StreamGitLinesInput): Promise<StreamGitLinesResult> {
|
|
109
|
+
return await new Promise<StreamGitLinesResult>((resolve, reject) => {
|
|
110
|
+
const child = spawn('git', [...input.args], {
|
|
111
|
+
cwd: input.cwd,
|
|
112
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
113
|
+
env: {
|
|
114
|
+
...process.env,
|
|
115
|
+
GIT_PAGER: 'cat',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
let timedOut = false;
|
|
119
|
+
let aborted = false;
|
|
120
|
+
let bytesRead = 0;
|
|
121
|
+
let peakLineBufferBytes = 0;
|
|
122
|
+
let pending = '';
|
|
123
|
+
let stderr = '';
|
|
124
|
+
|
|
125
|
+
const abort = (markTimedOut = false): void => {
|
|
126
|
+
aborted = true;
|
|
127
|
+
if (markTimedOut) {
|
|
128
|
+
timedOut = true;
|
|
129
|
+
}
|
|
130
|
+
child.kill('SIGTERM');
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const timeout = setTimeout(() => abort(true), Math.max(1, Math.floor(input.timeoutMs)));
|
|
134
|
+
|
|
135
|
+
child.once('error', (error) => {
|
|
136
|
+
clearTimeout(timeout);
|
|
137
|
+
reject(error);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
child.stdout.on('data', (chunk: Buffer) => {
|
|
141
|
+
bytesRead += chunk.length;
|
|
142
|
+
if (input.onBytes !== undefined) {
|
|
143
|
+
const keepReading = input.onBytes(chunk.length);
|
|
144
|
+
if (keepReading === false) {
|
|
145
|
+
abort();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
pending += chunk.toString('utf8');
|
|
149
|
+
const pendingBytes = Buffer.byteLength(pending);
|
|
150
|
+
if (pendingBytes > peakLineBufferBytes) {
|
|
151
|
+
peakLineBufferBytes = pendingBytes;
|
|
152
|
+
}
|
|
153
|
+
while (true) {
|
|
154
|
+
const newlineIndex = pending.indexOf('\n');
|
|
155
|
+
if (newlineIndex < 0) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
const line = trimLineEnding(pending.slice(0, newlineIndex));
|
|
159
|
+
pending = pending.slice(newlineIndex + 1);
|
|
160
|
+
if (input.onLine !== undefined) {
|
|
161
|
+
const keepReading = input.onLine(line);
|
|
162
|
+
if (keepReading === false) {
|
|
163
|
+
abort();
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
child.stderr.on('data', (chunk: Buffer) => {
|
|
171
|
+
stderr += chunk.toString('utf8');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
child.once('close', (code, signal) => {
|
|
175
|
+
if (pending.length > 0 && input.onLine !== undefined) {
|
|
176
|
+
const keepReading = input.onLine(trimLineEnding(pending));
|
|
177
|
+
if (keepReading === false) {
|
|
178
|
+
aborted = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
clearTimeout(timeout);
|
|
182
|
+
resolve({
|
|
183
|
+
exitCode: code ?? 1,
|
|
184
|
+
signal,
|
|
185
|
+
aborted,
|
|
186
|
+
timedOut,
|
|
187
|
+
bytesRead,
|
|
188
|
+
peakLineBufferBytes,
|
|
189
|
+
stderr: stderr.trim(),
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function runGitCommandCapture(
|
|
196
|
+
cwd: string,
|
|
197
|
+
args: readonly string[],
|
|
198
|
+
timeoutMs: number,
|
|
199
|
+
): Promise<GitCommandOutput> {
|
|
200
|
+
const lines: string[] = [];
|
|
201
|
+
const result = await streamGitLines({
|
|
202
|
+
cwd,
|
|
203
|
+
args,
|
|
204
|
+
timeoutMs,
|
|
205
|
+
onLine: (line) => {
|
|
206
|
+
lines.push(line);
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
exitCode: result.exitCode,
|
|
211
|
+
aborted: result.aborted,
|
|
212
|
+
timedOut: result.timedOut,
|
|
213
|
+
stdout: lines.join('\n').trim(),
|
|
214
|
+
stderr: result.stderr,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function resolveRangeBaseTargetRef(
|
|
219
|
+
cwd: string,
|
|
220
|
+
timeoutMs: number,
|
|
221
|
+
runCommand: ResolveRangeBaseRefCommandRunner,
|
|
222
|
+
): Promise<string> {
|
|
223
|
+
const remoteHead = await runCommand(
|
|
224
|
+
cwd,
|
|
225
|
+
['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'],
|
|
226
|
+
timeoutMs,
|
|
227
|
+
);
|
|
228
|
+
if (remoteHead.exitCode === 0 && !remoteHead.aborted) {
|
|
229
|
+
const remoteHeadRef = firstNonEmptyLine(remoteHead.stdout);
|
|
230
|
+
if (remoteHeadRef !== null) {
|
|
231
|
+
return remoteHeadRef;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const candidate of ['origin/main', 'main', 'origin/master', 'master']) {
|
|
236
|
+
const exists = await runCommand(
|
|
237
|
+
cwd,
|
|
238
|
+
['rev-parse', '--verify', '--quiet', candidate],
|
|
239
|
+
timeoutMs,
|
|
240
|
+
);
|
|
241
|
+
if (exists.exitCode === 0 && !exists.aborted && firstNonEmptyLine(exists.stdout) !== null) {
|
|
242
|
+
return candidate;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return 'HEAD';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export async function resolveRangeBaseRef(options: ResolveRangeBaseRefOptions): Promise<string> {
|
|
250
|
+
const runCommand = options.runCommand ?? runGitCommandCapture;
|
|
251
|
+
const targetRef = await resolveRangeBaseTargetRef(options.cwd, options.timeoutMs, runCommand);
|
|
252
|
+
const mergeBase = await runCommand(
|
|
253
|
+
options.cwd,
|
|
254
|
+
['merge-base', targetRef, options.headRef],
|
|
255
|
+
options.timeoutMs,
|
|
256
|
+
);
|
|
257
|
+
if (mergeBase.exitCode !== 0 || mergeBase.aborted) {
|
|
258
|
+
const reason = mergeBase.timedOut
|
|
259
|
+
? 'timed out'
|
|
260
|
+
: mergeBase.stderr.length > 0
|
|
261
|
+
? mergeBase.stderr
|
|
262
|
+
: 'unknown error';
|
|
263
|
+
throw new Error(`git merge-base ${targetRef} ${options.headRef} failed: ${reason}`);
|
|
264
|
+
}
|
|
265
|
+
const resolved = firstNonEmptyLine(mergeBase.stdout);
|
|
266
|
+
if (resolved === null) {
|
|
267
|
+
throw new Error(`git merge-base ${targetRef} ${options.headRef} returned no output`);
|
|
268
|
+
}
|
|
269
|
+
return resolved;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export async function readGitDiffPreflight(
|
|
273
|
+
options: GitDiffInvocationOptions,
|
|
274
|
+
timeoutMs: number,
|
|
275
|
+
): Promise<GitDiffPreflight> {
|
|
276
|
+
let filesChanged = 0;
|
|
277
|
+
let additions = 0;
|
|
278
|
+
let deletions = 0;
|
|
279
|
+
let binaryFiles = 0;
|
|
280
|
+
|
|
281
|
+
const nameStatus = await streamGitLines({
|
|
282
|
+
cwd: options.cwd,
|
|
283
|
+
args: buildGitDiffArgs(options, 'name-status'),
|
|
284
|
+
timeoutMs,
|
|
285
|
+
onLine: (_line) => {
|
|
286
|
+
filesChanged += 1;
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
if (nameStatus.exitCode !== 0 && !nameStatus.aborted) {
|
|
290
|
+
throw new Error(`git diff --name-status failed: ${nameStatus.stderr || 'unknown error'}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await streamGitLines({
|
|
294
|
+
cwd: options.cwd,
|
|
295
|
+
args: buildGitDiffArgs(options, 'numstat'),
|
|
296
|
+
timeoutMs,
|
|
297
|
+
onLine: (line) => {
|
|
298
|
+
const [addRaw = '', delRaw = ''] = line.split('\t');
|
|
299
|
+
if (addRaw === '-' || delRaw === '-') {
|
|
300
|
+
binaryFiles += 1;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const add = parseFiniteInteger(addRaw);
|
|
304
|
+
const del = parseFiniteInteger(delRaw);
|
|
305
|
+
additions += add;
|
|
306
|
+
deletions += del;
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
filesChanged,
|
|
311
|
+
additions,
|
|
312
|
+
deletions,
|
|
313
|
+
binaryFiles,
|
|
314
|
+
};
|
|
315
|
+
}
|