Files
ccs/src/auth/commands/remove-command.ts
T
Tam Nhu Tran 4f6e61739c refactor(config): adopt config-loader-facade across the codebase
Issue #1161. Sweeps 127 files to import from
src/config/config-loader-facade.ts instead of unified-config-loader or
utils/config-manager directly.

WRITE callers (32 files): replaced raw saveUnifiedConfig /
mutateUnifiedConfig / updateUnifiedConfig calls with the facade's
cache-coherent wrappers saveConfig / mutateConfig / updateConfig. This
fixes a latent stale-cache window where direct writes through the
underlying loader bypassed the facade's memoization.

READ callers (95 files): mechanical import-path migration only —
function names unchanged because the facade re-exports them. No
behavior change.

Also updated:
- tests/unit/utils/browser/browser-setup.test.ts (DI interface rename)
- src/management/checks/image-analysis-check.ts (dynamic import rename)
- src/web-server/health-service.ts (dynamic require rename)
- src/ccs.ts (path prefix fix from sweep script)

After sweep: zero raw write callers remain outside src/config/. Direct
imports of config-manager remain only for symbols not in the facade
(getConfigPath, getCcsDirSource, etc). Behavior unchanged; full suite
passes 1824/1824.

Out of scope: switching loadOrCreateUnifiedConfig() callers to
getCachedConfig() — needs per-callsite cache-safety analysis. Tracked
as follow-up.

Refs #1161
2026-05-03 01:42:53 -04:00

88 lines
2.8 KiB
TypeScript

/**
* Remove Command Handler
*
* Removes a saved profile and its instance directory.
*/
import * as fs from 'fs';
import * as path from 'path';
import { initUI, color, ok, fail, info } from '../../utils/ui';
import { InteractivePrompt } from '../../utils/prompt';
import { exitWithError } from '../../errors';
import { ExitCode } from '../../errors/exit-codes';
import { CommandContext, parseArgs } from './types';
import { isUnifiedMode } from '../../config/config-loader-facade';
/**
* Handle the remove command
*/
export async function handleRemove(ctx: CommandContext, args: string[]): Promise<void> {
await initUI();
const { profileName, yes } = parseArgs(args);
if (!profileName) {
console.log(fail('Profile name is required'));
console.log('');
console.log(`Usage: ${color('ccs auth remove <profile> [--yes]', 'command')}`);
exitWithError('Profile name is required', ExitCode.PROFILE_ERROR);
}
// Check existence in both legacy and unified
const existsLegacy = ctx.registry.hasProfile(profileName);
const existsUnified = ctx.registry.hasAccountUnified(profileName);
if (!existsLegacy && !existsUnified) {
console.log(fail(`Profile not found: ${profileName}`));
exitWithError(`Profile not found: ${profileName}`, ExitCode.PROFILE_ERROR);
}
try {
// Get instance path and session count for impact display
const instancePath = ctx.instanceMgr.getInstancePath(profileName);
let sessionCount = 0;
try {
const sessionsDir = path.join(instancePath, 'session-env');
if (fs.existsSync(sessionsDir)) {
const files = fs.readdirSync(sessionsDir);
sessionCount = files.filter((f) => f.endsWith('.json')).length;
}
} catch (_e) {
// Ignore errors counting sessions
}
// Display impact
console.log('');
console.log(`Profile '${color(profileName, 'command')}' will be permanently deleted.`);
console.log(` Instance path: ${instancePath}`);
console.log(` Sessions: ${sessionCount} conversation${sessionCount !== 1 ? 's' : ''}`);
console.log('');
// Interactive confirmation (or --yes flag)
const confirmed =
yes || (await InteractivePrompt.confirm('Delete this profile?', { default: false })); // Default to NO (safe)
if (!confirmed) {
console.log(info('Cancelled'));
process.exit(0);
}
// Delete instance
await ctx.instanceMgr.deleteInstance(profileName);
// Delete profile from appropriate config
if (isUnifiedMode() && existsUnified) {
ctx.registry.removeAccountUnified(profileName);
}
if (existsLegacy) {
ctx.registry.deleteProfile(profileName);
}
console.log(ok(`Profile removed: ${profileName}`));
console.log('');
} catch (error) {
exitWithError(`Failed to remove profile: ${(error as Error).message}`, ExitCode.GENERAL_ERROR);
}
}