import { describe, it, expect, vi, beforeEach } from 'vitest'; const mockOpts: Record = {}; const mockAddTask = vi.fn(); const { rootCommand, commandActions, commandMocks } = vi.hoisted(() => { const commandActions = new Map void>(); const commandMocks = new Map>(); function createCommandMock(actionKey: string): { description: ReturnType; argument: ReturnType; option: ReturnType; opts: ReturnType; action: (action: (...args: unknown[]) => void) => unknown; command: ReturnType; } { const command: Record = { description: vi.fn().mockReturnThis(), argument: vi.fn().mockReturnThis(), option: vi.fn().mockReturnThis(), opts: vi.fn(() => mockOpts), }; commandMocks.set(actionKey, command); command.command = vi.fn((subName: string) => createCommandMock(`${actionKey}.${subName}`)); command.action = vi.fn((action: (...args: unknown[]) => void) => { commandActions.set(actionKey, action); return command; }); return command as { description: ReturnType; argument: ReturnType; option: ReturnType; opts: ReturnType; action: (action: (...args: unknown[]) => void) => unknown; command: ReturnType; }; } return { rootCommand: createCommandMock('root'), commandActions, commandMocks, }; }); vi.mock('../app/cli/program.js', () => ({ program: rootCommand, resolvedCwd: '/test/cwd', pipelineMode: false, })); vi.mock('../infra/config/index.js', () => ({ clearPersonaSessions: vi.fn(), resolveConfigValue: vi.fn(), })); vi.mock('../infra/config/paths.js', () => ({ getGlobalConfigDir: vi.fn(() => '/tmp/takt'), })); vi.mock('../shared/ui/index.js', () => ({ success: vi.fn(), info: vi.fn(), })); vi.mock('../features/tasks/index.js', () => ({ runAllTasks: vi.fn(), addTask: (...args: unknown[]) => mockAddTask(...args), watchTasks: vi.fn(), listTasks: vi.fn(), })); vi.mock('../features/config/index.js', () => ({ ejectBuiltin: vi.fn(), ejectFacet: vi.fn(), parseFacetType: vi.fn(), VALID_FACET_TYPES: ['personas', 'policies', 'knowledge', 'instructions', 'output-contracts'], resetCategoriesToDefault: vi.fn(), resetConfigToDefault: vi.fn(), deploySkill: vi.fn(), deploySkillCodex: vi.fn(), })); vi.mock('../features/prompt/index.js', () => ({ previewPrompts: vi.fn(), })); vi.mock('../features/catalog/index.js', () => ({ showCatalog: vi.fn(), })); vi.mock('../features/analytics/index.js', () => ({ computeReviewMetrics: vi.fn(), formatReviewMetrics: vi.fn(), parseSinceDuration: vi.fn(), purgeOldEvents: vi.fn(), })); vi.mock('../commands/repertoire/add.js', () => ({ repertoireAddCommand: vi.fn(), })); vi.mock('../commands/repertoire/remove.js', () => ({ repertoireRemoveCommand: vi.fn(), })); vi.mock('../commands/repertoire/list.js', () => ({ repertoireListCommand: vi.fn(), })); import '../app/cli/commands.js'; const configFeatures = await import('../features/config/index.js'); describe('CLI add command', () => { beforeEach(() => { mockAddTask.mockClear(); for (const key of Object.keys(mockOpts)) { delete mockOpts[key]; } }); describe('when --pr option is provided', () => { it('should pass program.opts().pr to addTask as prNumber', async () => { const prNumber = 374; mockOpts.pr = prNumber; const addAction = commandActions.get('root.add'); expect(addAction).toBeTypeOf('function'); await addAction?.(); expect(mockAddTask).toHaveBeenCalledWith('/test/cwd', undefined, { prNumber }); }); }); describe('when --pr option is omitted', () => { it('should keep existing addTask call signature', async () => { const addAction = commandActions.get('root.add'); expect(addAction).toBeTypeOf('function'); await addAction?.('Regular task'); expect(mockAddTask).toHaveBeenCalledWith('/test/cwd', 'Regular task', undefined); }); }); it('should not register switch command', () => { const calledCommandNames = rootCommand.command.mock.calls .map((call: unknown[]) => call[0] as string); expect(calledCommandNames).not.toContain('switch'); }); it('should register export-codex command', () => { const calledCommandNames = rootCommand.command.mock.calls .map((call: unknown[]) => call[0] as string); expect(calledCommandNames).toContain('export-codex'); }); it('should invoke deploySkillCodex for export-codex command', async () => { const exportCodexAction = commandActions.get('root.export-codex'); expect(exportCodexAction).toBeTypeOf('function'); await exportCodexAction?.(); const deploySkillCodex = (configFeatures as Record).deploySkillCodex; expect(deploySkillCodex).toHaveBeenCalledTimes(1); }); it('should describe prompt piece argument as defaulting to "default"', () => { const promptCommand = commandMocks.get('root.prompt'); expect(promptCommand).toBeTruthy(); expect(promptCommand?.argument).toHaveBeenCalledWith( '[piece]', 'Piece name or path (defaults to "default")', ); }); });