/** * Create Command Handler * * Creates a new profile and prompts for login in an isolated Claude instance. */ import { spawn, ChildProcess } from 'child_process'; import { initUI, header, color, fail, warn, info, infoBox, warnBox } from '../../utils/ui'; import { getClaudeCliInfo } from '../../utils/claude-detector'; import { escapeShellArg, stripClaudeCodeEnv } from '../../utils/shell-executor'; import { isUnifiedMode } from '../../config/unified-config-loader'; import { exitWithError } from '../../errors'; import { ExitCode } from '../../errors/exit-codes'; import { CommandContext, parseArgs } from './types'; /** * Handle the create command */ export async function handleCreate(ctx: CommandContext, args: string[]): Promise { await initUI(); const { profileName, force } = parseArgs(args); if (!profileName) { console.log(fail('Profile name is required')); console.log(''); console.log(`Usage: ${color('ccs auth create [--force]', 'command')}`); console.log(''); console.log('Example:'); console.log(` ${color('ccs auth create work', 'command')}`); exitWithError('Profile name is required', ExitCode.PROFILE_ERROR); } // Check if profile already exists (check both legacy and unified) const existsLegacy = ctx.registry.hasProfile(profileName); const existsUnified = ctx.registry.hasAccountUnified(profileName); if (!force && (existsLegacy || existsUnified)) { console.log(fail(`Profile already exists: ${profileName}`)); console.log(` Use ${color('--force', 'command')} to overwrite`); exitWithError(`Profile already exists: ${profileName}`, ExitCode.PROFILE_ERROR); } try { // Create instance directory console.log(info(`Creating profile: ${profileName}`)); const instancePath = await ctx.instanceMgr.ensureInstance(profileName); // Create/update profile entry based on config mode if (isUnifiedMode()) { // Use unified config (config.yaml) if (existsUnified) { ctx.registry.touchAccountUnified(profileName); } else { ctx.registry.createAccountUnified(profileName); } } else { // Use legacy profiles.json if (existsLegacy) { ctx.registry.updateProfile(profileName, { type: 'account', }); } else { ctx.registry.createProfile(profileName, { type: 'account', }); } } console.log(info(`Instance directory: ${instancePath}`)); console.log(''); console.log(warn('Starting Claude in isolated instance...')); console.log(warn('You will be prompted to login with your account.')); console.log(''); // Detect Claude CLI const claudeInfo = getClaudeCliInfo(); if (!claudeInfo) { console.log(fail('Claude CLI not found')); console.log(''); console.log('Please install Claude CLI first:'); console.log(` ${color('https://claude.ai/download', 'path')}`); exitWithError('Claude CLI not found', ExitCode.BINARY_ERROR); } const { path: claudeCli, needsShell } = claudeInfo; const childEnv = stripClaudeCodeEnv({ ...process.env, CLAUDE_CONFIG_DIR: instancePath }); // Execute Claude in isolated instance (will auto-prompt for login if no credentials) // On Windows, .cmd/.bat/.ps1 files need shell: true to execute properly let child: ChildProcess; if (needsShell) { const cmdString = escapeShellArg(claudeCli); child = spawn(cmdString, { stdio: 'inherit', windowsHide: true, shell: true, env: childEnv, }); } else { child = spawn(claudeCli, [], { stdio: 'inherit', windowsHide: true, env: childEnv, }); } child.on('exit', (code: number | null) => { if (code === 0) { console.log(''); console.log( infoBox( `Profile: ${profileName}\n` + `Instance: ${instancePath}\n` + `Type: account`, 'Profile Created' ) ); console.log(''); console.log(header('Usage')); console.log(` ${color(`ccs ${profileName} "your prompt here"`, 'command')}`); console.log(''); console.log( warnBox( `Running the command below will SWITCH your default\n` + `CCS account to "${profileName}". After this, running\n` + `"ccs" without a profile name will use this account.\n\n` + ` ${color(`ccs auth default ${profileName}`, 'command')}\n\n` + `To restore the original default, run:\n` + ` ${color('ccs auth reset-default', 'command')}`, 'Set as Default?' ) ); console.log(''); process.exit(0); } else { console.log(''); console.log(fail('Login failed or cancelled')); console.log(''); console.log('To retry:'); console.log(` ${color(`ccs auth create ${profileName} --force`, 'command')}`); console.log(''); exitWithError('Login failed or cancelled', ExitCode.AUTH_ERROR); } }); child.on('error', (err: Error) => { exitWithError(`Failed to execute Claude CLI: ${err.message}`, ExitCode.BINARY_ERROR); }); } catch (error) { exitWithError(`Failed to create profile: ${(error as Error).message}`, ExitCode.GENERAL_ERROR); } }