diff --git a/.github/workflows/sisyphus-agent.yml b/.github/workflows/sisyphus-agent.yml index afa7d98b1..4bf6fb0bf 100644 --- a/.github/workflows/sisyphus-agent.yml +++ b/.github/workflows/sisyphus-agent.yml @@ -430,6 +430,10 @@ jobs: 2. **CREATE TODOS IMMEDIATELY**: Right after reading, create your todo list using todo tools. - First todo: "Summarize issue/PR context and requirements" - Break down ALL work into atomic, verifiable steps + - **GIT WORKFLOW (MANDATORY for implementation tasks)**: ALWAYS include these final todos: + - "Create new branch from origin/BRANCH_PLACEHOLDER (NEVER push directly to BRANCH_PLACEHOLDER)" + - "Commit changes" + - "Create PR to BRANCH_PLACEHOLDER branch" - Plan everything BEFORE starting any work --- diff --git a/bun.lock b/bun.lock index 8d6409908..af7276317 100644 --- a/bun.lock +++ b/bun.lock @@ -11,8 +11,8 @@ "@code-yeongyu/comment-checker": "^0.6.1", "@modelcontextprotocol/sdk": "^1.25.1", "@openauthjs/openauth": "^0.4.3", - "@opencode-ai/plugin": "^1.1.1", - "@opencode-ai/sdk": "^1.1.1", + "@opencode-ai/plugin": "^1.1.19", + "@opencode-ai/sdk": "^1.1.19", "commander": "^14.0.2", "hono": "^4.10.4", "js-yaml": "^4.1.1", @@ -85,9 +85,9 @@ "@openauthjs/openauth": ["@openauthjs/openauth@0.4.3", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw=="], - "@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.1", "", { "dependencies": { "@opencode-ai/sdk": "1.1.1", "zod": "4.1.8" } }, "sha512-OZGvpDal8YsSo6dnatHfwviSToGZ6mJJyEKZGxUyWDuGCP7VhcoPkoM16ktl7TCVHkDK+TdwY9tKzkzFqQNc5w=="], + "@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.19", "", { "dependencies": { "@opencode-ai/sdk": "1.1.19", "zod": "4.1.8" } }, "sha512-Q6qBEjHb/dJMEw4BUqQxEswTMxCCHUpFMMb6jR8HTTs8X/28XRkKt5pHNPA82GU65IlSoPRph+zd8LReBDN53Q=="], - "@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.1", "", {}, "sha512-PfXujMrHGeMnpS8Gd2BXSY+zZajlztcAvcokf06NtAhd0Mbo/hCLXgW0NBCQ+3FX3e/G2PNwz2DqMdtzyIZaCQ=="], + "@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.19", "", {}, "sha512-XhZhFuvlLCqDpvNtUEjOsi/wvFj3YCXb1dySp+OONQRMuHlorNYnNa7P2A2ntKuhRdGT1Xt5na0nFzlUyNw+4A=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], diff --git a/package.json b/package.json index b320dbdd1..cad0f8ced 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@code-yeongyu/comment-checker": "^0.6.1", "@modelcontextprotocol/sdk": "^1.25.1", "@openauthjs/openauth": "^0.4.3", - "@opencode-ai/plugin": "^1.1.1", - "@opencode-ai/sdk": "^1.1.1", + "@opencode-ai/plugin": "^1.1.19", + "@opencode-ai/sdk": "^1.1.19", "commander": "^14.0.2", "hono": "^4.10.4", "js-yaml": "^4.1.1", diff --git a/src/agents/prometheus-prompt.ts b/src/agents/prometheus-prompt.ts index 29202e884..71dea1c7f 100644 --- a/src/agents/prometheus-prompt.ts +++ b/src/agents/prometheus-prompt.ts @@ -479,6 +479,7 @@ sisyphus_task(agent="librarian", prompt="Find open source implementations of [fe - Maintain conversational tone - Use gathered evidence to inform suggestions - Ask questions that help user articulate needs +- **Use the \`Question\` tool when presenting multiple options** (structured UI for selection) - Confirm understanding before proceeding - **Update draft file after EVERY meaningful exchange** (see Rule 6) diff --git a/src/shared/migration.test.ts b/src/shared/migration.test.ts index ed0c3f8d2..506736499 100644 --- a/src/shared/migration.test.ts +++ b/src/shared/migration.test.ts @@ -457,13 +457,13 @@ describe("migrateConfigFile with backup", () => { }) }) - test("creates backup file with timestamp when migration needed", () => { - // #given: Config file path and config needing migration + test("creates backup file with timestamp when legacy migration needed", () => { + // #given: Config file path with legacy agent names needing migration const testConfigPath = "/tmp/test-config-migration.json" - const testConfigContent = globalThis.JSON.stringify({ agents: { oracle: { model: "openai/gpt-5.2" } } }, null, 2) + const testConfigContent = globalThis.JSON.stringify({ agents: { omo: { model: "test" } } }, null, 2) const rawConfig: Record = { agents: { - oracle: { model: "openai/gpt-5.2" }, + omo: { model: "test" }, }, } @@ -492,70 +492,54 @@ describe("migrateConfigFile with backup", () => { expect(backupContent).toBe(testConfigContent) }) - test("deletes agent config when all fields match category defaults", () => { - // #given: Config with agent matching category defaults - const testConfigPath = "/tmp/test-config-delete.json" + test("preserves model setting without auto-conversion to category", () => { + // #given: Config with model setting (should NOT be converted to category) + const testConfigPath = "/tmp/test-config-preserve-model.json" const rawConfig: Record = { agents: { - oracle: { - model: "openai/gpt-5.2", - temperature: 0.1, - }, + "multimodal-looker": { model: "anthropic/claude-haiku-4-5" }, + oracle: { model: "openai/gpt-5.2" }, + "my-custom-agent": { model: "google/gemini-3-pro-preview" }, }, } - fs.writeFileSync(testConfigPath, globalThis.JSON.stringify({ agents: { oracle: { model: "openai/gpt-5.2" } } }, null, 2)) + fs.writeFileSync(testConfigPath, globalThis.JSON.stringify(rawConfig, null, 2)) cleanupPaths.push(testConfigPath) // #when: Migrate config file const needsWrite = migrateConfigFile(testConfigPath, rawConfig) - // #then: Agent should be deleted (matches strategic category defaults) - expect(needsWrite).toBe(true) + // #then: No migration needed - model settings should be preserved as-is + expect(needsWrite).toBe(false) - const migratedConfig = JSON.parse(fs.readFileSync(testConfigPath, "utf-8")) - expect(migratedConfig.agents).toEqual({}) - - const dir = path.dirname(testConfigPath) - const basename = path.basename(testConfigPath) - const files = fs.readdirSync(dir) - const backupFiles = files.filter((f) => f.startsWith(`${basename}.bak.`)) - backupFiles.forEach((f) => cleanupPaths.push(path.join(dir, f))) + const agents = rawConfig.agents as Record> + expect(agents["multimodal-looker"].model).toBe("anthropic/claude-haiku-4-5") + expect(agents.oracle.model).toBe("openai/gpt-5.2") + expect(agents["my-custom-agent"].model).toBe("google/gemini-3-pro-preview") }) - test("keeps agent config with category when fields differ from defaults", () => { - // #given: Config with agent having custom temperature override - const testConfigPath = "/tmp/test-config-keep.json" + test("preserves category setting when explicitly set", () => { + // #given: Config with explicit category setting + const testConfigPath = "/tmp/test-config-preserve-category.json" const rawConfig: Record = { agents: { - oracle: { - model: "openai/gpt-5.2", - temperature: 0.5, - }, + "multimodal-looker": { category: "quick" }, + oracle: { category: "ultrabrain" }, }, } - fs.writeFileSync(testConfigPath, globalThis.JSON.stringify({ agents: { oracle: { model: "openai/gpt-5.2" } } }, null, 2)) + fs.writeFileSync(testConfigPath, globalThis.JSON.stringify(rawConfig, null, 2)) cleanupPaths.push(testConfigPath) // #when: Migrate config file const needsWrite = migrateConfigFile(testConfigPath, rawConfig) - // #then: Agent should be kept with category and custom override - expect(needsWrite).toBe(true) + // #then: No migration needed - category settings should be preserved as-is + expect(needsWrite).toBe(false) - const migratedConfig = JSON.parse(fs.readFileSync(testConfigPath, "utf-8")) - const agents = migratedConfig.agents as Record - expect(agents.oracle).toBeDefined() - expect((agents.oracle as Record).category).toBe("ultrabrain") - expect((agents.oracle as Record).temperature).toBe(0.5) - expect((agents.oracle as Record).model).toBeUndefined() - - const dir = path.dirname(testConfigPath) - const basename = path.basename(testConfigPath) - const files = fs.readdirSync(dir) - const backupFiles = files.filter((f) => f.startsWith(`${basename}.bak.`)) - backupFiles.forEach((f) => cleanupPaths.push(path.join(dir, f))) + const agents = rawConfig.agents as Record> + expect(agents["multimodal-looker"].category).toBe("quick") + expect(agents.oracle.category).toBe("ultrabrain") }) test("does not write when no migration needed", () => { @@ -583,56 +567,5 @@ describe("migrateConfigFile with backup", () => { expect(backupFiles.length).toBe(0) }) - test("handles multiple agent migrations correctly", () => { - // #given: Config with multiple agents needing migration - const testConfigPath = "/tmp/test-config-multi-agent.json" - const rawConfig: Record = { - agents: { - oracle: { model: "openai/gpt-5.2" }, - librarian: { model: "anthropic/claude-sonnet-4-5" }, - frontend: { - model: "google/gemini-3-pro-preview", - temperature: 0.9, - }, - }, - } - fs.writeFileSync( - testConfigPath, - globalThis.JSON.stringify( - { - agents: { - oracle: { model: "openai/gpt-5.2" }, - librarian: { model: "anthropic/claude-sonnet-4-5" }, - frontend: { model: "google/gemini-3-pro-preview" }, - }, - }, - null, - 2, - ), - ) - cleanupPaths.push(testConfigPath) - - // #when: Migrate config file - const needsWrite = migrateConfigFile(testConfigPath, rawConfig) - - // #then: Should migrate correctly - expect(needsWrite).toBe(true) - - const migratedConfig = JSON.parse(fs.readFileSync(testConfigPath, "utf-8")) - const agents = migratedConfig.agents as Record - - expect(agents.oracle).toBeUndefined() - expect(agents.librarian).toBeUndefined() - - expect(agents.frontend).toBeDefined() - expect((agents.frontend as Record).category).toBe("visual-engineering") - expect((agents.frontend as Record).temperature).toBe(0.9) - - const dir = path.dirname(testConfigPath) - const basename = path.basename(testConfigPath) - const files = fs.readdirSync(dir) - const backupFiles = files.filter((f) => f.startsWith(`${basename}.bak.`)) - backupFiles.forEach((f) => cleanupPaths.push(path.join(dir, f))) - }) }) diff --git a/src/shared/migration.ts b/src/shared/migration.ts index c0904e69b..7c3897d5a 100644 --- a/src/shared/migration.ts +++ b/src/shared/migration.ts @@ -22,6 +22,21 @@ export const AGENT_NAME_MAP: Record = { "multimodal-looker": "multimodal-looker", } +export const BUILTIN_AGENT_NAMES = new Set([ + "Sisyphus", + "oracle", + "librarian", + "explore", + "frontend-ui-ux-engineer", + "document-writer", + "multimodal-looker", + "Metis (Plan Consultant)", + "Momus (Plan Reviewer)", + "Prometheus (Planner)", + "orchestrator-sisyphus", + "build", +]) + // Migration map: old hook names → new hook names (for backward compatibility) export const HOOK_NAME_MAP: Record = { // Legacy names (backward compatibility) @@ -117,21 +132,7 @@ export function migrateConfigFile(configPath: string, rawConfig: Record> - for (const [name, config] of Object.entries(agents)) { - const { migrated, changed } = migrateAgentConfigToCategory(config) - if (changed) { - const category = migrated.category as string - if (shouldDeleteAgentConfig(migrated, category)) { - delete agents[name] - } else { - agents[name] = migrated - } - needsWrite = true - } - } - } + if (rawConfig.omo_agent) { rawConfig.sisyphus_agent = rawConfig.omo_agent