@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.
- package/.claude/agents/ccs-delegator.md +117 -0
- package/.claude/commands/ccs/glm/continue.md +22 -0
- package/.claude/commands/ccs/glm.md +22 -0
- package/.claude/commands/ccs/kimi/continue.md +22 -0
- package/.claude/commands/ccs/kimi.md +22 -0
- package/.claude/skills/ccs-delegation/SKILL.md +54 -0
- package/.claude/skills/ccs-delegation/references/README.md +24 -0
- package/.claude/skills/ccs-delegation/references/delegation-guidelines.md +99 -0
- package/.claude/skills/ccs-delegation/references/headless-workflow.md +174 -0
- package/.claude/skills/ccs-delegation/references/troubleshooting.md +268 -0
- package/README.ja.md +470 -146
- package/README.md +532 -145
- package/README.vi.md +484 -157
- package/VERSION +1 -1
- package/bin/auth/auth-commands.js +98 -13
- package/bin/auth/profile-detector.js +11 -6
- package/bin/ccs.js +148 -2
- package/bin/delegation/README.md +189 -0
- package/bin/delegation/delegation-handler.js +212 -0
- package/bin/delegation/headless-executor.js +617 -0
- package/bin/delegation/result-formatter.js +483 -0
- package/bin/delegation/session-manager.js +156 -0
- package/bin/delegation/settings-parser.js +109 -0
- package/bin/management/doctor.js +94 -1
- package/bin/utils/claude-symlink-manager.js +238 -0
- package/bin/utils/delegation-validator.js +154 -0
- package/bin/utils/error-codes.js +59 -0
- package/bin/utils/error-manager.js +38 -32
- package/bin/utils/helpers.js +65 -1
- package/bin/utils/progress-indicator.js +111 -0
- package/bin/utils/prompt.js +134 -0
- package/bin/utils/shell-completion.js +234 -0
- package/lib/ccs +575 -25
- package/lib/ccs.ps1 +381 -20
- package/lib/error-codes.ps1 +55 -0
- package/lib/error-codes.sh +63 -0
- package/lib/progress-indicator.ps1 +120 -0
- package/lib/progress-indicator.sh +117 -0
- package/lib/prompt.ps1 +109 -0
- package/lib/prompt.sh +99 -0
- package/package.json +2 -1
- package/scripts/completion/README.md +308 -0
- package/scripts/completion/ccs.bash +81 -0
- package/scripts/completion/ccs.fish +92 -0
- package/scripts/completion/ccs.ps1 +157 -0
- package/scripts/completion/ccs.zsh +130 -0
- package/scripts/postinstall.js +35 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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: ${
|
|
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> [--
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/`
|