@kaitranntt/ccs 3.5.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,189 @@
1
+ # CCS Delegation Module
2
+
3
+ Enhanced Claude Code delegation system for multi-model task delegation.
4
+
5
+ ## Files
6
+
7
+ ### Core Components
8
+ - **headless-executor.js** (405 lines) - Main executor, spawns `claude -p` with enhanced features
9
+ - **session-manager.js** (156 lines) - Session persistence and cost tracking
10
+ - **settings-parser.js** (88 lines) - Parse tool restrictions from settings
11
+ - **result-formatter.js** (326 lines) - Terminal output formatting
12
+
13
+ **Total**: 975 lines (down from 1,755 lines - 44% reduction)
14
+
15
+ ## Features
16
+
17
+ ### Enhanced Headless Execution
18
+ - Stream-JSON output parsing (`--output-format stream-json`)
19
+ - Real-time tool use visibility in TTY
20
+ - Permission mode acceptEdits (`--permission-mode acceptEdits`)
21
+ - Tool restrictions from `.claude/settings.local.json`
22
+ - Multi-turn session management (`--resume <session-id>`)
23
+ - Time-based limits (10 min default timeout with graceful termination)
24
+ - Cost tracking and aggregation
25
+
26
+ ### Session Management
27
+ - Persistence: `~/.ccs/delegation-sessions.json`
28
+ - Resume via `/ccs:glm:continue` and `/ccs:kimi:continue`
29
+ - Auto-cleanup expired sessions (>30 days)
30
+ - Cost aggregation across turns
31
+
32
+ ### Settings
33
+ - Profile location: `~/.ccs/{profile}.settings.json`
34
+ - Examples: `glm.settings.json`, `kimi.settings.json`, `glmt.settings.json`
35
+ - Tool restrictions from `.claude/settings.local.json`
36
+
37
+ ## Usage
38
+
39
+ ### Basic Delegation
40
+ ```javascript
41
+ const { HeadlessExecutor } = require('./headless-executor');
42
+
43
+ const result = await HeadlessExecutor.execute('glm', 'Refactor auth.js', {
44
+ cwd: '/path/to/project',
45
+ outputFormat: 'stream-json',
46
+ permissionMode: 'acceptEdits',
47
+ timeout: 600000 // 10 minutes
48
+ });
49
+
50
+ console.log(result.sessionId); // For multi-turn
51
+ console.log(result.totalCost); // Cost in USD
52
+ console.log(result.content); // Result text
53
+ ```
54
+
55
+ ### Multi-Turn Sessions
56
+ ```javascript
57
+ // Start session
58
+ const result1 = await HeadlessExecutor.execute('glm', 'Implement feature');
59
+ const sessionId = result1.sessionId;
60
+
61
+ // Continue session
62
+ const result2 = await HeadlessExecutor.execute('glm', 'Add tests', {
63
+ resumeSession: true
64
+ });
65
+
66
+ // Or with specific session ID
67
+ const result3 = await HeadlessExecutor.execute('glm', 'Run tests', {
68
+ sessionId: sessionId
69
+ });
70
+ ```
71
+
72
+ ### Tool Restrictions
73
+ Create `.claude/settings.local.json`:
74
+ ```json
75
+ {
76
+ "permissions": {
77
+ "allow": ["Bash(git:*)", "Read", "Edit"],
78
+ "deny": ["Bash(rm:*)", "Bash(sudo:*)"]
79
+ }
80
+ }
81
+ ```
82
+
83
+ Automatically applied as CLI flags:
84
+ ```bash
85
+ --allowedTools "Bash(git:*)" "Read" "Edit" \
86
+ --disallowedTools "Bash(rm:*)" "Bash(sudo:*)"
87
+ ```
88
+
89
+ ## Slash Commands
90
+
91
+ The delegation system is invoked via simple slash commands in `.claude/commands/ccs/`:
92
+
93
+ ### Basic Commands
94
+ - `/ccs:glm "task"` - Delegate to GLM-4.6
95
+ - `/ccs:kimi "task"` - Delegate to Kimi (long-context)
96
+
97
+ ### Multi-Turn Commands
98
+ - `/ccs:glm:continue "follow-up"` - Resume last GLM session
99
+ - `/ccs:kimi:continue "follow-up"` - Resume last Kimi session
100
+
101
+ Each command directly invokes:
102
+ ```bash
103
+ claude -p "$ARGUMENTS" \
104
+ --settings ~/.ccs/{profile}.settings.json \
105
+ --output-format stream-json \
106
+ --permission-mode acceptEdits
107
+ ```
108
+
109
+ ## Debug Mode
110
+
111
+ ```bash
112
+ export CCS_DEBUG=1
113
+ ```
114
+
115
+ Enables verbose logging:
116
+ - Permission mode selection
117
+ - Session resumption details
118
+ - Tool restrictions parsing
119
+ - CLI args construction
120
+ - Session persistence events
121
+
122
+ ## Testing
123
+
124
+ ```bash
125
+ # Run all delegation tests
126
+ node tests/unit/delegation/json-output.test.js
127
+ node tests/unit/delegation/permission-mode.test.js
128
+ node tests/unit/delegation/session-manager.test.js
129
+ node tests/unit/delegation/settings-parser.test.js
130
+ node tests/unit/delegation/max-turns.test.js
131
+ node tests/unit/delegation/result-formatter.test.js
132
+ ```
133
+
134
+ **Test Coverage:**
135
+ - JSON output parsing (6 tests)
136
+ - Permission modes (11 tests)
137
+ - Session management (7 tests)
138
+ - Settings parser (6 tests)
139
+ - Auto max-turns (14 tests)
140
+ - Result formatting (14 tests)
141
+ - **Total: 58 tests**
142
+
143
+ ## Architecture
144
+
145
+ ```
146
+ User → SlashCommand (/ccs:glm)
147
+ → Directly invokes: claude -p
148
+ → HeadlessExecutor (monitors execution)
149
+ → SessionManager (load last session)
150
+ → SettingsParser (tool restrictions)
151
+ → Parse JSON response
152
+ → SessionManager (store/update)
153
+ → ResultFormatter.format()
154
+ → Display to user
155
+ ```
156
+
157
+ **Key Simplification**: Slash commands invoke `claude -p` directly. No intermediate delegation engine or rule system - just direct headless execution with enhanced features.
158
+
159
+ ## File Permissions
160
+
161
+ All files should be `644` (rw-r--r--):
162
+ ```bash
163
+ chmod 644 bin/delegation/*.js
164
+ ```
165
+
166
+ ## Dependencies
167
+
168
+ - Node.js 14+
169
+ - Claude CLI installed and in PATH
170
+ - Profile settings configured in `~/.ccs/{profile}.settings.json`
171
+
172
+ ## Migration from Legacy System
173
+
174
+ **Removed components** (as of 2025-11-15):
175
+ - ~~delegation-engine.js~~ - Rule-based decision engine (unused)
176
+ - ~~cwd-resolver.js~~ - Working directory resolution (unused)
177
+ - ~~rules-schema.js~~ - Schema validation (unused)
178
+ - ~~delegation-rules.json~~ - Configuration file (not created)
179
+
180
+ **Why removed**: Current slash commands directly invoke `claude -p` without intermediate orchestration. The delegation engine, CWD resolver, and rules schema were designed for a more complex system that was never fully integrated.
181
+
182
+ **Result**: 44% code reduction (1,755 → 975 lines) with same functionality.
183
+
184
+ ## References
185
+
186
+ - Official docs: https://code.claude.com/docs/en/headless.md
187
+ - Skill: `.claude/skills/ccs-delegation/`
188
+ - Commands: `.claude/commands/ccs/`
189
+ - Tests: `tests/unit/delegation/`
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { HeadlessExecutor } = require('./headless-executor');
5
+ const { SessionManager } = require('./session-manager');
6
+ const { ResultFormatter } = require('./result-formatter');
7
+ const { DelegationValidator } = require('../utils/delegation-validator');
8
+ const { SettingsParser } = require('./settings-parser');
9
+
10
+ /**
11
+ * Delegation command handler
12
+ * Routes -p flag commands to HeadlessExecutor with enhanced features
13
+ */
14
+ class DelegationHandler {
15
+ /**
16
+ * Route delegation command
17
+ * @param {Array<string>} args - Full args array from ccs.js
18
+ */
19
+ async route(args) {
20
+ try {
21
+ // 1. Parse args into { profile, prompt, options }
22
+ const parsed = this._parseArgs(args);
23
+
24
+ // 2. Detect special profiles (glm:continue, kimi:continue)
25
+ if (parsed.profile.includes(':continue')) {
26
+ return await this._handleContinue(parsed);
27
+ }
28
+
29
+ // 3. Validate profile
30
+ this._validateProfile(parsed.profile);
31
+
32
+ // 4. Execute via HeadlessExecutor
33
+ const result = await HeadlessExecutor.execute(
34
+ parsed.profile,
35
+ parsed.prompt,
36
+ parsed.options
37
+ );
38
+
39
+ // 5. Format and display results
40
+ const formatted = ResultFormatter.format(result);
41
+ console.log(formatted);
42
+
43
+ // 6. Exit with proper code
44
+ process.exit(result.exitCode || 0);
45
+ } catch (error) {
46
+ console.error(`[X] Delegation error: ${error.message}`);
47
+ if (process.env.CCS_DEBUG) {
48
+ console.error(error.stack);
49
+ }
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Handle continue command (resume last session)
56
+ * @param {Object} parsed - Parsed args
57
+ */
58
+ async _handleContinue(parsed) {
59
+ const baseProfile = parsed.profile.replace(':continue', '');
60
+
61
+ // Get last session from SessionManager
62
+ const sessionMgr = new SessionManager();
63
+ const lastSession = sessionMgr.getLastSession(baseProfile);
64
+
65
+ if (!lastSession) {
66
+ console.error(`[X] No previous session found for ${baseProfile}`);
67
+ console.error(` Start a new session first with: ccs ${baseProfile} -p "task"`);
68
+ process.exit(1);
69
+ }
70
+
71
+ // Execute with resume flag
72
+ const result = await HeadlessExecutor.execute(
73
+ baseProfile,
74
+ parsed.prompt,
75
+ {
76
+ ...parsed.options,
77
+ resumeSession: true,
78
+ sessionId: lastSession.sessionId
79
+ }
80
+ );
81
+
82
+ const formatted = ResultFormatter.format(result);
83
+ console.log(formatted);
84
+
85
+ process.exit(result.exitCode || 0);
86
+ }
87
+
88
+ /**
89
+ * Parse args into structured format
90
+ * @param {Array<string>} args - Raw args
91
+ * @returns {Object} { profile, prompt, options }
92
+ */
93
+ _parseArgs(args) {
94
+ // Extract profile (first non-flag arg or 'default')
95
+ const profile = this._extractProfile(args);
96
+
97
+ // Extract prompt from -p or --prompt
98
+ const prompt = this._extractPrompt(args);
99
+
100
+ // Extract options (--timeout, --permission-mode, etc.)
101
+ const options = this._extractOptions(args);
102
+
103
+ return { profile, prompt, options };
104
+ }
105
+
106
+ /**
107
+ * Extract profile from args (first non-flag arg)
108
+ * @param {Array<string>} args - Args array
109
+ * @returns {string} Profile name
110
+ */
111
+ _extractProfile(args) {
112
+ // Find first arg that doesn't start with '-' and isn't -p value
113
+ let skipNext = false;
114
+ for (let i = 0; i < args.length; i++) {
115
+ if (skipNext) {
116
+ skipNext = false;
117
+ continue;
118
+ }
119
+
120
+ if (args[i] === '-p' || args[i] === '--prompt') {
121
+ skipNext = true;
122
+ continue;
123
+ }
124
+
125
+ if (!args[i].startsWith('-')) {
126
+ return args[i];
127
+ }
128
+ }
129
+
130
+ // No profile specified, return null (will error in validation)
131
+ return null;
132
+ }
133
+
134
+ /**
135
+ * Extract prompt from -p flag
136
+ * @param {Array<string>} args - Args array
137
+ * @returns {string} Prompt text
138
+ */
139
+ _extractPrompt(args) {
140
+ const pIndex = args.indexOf('-p');
141
+ const promptIndex = args.indexOf('--prompt');
142
+
143
+ const index = pIndex !== -1 ? pIndex : promptIndex;
144
+
145
+ if (index === -1 || index === args.length - 1) {
146
+ console.error('[X] Missing prompt after -p flag');
147
+ console.error(' Usage: ccs glm -p "task description"');
148
+ process.exit(1);
149
+ }
150
+
151
+ return args[index + 1];
152
+ }
153
+
154
+ /**
155
+ * Extract options from remaining args
156
+ * @param {Array<string>} args - Args array
157
+ * @returns {Object} Options for HeadlessExecutor
158
+ */
159
+ _extractOptions(args) {
160
+ const cwd = process.cwd();
161
+
162
+ // Read default permission mode from .claude/settings.local.json
163
+ // Falls back to 'acceptEdits' if file doesn't exist
164
+ const defaultPermissionMode = SettingsParser.parseDefaultPermissionMode(cwd);
165
+
166
+ const options = {
167
+ cwd,
168
+ outputFormat: 'stream-json',
169
+ permissionMode: defaultPermissionMode
170
+ };
171
+
172
+ // Parse permission-mode (CLI flag overrides settings file)
173
+ const permModeIndex = args.indexOf('--permission-mode');
174
+ if (permModeIndex !== -1 && permModeIndex < args.length - 1) {
175
+ options.permissionMode = args[permModeIndex + 1];
176
+ }
177
+
178
+ // Parse timeout
179
+ const timeoutIndex = args.indexOf('--timeout');
180
+ if (timeoutIndex !== -1 && timeoutIndex < args.length - 1) {
181
+ options.timeout = parseInt(args[timeoutIndex + 1], 10);
182
+ }
183
+
184
+ return options;
185
+ }
186
+
187
+ /**
188
+ * Validate profile exists and is configured
189
+ * @param {string} profile - Profile name
190
+ */
191
+ _validateProfile(profile) {
192
+ if (!profile) {
193
+ console.error('[X] No profile specified');
194
+ console.error(' Usage: ccs <profile> -p "task"');
195
+ console.error(' Examples: ccs glm -p "task", ccs kimi -p "task"');
196
+ process.exit(1);
197
+ }
198
+
199
+ // Use DelegationValidator to check profile
200
+ const validation = DelegationValidator.validate(profile);
201
+ if (!validation.valid) {
202
+ console.error(`[X] Profile '${profile}' is not configured for delegation`);
203
+ console.error(` ${validation.error}`);
204
+ console.error('');
205
+ console.error(' Run: ccs doctor');
206
+ console.error(' Or configure: ~/.ccs/${profile}.settings.json');
207
+ process.exit(1);
208
+ }
209
+ }
210
+ }
211
+
212
+ module.exports = DelegationHandler;