Files
oh-my-opencode/src/features/builtin-commands/commands.test.ts
T
YeonGyu-Kim 724d21b3cc fix(start-work): restore atlas-first slash discovery
Static slash-command discovery runs before agent registration, so /start-work regressed to Sisyphus even though config-time wiring still needed Atlas-aware fallback. Split builtin command resolution so discovery stays Atlas-first while command config remains availability-aware.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-01 17:32:46 -07:00

251 lines
7.3 KiB
TypeScript

import { afterEach, beforeEach, describe, test, expect } from "bun:test"
import { loadBuiltinCommands } from "./commands"
import { HANDOFF_TEMPLATE } from "./templates/handoff"
import { REMOVE_AI_SLOPS_TEMPLATE } from "./templates/remove-ai-slops"
import type { BuiltinCommandName } from "./types"
import { _resetForTesting, registerAgentName } from "../claude-code-session-state"
beforeEach(() => {
_resetForTesting()
})
afterEach(() => {
_resetForTesting()
})
describe("loadBuiltinCommands", () => {
test("should include handoff command in loaded commands", () => {
//#given
const disabledCommands: BuiltinCommandName[] = []
//#when
const commands = loadBuiltinCommands(disabledCommands)
//#then
expect(commands.handoff).toBeDefined()
expect(commands.handoff.name).toBe("handoff")
})
test("should exclude handoff when disabled", () => {
//#given
const disabledCommands: BuiltinCommandName[] = ["handoff"]
//#when
const commands = loadBuiltinCommands(disabledCommands)
//#then
expect(commands.handoff).toBeUndefined()
})
test("should include handoff template content in command template", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands.handoff.template).toContain(HANDOFF_TEMPLATE)
})
test("should include session context variables in handoff template", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands.handoff.template).toContain("$SESSION_ID")
expect(commands.handoff.template).toContain("$TIMESTAMP")
expect(commands.handoff.template).toContain("$ARGUMENTS")
})
test("should have correct description for handoff", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands.handoff.description).toContain("context summary")
})
test("should default start-work to Atlas for static slash-command discovery", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands["start-work"].agent).toBe("atlas")
})
test("should preassign Sisyphus as the native agent for start-work when command config checks registered agents", () => {
//#given - no atlas registration
//#when
const commands = loadBuiltinCommands(undefined, { useRegisteredAgents: true })
//#then
expect(commands["start-work"].agent).toBe("sisyphus")
})
test("should preassign Atlas as the native agent for start-work when Atlas is registered", () => {
//#given
registerAgentName("atlas")
//#when
const commands = loadBuiltinCommands(undefined, { useRegisteredAgents: true })
//#then
expect(commands["start-work"].agent).toBe("atlas")
})
})
describe("loadBuiltinCommands — remove-ai-slops", () => {
test("should include remove-ai-slops command in loaded commands", () => {
//#given
const disabledCommands: BuiltinCommandName[] = []
//#when
const commands = loadBuiltinCommands(disabledCommands)
//#then
expect(commands["remove-ai-slops"]).toBeDefined()
expect(commands["remove-ai-slops"].name).toBe("remove-ai-slops")
})
test("should exclude remove-ai-slops when disabled", () => {
//#given
const disabledCommands: BuiltinCommandName[] = ["remove-ai-slops"]
//#when
const commands = loadBuiltinCommands(disabledCommands)
//#then
expect(commands["remove-ai-slops"]).toBeUndefined()
})
test("should include remove-ai-slops template content in command template", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands["remove-ai-slops"].template).toContain(REMOVE_AI_SLOPS_TEMPLATE)
})
test("should have correct description for remove-ai-slops", () => {
//#given - no disabled commands
//#when
const commands = loadBuiltinCommands()
//#then
expect(commands["remove-ai-slops"].description).toContain("AI-generated code smells")
})
})
describe("REMOVE_AI_SLOPS_TEMPLATE", () => {
test("should include phase structure", () => {
//#given - the template string
//#when / #then
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("Identify Changed Files")
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("Parallel AI Slop Removal")
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("Critical Review")
})
test("should reference ai-slop-remover skill", () => {
//#given - the template string
//#when / #then
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("ai-slop-remover")
})
test("should include safety verification checklist", () => {
//#given - the template string
//#when / #then
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("Safety Verification")
expect(REMOVE_AI_SLOPS_TEMPLATE).toContain("Behavior Preservation")
})
})
describe("HANDOFF_TEMPLATE", () => {
test("should include session reading instruction", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("session_read")
})
test("should include compaction-style sections in output format", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("USER REQUESTS (AS-IS)")
expect(HANDOFF_TEMPLATE).toContain("EXPLICIT CONSTRAINTS")
})
test("should include programmatic context gathering instructions", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("todoread")
expect(HANDOFF_TEMPLATE).toContain("git diff")
expect(HANDOFF_TEMPLATE).toContain("git status")
})
test("should include context extraction format", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("WORK COMPLETED")
expect(HANDOFF_TEMPLATE).toContain("CURRENT STATE")
expect(HANDOFF_TEMPLATE).toContain("PENDING TASKS")
expect(HANDOFF_TEMPLATE).toContain("KEY FILES")
expect(HANDOFF_TEMPLATE).toContain("IMPORTANT DECISIONS")
expect(HANDOFF_TEMPLATE).toContain("CONTEXT FOR CONTINUATION")
expect(HANDOFF_TEMPLATE).toContain("GOAL")
})
test("should enforce first person perspective", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("first person perspective")
})
test("should limit key files to 10", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("Maximum 10 files")
})
test("should instruct plain text format without markdown", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("Plain text with bullets")
expect(HANDOFF_TEMPLATE).toContain("No markdown headers")
})
test("should include user instructions for new session", () => {
//#given - the template string
//#when / #then
expect(HANDOFF_TEMPLATE).toContain("new session")
expect(HANDOFF_TEMPLATE).toContain("opencode")
})
test("should not contain emojis", () => {
//#given - the template string
//#when / #then
const emojiRegex = /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2702}-\u{27B0}\u{24C2}-\u{1F251}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u
expect(emojiRegex.test(HANDOFF_TEMPLATE)).toBe(false)
})
})