feat(hooks/atlas): call completeBoulder when progress.isComplete
This commit is contained in:
@@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import {
|
import {
|
||||||
|
completeBoulder,
|
||||||
getPlanProgress,
|
getPlanProgress,
|
||||||
|
getWorkForSession,
|
||||||
getTaskSessionState,
|
getTaskSessionState,
|
||||||
readBoulderState,
|
readBoulderState,
|
||||||
readCurrentTopLevelTask,
|
readCurrentTopLevelTask,
|
||||||
@@ -220,6 +222,12 @@ export async function handleAtlasSessionIdle(input: {
|
|||||||
|
|
||||||
const { boulderState, progress, appendedSession } = activeBoulderSession
|
const { boulderState, progress, appendedSession } = activeBoulderSession
|
||||||
if (progress.isComplete) {
|
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 })
|
log(`[${HOOK_NAME}] Boulder complete`, { sessionID, plan: boulderState.plan_name })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user