mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 16:19:12 +00:00
## 🚀 Release v4.1.0 - Major Update **Breaking changes from v3.5.0**. This release includes the complete v4.0.0 delegation overhaul plus v4.1.0 architecture improvements. --- ## 🎯 v4.0.0: Delegation System Overhaul **Complete rewrite of the delegation infrastructure with enhanced decision-making and streaming support.** ### New Delegation Features **Stream-JSON Communication Protocol:** - Real-time token streaming with `{type: "content", data: "..."}` format - Progress indicators during delegation execution - Clean separation: stdout for data, stderr for errors - Handles tool calls, thinking blocks, and text content **Enhanced Decision Framework:** - `/ccs:glm` and `/ccs:kimi` slash commands with auto-enhancement - `[AUTO ENHANCE]` prompts for better model understanding - Context-aware task delegation with clear boundaries - Continuation support: `/ccs:glm:continue` and `/ccs:kimi:continue` **Robust Error Handling:** - Graceful degradation when profiles unconfigured - Clear error messages with actionable fixes - Signal handling (SIGINT/SIGTERM) for clean child process termination - Session state recovery on interruption **Performance & Reliability:** - Headless mode (`-p` flag) for background execution - Slash command detection and auto-routing - Validation system with `DelegationValidator` - Profile readiness checks in `ccs --version` ### Delegation Components **New Files:** - `bin/delegation/delegation-handler.js` - Core delegation orchestrator - `bin/delegation/stream-processor.js` - Real-time output handling - `bin/utils/delegation-validator.js` - Profile validation - `.claude/commands/ccs/*.md` - Slash command definitions - `.claude/skills/ccs-delegation/` - AI decision framework - `.claude/agents/ccs-delegator.md` - Proactive delegation agent **Documentation:** - Complete delegation workflows with mermaid diagrams - Troubleshooting guides for common issues - Headless execution patterns --- ## ✨ v4.1.0: Selective Symlinking Architecture **Single source of truth for CCS items with automatic propagation.** ### New Architecture **Ship .claude/ Directory:** - CCS items now ship with npm/sh/ps1 packages - Selective item-level symlinks: `~/.ccs/.claude/` → `~/.claude/` - Auto-propagation on `npm update` - zero manual sync - Backward compatible with existing `~/.ccs/shared/` mechanism **Symlink Chain:** ``` ~/.ccs/.claude/ (source) ↓ selective symlinks ~/.claude/ (CCS items installed here) ↑ symlinked by ~/.ccs/shared/ ↑ symlinked by profiles (work, personal, team) ``` ### New Commands **Maintenance Tools:** - `ccs update` - Re-install CCS symlinks to ~/.claude/ - `ccs doctor` - Added Check 9: CCS symlinks health verification **Safe Installation:** - Automatic conflict backup before symlinking - Idempotent operations (safe to run multiple times) - Health monitoring and recovery ### New Components - `bin/utils/claude-symlink-manager.js` - Manages selective symlinks - Updated all 3 installers (npm postinstall, install.sh, install.ps1) - Enhanced `ccs doctor` with symlink health checks --- ## 💥 Breaking Changes **From v3.5.0 → v4.x:** 1. **Delegation commands moved**: - Old: User manually created in `~/.claude/commands/` - New: Auto-shipped in `~/.ccs/.claude/`, symlinked to `~/.claude/commands/ccs/` 2. **Slash command format**: - New: `/ccs:glm`, `/ccs:kimi`, `/ccs:glm:continue` - Old custom commands may need migration 3. **Profile validation**: - Placeholders (`YOUR_API_KEY_HERE`) now detected and marked invalid - Must configure real API keys for delegation to work 4. **Stream output format**: - Headless mode (`-p`) now uses stream-JSON protocol - Old text output replaced with structured `{type, data}` format
155 lines
4.7 KiB
JavaScript
155 lines
4.7 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const os = require('os');
|
|
|
|
/**
|
|
* Validates delegation profiles for CCS delegation system
|
|
* Ensures profiles exist and have valid API keys configured
|
|
*/
|
|
class DelegationValidator {
|
|
/**
|
|
* Validate a delegation profile
|
|
* @param {string} profileName - Name of profile to validate (e.g., 'glm', 'kimi')
|
|
* @returns {Object} Validation result { valid: boolean, error?: string, settingsPath?: string }
|
|
*/
|
|
static validate(profileName) {
|
|
const homeDir = os.homedir();
|
|
const settingsPath = path.join(homeDir, '.ccs', `${profileName}.settings.json`);
|
|
|
|
// Check if profile directory exists
|
|
if (!fs.existsSync(settingsPath)) {
|
|
return {
|
|
valid: false,
|
|
error: `Profile not found: ${profileName}`,
|
|
suggestion: `Profile settings missing at: ${settingsPath}\n\n` +
|
|
`To set up ${profileName} profile:\n` +
|
|
` 1. Copy base settings: cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json\n` +
|
|
` 2. Edit settings: Edit ~/.ccs/${profileName}.settings.json\n` +
|
|
` 3. Set your API key in ANTHROPIC_AUTH_TOKEN field`
|
|
};
|
|
}
|
|
|
|
// Read and parse settings.json
|
|
let settings;
|
|
try {
|
|
const settingsContent = fs.readFileSync(settingsPath, 'utf8');
|
|
settings = JSON.parse(settingsContent);
|
|
} catch (error) {
|
|
return {
|
|
valid: false,
|
|
error: `Failed to parse settings.json for ${profileName}`,
|
|
suggestion: `Settings file is corrupted or invalid JSON.\n\n` +
|
|
`Location: ${settingsPath}\n` +
|
|
`Parse error: ${error.message}\n\n` +
|
|
`Fix: Restore from base config:\n` +
|
|
` cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json`
|
|
};
|
|
}
|
|
|
|
// Validate API key exists and is not default
|
|
const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
|
|
if (!apiKey) {
|
|
return {
|
|
valid: false,
|
|
error: `API key not configured for ${profileName}`,
|
|
suggestion: `Missing ANTHROPIC_AUTH_TOKEN in settings.\n\n` +
|
|
`Edit: ${settingsPath}\n` +
|
|
`Set: env.ANTHROPIC_AUTH_TOKEN to your API key`
|
|
};
|
|
}
|
|
|
|
// Check for default placeholder values
|
|
const defaultPlaceholders = [
|
|
'YOUR_GLM_API_KEY_HERE',
|
|
'YOUR_KIMI_API_KEY_HERE',
|
|
'YOUR_API_KEY_HERE',
|
|
'your-api-key-here',
|
|
'PLACEHOLDER'
|
|
];
|
|
|
|
if (defaultPlaceholders.some(placeholder => apiKey.includes(placeholder))) {
|
|
return {
|
|
valid: false,
|
|
error: `Default API key placeholder detected for ${profileName}`,
|
|
suggestion: `API key is still set to default placeholder.\n\n` +
|
|
`To configure your profile:\n` +
|
|
` 1. Edit: ${settingsPath}\n` +
|
|
` 2. Replace ANTHROPIC_AUTH_TOKEN with your actual API key\n\n` +
|
|
`Get API key:\n` +
|
|
` GLM: https://z.ai/manage-apikey/apikey-list\n` +
|
|
` Kimi: https://platform.moonshot.cn/console/api-keys`
|
|
};
|
|
}
|
|
|
|
// Validation passed
|
|
return {
|
|
valid: true,
|
|
settingsPath,
|
|
apiKey: apiKey.substring(0, 8) + '...' // Show first 8 chars for verification
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format validation error for display
|
|
* @param {Object} result - Validation result from validate()
|
|
* @returns {string} Formatted error message
|
|
*/
|
|
static formatError(result) {
|
|
if (result.valid) {
|
|
return '';
|
|
}
|
|
|
|
let message = `\n[X] ${result.error}\n\n`;
|
|
|
|
if (result.suggestion) {
|
|
message += `${result.suggestion}\n`;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
/**
|
|
* Check if profile is delegation-ready (shorthand)
|
|
* @param {string} profileName - Profile to check
|
|
* @returns {boolean} True if ready for delegation
|
|
*/
|
|
static isReady(profileName) {
|
|
const result = this.validate(profileName);
|
|
return result.valid;
|
|
}
|
|
|
|
/**
|
|
* Get all delegation-ready profiles
|
|
* @returns {Array<string>} List of profile names ready for delegation
|
|
*/
|
|
static getReadyProfiles() {
|
|
const homeDir = os.homedir();
|
|
const ccsDir = path.join(homeDir, '.ccs');
|
|
|
|
if (!fs.existsSync(ccsDir)) {
|
|
return [];
|
|
}
|
|
|
|
const profiles = [];
|
|
const entries = fs.readdirSync(ccsDir, { withFileTypes: true });
|
|
|
|
// Look for *.settings.json files
|
|
for (const entry of entries) {
|
|
if (entry.isFile() && entry.name.endsWith('.settings.json')) {
|
|
const profileName = entry.name.replace('.settings.json', '');
|
|
if (this.isReady(profileName)) {
|
|
profiles.push(profileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return profiles;
|
|
}
|
|
}
|
|
|
|
module.exports = { DelegationValidator };
|