diff --git a/src/commands/__tests__/disable.test.ts b/src/commands/__tests__/disable.test.ts index e45d8eb..6905052 100644 --- a/src/commands/__tests__/disable.test.ts +++ b/src/commands/__tests__/disable.test.ts @@ -12,7 +12,7 @@ vi.mock('../../services/git.service.js', () => ({ import { gitService } from '../../services/git.service.js'; import { disableAction } from '../memory/disable.js'; -import { MERGE_HOOK_MARKER, CODEX_NOTIFY_LINE } from '../memory/hooks.js'; +import { MERGE_HOOK_MARKER, CODEX_NOTIFY_LINE, CODEX_NOTIFY_LINE_LEGACY } from '../memory/hooks.js'; let tmpDir: string; let originalHome: string; @@ -70,6 +70,18 @@ describe('disableAction', () => { expect(content).toContain('model = "gpt-4"'); }); + it('removes legacy Codex notify line', async () => { + const codexPath = path.join(tmpDir, '.codex', 'config.toml'); + await fs.mkdir(path.dirname(codexPath), { recursive: true }); + await fs.writeFile(codexPath, `model = "gpt-4"\n${CODEX_NOTIFY_LINE_LEGACY}\n`); + + await disableAction(); + + const content = await fs.readFile(codexPath, 'utf-8'); + expect(content).not.toContain(CODEX_NOTIFY_LINE_LEGACY); + expect(content).toContain('model = "gpt-4"'); + }); + it('removes kodus section from post-merge hook', async () => { const hookPath = path.join(tmpDir, '.git', 'hooks', 'post-merge'); await fs.writeFile(hookPath, `#!/bin/sh\n${MERGE_HOOK_MARKER}\nif [ -n "$MERGED_BRANCH" ]; then\n kodus decisions promote --branch "$MERGED_BRANCH" &\nfi\n`, { mode: 0o755 }); diff --git a/src/commands/__tests__/enable.test.ts b/src/commands/__tests__/enable.test.ts index 99d5cfd..035bc67 100644 --- a/src/commands/__tests__/enable.test.ts +++ b/src/commands/__tests__/enable.test.ts @@ -16,7 +16,7 @@ vi.mock('../../utils/module-matcher.js', () => ({ import { gitService } from '../../services/git.service.js'; import { enableAction } from '../memory/enable.js'; -import { MERGE_HOOK_MARKER, CODEX_NOTIFY_LINE } from '../memory/hooks.js'; +import { MERGE_HOOK_MARKER, CODEX_NOTIFY_LINE, CODEX_NOTIFY_LINE_LEGACY } from '../memory/hooks.js'; let tmpDir: string; @@ -72,6 +72,18 @@ describe('enableAction', () => { expect(calls).toContain('already configured'); }); + it('migrates legacy codex notify line to stop event', async () => { + const codexConfig = path.join(tmpDir, '.codex', 'config.toml'); + await fs.mkdir(path.dirname(codexConfig), { recursive: true }); + await fs.writeFile(codexConfig, `${CODEX_NOTIFY_LINE_LEGACY}\n`, 'utf-8'); + + await enableAction({ codexConfig }); + + const content = await fs.readFile(codexConfig, 'utf-8'); + expect(content).toContain(CODEX_NOTIFY_LINE); + expect(content).not.toContain(CODEX_NOTIFY_LINE_LEGACY); + }); + it('--agents claude skips codex', async () => { await enableAction({ agents: 'claude', diff --git a/src/commands/memory/hooks.ts b/src/commands/memory/hooks.ts index 7d85c18..401d542 100644 --- a/src/commands/memory/hooks.ts +++ b/src/commands/memory/hooks.ts @@ -18,6 +18,8 @@ export const CLAUDE_CAPTURE_COMMANDS = { }; export const CODEX_NOTIFY_LINE = + 'notify = ["kodus", "decisions", "capture", "--agent", "codex", "--event", "stop"]'; +export const CODEX_NOTIFY_LINE_LEGACY = 'notify = ["kodus", "decisions", "capture", "--agent", "codex", "--event", "agent-turn-complete"]'; export const MERGE_HOOK_MARKER = '# kodus-memory-post-merge'; @@ -221,6 +223,12 @@ export async function installCodexNotify(configPath: string): Promise<{ return { configPath, changed: false, skipped: false, reason: '' }; } + if (content.includes(CODEX_NOTIFY_LINE_LEGACY)) { + const nextContent = content.replace(CODEX_NOTIFY_LINE_LEGACY, CODEX_NOTIFY_LINE); + await fs.writeFile(configPath, nextContent, 'utf-8'); + return { configPath, changed: true, skipped: false, reason: '' }; + } + if (notifyLinePattern.test(content)) { return { configPath, @@ -365,13 +373,13 @@ export async function removeCodexNotify(configPath: string): Promise<{ configPat return { configPath, removed: false }; } - if (!content.includes(CODEX_NOTIFY_LINE)) { + if (!content.includes(CODEX_NOTIFY_LINE) && !content.includes(CODEX_NOTIFY_LINE_LEGACY)) { return { configPath, removed: false }; } const nextContent = content .split('\n') - .filter((line) => line.trim() !== CODEX_NOTIFY_LINE) + .filter((line) => line.trim() !== CODEX_NOTIFY_LINE && line.trim() !== CODEX_NOTIFY_LINE_LEGACY) .join('\n') .replace(/\n{3,}/g, '\n\n') .replace(/^\n+/, '') diff --git a/tests/integration/cli.integration.test.ts b/tests/integration/cli.integration.test.ts index b007c21..f756e5b 100644 --- a/tests/integration/cli.integration.test.ts +++ b/tests/integration/cli.integration.test.ts @@ -342,7 +342,7 @@ describe('decisions integration', () => { const codexConfigPath = path.join(tmpHome, '.codex', 'config.toml'); const codexConfig = await fs.readFile(codexConfigPath, 'utf-8'); - expect(codexConfig).toContain('notify = ["kodus", "decisions", "capture", "--agent", "codex", "--event", "agent-turn-complete"]'); + expect(codexConfig).toContain('notify = ["kodus", "decisions", "capture", "--agent", "codex", "--event", "stop"]'); const hookPath = path.join(gitRepoDir, '.git', 'hooks', 'post-merge'); const hookContent = await fs.readFile(hookPath, 'utf-8');