fix(coding-agent): persist bash shortcut cwd changes

This commit is contained in:
Jeff Scott Ward
2026-07-17 16:58:32 -04:00
parent 48241afcc4
commit 3e24087111
5 changed files with 446 additions and 3 deletions
@@ -13,6 +13,7 @@ import {
import { Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@oh-my-pi/pi-tui";
import { formatDuration, Snowflake, sanitizeText } from "@oh-my-pi/pi-utils";
import { shouldEnableAppendOnlyContext } from "../../config/append-only-context-mode";
import { type BashResult, isPersistentShellCdCommand } from "../../exec/bash-executor";
import { type LoadedCustomShare, loadCustomShare } from "../../export/custom-share";
import { shareSession } from "../../export/share";
import type { CompactOptions } from "../../extensibility/extensions/types";
@@ -1100,6 +1101,12 @@ export class CommandController {
async handleBashCommand(command: string, excludeFromContext = false): Promise<void> {
const isDeferred = this.ctx.session.isStreaming;
const shouldPersistCwd = isPersistentShellCdCommand(command);
if (isDeferred && shouldPersistCwd) {
this.ctx.showWarning("Wait for the current response to finish or abort it before changing directories.");
return;
}
this.ctx.bashComponent = new BashExecutionComponent(command, this.ctx.ui, excludeFromContext);
if (isDeferred) {
@@ -1120,7 +1127,6 @@ export class CommandController {
},
{ excludeFromContext, useUserShell: true },
);
if (this.ctx.bashComponent) {
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
this.ctx.bashComponent.setComplete(result.exitCode, result.cancelled, {
@@ -1128,6 +1134,15 @@ export class CommandController {
truncation: meta?.truncation,
});
}
try {
if (shouldPersistCwd) await this.#applyBashResultCwd(result);
} catch (error) {
this.ctx.showError(
`Bash command completed, but OMP failed to update its working directory: ${
error instanceof Error ? error.message : "Unknown error"
}`,
);
}
} catch (error) {
if (this.ctx.bashComponent) {
this.ctx.bashComponent.setComplete(undefined, false);
@@ -1139,6 +1154,31 @@ export class CommandController {
this.ctx.ui.requestRender();
}
async #moveInteractiveCwd(resolvedPath: string): Promise<void> {
await this.ctx.sessionManager.moveTo(resolvedPath);
await this.ctx.applyCwdChange(resolvedPath);
this.ctx.updateEditorBorderColor();
await this.ctx.reloadTodos();
}
async #applyBashResultCwd(result: BashResult): Promise<void> {
if (result.cancelled || result.exitCode !== 0 || !result.workingDir) return;
if (!path.isAbsolute(result.workingDir)) return;
const resolvedPath = path.resolve(result.workingDir);
if (resolvedPath === path.resolve(this.ctx.sessionManager.getCwd())) return;
let isDirectory = false;
try {
isDirectory = (await fs.stat(resolvedPath)).isDirectory();
} catch {
isDirectory = false;
}
if (!isDirectory) return;
await this.#moveInteractiveCwd(resolvedPath);
}
async handlePythonCommand(code: string, excludeFromContext = false): Promise<void> {
const isDeferred = this.ctx.session.isStreaming;
this.ctx.pythonComponent = new EvalExecutionComponent(code, this.ctx.ui, excludeFromContext);