oh-my-opencode/src/shared/command-executor/execute-hook-command.ts
YeonGyu-Kim 119e18c810 refactor: wave 2 - split atlas, auto-update-checker, session-recovery, todo-enforcer, background-task hooks
- Extract atlas/ into 15 focused modules (hook, event handler, tool policies, types, etc.)
- Split auto-update-checker into checker/ and hook/ subdirectories with single-purpose files
- Decompose session-recovery into separate recovery strategy files per error type
- Extract todo-continuation-enforcer from monolith to directory with dedicated modules
- Split background-task/tools.ts into individual tool creator files
- Extract command-executor, tmux-utils into focused sub-modules
- Split config/schema.ts into domain-specific schema files
- Decompose cli/config-manager.ts into focused modules
- Rollback skill-mcp-manager, model-availability, index.ts splits that broke tests
- Fix all import path depths for moved files (../../ -> ../../../)
- Add explicit type annotations to resolve TS7006 implicit any errors

Typecheck: 0 errors
Tests: 2359 pass, 5 fail (all pre-existing)
2026-02-08 15:01:42 +09:00

79 lines
1.7 KiB
TypeScript

import { spawn } from "node:child_process"
import { getHomeDirectory } from "./home-directory"
import { findBashPath, findZshPath } from "./shell-path"
export interface CommandResult {
exitCode: number
stdout?: string
stderr?: string
}
export interface ExecuteHookOptions {
forceZsh?: boolean
zshPath?: string
}
export async function executeHookCommand(
command: string,
stdin: string,
cwd: string,
options?: ExecuteHookOptions,
): Promise<CommandResult> {
const home = getHomeDirectory()
const expandedCommand = command
.replace(/^~(?=\/|$)/g, home)
.replace(/\s~(?=\/)/g, ` ${home}`)
.replace(/\$CLAUDE_PROJECT_DIR/g, cwd)
.replace(/\$\{CLAUDE_PROJECT_DIR\}/g, cwd)
let finalCommand = expandedCommand
if (options?.forceZsh) {
const zshPath = findZshPath(options.zshPath)
const escapedCommand = expandedCommand.replace(/'/g, "'\\''")
if (zshPath) {
finalCommand = `${zshPath} -lc '${escapedCommand}'`
} else {
const bashPath = findBashPath()
if (bashPath) {
finalCommand = `${bashPath} -lc '${escapedCommand}'`
}
}
}
return new Promise((resolve) => {
const proc = spawn(finalCommand, {
cwd,
shell: true,
env: { ...process.env, HOME: home, CLAUDE_PROJECT_DIR: cwd },
})
let stdout = ""
let stderr = ""
proc.stdout?.on("data", (data) => {
stdout += data.toString()
})
proc.stderr?.on("data", (data) => {
stderr += data.toString()
})
proc.stdin?.write(stdin)
proc.stdin?.end()
proc.on("close", (code) => {
resolve({
exitCode: code ?? 0,
stdout: stdout.trim(),
stderr: stderr.trim(),
})
})
proc.on("error", (err) => {
resolve({ exitCode: 1, stderr: err.message })
})
})
}