wave-agent-sdk 1.0.5 → 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 (193) 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 +13 -6
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/telemetry/instrumentation.d.ts +1 -1
  10. package/dist/telemetry/instrumentation.js +57 -28
  11. package/dist/tools/bashTool.js +16 -6
  12. package/dist/tools/grepTool.js +11 -3
  13. package/dist/types/index.d.ts +3 -0
  14. package/dist/types/messaging.d.ts +2 -0
  15. package/dist/utils/containerSetup.js +0 -3
  16. package/dist/utils/messageOperations.d.ts +2 -1
  17. package/dist/utils/messageOperations.js +2 -1
  18. package/dist/utils/notificationXml.js +5 -0
  19. package/dist/utils/ripgrep.d.ts +4 -4
  20. package/dist/utils/ripgrep.js +4 -27
  21. package/package.json +20 -8
  22. package/bin/rg +0 -79
  23. package/dist/managers/forkedAgentManager.d.ts +0 -50
  24. package/dist/managers/forkedAgentManager.js +0 -130
  25. package/scripts/install_ripgrep.js +0 -111
  26. package/scripts/postinstall.js +0 -38
  27. package/src/agent.ts +0 -1271
  28. package/src/constants/subagents.ts +0 -4
  29. package/src/constants/toolLimits.ts +0 -15
  30. package/src/constants/tools.ts +0 -24
  31. package/src/core/plugin.ts +0 -237
  32. package/src/core/session.ts +0 -9
  33. package/src/index.ts +0 -38
  34. package/src/managers/MemoryRuleManager.ts +0 -198
  35. package/src/managers/aiManager.ts +0 -2429
  36. package/src/managers/backgroundTaskManager.ts +0 -480
  37. package/src/managers/bangManager.ts +0 -119
  38. package/src/managers/cronManager.ts +0 -401
  39. package/src/managers/foregroundTaskManager.ts +0 -30
  40. package/src/managers/forkedAgentManager.ts +0 -216
  41. package/src/managers/hookManager.ts +0 -1089
  42. package/src/managers/liveConfigManager.ts +0 -445
  43. package/src/managers/lspManager.ts +0 -470
  44. package/src/managers/mcpManager.ts +0 -1010
  45. package/src/managers/messageManager.ts +0 -1184
  46. package/src/managers/messageQueue.ts +0 -157
  47. package/src/managers/permissionManager.ts +0 -1140
  48. package/src/managers/planManager.ts +0 -112
  49. package/src/managers/pluginManager.ts +0 -330
  50. package/src/managers/pluginScopeManager.ts +0 -124
  51. package/src/managers/reversionManager.ts +0 -178
  52. package/src/managers/skillManager.ts +0 -578
  53. package/src/managers/slashCommandManager.ts +0 -564
  54. package/src/managers/subagentManager.ts +0 -915
  55. package/src/managers/toolManager.ts +0 -489
  56. package/src/managers/workflowManager.ts +0 -493
  57. package/src/prompts/autoMemory.ts +0 -33
  58. package/src/prompts/autoMemoryExtraction.ts +0 -146
  59. package/src/prompts/index.ts +0 -506
  60. package/src/prompts/planModeReminders.ts +0 -126
  61. package/src/services/GitService.ts +0 -131
  62. package/src/services/MarketplaceService.ts +0 -1052
  63. package/src/services/MemoryRuleService.ts +0 -71
  64. package/src/services/aiService.ts +0 -919
  65. package/src/services/authService.ts +0 -565
  66. package/src/services/autoMemoryService.ts +0 -280
  67. package/src/services/configurationService.ts +0 -1520
  68. package/src/services/fileWatcher.ts +0 -319
  69. package/src/services/hook.ts +0 -348
  70. package/src/services/initializationService.ts +0 -357
  71. package/src/services/interactionService.ts +0 -229
  72. package/src/services/jsonlHandler.ts +0 -295
  73. package/src/services/memory.ts +0 -234
  74. package/src/services/pluginLoader.ts +0 -252
  75. package/src/services/remoteSettingsService.ts +0 -366
  76. package/src/services/reversionService.ts +0 -122
  77. package/src/services/session.ts +0 -917
  78. package/src/services/taskManager.ts +0 -321
  79. package/src/telemetry/events.ts +0 -65
  80. package/src/telemetry/instrumentation.ts +0 -499
  81. package/src/telemetry/sessionTracing.ts +0 -348
  82. package/src/tools/agentTool.ts +0 -322
  83. package/src/tools/askUserQuestion.ts +0 -160
  84. package/src/tools/bashTool.ts +0 -621
  85. package/src/tools/buildTool.ts +0 -61
  86. package/src/tools/cronCreateTool.ts +0 -161
  87. package/src/tools/cronDeleteTool.ts +0 -51
  88. package/src/tools/cronListTool.ts +0 -47
  89. package/src/tools/editTool.ts +0 -313
  90. package/src/tools/enterPlanMode.ts +0 -124
  91. package/src/tools/enterWorktreeTool.ts +0 -190
  92. package/src/tools/exitPlanMode.ts +0 -139
  93. package/src/tools/exitWorktreeTool.ts +0 -236
  94. package/src/tools/globTool.ts +0 -167
  95. package/src/tools/grepTool.ts +0 -399
  96. package/src/tools/lspTool.ts +0 -883
  97. package/src/tools/readTool.ts +0 -426
  98. package/src/tools/skillTool.ts +0 -248
  99. package/src/tools/taskManagementTools.ts +0 -574
  100. package/src/tools/taskStopTool.ts +0 -78
  101. package/src/tools/types.ts +0 -137
  102. package/src/tools/webFetchTool.ts +0 -373
  103. package/src/tools/workflowTool.ts +0 -205
  104. package/src/tools/writeTool.ts +0 -267
  105. package/src/types/agent.ts +0 -128
  106. package/src/types/auth.ts +0 -19
  107. package/src/types/commands.ts +0 -30
  108. package/src/types/config.ts +0 -43
  109. package/src/types/configuration.ts +0 -168
  110. package/src/types/core.ts +0 -105
  111. package/src/types/cron.ts +0 -13
  112. package/src/types/environment.ts +0 -106
  113. package/src/types/fileSearch.ts +0 -4
  114. package/src/types/history.ts +0 -7
  115. package/src/types/hooks.ts +0 -256
  116. package/src/types/index.ts +0 -41
  117. package/src/types/lsp.ts +0 -98
  118. package/src/types/marketplace.ts +0 -71
  119. package/src/types/mcp.ts +0 -43
  120. package/src/types/memoryRule.ts +0 -31
  121. package/src/types/messaging.ts +0 -114
  122. package/src/types/permissions.ts +0 -80
  123. package/src/types/plugins.ts +0 -39
  124. package/src/types/processes.ts +0 -99
  125. package/src/types/reversion.ts +0 -29
  126. package/src/types/session.ts +0 -12
  127. package/src/types/skills.ts +0 -108
  128. package/src/types/tasks.ts +0 -13
  129. package/src/types/telemetry.ts +0 -98
  130. package/src/types/tools.ts +0 -44
  131. package/src/types/workflow.ts +0 -6
  132. package/src/utils/abortUtils.ts +0 -118
  133. package/src/utils/atomicWrite.ts +0 -61
  134. package/src/utils/bashParser.ts +0 -899
  135. package/src/utils/cacheControlUtils.ts +0 -426
  136. package/src/utils/commandArgumentParser.ts +0 -161
  137. package/src/utils/commandPathResolver.ts +0 -88
  138. package/src/utils/configPaths.ts +0 -220
  139. package/src/utils/configValidator.ts +0 -100
  140. package/src/utils/constants.ts +0 -39
  141. package/src/utils/container.ts +0 -92
  142. package/src/utils/containerSetup.ts +0 -371
  143. package/src/utils/convertMessagesForAPI.ts +0 -326
  144. package/src/utils/cronTasks.ts +0 -128
  145. package/src/utils/cronTasksLock.ts +0 -182
  146. package/src/utils/cronToHuman.ts +0 -99
  147. package/src/utils/customCommands.ts +0 -95
  148. package/src/utils/editUtils.ts +0 -20
  149. package/src/utils/fileFormat.ts +0 -40
  150. package/src/utils/fileSearch.ts +0 -151
  151. package/src/utils/fileUtils.ts +0 -223
  152. package/src/utils/gitUtils.ts +0 -315
  153. package/src/utils/globalLogger.ts +0 -128
  154. package/src/utils/groupMessagesByApiRound.ts +0 -121
  155. package/src/utils/hookMatcher.ts +0 -176
  156. package/src/utils/markdownParser.ts +0 -317
  157. package/src/utils/mcpUtils.ts +0 -138
  158. package/src/utils/messageOperations.ts +0 -659
  159. package/src/utils/modelCapabilities.ts +0 -30
  160. package/src/utils/nameGenerator.ts +0 -99
  161. package/src/utils/notificationXml.ts +0 -57
  162. package/src/utils/openaiClient.ts +0 -251
  163. package/src/utils/parseCronExpression.ts +0 -78
  164. package/src/utils/path.ts +0 -72
  165. package/src/utils/pathEncoder.ts +0 -400
  166. package/src/utils/pathSafety.ts +0 -40
  167. package/src/utils/promptHistory.ts +0 -168
  168. package/src/utils/ripgrep.ts +0 -35
  169. package/src/utils/shellResolver.ts +0 -190
  170. package/src/utils/skillParser.ts +0 -246
  171. package/src/utils/stringUtils.ts +0 -156
  172. package/src/utils/subagentParser.ts +0 -279
  173. package/src/utils/taskReminder.ts +0 -96
  174. package/src/utils/tokenCalculation.ts +0 -43
  175. package/src/utils/tokenEstimate.ts +0 -34
  176. package/src/utils/toolResultStorage.ts +0 -117
  177. package/src/utils/worktreeSession.ts +0 -26
  178. package/src/utils/worktreeUtils.ts +0 -786
  179. package/src/workflow/budgetTracker.ts +0 -34
  180. package/src/workflow/concurrencyLimiter.ts +0 -47
  181. package/src/workflow/journal.ts +0 -95
  182. package/src/workflow/progressReporter.ts +0 -141
  183. package/src/workflow/runState.ts +0 -65
  184. package/src/workflow/scriptRuntime.ts +0 -274
  185. package/src/workflow/structuredOutput.ts +0 -123
  186. package/src/workflow/types.ts +0 -101
  187. package/src/workflow/workflowApis.ts +0 -410
  188. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  189. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  190. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  191. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  192. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  193. 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
- }