refactor: major codebase cleanup - BDD comments, file splitting, bug fixes (#1350)

* style(tests): normalize BDD comments from '// #given' to '// given'

- Replace 4,668 Python-style BDD comments across 107 test files
- Patterns changed: // #given -> // given, // #when -> // when, // #then -> // then
- Also handles no-space variants: //#given -> // given

* fix(rules-injector): prefer output.metadata.filePath over output.title

- Extract file path resolution to dedicated output-path.ts module
- Prefer metadata.filePath which contains actual file path
- Fall back to output.title only when metadata unavailable
- Fixes issue where rules weren't injected when tool output title was a label

* feat(slashcommand): add optional user_message parameter

- Add user_message optional parameter for command arguments
- Model can now call: command='publish' user_message='patch'
- Improves error messages with clearer format guidance
- Helps LLMs understand correct parameter usage

* feat(hooks): restore compaction-context-injector hook

- Restore hook deleted in cbbc7bd0 for session compaction context
- Injects 7 mandatory sections: User Requests, Final Goal, Work Completed,
  Remaining Tasks, Active Working Context, MUST NOT Do, Agent Verification State
- Re-register in hooks/index.ts and main plugin entry

* refactor(background-agent): split manager.ts into focused modules

- Extract constants.ts for TTL values and internal types (52 lines)
- Extract state.ts for TaskStateManager class (204 lines)
- Extract spawner.ts for task creation logic (244 lines)
- Extract result-handler.ts for completion handling (265 lines)
- Reduce manager.ts from 1377 to 755 lines (45% reduction)
- Maintain backward compatible exports

* refactor(agents): split prometheus-prompt.ts into subdirectory

- Move 1196-line prometheus-prompt.ts to prometheus/ subdirectory
- Organize prompt sections into separate files for maintainability
- Update agents/index.ts exports

* refactor(delegate-task): split tools.ts into focused modules

- Extract categories.ts for category definitions and routing
- Extract executor.ts for task execution logic
- Extract helpers.ts for utility functions
- Extract prompt-builder.ts for prompt construction
- Reduce tools.ts complexity with cleaner separation of concerns

* refactor(builtin-skills): split skills.ts into individual skill files

- Move each skill to dedicated file in skills/ subdirectory
- Create barrel export for backward compatibility
- Improve maintainability with focused skill modules

* chore: update import paths and lockfile

- Update prometheus import path after refactor
- Update bun.lock

* fix(tests): complete BDD comment normalization

- Fix remaining #when/#then patterns missed by initial sed
- Affected: state.test.ts, events.test.ts

---------

Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com>
This commit is contained in:
YeonGyu-Kim
2026-02-01 16:47:50 +09:00
committed by GitHub
parent c83150d9ea
commit f146aeff0f
145 changed files with 10307 additions and 9562 deletions
+65 -65
View File
@@ -11,7 +11,7 @@ describe("getPluginNameWithVersion", () => {
})
test("returns @latest when current version matches latest tag", async () => {
// #given npm dist-tags with latest=2.14.0
// given npm dist-tags with latest=2.14.0
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -19,15 +19,15 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version is 2.14.0
// when current version is 2.14.0
const result = await getPluginNameWithVersion("2.14.0")
// #then should use @latest tag
// then should use @latest tag
expect(result).toBe("oh-my-opencode@latest")
})
test("returns @beta when current version matches beta tag", async () => {
// #given npm dist-tags with beta=3.0.0-beta.3
// given npm dist-tags with beta=3.0.0-beta.3
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -35,15 +35,15 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version is 3.0.0-beta.3
// when current version is 3.0.0-beta.3
const result = await getPluginNameWithVersion("3.0.0-beta.3")
// #then should use @beta tag
// then should use @beta tag
expect(result).toBe("oh-my-opencode@beta")
})
test("returns @next when current version matches next tag", async () => {
// #given npm dist-tags with next=3.1.0-next.1
// given npm dist-tags with next=3.1.0-next.1
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -51,15 +51,15 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version is 3.1.0-next.1
// when current version is 3.1.0-next.1
const result = await getPluginNameWithVersion("3.1.0-next.1")
// #then should use @next tag
// then should use @next tag
expect(result).toBe("oh-my-opencode@next")
})
test("returns pinned version when no tag matches", async () => {
// #given npm dist-tags with beta=3.0.0-beta.3
// given npm dist-tags with beta=3.0.0-beta.3
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -67,26 +67,26 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version is old beta 3.0.0-beta.2
// when current version is old beta 3.0.0-beta.2
const result = await getPluginNameWithVersion("3.0.0-beta.2")
// #then should pin to specific version
// then should pin to specific version
expect(result).toBe("oh-my-opencode@3.0.0-beta.2")
})
test("returns pinned version when fetch fails", async () => {
// #given network failure
// given network failure
globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch
// #when current version is 3.0.0-beta.3
// when current version is 3.0.0-beta.3
const result = await getPluginNameWithVersion("3.0.0-beta.3")
// #then should fall back to pinned version
// then should fall back to pinned version
expect(result).toBe("oh-my-opencode@3.0.0-beta.3")
})
test("returns pinned version when npm returns non-ok response", async () => {
// #given npm returns 404
// given npm returns 404
globalThis.fetch = mock(() =>
Promise.resolve({
ok: false,
@@ -94,15 +94,15 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version is 2.14.0
// when current version is 2.14.0
const result = await getPluginNameWithVersion("2.14.0")
// #then should fall back to pinned version
// then should fall back to pinned version
expect(result).toBe("oh-my-opencode@2.14.0")
})
test("prioritizes latest over other tags when version matches multiple", async () => {
// #given version matches both latest and beta (during release promotion)
// given version matches both latest and beta (during release promotion)
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -110,10 +110,10 @@ describe("getPluginNameWithVersion", () => {
} as Response)
) as unknown as typeof fetch
// #when current version matches both
// when current version matches both
const result = await getPluginNameWithVersion("3.0.0")
// #then should prioritize @latest
// then should prioritize @latest
expect(result).toBe("oh-my-opencode@latest")
})
})
@@ -126,7 +126,7 @@ describe("fetchNpmDistTags", () => {
})
test("returns dist-tags on success", async () => {
// #given npm returns dist-tags
// given npm returns dist-tags
globalThis.fetch = mock(() =>
Promise.resolve({
ok: true,
@@ -134,26 +134,26 @@ describe("fetchNpmDistTags", () => {
} as Response)
) as unknown as typeof fetch
// #when fetching dist-tags
// when fetching dist-tags
const result = await fetchNpmDistTags("oh-my-opencode")
// #then should return the tags
// then should return the tags
expect(result).toEqual({ latest: "2.14.0", beta: "3.0.0-beta.3" })
})
test("returns null on network failure", async () => {
// #given network failure
// given network failure
globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch
// #when fetching dist-tags
// when fetching dist-tags
const result = await fetchNpmDistTags("oh-my-opencode")
// #then should return null
// then should return null
expect(result).toBeNull()
})
test("returns null on non-ok response", async () => {
// #given npm returns 404
// given npm returns 404
globalThis.fetch = mock(() =>
Promise.resolve({
ok: false,
@@ -161,10 +161,10 @@ describe("fetchNpmDistTags", () => {
} as Response)
) as unknown as typeof fetch
// #when fetching dist-tags
// when fetching dist-tags
const result = await fetchNpmDistTags("oh-my-opencode")
// #then should return null
// then should return null
expect(result).toBeNull()
})
})
@@ -202,19 +202,19 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
})
test("Gemini models have variant definitions", () => {
// #given the antigravity provider config
// given the antigravity provider config
const models = (ANTIGRAVITY_PROVIDER_CONFIG as any).google.models as Record<string, any>
// #when checking Gemini Pro variants
// when checking Gemini Pro variants
const pro = models["antigravity-gemini-3-pro"]
// #then should have low and high variants
// then should have low and high variants
expect(pro.variants).toBeTruthy()
expect(pro.variants.low).toBeTruthy()
expect(pro.variants.high).toBeTruthy()
// #when checking Gemini Flash variants
// when checking Gemini Flash variants
const flash = models["antigravity-gemini-3-flash"]
// #then should have minimal, low, medium, high variants
// then should have minimal, low, medium, high variants
expect(flash.variants).toBeTruthy()
expect(flash.variants.minimal).toBeTruthy()
expect(flash.variants.low).toBeTruthy()
@@ -223,14 +223,14 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
})
test("Claude thinking models have variant definitions", () => {
// #given the antigravity provider config
// given the antigravity provider config
const models = (ANTIGRAVITY_PROVIDER_CONFIG as any).google.models as Record<string, any>
// #when checking Claude thinking variants
// when checking Claude thinking variants
const sonnetThinking = models["antigravity-claude-sonnet-4-5-thinking"]
const opusThinking = models["antigravity-claude-opus-4-5-thinking"]
// #then both should have low and max variants
// then both should have low and max variants
for (const model of [sonnetThinking, opusThinking]) {
expect(model.variants).toBeTruthy()
expect(model.variants.low).toBeTruthy()
@@ -241,7 +241,7 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
describe("generateOmoConfig - model fallback system", () => {
test("generates native sonnet models when Claude standard subscription", () => {
// #given user has Claude standard subscription (not max20)
// given user has Claude standard subscription (not max20)
const config: InstallConfig = {
hasClaude: true,
isMax20: false,
@@ -253,17 +253,17 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then should use native anthropic sonnet (cost-efficient for standard plan)
// then should use native anthropic sonnet (cost-efficient for standard plan)
expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json")
expect(result.agents).toBeDefined()
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("anthropic/claude-sonnet-4-5")
})
test("generates native opus models when Claude max20 subscription", () => {
// #given user has Claude max20 subscription
// given user has Claude max20 subscription
const config: InstallConfig = {
hasClaude: true,
isMax20: true,
@@ -275,15 +275,15 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then should use native anthropic opus (max power for max20 plan)
// then should use native anthropic opus (max power for max20 plan)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("anthropic/claude-opus-4-5")
})
test("uses github-copilot sonnet fallback when only copilot available", () => {
// #given user has only copilot (no max plan)
// given user has only copilot (no max plan)
const config: InstallConfig = {
hasClaude: false,
isMax20: false,
@@ -295,15 +295,15 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then should use github-copilot sonnet models (copilot fallback)
// then should use github-copilot sonnet models (copilot fallback)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("github-copilot/claude-sonnet-4.5")
})
test("uses ultimate fallback when no providers configured", () => {
// #given user has no providers
// given user has no providers
const config: InstallConfig = {
hasClaude: false,
isMax20: false,
@@ -315,16 +315,16 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then should use ultimate fallback for all agents
// then should use ultimate fallback for all agents
expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json")
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("opencode/glm-4.7-free")
})
test("uses zai-coding-plan/glm-4.7 for librarian when Z.ai available", () => {
// #given user has Z.ai and Claude max20
// given user has Z.ai and Claude max20
const config: InstallConfig = {
hasClaude: true,
isMax20: true,
@@ -336,17 +336,17 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then librarian should use zai-coding-plan/glm-4.7
// then librarian should use zai-coding-plan/glm-4.7
expect((result.agents as Record<string, { model: string }>).librarian.model).toBe("zai-coding-plan/glm-4.7")
// #then other agents should use native opus (max20 plan)
// then other agents should use native opus (max20 plan)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("anthropic/claude-opus-4-5")
})
test("uses native OpenAI models when only ChatGPT available", () => {
// #given user has only ChatGPT subscription
// given user has only ChatGPT subscription
const config: InstallConfig = {
hasClaude: false,
isMax20: false,
@@ -358,19 +358,19 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then Sisyphus should use native OpenAI (fallback within native tier)
// then Sisyphus should use native OpenAI (fallback within native tier)
expect((result.agents as Record<string, { model: string }>).sisyphus.model).toBe("openai/gpt-5.2")
// #then Oracle should use native OpenAI (first fallback entry)
// then Oracle should use native OpenAI (first fallback entry)
expect((result.agents as Record<string, { model: string }>).oracle.model).toBe("openai/gpt-5.2")
// #then multimodal-looker should use native OpenAI (fallback within native tier)
// then multimodal-looker should use native OpenAI (fallback within native tier)
expect((result.agents as Record<string, { model: string }>)["multimodal-looker"].model).toBe("openai/gpt-5.2")
})
test("uses haiku for explore when Claude max20", () => {
// #given user has Claude max20
// given user has Claude max20
const config: InstallConfig = {
hasClaude: true,
isMax20: true,
@@ -382,15 +382,15 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then explore should use haiku (max20 plan uses Claude quota)
// then explore should use haiku (max20 plan uses Claude quota)
expect((result.agents as Record<string, { model: string }>).explore.model).toBe("anthropic/claude-haiku-4-5")
})
test("uses haiku for explore regardless of max20 flag", () => {
// #given user has Claude but not max20
// given user has Claude but not max20
const config: InstallConfig = {
hasClaude: true,
isMax20: false,
@@ -402,10 +402,10 @@ describe("generateOmoConfig - model fallback system", () => {
hasKimiForCoding: false,
}
// #when generating config
// when generating config
const result = generateOmoConfig(config)
// #then explore should use haiku (isMax20 doesn't affect explore anymore)
// then explore should use haiku (isMax20 doesn't affect explore anymore)
expect((result.agents as Record<string, { model: string }>).explore.model).toBe("anthropic/claude-haiku-4-5")
})
})