From c4b862cbc4b8f38ce916c5f0d38f08ffadf1eb90 Mon Sep 17 00:00:00 2001 From: justsisyphus Date: Tue, 20 Jan 2026 15:52:13 +0900 Subject: [PATCH] refactor(hooks): rename sisyphus-orchestrator to atlas Hook name is now 'atlas' (not 'atlas-orchestrator'). Directory renamed to src/hooks/atlas/. All references updated. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../index.test.ts | 140 +++++++++--------- .../{sisyphus-orchestrator => atlas}/index.ts | 34 ++--- src/hooks/index.ts | 2 +- src/tools/delegate-task/constants.ts | 4 + src/tools/delegate-task/tools.ts | 2 +- 5 files changed, 93 insertions(+), 89 deletions(-) rename src/hooks/{sisyphus-orchestrator => atlas}/index.test.ts (87%) rename src/hooks/{sisyphus-orchestrator => atlas}/index.ts (97%) diff --git a/src/hooks/sisyphus-orchestrator/index.test.ts b/src/hooks/atlas/index.test.ts similarity index 87% rename from src/hooks/sisyphus-orchestrator/index.test.ts rename to src/hooks/atlas/index.test.ts index 3863200ae..8d51cde70 100644 --- a/src/hooks/sisyphus-orchestrator/index.test.ts +++ b/src/hooks/atlas/index.test.ts @@ -12,8 +12,8 @@ import type { BoulderState } from "../../features/boulder-state" import { MESSAGE_STORAGE } from "../../features/hook-message-injector" -describe("sisyphus-orchestrator hook", () => { - const TEST_DIR = join(tmpdir(), "sisyphus-orchestrator-test-" + Date.now()) +describe("atlas hook", () => { + const TEST_DIR = join(tmpdir(), "atlas-test-" + Date.now()) const SISYPHUS_DIR = join(TEST_DIR, ".sisyphus") function createMockPluginInput(overrides?: { promptMock?: ReturnType }) { @@ -85,10 +85,10 @@ describe("sisyphus-orchestrator hook", () => { expect(output.output).toBe("Original output") }) - test("should not transform when caller is not orchestrator-sisyphus", async () => { - // #given - boulder state exists but caller agent in message storage is not orchestrator - const sessionID = "session-non-orchestrator-test" - setupMessageStorage(sessionID, "other-agent") + test("should not transform when caller is not atlas", async () => { + // #given - boulder state exists but caller agent in message storage is not atlas + const sessionID = "session-non-orchestrator-test" + setupMessageStorage(sessionID, "other-agent") const planPath = join(TEST_DIR, "test-plan.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1") @@ -120,10 +120,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should append standalone verification when no boulder state but caller is orchestrator", async () => { - // #given - no boulder state, but caller is orchestrator - const sessionID = "session-no-boulder-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should append standalone verification when no boulder state but caller is atlas", async () => { + // #given - no boulder state, but caller is atlas + const sessionID = "session-no-boulder-test" + setupMessageStorage(sessionID, "atlas") const hook = createSisyphusOrchestratorHook(createMockPluginInput()) const output = { @@ -146,10 +146,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should transform output when caller is orchestrator-sisyphus with boulder state", async () => { - // #given - orchestrator-sisyphus caller with boulder state - const sessionID = "session-transform-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should transform output when caller is atlas with boulder state", async () => { + // #given - atlas caller with boulder state + const sessionID = "session-transform-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "test-plan.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [x] Task 2") @@ -185,10 +185,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should still transform when plan is complete (shows progress)", async () => { - // #given - boulder state with complete plan, orchestrator caller - const sessionID = "session-complete-plan-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should still transform when plan is complete (shows progress)", async () => { + // #given - boulder state with complete plan, atlas caller + const sessionID = "session-complete-plan-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "complete-plan.md") writeFileSync(planPath, "# Plan\n- [x] Task 1\n- [x] Task 2") @@ -222,10 +222,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should append session ID to boulder state if not present", async () => { - // #given - boulder state without session-append-test, orchestrator caller - const sessionID = "session-append-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should append session ID to boulder state if not present", async () => { + // #given - boulder state without session-append-test, atlas caller + const sessionID = "session-append-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "test-plan.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1") @@ -258,10 +258,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should not duplicate existing session ID", async () => { - // #given - boulder state already has session-dup-test, orchestrator caller - const sessionID = "session-dup-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should not duplicate existing session ID", async () => { + // #given - boulder state already has session-dup-test, atlas caller + const sessionID = "session-dup-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "test-plan.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1") @@ -295,10 +295,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should include boulder.json path and notepad path in transformed output", async () => { - // #given - boulder state, orchestrator caller - const sessionID = "session-path-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should include boulder.json path and notepad path in transformed output", async () => { + // #given - boulder state, atlas caller + const sessionID = "session-path-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "my-feature.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2\n- [x] Task 3") @@ -332,10 +332,10 @@ describe("sisyphus-orchestrator hook", () => { cleanupMessageStorage(sessionID) }) - test("should include resume and checkbox instructions in reminder", async () => { - // #given - boulder state, orchestrator caller - const sessionID = "session-resume-test" - setupMessageStorage(sessionID, "orchestrator-sisyphus") + test("should include resume and checkbox instructions in reminder", async () => { + // #given - boulder state, atlas caller + const sessionID = "session-resume-test" + setupMessageStorage(sessionID, "atlas") const planPath = join(TEST_DIR, "test-plan.md") writeFileSync(planPath, "# Plan\n- [ ] Task 1") @@ -372,9 +372,9 @@ describe("sisyphus-orchestrator hook", () => { describe("Write/Edit tool direct work reminder", () => { const ORCHESTRATOR_SESSION = "orchestrator-write-test" - beforeEach(() => { - setupMessageStorage(ORCHESTRATOR_SESSION, "orchestrator-sisyphus") - }) + beforeEach(() => { + setupMessageStorage(ORCHESTRATOR_SESSION, "atlas") + }) afterEach(() => { cleanupMessageStorage(ORCHESTRATOR_SESSION) @@ -596,13 +596,13 @@ describe("sisyphus-orchestrator hook", () => { describe("session.idle handler (boulder continuation)", () => { const MAIN_SESSION_ID = "main-session-123" - beforeEach(() => { - mock.module("../../features/claude-code-session-state", () => ({ - getMainSessionID: () => MAIN_SESSION_ID, - subagentSessions: new Set(), - })) - setupMessageStorage(MAIN_SESSION_ID, "orchestrator-sisyphus") - }) + beforeEach(() => { + mock.module("../../features/claude-code-session-state", () => ({ + getMainSessionID: () => MAIN_SESSION_ID, + subagentSessions: new Set(), + })) + setupMessageStorage(MAIN_SESSION_ID, "atlas") + }) afterEach(() => { cleanupMessageStorage(MAIN_SESSION_ID) @@ -830,37 +830,37 @@ describe("sisyphus-orchestrator hook", () => { expect(callArgs.body.parts[0].text).toContain("2 remaining") }) - test("should not inject when last agent is not orchestrator-sisyphus", async () => { - // #given - boulder state with incomplete plan, but last agent is NOT orchestrator-sisyphus - const planPath = join(TEST_DIR, "test-plan.md") - writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2") + test("should not inject when last agent is not atlas", async () => { + // #given - boulder state with incomplete plan, but last agent is NOT atlas + const planPath = join(TEST_DIR, "test-plan.md") + writeFileSync(planPath, "# Plan\n- [ ] Task 1\n- [ ] Task 2") - const state: BoulderState = { - active_plan: planPath, - started_at: "2026-01-02T10:00:00Z", - session_ids: [MAIN_SESSION_ID], - plan_name: "test-plan", - } - writeBoulderState(TEST_DIR, state) + const state: BoulderState = { + active_plan: planPath, + started_at: "2026-01-02T10:00:00Z", + session_ids: [MAIN_SESSION_ID], + plan_name: "test-plan", + } + writeBoulderState(TEST_DIR, state) - // #given - last agent is NOT orchestrator-sisyphus - cleanupMessageStorage(MAIN_SESSION_ID) - setupMessageStorage(MAIN_SESSION_ID, "Sisyphus") + // #given - last agent is NOT atlas + cleanupMessageStorage(MAIN_SESSION_ID) + setupMessageStorage(MAIN_SESSION_ID, "Sisyphus") - const mockInput = createMockPluginInput() - const hook = createSisyphusOrchestratorHook(mockInput) + const mockInput = createMockPluginInput() + const hook = createSisyphusOrchestratorHook(mockInput) - // #when - await hook.handler({ - event: { - type: "session.idle", - properties: { sessionID: MAIN_SESSION_ID }, - }, - }) + // #when + await hook.handler({ + event: { + type: "session.idle", + properties: { sessionID: MAIN_SESSION_ID }, + }, + }) - // #then - should NOT call prompt because agent is not orchestrator-sisyphus - expect(mockInput._promptMock).not.toHaveBeenCalled() - }) + // #then - should NOT call prompt because agent is not atlas + expect(mockInput._promptMock).not.toHaveBeenCalled() + }) test("should debounce rapid continuation injections (prevent infinite loop)", async () => { // #given - boulder state with incomplete plan diff --git a/src/hooks/sisyphus-orchestrator/index.ts b/src/hooks/atlas/index.ts similarity index 97% rename from src/hooks/sisyphus-orchestrator/index.ts rename to src/hooks/atlas/index.ts index 8b7a4f66b..859b462e2 100644 --- a/src/hooks/sisyphus-orchestrator/index.ts +++ b/src/hooks/atlas/index.ts @@ -13,7 +13,7 @@ import { log } from "../../shared/logger" import { createSystemDirective, SYSTEM_DIRECTIVE_PREFIX, SystemDirectiveTypes } from "../../shared/system-directive" import type { BackgroundManager } from "../../features/background-agent" -export const HOOK_NAME = "sisyphus-orchestrator" +export const HOOK_NAME = "atlas" /** * Cross-platform check if a path is inside .sisyphus/ directory. @@ -111,7 +111,7 @@ const ORCHESTRATOR_DELEGATION_REQUIRED = ` **STOP. YOU ARE VIOLATING ORCHESTRATOR PROTOCOL.** -You (orchestrator-sisyphus) are attempting to directly modify a file outside \`.sisyphus/\`. +You (atlas) are attempting to directly modify a file outside \`.sisyphus/\`. **Path attempted:** $FILE_PATH @@ -393,12 +393,12 @@ function getMessageDir(sessionID: string): string | null { } function isCallerOrchestrator(sessionID?: string): boolean { - if (!sessionID) return false - const messageDir = getMessageDir(sessionID) - if (!messageDir) return false - const nearest = findNearestMessageWithFields(messageDir) - return nearest?.agent === "orchestrator-sisyphus" -} + if (!sessionID) return false + const messageDir = getMessageDir(sessionID) + if (!messageDir) return false + const nearest = findNearestMessageWithFields(messageDir) + return nearest?.agent === "atlas" + } interface SessionState { lastEventWasAbortError?: boolean @@ -493,15 +493,15 @@ export function createSisyphusOrchestratorHook( : undefined } - await ctx.client.session.prompt({ - path: { id: sessionID }, - body: { - agent: "orchestrator-sisyphus", - ...(model !== undefined ? { model } : {}), - parts: [{ type: "text", text: prompt }], - }, - query: { directory: ctx.directory }, - }) + await ctx.client.session.prompt({ + path: { id: sessionID }, + body: { + agent: "atlas", + ...(model !== undefined ? { model } : {}), + parts: [{ type: "text", text: prompt }], + }, + query: { directory: ctx.directory }, + }) log(`[${HOOK_NAME}] Boulder continuation injected`, { sessionID }) } catch (err) { diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 55e194b08..31690d22e 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -28,5 +28,5 @@ export { createEditErrorRecoveryHook } from "./edit-error-recovery"; export { createPrometheusMdOnlyHook } from "./prometheus-md-only"; export { createTaskResumeInfoHook } from "./task-resume-info"; export { createStartWorkHook } from "./start-work"; -export { createSisyphusOrchestratorHook } from "./sisyphus-orchestrator"; +export { createSisyphusOrchestratorHook } from "./atlas"; export { createDelegateTaskRetryHook } from "./delegate-task-retry"; diff --git a/src/tools/delegate-task/constants.ts b/src/tools/delegate-task/constants.ts index 0df516d3b..a7a29e448 100644 --- a/src/tools/delegate-task/constants.ts +++ b/src/tools/delegate-task/constants.ts @@ -207,6 +207,10 @@ export const DEFAULT_CATEGORIES: Record = { }, } +export const CATEGORY_MODEL_CATALOG: Record = { + ultrabrain: { model: "openai/gpt-5.2-codex", variant: "xhigh" }, +} + export const CATEGORY_PROMPT_APPENDS: Record = { "visual-engineering": VISUAL_CATEGORY_PROMPT_APPEND, ultrabrain: STRATEGIC_CATEGORY_PROMPT_APPEND, diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index 110def28d..28b40b6d1 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -4,7 +4,7 @@ import { join } from "node:path" import type { BackgroundManager } from "../../features/background-agent" import type { DelegateTaskArgs } from "./types" import type { CategoryConfig, CategoriesConfig, GitMasterConfig } from "../../config/schema" -import { DELEGATE_TASK_DESCRIPTION, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS } from "./constants" +import { DELEGATE_TASK_DESCRIPTION, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS, CATEGORY_MODEL_CATALOG } from "./constants" import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector" import { resolveMultipleSkillsAsync } from "../../features/opencode-skill-loader/skill-content" import { discoverSkills } from "../../features/opencode-skill-loader"