feat(hooks/atlas): call completeBoulder when progress.isComplete

This commit is contained in:
YeonGyu-Kim
2026-05-11 13:40:48 +09:00
parent 127112e1e2
commit 29b44fffd0
2 changed files with 86 additions and 0 deletions
@@ -0,0 +1,78 @@
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
import { randomUUID } from "node:crypto"
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { clearBoulderState, readBoulderState, writeBoulderState } from "../../features/boulder-state"
const { createAtlasHook } = await import("./index")
describe("atlas hook idle-event complete boulder", () => {
let testDirectory = ""
beforeEach(() => {
testDirectory = join(tmpdir(), `atlas-idle-complete-${randomUUID()}`)
if (!existsSync(testDirectory)) {
mkdirSync(testDirectory, { recursive: true })
}
clearBoulderState(testDirectory)
})
afterEach(() => {
clearBoulderState(testDirectory)
if (existsSync(testDirectory)) {
rmSync(testDirectory, { recursive: true, force: true })
}
})
it("marks work completed with ended_at and elapsed_ms when progress is complete", async () => {
// given
const sessionID = "ses_complete"
const planPath = join(testDirectory, "complete-plan.md")
writeFileSync(planPath, "# Plan\n\n## TODOs\n- [x] 1. Done\n", "utf-8")
writeBoulderState(testDirectory, {
schema_version: 2,
active_work_id: "work-complete",
active_plan: planPath,
started_at: "2026-01-02T10:00:00.000Z",
session_ids: [sessionID],
plan_name: "complete-plan",
works: {
"work-complete": {
work_id: "work-complete",
active_plan: planPath,
plan_name: "complete-plan",
started_at: "2026-01-02T10:00:00.000Z",
session_ids: [sessionID],
status: "active",
},
},
})
const hook = createAtlasHook({
directory: testDirectory,
client: {
session: {
get: async () => ({ data: { id: sessionID } }),
messages: async () => ({ data: [] }),
prompt: async () => ({ data: {} }),
promptAsync: async () => ({ data: {} }),
},
},
} as unknown as Parameters<typeof createAtlasHook>[0])
// when
await hook.handler({
event: {
type: "session.idle",
properties: { sessionID },
},
})
// then
const work = readBoulderState(testDirectory)?.works?.["work-complete"]
expect(work?.status).toBe("completed")
expect(work?.ended_at).toBeString()
expect((work?.elapsed_ms ?? 0) > 0).toBe(true)
})
})
+8
View File
@@ -1,6 +1,8 @@
import type { PluginInput } from "@opencode-ai/plugin"
import {
completeBoulder,
getPlanProgress,
getWorkForSession,
getTaskSessionState,
readBoulderState,
readCurrentTopLevelTask,
@@ -220,6 +222,12 @@ export async function handleAtlasSessionIdle(input: {
const { boulderState, progress, appendedSession } = activeBoulderSession
if (progress.isComplete) {
const work = getWorkForSession(ctx.directory, sessionID)
if (work) {
completeBoulder(ctx.directory, work.work_id)
} else {
completeBoulder(ctx.directory, boulderState.active_work_id)
}
log(`[${HOOK_NAME}] Boulder complete`, { sessionID, plan: boulderState.plan_name })
return
}