fix(tmux): inject pane action dependencies

This commit is contained in:
YeonGyu-Kim
2026-05-15 17:54:43 +09:00
parent 7a94cc72be
commit 8dcbccf063
6 changed files with 106 additions and 74 deletions
+11 -14
View File
@@ -4,10 +4,6 @@ import type { TmuxConfig } from "../../../config/schema"
import type { TmuxCommandResult } from "../runner"
const paneReplaceSpecifier = import.meta.resolve("./pane-replace")
const environmentSpecifier = import.meta.resolve("./environment")
const loggerSpecifier = import.meta.resolve("../../logger")
const runnerSpecifier = import.meta.resolve("../runner")
const tmuxPathResolverSpecifier = import.meta.resolve("../../../tools/interactive-bash/tmux-path-resolver")
const enabledTmuxConfig = {
enabled: true,
@@ -67,17 +63,18 @@ async function loadReplaceTmuxPane(): Promise<typeof import("./pane-replace").re
return module.replaceTmuxPane
}
function registerModuleMocks(): void {
mock.module(environmentSpecifier, () => ({ isInsideTmux: isInsideTmuxMock }))
mock.module(loggerSpecifier, () => ({ log: logMock }))
mock.module(runnerSpecifier, () => ({ runTmuxCommand: runTmuxCommandMock }))
mock.module(tmuxPathResolverSpecifier, () => ({ getTmuxPath: getTmuxPathMock }))
function createDeps(): NonNullable<Parameters<typeof import("./pane-replace").replaceTmuxPane>[6]> {
return {
log: logMock,
runTmuxCommand: runTmuxCommandMock,
isInsideTmux: isInsideTmuxMock,
getTmuxPath: getTmuxPathMock,
}
}
describe("replaceTmuxPane runner integration", () => {
beforeEach(() => {
mock.restore()
registerModuleMocks()
runTmuxCommandMock.mockClear()
isInsideTmuxMock.mockClear()
getTmuxPathMock.mockClear()
@@ -105,7 +102,7 @@ describe("replaceTmuxPane runner integration", () => {
const directory = "/tmp/omo-project/(replace)"
// when
const result = await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", directory)
const result = await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", directory, createDeps())
// then
const sendKeysCall = getRunTmuxCommandCall(0)
@@ -123,7 +120,7 @@ describe("replaceTmuxPane runner integration", () => {
const replaceTmuxPane = await loadReplaceTmuxPane()
// when
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path with spaces/here")
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path with spaces/here", createDeps())
// then
expect(getRespawnCommand()).toContain("--dir '/path with spaces/here'")
@@ -134,7 +131,7 @@ describe("replaceTmuxPane runner integration", () => {
const replaceTmuxPane = await loadReplaceTmuxPane()
// when
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "")
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "", createDeps())
// then
expect(getRespawnCommand()).toContain(`--dir '${process.cwd()}'`)
@@ -145,7 +142,7 @@ describe("replaceTmuxPane runner integration", () => {
const replaceTmuxPane = await loadReplaceTmuxPane()
// when
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path/with'quote")
await replaceTmuxPane("%42", "session-1", "worker", enabledTmuxConfig, "http://127.0.0.1:1234", "/path/with'quote", createDeps())
// then
expect(getRespawnCommand()).toContain("--dir '/path/with'\\''quote'")
+28 -6
View File
@@ -1,9 +1,32 @@
import type { TmuxConfig } from "../../../config/schema"
import { getTmuxPath } from "../../../tools/interactive-bash/tmux-path-resolver"
import type { SpawnPaneResult } from "../types"
import type { runTmuxCommand as RunTmuxCommand } from "../runner"
import { isInsideTmux } from "./environment"
import { shellSingleQuote } from "../../shell-env"
type ReplaceTmuxPaneDeps = {
log: (message: string, data?: unknown) => void
runTmuxCommand: typeof RunTmuxCommand
isInsideTmux: typeof isInsideTmux
getTmuxPath: typeof getTmuxPath
}
async function resolveReplaceTmuxPaneDeps(deps?: Partial<ReplaceTmuxPaneDeps>): Promise<ReplaceTmuxPaneDeps> {
const [{ log }, { runTmuxCommand }] = await Promise.all([
import("../../logger"),
import("../runner"),
])
return {
log,
runTmuxCommand,
isInsideTmux,
getTmuxPath,
...deps,
}
}
export async function replaceTmuxPane(
paneId: string,
sessionId: string,
@@ -11,22 +34,21 @@ export async function replaceTmuxPane(
config: TmuxConfig,
serverUrl: string,
directory: string,
depsInput?: Partial<ReplaceTmuxPaneDeps>,
): Promise<SpawnPaneResult> {
const [{ log }, { runTmuxCommand }] = await Promise.all([
import("../../logger"),
import("../runner"),
])
const deps = await resolveReplaceTmuxPaneDeps(depsInput)
const { log, runTmuxCommand } = deps
log("[replaceTmuxPane] called", { paneId, sessionId, description })
if (!config.enabled) {
return { success: false }
}
if (!isInsideTmux()) {
if (!deps.isInsideTmux()) {
return { success: false }
}
const tmux = await getTmuxPath()
const tmux = await deps.getTmuxPath()
if (!tmux) {
return { success: false }
}