fix(ralph-loop): generate transcript path from sessionID instead of relying on event properties (#355)

OpenCode doesn't pass transcriptPath in the session.idle event properties,
which caused detectCompletionPromise to always return false (the first check
returns early if transcriptPath is undefined).

This fix:
- Imports getTranscriptPath from claude-code-hooks/transcript
- Generates the transcript path from sessionID instead of reading from event
- Adds optional getTranscriptPath callback to RalphLoopOptions for testability

Fixes #354

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
Sisyphus
2025-12-30 23:08:30 +09:00
committed by GitHub
parent e3040ecb28
commit a295202a81
3 changed files with 11 additions and 5 deletions

View File

@@ -329,17 +329,19 @@ describe("ralph-loop", () => {
test("should detect completion promise and stop loop", async () => {
// #given - active loop with transcript containing completion
const hook = createRalphLoopHook(createMockPluginInput())
const transcriptPath = join(TEST_DIR, "transcript.jsonl")
const hook = createRalphLoopHook(createMockPluginInput(), {
getTranscriptPath: () => transcriptPath,
})
hook.startLoop("session-123", "Build something", { completionPromise: "COMPLETE" })
const transcriptPath = join(TEST_DIR, "transcript.jsonl")
writeFileSync(transcriptPath, JSON.stringify({ content: "Task done <promise>COMPLETE</promise>" }))
// #when - session goes idle with transcript
// #when - session goes idle (transcriptPath now derived from sessionID via getTranscriptPath)
await hook.event({
event: {
type: "session.idle",
properties: { sessionID: "session-123", transcriptPath },
properties: { sessionID: "session-123" },
},
})

View File

@@ -8,6 +8,7 @@ import {
DEFAULT_COMPLETION_PROMISE,
} from "./constants"
import type { RalphLoopState, RalphLoopOptions } from "./types"
import { getTranscriptPath as getDefaultTranscriptPath } from "../claude-code-hooks/transcript"
export * from "./types"
export * from "./constants"
@@ -48,6 +49,7 @@ export function createRalphLoopHook(
const sessions = new Map<string, SessionState>()
const config = options?.config
const stateDir = config?.state_dir
const getTranscriptPath = options?.getTranscriptPath ?? getDefaultTranscriptPath
function getSessionState(sessionID: string): SessionState {
let state = sessions.get(sessionID)
@@ -149,7 +151,8 @@ export function createRalphLoopHook(
return
}
const transcriptPath = props?.transcriptPath as string | undefined
// Generate transcript path from sessionID - OpenCode doesn't pass it in event properties
const transcriptPath = getTranscriptPath(sessionID)
if (detectCompletionPromise(transcriptPath, state.completion_promise)) {
log(`[${HOOK_NAME}] Completion detected!`, {

View File

@@ -12,4 +12,5 @@ export interface RalphLoopState {
export interface RalphLoopOptions {
config?: RalphLoopConfig
getTranscriptPath?: (sessionId: string) => string
}