From a86221b1a9ce129fc3f0517812bb674de3cf82ee Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 16 May 2026 17:40:11 +0900 Subject: [PATCH] feat(workspace): store runtime state under omo --- src/cli/boulder/boulder.test.ts | 4 +- src/cli/run/completion-continuation.test.ts | 76 +++++++++---------- .../continuation-state.json-backend.test.ts | 12 +-- src/features/boulder-state/AGENTS.md | 4 +- src/features/boulder-state/constants.ts | 4 +- src/features/boulder-state/storage.test.ts | 62 +++++++-------- src/features/boulder-state/storage.ts | 2 +- .../builtin-commands/templates/start-work.ts | 10 +-- .../run-continuation-state/constants.ts | 2 +- .../start-work/context-info-builder.test.ts | 4 +- src/hooks/start-work/context-info-builder.ts | 2 +- src/hooks/start-work/index.test.ts | 50 ++++++------ src/tools/task/task-list.test.ts | 42 +++++----- 13 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/cli/boulder/boulder.test.ts b/src/cli/boulder/boulder.test.ts index c4fb9bc91..158961873 100644 --- a/src/cli/boulder/boulder.test.ts +++ b/src/cli/boulder/boulder.test.ts @@ -10,7 +10,7 @@ function createTempDirectory(): string { } function seedPlanAndState(directory: string): void { - const planDirectory = join(directory, ".sisyphus", "plans") + const planDirectory = join(directory, ".omo", "plans") mkdirSync(planDirectory, { recursive: true }) const planAPath = join(planDirectory, "alpha.md") @@ -35,7 +35,7 @@ function seedPlanAndState(directory: string): void { "utf-8", ) - const boulderDirectory = join(directory, ".sisyphus") + const boulderDirectory = join(directory, ".omo") mkdirSync(boulderDirectory, { recursive: true }) writeFileSync( diff --git a/src/cli/run/completion-continuation.test.ts b/src/cli/run/completion-continuation.test.ts index f707b08f4..993fb040e 100644 --- a/src/cli/run/completion-continuation.test.ts +++ b/src/cli/run/completion-continuation.test.ts @@ -53,10 +53,10 @@ function writeBoulderStateFile( sessionIDs: string[], sessionOrigins?: Record, ): void { - const sisyphusDir = join(directory, ".sisyphus") - mkdirSync(sisyphusDir, { recursive: true }) + const omoDir = join(directory, ".omo") + mkdirSync(omoDir, { recursive: true }) writeFileSync( - join(sisyphusDir, "boulder.json"), + join(omoDir, "boulder.json"), JSON.stringify({ active_plan: activePlanPath, started_at: new Date().toISOString(), @@ -74,8 +74,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "active-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "active-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] incomplete task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["test-session"]) const ctx = createMockContext(directory) @@ -92,8 +92,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "done-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "done-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [x] completed task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["test-session"]) const ctx = createMockContext(directory) @@ -110,17 +110,17 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const mainPlanPath = join(directory, ".sisyphus", "plans", "done-in-worktree-plan.md") + const mainPlanPath = join(directory, ".omo", "plans", "done-in-worktree-plan.md") const worktreeDirectory = createTempDir() - const worktreePlanPath = join(worktreeDirectory, ".sisyphus", "plans", "done-in-worktree-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) - mkdirSync(join(worktreeDirectory, ".sisyphus", "plans"), { recursive: true }) + const worktreePlanPath = join(worktreeDirectory, ".omo", "plans", "done-in-worktree-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) + mkdirSync(join(worktreeDirectory, ".omo", "plans"), { recursive: true }) writeFileSync(mainPlanPath, "- [ ] stale main repo task\n", "utf-8") writeFileSync(worktreePlanPath, "- [x] completed worktree task\n", "utf-8") - const sisyphusDir = join(directory, ".sisyphus") - mkdirSync(sisyphusDir, { recursive: true }) + const omoDir = join(directory, ".omo") + mkdirSync(omoDir, { recursive: true }) writeFileSync( - join(sisyphusDir, "boulder.json"), + join(omoDir, "boulder.json"), JSON.stringify({ active_plan: mainPlanPath, started_at: new Date().toISOString(), @@ -145,8 +145,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "active-descendant-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "active-descendant-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session", "child-session"], { "root-session": "direct", @@ -181,8 +181,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "lineage-non-subagent-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "lineage-non-subagent-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session"]) @@ -209,8 +209,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "lineage-agent-mismatch-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "lineage-agent-mismatch-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session", "mismatch-subagent-session"], { "root-session": "direct", @@ -244,8 +244,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "appended-mismatch-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "appended-mismatch-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session", "appended-mismatch-session"], { "root-session": "direct", @@ -279,8 +279,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "appended-unresolved-lineage-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "appended-unresolved-lineage-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session", "ses_appended_descendant"], { "root-session": "direct", @@ -311,8 +311,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "direct-tracked-child-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "direct-tracked-child-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["ses_direct_child"]) @@ -338,8 +338,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "multi-tracked-direct-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "multi-tracked-direct-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["ses_other_tracked", "ses_direct_tracked"], { "ses_other_tracked": "direct", @@ -368,8 +368,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "unknown-origin-multi-session-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "unknown-origin-multi-session-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["ses_root_tracked", "ses_unknown_child"]) @@ -392,8 +392,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "multi-tracked-direct-child-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "multi-tracked-direct-child-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["ses_root_tracked", "ses_direct_child"], { "ses_root_tracked": "direct", @@ -427,8 +427,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "compaction-descendant-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "compaction-descendant-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session", "ses_child_after_compaction"], { "root-session": "direct", @@ -466,8 +466,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "sqlite-ordered-descendant-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "sqlite-ordered-descendant-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["root-session"]) @@ -502,8 +502,8 @@ describe("checkCompletionConditions continuation coverage", () => { // given spyOn(console, "log").mockImplementation(() => {}) const directory = createTempDir() - const planPath = join(directory, ".sisyphus", "plans", "session-agent-fallback-plan.md") - mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true }) + const planPath = join(directory, ".omo", "plans", "session-agent-fallback-plan.md") + mkdirSync(join(directory, ".omo", "plans"), { recursive: true }) writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") writeBoulderStateFile(directory, planPath, ["ses_root_tracked", "ses_appended_child"], { "ses_root_tracked": "direct", diff --git a/src/cli/run/continuation-state.json-backend.test.ts b/src/cli/run/continuation-state.json-backend.test.ts index f53cdd547..25ef2d8c2 100644 --- a/src/cli/run/continuation-state.json-backend.test.ts +++ b/src/cli/run/continuation-state.json-backend.test.ts @@ -68,12 +68,12 @@ describe("getContinuationState JSON backend descendant coverage", () => { test("returns active boulder for explicitly tracked appended descendant on JSON message storage backend", async () => { // given const directory = createTempDir() - const plansDir = join(directory, ".sisyphus", "plans") + const plansDir = join(directory, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "json-descendant-plan.md") writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") - mkdirSync(join(directory, ".sisyphus"), { recursive: true }) - writeFileSync(join(directory, ".sisyphus", "boulder.json"), JSON.stringify({ + mkdirSync(join(directory, ".omo"), { recursive: true }) + writeFileSync(join(directory, ".omo", "boulder.json"), JSON.stringify({ active_plan: planPath, started_at: new Date().toISOString(), session_ids: ["ses_root_session", "ses_child_session"], @@ -134,12 +134,12 @@ describe("getContinuationState JSON backend descendant coverage", () => { test("prefers newest JSON agent by time.created even when filenames look reversed and timestamps tie-break by filename only", async () => { // given const directory = createTempDir() - const plansDir = join(directory, ".sisyphus", "plans") + const plansDir = join(directory, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "json-random-id-plan.md") writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8") - mkdirSync(join(directory, ".sisyphus"), { recursive: true }) - writeFileSync(join(directory, ".sisyphus", "boulder.json"), JSON.stringify({ + mkdirSync(join(directory, ".omo"), { recursive: true }) + writeFileSync(join(directory, ".omo", "boulder.json"), JSON.stringify({ active_plan: planPath, started_at: new Date().toISOString(), session_ids: ["ses_root_random"], diff --git a/src/features/boulder-state/AGENTS.md b/src/features/boulder-state/AGENTS.md index 8c26ddd72..d403c7c59 100644 --- a/src/features/boulder-state/AGENTS.md +++ b/src/features/boulder-state/AGENTS.md @@ -34,7 +34,7 @@ interface BoulderState { | File | Purpose | |------|---------| | `types.ts` | `BoulderState`, `BoulderWorkState`, `TaskSessionState`, status enums | -| `storage.ts` | Atomic CRUD on `.sisyphus/boulder-state.json`. Writes via temp file + rename; file lock per work_id | +| `storage.ts` | Atomic CRUD on `.omo/boulder-state.json`. Writes via temp file + rename; file lock per work_id | | `constants.ts` | Path resolution + schema version constant | | `top-level-task.ts` | Helpers to identify the current top-level plan task and resolve its reusable subagent session | | `format-duration.ts` | `formatDurationHuman(ms)` — "1h 23m 5s" formatting for boulder duration | @@ -67,7 +67,7 @@ session.completed ## STORAGE ``` -/.sisyphus/boulder-state.json # gitignored; one file per worktree +/.omo/boulder-state.json # gitignored; one file per worktree ``` Atomic writes: temp file → fsync (where supported) → rename. File lock prevents concurrent corruption. Schema migrations between versions handled inline in `storage.ts`. diff --git a/src/features/boulder-state/constants.ts b/src/features/boulder-state/constants.ts index b0de70db8..323d862d8 100644 --- a/src/features/boulder-state/constants.ts +++ b/src/features/boulder-state/constants.ts @@ -2,7 +2,7 @@ * Boulder State Constants */ -export const BOULDER_DIR = ".sisyphus" +export const BOULDER_DIR = ".omo" export const BOULDER_FILE = "boulder.json" export const BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}` @@ -10,4 +10,4 @@ export const NOTEPAD_DIR = "notepads" export const NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}` /** Prometheus plan directory pattern */ -export const PROMETHEUS_PLANS_DIR = ".sisyphus/plans" +export const PROMETHEUS_PLANS_DIR = ".omo/plans" diff --git a/src/features/boulder-state/storage.test.ts b/src/features/boulder-state/storage.test.ts index aa7bf1858..55d66b379 100644 --- a/src/features/boulder-state/storage.test.ts +++ b/src/features/boulder-state/storage.test.ts @@ -34,14 +34,14 @@ import { readCurrentTopLevelTask } from "./top-level-task" describe("boulder-state", () => { const TEST_DIR = join(tmpdir(), "boulder-state-test-" + Date.now()) - const SISYPHUS_DIR = join(TEST_DIR, ".sisyphus") + const OMO_DIR = join(TEST_DIR, ".omo") beforeEach(() => { if (!existsSync(TEST_DIR)) { mkdirSync(TEST_DIR, { recursive: true }) } - if (!existsSync(SISYPHUS_DIR)) { - mkdirSync(SISYPHUS_DIR, { recursive: true }) + if (!existsSync(OMO_DIR)) { + mkdirSync(OMO_DIR, { recursive: true }) } clearBoulderState(TEST_DIR) }) @@ -55,7 +55,7 @@ describe("boulder-state", () => { describe("readBoulderState", () => { test("should preserve legacy boulder.json fields during round-trip", () => { // given - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") const legacyRawState = { active_plan: "/path/to/legacy-plan.md", started_at: "2026-01-01T00:00:00.000Z", @@ -88,7 +88,7 @@ describe("boulder-state", () => { test("should return null for JSON null value", () => { //#given - boulder.json containing null - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, "null") //#when @@ -100,7 +100,7 @@ describe("boulder-state", () => { test("should return null for JSON primitive value", () => { //#given - boulder.json containing a string - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, '"just a string"') //#when @@ -112,7 +112,7 @@ describe("boulder-state", () => { test("should default session_ids to [] when missing from JSON", () => { //#given - boulder.json without session_ids field - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/path/to/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -129,7 +129,7 @@ describe("boulder-state", () => { test("should default session_ids to [] when not an array", () => { //#given - boulder.json with session_ids as a string - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/path/to/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -147,7 +147,7 @@ describe("boulder-state", () => { test("should default session_ids to [] for empty object", () => { //#given - boulder.json with empty object - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({})) //#when @@ -160,7 +160,7 @@ describe("boulder-state", () => { test("should backfill missing origin as direct only for a single tracked session", () => { // given - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/path/to/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -177,7 +177,7 @@ describe("boulder-state", () => { test("should keep missing origins empty when multiple sessions are tracked", () => { // given - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/path/to/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -213,7 +213,7 @@ describe("boulder-state", () => { test("should default task_sessions to empty object when missing from JSON", () => { // given - boulder.json without task_sessions field - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/path/to/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -231,7 +231,7 @@ describe("boulder-state", () => { }) describe("writeBoulderState", () => { - test("should write state and create .sisyphus directory if needed", () => { + test("should write state and create .omo directory if needed", () => { // given - state to write const state: BoulderState = { active_plan: "/test/plan.md", @@ -298,7 +298,7 @@ describe("boulder-state", () => { test("should not crash when boulder.json has no session_ids field", () => { //#given - boulder.json without session_ids - const boulderFile = join(SISYPHUS_DIR, "boulder.json") + const boulderFile = join(OMO_DIR, "boulder.json") writeFileSync(boulderFile, JSON.stringify({ active_plan: "/plan.md", started_at: "2026-01-01T00:00:00Z", @@ -430,7 +430,7 @@ describe("boulder-state", () => { test("should add second work and keep both active works", () => { // given const firstState = createBoulderState( - join(TEST_DIR, ".sisyphus/plans/plan-a.md"), + join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a", "atlas", "/worktree-a", @@ -440,7 +440,7 @@ describe("boulder-state", () => { // when const updatedState = addBoulderWork(TEST_DIR, { - planPath: join(TEST_DIR, ".sisyphus/plans/plan-b.md"), + planPath: join(TEST_DIR, ".omo/plans/plan-b.md"), sessionId: "session-b", agent: "atlas", worktreePath: "/worktree-b", @@ -459,12 +459,12 @@ describe("boulder-state", () => { test("should resolve work for session using updated_at tie-break", () => { // given const baseState = createBoulderState( - join(TEST_DIR, ".sisyphus/plans/plan-a.md"), + join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a", ) writeBoulderState(TEST_DIR, baseState) const stateWithSecond = addBoulderWork(TEST_DIR, { - planPath: join(TEST_DIR, ".sisyphus/plans/plan-b.md"), + planPath: join(TEST_DIR, ".omo/plans/plan-b.md"), sessionId: "session-b", }) expect(stateWithSecond).not.toBeNull() @@ -486,10 +486,10 @@ describe("boulder-state", () => { test("should support selecting active work and read helpers", () => { // given - const initialState = createBoulderState(join(TEST_DIR, ".sisyphus/plans/plan-a.md"), "session-a") + const initialState = createBoulderState(join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a") writeBoulderState(TEST_DIR, initialState) const added = addBoulderWork(TEST_DIR, { - planPath: join(TEST_DIR, ".sisyphus/plans/plan-b.md"), + planPath: join(TEST_DIR, ".omo/plans/plan-b.md"), sessionId: "session-b", worktreePath: "/tmp/worktree-b", }) @@ -516,7 +516,7 @@ describe("boulder-state", () => { test("should upsert task session for specific work and keep first started_at", () => { // given - const initialState = createBoulderState(join(TEST_DIR, ".sisyphus/plans/plan-a.md"), "session-a") + const initialState = createBoulderState(join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a") writeBoulderState(TEST_DIR, initialState) const workId = initialState.active_work_id! @@ -550,7 +550,7 @@ describe("boulder-state", () => { describe("task timer and completion helpers", () => { test("should keep started_at stable when starting timer repeatedly", () => { // given - const initialState = createBoulderState(join(TEST_DIR, ".sisyphus/plans/plan-a.md"), "session-a") + const initialState = createBoulderState(join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a") writeBoulderState(TEST_DIR, initialState) const workId = initialState.active_work_id! @@ -578,7 +578,7 @@ describe("boulder-state", () => { test("should compute elapsed_ms when ending task timer", () => { // given - const initialState = createBoulderState(join(TEST_DIR, ".sisyphus/plans/plan-a.md"), "session-a") + const initialState = createBoulderState(join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a") writeBoulderState(TEST_DIR, initialState) const workId = initialState.active_work_id! startTaskTimer(TEST_DIR, workId, { @@ -601,11 +601,11 @@ describe("boulder-state", () => { test("should complete one work and keep other work untouched", () => { // given - const initialState = createBoulderState(join(TEST_DIR, ".sisyphus/plans/plan-a.md"), "session-a") + const initialState = createBoulderState(join(TEST_DIR, ".omo/plans/plan-a.md"), "session-a") writeBoulderState(TEST_DIR, initialState) const firstWorkId = initialState.active_work_id! const withSecond = addBoulderWork(TEST_DIR, { - planPath: join(TEST_DIR, ".sisyphus/plans/plan-b.md"), + planPath: join(TEST_DIR, ".omo/plans/plan-b.md"), sessionId: "session-b", }) const secondWorkId = Object.keys(withSecond!.works!).find((workId) => workId !== firstWorkId)! @@ -620,13 +620,13 @@ describe("boulder-state", () => { Date.parse("2026-01-01T01:00:00.000Z") - Date.parse(completedState!.works![firstWorkId]!.started_at), ) expect(completedState?.works?.[secondWorkId]?.status).not.toBe("completed") - expect(existsSync(join(SISYPHUS_DIR, "boulder.json"))).toBe(true) + expect(existsSync(join(OMO_DIR, "boulder.json"))).toBe(true) }) test("should keep first completion timing when completeBoulder is called repeatedly", () => { // given const initialState = createBoulderState( - join(TEST_DIR, ".sisyphus/plans/plan-idempotent.md"), + join(TEST_DIR, ".omo/plans/plan-idempotent.md"), "session-a", ) writeBoulderState(TEST_DIR, initialState) @@ -974,7 +974,7 @@ describe("boulder-state", () => { describe("getPlanName", () => { test("should extract plan name from path", () => { // given - const path = "/home/user/.sisyphus/plans/project/my-feature.md" + const path = "/home/user/.omo/plans/project/my-feature.md" // when const name = getPlanName(path) // then @@ -1042,9 +1042,9 @@ describe("boulder-state", () => { describe("resolveBoulderPlanPath", () => { test("should prefer the mirrored worktree plan when it exists", () => { // given - const planPath = join(TEST_DIR, ".sisyphus", "plans", "worktree-plan.md") + const planPath = join(TEST_DIR, ".omo", "plans", "worktree-plan.md") const worktreeDir = join(tmpdir(), `boulder-state-worktree-${Date.now()}`) - const worktreePlanPath = join(worktreeDir, ".sisyphus", "plans", "worktree-plan.md") + const worktreePlanPath = join(worktreeDir, ".omo", "plans", "worktree-plan.md") mkdirSync(dirname(planPath), { recursive: true }) mkdirSync(dirname(worktreePlanPath), { recursive: true }) writeFileSync(planPath, "# Plan\n- [ ] Main repo task\n") @@ -1066,7 +1066,7 @@ describe("boulder-state", () => { test("should fall back to the tracked plan when the mirrored worktree plan is missing", () => { // given - const planPath = join(TEST_DIR, ".sisyphus", "plans", "fallback-plan.md") + const planPath = join(TEST_DIR, ".omo", "plans", "fallback-plan.md") mkdirSync(dirname(planPath), { recursive: true }) writeFileSync(planPath, "# Plan\n- [ ] Main repo task\n") diff --git a/src/features/boulder-state/storage.ts b/src/features/boulder-state/storage.ts index 2eeda2436..54517f239 100644 --- a/src/features/boulder-state/storage.ts +++ b/src/features/boulder-state/storage.ts @@ -348,7 +348,7 @@ export function upsertTaskSessionState( /** * Find Prometheus plan files for this project. - * Prometheus stores plans at: {project}/.sisyphus/plans/{name}.md + * Prometheus stores plans at: {project}/.omo/plans/{name}.md */ export function findPrometheusPlans(directory: string): string[] { const plansDir = join(directory, PROMETHEUS_PLANS_DIR) diff --git a/src/features/builtin-commands/templates/start-work.ts b/src/features/builtin-commands/templates/start-work.ts index 70c0a8aa3..fe0a833cd 100644 --- a/src/features/builtin-commands/templates/start-work.ts +++ b/src/features/builtin-commands/templates/start-work.ts @@ -11,9 +11,9 @@ export const START_WORK_TEMPLATE = `You are starting a Sisyphus work session. ## WHAT TO DO -1. **Find available plans**: Search for Prometheus-generated plan files at \`.sisyphus/plans/\` +1. **Find available plans**: Search for Prometheus-generated plan files at \`.omo/plans/\` -2. **Check for active boulder state**: Read \`.sisyphus/boulder.json\` if it exists +2. **Check for active boulder state**: Read \`.omo/boulder.json\` if it exists 3. **Decision logic**: - If multiple active works are listed in your context: @@ -119,10 +119,10 @@ Register these as task/todo items so progress is tracked and visible throughout When working in a worktree (\`worktree_path\` is set in boulder.json) and ALL plan tasks are complete: 1. Commit all remaining changes in the worktree -2. **Sync .sisyphus state back**: Copy \`.sisyphus/\` from the worktree to the main repo before removal. - This is CRITICAL when \`.sisyphus/\` is gitignored - state written during worktree execution would otherwise be lost. +2. **Sync .omo state back**: Copy \`.omo/\` from the worktree to the main repo before removal. + This is CRITICAL when \`.omo/\` is gitignored - state written during worktree execution would otherwise be lost. \`\`\`bash - cp -r /.sisyphus/* /.sisyphus/ 2>/dev/null || true + cp -r /.omo/* /.omo/ 2>/dev/null || true \`\`\` 3. Switch to the main working directory (the original repo, NOT the worktree) 4. Merge the worktree branch into the current branch: \`git merge \` diff --git a/src/features/run-continuation-state/constants.ts b/src/features/run-continuation-state/constants.ts index 0f9c581f1..6fe2e2258 100644 --- a/src/features/run-continuation-state/constants.ts +++ b/src/features/run-continuation-state/constants.ts @@ -1 +1 @@ -export const CONTINUATION_MARKER_DIR = ".sisyphus/run-continuation" +export const CONTINUATION_MARKER_DIR = ".omo/run-continuation" diff --git a/src/hooks/start-work/context-info-builder.test.ts b/src/hooks/start-work/context-info-builder.test.ts index 139cc179e..cd947f3dd 100644 --- a/src/hooks/start-work/context-info-builder.test.ts +++ b/src/hooks/start-work/context-info-builder.test.ts @@ -26,7 +26,7 @@ describe("buildStartWorkContextInfo", () => { } function writePlan(planName: string, content: string): string { - const plansDirectory = join(testDirectory, ".sisyphus", "plans") + const plansDirectory = join(testDirectory, ".omo", "plans") mkdirSync(plansDirectory, { recursive: true }) const planPath = join(plansDirectory, `${planName}.md`) writeFileSync(planPath, content) @@ -176,7 +176,7 @@ describe("buildStartWorkContextInfo", () => { writePlan("new-plan-c", "## TODOs\n- [ ] 1. Work C") const initialState = createBoulderState( - join(testDirectory, ".sisyphus", "plans", "work-a.md"), + join(testDirectory, ".omo", "plans", "work-a.md"), "session-a", "atlas", "/tmp/worktree-a", diff --git a/src/hooks/start-work/context-info-builder.ts b/src/hooks/start-work/context-info-builder.ts index 9fc8e0fd4..c94a3e473 100644 --- a/src/hooks/start-work/context-info-builder.ts +++ b/src/hooks/start-work/context-info-builder.ts @@ -328,7 +328,7 @@ function buildPlanDiscoveryContext(params: { return contextInfo + ` ## No Plans Found - No Prometheus plan files found in the .sisyphus plans directory. + No Prometheus plan files found in the .omo plans directory. Use the Prometheus agent to create a work plan first.` } diff --git a/src/hooks/start-work/index.test.ts b/src/hooks/start-work/index.test.ts index e4a981218..8a4f4c3d7 100644 --- a/src/hooks/start-work/index.test.ts +++ b/src/hooks/start-work/index.test.ts @@ -20,7 +20,7 @@ import { unsafeTestValue } from "../../../test-support/unsafe-test-value" describe("start-work hook", () => { let testDir: string - let sisyphusDir: string + let omoDir: string function createMockPluginInput() { return { @@ -50,12 +50,12 @@ You are starting a Sisyphus work session. sessionState.registerAgentName("atlas") sessionState.registerAgentName("sisyphus") testDir = join(tmpdir(), `start-work-test-${randomUUID()}`) - sisyphusDir = join(testDir, ".sisyphus") + omoDir = join(testDir, ".omo") if (!existsSync(testDir)) { mkdirSync(testDir, { recursive: true }) } - if (!existsSync(sisyphusDir)) { - mkdirSync(sisyphusDir, { recursive: true }) + if (!existsSync(omoDir)) { + mkdirSync(omoDir, { recursive: true }) } clearBoulderState(testDir) }) @@ -224,7 +224,7 @@ You are starting a Sisyphus work session. test("should auto-select when only one incomplete plan among multiple plans", async () => { // given - multiple plans but only one incomplete - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) // Plan 1: complete (all checked) @@ -254,7 +254,7 @@ You are starting a Sisyphus work session. test("should wrap multiple plans message in system-reminder tag", async () => { // given - multiple incomplete plans - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const plan1Path = join(plansDir, "plan-a.md") @@ -282,7 +282,7 @@ You are starting a Sisyphus work session. test("should use 'ask user' prompt style for multiple plans", async () => { // given - multiple incomplete plans - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const plan1Path = join(plansDir, "plan-x.md") @@ -309,7 +309,7 @@ You are starting a Sisyphus work session. test("should select explicitly specified plan name from user-request, ignoring existing boulder state", async () => { // given - existing boulder state pointing to old plan - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) // Old plan (in boulder state) @@ -353,7 +353,7 @@ You are starting a Sisyphus work session. test("should strip ultrawork/ulw keywords from plan name argument", async () => { // given - plan with ultrawork keyword in user-request - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "my-feature-plan.md") @@ -382,7 +382,7 @@ You are starting a Sisyphus work session. test("should strip ulw keyword from plan name argument", async () => { // given - plan with ulw keyword in user-request - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "api-refactor.md") @@ -411,7 +411,7 @@ You are starting a Sisyphus work session. test("should match plan by partial name", async () => { // given - user specifies partial plan name - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "2026-01-15-feature-implementation.md") @@ -440,7 +440,7 @@ You are starting a Sisyphus work session. test("should match quoted human-readable plan names to slugged filenames", async () => { // given - saved plan uses a slugged filename - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "my-feature-plan.md") @@ -469,7 +469,7 @@ You are starting a Sisyphus work session. test("should match Korean plan names after Unicode-aware normalization", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "결제-플로우.md") @@ -498,7 +498,7 @@ You are starting a Sisyphus work session. test("should match Japanese plan names after Unicode-aware normalization", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "支払い-フロー.md") @@ -527,7 +527,7 @@ You are starting a Sisyphus work session. test("should keep ASCII plan name matching behavior unchanged", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "checkout-flow.md") @@ -556,7 +556,7 @@ You are starting a Sisyphus work session. test("should match mixed ASCII and non-ASCII plan names", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) const planPath = join(plansDir, "v2-결제-flow.md") @@ -674,7 +674,7 @@ You are starting a Sisyphus work session. sessionState.registerAgentName("sisyphus") sessionState.updateSessionAgent("ses-prometheus-to-worker", "prometheus") - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "worker-plan.md"), "# Plan\n- [ ] Task 1") @@ -732,7 +732,7 @@ You are starting a Sisyphus work session. test("#given start-work hands the session to Atlas #when Atlas later receives session.idle #then the same session continues the selected plan", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "atlas-plan.md"), "# Plan\n- [ ] Task 1\n- [ ] Task 2") @@ -770,7 +770,7 @@ You are starting a Sisyphus work session. test("#given start-work hands the session to Atlas but background work is still running #when that work finishes #then Atlas resumes via retry for the same session", async () => { // given - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "atlas-plan.md"), "# Plan\n- [ ] Task 1\n- [ ] Task 2") @@ -877,7 +877,7 @@ You are starting a Sisyphus work session. test("should NOT inject worktree instructions when no --worktree flag", async () => { // given - single plan, no worktree flag - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "my-plan.md"), "# Plan\n- [ ] Task 1") @@ -897,7 +897,7 @@ You are starting a Sisyphus work session. test("should inject worktree path when --worktree flag is valid", async () => { // given - single plan + valid worktree path - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "my-plan.md"), "# Plan\n- [ ] Task 1") detectSpy.mockReturnValue("/validated/worktree") @@ -919,7 +919,7 @@ You are starting a Sisyphus work session. test("should store worktree_path in boulder when --worktree is valid", async () => { // given - plan + valid worktree - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "my-plan.md"), "# Plan\n- [ ] Task 1") detectSpy.mockReturnValue("/valid/wt") @@ -939,7 +939,7 @@ You are starting a Sisyphus work session. test("should NOT store worktree_path when --worktree path is invalid", async () => { // given - plan + invalid worktree path (detectWorktreePath returns null) - const plansDir = join(testDir, ".sisyphus", "plans") + const plansDir = join(testDir, ".omo", "plans") mkdirSync(plansDir, { recursive: true }) writeFileSync(join(plansDir, "my-plan.md"), "# Plan\n- [ ] Task 1") // detectSpy already returns null by default @@ -1017,9 +1017,9 @@ You are starting a Sisyphus work session. test("should show worktree plan progress and path when the mirrored plan exists", async () => { // given - const mainPlanPath = join(testDir, ".sisyphus", "plans", "resume-worktree-plan.md") + const mainPlanPath = join(testDir, ".omo", "plans", "resume-worktree-plan.md") const worktreeDir = join(testDir, "..", `resume-worktree-${randomUUID()}`) - const worktreePlanPath = join(worktreeDir, ".sisyphus", "plans", "resume-worktree-plan.md") + const worktreePlanPath = join(worktreeDir, ".omo", "plans", "resume-worktree-plan.md") mkdirSync(dirname(mainPlanPath), { recursive: true }) mkdirSync(dirname(worktreePlanPath), { recursive: true }) writeFileSync(mainPlanPath, "# Plan\n- [ ] Main repo task\n") diff --git a/src/tools/task/task-list.test.ts b/src/tools/task/task-list.test.ts index da7f6d3c5..1f40d14f9 100644 --- a/src/tools/task/task-list.test.ts +++ b/src/tools/task/task-list.test.ts @@ -11,7 +11,7 @@ describe("createTaskList", () => { let taskDir: string beforeEach(() => { - taskDir = join(testProjectDir, ".sisyphus/tasks") + taskDir = join(testProjectDir, ".omo/tasks") if (existsSync(taskDir)) { rmSync(taskDir, { recursive: true }) } @@ -28,7 +28,7 @@ describe("createTaskList", () => { const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -64,13 +64,13 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task1) - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-2.json"), task2) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task1) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-2.json"), task2) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -107,13 +107,13 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task1) - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-2.json"), task2) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task1) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-2.json"), task2) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -142,12 +142,12 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -204,14 +204,14 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-blocker-completed.json"), blockerCompleted) - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-blocker-pending.json"), blockerPending) - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-main.json"), mainTask) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-blocker-completed.json"), blockerCompleted) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-blocker-pending.json"), blockerPending) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-main.json"), mainTask) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -248,13 +248,13 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task1) - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-2.json"), task2) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task1) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-2.json"), task2) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -281,12 +281,12 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, }, @@ -313,12 +313,12 @@ describe("createTaskList", () => { threadID: "test-session", } - writeJsonAtomic(join(testProjectDir, ".sisyphus/tasks", "T-1.json"), task) + writeJsonAtomic(join(testProjectDir, ".omo/tasks", "T-1.json"), task) const config = { sisyphus: { tasks: { - storage_path: join(testProjectDir, ".sisyphus/tasks"), + storage_path: join(testProjectDir, ".omo/tasks"), claude_code_compat: false, }, },