import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js'; import { detectInstallStage } from '../../src/stages/detect-install.js'; import type { WizardState } from '../../src/types.js'; import type { ConfigService } from '../../src/config/config-service.js'; function createState(mosaicHome: string): WizardState { return { mosaicHome, sourceDir: mosaicHome, mode: 'quick', installAction: 'fresh', soul: {}, user: {}, tools: {}, runtimes: { detected: [], mcpConfigured: false }, selectedSkills: [], }; } const mockConfig: ConfigService = { readSoul: async () => ({ agentName: 'TestAgent' }), readUser: async () => ({ userName: 'TestUser' }), readTools: async () => ({}), writeSoul: async () => {}, writeUser: async () => {}, writeTools: async () => {}, syncFramework: async () => {}, }; describe('detectInstallStage', () => { let tmpDir: string; beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), 'mosaic-test-')); }); afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); }); it('sets fresh for empty directory', async () => { const p = new HeadlessPrompter({}); const state = createState(join(tmpDir, 'nonexistent')); await detectInstallStage(p, state, mockConfig); expect(state.installAction).toBe('fresh'); }); it('detects existing install and offers choices', async () => { // Create a mock existing install mkdirSync(join(tmpDir, 'bin'), { recursive: true }); writeFileSync(join(tmpDir, 'AGENTS.md'), '# Test'); writeFileSync( join(tmpDir, 'SOUL.md'), 'You are **Jarvis** in this session.', ); const p = new HeadlessPrompter({ 'What would you like to do?': 'keep', }); const state = createState(tmpDir); await detectInstallStage(p, state, mockConfig); expect(state.installAction).toBe('keep'); expect(state.soul.agentName).toBe('TestAgent'); }); });