indusagi-coding-agent 0.1.23 → 0.1.24

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 (213) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +2 -0
  3. package/dist/cli/args.d.ts +117 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +221 -52
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/config-selector.d.ts +58 -2
  8. package/dist/cli/config-selector.d.ts.map +1 -1
  9. package/dist/cli/config-selector.js +130 -12
  10. package/dist/cli/config-selector.js.map +1 -1
  11. package/dist/cli/file-processor.d.ts +70 -2
  12. package/dist/cli/file-processor.d.ts.map +1 -1
  13. package/dist/cli/file-processor.js +240 -15
  14. package/dist/cli/file-processor.js.map +1 -1
  15. package/dist/cli/list-models.d.ts +63 -3
  16. package/dist/cli/list-models.d.ts.map +1 -1
  17. package/dist/cli/list-models.js +202 -27
  18. package/dist/cli/list-models.js.map +1 -1
  19. package/dist/cli/login-handler.d.ts +82 -8
  20. package/dist/cli/login-handler.d.ts.map +1 -1
  21. package/dist/cli/login-handler.js +410 -77
  22. package/dist/cli/login-handler.js.map +1 -1
  23. package/dist/cli/session-picker.d.ts +74 -2
  24. package/dist/cli/session-picker.d.ts.map +1 -1
  25. package/dist/cli/session-picker.js +236 -12
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +214 -9
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +214 -9
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts +302 -12
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +302 -12
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/diagnostics.d.ts +191 -0
  36. package/dist/core/diagnostics.d.ts.map +1 -1
  37. package/dist/core/diagnostics.js +142 -0
  38. package/dist/core/diagnostics.js.map +1 -1
  39. package/dist/core/event-bus.d.ts +146 -0
  40. package/dist/core/event-bus.d.ts.map +1 -1
  41. package/dist/core/event-bus.js +93 -0
  42. package/dist/core/event-bus.js.map +1 -1
  43. package/dist/core/export-html/ansi-to-html.d.ts +4 -0
  44. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  45. package/dist/core/export-html/ansi-to-html.js +4 -0
  46. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  47. package/dist/core/export-html/index.d.ts +128 -0
  48. package/dist/core/export-html/index.d.ts.map +1 -1
  49. package/dist/core/export-html/index.js +128 -0
  50. package/dist/core/export-html/index.js.map +1 -1
  51. package/dist/core/export-html/tool-renderer.d.ts +4 -0
  52. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  53. package/dist/core/export-html/tool-renderer.js +4 -0
  54. package/dist/core/export-html/tool-renderer.js.map +1 -1
  55. package/dist/core/keybindings.d.ts +142 -0
  56. package/dist/core/keybindings.d.ts.map +1 -1
  57. package/dist/core/keybindings.js +142 -0
  58. package/dist/core/keybindings.js.map +1 -1
  59. package/dist/core/model-registry.d.ts +98 -1
  60. package/dist/core/model-registry.d.ts.map +1 -1
  61. package/dist/core/model-registry.js +98 -1
  62. package/dist/core/model-registry.js.map +1 -1
  63. package/dist/core/model-resolver.d.ts +99 -1
  64. package/dist/core/model-resolver.d.ts.map +1 -1
  65. package/dist/core/model-resolver.js +99 -1
  66. package/dist/core/model-resolver.js.map +1 -1
  67. package/dist/core/prompt-templates.js.map +1 -1
  68. package/dist/core/session-manager.d.ts +127 -0
  69. package/dist/core/session-manager.d.ts.map +1 -1
  70. package/dist/core/session-manager.js +125 -0
  71. package/dist/core/session-manager.js.map +1 -1
  72. package/dist/core/skills.js.map +1 -1
  73. package/dist/core/subagents.js.map +1 -1
  74. package/dist/core/tools/bash.d.ts +391 -11
  75. package/dist/core/tools/bash.d.ts.map +1 -1
  76. package/dist/core/tools/bash.js +269 -2
  77. package/dist/core/tools/bash.js.map +1 -1
  78. package/dist/core/tools/edit.d.ts +284 -6
  79. package/dist/core/tools/edit.d.ts.map +1 -1
  80. package/dist/core/tools/edit.js +238 -0
  81. package/dist/core/tools/edit.js.map +1 -1
  82. package/dist/core/tools/find.d.ts +169 -5
  83. package/dist/core/tools/find.d.ts.map +1 -1
  84. package/dist/core/tools/find.js +136 -0
  85. package/dist/core/tools/find.js.map +1 -1
  86. package/dist/core/tools/grep.d.ts +285 -5
  87. package/dist/core/tools/grep.d.ts.map +1 -1
  88. package/dist/core/tools/grep.js +247 -0
  89. package/dist/core/tools/grep.js.map +1 -1
  90. package/dist/core/tools/ls.d.ts +6 -0
  91. package/dist/core/tools/ls.d.ts.map +1 -1
  92. package/dist/core/tools/ls.js +6 -0
  93. package/dist/core/tools/ls.js.map +1 -1
  94. package/dist/core/tools/read.d.ts +308 -7
  95. package/dist/core/tools/read.d.ts.map +1 -1
  96. package/dist/core/tools/read.js +231 -0
  97. package/dist/core/tools/read.js.map +1 -1
  98. package/dist/core/tools/webfetch.d.ts +118 -3
  99. package/dist/core/tools/webfetch.d.ts.map +1 -1
  100. package/dist/core/tools/webfetch.js +118 -3
  101. package/dist/core/tools/webfetch.js.map +1 -1
  102. package/dist/core/tools/websearch.d.ts +130 -3
  103. package/dist/core/tools/websearch.d.ts.map +1 -1
  104. package/dist/core/tools/websearch.js +130 -3
  105. package/dist/core/tools/websearch.js.map +1 -1
  106. package/dist/core/tools/write.d.ts +251 -5
  107. package/dist/core/tools/write.d.ts.map +1 -1
  108. package/dist/core/tools/write.js +210 -0
  109. package/dist/core/tools/write.js.map +1 -1
  110. package/dist/modes/interactive/components/assistant-message.d.ts +164 -1
  111. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  112. package/dist/modes/interactive/components/assistant-message.js +164 -1
  113. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  114. package/dist/modes/interactive/components/bash-execution.d.ts +297 -1
  115. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  116. package/dist/modes/interactive/components/bash-execution.js +297 -1
  117. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  118. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/tool-execution.js +251 -1
  120. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  121. package/dist/modes/interactive/components/user-message.d.ts +186 -1
  122. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  123. package/dist/modes/interactive/components/user-message.js +186 -1
  124. package/dist/modes/interactive/components/user-message.js.map +1 -1
  125. package/dist/modes/interactive/interactive-mode.d.ts +1567 -13
  126. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  127. package/dist/modes/interactive/interactive-mode.js +1567 -13
  128. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  129. package/dist/modes/interactive/theme/theme.d.ts +422 -0
  130. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  131. package/dist/modes/interactive/theme/theme.js +422 -0
  132. package/dist/modes/interactive/theme/theme.js.map +1 -1
  133. package/dist/modes/print-mode.d.ts +538 -5
  134. package/dist/modes/print-mode.d.ts.map +1 -1
  135. package/dist/modes/print-mode.js +538 -5
  136. package/dist/modes/print-mode.js.map +1 -1
  137. package/dist/modes/rpc/rpc-client.d.ts +921 -8
  138. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  139. package/dist/modes/rpc/rpc-client.js +921 -8
  140. package/dist/modes/rpc/rpc-client.js.map +1 -1
  141. package/dist/modes/rpc/rpc-mode.d.ts +802 -9
  142. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-mode.js +802 -9
  144. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +356 -3
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js +356 -3
  148. package/dist/modes/rpc/rpc-types.js.map +1 -1
  149. package/dist/modes/shared.d.ts +386 -0
  150. package/dist/modes/shared.d.ts.map +1 -0
  151. package/dist/modes/shared.js +543 -0
  152. package/dist/modes/shared.js.map +1 -0
  153. package/dist/utils/array.d.ts +389 -0
  154. package/dist/utils/array.d.ts.map +1 -0
  155. package/dist/utils/array.js +585 -0
  156. package/dist/utils/array.js.map +1 -0
  157. package/dist/utils/color-formatter.d.ts +318 -0
  158. package/dist/utils/color-formatter.d.ts.map +1 -0
  159. package/dist/utils/color-formatter.js +442 -0
  160. package/dist/utils/color-formatter.js.map +1 -0
  161. package/dist/utils/data-transformer.d.ts +326 -0
  162. package/dist/utils/data-transformer.d.ts.map +1 -0
  163. package/dist/utils/data-transformer.js +512 -0
  164. package/dist/utils/data-transformer.js.map +1 -0
  165. package/dist/utils/date-formatter.d.ts +281 -0
  166. package/dist/utils/date-formatter.d.ts.map +1 -0
  167. package/dist/utils/date-formatter.js +503 -0
  168. package/dist/utils/date-formatter.js.map +1 -0
  169. package/dist/utils/error-handler.d.ts +541 -0
  170. package/dist/utils/error-handler.d.ts.map +1 -0
  171. package/dist/utils/error-handler.js +726 -0
  172. package/dist/utils/error-handler.js.map +1 -0
  173. package/dist/utils/file-operations.d.ts +297 -0
  174. package/dist/utils/file-operations.d.ts.map +1 -0
  175. package/dist/utils/file-operations.js +505 -0
  176. package/dist/utils/file-operations.js.map +1 -0
  177. package/dist/utils/frontmatter.d.ts +268 -6
  178. package/dist/utils/frontmatter.d.ts.map +1 -1
  179. package/dist/utils/frontmatter.js +500 -21
  180. package/dist/utils/frontmatter.js.map +1 -1
  181. package/dist/utils/json-formatter.d.ts +259 -0
  182. package/dist/utils/json-formatter.d.ts.map +1 -0
  183. package/dist/utils/json-formatter.js +517 -0
  184. package/dist/utils/json-formatter.js.map +1 -0
  185. package/dist/utils/logger.d.ts +176 -0
  186. package/dist/utils/logger.d.ts.map +1 -0
  187. package/dist/utils/logger.js +346 -0
  188. package/dist/utils/logger.js.map +1 -0
  189. package/dist/utils/markdown-formatter.d.ts +211 -0
  190. package/dist/utils/markdown-formatter.d.ts.map +1 -0
  191. package/dist/utils/markdown-formatter.js +482 -0
  192. package/dist/utils/markdown-formatter.js.map +1 -0
  193. package/dist/utils/path-validator.d.ts +603 -0
  194. package/dist/utils/path-validator.d.ts.map +1 -0
  195. package/dist/utils/path-validator.js +870 -0
  196. package/dist/utils/path-validator.js.map +1 -0
  197. package/dist/utils/string-formatter.d.ts +609 -0
  198. package/dist/utils/string-formatter.d.ts.map +1 -0
  199. package/dist/utils/string-formatter.js +806 -0
  200. package/dist/utils/string-formatter.js.map +1 -0
  201. package/dist/utils/type-guards.d.ts +629 -0
  202. package/dist/utils/type-guards.d.ts.map +1 -0
  203. package/dist/utils/type-guards.js +662 -0
  204. package/dist/utils/type-guards.js.map +1 -0
  205. package/docs/COMPLETE-GUIDE.md +300 -0
  206. package/docs/MODES-ARCHITECTURE.md +565 -0
  207. package/docs/PRINT-MODE-GUIDE.md +456 -0
  208. package/docs/RPC-GUIDE.md +705 -0
  209. package/docs/UTILS-IMPLEMENTATION-SUMMARY.md +647 -0
  210. package/docs/UTILS-MODULE-OVERVIEW.md +1480 -0
  211. package/docs/UTILS-QA-CHECKLIST.md +1061 -0
  212. package/docs/UTILS-USAGE-GUIDE.md +1419 -0
  213. package/package.json +1 -1
@@ -0,0 +1,543 @@
1
+ /**
2
+ * Shared Utilities for All Execution Modes
3
+ *
4
+ * This module provides common utilities, validators, and formatters used across all three
5
+ * execution modes (interactive, print, RPC).
6
+ *
7
+ * Purpose:
8
+ * --------
9
+ * - Centralize common functionality to avoid duplication
10
+ * - Ensure consistent behavior across modes
11
+ * - Provide standard data transformation and validation
12
+ *
13
+ * Usage by Modes:
14
+ * ---------------
15
+ * INTERACTIVE MODE:
16
+ * - Uses formatDuration for displaying execution times
17
+ * - Uses validatePrompt for user input validation
18
+ * - Uses extractSessionId for session switching
19
+ *
20
+ * PRINT MODE:
21
+ * - Uses createOutputFormatter to format results as JSON/text
22
+ * - Uses escapeJsonString for JSON output
23
+ * - Uses formatErrorMessage for error reporting
24
+ *
25
+ * RPC MODE:
26
+ * - Uses parseSessionPath for session resolution
27
+ * - Uses validatePrompt for incoming RPC requests
28
+ * - Uses escapeJsonString for JSON-RPC responses
29
+ *
30
+ * Architecture:
31
+ * -------------
32
+ * This module acts as a utility facade, providing a single source of truth for:
33
+ * 1. Data transformation (format functions)
34
+ * 2. Input validation (validate functions)
35
+ * 3. Error handling (error formatting)
36
+ * 4. Conversion utilities (token counts, durations)
37
+ *
38
+ * All functions are pure (no side effects) and stateless for maximum reusability.
39
+ */
40
+ /**
41
+ * Create an output formatter for a specific format type
42
+ *
43
+ * Factory function that returns a formatter implementing the OutputFormatter interface.
44
+ * Each formatter knows how to convert data to its target output format.
45
+ *
46
+ * Formats:
47
+ * - 'json': Outputs JSON lines (each event as a JSON object)
48
+ * - 'text': Outputs human-readable text with formatting
49
+ * - 'compact': Outputs minimal status information
50
+ *
51
+ * Usage:
52
+ * ------
53
+ * PRINT MODE:
54
+ * const formatter = createOutputFormatter('json');
55
+ * console.log(formatter.format(agentResponse));
56
+ * console.error(formatter.formatError(error));
57
+ *
58
+ * @param format - Output format type ('json' | 'text' | 'compact')
59
+ * @returns OutputFormatter instance for the specified format
60
+ *
61
+ * @example
62
+ * // JSON output for event streaming
63
+ * const jsonFormatter = createOutputFormatter('json');
64
+ * process.stdout.write(jsonFormatter.format(event));
65
+ *
66
+ * @example
67
+ * // Text output for human readers
68
+ * const textFormatter = createOutputFormatter('text');
69
+ * process.stdout.write(textFormatter.format(response));
70
+ *
71
+ * @throws Error if format is not recognized
72
+ */
73
+ export function createOutputFormatter(format) {
74
+ switch (format) {
75
+ case 'json':
76
+ return new JsonFormatter();
77
+ case 'text':
78
+ return new TextFormatter();
79
+ case 'compact':
80
+ return new CompactFormatter();
81
+ default:
82
+ throw new Error(`Unknown output format: ${format}`);
83
+ }
84
+ }
85
+ /**
86
+ * JSON output formatter - Machine-readable JSON lines format
87
+ * @internal
88
+ */
89
+ class JsonFormatter {
90
+ format(data) {
91
+ return JSON.stringify(data) + '\n';
92
+ }
93
+ formatError(error) {
94
+ return JSON.stringify({
95
+ type: 'error',
96
+ message: error.message,
97
+ code: error.code,
98
+ stack: process.env.DEBUG ? error.stack : undefined,
99
+ }) + '\n';
100
+ }
101
+ getContentType() {
102
+ return 'application/x-ndjson';
103
+ }
104
+ }
105
+ /**
106
+ * Text output formatter - Human-readable text format
107
+ * @internal
108
+ */
109
+ class TextFormatter {
110
+ format(data) {
111
+ if (typeof data === 'string')
112
+ return data + '\n';
113
+ if (typeof data === 'object')
114
+ return JSON.stringify(data, null, 2) + '\n';
115
+ return String(data) + '\n';
116
+ }
117
+ formatError(error) {
118
+ return `ERROR: ${error.message}\n`;
119
+ }
120
+ getContentType() {
121
+ return 'text/plain';
122
+ }
123
+ }
124
+ /**
125
+ * Compact output formatter - Minimal status output
126
+ * @internal
127
+ */
128
+ class CompactFormatter {
129
+ format(data) {
130
+ if (typeof data === 'string')
131
+ return data;
132
+ if (typeof data === 'object' && data !== null && 'message' in data) {
133
+ return data.message;
134
+ }
135
+ return String(data);
136
+ }
137
+ formatError(error) {
138
+ return `ERROR: ${error.message}`;
139
+ }
140
+ getContentType() {
141
+ return 'text/plain';
142
+ }
143
+ }
144
+ /**
145
+ * Parse and validate a session path/ID
146
+ *
147
+ * Extracts and validates session identifiers. Supports multiple formats:
148
+ * - UUID: "550e8400-e29b-41d4-a716-446655440000"
149
+ * - Filename: "session-123.jsonl"
150
+ * - Path: "/home/user/.indusagi/sessions/session-123.jsonl"
151
+ *
152
+ * Returns parsed session ID or throws ValidationError if invalid.
153
+ *
154
+ * Usage:
155
+ * ------
156
+ * RPC MODE:
157
+ * const sessionId = parseSessionPath(request.params.sessionId);
158
+ * const session = sessionManager.load(sessionId);
159
+ *
160
+ * INTERACTIVE MODE:
161
+ * const sessionId = parseSessionPath(userInput);
162
+ * // Used in session switching command
163
+ *
164
+ * @param path - Session ID, filename, or filesystem path
165
+ * @returns Normalized session UUID
166
+ *
167
+ * @example
168
+ * parseSessionPath('550e8400-e29b-41d4-a716-446655440000')
169
+ * // Returns: '550e8400-e29b-41d4-a716-446655440000'
170
+ *
171
+ * @example
172
+ * parseSessionPath('session-123.jsonl')
173
+ * // Returns: 'session-123' (extracts ID from filename)
174
+ *
175
+ * @throws Error if path is empty or invalid format
176
+ */
177
+ export function parseSessionPath(path) {
178
+ if (!path || typeof path !== 'string') {
179
+ throw new Error('Session path must be a non-empty string');
180
+ }
181
+ // Remove file extensions if present
182
+ let sessionId = path.replace(/\.jsonl?$/, '');
183
+ // Extract from filesystem path
184
+ const match = sessionId.match(/([a-zA-Z0-9\-]+)$/);
185
+ if (match) {
186
+ sessionId = match[1];
187
+ }
188
+ // Validate format (UUID or session-id format)
189
+ if (!/^[a-zA-Z0-9\-]{20,}$/.test(sessionId)) {
190
+ throw new Error(`Invalid session ID format: ${sessionId}`);
191
+ }
192
+ return sessionId;
193
+ }
194
+ /**
195
+ * Validate user input/prompt
196
+ *
197
+ * Checks if a prompt is valid for sending to the agent.
198
+ *
199
+ * Validation rules:
200
+ * - Non-empty (after trimming whitespace)
201
+ * - No longer than 100,000 characters (prevents DOS)
202
+ * - Must be a string
203
+ *
204
+ * Returns ValidationResult with detailed error information if invalid.
205
+ *
206
+ * Usage:
207
+ * ------
208
+ * INTERACTIVE MODE:
209
+ * const result = validatePrompt(userInput);
210
+ * if (!result.valid) {
211
+ * showError(result.error);
212
+ * return;
213
+ * }
214
+ * session.prompt(userInput);
215
+ *
216
+ * RPC MODE:
217
+ * const result = validatePrompt(request.message);
218
+ * if (!result.valid) {
219
+ * return rpcError(-32602, result.error);
220
+ * }
221
+ *
222
+ * @param prompt - User input to validate
223
+ * @returns ValidationResult with valid flag and optional error message
224
+ *
225
+ * @example
226
+ * validatePrompt('What does this code do?')
227
+ * // Returns: { valid: true }
228
+ *
229
+ * @example
230
+ * validatePrompt(' ')
231
+ * // Returns: { valid: false, error: 'Prompt cannot be empty' }
232
+ *
233
+ * @example
234
+ * validatePrompt('x'.repeat(100001))
235
+ * // Returns: { valid: false, error: 'Prompt exceeds maximum length (100000)' }
236
+ */
237
+ export function validatePrompt(prompt) {
238
+ if (typeof prompt !== 'string') {
239
+ return {
240
+ valid: false,
241
+ error: 'Prompt must be a string',
242
+ details: { received: typeof prompt },
243
+ };
244
+ }
245
+ const trimmed = prompt.trim();
246
+ if (trimmed.length === 0) {
247
+ return {
248
+ valid: false,
249
+ error: 'Prompt cannot be empty',
250
+ };
251
+ }
252
+ const MAX_LENGTH = 100000;
253
+ if (prompt.length > MAX_LENGTH) {
254
+ return {
255
+ valid: false,
256
+ error: `Prompt exceeds maximum length (${MAX_LENGTH})`,
257
+ details: { length: prompt.length, max: MAX_LENGTH },
258
+ };
259
+ }
260
+ return { valid: true };
261
+ }
262
+ /**
263
+ * Format milliseconds to human-readable duration string
264
+ *
265
+ * Converts milliseconds into a readable format like "1h 23m 45s 123ms".
266
+ * Omits zero values and units for brevity.
267
+ *
268
+ * Examples:
269
+ * - 1000 ms → "1s"
270
+ * - 61000 ms → "1m 1s"
271
+ * - 3661000 ms → "1h 1m 1s"
272
+ *
273
+ * Usage:
274
+ * ------
275
+ * INTERACTIVE MODE:
276
+ * const elapsed = Date.now() - startTime;
277
+ * footer.text = `Completed in ${formatDuration(elapsed)}`;
278
+ *
279
+ * PRINT MODE:
280
+ * console.log(`"elapsed_time": "${formatDuration(duration)}"`);
281
+ *
282
+ * @param ms - Duration in milliseconds
283
+ * @returns Formatted duration string
284
+ *
285
+ * @example
286
+ * formatDuration(1234567) // "20m 34s 567ms"
287
+ *
288
+ * @example
289
+ * formatDuration(5000) // "5s"
290
+ *
291
+ * @example
292
+ * formatDuration(0) // "0ms"
293
+ */
294
+ export function formatDuration(ms) {
295
+ if (ms < 0)
296
+ return '0ms';
297
+ const units = [
298
+ { name: 'h', ms: 3600000 },
299
+ { name: 'm', ms: 60000 },
300
+ { name: 's', ms: 1000 },
301
+ { name: 'ms', ms: 1 },
302
+ ];
303
+ const parts = [];
304
+ let remaining = ms;
305
+ for (const unit of units) {
306
+ if (remaining >= unit.ms) {
307
+ const value = Math.floor(remaining / unit.ms);
308
+ parts.push(`${value}${unit.name}`);
309
+ remaining %= unit.ms;
310
+ }
311
+ }
312
+ return parts.length > 0 ? parts.join(' ') : '0ms';
313
+ }
314
+ /**
315
+ * Format token count with usage estimates
316
+ *
317
+ * Converts a token count into a readable format with estimates of cost/usage:
318
+ * - Shows raw token count
319
+ * - Estimates word count (roughly 0.75 words per token)
320
+ * - Estimates cost at $0.002 per 1000 tokens (GPT-4 average)
321
+ *
322
+ * Useful for displaying resource usage in logs and UI.
323
+ *
324
+ * Usage:
325
+ * ------
326
+ * PRINT MODE:
327
+ * const formatted = formatTokens(response.usage.total_tokens);
328
+ * console.log(`Tokens: ${formatted}`);
329
+ *
330
+ * INTERACTIVE MODE:
331
+ * footer.text = `Tokens: ${formatTokens(totalTokens)}`;
332
+ *
333
+ * @param tokens - Number of tokens used
334
+ * @returns Formatted string with tokens, word estimate, and cost estimate
335
+ *
336
+ * @example
337
+ * formatTokens(1500)
338
+ * // Returns: "1,500 tokens (~1,125 words, ~$0.003 cost)"
339
+ *
340
+ * @example
341
+ * formatTokens(0)
342
+ * // Returns: "0 tokens"
343
+ */
344
+ export function formatTokens(tokens) {
345
+ if (tokens === 0)
346
+ return '0 tokens';
347
+ const formatted = tokens.toLocaleString();
348
+ const wordEstimate = Math.round(tokens * 0.75);
349
+ const costEstimate = (tokens / 1000) * 0.002;
350
+ return `${formatted} tokens (~${wordEstimate.toLocaleString()} words, ~$${costEstimate.toFixed(3)} cost)`;
351
+ }
352
+ /**
353
+ * Extract session ID from user input
354
+ *
355
+ * Parses user input to extract a session ID. Handles multiple formats:
356
+ * - Direct UUID
357
+ * - Filenames with session ID
358
+ * - Prefixes like "session:uuid"
359
+ *
360
+ * Returns null if no valid session ID found.
361
+ *
362
+ * Usage:
363
+ * ------
364
+ * INTERACTIVE MODE:
365
+ * // User types: "/switch 550e8400-e29b-41d4-a716-446655440000"
366
+ * const sessionId = extractSessionId(userInput);
367
+ * if (sessionId) interactiveMode.switchSession(sessionId);
368
+ *
369
+ * @param input - User input string
370
+ * @returns Session ID if found, null otherwise
371
+ *
372
+ * @example
373
+ * extractSessionId('session:550e8400-e29b-41d4-a716-446655440000')
374
+ * // Returns: '550e8400-e29b-41d4-a716-446655440000'
375
+ *
376
+ * @example
377
+ * extractSessionId('/switch my-session-123')
378
+ * // Returns: 'my-session-123'
379
+ *
380
+ * @example
381
+ * extractSessionId('invalid input')
382
+ * // Returns: null
383
+ */
384
+ export function extractSessionId(input) {
385
+ if (!input || typeof input !== 'string')
386
+ return null;
387
+ // Try to match UUID directly
388
+ const uuidMatch = input.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
389
+ if (uuidMatch)
390
+ return uuidMatch[0];
391
+ // Try to match session:id format
392
+ const prefixMatch = input.match(/session:([a-zA-Z0-9\-]+)/);
393
+ if (prefixMatch)
394
+ return prefixMatch[1];
395
+ // Try to match standalone session ID (alphanumeric with hyphens)
396
+ const idMatch = input.match(/^([a-zA-Z0-9\-]{20,})$/);
397
+ if (idMatch)
398
+ return idMatch[1];
399
+ return null;
400
+ }
401
+ /**
402
+ * Escape string for safe JSON output
403
+ *
404
+ * Properly escapes special characters for JSON strings:
405
+ * - Quotes: " → \"
406
+ * - Backslashes: \ → \\
407
+ * - Newlines: \n, \r, \t etc
408
+ * - Control characters
409
+ *
410
+ * Usage:
411
+ * ------
412
+ * PRINT MODE:
413
+ * const jsonLine = `{"message": "${escapeJsonString(message)}"}`;
414
+ * console.log(jsonLine);
415
+ *
416
+ * RPC MODE:
417
+ * const response = {
418
+ * message: escapeJsonString(plainText)
419
+ * };
420
+ * sendJsonRpc(response);
421
+ *
422
+ * @param str - String to escape
423
+ * @returns Escaped string safe for JSON output
424
+ *
425
+ * @example
426
+ * escapeJsonString('Hello "World"\n')
427
+ * // Returns: 'Hello \"World\"\\n'
428
+ *
429
+ * @example
430
+ * escapeJsonString('Path: C:\\Users\\test')
431
+ * // Returns: 'Path: C:\\\\Users\\\\test'
432
+ */
433
+ export function escapeJsonString(str) {
434
+ if (typeof str !== 'string')
435
+ return '';
436
+ return str
437
+ .replace(/\\/g, '\\\\')
438
+ .replace(/"/g, '\\"')
439
+ .replace(/\n/g, '\\n')
440
+ .replace(/\r/g, '\\r')
441
+ .replace(/\t/g, '\\t')
442
+ .replace(/\f/g, '\\f')
443
+ .replace(/\b/g, '\\b');
444
+ }
445
+ /**
446
+ * Format error message for display
447
+ *
448
+ * Converts an Error object into a formatted error message suitable for:
449
+ * - Terminal display (interactive mode)
450
+ * - Log output (print mode)
451
+ * - JSON responses (RPC mode)
452
+ *
453
+ * Includes error message, type, and optional stack trace (if DEBUG enabled).
454
+ *
455
+ * Usage:
456
+ * ------
457
+ * INTERACTIVE MODE:
458
+ * try {
459
+ * await session.prompt(input);
460
+ * } catch (error) {
461
+ * displayError(formatErrorMessage(error));
462
+ * }
463
+ *
464
+ * PRINT MODE:
465
+ * process.stderr.write(formatErrorMessage(error));
466
+ * process.exit(1);
467
+ *
468
+ * RPC MODE:
469
+ * catch (error) {
470
+ * response.error = formatErrorMessage(error);
471
+ * }
472
+ *
473
+ * @param error - Error object or string
474
+ * @returns Formatted error message string
475
+ *
476
+ * @example
477
+ * const error = new Error('Invalid session');
478
+ * formatErrorMessage(error)
479
+ * // Returns: 'Error: Invalid session'
480
+ *
481
+ * @example
482
+ * formatErrorMessage('Something went wrong')
483
+ * // Returns: 'Something went wrong'
484
+ */
485
+ export function formatErrorMessage(error) {
486
+ if (error instanceof Error) {
487
+ let message = `${error.name}: ${error.message}`;
488
+ if (process.env.DEBUG) {
489
+ message += '\n' + (error.stack || '');
490
+ }
491
+ return message;
492
+ }
493
+ if (typeof error === 'string') {
494
+ return error;
495
+ }
496
+ return String(error);
497
+ }
498
+ /**
499
+ * Determine if session should be auto-saved
500
+ *
501
+ * Checks configuration and state to determine whether to automatically persist
502
+ * the session to disk.
503
+ *
504
+ * Auto-save is enabled when:
505
+ * - config.autoSave is true
506
+ * - Session has been modified (dirty flag)
507
+ * - Minimum time interval has passed since last save
508
+ *
509
+ * Usage:
510
+ * ------
511
+ * INTERACTIVE MODE:
512
+ * if (shouldAutoSave(config)) {
513
+ * await sessionManager.save(session);
514
+ * }
515
+ *
516
+ * @param config - Configuration object with autoSave setting
517
+ * @returns Boolean indicating if session should be auto-saved
518
+ *
519
+ * @example
520
+ * const config = { autoSave: true, autoSaveInterval: 30000 };
521
+ * if (shouldAutoSave(config)) {
522
+ * sessionManager.save(currentSession);
523
+ * }
524
+ */
525
+ export function shouldAutoSave(config) {
526
+ if (!config)
527
+ return false;
528
+ if (typeof config.autoSave !== 'boolean')
529
+ return false;
530
+ return config.autoSave === true;
531
+ }
532
+ export default {
533
+ createOutputFormatter,
534
+ parseSessionPath,
535
+ validatePrompt,
536
+ formatDuration,
537
+ formatTokens,
538
+ extractSessionId,
539
+ escapeJsonString,
540
+ formatErrorMessage,
541
+ shouldAutoSave,
542
+ };
543
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/modes/shared.ts"],"names":[],"mappings":"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"}