wave-agent-sdk 1.0.4 → 1.0.6

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 (194) hide show
  1. package/dist/agent.d.ts +23 -1
  2. package/dist/agent.js +34 -4
  3. package/dist/managers/aiManager.d.ts +36 -0
  4. package/dist/managers/aiManager.js +240 -8
  5. package/dist/managers/messageManager.d.ts +1 -3
  6. package/dist/managers/messageManager.js +19 -9
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/services/session.js +2 -9
  10. package/dist/telemetry/instrumentation.d.ts +1 -1
  11. package/dist/telemetry/instrumentation.js +57 -28
  12. package/dist/tools/bashTool.js +16 -6
  13. package/dist/tools/grepTool.js +11 -3
  14. package/dist/types/index.d.ts +3 -0
  15. package/dist/types/messaging.d.ts +2 -0
  16. package/dist/utils/containerSetup.js +0 -3
  17. package/dist/utils/messageOperations.d.ts +10 -1
  18. package/dist/utils/messageOperations.js +19 -1
  19. package/dist/utils/notificationXml.js +5 -0
  20. package/dist/utils/ripgrep.d.ts +4 -4
  21. package/dist/utils/ripgrep.js +4 -27
  22. package/package.json +20 -8
  23. package/bin/rg +0 -79
  24. package/dist/managers/forkedAgentManager.d.ts +0 -50
  25. package/dist/managers/forkedAgentManager.js +0 -130
  26. package/scripts/install_ripgrep.js +0 -111
  27. package/scripts/postinstall.js +0 -38
  28. package/src/agent.ts +0 -1271
  29. package/src/constants/subagents.ts +0 -4
  30. package/src/constants/toolLimits.ts +0 -15
  31. package/src/constants/tools.ts +0 -24
  32. package/src/core/plugin.ts +0 -237
  33. package/src/core/session.ts +0 -9
  34. package/src/index.ts +0 -38
  35. package/src/managers/MemoryRuleManager.ts +0 -198
  36. package/src/managers/aiManager.ts +0 -2429
  37. package/src/managers/backgroundTaskManager.ts +0 -480
  38. package/src/managers/bangManager.ts +0 -119
  39. package/src/managers/cronManager.ts +0 -401
  40. package/src/managers/foregroundTaskManager.ts +0 -30
  41. package/src/managers/forkedAgentManager.ts +0 -216
  42. package/src/managers/hookManager.ts +0 -1089
  43. package/src/managers/liveConfigManager.ts +0 -445
  44. package/src/managers/lspManager.ts +0 -470
  45. package/src/managers/mcpManager.ts +0 -1010
  46. package/src/managers/messageManager.ts +0 -1180
  47. package/src/managers/messageQueue.ts +0 -157
  48. package/src/managers/permissionManager.ts +0 -1140
  49. package/src/managers/planManager.ts +0 -112
  50. package/src/managers/pluginManager.ts +0 -330
  51. package/src/managers/pluginScopeManager.ts +0 -124
  52. package/src/managers/reversionManager.ts +0 -178
  53. package/src/managers/skillManager.ts +0 -578
  54. package/src/managers/slashCommandManager.ts +0 -564
  55. package/src/managers/subagentManager.ts +0 -915
  56. package/src/managers/toolManager.ts +0 -489
  57. package/src/managers/workflowManager.ts +0 -493
  58. package/src/prompts/autoMemory.ts +0 -33
  59. package/src/prompts/autoMemoryExtraction.ts +0 -146
  60. package/src/prompts/index.ts +0 -506
  61. package/src/prompts/planModeReminders.ts +0 -126
  62. package/src/services/GitService.ts +0 -131
  63. package/src/services/MarketplaceService.ts +0 -1052
  64. package/src/services/MemoryRuleService.ts +0 -71
  65. package/src/services/aiService.ts +0 -919
  66. package/src/services/authService.ts +0 -565
  67. package/src/services/autoMemoryService.ts +0 -280
  68. package/src/services/configurationService.ts +0 -1520
  69. package/src/services/fileWatcher.ts +0 -319
  70. package/src/services/hook.ts +0 -348
  71. package/src/services/initializationService.ts +0 -357
  72. package/src/services/interactionService.ts +0 -229
  73. package/src/services/jsonlHandler.ts +0 -295
  74. package/src/services/memory.ts +0 -234
  75. package/src/services/pluginLoader.ts +0 -252
  76. package/src/services/remoteSettingsService.ts +0 -366
  77. package/src/services/reversionService.ts +0 -122
  78. package/src/services/session.ts +0 -922
  79. package/src/services/taskManager.ts +0 -321
  80. package/src/telemetry/events.ts +0 -65
  81. package/src/telemetry/instrumentation.ts +0 -499
  82. package/src/telemetry/sessionTracing.ts +0 -348
  83. package/src/tools/agentTool.ts +0 -322
  84. package/src/tools/askUserQuestion.ts +0 -160
  85. package/src/tools/bashTool.ts +0 -621
  86. package/src/tools/buildTool.ts +0 -61
  87. package/src/tools/cronCreateTool.ts +0 -161
  88. package/src/tools/cronDeleteTool.ts +0 -51
  89. package/src/tools/cronListTool.ts +0 -47
  90. package/src/tools/editTool.ts +0 -313
  91. package/src/tools/enterPlanMode.ts +0 -124
  92. package/src/tools/enterWorktreeTool.ts +0 -190
  93. package/src/tools/exitPlanMode.ts +0 -139
  94. package/src/tools/exitWorktreeTool.ts +0 -236
  95. package/src/tools/globTool.ts +0 -167
  96. package/src/tools/grepTool.ts +0 -399
  97. package/src/tools/lspTool.ts +0 -883
  98. package/src/tools/readTool.ts +0 -426
  99. package/src/tools/skillTool.ts +0 -248
  100. package/src/tools/taskManagementTools.ts +0 -574
  101. package/src/tools/taskStopTool.ts +0 -78
  102. package/src/tools/types.ts +0 -137
  103. package/src/tools/webFetchTool.ts +0 -373
  104. package/src/tools/workflowTool.ts +0 -205
  105. package/src/tools/writeTool.ts +0 -267
  106. package/src/types/agent.ts +0 -128
  107. package/src/types/auth.ts +0 -19
  108. package/src/types/commands.ts +0 -30
  109. package/src/types/config.ts +0 -43
  110. package/src/types/configuration.ts +0 -168
  111. package/src/types/core.ts +0 -105
  112. package/src/types/cron.ts +0 -13
  113. package/src/types/environment.ts +0 -106
  114. package/src/types/fileSearch.ts +0 -4
  115. package/src/types/history.ts +0 -7
  116. package/src/types/hooks.ts +0 -256
  117. package/src/types/index.ts +0 -41
  118. package/src/types/lsp.ts +0 -98
  119. package/src/types/marketplace.ts +0 -71
  120. package/src/types/mcp.ts +0 -43
  121. package/src/types/memoryRule.ts +0 -31
  122. package/src/types/messaging.ts +0 -114
  123. package/src/types/permissions.ts +0 -80
  124. package/src/types/plugins.ts +0 -39
  125. package/src/types/processes.ts +0 -99
  126. package/src/types/reversion.ts +0 -29
  127. package/src/types/session.ts +0 -12
  128. package/src/types/skills.ts +0 -108
  129. package/src/types/tasks.ts +0 -13
  130. package/src/types/telemetry.ts +0 -98
  131. package/src/types/tools.ts +0 -44
  132. package/src/types/workflow.ts +0 -6
  133. package/src/utils/abortUtils.ts +0 -118
  134. package/src/utils/atomicWrite.ts +0 -61
  135. package/src/utils/bashParser.ts +0 -899
  136. package/src/utils/cacheControlUtils.ts +0 -426
  137. package/src/utils/commandArgumentParser.ts +0 -161
  138. package/src/utils/commandPathResolver.ts +0 -88
  139. package/src/utils/configPaths.ts +0 -220
  140. package/src/utils/configValidator.ts +0 -100
  141. package/src/utils/constants.ts +0 -39
  142. package/src/utils/container.ts +0 -92
  143. package/src/utils/containerSetup.ts +0 -371
  144. package/src/utils/convertMessagesForAPI.ts +0 -326
  145. package/src/utils/cronTasks.ts +0 -128
  146. package/src/utils/cronTasksLock.ts +0 -182
  147. package/src/utils/cronToHuman.ts +0 -99
  148. package/src/utils/customCommands.ts +0 -95
  149. package/src/utils/editUtils.ts +0 -20
  150. package/src/utils/fileFormat.ts +0 -40
  151. package/src/utils/fileSearch.ts +0 -151
  152. package/src/utils/fileUtils.ts +0 -223
  153. package/src/utils/gitUtils.ts +0 -315
  154. package/src/utils/globalLogger.ts +0 -128
  155. package/src/utils/groupMessagesByApiRound.ts +0 -121
  156. package/src/utils/hookMatcher.ts +0 -176
  157. package/src/utils/markdownParser.ts +0 -317
  158. package/src/utils/mcpUtils.ts +0 -138
  159. package/src/utils/messageOperations.ts +0 -641
  160. package/src/utils/modelCapabilities.ts +0 -30
  161. package/src/utils/nameGenerator.ts +0 -99
  162. package/src/utils/notificationXml.ts +0 -57
  163. package/src/utils/openaiClient.ts +0 -251
  164. package/src/utils/parseCronExpression.ts +0 -78
  165. package/src/utils/path.ts +0 -72
  166. package/src/utils/pathEncoder.ts +0 -400
  167. package/src/utils/pathSafety.ts +0 -40
  168. package/src/utils/promptHistory.ts +0 -168
  169. package/src/utils/ripgrep.ts +0 -35
  170. package/src/utils/shellResolver.ts +0 -190
  171. package/src/utils/skillParser.ts +0 -246
  172. package/src/utils/stringUtils.ts +0 -156
  173. package/src/utils/subagentParser.ts +0 -279
  174. package/src/utils/taskReminder.ts +0 -96
  175. package/src/utils/tokenCalculation.ts +0 -43
  176. package/src/utils/tokenEstimate.ts +0 -34
  177. package/src/utils/toolResultStorage.ts +0 -117
  178. package/src/utils/worktreeSession.ts +0 -26
  179. package/src/utils/worktreeUtils.ts +0 -786
  180. package/src/workflow/budgetTracker.ts +0 -34
  181. package/src/workflow/concurrencyLimiter.ts +0 -47
  182. package/src/workflow/journal.ts +0 -95
  183. package/src/workflow/progressReporter.ts +0 -141
  184. package/src/workflow/runState.ts +0 -65
  185. package/src/workflow/scriptRuntime.ts +0 -274
  186. package/src/workflow/structuredOutput.ts +0 -123
  187. package/src/workflow/types.ts +0 -101
  188. package/src/workflow/workflowApis.ts +0 -410
  189. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  190. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  191. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  192. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  193. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  194. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -1,426 +0,0 @@
1
- /**
2
- * Cache Control Utilities for Claude Models
3
- *
4
- * This module provides utilities for adding cache_control markers to Claude models
5
- * to optimize token usage and reduce costs. Cache control is only applied to Claude
6
- * models and preserves backward compatibility with existing message formats.
7
- */
8
-
9
- import type {
10
- ChatCompletionMessageParam,
11
- ChatCompletionContentPart,
12
- ChatCompletionContentPartText,
13
- CompletionUsage,
14
- } from "openai/resources";
15
- import { logger } from "./globalLogger.js";
16
- import { supportsPromptCaching } from "./modelCapabilities.js";
17
- import type { ModelCapabilities } from "../types/config.js";
18
-
19
- // ============================================================================
20
- // Core Types
21
- // ============================================================================
22
-
23
- /**
24
- * Cache control directive for Claude models
25
- */
26
- export interface CacheControl {
27
- type: "ephemeral";
28
- }
29
-
30
- /**
31
- * Extended text content part with cache control support
32
- */
33
- export interface ClaudeChatCompletionContentPartText
34
- extends ChatCompletionContentPartText {
35
- type: "text";
36
- text: string;
37
- cache_control?: CacheControl;
38
- }
39
-
40
- /**
41
- * Extended prompt_tokens_details with cache_creation_input_tokens
42
- * Some models (e.g. Gemini, DeepSeek) return this field inside prompt_tokens_details
43
- */
44
- export interface ExtendedPromptTokensDetails
45
- extends CompletionUsage.PromptTokensDetails {
46
- cache_creation_input_tokens?: number;
47
- }
48
-
49
- /**
50
- * Enhanced usage metrics including cache information
51
- * Supports both Claude-specific top-level fields and OpenAI-standard prompt_tokens_details
52
- */
53
- export interface ClaudeUsage extends CompletionUsage {
54
- prompt_tokens: number;
55
- completion_tokens: number;
56
- total_tokens: number;
57
-
58
- // Cache extensions (from Claude top-level or OpenAI prompt_tokens_details)
59
- cache_read_input_tokens?: number; // Claude: cache_read_input_tokens / OpenAI: prompt_tokens_details.cached_tokens
60
- cache_creation_input_tokens?: number; // Claude: cache_creation_input_tokens / OpenAI: prompt_tokens_details.cache_creation_input_tokens
61
- cache_creation?: {
62
- ephemeral_5m_input_tokens: number;
63
- ephemeral_1h_input_tokens: number;
64
- };
65
-
66
- // Override prompt_tokens_details to include cache_creation_input_tokens
67
- prompt_tokens_details?: ExtendedPromptTokensDetails;
68
- }
69
-
70
- // ============================================================================
71
- // Default Configuration
72
- // ============================================================================
73
-
74
- // ============================================================================
75
- // Utility Functions (Basic Structure - to be implemented)
76
- // ============================================================================
77
-
78
- /**
79
- * Validates cache control structure
80
- * @param control - Object to validate
81
- * @returns True if valid cache control object
82
- */
83
- export function isValidCacheControl(control: unknown): control is CacheControl {
84
- return (
85
- control !== null &&
86
- typeof control === "object" &&
87
- control !== undefined &&
88
- "type" in control &&
89
- (control as { type: unknown }).type === "ephemeral"
90
- );
91
- }
92
-
93
- /**
94
- * Adds cache control markers to message content
95
- * @param content - Original content (string or structured)
96
- * @param shouldCache - Whether to add cache control
97
- * @returns Structured content with cache control markers
98
- */
99
- export function addCacheControlToContent(
100
- content: string | ChatCompletionContentPart[],
101
- shouldCache: boolean,
102
- ): ClaudeChatCompletionContentPartText[] {
103
- // Handle null/undefined content
104
- if (content == null) {
105
- return [];
106
- }
107
-
108
- // If shouldCache is false, return content as text parts without cache control
109
- if (!shouldCache) {
110
- if (typeof content === "string") {
111
- return [{ type: "text", text: content }];
112
- }
113
-
114
- // Validate array input
115
- if (!Array.isArray(content)) {
116
- logger.warn(
117
- "Invalid content type for cache control transformation:",
118
- typeof content,
119
- );
120
- return [];
121
- }
122
-
123
- // Filter and convert only text parts with validation
124
- return content
125
- .filter((part): part is ChatCompletionContentPartText => {
126
- if (!part || typeof part !== "object") {
127
- return false;
128
- }
129
- return part.type === "text" && typeof part.text === "string";
130
- })
131
- .map((part) => ({ type: "text", text: part.text }));
132
- }
133
-
134
- // shouldCache is true - add cache control markers
135
- if (typeof content === "string") {
136
- // Transform string content to structured array with cache control
137
- return [
138
- {
139
- type: "text",
140
- text: content,
141
- cache_control: { type: "ephemeral" },
142
- },
143
- ];
144
- }
145
-
146
- // Validate array input
147
- if (!Array.isArray(content)) {
148
- logger.warn(
149
- "Invalid content type for cache control transformation:",
150
- typeof content,
151
- );
152
- return [];
153
- }
154
-
155
- // Handle structured content - preserve all parts, add cache control to last text part only
156
- let lastTextIndex = -1;
157
- for (let i = content.length - 1; i >= 0; i--) {
158
- const part = content[i];
159
- if (
160
- part &&
161
- typeof part === "object" &&
162
- part.type === "text" &&
163
- typeof (part as ChatCompletionContentPartText).text === "string"
164
- ) {
165
- lastTextIndex = i;
166
- break;
167
- }
168
- }
169
-
170
- return content.map((part, index) => {
171
- if (
172
- index === lastTextIndex &&
173
- part &&
174
- typeof part === "object" &&
175
- part.type === "text" &&
176
- typeof (part as ChatCompletionContentPartText).text === "string"
177
- ) {
178
- return {
179
- ...(part as ChatCompletionContentPartText),
180
- cache_control: { type: "ephemeral" },
181
- };
182
- }
183
- return part;
184
- }) as ClaudeChatCompletionContentPartText[];
185
- }
186
-
187
- /**
188
- * Counts the total number of content blocks across all messages.
189
- * Each element in a message's content array counts as one block.
190
- * String content counts as one block. Null/undefined content counts as zero
191
- * (e.g. assistant messages with only tool_calls).
192
- * @param messages - Array of chat messages
193
- * @returns Total content block count
194
- */
195
- export function countContentBlocks(
196
- messages: ChatCompletionMessageParam[],
197
- ): number {
198
- let count = 0;
199
- for (const message of messages) {
200
- if (!message || typeof message !== "object") continue;
201
- const content = message.content;
202
- if (typeof content === "string") {
203
- count += 1;
204
- } else if (Array.isArray(content)) {
205
- count += content.length;
206
- }
207
- }
208
- return count;
209
- }
210
-
211
- /**
212
- * Transforms messages for explicit cache control.
213
- *
214
- * Cache breakpoints (following Claude Code's "last message marker" strategy):
215
- * 1. System message — always marked (stable prefix)
216
- * 2. Last message — the last user/assistant message with content is marked.
217
- * The API scans backward from this marker within 20 blocks. Since each
218
- * turn adds ~2 blocks (< 20), the previous request's marker position
219
- * always falls within the scan window, ensuring cache hits.
220
- *
221
- * Tools are marked separately via addCacheControlToLastTool (called by aiService).
222
- *
223
- * @param messages - Original OpenAI message array
224
- * @param capabilities - Declarative model capabilities for cache detection
225
- * @returns Messages with cache control markers applied
226
- */
227
- export function transformMessagesForExplicitCache(
228
- messages: ChatCompletionMessageParam[],
229
- capabilities?: ModelCapabilities,
230
- ): ChatCompletionMessageParam[] {
231
- // Validate inputs
232
- if (!messages || !Array.isArray(messages)) {
233
- logger.warn(
234
- "Invalid messages array provided to transformMessagesForExplicitCache",
235
- );
236
- return [];
237
- }
238
-
239
- if (messages.length === 0) {
240
- return [];
241
- }
242
-
243
- // Only apply cache control for models that support prompt caching
244
- if (!supportsPromptCaching(capabilities)) {
245
- return messages;
246
- }
247
-
248
- // Find first system message index
249
- const firstSystemIndex = messages.findIndex((m) => m.role === "system");
250
-
251
- // Determine which message indices should receive cache_control markers
252
- const cacheIndices = new Set<number>();
253
-
254
- // Marker 1: First system message (always — stable prefix)
255
- if (firstSystemIndex !== -1) {
256
- cacheIndices.add(firstSystemIndex);
257
- }
258
-
259
- // Marker 2: Last message with content (user or assistant).
260
- // This marker moves each turn (~2 blocks), but since the move is < 20 blocks,
261
- // the previous marker position is always within the API's 20-block scan window.
262
- // This ensures the entire conversation prefix is cached.
263
- for (let i = messages.length - 1; i >= 0; i--) {
264
- const msg = messages[i];
265
- if (!msg || typeof msg !== "object") continue;
266
- if (i === firstSystemIndex) continue;
267
- if (msg.role !== "user" && msg.role !== "assistant") continue;
268
-
269
- const content = msg.content;
270
- const hasContent =
271
- (typeof content === "string" && content.length > 0) ||
272
- (Array.isArray(content) && content.length > 0);
273
-
274
- if (hasContent) {
275
- cacheIndices.add(i);
276
- break;
277
- }
278
- }
279
-
280
- // Apply cache_control markers to selected messages
281
- const result = messages.map((message, index) => {
282
- // Validate message structure
283
- if (!message || typeof message !== "object" || !message.role) {
284
- logger.warn("Invalid message structure at index", index, ":", message);
285
- return message;
286
- }
287
-
288
- if (!cacheIndices.has(index)) {
289
- return message;
290
- }
291
-
292
- const content =
293
- (message.content as string | ChatCompletionContentPart[]) || "";
294
-
295
- // Idempotency: skip if content already has cache_control (system message)
296
- if (message.role === "system" && Array.isArray(content)) {
297
- const hasCacheControl = content.some(
298
- (part) =>
299
- part.type === "text" &&
300
- (part as ClaudeChatCompletionContentPartText).cache_control,
301
- );
302
- if (hasCacheControl) {
303
- return message;
304
- }
305
- }
306
-
307
- const transformedContent = addCacheControlToContent(content, true);
308
-
309
- return {
310
- ...message,
311
- content: transformedContent,
312
- } as ChatCompletionMessageParam;
313
- });
314
-
315
- return result;
316
- }
317
-
318
- /**
319
- * Extends standard usage with cache metrics
320
- * Extracts cache tokens from both Claude-specific top-level fields and
321
- * OpenAI-standard prompt_tokens_details (used by Gemini, DeepSeek, etc.)
322
- * @param standardUsage - OpenAI usage response
323
- * @param cacheMetrics - Additional cache metrics from the API response
324
- * @returns Extended usage with cache information
325
- */
326
- export function extendUsageWithCacheMetrics(
327
- standardUsage: CompletionUsage,
328
- cacheMetrics?: Partial<ClaudeUsage>,
329
- ): ClaudeUsage {
330
- const baseUsage: ClaudeUsage = {
331
- prompt_tokens: standardUsage.prompt_tokens,
332
- completion_tokens: standardUsage.completion_tokens,
333
- total_tokens: standardUsage.total_tokens,
334
- };
335
-
336
- if (!cacheMetrics) {
337
- return baseUsage;
338
- }
339
-
340
- // Extract cache_read_input_tokens from Claude top-level field
341
- if (typeof cacheMetrics.cache_read_input_tokens === "number") {
342
- baseUsage.cache_read_input_tokens = cacheMetrics.cache_read_input_tokens;
343
- }
344
- // Fallback to prompt_tokens_details.cached_tokens (OpenAI standard)
345
- else if (cacheMetrics.prompt_tokens_details?.cached_tokens != null) {
346
- baseUsage.cache_read_input_tokens =
347
- cacheMetrics.prompt_tokens_details.cached_tokens;
348
- }
349
-
350
- // Extract cache_creation_input_tokens from Claude top-level field
351
- if (typeof cacheMetrics.cache_creation_input_tokens === "number") {
352
- baseUsage.cache_creation_input_tokens =
353
- cacheMetrics.cache_creation_input_tokens;
354
- }
355
- // Fallback to prompt_tokens_details.cache_creation_input_tokens
356
- else if (
357
- cacheMetrics.prompt_tokens_details?.cache_creation_input_tokens != null
358
- ) {
359
- baseUsage.cache_creation_input_tokens =
360
- cacheMetrics.prompt_tokens_details.cache_creation_input_tokens;
361
- }
362
-
363
- // Extract cache_creation breakdown (Claude-specific)
364
- if (
365
- cacheMetrics.cache_creation &&
366
- typeof cacheMetrics.cache_creation.ephemeral_5m_input_tokens === "number" &&
367
- typeof cacheMetrics.cache_creation.ephemeral_1h_input_tokens === "number"
368
- ) {
369
- baseUsage.cache_creation = {
370
- ephemeral_5m_input_tokens:
371
- cacheMetrics.cache_creation.ephemeral_5m_input_tokens,
372
- ephemeral_1h_input_tokens:
373
- cacheMetrics.cache_creation.ephemeral_1h_input_tokens,
374
- };
375
- }
376
-
377
- return baseUsage;
378
- }
379
-
380
- /**
381
- * Validates Claude usage structure
382
- * @param usage - Usage object to validate
383
- * @returns True if usage structure is valid
384
- */
385
- export function isValidClaudeUsage(usage: unknown): usage is ClaudeUsage {
386
- if (!usage || typeof usage !== "object") {
387
- return false;
388
- }
389
-
390
- const usageObj = usage as Record<string, unknown>;
391
-
392
- // Check required standard fields
393
- const hasStandardFields =
394
- typeof usageObj.prompt_tokens === "number" &&
395
- typeof usageObj.completion_tokens === "number" &&
396
- typeof usageObj.total_tokens === "number";
397
-
398
- if (!hasStandardFields) {
399
- return false;
400
- }
401
-
402
- // Check optional cache fields
403
- const hasCacheFields =
404
- (usageObj.cache_read_input_tokens === undefined ||
405
- typeof usageObj.cache_read_input_tokens === "number") &&
406
- (usageObj.cache_creation_input_tokens === undefined ||
407
- typeof usageObj.cache_creation_input_tokens === "number");
408
-
409
- if (!hasCacheFields) {
410
- return false;
411
- }
412
-
413
- // Check cache_creation object if present
414
- if (usageObj.cache_creation !== undefined) {
415
- const cacheCreation = usageObj.cache_creation as Record<string, unknown>;
416
- if (
417
- typeof cacheCreation !== "object" ||
418
- typeof cacheCreation.ephemeral_5m_input_tokens !== "number" ||
419
- typeof cacheCreation.ephemeral_1h_input_tokens !== "number"
420
- ) {
421
- return false;
422
- }
423
- }
424
-
425
- return true;
426
- }
@@ -1,161 +0,0 @@
1
- /**
2
- * Command Argument Parser
3
- *
4
- * Provides parameter substitution for custom slash commands similar to Claude's system:
5
- * - $ARGUMENTS: All arguments as a single string
6
- * - $1, $2, $3, etc.: Individual positional arguments
7
- * - Supports quoted arguments with spaces
8
- * - Handles escaped quotes within arguments
9
- */
10
-
11
- /**
12
- * Parse command arguments from a string, respecting quotes
13
- */
14
- export function parseCommandArguments(argsString: string): string[] {
15
- if (!argsString.trim()) {
16
- return [];
17
- }
18
-
19
- const args: string[] = [];
20
- let current = "";
21
- let inQuotes = false;
22
- let quoteChar = "";
23
- let escaped = false;
24
-
25
- for (let i = 0; i < argsString.length; i++) {
26
- const char = argsString[i];
27
- const nextChar = argsString[i + 1];
28
-
29
- if (escaped) {
30
- current += char;
31
- escaped = false;
32
- continue;
33
- }
34
-
35
- if (char === "\\") {
36
- // Handle escape sequences
37
- if (inQuotes && (nextChar === quoteChar || nextChar === "\\")) {
38
- escaped = true;
39
- continue;
40
- }
41
- current += char;
42
- continue;
43
- }
44
-
45
- if (!inQuotes && (char === '"' || char === "'")) {
46
- // Start quoted string
47
- inQuotes = true;
48
- quoteChar = char;
49
- continue;
50
- }
51
-
52
- if (inQuotes && char === quoteChar) {
53
- // End quoted string
54
- inQuotes = false;
55
- quoteChar = "";
56
- continue;
57
- }
58
-
59
- if (!inQuotes && /\s/.test(char)) {
60
- // Whitespace outside quotes - end current argument
61
- if (current) {
62
- args.push(current);
63
- current = "";
64
- }
65
- continue;
66
- }
67
-
68
- current += char;
69
- }
70
-
71
- // Add final argument if any
72
- if (current) {
73
- args.push(current);
74
- }
75
-
76
- return args;
77
- }
78
-
79
- /**
80
- * Substitute command parameters in content
81
- */
82
- export function substituteCommandParameters(
83
- content: string,
84
- argsString: string,
85
- ): string {
86
- if (!hasParameterPlaceholders(content)) {
87
- if (!argsString.trim()) {
88
- return content;
89
- }
90
- const separator = content && !/\s$/.test(content) ? " " : "";
91
- return `${content}${separator}${argsString}`;
92
- }
93
-
94
- const args = parseCommandArguments(argsString);
95
-
96
- let result = content;
97
-
98
- // Replace $ARGUMENTS with all arguments
99
- result = result.replace(/\$ARGUMENTS/g, argsString);
100
-
101
- // Replace positional parameters $1, $2, etc.
102
- // Sort by parameter number (descending) to avoid replacing $10 with $1 + "0"
103
- const positionalParams = [...result.matchAll(/\$(\d+)/g)]
104
- .map((match) => parseInt(match[1], 10))
105
- .filter((value, index, array) => array.indexOf(value) === index) // unique
106
- .sort((a, b) => b - a); // descending order
107
-
108
- for (const paramNum of positionalParams) {
109
- const paramValue = args[paramNum - 1] || ""; // Arrays are 0-indexed, params are 1-indexed
110
- const paramRegex = new RegExp(`\\$${paramNum}`, "g");
111
- result = result.replace(paramRegex, paramValue);
112
- }
113
-
114
- return result;
115
- }
116
-
117
- /**
118
- * Extract command name and arguments from a slash command input
119
- * Example: "/fix-issue 123 high-priority" -> { command: "fix-issue", args: "123 high-priority" }
120
- */
121
- export function parseSlashCommandInput(input: string): {
122
- command: string;
123
- args: string;
124
- } {
125
- const trimmed = input.trim();
126
-
127
- if (!trimmed.startsWith("/")) {
128
- throw new Error("Input must start with /");
129
- }
130
-
131
- const withoutSlash = trimmed.substring(1);
132
- const firstSpaceIndex = withoutSlash.indexOf(" ");
133
-
134
- if (firstSpaceIndex === -1) {
135
- // No arguments
136
- return {
137
- command: withoutSlash,
138
- args: "",
139
- };
140
- }
141
-
142
- return {
143
- command: withoutSlash.substring(0, firstSpaceIndex),
144
- args: withoutSlash.substring(firstSpaceIndex + 1).trim(),
145
- };
146
- }
147
-
148
- /**
149
- * Check if content contains parameter placeholders
150
- */
151
- export function hasParameterPlaceholders(content: string): boolean {
152
- return /\$(?:ARGUMENTS|\d+)/.test(content);
153
- }
154
-
155
- /**
156
- * Get all parameter placeholders used in content
157
- */
158
- export function getUsedParameterPlaceholders(content: string): string[] {
159
- const matches = content.match(/\$(?:ARGUMENTS|\d+)/g);
160
- return matches ? [...new Set(matches)] : [];
161
- }
@@ -1,88 +0,0 @@
1
- import { relative, basename } from "path";
2
-
3
- /**
4
- * Command path resolver utilities for command discovery
5
- * Handles conversion between file paths and command IDs
6
- */
7
-
8
- /**
9
- * Generate command ID from file path
10
- * @param filePath - Absolute path to markdown file
11
- * @param rootDir - Root commands directory path
12
- * @returns Command identifier string
13
- * @throws Error on invalid path structure
14
- */
15
- export function generateCommandId(filePath: string, rootDir: string): string {
16
- // Handle null/undefined inputs
17
- if (filePath == null || rootDir == null) {
18
- throw new Error("File path and root directory must be provided");
19
- }
20
-
21
- // Handle empty root directory (for root level commands)
22
- const relativePath = rootDir === "" ? filePath : relative(rootDir, filePath);
23
-
24
- // Handle edge cases
25
- if (!relativePath || relativePath === ".") {
26
- throw new Error("Command filename cannot be empty");
27
- }
28
-
29
- const segments = relativePath
30
- .split(/[\\/]/)
31
- .filter((segment) => segment !== "");
32
-
33
- // Remove .md extension from the last segment
34
- const lastSegment = segments[segments.length - 1];
35
- if (!lastSegment.endsWith(".md")) {
36
- throw new Error(`Command files must have .md extension`);
37
- }
38
-
39
- const commandName = basename(lastSegment, ".md");
40
-
41
- // Handle empty filename after removing extension
42
- if (commandName === "") {
43
- throw new Error("Command filename cannot be empty");
44
- }
45
-
46
- // Validate depth (no nesting allowed)
47
- if (segments.length > 1) {
48
- throw new Error(
49
- `Command nesting not supported: ${relativePath}. Commands must be in the root directory.`,
50
- );
51
- }
52
-
53
- // Validate command name
54
- if (!validateSegment(commandName)) {
55
- throw new Error(
56
- `Invalid command name: "${commandName}" in ${relativePath}. Must match pattern /^[a-zA-Z][a-zA-Z0-9_.-]*$/`,
57
- );
58
- }
59
-
60
- return commandName;
61
- }
62
-
63
- /**
64
- * Validate command ID format
65
- * @param commandId - Command identifier to validate
66
- * @returns Boolean indicating validity
67
- */
68
- export function validateCommandId(commandId: string): boolean {
69
- // Handle null/undefined inputs
70
- if (commandId == null) {
71
- return false;
72
- }
73
-
74
- // Command ID must be a single segment (no colons)
75
- const pattern = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
76
- return pattern.test(commandId);
77
- }
78
-
79
- /**
80
- * Validate individual path segment
81
- * @param segment - Path segment to validate
82
- * @returns Boolean indicating validity
83
- */
84
- function validateSegment(segment: string): boolean {
85
- // Segments should start with letters and can contain letters, numbers, dashes, underscores, dots
86
- const pattern = /^[a-zA-Z][a-zA-Z0-9_.-]*$/;
87
- return pattern.test(segment);
88
- }