Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/coding-agent/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ function readCommandOutput(
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"],
shell: shouldUseWindowsShell(command),
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
if (result.status === 0) return result.stdout.trim() || undefined;
if (options.requireSuccess) {
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/core/autonomous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,9 @@ function runChildProcess(
detached: process.platform !== "win32",
shell: options.shell === true,
stdio: ["ignore", "pipe", "pipe"],
// These run inside console-less daemon workers; avoid a console window
// flash on Windows (see runGit in utils/git.ts).
windowsHide: true,
});
if (child.pid) {
trackDetachedChildPid(child.pid);
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/core/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export async function execCommand(
cwd,
shell: false,
stdio: ["ignore", "pipe", "pipe"],
// Runs inside console-less daemon workers; avoid a console window
// flash on Windows (see runGit in utils/git.ts).
windowsHide: true,
// Merge per-call env over the parent env so callers can scope vars
// (e.g. herdr pane identity) without mutating the shared process.env.
env: mergeExecEnv(options?.env),
Expand Down
4 changes: 4 additions & 0 deletions packages/coding-agent/src/core/footer-data-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ function resolveBranchWithGitSync(repoDir: string): string | null {
cwd: repoDir,
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
// See runGit in utils/git.ts: avoid a console window flash on Windows.
windowsHide: true,
});
const branch = result.status === 0 ? result.stdout.trim() : "";
return branch || null;
Expand All @@ -24,6 +26,8 @@ function resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {
{
cwd: repoDir,
encoding: "utf8",
// See runGit in utils/git.ts: avoid a console window flash on Windows.
windowsHide: true,
},
(error: ExecFileException | null, stdout: string) => {
if (error) {
Expand Down
11 changes: 9 additions & 2 deletions packages/coding-agent/src/core/kernel/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ function run(command: string, args: string[], options: { stdio?: "ignore" | "inh
const child = spawn(command, args, {
env: process.env,
stdio: options.stdio ?? "ignore",
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
child.on("error", reject);
child.on("exit", (code, signal) => {
Expand Down Expand Up @@ -497,6 +498,12 @@ async function acquireBootstrapLock(venv: string): Promise<() => Promise<void>>
}
}

// uv venv layouts differ by platform: POSIX puts the interpreter at bin/python,
// Windows at Scripts/python.exe. Hardcoding "bin/python" breaks bootstrap on Windows.
function venvPythonPath(venv: string): string {
return process.platform === "win32" ? path.join(venv, "Scripts", "python.exe") : path.join(venv, "bin", "python");
}

async function findExecutable(name: string): Promise<string | null> {
const pathValue = process.env.PATH;
if (!pathValue) return null;
Expand Down Expand Up @@ -725,7 +732,7 @@ async function bootstrapVenv(
): Promise<void> {
await mkdir(path.dirname(venv), { recursive: true });
const uv = await ensureUv(options);
const python = path.join(venv, "bin", "python");
const python = venvPythonPath(venv);
const sourceDir = await resolveRuntimeSourceDir();
const runtimeRequirement = sourceDir ?? RUNTIME_REQUIREMENT;
const runtimeIdentity = await resolveRuntimeIdentity();
Expand Down Expand Up @@ -886,7 +893,7 @@ async function ensureKernelPythonUncached(
}

const venv = await resolveWritableKernelVenvDir();
const python = path.join(venv, "bin", "python");
const python = venvPythonPath(venv);
const runtimeIdentity = await resolveRuntimeIdentity();
if (await kernelReady(python, venv, runtimeIdentity, pythonSkills)) return python;

Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/core/kernel/fork-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class ForkServer {
const proc = spawn(this.params.python, ["-c", FORK_SERVER_SCRIPT, socketPath], {
env: this.launchEnv,
stdio: ["ignore", "ignore", "pipe"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
this.proc = proc;
proc.stderr?.on("data", (buf: Buffer) => {
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/core/kernel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ export class KernelManager {
cwd: this.options.cwd,
env: this.options.env ? { ...process.env, ...this.options.env } : process.env,
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
this.kernel = kernel;

Expand Down
2 changes: 2 additions & 0 deletions packages/coding-agent/src/core/package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2396,6 +2396,7 @@ export class DefaultPackageManager implements PackageManager {
cwd: options?.cwd,
stdio: ["ignore", "pipe", "pipe"],
shell: shouldUseWindowsShell(command),
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
env: options?.env ? { ...baseEnv, ...options.env } : baseEnv,
});
}
Expand Down Expand Up @@ -2463,6 +2464,7 @@ export class DefaultPackageManager implements PackageManager {
stdio: ["ignore", "pipe", "pipe"],
encoding: "utf-8",
shell: shouldUseWindowsShell(command),
windowsHide: true,
env: getEnv(),
});
if (result.error || result.status !== 0) {
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/src/core/session-file-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function deleteSessionArtifacts(sessionPath: string): Promise<void> {
/** Remove the session `.jsonl`, trying the `trash` CLI first, then falling back to unlink. */
async function removeSessionFile(sessionPath: string): Promise<DeleteSessionFileResult> {
const trashArgs = sessionPath.startsWith("-") ? ["--", sessionPath] : [sessionPath];
const trashResult = spawnSync("trash", trashArgs, { encoding: "utf-8" });
const trashResult = spawnSync("trash", trashArgs, { encoding: "utf-8", windowsHide: true });

const getTrashErrorHint = (): string | null => {
const parts: string[] = [];
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/core/session-lease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ function runProcessQuery(command: string, args: string[]): string {
return execFileSync(command, args, {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
// Called from console-less daemon workers; avoid a console window flash
// on Windows (see runGit in utils/git.ts).
windowsHide: true,
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/core/tools/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function createLocalBashOperations(options?: { shellPath?: string }): Bas
detached: process.platform !== "win32",
env: env ?? getShellEnv(),
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
if (child.pid) trackDetachedChildPid(child.pid);
let timedOut = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,16 @@ export class CommandRecoveryJournal {
closeSync(descriptor);
}
renameSync(tempPath, this.path);
const directoryDescriptor = openSync(dirname(this.path), "r");
try {
fsyncSync(directoryDescriptor);
} finally {
closeSync(directoryDescriptor);
const directoryDescriptor = openSync(dirname(this.path), "r");
try {
fsyncSync(directoryDescriptor);
} finally {
closeSync(directoryDescriptor);
}
} catch {
// Directory fsync is unavailable on some platforms (e.g. Windows raises
// EPERM); the atomic rename still protects readers.
}
this.recordCount = records.length;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ export class DaemonCatalogClient {
cwd: process.cwd(),
env: createCliSubprocessEnv({ ...process.env, [DAEMON_CATALOG_ROLE_ENV]: "1" }),
stdio: ["ignore", "ignore", "ignore", "ipc"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
this.child = child;
child.on("message", (value: unknown) => this.handleMessage(value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8665,7 +8665,7 @@ export class InteractiveMode {
private async handleShareCommand(): Promise<void> {
// Check if gh is available and logged in
try {
const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8", windowsHide: true });
if (authResult.status !== 0) {
this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
return;
Expand Down Expand Up @@ -8714,7 +8714,7 @@ export class InteractiveMode {

try {
const result = await new Promise<{ stdout: string; stderr: string; code: number | null }>((resolve) => {
proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
proc = spawn("gh", ["gist", "create", "--public=false", tmpFile], { windowsHide: true });
let stdout = "";
let stderr = "";
proc.stdout?.on("data", (data) => {
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/modes/rpc/rpc-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class RpcClient {
cwd: this.options.cwd,
env: { ...process.env, ...this.options.env },
stdio: ["pipe", "pipe", "pipe"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});

// Collect stderr for debugging
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/utils/clipboard-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ function runCommand(
timeout: timeoutMs,
maxBuffer: maxBufferBytes,
env: options?.env,
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});

if (result.error) {
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/utils/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ function runGit(cwd: string, args: string[]): string | null {
cwd,
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
// Daemon workers run without a console; without this, each git spawn makes
// Windows allocate a fresh console window (visible flicker).
windowsHide: true,
});
if (result.status !== 0 || typeof result.stdout !== "string") return null;
return result.stdout.trim() || null;
Expand Down
5 changes: 3 additions & 2 deletions packages/coding-agent/src/utils/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function findBashOnPath(): string | null {
if (process.platform === "win32") {
// Windows: Use 'where' and verify file exists (where can return non-existent paths)
try {
const result = spawnSync("where", ["bash.exe"], { encoding: "utf-8", timeout: 5000 });
const result = spawnSync("where", ["bash.exe"], { encoding: "utf-8", timeout: 5000, windowsHide: true });
if (result.status === 0 && result.stdout) {
const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
if (firstMatch && existsSync(firstMatch)) {
Expand All @@ -31,7 +31,7 @@ function findBashOnPath(): string | null {

// Unix: Use 'which' and trust its output (handles Termux and special filesystems)
try {
const result = spawnSync("which", ["bash"], { encoding: "utf-8", timeout: 5000 });
const result = spawnSync("which", ["bash"], { encoding: "utf-8", timeout: 5000, windowsHide: true });
if (result.status === 0 && result.stdout) {
const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
if (firstMatch) {
Expand Down Expand Up @@ -194,6 +194,7 @@ export function killProcessTree(pid: number): void {
spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
stdio: "ignore",
detached: true,
windowsHide: true,
});
} catch {
// Ignore errors if taskkill fails
Expand Down
7 changes: 5 additions & 2 deletions packages/coding-agent/src/utils/tools-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const TOOLS: Record<string, ToolConfig> = {
// Check that a command both launches and reports a successful version.
function commandWorks(cmd: string): boolean {
try {
const result = spawnSync(cmd, ["--version"], { stdio: "pipe", timeout: COMMAND_TIMEOUT_MS });
const result = spawnSync(cmd, ["--version"], { stdio: "pipe", timeout: COMMAND_TIMEOUT_MS, windowsHide: true });
return !result.error && result.status === 0;
} catch {
return false;
Expand Down Expand Up @@ -224,7 +224,10 @@ async function downloadTool(tool: ManagedTool): Promise<string> {

try {
if (assetName.endsWith(".tar.gz")) {
const extractResult = spawnSync("tar", ["xzf", archivePath, "-C", extractDir], { stdio: "pipe" });
const extractResult = spawnSync("tar", ["xzf", archivePath, "-C", extractDir], {
stdio: "pipe",
windowsHide: true,
});
if (extractResult.error || extractResult.status !== 0) {
const errMsg = extractResult.error?.message ?? extractResult.stderr?.toString().trim() ?? "unknown error";
throw new Error(`Failed to extract ${assetName}: ${errMsg}`);
Expand Down
1 change: 1 addition & 0 deletions packages/tui/src/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ async function walkDirectoryWithFd(

const child = spawn(fdPath, args, {
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true, // avoid console window flash on Windows (console-less daemon workers)
});
let stdout = "";
let resolved = false;
Expand Down