feat: TypeScript installation wizard with @clack/prompts TUI (#1)

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-02-21 18:25:51 +00:00
committed by jason.woltje
parent e3ec3e32e5
commit 6a84f7e210
56 changed files with 20647 additions and 31 deletions

View File

@@ -0,0 +1,60 @@
import { describe, it, expect } from 'vitest';
import { HeadlessPrompter } from '../../src/prompter/headless-prompter.js';
import { userSetupStage } from '../../src/stages/user-setup.js';
import type { WizardState } from '../../src/types.js';
function createState(overrides: Partial<WizardState> = {}): WizardState {
return {
mosaicHome: '/tmp/test-mosaic',
sourceDir: '/tmp/test-mosaic',
mode: 'quick',
installAction: 'fresh',
soul: { communicationStyle: 'direct' },
user: {},
tools: {},
runtimes: { detected: [], mcpConfigured: false },
selectedSkills: [],
...overrides,
};
}
describe('userSetupStage', () => {
it('collects basic info in quick mode', async () => {
const p = new HeadlessPrompter({
'Your name': 'Jason',
'Your pronouns': 'He/Him',
'Your timezone': 'America/Chicago',
});
const state = createState({ mode: 'quick' });
await userSetupStage(p, state);
expect(state.user.userName).toBe('Jason');
expect(state.user.pronouns).toBe('He/Him');
expect(state.user.timezone).toBe('America/Chicago');
expect(state.user.communicationPrefs).toContain('Direct and concise');
});
it('skips when install action is keep', async () => {
const p = new HeadlessPrompter({});
const state = createState({ installAction: 'keep' });
state.user.userName = 'Existing';
await userSetupStage(p, state);
expect(state.user.userName).toBe('Existing');
});
it('derives communication prefs from soul style', async () => {
const p = new HeadlessPrompter({
'Your name': 'Test',
});
const state = createState({
mode: 'quick',
soul: { communicationStyle: 'friendly' },
});
await userSetupStage(p, state);
expect(state.user.communicationPrefs).toContain('Warm and conversational');
});
});