Merge branch 'main' into dev
Resolve 38 conflicts preserving both sides' work: - Server: DB-based registries (dev) + WorkspaceGitService/DaemonConfigStore (main) - App: script proxy/voice MCP (dev) + pin/hide tabs/bootstrap boundary (main) - Shared: WorkspaceScriptPayload (dev) + git/GitHub runtime schemas (main) - Deleted workspace-registry-bootstrap.ts (dev's DB approach supersedes)
This commit is contained in:
@@ -617,7 +617,9 @@ const x = 1;
|
||||
|
||||
writeFileSync(join(repoDir, "conflict.txt"), "local\n");
|
||||
execSync("git add conflict.txt", { cwd: repoDir });
|
||||
execSync("git -c commit.gpgsign=false commit -m 'local rebase conflict commit'", { cwd: repoDir });
|
||||
execSync("git -c commit.gpgsign=false commit -m 'local rebase conflict commit'", {
|
||||
cwd: repoDir,
|
||||
});
|
||||
|
||||
const otherClone = join(tempDir, "other-clone");
|
||||
execSync(`git clone ${remoteDir} ${otherClone}`);
|
||||
|
||||
@@ -191,7 +191,6 @@ type CheckoutFileChange = {
|
||||
isUntracked?: boolean;
|
||||
};
|
||||
|
||||
|
||||
function normalizeBranchSuggestionName(raw: string): string | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -678,7 +677,7 @@ async function requireGitRepo(cwd: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function getCurrentBranch(cwd: string): Promise<string | null> {
|
||||
export async function getCurrentBranch(cwd: string): Promise<string | null> {
|
||||
const { stdout } = await execAsync("git rev-parse --abbrev-ref HEAD", {
|
||||
cwd,
|
||||
env: READ_ONLY_GIT_ENV,
|
||||
@@ -700,7 +699,7 @@ async function getWorktreeRoot(cwd: string): Promise<string | null> {
|
||||
}
|
||||
}
|
||||
|
||||
async function getMainRepoRoot(cwd: string): Promise<string> {
|
||||
export async function getMainRepoRoot(cwd: string): Promise<string> {
|
||||
const { stdout: commonDirOut } = await execAsync(
|
||||
"git rev-parse --path-format=absolute --git-common-dir",
|
||||
{ cwd, env: READ_ONLY_GIT_ENV },
|
||||
@@ -717,9 +716,7 @@ async function getMainRepoRoot(cwd: string): Promise<string> {
|
||||
env: READ_ONLY_GIT_ENV,
|
||||
});
|
||||
const worktrees = parseWorktreeList(worktreeOut);
|
||||
const nonBareNonPaseo = worktrees.filter(
|
||||
(wt) => !wt.isBare && !isPaseoWorktreePath(wt.path),
|
||||
);
|
||||
const nonBareNonPaseo = worktrees.filter((wt) => !wt.isBare && !isPaseoWorktreePath(wt.path));
|
||||
const childrenOfBareRepo = nonBareNonPaseo.filter((wt) => isDescendantPath(wt.path, normalized));
|
||||
const mainChild = childrenOfBareRepo.find((wt) => basename(wt.path) === "main");
|
||||
return mainChild?.path ?? childrenOfBareRepo[0]?.path ?? nonBareNonPaseo[0]?.path ?? normalized;
|
||||
@@ -844,7 +841,7 @@ async function isWorkingTreeDirty(cwd: string): Promise<boolean> {
|
||||
return stdout.trim().length > 0;
|
||||
}
|
||||
|
||||
async function getOriginRemoteUrl(cwd: string): Promise<string | null> {
|
||||
export async function getOriginRemoteUrl(cwd: string): Promise<string | null> {
|
||||
try {
|
||||
const { stdout } = await execAsync("git config --get remote.origin.url", {
|
||||
cwd,
|
||||
@@ -857,12 +854,12 @@ async function getOriginRemoteUrl(cwd: string): Promise<string | null> {
|
||||
}
|
||||
}
|
||||
|
||||
async function hasOriginRemote(cwd: string): Promise<boolean> {
|
||||
export async function hasOriginRemote(cwd: string): Promise<boolean> {
|
||||
const url = await getOriginRemoteUrl(cwd);
|
||||
return url !== null;
|
||||
}
|
||||
|
||||
async function resolveAbsoluteGitDir(cwd: string): Promise<string | null> {
|
||||
export async function resolveAbsoluteGitDir(cwd: string): Promise<string | null> {
|
||||
try {
|
||||
const { stdout } = await execAsync("git rev-parse --absolute-git-dir", {
|
||||
cwd,
|
||||
@@ -1042,7 +1039,16 @@ async function getAheadOfOrigin(cwd: string, currentBranch: string): Promise<num
|
||||
const count = Number.parseInt(stdout.trim(), 10);
|
||||
return Number.isNaN(count) ? null : count;
|
||||
} catch {
|
||||
return null;
|
||||
try {
|
||||
const { stdout } = await execAsync(`git rev-list --count ${currentBranch}`, {
|
||||
cwd,
|
||||
env: READ_ONLY_GIT_ENV,
|
||||
});
|
||||
const count = Number.parseInt(stdout.trim(), 10);
|
||||
return Number.isNaN(count) ? null : count;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1630,11 +1636,7 @@ export async function getCheckoutDiff(
|
||||
if (diffBytes >= TOTAL_DIFF_MAX_BYTES) {
|
||||
break;
|
||||
}
|
||||
const { text, truncated, stat } = await getUntrackedDiffText(
|
||||
cwd,
|
||||
change,
|
||||
ignoreWhitespace,
|
||||
);
|
||||
const { text, truncated, stat } = await getUntrackedDiffText(cwd, change, ignoreWhitespace);
|
||||
|
||||
if (!compare.includeStructured) {
|
||||
if (stat?.isBinary) {
|
||||
@@ -2015,7 +2017,7 @@ type CheckRunNode = z.infer<typeof CheckRunNodeSchema>;
|
||||
type StatusContextNode = z.infer<typeof StatusContextNodeSchema>;
|
||||
|
||||
|
||||
async function resolveGhPath(): Promise<string> {
|
||||
export async function resolveGhPath(): Promise<string> {
|
||||
if (cachedGhPath === undefined) {
|
||||
cachedGhPath = await findExecutable("gh");
|
||||
}
|
||||
@@ -2238,7 +2240,6 @@ async function resolveGitHubRepo(cwd: string): Promise<string | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export async function createPullRequest(
|
||||
cwd: string,
|
||||
options: CreatePullRequestOptions,
|
||||
@@ -2340,9 +2341,7 @@ async function getPullRequestStatusUncached(cwd: string): Promise<PullRequestSta
|
||||
return { status: null, githubFeaturesEnabled: true };
|
||||
}
|
||||
const mergedAt =
|
||||
typeof pr.mergedAt === "string" && pr.mergedAt.trim().length > 0
|
||||
? pr.mergedAt
|
||||
: null;
|
||||
typeof pr.mergedAt === "string" && pr.mergedAt.trim().length > 0 ? pr.mergedAt : null;
|
||||
const state =
|
||||
mergedAt !== null
|
||||
? "merged"
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import {
|
||||
findExecutableSync,
|
||||
quoteWindowsArgument,
|
||||
quoteWindowsCommand,
|
||||
} from "./executable.js";
|
||||
import { findExecutableSync, quoteWindowsArgument, quoteWindowsCommand } from "./executable.js";
|
||||
|
||||
type FindExecutableDependencies = NonNullable<Parameters<typeof findExecutableSync>[1]>;
|
||||
|
||||
@@ -46,20 +42,18 @@ describe("findExecutableSync", () => {
|
||||
"C:\\nvm4w\\nodejs\\codex\r\nC:\\nvm4w\\nodejs\\codex.cmd\r\n",
|
||||
);
|
||||
|
||||
expect(findExecutableSync("codex", findExecutableDependencies)).toBe("C:\\nvm4w\\nodejs\\codex");
|
||||
expect(findExecutableSync("codex", findExecutableDependencies)).toBe(
|
||||
"C:\\nvm4w\\nodejs\\codex",
|
||||
);
|
||||
});
|
||||
|
||||
test("on Unix, uses the last line from which output", () => {
|
||||
findExecutableDependencies.execFileSync.mockReturnValue(
|
||||
"/usr/local/bin/codex\n",
|
||||
);
|
||||
findExecutableDependencies.execFileSync.mockReturnValue("/usr/local/bin/codex\n");
|
||||
|
||||
expect(findExecutableSync("codex", findExecutableDependencies)).toBe("/usr/local/bin/codex");
|
||||
expect(findExecutableDependencies.execFileSync).toHaveBeenCalledWith(
|
||||
"which",
|
||||
["codex"],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
expect(findExecutableDependencies.execFileSync).toHaveBeenCalledWith("which", ["codex"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
});
|
||||
|
||||
test("warns and returns null when the final which line is not an absolute path", () => {
|
||||
|
||||
@@ -66,10 +66,12 @@ export function findExecutableSync(
|
||||
|
||||
if (deps.platform() === "win32") {
|
||||
try {
|
||||
const out = deps.execFileSync("where.exe", [trimmed], {
|
||||
encoding: "utf8",
|
||||
windowsHide: true,
|
||||
}).trim();
|
||||
const out = deps
|
||||
.execFileSync("where.exe", [trimmed], {
|
||||
encoding: "utf8",
|
||||
windowsHide: true,
|
||||
})
|
||||
.trim();
|
||||
return (
|
||||
out
|
||||
.split(/\r?\n/)
|
||||
@@ -160,4 +162,3 @@ export function quoteWindowsArgument(argument: string): string {
|
||||
if (argument.startsWith('"') && argument.endsWith('"')) return argument;
|
||||
return `"${argument}"`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
spawn,
|
||||
type ChildProcess,
|
||||
type SpawnOptions,
|
||||
} from "node:child_process";
|
||||
import { spawn, type ChildProcess, type SpawnOptions } from "node:child_process";
|
||||
|
||||
/**
|
||||
* Platform-aware spawn that centralizes Windows shell and quoting concerns.
|
||||
|
||||
@@ -13,9 +13,7 @@ import {
|
||||
runWorktreeSetupCommands,
|
||||
slugify,
|
||||
} from "./worktree";
|
||||
import {
|
||||
getPaseoWorktreeMetadataPath,
|
||||
} from "./worktree-metadata.js";
|
||||
import { getPaseoWorktreeMetadataPath } from "./worktree-metadata.js";
|
||||
import { execSync } from "child_process";
|
||||
import { mkdtempSync, rmSync, existsSync, realpathSync, writeFileSync, readFileSync } from "fs";
|
||||
import { dirname, join } from "path";
|
||||
@@ -161,6 +159,7 @@ describe("createWorktree", () => {
|
||||
execSync(`git clone ${remoteDir} ${remoteCloneDir}`);
|
||||
execSync("git config user.email 'test@test.com'", { cwd: remoteCloneDir });
|
||||
execSync("git config user.name 'Test'", { cwd: remoteCloneDir });
|
||||
execSync("git checkout -B main origin/main", { cwd: remoteCloneDir });
|
||||
writeFileSync(join(remoteCloneDir, "file.txt"), "from-origin\n");
|
||||
execSync("git add file.txt", { cwd: remoteCloneDir });
|
||||
execSync("git -c commit.gpgsign=false commit -m 'advance origin main'", {
|
||||
|
||||
Reference in New Issue
Block a user