@kaitranntt/ccs 3.4.6 → 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.
Files changed (47) hide show
  1. package/.claude/agents/ccs-delegator.md +117 -0
  2. package/.claude/commands/ccs/glm/continue.md +22 -0
  3. package/.claude/commands/ccs/glm.md +22 -0
  4. package/.claude/commands/ccs/kimi/continue.md +22 -0
  5. package/.claude/commands/ccs/kimi.md +22 -0
  6. package/.claude/skills/ccs-delegation/SKILL.md +54 -0
  7. package/.claude/skills/ccs-delegation/references/README.md +24 -0
  8. package/.claude/skills/ccs-delegation/references/delegation-guidelines.md +99 -0
  9. package/.claude/skills/ccs-delegation/references/headless-workflow.md +174 -0
  10. package/.claude/skills/ccs-delegation/references/troubleshooting.md +268 -0
  11. package/README.ja.md +470 -146
  12. package/README.md +532 -145
  13. package/README.vi.md +484 -157
  14. package/VERSION +1 -1
  15. package/bin/auth/auth-commands.js +98 -13
  16. package/bin/auth/profile-detector.js +11 -6
  17. package/bin/ccs.js +148 -2
  18. package/bin/delegation/README.md +189 -0
  19. package/bin/delegation/delegation-handler.js +212 -0
  20. package/bin/delegation/headless-executor.js +617 -0
  21. package/bin/delegation/result-formatter.js +483 -0
  22. package/bin/delegation/session-manager.js +156 -0
  23. package/bin/delegation/settings-parser.js +109 -0
  24. package/bin/management/doctor.js +94 -1
  25. package/bin/utils/claude-symlink-manager.js +238 -0
  26. package/bin/utils/delegation-validator.js +154 -0
  27. package/bin/utils/error-codes.js +59 -0
  28. package/bin/utils/error-manager.js +38 -32
  29. package/bin/utils/helpers.js +65 -1
  30. package/bin/utils/progress-indicator.js +111 -0
  31. package/bin/utils/prompt.js +134 -0
  32. package/bin/utils/shell-completion.js +234 -0
  33. package/lib/ccs +575 -25
  34. package/lib/ccs.ps1 +381 -20
  35. package/lib/error-codes.ps1 +55 -0
  36. package/lib/error-codes.sh +63 -0
  37. package/lib/progress-indicator.ps1 +120 -0
  38. package/lib/progress-indicator.sh +117 -0
  39. package/lib/prompt.ps1 +109 -0
  40. package/lib/prompt.sh +99 -0
  41. package/package.json +2 -1
  42. package/scripts/completion/README.md +308 -0
  43. package/scripts/completion/ccs.bash +81 -0
  44. package/scripts/completion/ccs.fish +92 -0
  45. package/scripts/completion/ccs.ps1 +157 -0
  46. package/scripts/completion/ccs.zsh +130 -0
  47. package/scripts/postinstall.js +35 -0
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.4.6
1
+ 4.1.0
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const { spawn } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
4
6
  const ProfileRegistry = require('./profile-registry');
5
7
  const InstanceManager = require('../management/instance-manager');
6
8
  const { colored } = require('../utils/helpers');
7
9
  const { detectClaudeCli } = require('../utils/claude-detector');
10
+ const { InteractivePrompt } = require('../utils/prompt');
11
+ const CCS_VERSION = require('../../package.json').version;
8
12
 
9
13
  /**
10
14
  * Auth Commands (Simplified)
@@ -45,7 +49,10 @@ class AuthCommands {
45
49
  console.log(` ${colored('ccs "review code"', 'yellow')} # Use default profile`);
46
50
  console.log('');
47
51
  console.log(colored('Options:', 'cyan'));
48
- console.log(` ${colored('--force', 'yellow')} Allow overwriting existing profile`);
52
+ console.log(` ${colored('--force', 'yellow')} Allow overwriting existing profile (create)`);
53
+ console.log(` ${colored('--yes, -y', 'yellow')} Skip confirmation prompts (remove)`);
54
+ console.log(` ${colored('--json', 'yellow')} Output in JSON format (list, show)`);
55
+ console.log(` ${colored('--verbose', 'yellow')} Show additional details (list)`);
49
56
  console.log('');
50
57
  console.log(colored('Note:', 'cyan'));
51
58
  console.log(` By default, ${colored('ccs', 'yellow')} uses Claude CLI defaults from ~/.claude/`);
@@ -159,12 +166,37 @@ class AuthCommands {
159
166
  */
160
167
  async handleList(args) {
161
168
  const verbose = args.includes('--verbose');
169
+ const json = args.includes('--json');
162
170
 
163
171
  try {
164
172
  const profiles = this.registry.getAllProfiles();
165
173
  const defaultProfile = this.registry.getDefaultProfile();
166
174
  const profileNames = Object.keys(profiles);
167
175
 
176
+ // JSON output mode
177
+ if (json) {
178
+ const output = {
179
+ version: CCS_VERSION,
180
+ profiles: profileNames.map(name => {
181
+ const profile = profiles[name];
182
+ const isDefault = name === defaultProfile;
183
+ const instancePath = this.instanceMgr.getInstancePath(name);
184
+
185
+ return {
186
+ name: name,
187
+ type: profile.type || 'account',
188
+ is_default: isDefault,
189
+ created: profile.created,
190
+ last_used: profile.last_used || null,
191
+ instance_path: instancePath
192
+ };
193
+ })
194
+ };
195
+ console.log(JSON.stringify(output, null, 2));
196
+ return;
197
+ }
198
+
199
+ // Human-readable output
168
200
  if (profileNames.length === 0) {
169
201
  console.log(colored('No account profiles found', 'yellow'));
170
202
  console.log('');
@@ -234,11 +266,12 @@ class AuthCommands {
234
266
  */
235
267
  async handleShow(args) {
236
268
  const profileName = args.find(arg => !arg.startsWith('--'));
269
+ const json = args.includes('--json');
237
270
 
238
271
  if (!profileName) {
239
272
  console.error('[X] Profile name is required');
240
273
  console.log('');
241
- console.log(`Usage: ${colored('ccs auth show <profile>', 'yellow')}`);
274
+ console.log(`Usage: ${colored('ccs auth show <profile> [--json]', 'yellow')}`);
242
275
  process.exit(1);
243
276
  }
244
277
 
@@ -246,12 +279,41 @@ class AuthCommands {
246
279
  const profile = this.registry.getProfile(profileName);
247
280
  const defaultProfile = this.registry.getDefaultProfile();
248
281
  const isDefault = profileName === defaultProfile;
282
+ const instancePath = this.instanceMgr.getInstancePath(profileName);
283
+
284
+ // Count sessions
285
+ let sessionCount = 0;
286
+ try {
287
+ const sessionsDir = path.join(instancePath, 'session-env');
288
+ if (fs.existsSync(sessionsDir)) {
289
+ const files = fs.readdirSync(sessionsDir);
290
+ sessionCount = files.filter(f => f.endsWith('.json')).length;
291
+ }
292
+ } catch (e) {
293
+ // Ignore errors counting sessions
294
+ }
249
295
 
296
+ // JSON output mode
297
+ if (json) {
298
+ const output = {
299
+ name: profileName,
300
+ type: profile.type || 'account',
301
+ is_default: isDefault,
302
+ created: profile.created,
303
+ last_used: profile.last_used || null,
304
+ instance_path: instancePath,
305
+ session_count: sessionCount
306
+ };
307
+ console.log(JSON.stringify(output, null, 2));
308
+ return;
309
+ }
310
+
311
+ // Human-readable output
250
312
  console.log(colored(`Profile: ${profileName}`, 'bold'));
251
313
  console.log('');
252
314
  console.log(` Type: ${profile.type || 'account'}`);
253
315
  console.log(` Default: ${isDefault ? 'Yes' : 'No'}`);
254
- console.log(` Instance: ${this.instanceMgr.getInstancePath(profileName)}`);
316
+ console.log(` Instance: ${instancePath}`);
255
317
  console.log(` Created: ${new Date(profile.created).toLocaleString()}`);
256
318
 
257
319
  if (profile.last_used) {
@@ -273,13 +335,12 @@ class AuthCommands {
273
335
  * @param {Array} args - Command arguments
274
336
  */
275
337
  async handleRemove(args) {
276
- const profileName = args.find(arg => !arg.startsWith('--'));
277
- const force = args.includes('--force');
338
+ const profileName = args.find(arg => !arg.startsWith('--') && !arg.startsWith('-'));
278
339
 
279
340
  if (!profileName) {
280
341
  console.error('[X] Profile name is required');
281
342
  console.log('');
282
- console.log(`Usage: ${colored('ccs auth remove <profile> [--force]', 'yellow')}`);
343
+ console.log(`Usage: ${colored('ccs auth remove <profile> [--yes]', 'yellow')}`);
283
344
  process.exit(1);
284
345
  }
285
346
 
@@ -288,15 +349,39 @@ class AuthCommands {
288
349
  process.exit(1);
289
350
  }
290
351
 
291
- // Require --force for safety
292
- if (!force) {
293
- console.error('[X] Removal requires --force flag for safety');
352
+ try {
353
+ // Get instance path and session count for impact display
354
+ const instancePath = this.instanceMgr.getInstancePath(profileName);
355
+ let sessionCount = 0;
356
+
357
+ try {
358
+ const sessionsDir = path.join(instancePath, 'session-env');
359
+ if (fs.existsSync(sessionsDir)) {
360
+ const files = fs.readdirSync(sessionsDir);
361
+ sessionCount = files.filter(f => f.endsWith('.json')).length;
362
+ }
363
+ } catch (e) {
364
+ // Ignore errors counting sessions
365
+ }
366
+
367
+ // Display impact
368
+ console.log('');
369
+ console.log(`Profile '${colored(profileName, 'cyan')}' will be permanently deleted.`);
370
+ console.log(` Instance path: ${instancePath}`);
371
+ console.log(` Sessions: ${sessionCount} conversation${sessionCount !== 1 ? 's' : ''}`);
294
372
  console.log('');
295
- console.log(`Run: ${colored(`ccs auth remove ${profileName} --force`, 'yellow')}`);
296
- process.exit(1);
297
- }
298
373
 
299
- try {
374
+ // Interactive confirmation (or --yes flag)
375
+ const confirmed = await InteractivePrompt.confirm(
376
+ 'Delete this profile?',
377
+ { default: false } // Default to NO (safe)
378
+ );
379
+
380
+ if (!confirmed) {
381
+ console.log('[i] Cancelled');
382
+ process.exit(0);
383
+ }
384
+
300
385
  // Delete instance
301
386
  this.instanceMgr.deleteInstance(profileName);
302
387
 
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
+ const { findSimilarStrings } = require('../utils/helpers');
6
7
 
7
8
  /**
8
9
  * Profile Detector
@@ -85,12 +86,16 @@ class ProfileDetector {
85
86
  };
86
87
  }
87
88
 
88
- // Not found
89
- throw new Error(
90
- `Profile not found: ${profileName}\n` +
91
- `Available profiles:\n` +
92
- this._listAvailableProfiles()
93
- );
89
+ // Not found - generate suggestions
90
+ const allProfiles = this.getAllProfiles();
91
+ const allProfileNames = [...allProfiles.settings, ...allProfiles.accounts];
92
+ const suggestions = findSimilarStrings(profileName, allProfileNames);
93
+
94
+ const error = new Error(`Profile not found: ${profileName}`);
95
+ error.profileName = profileName;
96
+ error.suggestions = suggestions;
97
+ error.availableProfiles = this._listAvailableProfiles();
98
+ throw error;
94
99
  }
95
100
 
96
101
  /**
package/bin/ccs.js CHANGED
@@ -73,6 +73,33 @@ function handleVersionCommand() {
73
73
  // Config path
74
74
  const configPath = getConfigPath();
75
75
  console.log(` ${colored('Config:', 'cyan')} ${configPath}`);
76
+
77
+ // Delegation status
78
+ const delegationRulesPath = path.join(os.homedir(), '.ccs', 'delegation-rules.json');
79
+ const delegationEnabled = fs.existsSync(delegationRulesPath);
80
+
81
+ if (delegationEnabled) {
82
+ console.log(` ${colored('Delegation:', 'cyan')} Enabled`);
83
+
84
+ // Check which profiles are delegation-ready
85
+ const readyProfiles = [];
86
+ const { DelegationValidator } = require('./utils/delegation-validator');
87
+
88
+ for (const profile of ['glm', 'kimi']) {
89
+ const validation = DelegationValidator.validate(profile);
90
+ if (validation.valid) {
91
+ readyProfiles.push(profile);
92
+ }
93
+ }
94
+
95
+ if (readyProfiles.length > 0) {
96
+ console.log(` ${colored('Ready:', 'cyan')} ${readyProfiles.join(', ')}`);
97
+ } else {
98
+ console.log(` ${colored('Ready:', 'cyan')} None (configure profiles first)`);
99
+ }
100
+ } else {
101
+ console.log(` ${colored('Delegation:', 'cyan')} Not configured`);
102
+ }
76
103
  console.log('');
77
104
 
78
105
  // Documentation
@@ -121,15 +148,26 @@ function handleHelpCommand() {
121
148
  console.log(` ${colored('ccs personal', 'yellow')} Switch to personal account`);
122
149
  console.log('');
123
150
 
151
+ // Delegation (NEW)
152
+ console.log(colored('Delegation (Token Optimization):', 'cyan'));
153
+ console.log(` ${colored('/ccs:glm "task"', 'yellow')} Delegate to GLM-4.6 within Claude session`);
154
+ console.log(` ${colored('/ccs:kimi "task"', 'yellow')} Delegate to Kimi for long context`);
155
+ console.log(` ${colored('/ccs:create m2', 'yellow')} Create custom delegation command`);
156
+ console.log(' Use delegation to save tokens on simple tasks');
157
+ console.log(' Commands work inside Claude Code sessions only');
158
+ console.log('');
159
+
124
160
  // Diagnostics
125
161
  console.log(colored('Diagnostics:', 'cyan'));
126
162
  console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
163
+ console.log(` ${colored('ccs update', 'yellow')} Re-install CCS items to ~/.claude/`);
127
164
  console.log('');
128
165
 
129
166
  // Flags
130
167
  console.log(colored('Flags:', 'cyan'));
131
168
  console.log(` ${colored('-h, --help', 'yellow')} Show this help message`);
132
169
  console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`);
170
+ console.log(` ${colored('--shell-completion', 'yellow')} Install shell auto-completion`);
133
171
  console.log('');
134
172
 
135
173
  // Configuration
@@ -149,6 +187,19 @@ function handleHelpCommand() {
149
187
  console.log(' Note: Commands, skills, and agents are symlinked across all profiles');
150
188
  console.log('');
151
189
 
190
+ // Examples
191
+ console.log(colored('Examples:', 'cyan'));
192
+ console.log(' Quick start:');
193
+ console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
194
+ console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
195
+ console.log('');
196
+ console.log(' Multi-account workflow:');
197
+ console.log(` ${colored('$ ccs auth create work', 'yellow')} # Create work profile`);
198
+ console.log(` ${colored('$ ccs work "review PR"', 'yellow')} # Use work account`);
199
+ console.log('');
200
+ console.log(` For more: ${colored('https://github.com/kaitranntt/ccs#usage', 'cyan')}`);
201
+ console.log('');
202
+
152
203
  // Uninstall
153
204
  console.log(colored('Uninstall:', 'yellow'));
154
205
  console.log(' npm: npm uninstall -g @kaitranntt/ccs');
@@ -203,6 +254,16 @@ async function handleDoctorCommand() {
203
254
  process.exit(doctor.results.isHealthy() ? 0 : 1);
204
255
  }
205
256
 
257
+ async function handleUpdateCommand() {
258
+ const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
259
+ const manager = new ClaudeSymlinkManager();
260
+
261
+ console.log('[i] Updating CCS items in ~/.claude/...');
262
+ manager.update();
263
+
264
+ process.exit(0);
265
+ }
266
+
206
267
  // Smart profile detection
207
268
  function detectProfile(args) {
208
269
  if (args.length === 0 || args[0].startsWith('-')) {
@@ -241,6 +302,10 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
241
302
  });
242
303
 
243
304
  // 3. Wait for proxy ready signal (with timeout)
305
+ const { ProgressIndicator } = require('./utils/progress-indicator');
306
+ const spinner = new ProgressIndicator('Starting GLMT proxy');
307
+ spinner.start();
308
+
244
309
  let port;
245
310
  try {
246
311
  port = await new Promise((resolve, reject) => {
@@ -268,8 +333,11 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
268
333
  }
269
334
  });
270
335
  });
336
+
337
+ spinner.succeed(`GLMT proxy ready on port ${port}`);
271
338
  } catch (error) {
272
- console.error('[X] Failed to start GLMT proxy:', error.message);
339
+ spinner.fail('Failed to start GLMT proxy');
340
+ console.error('[X] Error:', error.message);
273
341
  console.error('');
274
342
  console.error('Possible causes:');
275
343
  console.error(' 1. Port conflict (unlikely with random port)');
@@ -341,6 +409,58 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
341
409
  });
342
410
  }
343
411
 
412
+ /**
413
+ * Handle shell completion installation
414
+ */
415
+ async function handleShellCompletionCommand(args) {
416
+ const { ShellCompletionInstaller } = require('./utils/shell-completion');
417
+ const { colored } = require('./utils/helpers');
418
+
419
+ console.log(colored('Shell Completion Installer', 'bold'));
420
+ console.log('');
421
+
422
+ // Parse flags
423
+ let targetShell = null;
424
+ if (args.includes('--bash')) targetShell = 'bash';
425
+ else if (args.includes('--zsh')) targetShell = 'zsh';
426
+ else if (args.includes('--fish')) targetShell = 'fish';
427
+ else if (args.includes('--powershell')) targetShell = 'powershell';
428
+
429
+ try {
430
+ const installer = new ShellCompletionInstaller();
431
+ const result = installer.install(targetShell);
432
+
433
+ if (result.alreadyInstalled) {
434
+ console.log(colored('[OK] Shell completion already installed', 'green'));
435
+ console.log('');
436
+ return;
437
+ }
438
+
439
+ console.log(colored('[OK] Shell completion installed successfully!', 'green'));
440
+ console.log('');
441
+ console.log(result.message);
442
+ console.log('');
443
+ console.log(colored('To activate:', 'cyan'));
444
+ console.log(` ${result.reload}`);
445
+ console.log('');
446
+ console.log(colored('Then test:', 'cyan'));
447
+ console.log(' ccs <TAB> # See available profiles');
448
+ console.log(' ccs auth <TAB> # See auth subcommands');
449
+ console.log('');
450
+ } catch (error) {
451
+ console.error(colored('[X] Error:', 'red'), error.message);
452
+ console.error('');
453
+ console.error(colored('Usage:', 'yellow'));
454
+ console.error(' ccs --shell-completion # Auto-detect shell');
455
+ console.error(' ccs --shell-completion --bash # Install for bash');
456
+ console.error(' ccs --shell-completion --zsh # Install for zsh');
457
+ console.error(' ccs --shell-completion --fish # Install for fish');
458
+ console.error(' ccs --shell-completion --powershell # Install for PowerShell');
459
+ console.error('');
460
+ process.exit(1);
461
+ }
462
+ }
463
+
344
464
  // Main execution
345
465
  async function main() {
346
466
  const args = process.argv.slice(2);
@@ -369,12 +489,24 @@ async function main() {
369
489
  return;
370
490
  }
371
491
 
492
+ // Special case: shell completion installer
493
+ if (firstArg === '--shell-completion') {
494
+ await handleShellCompletionCommand(args.slice(1));
495
+ return;
496
+ }
497
+
372
498
  // Special case: doctor command
373
499
  if (firstArg === 'doctor' || firstArg === '--doctor') {
374
500
  await handleDoctorCommand();
375
501
  return;
376
502
  }
377
503
 
504
+ // Special case: update command (re-install CCS symlinks)
505
+ if (firstArg === 'update' || firstArg === '--update') {
506
+ await handleUpdateCommand();
507
+ return;
508
+ }
509
+
378
510
  // Special case: auth command (multi-account management)
379
511
  if (firstArg === 'auth') {
380
512
  const AuthCommands = require('./auth/auth-commands');
@@ -383,6 +515,14 @@ async function main() {
383
515
  return;
384
516
  }
385
517
 
518
+ // Special case: headless delegation (-p flag)
519
+ if (args.includes('-p') || args.includes('--prompt')) {
520
+ const DelegationHandler = require('./delegation/delegation-handler');
521
+ const handler = new DelegationHandler();
522
+ await handler.route(args);
523
+ return;
524
+ }
525
+
386
526
  // Auto-recovery for missing configuration
387
527
  const recovery = new RecoveryManager();
388
528
  const recovered = recovery.recoverAll();
@@ -443,7 +583,13 @@ async function main() {
443
583
  execClaude(claudeCli, remainingArgs);
444
584
  }
445
585
  } catch (error) {
446
- console.error(`[X] ${error.message}`);
586
+ // Check if this is a profile not found error with suggestions
587
+ if (error.profileName && error.availableProfiles !== undefined) {
588
+ const allProfiles = error.availableProfiles.split('\n');
589
+ ErrorManager.showProfileNotFound(error.profileName, allProfiles, error.suggestions);
590
+ } else {
591
+ console.error(`[X] ${error.message}`);
592
+ }
447
593
  process.exit(1);
448
594
  }
449
595
  }
@@ -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/`