Files
ccs/docs/codebase-summary.md
T
kaitranntt 7b25d5d437 feat!: v3.0.0 simplification - login-per-profile model
BREAKING CHANGE: Remove vault/encryption, implement login-per-profile

- Remove vault-manager.js, credential-reader.js, credential-injector.js (~642 lines)
- Implement login-per-profile (no credential copying)
- Rename 'auth save' to 'auth create'
- Fix profile schema (remove vault/subscription/email fields)
- Remove macOS credential switcher (CLAUDE_CONFIG_DIR works everywhere)
- Auto-create missing instance directories
- Maintain GLM/Kimi backward compatibility (settings profiles)

Performance: 50-120ms faster (no decryption overhead)
Code reduction: ~600 lines deleted (40% simpler)

Migration required: Users must recreate profiles with 'ccs auth create'
2025-11-09 05:43:09 -05:00

551 lines
18 KiB
Markdown

# CCS Codebase Summary (v3.0)
## Overview
CCS (Claude Code Switch) v3.0 is a lightweight CLI wrapper enabling instant profile switching between Claude Sonnet 4.5, GLM 4.6, and Kimi for Coding models. Version 3.0 represents a major architectural simplification through vault removal and adoption of a login-per-profile model.
## Version Evolution
### v2.x Architecture
- **Total LOC**: ~1,700 (includes vault, encryption, credential management)
- **Key Components**: vault-manager.js, credential-reader.js, credential-switcher-macos.js
- **Flow**: Login → Encrypt → Store in vault → Decrypt on use → Sync to instance → Execute
- **Complexity**: 6 steps, encryption overhead 50-100ms
### v3.0 Architecture (Current)
- **Total LOC**: ~1,100 (600 lines deleted)
- **Deleted Files**: vault-manager.js (250 lines), credential-reader.js (136 lines), credential-switcher-macos.js (129 lines)
- **Flow**: Create instance → Login in instance → Execute
- **Complexity**: 3 steps, no encryption overhead
## Core Components (v3.0)
### 1. Main Entry Point (`bin/ccs.js` - 300 lines)
**Role**: Central orchestrator for all CCS operations
**Key Functions**:
- `execClaude(claudeCli, args, envVars)`: Unified spawn logic for all execution paths
- `handleVersionCommand()`: Display version and installation info
- `handleHelpCommand()`: Show usage information
- `detectProfile(args)`: Smart profile detection from arguments
- `main()`: Main entry point and routing logic
**v3.0 Changes**:
- Unified `execClaude()` supports optional `envVars` parameter for `CLAUDE_CONFIG_DIR`
- Dual-path execution: settings-based (`--settings`) vs account-based (`CLAUDE_CONFIG_DIR`)
- Auth command routing to AuthCommands class
- Help text updated (`create` not `save`)
**Architecture Flow**:
```javascript
// Settings profile (glm, kimi)
const expandedSettingsPath = getSettingsPath(profileInfo.name);
execClaude(claudeCli, ['--settings', expandedSettingsPath, ...remainingArgs]);
// Account profile (work, personal) - v3.0
const instancePath = instanceMgr.ensureInstance(profileInfo.name);
registry.touchProfile(profileInfo.name);
const envVars = { CLAUDE_CONFIG_DIR: instancePath };
execClaude(claudeCli, remainingArgs, envVars);
```
### 2. Instance Manager (`bin/instance-manager.js` - 219 lines)
**Role**: Manage isolated Claude CLI instances per profile
**Key Functions**:
- `ensureInstance(profileName)`: Lazy initialization, auto-create missing directories
- `initializeInstance(profileName, instancePath)`: Create instance directory structure
- `validateInstance(instancePath)`: Auto-fix missing subdirectories (migration support)
- `deleteInstance(profileName)`: Clean removal of instance directory
- `getInstancePath(profileName)`: Get instance directory path
**v3.0 Simplification**:
- **Removed**: `activateInstance()`, `syncCredentialsToInstance()` (no vault)
- **Added**: Auto-create missing directories in `validateInstance()` (robustness)
- **Changed**: No credential copying, Claude CLI manages credentials directly
**Directory Structure Created**:
```
~/.ccs/instances/<profile>/
├── session-env/ # Claude sessions
├── todos/ # Per-profile todos
├── logs/ # Execution logs
├── file-history/ # File edits
├── shell-snapshots/ # Shell state
├── debug/ # Debug info
├── .anthropic/ # SDK config
├── commands/ # Custom commands (copied from ~/.claude/)
└── skills/ # Custom skills (copied from ~/.claude/)
```
**Key Insight**: No `.credentials.json` synced - Claude CLI creates/manages it via standard login flow in isolated instance.
### 3. Profile Registry (`bin/profile-registry.js` - 227 lines)
**Role**: Manage account profile metadata in `~/.ccs/profiles.json`
**Key Functions**:
- `createProfile(name, metadata)`: Create new profile with minimal schema
- `getProfile(name)`: Retrieve profile metadata
- `updateProfile(name, updates)`: Update profile fields
- `deleteProfile(name)`: Remove profile
- `touchProfile(name)`: Update `last_used` timestamp
- `setDefaultProfile(name)`: Set default profile
**v3.0 Schema (Minimal)**:
```json
{
"version": "2.0.0",
"profiles": {
"work": {
"type": "account",
"created": "2025-11-09T10:00:00.000Z",
"last_used": "2025-11-09T15:30:00.000Z"
}
},
"default": "work"
}
```
**Removed Fields**:
- `vault`: No encrypted vault (credentials in instance)
- `subscription`: Not needed (no credential reading)
- `email`: Not needed (no credential reading)
**Atomic Writes**: Uses temp file + rename for data integrity
### 4. Profile Detector (`bin/profile-detector.js` - ~150 lines estimated)
**Role**: Determine profile type for routing decisions
**Key Functions**:
- `detectProfileType(profileName)`: Determine if settings-based or account-based
- Priority: Settings profiles first (backward compat), then account profiles
- Returns: `{type: 'settings'|'account', name: string}` or throws error
**Detection Logic**:
```javascript
// 1. Check settings-based profiles (config.json)
if (config.profiles[profileName]) {
return { type: 'settings', settingsPath: config.profiles[profileName] };
}
// 2. Check account-based profiles (profiles.json)
if (registry.hasProfile(profileName)) {
return { type: 'account', name: profileName };
}
// 3. Error with available profiles
throw new Error(`Profile not found: ${profileName}`);
```
### 5. Auth Commands (`bin/auth-commands.js` - 406 lines)
**Role**: Handle `ccs auth` subcommands for multi-account management
**Key Functions**:
- `handleCreate(args)`: Create profile and prompt for login (v3.0)
- `handleList(args)`: List all profiles with metadata
- `handleShow(args)`: Show profile details
- `handleRemove(args)`: Remove profile and instance
- `handleDefault(args)`: Set default profile
- `showHelp()`: Display auth command help
**v3.0 Changes**:
- **Renamed**: `save``create` (better reflects action)
- **New Flow**: Spawn Claude CLI in isolated instance, auto-prompts for login
- **Removed**: Vault encryption, credential reading logic
- **Deprecated Handlers**: `save` redirects to `create`, `current`/`cleanup` show removal notice
**Profile Creation Flow (v3.0)**:
```javascript
// 1. Create instance directory
const instancePath = instanceMgr.ensureInstance(profileName);
// 2. Create/update profile entry
registry.createProfile(profileName, { type: 'account' });
// 3. Spawn Claude CLI in isolated instance (auto-prompts login)
const child = spawn(claudeCli, [], {
stdio: 'inherit',
env: { ...process.env, CLAUDE_CONFIG_DIR: instancePath }
});
// 4. Claude CLI detects no credentials, prompts OAuth login
// 5. Credentials stored in instance/.anthropic/ by Claude CLI
```
### 6. Configuration Manager (`bin/config-manager.js` - 73 lines)
**Role**: Manage settings-based profile configuration (glm, kimi)
**Key Functions**:
- `getConfigPath()`: Resolve config file path (supports `CCS_CONFIG` override)
- `readConfig()`: Parse config.json
- `getSettingsPath(profile)`: Get settings file path for profile
- `expandPath(pathStr)`: Expand tilde and environment variables
**v3.0 Status**: Unchanged (backward compatible for settings profiles)
**Config Format**:
```json
{
"profiles": {
"glm": "~/.ccs/glm.settings.json",
"kimi": "~/.ccs/kimi.settings.json",
"default": "~/.claude/settings.json"
}
}
```
### 7. Claude Detector (`bin/claude-detector.js` - 72 lines)
**Role**: Locate Claude CLI executable
**Key Functions**:
- `detectClaudeCli()`: Find Claude CLI in PATH or custom location
- `showClaudeNotFoundError()`: Display helpful error when Claude CLI missing
**Detection Priority**:
1. `CCS_CLAUDE_PATH` environment variable
2. System PATH lookup (platform-specific: `which` on Unix, `where.exe` on Windows)
3. Return null if not found
**v3.0 Status**: Unchanged
### 8. Helpers Module (`bin/helpers.js` - 48 lines)
**Role**: Utility functions
**Key Functions**:
- `colored(text, color)`: TTY-aware color formatting
- `expandPath(pathStr)`: Path expansion with tilde and env vars
- `error(message)`: Simple error reporting
**v3.0 Status**: Unchanged (already simplified in v2.x)
## Deleted Components (v3.0)
### 1. Vault Manager (`bin/vault-manager.js` - 191 lines) ❌ DELETED
**Former Role**: Encrypt/decrypt credentials with AES-256-GCM
**Why Deleted**:
- Login-per-profile model makes vault unnecessary
- Claude CLI manages credentials directly in instance directory
- Eliminates PBKDF2 key derivation overhead (50-100ms)
- Simplifies mental model (no abstract "vault" concept)
### 2. Credential Reader (`bin/credential-reader.js` - 136 lines) ❌ DELETED
**Former Role**: Read credentials from `~/.claude/.credentials.json`
**Why Deleted**:
- No credential reading needed in v3.0
- Users login interactively via Claude CLI
- Profile schema no longer stores `subscription` or `email`
### 3. Credential Switcher macOS (`bin/credential-switcher-macos.js` - 129 lines) ❌ DELETED
**Former Role**: macOS-specific credential switching with file locking
**Why Deleted**:
- `CLAUDE_CONFIG_DIR` now works on macOS (platform parity achieved)
- No need for platform-specific credential replacement
- File locking unnecessary (isolated instances prevent conflicts)
## File Structure (v3.0)
```
bin/
├── ccs.js # Main entry (300 lines)
├── config-manager.js # Settings config (73 lines)
├── claude-detector.js # CLI detection (72 lines)
├── instance-manager.js # Instance lifecycle (219 lines) - v3.0 simplified
├── profile-detector.js # Profile routing (150 lines est.)
├── profile-registry.js # Metadata management (227 lines) - v3.0 schema
├── auth-commands.js # Auth CLI (406 lines) - v3.0 create flow
└── helpers.js # Utilities (48 lines)
DELETED (v3.0):
❌ bin/vault-manager.js (191 lines)
❌ bin/credential-reader.js (136 lines)
❌ bin/credential-switcher-macos.js (129 lines)
scripts/
├── postinstall.js # npm auto-config
├── sync-version.js # Version management
└── check-executables.js # Validation
config/
├── config.example.json # Settings template
├── base-glm.settings.json
└── base-kimi.settings.json
tests/
├── shared/unit/
│ ├── helpers.test.js
│ └── instance-manager.test.js
├── npm/
│ ├── cli.test.js
│ ├── cross-platform.test.js
│ └── integration/
│ └── concurrent-sessions.test.js
└── manual/
└── test-concurrent-sessions.md
```
## Data Flow (v3.0)
### Settings Profile Execution (glm, kimi)
```
User: ccs glm "task"
ccs.js: detectProfile() → "glm"
ProfileDetector: detectProfileType("glm") → {type: 'settings'}
ConfigManager: getSettingsPath("glm") → "~/.ccs/glm.settings.json"
ccs.js: execClaude(claude, ['--settings', path, 'task'])
Claude CLI: Reads settings, executes with GLM API
```
### Account Profile Execution (work, personal) - v3.0
```
User: ccs work "task"
ccs.js: detectProfile() → "work"
ProfileDetector: detectProfileType("work") → {type: 'account'}
InstanceManager: ensureInstance("work") → "~/.ccs/instances/work/"
├─ Create directories if missing (lazy init)
└─ Auto-fix missing subdirectories (validateInstance)
ProfileRegistry: touchProfile("work") → Update last_used
ccs.js: execClaude(claude, ['task'], {CLAUDE_CONFIG_DIR: instancePath})
Claude CLI: Reads credentials from instance/.anthropic/, executes
```
### Profile Creation Flow (v3.0)
```
User: ccs auth create work
AuthCommands: handleCreate(["work"])
InstanceManager: ensureInstance("work") → Create directory structure
ProfileRegistry: createProfile("work", {type: 'account'})
AuthCommands: spawn(claude, [], {CLAUDE_CONFIG_DIR: instancePath})
Claude CLI: Detects no credentials, prompts OAuth login
User: Completes login in browser
Claude CLI: Stores credentials in instance/.anthropic/
Profile ready for use
```
## Key Simplifications (v3.0)
### 1. Vault Removal
**Before (v2.x)**:
- Encrypt credentials with AES-256-GCM
- PBKDF2 key derivation (100k iterations, 50-100ms)
- Store in `~/.ccs/accounts/<profile>.json.enc`
- Decrypt on each activation
- Copy to instance/.credentials.json
**After (v3.0)**:
- Users login directly via Claude CLI
- Credentials stored by Claude CLI in instance/.anthropic/
- No encryption/decryption overhead
- No credential copying
**Benefits**: 50-100ms faster activation, simpler mental model, easier debugging
### 2. Login-Per-Profile Model
**Before (v2.x)**:
```bash
# Login once globally
claude /login
# Save credentials to encrypted vault
ccs auth save work
# Decrypt and copy on each use
ccs work "task"
```
**After (v3.0)**:
```bash
# Create profile (prompts login)
ccs auth create work # Opens Claude, auto-prompts OAuth
# Use directly (credentials already in instance)
ccs work "task"
```
**Benefits**: Intuitive flow, matches Claude CLI UX, no abstraction layers
### 3. Auto-Directory Creation
**Before (v2.x)**:
- `initializeInstance()` required before use
- Error if directories missing
- Manual intervention needed for migration
**After (v3.0)**:
- `validateInstance()` auto-creates missing directories
- Seamless migration from older versions
- Robust against partial instance corruption
### 4. Platform Parity
**Before (v2.x)**:
- macOS: credential-switcher-macos.js (file locking, credential replacement)
- Linux/Windows: CLAUDE_CONFIG_DIR env var
- Different code paths, different behaviors
**After (v3.0)**:
- All platforms: CLAUDE_CONFIG_DIR env var
- Unified code path in execClaude()
- Consistent behavior everywhere
## Breaking Changes (v2.x → v3.0)
### Command Changes
- `ccs auth save <profile>``ccs auth create <profile>`
- `ccs auth current` → Removed (use `ccs auth list`)
- `ccs auth cleanup` → Removed (no vault to cleanup)
### Profile Schema Changes
```json
// v2.x schema
{
"type": "account",
"vault": "~/.ccs/accounts/work.json.enc",
"subscription": "pro",
"email": "user@work.com",
"created": "...",
"last_used": "..."
}
// v3.0 schema (minimal)
{
"type": "account",
"created": "...",
"last_used": "..."
}
```
### Migration Path
Users must recreate profiles:
```bash
# 1. List old profiles
ccs auth list
# 2. Recreate with v3.0
ccs auth create work # Login when prompted
ccs auth create personal # Login when prompted
# 3. Old vault files can be deleted
rm -rf ~/.ccs/accounts/
```
## Testing Coverage
### Unit Tests
- `tests/shared/unit/helpers.test.js`: Utility functions
- `tests/shared/unit/instance-manager.test.js`: Instance lifecycle (v3.0 updated)
### Integration Tests
- `tests/npm/cli.test.js`: End-to-end CLI functionality
- `tests/npm/cross-platform.test.js`: Platform-specific behavior
- `tests/npm/integration/concurrent-sessions.test.js`: Multi-profile execution
### Manual Testing
- `tests/manual/test-concurrent-sessions.md`: Concurrent session validation
- `TEST-V3.md`: Comprehensive v3.0 test guide
## Performance Characteristics (v3.0)
### Profile Creation
- Instance directory creation: ~5-10ms
- Copy global configs (if exist): ~10-20ms
- Login prompt (interactive): user-dependent
- **Total overhead**: ~15-30ms (excluding login)
### Profile Activation
- Instance validation: ~5ms
- `CLAUDE_CONFIG_DIR` env var: ~1ms
- Claude CLI spawn: ~20-30ms (Node.js overhead)
- **Total overhead**: ~26-36ms
- **v2.x overhead**: ~76-136ms (included decryption)
- **Improvement**: ~50-100ms faster (60-75% reduction)
### Memory Footprint
- Instance Manager: ~2 KB
- Profile Registry: ~2 KB
- Profile Detector: ~1 KB
- **Total overhead**: ~5 KB (vs ~10 KB in v2.x due to vault crypto)
## Security Considerations (v3.0)
### Credential Storage
- **Location**: Instance directory (`~/.ccs/instances/<profile>/.anthropic/`)
- **Management**: Claude CLI standard mechanisms
- **Permissions**: Inherited from Claude CLI (typically 0600)
- **Encryption**: Handled by Claude CLI (if applicable)
### Removed Attack Vectors (v3.0)
- No custom encryption implementation (fewer crypto bugs)
- No key derivation code (no PBKDF2 vulnerabilities)
- No credential reading/parsing (no credential leak risks)
### Remaining Security Controls
- File existence validation (prevent path traversal)
- Spawn with array arguments (no shell injection)
- Instance directory permissions (0700, owner only)
- Atomic file writes (temp + rename, prevents corruption)
## Future Extensibility
### Extension Points (v3.0)
1. **New Profile Types**: Easy via ProfileDetector routing
2. **Instance Cleanup**: Add auto-rotation policies for sessions/logs
3. **PID Locking**: Prevent same-profile concurrent access
4. **Migration Tools**: Auto-migrate v2.x vaults to v3.0 instances
5. **Enhanced Validation**: Credential health checks
### Architectural Guarantees
- **Backward Compatibility**: Settings profiles (glm, kimi) unchanged
- **Performance**: Lazy init minimizes overhead
- **Maintainability**: Fewer files, clearer separation of concerns
- **Reliability**: Auto-fix missing directories reduces failure modes
## Summary
**CCS v3.0 Evolution**:
- **Code Reduction**: 600 lines deleted (40% from v2.x)
- **Performance**: 50-100ms faster activation
- **Simplicity**: 6 steps → 3 steps (50% reduction)
- **Platform Parity**: Unified behavior across all platforms
**Key Achievements**:
- ✅ Vault removal eliminates encryption complexity
- ✅ Login-per-profile matches Claude CLI UX
- ✅ Auto-directory creation improves robustness
- ✅ Platform parity simplifies maintenance
- ✅ Minimal schema reduces metadata overhead
**Design Principles Maintained**:
- **YAGNI**: Lazy instance init, only create when needed
- **KISS**: Simple dual-path routing, no abstraction layers
- **DRY**: Unified spawn logic, single source of truth per concern
v3.0 demonstrates how architectural simplification can remove substantial code while improving performance, maintainability, and user experience. The login-per-profile model provides a sustainable foundation for future enhancements.