@kaitranntt/ccs 4.4.0 → 5.0.1

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 (262) hide show
  1. package/README.md +98 -7
  2. package/VERSION +1 -1
  3. package/config/base-agy.settings.json +10 -0
  4. package/config/base-codex.settings.json +10 -0
  5. package/config/base-gemini.settings.json +10 -0
  6. package/dist/auth/auth-commands.d.ts +52 -0
  7. package/dist/auth/auth-commands.d.ts.map +1 -0
  8. package/dist/auth/auth-commands.js +479 -0
  9. package/dist/auth/auth-commands.js.map +1 -0
  10. package/dist/auth/profile-detector.d.ts +68 -0
  11. package/dist/auth/profile-detector.d.ts.map +1 -0
  12. package/dist/auth/profile-detector.js +209 -0
  13. package/dist/auth/profile-detector.js.map +1 -0
  14. package/dist/auth/profile-registry.d.ts +60 -0
  15. package/dist/auth/profile-registry.d.ts.map +1 -0
  16. package/dist/auth/profile-registry.js +188 -0
  17. package/dist/auth/profile-registry.js.map +1 -0
  18. package/dist/ccs.d.ts +10 -0
  19. package/dist/ccs.d.ts.map +1 -0
  20. package/dist/ccs.js +320 -0
  21. package/dist/ccs.js.map +1 -0
  22. package/dist/cliproxy/auth-handler.d.ts +95 -0
  23. package/dist/cliproxy/auth-handler.d.ts.map +1 -0
  24. package/dist/cliproxy/auth-handler.js +443 -0
  25. package/dist/cliproxy/auth-handler.js.map +1 -0
  26. package/dist/cliproxy/base-config-loader.d.ts +42 -0
  27. package/dist/cliproxy/base-config-loader.d.ts.map +1 -0
  28. package/dist/cliproxy/base-config-loader.js +123 -0
  29. package/dist/cliproxy/base-config-loader.js.map +1 -0
  30. package/dist/cliproxy/binary-manager.d.ts +104 -0
  31. package/dist/cliproxy/binary-manager.d.ts.map +1 -0
  32. package/dist/cliproxy/binary-manager.js +567 -0
  33. package/dist/cliproxy/binary-manager.js.map +1 -0
  34. package/dist/cliproxy/cliproxy-executor.d.ts +33 -0
  35. package/dist/cliproxy/cliproxy-executor.d.ts.map +1 -0
  36. package/dist/cliproxy/cliproxy-executor.js +297 -0
  37. package/dist/cliproxy/cliproxy-executor.js.map +1 -0
  38. package/dist/cliproxy/config-generator.d.ts +89 -0
  39. package/dist/cliproxy/config-generator.d.ts.map +1 -0
  40. package/dist/cliproxy/config-generator.js +263 -0
  41. package/dist/cliproxy/config-generator.js.map +1 -0
  42. package/dist/cliproxy/index.d.ts +13 -0
  43. package/dist/cliproxy/index.d.ts.map +1 -0
  44. package/dist/cliproxy/index.js +62 -0
  45. package/dist/cliproxy/index.js.map +1 -0
  46. package/dist/cliproxy/platform-detector.d.ts +48 -0
  47. package/dist/cliproxy/platform-detector.d.ts.map +1 -0
  48. package/dist/cliproxy/platform-detector.js +118 -0
  49. package/dist/cliproxy/platform-detector.js.map +1 -0
  50. package/dist/cliproxy/types.d.ts +169 -0
  51. package/dist/cliproxy/types.d.ts.map +1 -0
  52. package/dist/cliproxy/types.js +7 -0
  53. package/dist/cliproxy/types.js.map +1 -0
  54. package/dist/commands/doctor-command.d.ts +10 -0
  55. package/dist/commands/doctor-command.d.ts.map +1 -0
  56. package/dist/commands/doctor-command.js +44 -0
  57. package/dist/commands/doctor-command.js.map +1 -0
  58. package/dist/commands/help-command.d.ts +5 -0
  59. package/dist/commands/help-command.d.ts.map +1 -0
  60. package/dist/commands/help-command.js +104 -0
  61. package/dist/commands/help-command.js.map +1 -0
  62. package/dist/commands/install-command.d.ts +14 -0
  63. package/dist/commands/install-command.d.ts.map +1 -0
  64. package/dist/commands/install-command.js +39 -0
  65. package/dist/commands/install-command.js.map +1 -0
  66. package/dist/commands/shell-completion-command.d.ts +10 -0
  67. package/dist/commands/shell-completion-command.d.ts.map +1 -0
  68. package/dist/commands/shell-completion-command.js +85 -0
  69. package/dist/commands/shell-completion-command.js.map +1 -0
  70. package/dist/commands/sync-command.d.ts +10 -0
  71. package/dist/commands/sync-command.d.ts.map +1 -0
  72. package/dist/commands/sync-command.js +59 -0
  73. package/dist/commands/sync-command.js.map +1 -0
  74. package/dist/commands/update-command.d.ts +12 -0
  75. package/dist/commands/update-command.d.ts.map +1 -0
  76. package/dist/commands/update-command.js +295 -0
  77. package/dist/commands/update-command.js.map +1 -0
  78. package/dist/commands/version-command.d.ts +10 -0
  79. package/dist/commands/version-command.d.ts.map +1 -0
  80. package/dist/commands/version-command.js +100 -0
  81. package/dist/commands/version-command.js.map +1 -0
  82. package/dist/delegation/delegation-handler.d.ts +60 -0
  83. package/dist/delegation/delegation-handler.d.ts.map +1 -0
  84. package/dist/delegation/delegation-handler.js +174 -0
  85. package/dist/delegation/delegation-handler.js.map +1 -0
  86. package/dist/delegation/headless-executor.d.ts +114 -0
  87. package/dist/delegation/headless-executor.d.ts.map +1 -0
  88. package/dist/delegation/headless-executor.js +562 -0
  89. package/dist/delegation/headless-executor.js.map +1 -0
  90. package/dist/delegation/result-formatter.d.ts +108 -0
  91. package/dist/delegation/result-formatter.d.ts.map +1 -0
  92. package/dist/delegation/result-formatter.js +391 -0
  93. package/dist/delegation/result-formatter.js.map +1 -0
  94. package/dist/delegation/session-manager.d.ts +58 -0
  95. package/dist/delegation/session-manager.d.ts.map +1 -0
  96. package/dist/delegation/session-manager.js +153 -0
  97. package/dist/delegation/session-manager.js.map +1 -0
  98. package/dist/delegation/settings-parser.d.ts +31 -0
  99. package/dist/delegation/settings-parser.d.ts.map +1 -0
  100. package/dist/delegation/settings-parser.js +107 -0
  101. package/dist/delegation/settings-parser.js.map +1 -0
  102. package/dist/glmt/delta-accumulator.d.ts +210 -0
  103. package/dist/glmt/delta-accumulator.d.ts.map +1 -0
  104. package/dist/glmt/delta-accumulator.js +351 -0
  105. package/dist/glmt/delta-accumulator.js.map +1 -0
  106. package/dist/glmt/glmt-proxy.d.ts +72 -0
  107. package/dist/glmt/glmt-proxy.d.ts.map +1 -0
  108. package/dist/glmt/glmt-proxy.js +427 -0
  109. package/dist/glmt/glmt-proxy.js.map +1 -0
  110. package/dist/glmt/glmt-transformer.d.ts +265 -0
  111. package/dist/glmt/glmt-transformer.d.ts.map +1 -0
  112. package/dist/glmt/glmt-transformer.js +832 -0
  113. package/dist/glmt/glmt-transformer.js.map +1 -0
  114. package/dist/glmt/locale-enforcer.d.ts +38 -0
  115. package/dist/glmt/locale-enforcer.d.ts.map +1 -0
  116. package/dist/glmt/locale-enforcer.js +69 -0
  117. package/dist/glmt/locale-enforcer.js.map +1 -0
  118. package/dist/glmt/reasoning-enforcer.d.ts +52 -0
  119. package/dist/glmt/reasoning-enforcer.d.ts.map +1 -0
  120. package/dist/glmt/reasoning-enforcer.js +151 -0
  121. package/dist/glmt/reasoning-enforcer.js.map +1 -0
  122. package/dist/glmt/sse-parser.d.ts +47 -0
  123. package/dist/glmt/sse-parser.d.ts.map +1 -0
  124. package/dist/glmt/sse-parser.js +93 -0
  125. package/dist/glmt/sse-parser.js.map +1 -0
  126. package/dist/management/doctor.d.ts +104 -0
  127. package/dist/management/doctor.d.ts.map +1 -0
  128. package/dist/management/doctor.js +673 -0
  129. package/dist/management/doctor.js.map +1 -0
  130. package/dist/management/instance-manager.d.ts +57 -0
  131. package/dist/management/instance-manager.d.ts.map +1 -0
  132. package/dist/management/instance-manager.js +195 -0
  133. package/dist/management/instance-manager.js.map +1 -0
  134. package/dist/management/recovery-manager.d.ts +39 -0
  135. package/dist/management/recovery-manager.d.ts.map +1 -0
  136. package/dist/management/recovery-manager.js +141 -0
  137. package/dist/management/recovery-manager.js.map +1 -0
  138. package/dist/management/shared-manager.d.ts +47 -0
  139. package/dist/management/shared-manager.d.ts.map +1 -0
  140. package/dist/management/shared-manager.js +388 -0
  141. package/dist/management/shared-manager.js.map +1 -0
  142. package/dist/types/cli.d.ts +50 -0
  143. package/dist/types/cli.d.ts.map +1 -0
  144. package/dist/types/cli.js +16 -0
  145. package/dist/types/cli.js.map +1 -0
  146. package/dist/types/config.d.ts +51 -0
  147. package/dist/types/config.d.ts.map +1 -0
  148. package/dist/types/config.js +26 -0
  149. package/dist/types/config.js.map +1 -0
  150. package/dist/types/delegation.d.ts +61 -0
  151. package/dist/types/delegation.d.ts.map +1 -0
  152. package/dist/types/delegation.js +6 -0
  153. package/dist/types/delegation.js.map +1 -0
  154. package/dist/types/glmt.d.ts +95 -0
  155. package/dist/types/glmt.d.ts.map +1 -0
  156. package/dist/types/glmt.js +7 -0
  157. package/dist/types/glmt.js.map +1 -0
  158. package/dist/types/index.d.ts +13 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/index.js +16 -0
  161. package/dist/types/index.js.map +1 -0
  162. package/dist/types/utils.d.ts +36 -0
  163. package/dist/types/utils.d.ts.map +1 -0
  164. package/dist/types/utils.js +22 -0
  165. package/dist/types/utils.js.map +1 -0
  166. package/dist/utils/claude-detector.d.ts +14 -0
  167. package/dist/utils/claude-detector.d.ts.map +1 -0
  168. package/dist/utils/claude-detector.js +112 -0
  169. package/dist/utils/claude-detector.js.map +1 -0
  170. package/dist/utils/claude-dir-installer.d.ts +46 -0
  171. package/dist/utils/claude-dir-installer.d.ts.map +1 -0
  172. package/dist/utils/claude-dir-installer.js +289 -0
  173. package/dist/utils/claude-dir-installer.js.map +1 -0
  174. package/dist/utils/claude-symlink-manager.d.ts +61 -0
  175. package/dist/utils/claude-symlink-manager.d.ts.map +1 -0
  176. package/dist/utils/claude-symlink-manager.js +291 -0
  177. package/dist/utils/claude-symlink-manager.js.map +1 -0
  178. package/dist/utils/config-manager.d.ts +32 -0
  179. package/dist/utils/config-manager.d.ts.map +1 -0
  180. package/dist/utils/config-manager.js +143 -0
  181. package/dist/utils/config-manager.js.map +1 -0
  182. package/dist/utils/delegation-validator.d.ts +39 -0
  183. package/dist/utils/delegation-validator.d.ts.map +1 -0
  184. package/dist/utils/delegation-validator.js +161 -0
  185. package/dist/utils/delegation-validator.js.map +1 -0
  186. package/dist/utils/error-codes.d.ts +36 -0
  187. package/dist/utils/error-codes.d.ts.map +1 -0
  188. package/dist/utils/error-codes.js +63 -0
  189. package/dist/utils/error-codes.js.map +1 -0
  190. package/dist/utils/error-manager.d.ts +59 -0
  191. package/dist/utils/error-manager.d.ts.map +1 -0
  192. package/dist/utils/error-manager.js +228 -0
  193. package/dist/utils/error-manager.js.map +1 -0
  194. package/dist/utils/helpers.d.ts +27 -0
  195. package/dist/utils/helpers.d.ts.map +1 -0
  196. package/dist/utils/helpers.js +150 -0
  197. package/dist/utils/helpers.js.map +1 -0
  198. package/dist/utils/package-manager-detector.d.ts +14 -0
  199. package/dist/utils/package-manager-detector.d.ts.map +1 -0
  200. package/dist/utils/package-manager-detector.js +162 -0
  201. package/dist/utils/package-manager-detector.js.map +1 -0
  202. package/dist/utils/progress-indicator.d.ts +52 -0
  203. package/dist/utils/progress-indicator.d.ts.map +1 -0
  204. package/dist/utils/progress-indicator.js +102 -0
  205. package/dist/utils/progress-indicator.js.map +1 -0
  206. package/dist/utils/prompt.d.ts +29 -0
  207. package/dist/utils/prompt.d.ts.map +1 -0
  208. package/dist/utils/prompt.js +116 -0
  209. package/dist/utils/prompt.js.map +1 -0
  210. package/dist/utils/shell-completion.d.ts +52 -0
  211. package/dist/utils/shell-completion.d.ts.map +1 -0
  212. package/dist/utils/shell-completion.js +231 -0
  213. package/dist/utils/shell-completion.js.map +1 -0
  214. package/dist/utils/shell-executor.d.ts +15 -0
  215. package/dist/utils/shell-executor.d.ts.map +1 -0
  216. package/dist/utils/shell-executor.js +57 -0
  217. package/dist/utils/shell-executor.js.map +1 -0
  218. package/dist/utils/update-checker.d.ts +48 -0
  219. package/dist/utils/update-checker.d.ts.map +1 -0
  220. package/dist/utils/update-checker.js +241 -0
  221. package/dist/utils/update-checker.js.map +1 -0
  222. package/lib/ccs +21 -1907
  223. package/lib/ccs.ps1 +26 -1800
  224. package/lib/error-codes.ps1 +2 -1
  225. package/lib/prompt.ps1 +2 -2
  226. package/package.json +31 -11
  227. package/scripts/add-shebang.js +39 -0
  228. package/scripts/bump-version.sh +25 -37
  229. package/scripts/dev-install.sh +32 -11
  230. package/scripts/postinstall.js +29 -29
  231. package/bin/auth/auth-commands.js +0 -499
  232. package/bin/auth/profile-detector.js +0 -204
  233. package/bin/auth/profile-registry.js +0 -225
  234. package/bin/ccs.js +0 -1034
  235. package/bin/delegation/README.md +0 -191
  236. package/bin/delegation/delegation-handler.js +0 -212
  237. package/bin/delegation/headless-executor.js +0 -618
  238. package/bin/delegation/result-formatter.js +0 -485
  239. package/bin/delegation/session-manager.js +0 -157
  240. package/bin/delegation/settings-parser.js +0 -109
  241. package/bin/glmt/delta-accumulator.js +0 -276
  242. package/bin/glmt/glmt-proxy.js +0 -495
  243. package/bin/glmt/glmt-transformer.js +0 -999
  244. package/bin/glmt/locale-enforcer.js +0 -72
  245. package/bin/glmt/reasoning-enforcer.js +0 -173
  246. package/bin/glmt/sse-parser.js +0 -96
  247. package/bin/management/doctor.js +0 -721
  248. package/bin/management/instance-manager.js +0 -202
  249. package/bin/management/recovery-manager.js +0 -135
  250. package/bin/management/shared-manager.js +0 -402
  251. package/bin/utils/claude-detector.js +0 -73
  252. package/bin/utils/claude-dir-installer.js +0 -283
  253. package/bin/utils/claude-symlink-manager.js +0 -289
  254. package/bin/utils/config-manager.js +0 -103
  255. package/bin/utils/delegation-validator.js +0 -154
  256. package/bin/utils/error-codes.js +0 -59
  257. package/bin/utils/error-manager.js +0 -165
  258. package/bin/utils/helpers.js +0 -136
  259. package/bin/utils/progress-indicator.js +0 -111
  260. package/bin/utils/prompt.js +0 -134
  261. package/bin/utils/shell-completion.js +0 -256
  262. package/bin/utils/update-checker.js +0 -243
package/bin/ccs.js DELETED
@@ -1,1034 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const { spawn } = require('child_process');
5
- const path = require('path');
6
- const fs = require('fs');
7
- const os = require('os');
8
- const { error, colored } = require('./utils/helpers');
9
- const { detectClaudeCli, showClaudeNotFoundError } = require('./utils/claude-detector');
10
- const { getSettingsPath, getConfigPath } = require('./utils/config-manager');
11
- const { ErrorManager } = require('./utils/error-manager');
12
- const RecoveryManager = require('./management/recovery-manager');
13
-
14
- // Version (sync with package.json)
15
- const CCS_VERSION = require('../package.json').version;
16
-
17
- // Helper: Escape arguments for shell execution
18
- function escapeShellArg(arg) {
19
- // Windows: escape double quotes and wrap in double quotes
20
- return '"' + String(arg).replace(/"/g, '""') + '"';
21
- }
22
-
23
- // Execute Claude CLI with unified spawn logic
24
- function execClaude(claudeCli, args, envVars = null) {
25
- const isWindows = process.platform === 'win32';
26
- const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudeCli);
27
-
28
- // Prepare environment (merge with process.env if envVars provided)
29
- const env = envVars ? { ...process.env, ...envVars } : process.env;
30
-
31
- let child;
32
- if (needsShell) {
33
- // When shell needed: concatenate into string to avoid DEP0190 warning
34
- const cmdString = [claudeCli, ...args].map(escapeShellArg).join(' ');
35
- child = spawn(cmdString, {
36
- stdio: 'inherit',
37
- windowsHide: true,
38
- shell: true,
39
- env
40
- });
41
- } else {
42
- // When no shell needed: use array form (faster, no shell overhead)
43
- child = spawn(claudeCli, args, {
44
- stdio: 'inherit',
45
- windowsHide: true,
46
- env
47
- });
48
- }
49
-
50
- child.on('exit', (code, signal) => {
51
- if (signal) process.kill(process.pid, signal);
52
- else process.exit(code || 0);
53
- });
54
- child.on('error', () => {
55
- showClaudeNotFoundError();
56
- process.exit(1);
57
- });
58
- }
59
-
60
- // Special command handlers
61
- function handleVersionCommand() {
62
- // Title
63
- console.log(colored(`CCS (Claude Code Switch) v${CCS_VERSION}`, 'bold'));
64
- console.log('');
65
-
66
- // Installation section with table-like formatting
67
- console.log(colored('Installation:', 'cyan'));
68
-
69
- // Location
70
- const installLocation = process.argv[1] || '(not found)';
71
- console.log(` ${colored('Location:'.padEnd(17), 'cyan')} ${installLocation}`);
72
-
73
- // .ccs/ directory location
74
- const ccsDir = path.join(os.homedir(), '.ccs');
75
- console.log(` ${colored('CCS Directory:'.padEnd(17), 'cyan')} ${ccsDir}`);
76
-
77
- // Config path
78
- const configPath = getConfigPath();
79
- console.log(` ${colored('Config:'.padEnd(17), 'cyan')} ${configPath}`);
80
-
81
- // Profiles.json location
82
- const profilesJson = path.join(os.homedir(), '.ccs', 'profiles.json');
83
- console.log(` ${colored('Profiles:'.padEnd(17), 'cyan')} ${profilesJson}`);
84
-
85
- // Delegation status - check multiple indicators
86
- const delegationSessionsPath = path.join(os.homedir(), '.ccs', 'delegation-sessions.json');
87
- const delegationConfigured = fs.existsSync(delegationSessionsPath);
88
-
89
- let readyProfiles = [];
90
-
91
- // Check for profiles with valid API keys
92
- for (const profile of ['glm', 'kimi']) {
93
- const settingsPath = path.join(os.homedir(), '.ccs', `${profile}.settings.json`);
94
- if (fs.existsSync(settingsPath)) {
95
- try {
96
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
97
- const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
98
- if (apiKey && !apiKey.match(/YOUR_.*_API_KEY_HERE/) && !apiKey.match(/sk-test.*/)) {
99
- readyProfiles.push(profile);
100
- }
101
- } catch (error) {
102
- // Invalid JSON, skip
103
- }
104
- }
105
- }
106
-
107
- const hasValidApiKeys = readyProfiles.length > 0;
108
- const delegationEnabled = delegationConfigured || hasValidApiKeys;
109
-
110
- if (delegationEnabled) {
111
- console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Enabled`);
112
- } else {
113
- console.log(` ${colored('Delegation:'.padEnd(17), 'cyan')} Not configured`);
114
- }
115
-
116
- console.log('');
117
-
118
- // Ready Profiles section - make it more prominent
119
- if (readyProfiles.length > 0) {
120
- console.log(colored('Delegation Ready:', 'cyan'));
121
- console.log(` ${colored('✓', 'yellow')} ${readyProfiles.join(', ')} profiles are ready for delegation`);
122
- console.log('');
123
- } else if (delegationEnabled) {
124
- console.log(colored('Delegation Ready:', 'cyan'));
125
- console.log(` ${colored('!', 'yellow')} Delegation configured but no valid API keys found`);
126
- console.log('');
127
- }
128
-
129
- // Documentation
130
- console.log(`${colored('Documentation:', 'cyan')} https://github.com/kaitranntt/ccs`);
131
- console.log(`${colored('License:', 'cyan')} MIT`);
132
- console.log('');
133
-
134
- // Help hint
135
- console.log(colored('Run \'ccs --help\' for usage information', 'yellow'));
136
-
137
- process.exit(0);
138
- }
139
-
140
- function handleHelpCommand() {
141
- // Title
142
- console.log(colored('CCS (Claude Code Switch) - Instant profile switching for Claude CLI', 'bold'));
143
- console.log('');
144
-
145
- // Usage
146
- console.log(colored('Usage:', 'cyan'));
147
- console.log(` ${colored('ccs', 'yellow')} [profile] [claude-args...]`);
148
- console.log(` ${colored('ccs', 'yellow')} [flags]`);
149
- console.log('');
150
-
151
- // Description
152
- console.log(colored('Description:', 'cyan'));
153
- console.log(' Switch between multiple Claude accounts and alternative models');
154
- console.log(' (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently');
155
- console.log(' with auto-recovery. Zero downtime.');
156
- console.log('');
157
-
158
- // Model Switching
159
- console.log(colored('Model Switching:', 'cyan'));
160
- console.log(` ${colored('ccs', 'yellow')} Use default Claude account`);
161
- console.log(` ${colored('ccs glm', 'yellow')} Switch to GLM 4.6 model`);
162
- console.log(` ${colored('ccs glmt', 'yellow')} Switch to GLM with thinking mode`);
163
- console.log(` ${colored('ccs glmt --verbose', 'yellow')} Enable debug logging`);
164
- console.log(` ${colored('ccs kimi', 'yellow')} Switch to Kimi for Coding`);
165
- console.log(` ${colored('ccs glm', 'yellow')} "debug this code" Use GLM and run command`);
166
- console.log('');
167
-
168
- // Account Management
169
- console.log(colored('Account Management:', 'cyan'));
170
- console.log(` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently`);
171
- console.log('');
172
-
173
- // Delegation (inside Claude Code CLI)
174
- console.log(colored('Delegation (inside Claude Code CLI):', 'cyan'));
175
- console.log(` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects best profile)`);
176
- console.log(` ${colored('/ccs --glm "task"', 'yellow')} Force GLM-4.6 for simple tasks`);
177
- console.log(` ${colored('/ccs --kimi "task"', 'yellow')} Force Kimi for long context`);
178
- console.log(` ${colored('/ccs:continue "follow-up"', 'yellow')} Continue last delegation session`);
179
- console.log(' Save tokens by delegating simple tasks to cost-optimized models');
180
- console.log('');
181
-
182
- // Diagnostics
183
- console.log(colored('Diagnostics:', 'cyan'));
184
- console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`);
185
- console.log(` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills`);
186
- console.log(` ${colored('ccs update', 'yellow')} Update CCS to latest version`);
187
- console.log('');
188
-
189
- // Flags
190
- console.log(colored('Flags:', 'cyan'));
191
- console.log(` ${colored('-h, --help', 'yellow')} Show this help message`);
192
- console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`);
193
- console.log(` ${colored('-sc, --shell-completion', 'yellow')} Install shell auto-completion`);
194
- console.log('');
195
-
196
- // Configuration
197
- console.log(colored('Configuration:', 'cyan'));
198
- console.log(' Config File: ~/.ccs/config.json');
199
- console.log(' Profiles: ~/.ccs/profiles.json');
200
- console.log(' Instances: ~/.ccs/instances/');
201
- console.log(' Settings: ~/.ccs/*.settings.json');
202
- console.log(' Environment: CCS_CONFIG (override config path)');
203
- console.log('');
204
-
205
- // Shared Data
206
- console.log(colored('Shared Data:', 'cyan'));
207
- console.log(' Commands: ~/.ccs/shared/commands/');
208
- console.log(' Skills: ~/.ccs/shared/skills/');
209
- console.log(' Agents: ~/.ccs/shared/agents/');
210
- console.log(' Plugins: ~/.ccs/shared/plugins/');
211
- console.log(' Note: Commands, skills, agents, and plugins are symlinked across all profiles');
212
- console.log('');
213
-
214
- // Examples
215
- console.log(colored('Examples:', 'cyan'));
216
- console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
217
- console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
218
- console.log('');
219
- console.log(` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}`);
220
- console.log('');
221
-
222
- // Uninstall
223
- console.log(colored('Uninstall:', 'yellow'));
224
- console.log(' npm: npm uninstall -g @kaitranntt/ccs');
225
- console.log(' macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash');
226
- console.log(' Windows: irm ccs.kaitran.ca/uninstall | iex');
227
- console.log('');
228
-
229
- // Documentation
230
- console.log(colored('Documentation:', 'cyan'));
231
- console.log(` GitHub: ${colored('https://github.com/kaitranntt/ccs', 'cyan')}`);
232
- console.log(' Docs: https://github.com/kaitranntt/ccs/blob/main/README.md');
233
- console.log(' Issues: https://github.com/kaitranntt/ccs/issues');
234
- console.log('');
235
-
236
- // License
237
- console.log(`${colored('License:', 'cyan')} MIT`);
238
-
239
- process.exit(0);
240
- }
241
-
242
- function handleInstallCommand() {
243
- console.log('');
244
- console.log('Feature not available');
245
- console.log('');
246
- console.log('The --install flag is currently under development.');
247
- console.log('.claude/ integration testing is not complete.');
248
- console.log('');
249
- console.log('For updates: https://github.com/kaitranntt/ccs/issues');
250
- console.log('');
251
- process.exit(0);
252
- }
253
-
254
- function handleUninstallCommand() {
255
- console.log('');
256
- console.log('Feature not available');
257
- console.log('');
258
- console.log('The --uninstall flag is currently under development.');
259
- console.log('.claude/ integration testing is not complete.');
260
- console.log('');
261
- console.log('For updates: https://github.com/kaitranntt/ccs/issues');
262
- console.log('');
263
- process.exit(0);
264
- }
265
-
266
- async function handleDoctorCommand() {
267
- const Doctor = require('./management/doctor');
268
- const doctor = new Doctor();
269
-
270
- await doctor.runAllChecks();
271
-
272
- // Exit with error code if unhealthy
273
- process.exit(doctor.results.isHealthy() ? 0 : 1);
274
- }
275
-
276
- async function handleSyncCommand() {
277
- const { colored } = require('./utils/helpers');
278
-
279
- console.log('');
280
- console.log(colored('Syncing CCS Components...', 'cyan'));
281
- console.log('');
282
-
283
- // First, copy .claude/ directory from package to ~/.ccs/.claude/
284
- const ClaudeDirInstaller = require('./utils/claude-dir-installer');
285
- const installer = new ClaudeDirInstaller();
286
- installer.install();
287
-
288
- console.log('');
289
-
290
- const cleanupResult = installer.cleanupDeprecated();
291
- if (cleanupResult.success && cleanupResult.cleanedFiles.length > 0) {
292
- console.log('');
293
- }
294
-
295
- // Then, create symlinks from ~/.ccs/.claude/ to ~/.claude/
296
- const ClaudeSymlinkManager = require('./utils/claude-symlink-manager');
297
- const manager = new ClaudeSymlinkManager();
298
- manager.install(false);
299
-
300
- console.log('');
301
- console.log(colored('[OK] Sync complete!', 'green'));
302
- console.log('');
303
-
304
- process.exit(0);
305
- }
306
-
307
- /**
308
- * Detect installation method
309
- * @returns {'npm'|'direct'} - Installation method
310
- */
311
- function detectInstallationMethod() {
312
- const scriptPath = process.argv[1];
313
-
314
- // Method 1: Check if script is inside node_modules
315
- if (scriptPath.includes('node_modules')) {
316
- return 'npm';
317
- }
318
-
319
- // Method 2: Check if script is in npm global bin directory
320
- // Common patterns for npm global installations
321
- const npmGlobalBinPatterns = [
322
- /\.npm\/global\/bin\//, // ~/.npm/global/bin/ccs
323
- /\/\.nvm\/versions\/node\/[^\/]+\/bin\//, // ~/.nvm/versions/node/v22.19.0/bin/ccs
324
- /\/usr\/local\/bin\//, // /usr/local/bin/ccs (if npm global prefix is /usr/local)
325
- /\/usr\/bin\// // /usr/bin/ccs (if npm global prefix is /usr)
326
- ];
327
-
328
- for (const pattern of npmGlobalBinPatterns) {
329
- if (pattern.test(scriptPath)) {
330
- // Verify this is actually CCS by checking the linked target
331
- try {
332
- const binDir = path.dirname(scriptPath);
333
- const nodeModulesDir = path.join(binDir, '..', 'lib', 'node_modules', '@kaitranntt', 'ccs');
334
- const globalModulesDir = path.join(binDir, '..', 'node_modules', '@kaitranntt', 'ccs');
335
-
336
- if (fs.existsSync(nodeModulesDir) || fs.existsSync(globalModulesDir)) {
337
- return 'npm';
338
- }
339
- } catch (err) {
340
- // Continue checking other patterns
341
- }
342
- }
343
- }
344
-
345
- // Method 3: Check if package.json exists in parent directory (development mode)
346
- const packageJsonPath = path.join(__dirname, '..', 'package.json');
347
-
348
- if (fs.existsSync(packageJsonPath)) {
349
- try {
350
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
351
- // If package.json has name "@kaitranntt/ccs", it's npm install
352
- if (pkg.name === '@kaitranntt/ccs') {
353
- return 'npm';
354
- }
355
- } catch (err) {
356
- // Ignore parse errors
357
- }
358
- }
359
-
360
- // Method 4: Check if script is a symlink pointing to node_modules
361
- try {
362
- const stats = fs.lstatSync(scriptPath);
363
- if (stats.isSymbolicLink()) {
364
- const targetPath = fs.readlinkSync(scriptPath);
365
- if (targetPath.includes('node_modules') || targetPath.includes('@kaitranntt/ccs')) {
366
- return 'npm';
367
- }
368
- }
369
- } catch (err) {
370
- // Continue to default
371
- }
372
-
373
- // Default to direct installation
374
- return 'direct';
375
- }
376
-
377
- /**
378
- * Detect which package manager was used for installation
379
- * @returns {'npm'|'yarn'|'pnpm'|'bun'|'unknown'}
380
- */
381
- function detectPackageManager() {
382
- const scriptPath = process.argv[1];
383
-
384
- // Check if script path contains package manager indicators
385
- if (scriptPath.includes('.pnpm')) return 'pnpm';
386
- if (scriptPath.includes('yarn')) return 'yarn';
387
- if (scriptPath.includes('bun')) return 'bun';
388
-
389
- // Check parent directories for lock files
390
- const binDir = path.dirname(scriptPath);
391
- const fs = require('fs');
392
-
393
- // Check global node_modules parent for lock files
394
- let checkDir = binDir;
395
- for (let i = 0; i < 5; i++) {
396
- if (fs.existsSync(path.join(checkDir, 'pnpm-lock.yaml'))) return 'pnpm';
397
- if (fs.existsSync(path.join(checkDir, 'yarn.lock'))) return 'yarn';
398
- if (fs.existsSync(path.join(checkDir, 'bun.lockb'))) return 'bun';
399
- checkDir = path.dirname(checkDir);
400
- }
401
-
402
- // Check if package managers are available on the system
403
- const { spawnSync } = require('child_process');
404
-
405
- // Try yarn global list to see if CCS is installed via yarn
406
- try {
407
- const yarnResult = spawnSync('yarn', ['global', 'list', '--pattern', '@kaitranntt/ccs'], {
408
- encoding: 'utf8',
409
- shell: true,
410
- timeout: 5000
411
- });
412
- if (yarnResult.status === 0 && yarnResult.stdout.includes('@kaitranntt/ccs')) {
413
- return 'yarn';
414
- }
415
- } catch (err) {
416
- // Continue to next check
417
- }
418
-
419
- // Try pnpm list -g to see if CCS is installed via pnpm
420
- try {
421
- const pnpmResult = spawnSync('pnpm', ['list', '-g', '--pattern', '@kaitranntt/ccs'], {
422
- encoding: 'utf8',
423
- shell: true,
424
- timeout: 5000
425
- });
426
- if (pnpmResult.status === 0 && pnpmResult.stdout.includes('@kaitranntt/ccs')) {
427
- return 'pnpm';
428
- }
429
- } catch (err) {
430
- // Continue to next check
431
- }
432
-
433
- // Try bun pm ls -g to see if CCS is installed via bun
434
- try {
435
- const bunResult = spawnSync('bun', ['pm', 'ls', '-g', '--pattern', '@kaitranntt/ccs'], {
436
- encoding: 'utf8',
437
- shell: true,
438
- timeout: 5000
439
- });
440
- if (bunResult.status === 0 && bunResult.stdout.includes('@kaitranntt/ccs')) {
441
- return 'bun';
442
- }
443
- } catch (err) {
444
- // Continue to default
445
- }
446
-
447
- // Default to npm
448
- return 'npm';
449
- }
450
-
451
- async function handleUpdateCommand() {
452
- const { checkForUpdates } = require('./utils/update-checker');
453
- const { spawn } = require('child_process');
454
-
455
- console.log('');
456
- console.log(colored('Checking for updates...', 'cyan'));
457
- console.log('');
458
-
459
- // Detect installation method for proper update source
460
- const installMethod = detectInstallationMethod();
461
- const isNpmInstall = installMethod === 'npm';
462
-
463
- // Check for updates (force check)
464
- const updateResult = await checkForUpdates(CCS_VERSION, true, installMethod);
465
-
466
- if (updateResult.status === 'check_failed') {
467
- console.log(colored(`[X] ${updateResult.message}`, 'red'));
468
- console.log('');
469
- console.log(colored('[i] Possible causes:', 'yellow'));
470
- console.log(' • Network connection issues');
471
- console.log(' • Firewall blocking requests');
472
- console.log(' • GitHub/npm API temporarily unavailable');
473
- console.log('');
474
- console.log('Try again later or update manually:');
475
- if (isNpmInstall) {
476
- const packageManager = detectPackageManager();
477
- let manualCommand;
478
-
479
- switch (packageManager) {
480
- case 'npm':
481
- manualCommand = 'npm install -g @kaitranntt/ccs@latest';
482
- break;
483
- case 'yarn':
484
- manualCommand = 'yarn global add @kaitranntt/ccs@latest';
485
- break;
486
- case 'pnpm':
487
- manualCommand = 'pnpm add -g @kaitranntt/ccs@latest';
488
- break;
489
- case 'bun':
490
- manualCommand = 'bun add -g @kaitranntt/ccs@latest';
491
- break;
492
- default:
493
- manualCommand = 'npm install -g @kaitranntt/ccs@latest';
494
- }
495
-
496
- console.log(colored(` ${manualCommand}`, 'yellow'));
497
- } else {
498
- const isWindows = process.platform === 'win32';
499
- if (isWindows) {
500
- console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
501
- } else {
502
- console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
503
- }
504
- }
505
- console.log('');
506
- process.exit(1);
507
- }
508
-
509
- if (updateResult.status === 'no_update') {
510
- let message = `You are already on the latest version (${CCS_VERSION})`;
511
-
512
- // Add context for why no update is shown
513
- switch (updateResult.reason) {
514
- case 'dismissed':
515
- message = `Update dismissed. You are on version ${CCS_VERSION}`;
516
- console.log(colored(`[i] ${message}`, 'yellow'));
517
- break;
518
- case 'cached':
519
- message = `No updates available (cached result). You are on version ${CCS_VERSION}`;
520
- console.log(colored(`[i] ${message}`, 'cyan'));
521
- break;
522
- default:
523
- console.log(colored(`[OK] ${message}`, 'green'));
524
- }
525
- console.log('');
526
- process.exit(0);
527
- }
528
-
529
- // Update available
530
- console.log(colored(`[i] Update available: ${updateResult.current} → ${updateResult.latest}`, 'yellow'));
531
- console.log('');
532
-
533
- if (isNpmInstall) {
534
- // npm installation - detect package manager and update
535
- const packageManager = detectPackageManager();
536
- let updateCommand, updateArgs, cacheCommand, cacheArgs;
537
-
538
- switch (packageManager) {
539
- case 'npm':
540
- updateCommand = 'npm';
541
- updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
542
- cacheCommand = 'npm';
543
- cacheArgs = ['cache', 'clean', '--force'];
544
- break;
545
- case 'yarn':
546
- updateCommand = 'yarn';
547
- updateArgs = ['global', 'add', '@kaitranntt/ccs@latest'];
548
- cacheCommand = 'yarn';
549
- cacheArgs = ['cache', 'clean'];
550
- break;
551
- case 'pnpm':
552
- updateCommand = 'pnpm';
553
- updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
554
- cacheCommand = 'pnpm';
555
- cacheArgs = ['store', 'prune'];
556
- break;
557
- case 'bun':
558
- updateCommand = 'bun';
559
- updateArgs = ['add', '-g', '@kaitranntt/ccs@latest'];
560
- cacheCommand = null; // bun doesn't need explicit cache clearing
561
- cacheArgs = null;
562
- break;
563
- default:
564
- updateCommand = 'npm';
565
- updateArgs = ['install', '-g', '@kaitranntt/ccs@latest'];
566
- cacheCommand = 'npm';
567
- cacheArgs = ['cache', 'clean', '--force'];
568
- }
569
-
570
- console.log(colored(`Updating via ${packageManager}...`, 'cyan'));
571
- console.log('');
572
-
573
- // Clear package manager cache first to ensure fresh download
574
- if (cacheCommand) {
575
- console.log(colored('Clearing package cache...', 'cyan'));
576
- const cacheChild = spawn(cacheCommand, cacheArgs, {
577
- stdio: 'inherit'
578
- });
579
-
580
- cacheChild.on('exit', (code) => {
581
- if (code !== 0) {
582
- console.log(colored('[!] Cache clearing failed, proceeding anyway...', 'yellow'));
583
- }
584
- // Continue with update regardless of cache clearing result
585
- performUpdate();
586
- });
587
-
588
- cacheChild.on('error', (err) => {
589
- console.log(colored('[!] Cache clearing failed, proceeding anyway...', 'yellow'));
590
- // Continue with update regardless of cache clearing result
591
- performUpdate();
592
- });
593
- } else {
594
- // No cache clearing needed, proceed directly
595
- performUpdate();
596
- }
597
-
598
- function performUpdate() {
599
- const child = spawn(updateCommand, updateArgs, {
600
- stdio: 'inherit'
601
- // No shell needed for direct commands
602
- });
603
-
604
- child.on('exit', (code) => {
605
- if (code === 0) {
606
- console.log('');
607
- console.log(colored('[OK] Update successful!', 'green'));
608
- console.log('');
609
- console.log(`Run ${colored('ccs --version', 'yellow')} to verify`);
610
- console.log('');
611
- } else {
612
- console.log('');
613
- console.log(colored('[X] Update failed', 'red'));
614
- console.log('');
615
- console.log('Try manually:');
616
- console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
617
- console.log('');
618
- }
619
- process.exit(code || 0);
620
- });
621
-
622
- child.on('error', (err) => {
623
- console.log('');
624
- console.log(colored(`[X] Failed to run ${packageManager} update`, 'red'));
625
- console.log('');
626
- console.log('Try manually:');
627
- console.log(colored(` ${updateCommand} ${updateArgs.join(' ')}`, 'yellow'));
628
- console.log('');
629
- process.exit(1);
630
- });
631
- }
632
- } else {
633
- // Direct installation - re-run installer
634
- console.log(colored('Updating via installer...', 'cyan'));
635
- console.log('');
636
-
637
- const isWindows = process.platform === 'win32';
638
- let command, args;
639
-
640
- if (isWindows) {
641
- // PowerShell
642
- command = 'powershell.exe';
643
- args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command',
644
- 'irm ccs.kaitran.ca/install | iex'];
645
- } else {
646
- // Unix (bash with proper shell invocation)
647
- command = '/bin/bash';
648
- args = ['-c', 'curl -fsSL ccs.kaitran.ca/install | bash'];
649
- }
650
-
651
- const child = spawn(command, args, {
652
- stdio: 'inherit'
653
- // Do NOT use shell: true
654
- });
655
-
656
- child.on('exit', (code) => {
657
- if (code === 0) {
658
- console.log('');
659
- console.log(colored('[OK] Update successful!', 'green'));
660
- console.log('');
661
- console.log(`Run ${colored('ccs --version', 'yellow')} to verify`);
662
- console.log('');
663
- } else {
664
- console.log('');
665
- console.log(colored('[X] Update failed', 'red'));
666
- console.log('');
667
- console.log('Try manually:');
668
- if (isWindows) {
669
- console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
670
- } else {
671
- console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
672
- }
673
- console.log('');
674
- }
675
- process.exit(code || 0);
676
- });
677
-
678
- child.on('error', (err) => {
679
- console.log('');
680
- console.log(colored('[X] Failed to run installer', 'red'));
681
- console.log('');
682
- console.log('Try manually:');
683
- if (isWindows) {
684
- console.log(colored(' irm ccs.kaitran.ca/install | iex', 'yellow'));
685
- } else {
686
- console.log(colored(' curl -fsSL ccs.kaitran.ca/install | bash', 'yellow'));
687
- }
688
- console.log('');
689
- process.exit(1);
690
- });
691
- }
692
- }
693
-
694
- // Smart profile detection
695
- function detectProfile(args) {
696
- if (args.length === 0 || args[0].startsWith('-')) {
697
- // No args or first arg is a flag → use default profile
698
- return { profile: 'default', remainingArgs: args };
699
- } else {
700
- // First arg doesn't start with '-' → treat as profile name
701
- return { profile: args[0], remainingArgs: args.slice(1) };
702
- }
703
- }
704
-
705
- // Execute Claude CLI with embedded proxy (for GLMT profile)
706
- async function execClaudeWithProxy(claudeCli, profileName, args) {
707
- const { getSettingsPath } = require('./utils/config-manager');
708
-
709
- // 1. Read settings to get API key
710
- const settingsPath = getSettingsPath(profileName);
711
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
712
- const apiKey = settings.env.ANTHROPIC_AUTH_TOKEN;
713
-
714
- if (!apiKey || apiKey === 'YOUR_GLM_API_KEY_HERE') {
715
- console.error('[X] GLMT profile requires Z.AI API key');
716
- console.error(' Edit ~/.ccs/glmt.settings.json and set ANTHROPIC_AUTH_TOKEN');
717
- process.exit(1);
718
- }
719
-
720
- // Detect verbose flag
721
- const verbose = args.includes('--verbose') || args.includes('-v');
722
-
723
- // 2. Spawn embedded proxy with verbose flag
724
- const proxyPath = path.join(__dirname, 'glmt', 'glmt-proxy.js');
725
- const proxyArgs = verbose ? ['--verbose'] : [];
726
- // Use process.execPath for Windows compatibility (CVE-2024-27980)
727
- const proxy = spawn(process.execPath, [proxyPath, ...proxyArgs], {
728
- stdio: ['ignore', 'pipe', verbose ? 'pipe' : 'inherit']
729
- });
730
-
731
- // 3. Wait for proxy ready signal (with timeout)
732
- const { ProgressIndicator } = require('./utils/progress-indicator');
733
- const spinner = new ProgressIndicator('Starting GLMT proxy');
734
- spinner.start();
735
-
736
- let port;
737
- try {
738
- port = await new Promise((resolve, reject) => {
739
- const timeout = setTimeout(() => {
740
- reject(new Error('Proxy startup timeout (5s)'));
741
- }, 5000);
742
-
743
- proxy.stdout.on('data', (data) => {
744
- const match = data.toString().match(/PROXY_READY:(\d+)/);
745
- if (match) {
746
- clearTimeout(timeout);
747
- resolve(parseInt(match[1]));
748
- }
749
- });
750
-
751
- proxy.on('error', (error) => {
752
- clearTimeout(timeout);
753
- reject(error);
754
- });
755
-
756
- proxy.on('exit', (code) => {
757
- if (code !== 0 && code !== null) {
758
- clearTimeout(timeout);
759
- reject(new Error(`Proxy exited with code ${code}`));
760
- }
761
- });
762
- });
763
-
764
- spinner.succeed(`GLMT proxy ready on port ${port}`);
765
- } catch (error) {
766
- spinner.fail('Failed to start GLMT proxy');
767
- console.error('[X] Error:', error.message);
768
- console.error('');
769
- console.error('Possible causes:');
770
- console.error(' 1. Port conflict (unlikely with random port)');
771
- console.error(' 2. Node.js permission issue');
772
- console.error(' 3. Firewall blocking localhost');
773
- console.error('');
774
- console.error('Workarounds:');
775
- console.error(' - Use non-thinking mode: ccs glm "prompt"');
776
- console.error(' - Enable verbose logging: ccs glmt --verbose "prompt"');
777
- console.error(' - Check proxy logs in ~/.ccs/logs/ (if debug enabled)');
778
- console.error('');
779
- proxy.kill();
780
- process.exit(1);
781
- }
782
-
783
- // 4. Spawn Claude CLI with proxy URL
784
- const envVars = {
785
- ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}`,
786
- ANTHROPIC_AUTH_TOKEN: apiKey,
787
- ANTHROPIC_MODEL: 'glm-4.6'
788
- };
789
-
790
- // Use existing execClaude helper for consistent Windows handling
791
- const isWindows = process.platform === 'win32';
792
- const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudeCli);
793
- const env = { ...process.env, ...envVars };
794
-
795
- let claude;
796
- if (needsShell) {
797
- // When shell needed: concatenate into string to avoid DEP0190 warning
798
- const cmdString = [claudeCli, ...args].map(escapeShellArg).join(' ');
799
- claude = spawn(cmdString, {
800
- stdio: 'inherit',
801
- windowsHide: true,
802
- shell: true,
803
- env
804
- });
805
- } else {
806
- // When no shell needed: use array form (faster, no shell overhead)
807
- claude = spawn(claudeCli, args, {
808
- stdio: 'inherit',
809
- windowsHide: true,
810
- env
811
- });
812
- }
813
-
814
- // 5. Cleanup: kill proxy when Claude exits
815
- claude.on('exit', (code, signal) => {
816
- proxy.kill('SIGTERM');
817
- if (signal) process.kill(process.pid, signal);
818
- else process.exit(code || 0);
819
- });
820
-
821
- claude.on('error', (error) => {
822
- console.error('[X] Claude CLI error:', error);
823
- proxy.kill('SIGTERM');
824
- process.exit(1);
825
- });
826
-
827
- // Also handle parent process termination (use .once to avoid duplicates)
828
- process.once('SIGTERM', () => {
829
- proxy.kill('SIGTERM');
830
- claude.kill('SIGTERM');
831
- });
832
-
833
- process.once('SIGINT', () => {
834
- proxy.kill('SIGTERM');
835
- claude.kill('SIGTERM');
836
- });
837
- }
838
-
839
- /**
840
- * Handle shell completion installation
841
- */
842
- async function handleShellCompletionCommand(args) {
843
- const { ShellCompletionInstaller } = require('./utils/shell-completion');
844
- const { colored } = require('./utils/helpers');
845
-
846
- console.log(colored('Shell Completion Installer', 'bold'));
847
- console.log('');
848
-
849
- // Parse flags
850
- let targetShell = null;
851
- if (args.includes('--bash')) targetShell = 'bash';
852
- else if (args.includes('--zsh')) targetShell = 'zsh';
853
- else if (args.includes('--fish')) targetShell = 'fish';
854
- else if (args.includes('--powershell')) targetShell = 'powershell';
855
-
856
- try {
857
- const installer = new ShellCompletionInstaller();
858
- const result = installer.install(targetShell);
859
-
860
- if (result.alreadyInstalled) {
861
- console.log(colored('[OK] Shell completion already installed', 'green'));
862
- console.log('');
863
- return;
864
- }
865
-
866
- console.log(colored('[OK] Shell completion installed successfully!', 'green'));
867
- console.log('');
868
- console.log(result.message);
869
- console.log('');
870
- console.log(colored('To activate:', 'cyan'));
871
- console.log(` ${result.reload}`);
872
- console.log('');
873
- console.log(colored('Then test:', 'cyan'));
874
- console.log(' ccs <TAB> # See available profiles');
875
- console.log(' ccs auth <TAB> # See auth subcommands');
876
- console.log('');
877
- } catch (error) {
878
- console.error(colored('[X] Error:', 'red'), error.message);
879
- console.error('');
880
- console.error(colored('Usage:', 'yellow'));
881
- console.error(' ccs --shell-completion # Auto-detect shell');
882
- console.error(' ccs --shell-completion --bash # Install for bash');
883
- console.error(' ccs --shell-completion --zsh # Install for zsh');
884
- console.error(' ccs --shell-completion --fish # Install for fish');
885
- console.error(' ccs --shell-completion --powershell # Install for PowerShell');
886
- console.error('');
887
- process.exit(1);
888
- }
889
- }
890
-
891
- // Main execution
892
- async function main() {
893
- const args = process.argv.slice(2);
894
-
895
- // Special case: version command (check BEFORE profile detection)
896
- const firstArg = args[0];
897
- if (firstArg === 'version' || firstArg === '--version' || firstArg === '-v') {
898
- handleVersionCommand();
899
- }
900
-
901
- // Special case: help command
902
- if (firstArg === '--help' || firstArg === '-h' || firstArg === 'help') {
903
- handleHelpCommand();
904
- return;
905
- }
906
-
907
- // Special case: install command
908
- if (firstArg === '--install') {
909
- handleInstallCommand();
910
- return;
911
- }
912
-
913
- // Special case: uninstall command
914
- if (firstArg === '--uninstall') {
915
- handleUninstallCommand();
916
- return;
917
- }
918
-
919
- // Special case: shell completion installer
920
- if (firstArg === '--shell-completion' || firstArg === '-sc') {
921
- await handleShellCompletionCommand(args.slice(1));
922
- return;
923
- }
924
-
925
- // Special case: doctor command
926
- if (firstArg === 'doctor' || firstArg === '--doctor') {
927
- await handleDoctorCommand();
928
- return;
929
- }
930
-
931
- // Special case: sync command (sync delegation commands and skills to ~/.claude/)
932
- if (firstArg === 'sync' || firstArg === '--sync') {
933
- await handleSyncCommand();
934
- return;
935
- }
936
-
937
- // Special case: update command (update CCS to latest version)
938
- if (firstArg === 'update' || firstArg === '--update') {
939
- await handleUpdateCommand();
940
- return;
941
- }
942
-
943
- // Special case: auth command (multi-account management)
944
- if (firstArg === 'auth') {
945
- const AuthCommands = require('./auth/auth-commands');
946
- const authCommands = new AuthCommands();
947
- await authCommands.route(args.slice(1));
948
- return;
949
- }
950
-
951
- // Special case: headless delegation (-p flag)
952
- if (args.includes('-p') || args.includes('--prompt')) {
953
- const DelegationHandler = require('./delegation/delegation-handler');
954
- const handler = new DelegationHandler();
955
- await handler.route(args);
956
- return;
957
- }
958
-
959
- // Auto-recovery for missing configuration
960
- const recovery = new RecoveryManager();
961
- const recovered = recovery.recoverAll();
962
-
963
- if (recovered) {
964
- recovery.showRecoveryHints();
965
- }
966
-
967
- // Detect profile
968
- const { profile, remainingArgs } = detectProfile(args);
969
-
970
- // Detect Claude CLI first (needed for all paths)
971
- const claudeCli = detectClaudeCli();
972
- if (!claudeCli) {
973
- ErrorManager.showClaudeNotFound();
974
- process.exit(1);
975
- }
976
-
977
- // Use ProfileDetector to determine profile type
978
- const ProfileDetector = require('./auth/profile-detector');
979
- const InstanceManager = require('./management/instance-manager');
980
- const ProfileRegistry = require('./auth/profile-registry');
981
- const { getSettingsPath } = require('./utils/config-manager');
982
-
983
- const detector = new ProfileDetector();
984
-
985
- try {
986
- const profileInfo = detector.detectProfileType(profile);
987
-
988
- if (profileInfo.type === 'settings') {
989
- // Check if this is GLMT profile (requires proxy)
990
- if (profileInfo.name === 'glmt') {
991
- // GLMT FLOW: Settings-based with embedded proxy for thinking support
992
- await execClaudeWithProxy(claudeCli, profileInfo.name, remainingArgs);
993
- } else {
994
- // EXISTING FLOW: Settings-based profile (glm, kimi)
995
- // Use --settings flag (backward compatible)
996
- const expandedSettingsPath = getSettingsPath(profileInfo.name);
997
- execClaude(claudeCli, ['--settings', expandedSettingsPath, ...remainingArgs]);
998
- }
999
- } else if (profileInfo.type === 'account') {
1000
- // NEW FLOW: Account-based profile (work, personal)
1001
- // All platforms: Use instance isolation with CLAUDE_CONFIG_DIR
1002
- const registry = new ProfileRegistry();
1003
- const instanceMgr = new InstanceManager();
1004
-
1005
- // Ensure instance exists (lazy init if needed)
1006
- const instancePath = instanceMgr.ensureInstance(profileInfo.name);
1007
-
1008
- // Update last_used timestamp
1009
- registry.touchProfile(profileInfo.name);
1010
-
1011
- // Execute Claude with instance isolation
1012
- const envVars = { CLAUDE_CONFIG_DIR: instancePath };
1013
- execClaude(claudeCli, remainingArgs, envVars);
1014
- } else {
1015
- // DEFAULT: No profile configured, use Claude's own defaults
1016
- execClaude(claudeCli, remainingArgs);
1017
- }
1018
- } catch (error) {
1019
- // Check if this is a profile not found error with suggestions
1020
- if (error.profileName && error.availableProfiles !== undefined) {
1021
- const allProfiles = error.availableProfiles.split('\n');
1022
- ErrorManager.showProfileNotFound(error.profileName, allProfiles, error.suggestions);
1023
- } else {
1024
- console.error(`[X] ${error.message}`);
1025
- }
1026
- process.exit(1);
1027
- }
1028
- }
1029
-
1030
- // Run main
1031
- main().catch(error => {
1032
- console.error('Fatal error:', error.message);
1033
- process.exit(1);
1034
- });