@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
@@ -1,485 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const path = require('path');
5
-
6
- /**
7
- * Formats delegation execution results for display
8
- * Creates ASCII box output with file change tracking
9
- */
10
- class ResultFormatter {
11
- /**
12
- * Format execution result with complete source-of-truth
13
- * @param {Object} result - Execution result from HeadlessExecutor
14
- * @param {string} result.profile - Profile used (glm, kimi, etc.)
15
- * @param {string} result.cwd - Working directory
16
- * @param {number} result.exitCode - Exit code
17
- * @param {string} result.stdout - Standard output
18
- * @param {string} result.stderr - Standard error
19
- * @param {number} result.duration - Duration in milliseconds
20
- * @param {boolean} result.success - Success flag
21
- * @param {string} result.content - Parsed content (from JSON or stdout)
22
- * @param {string} result.sessionId - Session ID (from JSON)
23
- * @param {number} result.totalCost - Total cost USD (from JSON)
24
- * @param {number} result.numTurns - Number of turns (from JSON)
25
- * @returns {string} Formatted result
26
- */
27
- static format(result) {
28
- const { profile, cwd, exitCode, stdout, stderr, duration, success, content, sessionId, totalCost, numTurns, subtype, permissionDenials, errors, json, timedOut } = result;
29
-
30
- // Handle timeout (graceful termination)
31
- if (timedOut) {
32
- return this._formatTimeoutError(result);
33
- }
34
-
35
- // Handle legacy max_turns error (Claude CLI might still return this)
36
- if (subtype === 'error_max_turns') {
37
- return this._formatTimeoutError(result);
38
- }
39
-
40
- // Use content field for output (JSON result or fallback stdout)
41
- const displayOutput = content || stdout;
42
-
43
- // Build formatted output
44
- let output = '';
45
-
46
- // Header
47
- output += this._formatHeader(profile, success);
48
-
49
- // Info box (file detection handled by delegated session itself)
50
- output += this._formatInfoBox(cwd, profile, duration, exitCode, sessionId, totalCost, numTurns);
51
-
52
- // Task output
53
- output += '\n';
54
- output += this._formatOutput(displayOutput);
55
-
56
- // Permission denials if present
57
- if (permissionDenials && permissionDenials.length > 0) {
58
- output += '\n';
59
- output += this._formatPermissionDenials(permissionDenials);
60
- }
61
-
62
- // Errors if present
63
- if (errors && errors.length > 0) {
64
- output += '\n';
65
- output += this._formatErrors(errors);
66
- }
67
-
68
- // Stderr if present
69
- if (stderr && stderr.trim()) {
70
- output += '\n';
71
- output += this._formatStderr(stderr);
72
- }
73
-
74
- // Footer
75
- output += '\n';
76
- output += this._formatFooter(success, duration);
77
-
78
- return output;
79
- }
80
-
81
- /**
82
- * Extract file changes from output
83
- * @param {string} output - Command output
84
- * @param {string} cwd - Working directory for filesystem scanning fallback
85
- * @returns {Object} { created: Array<string>, modified: Array<string> }
86
- */
87
- static extractFileChanges(output, cwd) {
88
- const created = [];
89
- const modified = [];
90
-
91
- // Patterns to match file operations (case-insensitive)
92
- const createdPatterns = [
93
- /created:\s*([^\n\r]+)/gi,
94
- /create:\s*([^\n\r]+)/gi,
95
- /wrote:\s*([^\n\r]+)/gi,
96
- /write:\s*([^\n\r]+)/gi,
97
- /new file:\s*([^\n\r]+)/gi,
98
- /generated:\s*([^\n\r]+)/gi,
99
- /added:\s*([^\n\r]+)/gi
100
- ];
101
-
102
- const modifiedPatterns = [
103
- /modified:\s*([^\n\r]+)/gi,
104
- /update:\s*([^\n\r]+)/gi,
105
- /updated:\s*([^\n\r]+)/gi,
106
- /edit:\s*([^\n\r]+)/gi,
107
- /edited:\s*([^\n\r]+)/gi,
108
- /changed:\s*([^\n\r]+)/gi
109
- ];
110
-
111
- // Helper to check if file is infrastructure (should be ignored)
112
- const isInfrastructure = (filePath) => {
113
- return filePath.includes('/.claude/') || filePath.startsWith('.claude/');
114
- };
115
-
116
- // Extract created files
117
- for (const pattern of createdPatterns) {
118
- let match;
119
- while ((match = pattern.exec(output)) !== null) {
120
- const filePath = match[1].trim();
121
- if (filePath && !created.includes(filePath) && !isInfrastructure(filePath)) {
122
- created.push(filePath);
123
- }
124
- }
125
- }
126
-
127
- // Extract modified files
128
- for (const pattern of modifiedPatterns) {
129
- let match;
130
- while ((match = pattern.exec(output)) !== null) {
131
- const filePath = match[1].trim();
132
- // Don't include if already in created list or is infrastructure
133
- if (filePath && !modified.includes(filePath) && !created.includes(filePath) && !isInfrastructure(filePath)) {
134
- modified.push(filePath);
135
- }
136
- }
137
- }
138
-
139
- // Fallback: Scan filesystem for recently modified files (last 5 minutes)
140
- if (created.length === 0 && modified.length === 0 && cwd) {
141
- try {
142
- const fs = require('fs');
143
- const childProcess = require('child_process');
144
-
145
- // Use find command to get recently modified files (excluding infrastructure)
146
- const findCmd = `find . -type f -mmin -5 -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.claude/*" 2>/dev/null | head -20`;
147
- const result = childProcess.execSync(findCmd, { cwd, encoding: 'utf8', timeout: 5000 });
148
-
149
- const files = result.split('\n').filter(f => f.trim());
150
- files.forEach(file => {
151
- const fullPath = path.join(cwd, file);
152
-
153
- // Double-check not infrastructure
154
- if (isInfrastructure(fullPath)) {
155
- return;
156
- }
157
-
158
- try {
159
- const stats = fs.statSync(fullPath);
160
- const now = Date.now();
161
- const mtime = stats.mtimeMs;
162
- const ctime = stats.ctimeMs;
163
-
164
- // If both mtime and ctime are very recent (within 10 minutes), likely created
165
- // ctime = inode change time, for new files this is close to creation time
166
- const isVeryRecent = (now - mtime) < 600000 && (now - ctime) < 600000;
167
- const timeDiff = Math.abs(mtime - ctime);
168
-
169
- // If mtime and ctime are very close (< 1 second apart) and both recent, it's created
170
- if (isVeryRecent && timeDiff < 1000) {
171
- if (!created.includes(fullPath)) {
172
- created.push(fullPath);
173
- }
174
- } else {
175
- // Otherwise, it's modified
176
- if (!modified.includes(fullPath)) {
177
- modified.push(fullPath);
178
- }
179
- }
180
- } catch (statError) {
181
- // If stat fails, default to created (since we're in fallback mode)
182
- if (!created.includes(fullPath) && !modified.includes(fullPath)) {
183
- created.push(fullPath);
184
- }
185
- }
186
- });
187
- } catch (scanError) {
188
- // Silently fail if filesystem scan doesn't work
189
- if (process.env.CCS_DEBUG) {
190
- console.error(`[!] Filesystem scan failed: ${scanError.message}`);
191
- }
192
- }
193
- }
194
-
195
- return { created, modified };
196
- }
197
-
198
- /**
199
- * Format header with delegation indicator
200
- * @param {string} profile - Profile name
201
- * @param {boolean} success - Success flag
202
- * @returns {string} Formatted header
203
- * @private
204
- */
205
- static _formatHeader(profile, success) {
206
- const modelName = this._getModelDisplayName(profile);
207
- const icon = success ? '[i]' : '[X]';
208
- return `${icon} Delegated to ${modelName} (ccs:${profile})\n`;
209
- }
210
-
211
- /**
212
- * Format info box with delegation details
213
- * @param {string} cwd - Working directory
214
- * @param {string} profile - Profile name
215
- * @param {number} duration - Duration in ms
216
- * @param {number} exitCode - Exit code
217
- * @param {string} sessionId - Session ID (from JSON)
218
- * @param {number} totalCost - Total cost USD (from JSON)
219
- * @param {number} numTurns - Number of turns (from JSON)
220
- * @returns {string} Formatted info box
221
- * @private
222
- */
223
- static _formatInfoBox(cwd, profile, duration, exitCode, sessionId, totalCost, numTurns) {
224
- const modelName = this._getModelDisplayName(profile);
225
- const durationSec = (duration / 1000).toFixed(1);
226
-
227
- // Calculate box width (fit longest line + padding)
228
- const maxWidth = 70;
229
- const cwdLine = `Working Directory: ${cwd}`;
230
- const boxWidth = Math.min(Math.max(cwdLine.length + 4, 50), maxWidth);
231
-
232
- const lines = [
233
- `Working Directory: ${this._truncate(cwd, boxWidth - 22)}`,
234
- `Model: ${modelName}`,
235
- `Duration: ${durationSec}s`,
236
- `Exit Code: ${exitCode}`
237
- ];
238
-
239
- // Add JSON-specific fields if available
240
- if (sessionId) {
241
- // Abbreviate session ID (Git-style first 8 chars) to prevent wrapping
242
- const shortId = sessionId.length > 8 ? sessionId.substring(0, 8) : sessionId;
243
- lines.push(`Session ID: ${shortId}`);
244
- }
245
- if (totalCost !== undefined && totalCost !== null) {
246
- lines.push(`Cost: $${totalCost.toFixed(4)}`);
247
- }
248
- if (numTurns) {
249
- lines.push(`Turns: ${numTurns}`);
250
- }
251
-
252
- let box = '';
253
- box += '╔' + '═'.repeat(boxWidth - 2) + '╗\n';
254
-
255
- for (const line of lines) {
256
- const padding = boxWidth - line.length - 4;
257
- box += '║ ' + line + ' '.repeat(Math.max(0, padding)) + ' ║\n';
258
- }
259
-
260
- box += '╚' + '═'.repeat(boxWidth - 2) + '╝';
261
-
262
- return box;
263
- }
264
-
265
- /**
266
- * Format task output
267
- * @param {string} output - Standard output
268
- * @returns {string} Formatted output
269
- * @private
270
- */
271
- static _formatOutput(output) {
272
- if (!output || !output.trim()) {
273
- return '[i] No output from delegated task\n';
274
- }
275
-
276
- return output.trim() + '\n';
277
- }
278
-
279
- /**
280
- * Format stderr output
281
- * @param {string} stderr - Standard error
282
- * @returns {string} Formatted stderr
283
- * @private
284
- */
285
- static _formatStderr(stderr) {
286
- return `[!] Stderr:\n${stderr.trim()}\n\n`;
287
- }
288
-
289
- /**
290
- * Format file list (created or modified)
291
- * @param {string} label - Label (Created/Modified)
292
- * @param {Array<string>} files - File paths
293
- * @returns {string} Formatted file list
294
- * @private
295
- */
296
- static _formatFileList(label, files) {
297
- let output = `[i] ${label} Files:\n`;
298
-
299
- for (const file of files) {
300
- output += ` - ${file}\n`;
301
- }
302
-
303
- return output;
304
- }
305
-
306
- /**
307
- * Format footer with completion status
308
- * @param {boolean} success - Success flag
309
- * @param {number} duration - Duration in ms
310
- * @returns {string} Formatted footer
311
- * @private
312
- */
313
- static _formatFooter(success, duration) {
314
- const icon = success ? '[OK]' : '[X]';
315
- const status = success ? 'Delegation completed' : 'Delegation failed';
316
- return `${icon} ${status}\n`;
317
- }
318
-
319
- /**
320
- * Get display name for model profile
321
- * @param {string} profile - Profile name
322
- * @returns {string} Display name
323
- * @private
324
- */
325
- static _getModelDisplayName(profile) {
326
- const displayNames = {
327
- 'glm': 'GLM-4.6',
328
- 'glmt': 'GLM-4.6 (Thinking)',
329
- 'kimi': 'Kimi',
330
- 'default': 'Claude'
331
- };
332
-
333
- return displayNames[profile] || profile.toUpperCase();
334
- }
335
-
336
- /**
337
- * Truncate string to max length
338
- * @param {string} str - String to truncate
339
- * @param {number} maxLength - Maximum length
340
- * @returns {string} Truncated string
341
- * @private
342
- */
343
- static _truncate(str, maxLength) {
344
- if (str.length <= maxLength) {
345
- return str;
346
- }
347
- return str.substring(0, maxLength - 3) + '...';
348
- }
349
-
350
- /**
351
- * Format minimal result (for quick tasks)
352
- * @param {Object} result - Execution result
353
- * @returns {string} Minimal formatted result
354
- */
355
- static formatMinimal(result) {
356
- const { profile, success, duration } = result;
357
- const modelName = this._getModelDisplayName(profile);
358
- const icon = success ? '[OK]' : '[X]';
359
- const durationSec = (duration / 1000).toFixed(1);
360
-
361
- return `${icon} ${modelName} delegation ${success ? 'completed' : 'failed'} (${durationSec}s)\n`;
362
- }
363
-
364
- /**
365
- * Format verbose result (with full details)
366
- * @param {Object} result - Execution result
367
- * @returns {string} Verbose formatted result
368
- */
369
- static formatVerbose(result) {
370
- const basic = this.format(result);
371
-
372
- // Add additional debug info
373
- let verbose = basic;
374
- verbose += '\n=== Debug Information ===\n';
375
- verbose += `CWD: ${result.cwd}\n`;
376
- verbose += `Profile: ${result.profile}\n`;
377
- verbose += `Exit Code: ${result.exitCode}\n`;
378
- verbose += `Duration: ${result.duration}ms\n`;
379
- verbose += `Success: ${result.success}\n`;
380
- verbose += `Stdout Length: ${result.stdout.length} chars\n`;
381
- verbose += `Stderr Length: ${result.stderr.length} chars\n`;
382
-
383
- return verbose;
384
- }
385
-
386
- /**
387
- * Check if NO_COLOR environment variable is set
388
- * @returns {boolean} True if colors should be disabled
389
- * @private
390
- */
391
- static _shouldDisableColors() {
392
- return process.env.NO_COLOR !== undefined;
393
- }
394
-
395
- /**
396
- * Format timeout error (session exceeded time limit)
397
- * @param {Object} result - Execution result
398
- * @returns {string} Formatted timeout error
399
- * @private
400
- */
401
- static _formatTimeoutError(result) {
402
- const { profile, cwd, duration, sessionId, totalCost, numTurns, permissionDenials } = result;
403
-
404
- let output = '';
405
-
406
- // Header
407
- output += this._formatHeader(profile, false);
408
-
409
- // Info box
410
- output += this._formatInfoBox(cwd, profile, duration, 0, sessionId, totalCost, numTurns);
411
-
412
- // Timeout message
413
- output += '\n';
414
- const timeoutMin = (duration / 60000).toFixed(1);
415
- output += `[!] Execution timed out after ${timeoutMin} minutes\n\n`;
416
- output += 'The delegated session exceeded its time limit before completing the task.\n';
417
- output += 'Session was gracefully terminated and saved for continuation.\n';
418
-
419
- // Permission denials if present
420
- if (permissionDenials && permissionDenials.length > 0) {
421
- output += '\n';
422
- output += this._formatPermissionDenials(permissionDenials);
423
- output += '\n';
424
- output += 'The task may require permissions that were denied.\n';
425
- output += 'Consider running with --permission-mode bypassPermissions or execute manually.\n';
426
- }
427
-
428
- // Suggestions
429
- output += '\n';
430
- output += 'Suggestions:\n';
431
- output += ` - Continue session: ccs ${profile}:continue -p "finish the task"\n`;
432
- output += ` - Increase timeout: ccs ${profile} -p "task" --timeout ${duration * 2}\n`;
433
- output += ' - Break task into smaller steps\n';
434
- output += ' - Run task manually in main Claude session\n';
435
-
436
- output += '\n';
437
- // Abbreviate session ID (Git-style first 8 chars)
438
- const shortId = sessionId && sessionId.length > 8 ? sessionId.substring(0, 8) : sessionId;
439
- output += `[i] Session persisted with ID: ${shortId}\n`;
440
- if (totalCost !== undefined && totalCost !== null) {
441
- output += `[i] Cost: $${totalCost.toFixed(4)}\n`;
442
- }
443
-
444
- return output;
445
- }
446
-
447
- /**
448
- * Format permission denials
449
- * @param {Array<Object>} denials - Permission denial objects
450
- * @returns {string} Formatted permission denials
451
- * @private
452
- */
453
- static _formatPermissionDenials(denials) {
454
- let output = '[!] Permission Denials:\n';
455
-
456
- for (const denial of denials) {
457
- const tool = denial.tool_name || 'Unknown';
458
- const input = denial.tool_input || {};
459
- const command = input.command || input.description || JSON.stringify(input);
460
-
461
- output += ` - ${tool}: ${command}\n`;
462
- }
463
-
464
- return output;
465
- }
466
-
467
- /**
468
- * Format errors array
469
- * @param {Array<Object>} errors - Error objects
470
- * @returns {string} Formatted errors
471
- * @private
472
- */
473
- static _formatErrors(errors) {
474
- let output = '[X] Errors:\n';
475
-
476
- for (const error of errors) {
477
- const message = error.message || error.error || JSON.stringify(error);
478
- output += ` - ${message}\n`;
479
- }
480
-
481
- return output;
482
- }
483
- }
484
-
485
- module.exports = { ResultFormatter };
@@ -1,157 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const os = require('os');
7
-
8
- /**
9
- * Manages delegation session persistence for multi-turn conversations
10
- */
11
- class SessionManager {
12
- constructor() {
13
- this.sessionsPath = path.join(os.homedir(), '.ccs', 'delegation-sessions.json');
14
- }
15
-
16
- /**
17
- * Store new session metadata
18
- * @param {string} profile - Profile name (glm, kimi, etc.)
19
- * @param {Object} sessionData - Session data
20
- * @param {string} sessionData.sessionId - Claude session ID
21
- * @param {number} sessionData.totalCost - Initial cost
22
- * @param {string} sessionData.cwd - Working directory
23
- */
24
- storeSession(profile, sessionData) {
25
- const sessions = this._loadSessions();
26
- const key = `${profile}:latest`;
27
-
28
- sessions[key] = {
29
- sessionId: sessionData.sessionId,
30
- profile,
31
- startTime: Date.now(),
32
- lastTurnTime: Date.now(),
33
- totalCost: sessionData.totalCost || 0,
34
- turns: 1,
35
- cwd: sessionData.cwd || process.cwd()
36
- };
37
-
38
- this._saveSessions(sessions);
39
-
40
- if (process.env.CCS_DEBUG) {
41
- console.error(`[i] Stored session: ${sessionData.sessionId} for ${profile}`);
42
- }
43
- }
44
-
45
- /**
46
- * Update session after additional turn
47
- * @param {string} profile - Profile name
48
- * @param {string} sessionId - Session ID
49
- * @param {Object} turnData - Turn data
50
- * @param {number} turnData.totalCost - Turn cost
51
- */
52
- updateSession(profile, sessionId, turnData) {
53
- const sessions = this._loadSessions();
54
- const key = `${profile}:latest`;
55
-
56
- if (sessions[key]?.sessionId === sessionId) {
57
- sessions[key].lastTurnTime = Date.now();
58
- sessions[key].totalCost += turnData.totalCost || 0;
59
- sessions[key].turns += 1;
60
- this._saveSessions(sessions);
61
-
62
- if (process.env.CCS_DEBUG) {
63
- const cost = sessions[key].totalCost !== undefined && sessions[key].totalCost !== null ? sessions[key].totalCost.toFixed(4) : '0.0000';
64
- console.error(`[i] Updated session: ${sessionId}, total: $${cost}, turns: ${sessions[key].turns}`);
65
- }
66
- }
67
- }
68
-
69
- /**
70
- * Get last session for profile
71
- * @param {string} profile - Profile name
72
- * @returns {Object|null} Session metadata or null
73
- */
74
- getLastSession(profile) {
75
- const sessions = this._loadSessions();
76
- const key = `${profile}:latest`;
77
- return sessions[key] || null;
78
- }
79
-
80
- /**
81
- * Clear all sessions for profile
82
- * @param {string} profile - Profile name
83
- */
84
- clearProfile(profile) {
85
- const sessions = this._loadSessions();
86
- const key = `${profile}:latest`;
87
- delete sessions[key];
88
- this._saveSessions(sessions);
89
- }
90
-
91
- /**
92
- * Clean up expired sessions (>30 days)
93
- */
94
- cleanupExpired() {
95
- const sessions = this._loadSessions();
96
- const now = Date.now();
97
- const maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days
98
-
99
- let cleaned = 0;
100
- Object.keys(sessions).forEach(key => {
101
- if (now - sessions[key].lastTurnTime > maxAge) {
102
- delete sessions[key];
103
- cleaned++;
104
- }
105
- });
106
-
107
- if (cleaned > 0) {
108
- this._saveSessions(sessions);
109
- if (process.env.CCS_DEBUG) {
110
- console.error(`[i] Cleaned ${cleaned} expired sessions`);
111
- }
112
- }
113
- }
114
-
115
- /**
116
- * Load sessions from disk
117
- * @returns {Object} Sessions object
118
- * @private
119
- */
120
- _loadSessions() {
121
- try {
122
- if (!fs.existsSync(this.sessionsPath)) {
123
- return {};
124
- }
125
- const content = fs.readFileSync(this.sessionsPath, 'utf8');
126
- return JSON.parse(content);
127
- } catch (error) {
128
- if (process.env.CCS_DEBUG) {
129
- console.warn(`[!] Failed to load sessions: ${error.message}`);
130
- }
131
- return {};
132
- }
133
- }
134
-
135
- /**
136
- * Save sessions to disk
137
- * @param {Object} sessions - Sessions object
138
- * @private
139
- */
140
- _saveSessions(sessions) {
141
- try {
142
- const dir = path.dirname(this.sessionsPath);
143
- if (!fs.existsSync(dir)) {
144
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
145
- }
146
- fs.writeFileSync(
147
- this.sessionsPath,
148
- JSON.stringify(sessions, null, 2),
149
- { mode: 0o600 }
150
- );
151
- } catch (error) {
152
- console.error(`[!] Failed to save sessions: ${error.message}`);
153
- }
154
- }
155
- }
156
-
157
- module.exports = { SessionManager };