import { existsSync, mkdirSync } from 'node:fs'; import { join } from 'node:path'; import { DATA_PATH, getHomeDir, toShellUsername } from '@@/data-path'; import { generateContainerContext, generateClaudeSettings } from '@@/generate-container-context'; const TEMPLATE_DIR = join(import.meta.dir, 'templates'); const copyTemplate = async (src: string, dest: string) => { if (existsSync(dest)) return; const content = await Bun.file(src).text(); await Bun.write(dest, content); }; export async function provisionUserEnvironment(email: string, username: string): Promise { const shellUsername = toShellUsername(username, email); const homeDir = getHomeDir(email); const userRoot = join(DATA_PATH, email); console.log(`[provision] provisioning environment for ${email}`); // Ensure data directories exist mkdirSync(userRoot, { recursive: true }); mkdirSync(homeDir, { recursive: true }); // Seed shell config files await seedShellConfigs(homeDir); // Generate and write CLAUDE.md + settings.json const contextFile = generateContainerContext(email); const claudeDir = join(homeDir, '.claude'); mkdirSync(claudeDir, { recursive: true }); const contextContent = await Bun.file(contextFile).text(); await Bun.write(join(claudeDir, 'CLAUDE.md'), contextContent); const settingsFile = generateClaudeSettings(email, shellUsername); const settingsContent = await Bun.file(settingsFile).text(); await Bun.write(join(claudeDir, 'settings.json'), settingsContent); console.log(`[provision] provisioning complete for ${email}`); return true; } async function seedShellConfigs(homeDir: string): Promise { // .zshenv (must be first — prevents system compinit before oh-my-zsh) await copyTemplate(join(TEMPLATE_DIR, '.zshenv'), join(homeDir, '.zshenv')); // .zshrc await copyTemplate(join(TEMPLATE_DIR, '.zshrc'), join(homeDir, '.zshrc')); // .tmux.conf await copyTemplate(join(TEMPLATE_DIR, '.tmux.conf'), join(homeDir, '.tmux.conf')); // starship config const configDir = join(homeDir, '.config'); mkdirSync(configDir, { recursive: true }); await copyTemplate(join(TEMPLATE_DIR, 'starship-officer.toml'), join(configDir, 'starship-officer.toml')); // Oh My Zsh — copy from host install const ohMyZshDest = join(homeDir, '.oh-my-zsh'); if (!existsSync(ohMyZshDest)) { const hostOhMyZsh = join(process.env.HOME ?? '', '.oh-my-zsh'); if (existsSync(hostOhMyZsh)) { Bun.spawnSync({ cmd: ['cp', '-r', hostOhMyZsh, ohMyZshDest], stdout: 'ignore', stderr: 'ignore' }); } else { Bun.spawnSync({ cmd: ['git', 'clone', '--depth=1', 'https://github.com/ohmyzsh/ohmyzsh.git', ohMyZshDest], stdout: 'ignore', stderr: 'ignore', }); } } // LazyVim config const nvimDir = join(homeDir, '.config', 'nvim'); if (!existsSync(nvimDir)) { const hostNvim = join(process.env.HOME ?? '', '.config', 'nvim'); if (existsSync(hostNvim)) { Bun.spawnSync({ cmd: ['cp', '-r', hostNvim, nvimDir], stdout: 'ignore', stderr: 'ignore' }); } } // Ensure .local/bin exists mkdirSync(join(homeDir, '.local', 'bin'), { recursive: true }); }