@kaitranntt/ccs 3.4.5 → 3.5.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/README.ja.md +470 -146
- package/README.md +380 -182
- 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 +87 -2
- package/bin/glmt/glmt-transformer.js +13 -1
- package/bin/glmt/reasoning-enforcer.js +173 -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 +541 -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 +1 -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 +24 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.5.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
|
@@ -130,6 +130,7 @@ function handleHelpCommand() {
|
|
|
130
130
|
console.log(colored('Flags:', 'cyan'));
|
|
131
131
|
console.log(` ${colored('-h, --help', 'yellow')} Show this help message`);
|
|
132
132
|
console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`);
|
|
133
|
+
console.log(` ${colored('--shell-completion', 'yellow')} Install shell auto-completion`);
|
|
133
134
|
console.log('');
|
|
134
135
|
|
|
135
136
|
// Configuration
|
|
@@ -149,6 +150,19 @@ function handleHelpCommand() {
|
|
|
149
150
|
console.log(' Note: Commands, skills, and agents are symlinked across all profiles');
|
|
150
151
|
console.log('');
|
|
151
152
|
|
|
153
|
+
// Examples
|
|
154
|
+
console.log(colored('Examples:', 'cyan'));
|
|
155
|
+
console.log(' Quick start:');
|
|
156
|
+
console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
|
|
157
|
+
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log(' Multi-account workflow:');
|
|
160
|
+
console.log(` ${colored('$ ccs auth create work', 'yellow')} # Create work profile`);
|
|
161
|
+
console.log(` ${colored('$ ccs work "review PR"', 'yellow')} # Use work account`);
|
|
162
|
+
console.log('');
|
|
163
|
+
console.log(` For more: ${colored('https://github.com/kaitranntt/ccs#usage', 'cyan')}`);
|
|
164
|
+
console.log('');
|
|
165
|
+
|
|
152
166
|
// Uninstall
|
|
153
167
|
console.log(colored('Uninstall:', 'yellow'));
|
|
154
168
|
console.log(' npm: npm uninstall -g @kaitranntt/ccs');
|
|
@@ -241,6 +255,10 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
|
|
|
241
255
|
});
|
|
242
256
|
|
|
243
257
|
// 3. Wait for proxy ready signal (with timeout)
|
|
258
|
+
const { ProgressIndicator } = require('./utils/progress-indicator');
|
|
259
|
+
const spinner = new ProgressIndicator('Starting GLMT proxy');
|
|
260
|
+
spinner.start();
|
|
261
|
+
|
|
244
262
|
let port;
|
|
245
263
|
try {
|
|
246
264
|
port = await new Promise((resolve, reject) => {
|
|
@@ -268,8 +286,11 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
|
|
|
268
286
|
}
|
|
269
287
|
});
|
|
270
288
|
});
|
|
289
|
+
|
|
290
|
+
spinner.succeed(`GLMT proxy ready on port ${port}`);
|
|
271
291
|
} catch (error) {
|
|
272
|
-
|
|
292
|
+
spinner.fail('Failed to start GLMT proxy');
|
|
293
|
+
console.error('[X] Error:', error.message);
|
|
273
294
|
console.error('');
|
|
274
295
|
console.error('Possible causes:');
|
|
275
296
|
console.error(' 1. Port conflict (unlikely with random port)');
|
|
@@ -341,6 +362,58 @@ async function execClaudeWithProxy(claudeCli, profileName, args) {
|
|
|
341
362
|
});
|
|
342
363
|
}
|
|
343
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Handle shell completion installation
|
|
367
|
+
*/
|
|
368
|
+
async function handleShellCompletionCommand(args) {
|
|
369
|
+
const { ShellCompletionInstaller } = require('./utils/shell-completion');
|
|
370
|
+
const { colored } = require('./utils/helpers');
|
|
371
|
+
|
|
372
|
+
console.log(colored('Shell Completion Installer', 'bold'));
|
|
373
|
+
console.log('');
|
|
374
|
+
|
|
375
|
+
// Parse flags
|
|
376
|
+
let targetShell = null;
|
|
377
|
+
if (args.includes('--bash')) targetShell = 'bash';
|
|
378
|
+
else if (args.includes('--zsh')) targetShell = 'zsh';
|
|
379
|
+
else if (args.includes('--fish')) targetShell = 'fish';
|
|
380
|
+
else if (args.includes('--powershell')) targetShell = 'powershell';
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
const installer = new ShellCompletionInstaller();
|
|
384
|
+
const result = installer.install(targetShell);
|
|
385
|
+
|
|
386
|
+
if (result.alreadyInstalled) {
|
|
387
|
+
console.log(colored('[OK] Shell completion already installed', 'green'));
|
|
388
|
+
console.log('');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
console.log(colored('[OK] Shell completion installed successfully!', 'green'));
|
|
393
|
+
console.log('');
|
|
394
|
+
console.log(result.message);
|
|
395
|
+
console.log('');
|
|
396
|
+
console.log(colored('To activate:', 'cyan'));
|
|
397
|
+
console.log(` ${result.reload}`);
|
|
398
|
+
console.log('');
|
|
399
|
+
console.log(colored('Then test:', 'cyan'));
|
|
400
|
+
console.log(' ccs <TAB> # See available profiles');
|
|
401
|
+
console.log(' ccs auth <TAB> # See auth subcommands');
|
|
402
|
+
console.log('');
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error(colored('[X] Error:', 'red'), error.message);
|
|
405
|
+
console.error('');
|
|
406
|
+
console.error(colored('Usage:', 'yellow'));
|
|
407
|
+
console.error(' ccs --shell-completion # Auto-detect shell');
|
|
408
|
+
console.error(' ccs --shell-completion --bash # Install for bash');
|
|
409
|
+
console.error(' ccs --shell-completion --zsh # Install for zsh');
|
|
410
|
+
console.error(' ccs --shell-completion --fish # Install for fish');
|
|
411
|
+
console.error(' ccs --shell-completion --powershell # Install for PowerShell');
|
|
412
|
+
console.error('');
|
|
413
|
+
process.exit(1);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
344
417
|
// Main execution
|
|
345
418
|
async function main() {
|
|
346
419
|
const args = process.argv.slice(2);
|
|
@@ -369,6 +442,12 @@ async function main() {
|
|
|
369
442
|
return;
|
|
370
443
|
}
|
|
371
444
|
|
|
445
|
+
// Special case: shell completion installer
|
|
446
|
+
if (firstArg === '--shell-completion') {
|
|
447
|
+
await handleShellCompletionCommand(args.slice(1));
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
372
451
|
// Special case: doctor command
|
|
373
452
|
if (firstArg === 'doctor' || firstArg === '--doctor') {
|
|
374
453
|
await handleDoctorCommand();
|
|
@@ -443,7 +522,13 @@ async function main() {
|
|
|
443
522
|
execClaude(claudeCli, remainingArgs);
|
|
444
523
|
}
|
|
445
524
|
} catch (error) {
|
|
446
|
-
|
|
525
|
+
// Check if this is a profile not found error with suggestions
|
|
526
|
+
if (error.profileName && error.availableProfiles !== undefined) {
|
|
527
|
+
const allProfiles = error.availableProfiles.split('\n');
|
|
528
|
+
ErrorManager.showProfileNotFound(error.profileName, allProfiles, error.suggestions);
|
|
529
|
+
} else {
|
|
530
|
+
console.error(`[X] ${error.message}`);
|
|
531
|
+
}
|
|
447
532
|
process.exit(1);
|
|
448
533
|
}
|
|
449
534
|
}
|
|
@@ -8,6 +8,7 @@ const os = require('os');
|
|
|
8
8
|
const SSEParser = require('./sse-parser');
|
|
9
9
|
const DeltaAccumulator = require('./delta-accumulator');
|
|
10
10
|
const LocaleEnforcer = require('./locale-enforcer');
|
|
11
|
+
const ReasoningEnforcer = require('./reasoning-enforcer');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* GlmtTransformer - Convert between Anthropic and OpenAI formats with thinking and tool support
|
|
@@ -54,6 +55,11 @@ class GlmtTransformer {
|
|
|
54
55
|
|
|
55
56
|
// Initialize locale enforcer (always enforce English)
|
|
56
57
|
this.localeEnforcer = new LocaleEnforcer();
|
|
58
|
+
|
|
59
|
+
// Initialize reasoning enforcer (enabled by default for all GLMT usage)
|
|
60
|
+
this.reasoningEnforcer = new ReasoningEnforcer({
|
|
61
|
+
enabled: config.explicitReasoning ?? true
|
|
62
|
+
});
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
/**
|
|
@@ -104,10 +110,16 @@ class GlmtTransformer {
|
|
|
104
110
|
anthropicRequest.messages || []
|
|
105
111
|
);
|
|
106
112
|
|
|
113
|
+
// 4.5. Inject reasoning instruction (if enabled or thinking requested)
|
|
114
|
+
const messagesWithReasoning = this.reasoningEnforcer.injectInstruction(
|
|
115
|
+
messagesWithLocale,
|
|
116
|
+
thinkingConfig
|
|
117
|
+
);
|
|
118
|
+
|
|
107
119
|
// 5. Convert to OpenAI format
|
|
108
120
|
const openaiRequest = {
|
|
109
121
|
model: glmModel,
|
|
110
|
-
messages: this._sanitizeMessages(
|
|
122
|
+
messages: this._sanitizeMessages(messagesWithReasoning),
|
|
111
123
|
max_tokens: this._getMaxTokens(glmModel),
|
|
112
124
|
stream: anthropicRequest.stream ?? false
|
|
113
125
|
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ReasoningEnforcer - Inject explicit reasoning instructions into prompts
|
|
6
|
+
*
|
|
7
|
+
* Purpose: Force GLM models to use structured reasoning output format (<reasoning_content>)
|
|
8
|
+
* This complements API parameters (reasoning: true) with explicit prompt instructions.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const enforcer = new ReasoningEnforcer({ enabled: true });
|
|
12
|
+
* const modifiedMessages = enforcer.injectInstruction(messages, thinkingConfig);
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* 1. If system prompt exists: Prepend reasoning instruction
|
|
16
|
+
* 2. If no system prompt: Prepend to first user message
|
|
17
|
+
* 3. Select prompt template based on effort level (low/medium/high/max)
|
|
18
|
+
* 4. Preserve message structure (string vs array content)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
class ReasoningEnforcer {
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.enabled = options.enabled ?? false; // Opt-in by default
|
|
24
|
+
this.prompts = options.prompts || this._getDefaultPrompts();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Inject reasoning instruction into messages
|
|
29
|
+
* @param {Array} messages - Messages array to modify
|
|
30
|
+
* @param {Object} thinkingConfig - { thinking: boolean, effort: string }
|
|
31
|
+
* @returns {Array} Modified messages array
|
|
32
|
+
*/
|
|
33
|
+
injectInstruction(messages, thinkingConfig = {}) {
|
|
34
|
+
// Only inject if enabled or thinking explicitly requested
|
|
35
|
+
if (!this.enabled && !thinkingConfig.thinking) {
|
|
36
|
+
return messages;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Clone messages to avoid mutation
|
|
40
|
+
const modifiedMessages = JSON.parse(JSON.stringify(messages));
|
|
41
|
+
|
|
42
|
+
// Select prompt based on effort level
|
|
43
|
+
const prompt = this._selectPrompt(thinkingConfig.effort || 'medium');
|
|
44
|
+
|
|
45
|
+
// Strategy 1: Inject into system prompt (preferred)
|
|
46
|
+
const systemIndex = modifiedMessages.findIndex(m => m.role === 'system');
|
|
47
|
+
if (systemIndex >= 0) {
|
|
48
|
+
const systemMsg = modifiedMessages[systemIndex];
|
|
49
|
+
|
|
50
|
+
if (typeof systemMsg.content === 'string') {
|
|
51
|
+
systemMsg.content = `${prompt}\n\n${systemMsg.content}`;
|
|
52
|
+
} else if (Array.isArray(systemMsg.content)) {
|
|
53
|
+
systemMsg.content.unshift({
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: prompt
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return modifiedMessages;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Strategy 2: Prepend to first user message
|
|
63
|
+
const userIndex = modifiedMessages.findIndex(m => m.role === 'user');
|
|
64
|
+
if (userIndex >= 0) {
|
|
65
|
+
const userMsg = modifiedMessages[userIndex];
|
|
66
|
+
|
|
67
|
+
if (typeof userMsg.content === 'string') {
|
|
68
|
+
userMsg.content = `${prompt}\n\n${userMsg.content}`;
|
|
69
|
+
} else if (Array.isArray(userMsg.content)) {
|
|
70
|
+
userMsg.content.unshift({
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: prompt
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return modifiedMessages;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// No system or user messages found (edge case)
|
|
80
|
+
return modifiedMessages;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Select prompt template based on effort level
|
|
85
|
+
* @param {string} effort - 'low', 'medium', 'high', or 'max'
|
|
86
|
+
* @returns {string} Prompt template
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
_selectPrompt(effort) {
|
|
90
|
+
const normalizedEffort = effort.toLowerCase();
|
|
91
|
+
return this.prompts[normalizedEffort] || this.prompts.medium;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get default prompt templates
|
|
96
|
+
* @returns {Object} Map of effort levels to prompts
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
_getDefaultPrompts() {
|
|
100
|
+
return {
|
|
101
|
+
low: `You are an expert reasoning model using GLM-4.6 architecture.
|
|
102
|
+
|
|
103
|
+
CRITICAL: Before answering, write 2-3 sentences of reasoning in <reasoning_content> tags.
|
|
104
|
+
|
|
105
|
+
OUTPUT FORMAT:
|
|
106
|
+
<reasoning_content>
|
|
107
|
+
(Brief analysis: what is the problem? what's the approach?)
|
|
108
|
+
</reasoning_content>
|
|
109
|
+
|
|
110
|
+
(Write your final answer here)`,
|
|
111
|
+
|
|
112
|
+
medium: `You are an expert reasoning model using GLM-4.6 architecture.
|
|
113
|
+
|
|
114
|
+
CRITICAL REQUIREMENTS:
|
|
115
|
+
1. Always think step-by-step before answering
|
|
116
|
+
2. Write your reasoning process explicitly in <reasoning_content> tags
|
|
117
|
+
3. Never skip your chain of thought, even for simple problems
|
|
118
|
+
|
|
119
|
+
OUTPUT FORMAT:
|
|
120
|
+
<reasoning_content>
|
|
121
|
+
(Write your detailed thinking here: analyze the problem, explore approaches,
|
|
122
|
+
evaluate trade-offs, and arrive at a conclusion)
|
|
123
|
+
</reasoning_content>
|
|
124
|
+
|
|
125
|
+
(Write your final answer here based on your reasoning above)`,
|
|
126
|
+
|
|
127
|
+
high: `You are an expert reasoning model using GLM-4.6 architecture.
|
|
128
|
+
|
|
129
|
+
CRITICAL REQUIREMENTS:
|
|
130
|
+
1. Think deeply and systematically before answering
|
|
131
|
+
2. Write comprehensive reasoning in <reasoning_content> tags
|
|
132
|
+
3. Explore multiple approaches and evaluate trade-offs
|
|
133
|
+
4. Show all steps in your problem-solving process
|
|
134
|
+
|
|
135
|
+
OUTPUT FORMAT:
|
|
136
|
+
<reasoning_content>
|
|
137
|
+
(Write exhaustive analysis here:
|
|
138
|
+
- Problem decomposition
|
|
139
|
+
- Multiple approach exploration
|
|
140
|
+
- Trade-off analysis for each approach
|
|
141
|
+
- Edge case consideration
|
|
142
|
+
- Final conclusion with justification)
|
|
143
|
+
</reasoning_content>
|
|
144
|
+
|
|
145
|
+
(Write your final answer here based on your systematic reasoning above)`,
|
|
146
|
+
|
|
147
|
+
max: `You are an expert reasoning model using GLM-4.6 architecture.
|
|
148
|
+
|
|
149
|
+
CRITICAL REQUIREMENTS:
|
|
150
|
+
1. Think exhaustively from first principles
|
|
151
|
+
2. Write extremely detailed reasoning in <reasoning_content> tags
|
|
152
|
+
3. Analyze ALL possible angles, approaches, and edge cases
|
|
153
|
+
4. Challenge your own assumptions and explore alternatives
|
|
154
|
+
5. Provide rigorous justification for every claim
|
|
155
|
+
|
|
156
|
+
OUTPUT FORMAT:
|
|
157
|
+
<reasoning_content>
|
|
158
|
+
(Write comprehensive analysis here:
|
|
159
|
+
- First principles breakdown
|
|
160
|
+
- Exhaustive approach enumeration
|
|
161
|
+
- Comparative analysis of all approaches
|
|
162
|
+
- Edge case and failure mode analysis
|
|
163
|
+
- Assumption validation
|
|
164
|
+
- Counter-argument consideration
|
|
165
|
+
- Final conclusion with rigorous justification)
|
|
166
|
+
</reasoning_content>
|
|
167
|
+
|
|
168
|
+
(Write your final answer here based on your exhaustive reasoning above)`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = ReasoningEnforcer;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// CCS Error Codes
|
|
2
|
+
// Documentation: ../../docs/errors/README.md
|
|
3
|
+
|
|
4
|
+
const ERROR_CODES = {
|
|
5
|
+
// Configuration Errors (E100-E199)
|
|
6
|
+
CONFIG_MISSING: 'E101',
|
|
7
|
+
CONFIG_INVALID_JSON: 'E102',
|
|
8
|
+
CONFIG_INVALID_PROFILE: 'E103',
|
|
9
|
+
|
|
10
|
+
// Profile Management Errors (E200-E299)
|
|
11
|
+
PROFILE_NOT_FOUND: 'E104',
|
|
12
|
+
PROFILE_ALREADY_EXISTS: 'E105',
|
|
13
|
+
PROFILE_CANNOT_DELETE_DEFAULT: 'E106',
|
|
14
|
+
PROFILE_INVALID_NAME: 'E107',
|
|
15
|
+
|
|
16
|
+
// Claude CLI Detection Errors (E300-E399)
|
|
17
|
+
CLAUDE_NOT_FOUND: 'E301',
|
|
18
|
+
CLAUDE_VERSION_INCOMPATIBLE: 'E302',
|
|
19
|
+
CLAUDE_EXECUTION_FAILED: 'E303',
|
|
20
|
+
|
|
21
|
+
// Network/API Errors (E400-E499)
|
|
22
|
+
GLMT_PROXY_TIMEOUT: 'E401',
|
|
23
|
+
API_KEY_MISSING: 'E402',
|
|
24
|
+
API_AUTH_FAILED: 'E403',
|
|
25
|
+
API_RATE_LIMIT: 'E404',
|
|
26
|
+
|
|
27
|
+
// File System Errors (E500-E599)
|
|
28
|
+
FS_CANNOT_CREATE_DIR: 'E501',
|
|
29
|
+
FS_CANNOT_WRITE_FILE: 'E502',
|
|
30
|
+
FS_CANNOT_READ_FILE: 'E503',
|
|
31
|
+
FS_INSTANCE_NOT_FOUND: 'E504',
|
|
32
|
+
|
|
33
|
+
// Internal Errors (E900-E999)
|
|
34
|
+
INTERNAL_ERROR: 'E900',
|
|
35
|
+
INVALID_STATE: 'E901'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Error code documentation URL generator
|
|
39
|
+
function getErrorDocUrl(errorCode) {
|
|
40
|
+
return `https://github.com/kaitranntt/ccs/blob/main/docs/errors/README.md#${errorCode.toLowerCase()}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get error category from code
|
|
44
|
+
function getErrorCategory(errorCode) {
|
|
45
|
+
const code = parseInt(errorCode.substring(1));
|
|
46
|
+
if (code >= 100 && code < 200) return 'Configuration';
|
|
47
|
+
if (code >= 200 && code < 300) return 'Profile Management';
|
|
48
|
+
if (code >= 300 && code < 400) return 'Claude CLI Detection';
|
|
49
|
+
if (code >= 400 && code < 500) return 'Network/API';
|
|
50
|
+
if (code >= 500 && code < 600) return 'File System';
|
|
51
|
+
if (code >= 900 && code < 1000) return 'Internal';
|
|
52
|
+
return 'Unknown';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
ERROR_CODES,
|
|
57
|
+
getErrorDocUrl,
|
|
58
|
+
getErrorCategory
|
|
59
|
+
};
|