@getverbal/cli 0.7.0 → 0.8.0

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 (249) hide show
  1. package/dist/agent-hooks/claude.d.ts +10 -0
  2. package/dist/agent-hooks/claude.d.ts.map +1 -0
  3. package/dist/agent-hooks/claude.js +218 -0
  4. package/dist/agent-hooks/claude.js.map +1 -0
  5. package/dist/agent-hooks/cli.d.ts +2 -0
  6. package/dist/agent-hooks/cli.d.ts.map +1 -0
  7. package/dist/agent-hooks/cli.js +271 -0
  8. package/dist/agent-hooks/cli.js.map +1 -0
  9. package/dist/agent-hooks/codex.d.ts +9 -0
  10. package/dist/agent-hooks/codex.d.ts.map +1 -0
  11. package/dist/agent-hooks/codex.js +349 -0
  12. package/dist/agent-hooks/codex.js.map +1 -0
  13. package/dist/agent-hooks/config.d.ts +32 -0
  14. package/dist/agent-hooks/config.d.ts.map +1 -0
  15. package/dist/agent-hooks/config.js +176 -0
  16. package/dist/agent-hooks/config.js.map +1 -0
  17. package/dist/agent-hooks/ingest.d.ts +4 -0
  18. package/dist/agent-hooks/ingest.d.ts.map +1 -0
  19. package/dist/agent-hooks/ingest.js +22 -0
  20. package/dist/agent-hooks/ingest.js.map +1 -0
  21. package/dist/agent-hooks/launchagent.d.ts +7 -0
  22. package/dist/agent-hooks/launchagent.d.ts.map +1 -0
  23. package/dist/agent-hooks/launchagent.js +51 -0
  24. package/dist/agent-hooks/launchagent.js.map +1 -0
  25. package/dist/agent-hooks/runtime-context.d.ts +20 -0
  26. package/dist/agent-hooks/runtime-context.d.ts.map +1 -0
  27. package/dist/agent-hooks/runtime-context.js +90 -0
  28. package/dist/agent-hooks/runtime-context.js.map +1 -0
  29. package/dist/agent-hooks/state.d.ts +26 -0
  30. package/dist/agent-hooks/state.d.ts.map +1 -0
  31. package/dist/agent-hooks/state.js +67 -0
  32. package/dist/agent-hooks/state.js.map +1 -0
  33. package/dist/agent-hooks/tokscale.d.ts +70 -0
  34. package/dist/agent-hooks/tokscale.d.ts.map +1 -0
  35. package/dist/agent-hooks/tokscale.js +142 -0
  36. package/dist/agent-hooks/tokscale.js.map +1 -0
  37. package/dist/agent-hooks/tool-extraction.d.ts +7 -0
  38. package/dist/agent-hooks/tool-extraction.d.ts.map +1 -0
  39. package/dist/agent-hooks/tool-extraction.js +100 -0
  40. package/dist/agent-hooks/tool-extraction.js.map +1 -0
  41. package/dist/agent-hooks/trace.d.ts +17 -0
  42. package/dist/agent-hooks/trace.d.ts.map +1 -0
  43. package/dist/agent-hooks/trace.js +25 -0
  44. package/dist/agent-hooks/trace.js.map +1 -0
  45. package/dist/auth/browser-auth.d.ts +6 -0
  46. package/dist/auth/browser-auth.d.ts.map +1 -0
  47. package/dist/auth/browser-auth.js +202 -0
  48. package/dist/auth/browser-auth.js.map +1 -0
  49. package/dist/auth/credentials.d.ts +6 -0
  50. package/dist/auth/credentials.d.ts.map +1 -0
  51. package/dist/auth/credentials.js +78 -0
  52. package/dist/auth/credentials.js.map +1 -0
  53. package/dist/cli.d.ts +3 -0
  54. package/dist/cli.d.ts.map +1 -0
  55. package/dist/cli.js +550 -148
  56. package/dist/cli.js.map +1 -0
  57. package/dist/commands/dashboard.d.ts +2 -0
  58. package/dist/commands/dashboard.d.ts.map +1 -0
  59. package/dist/commands/dashboard.js +19 -0
  60. package/dist/commands/dashboard.js.map +1 -0
  61. package/dist/commands/hooks.d.ts +2 -0
  62. package/dist/commands/hooks.d.ts.map +1 -0
  63. package/dist/commands/hooks.js +6 -0
  64. package/dist/commands/hooks.js.map +1 -0
  65. package/dist/commands/import.d.ts +2 -0
  66. package/dist/commands/import.d.ts.map +1 -0
  67. package/dist/commands/import.js +129 -0
  68. package/dist/commands/import.js.map +1 -0
  69. package/dist/commands/init.d.ts +2 -0
  70. package/dist/commands/init.d.ts.map +1 -0
  71. package/dist/commands/init.js +262 -0
  72. package/dist/commands/init.js.map +1 -0
  73. package/dist/commands/logout.d.ts +2 -0
  74. package/dist/commands/logout.d.ts.map +1 -0
  75. package/dist/commands/logout.js +17 -0
  76. package/dist/commands/logout.js.map +1 -0
  77. package/dist/commands/mcp-serve.d.ts +2 -0
  78. package/dist/commands/mcp-serve.d.ts.map +1 -0
  79. package/dist/commands/mcp-serve.js +7 -0
  80. package/dist/commands/mcp-serve.js.map +1 -0
  81. package/dist/commands/status.d.ts +2 -0
  82. package/dist/commands/status.d.ts.map +1 -0
  83. package/dist/commands/status.js +43 -0
  84. package/dist/commands/status.js.map +1 -0
  85. package/dist/commands/uninstall.d.ts +2 -0
  86. package/dist/commands/uninstall.d.ts.map +1 -0
  87. package/dist/commands/uninstall.js +43 -0
  88. package/dist/commands/uninstall.js.map +1 -0
  89. package/dist/commands/update.d.ts +2 -0
  90. package/dist/commands/update.d.ts.map +1 -0
  91. package/dist/commands/update.js +58 -0
  92. package/dist/commands/update.js.map +1 -0
  93. package/dist/configure/claude-code.d.ts +7 -0
  94. package/dist/configure/claude-code.d.ts.map +1 -0
  95. package/dist/configure/claude-code.js +11 -0
  96. package/dist/configure/claude-code.js.map +1 -0
  97. package/dist/configure/claude-desktop.d.ts +8 -0
  98. package/dist/configure/claude-desktop.d.ts.map +1 -0
  99. package/dist/configure/claude-desktop.js +28 -0
  100. package/dist/configure/claude-desktop.js.map +1 -0
  101. package/dist/configure/codex.d.ts +7 -0
  102. package/dist/configure/codex.d.ts.map +1 -0
  103. package/dist/configure/codex.js +12 -0
  104. package/dist/configure/codex.js.map +1 -0
  105. package/dist/configure/cursor.d.ts +7 -0
  106. package/dist/configure/cursor.d.ts.map +1 -0
  107. package/dist/configure/cursor.js +12 -0
  108. package/dist/configure/cursor.js.map +1 -0
  109. package/dist/configure/index.d.ts +34 -0
  110. package/dist/configure/index.d.ts.map +1 -0
  111. package/dist/configure/index.js +153 -0
  112. package/dist/configure/index.js.map +1 -0
  113. package/dist/detect/claude-code.d.ts +3 -0
  114. package/dist/detect/claude-code.d.ts.map +1 -0
  115. package/dist/detect/claude-code.js +82 -0
  116. package/dist/detect/claude-code.js.map +1 -0
  117. package/dist/detect/claude-desktop.d.ts +3 -0
  118. package/dist/detect/claude-desktop.d.ts.map +1 -0
  119. package/dist/detect/claude-desktop.js +89 -0
  120. package/dist/detect/claude-desktop.js.map +1 -0
  121. package/dist/detect/codex.d.ts +3 -0
  122. package/dist/detect/codex.d.ts.map +1 -0
  123. package/dist/detect/codex.js +64 -0
  124. package/dist/detect/codex.js.map +1 -0
  125. package/dist/detect/cursor.d.ts +3 -0
  126. package/dist/detect/cursor.d.ts.map +1 -0
  127. package/dist/detect/cursor.js +81 -0
  128. package/dist/detect/cursor.js.map +1 -0
  129. package/dist/detect/index.d.ts +3 -0
  130. package/dist/detect/index.d.ts.map +1 -0
  131. package/dist/detect/index.js +28 -0
  132. package/dist/detect/index.js.map +1 -0
  133. package/dist/import/file-upload.d.ts +10 -0
  134. package/dist/import/file-upload.d.ts.map +1 -0
  135. package/dist/import/file-upload.js +37 -0
  136. package/dist/import/file-upload.js.map +1 -0
  137. package/dist/import/index.d.ts +11 -0
  138. package/dist/import/index.d.ts.map +1 -0
  139. package/dist/import/index.js +51 -0
  140. package/dist/import/index.js.map +1 -0
  141. package/dist/mcp/exports.d.ts +13 -0
  142. package/dist/mcp/exports.d.ts.map +1 -0
  143. package/dist/mcp/exports.js +13 -0
  144. package/dist/mcp/exports.js.map +1 -0
  145. package/dist/mcp/git-context.d.ts +17 -0
  146. package/dist/mcp/git-context.d.ts.map +1 -0
  147. package/dist/mcp/git-context.js +72 -0
  148. package/dist/mcp/git-context.js.map +1 -0
  149. package/dist/mcp/hooks/anthropic.d.ts +31 -0
  150. package/dist/mcp/hooks/anthropic.d.ts.map +1 -0
  151. package/dist/mcp/hooks/anthropic.js +137 -0
  152. package/dist/mcp/hooks/anthropic.js.map +1 -0
  153. package/dist/mcp/hooks/google.d.ts +53 -0
  154. package/dist/mcp/hooks/google.d.ts.map +1 -0
  155. package/dist/mcp/hooks/google.js +161 -0
  156. package/dist/mcp/hooks/google.js.map +1 -0
  157. package/dist/mcp/hooks/index.d.ts +9 -0
  158. package/dist/mcp/hooks/index.d.ts.map +1 -0
  159. package/dist/mcp/hooks/index.js +7 -0
  160. package/dist/mcp/hooks/index.js.map +1 -0
  161. package/dist/mcp/hooks/openai.d.ts +59 -0
  162. package/dist/mcp/hooks/openai.d.ts.map +1 -0
  163. package/dist/mcp/hooks/openai.js +158 -0
  164. package/dist/mcp/hooks/openai.js.map +1 -0
  165. package/dist/mcp/hooks/types.d.ts +8 -0
  166. package/dist/mcp/hooks/types.d.ts.map +1 -0
  167. package/dist/mcp/hooks/types.js +5 -0
  168. package/dist/mcp/hooks/types.js.map +1 -0
  169. package/dist/mcp/ingestor.d.ts +23 -0
  170. package/dist/mcp/ingestor.d.ts.map +1 -0
  171. package/dist/mcp/ingestor.js +310 -0
  172. package/dist/mcp/ingestor.js.map +1 -0
  173. package/dist/mcp/pricing.d.ts +19 -0
  174. package/dist/mcp/pricing.d.ts.map +1 -0
  175. package/dist/mcp/pricing.js +130 -0
  176. package/dist/mcp/pricing.js.map +1 -0
  177. package/dist/mcp/server.d.ts +10 -0
  178. package/dist/mcp/server.d.ts.map +1 -0
  179. package/dist/mcp/server.js +689 -0
  180. package/dist/mcp/server.js.map +1 -0
  181. package/dist/mcp/session-tracker.d.ts +52 -0
  182. package/dist/mcp/session-tracker.d.ts.map +1 -0
  183. package/dist/mcp/session-tracker.js +186 -0
  184. package/dist/mcp/session-tracker.js.map +1 -0
  185. package/dist/mcp/tools/analyze-spending-trend.d.ts +10 -0
  186. package/dist/mcp/tools/analyze-spending-trend.d.ts.map +1 -0
  187. package/dist/mcp/tools/analyze-spending-trend.js +126 -0
  188. package/dist/mcp/tools/analyze-spending-trend.js.map +1 -0
  189. package/dist/mcp/tools/get-budget-status.d.ts +9 -0
  190. package/dist/mcp/tools/get-budget-status.d.ts.map +1 -0
  191. package/dist/mcp/tools/get-budget-status.js +59 -0
  192. package/dist/mcp/tools/get-budget-status.js.map +1 -0
  193. package/dist/mcp/tools/get-cost-breakdown.d.ts +10 -0
  194. package/dist/mcp/tools/get-cost-breakdown.d.ts.map +1 -0
  195. package/dist/mcp/tools/get-cost-breakdown.js +52 -0
  196. package/dist/mcp/tools/get-cost-breakdown.js.map +1 -0
  197. package/dist/mcp/tools/get-model-efficiency.d.ts +9 -0
  198. package/dist/mcp/tools/get-model-efficiency.d.ts.map +1 -0
  199. package/dist/mcp/tools/get-model-efficiency.js +137 -0
  200. package/dist/mcp/tools/get-model-efficiency.js.map +1 -0
  201. package/dist/mcp/tools/get-recent-prompts.d.ts +13 -0
  202. package/dist/mcp/tools/get-recent-prompts.d.ts.map +1 -0
  203. package/dist/mcp/tools/get-recent-prompts.js +56 -0
  204. package/dist/mcp/tools/get-recent-prompts.js.map +1 -0
  205. package/dist/mcp/tools/get-roi-metrics.d.ts +9 -0
  206. package/dist/mcp/tools/get-roi-metrics.d.ts.map +1 -0
  207. package/dist/mcp/tools/get-roi-metrics.js +84 -0
  208. package/dist/mcp/tools/get-roi-metrics.js.map +1 -0
  209. package/dist/mcp/tools/get-usage-summary.d.ts +10 -0
  210. package/dist/mcp/tools/get-usage-summary.d.ts.map +1 -0
  211. package/dist/mcp/tools/get-usage-summary.js +47 -0
  212. package/dist/mcp/tools/get-usage-summary.js.map +1 -0
  213. package/dist/mcp/tools/index.d.ts +221 -0
  214. package/dist/mcp/tools/index.d.ts.map +1 -0
  215. package/dist/mcp/tools/index.js +161 -0
  216. package/dist/mcp/tools/index.js.map +1 -0
  217. package/dist/mcp/tools/list-projects.d.ts +6 -0
  218. package/dist/mcp/tools/list-projects.d.ts.map +1 -0
  219. package/dist/mcp/tools/list-projects.js +43 -0
  220. package/dist/mcp/tools/list-projects.js.map +1 -0
  221. package/dist/mcp/tools/optimize-prompt.d.ts +44 -0
  222. package/dist/mcp/tools/optimize-prompt.d.ts.map +1 -0
  223. package/dist/mcp/tools/optimize-prompt.js +95 -0
  224. package/dist/mcp/tools/optimize-prompt.js.map +1 -0
  225. package/dist/mcp/types.d.ts +118 -0
  226. package/dist/mcp/types.d.ts.map +1 -0
  227. package/dist/mcp/types.js +5 -0
  228. package/dist/mcp/types.js.map +1 -0
  229. package/dist/types.d.ts +18 -0
  230. package/dist/types.d.ts.map +1 -0
  231. package/dist/types.js +2 -0
  232. package/dist/types.js.map +1 -0
  233. package/dist/update-check/check.d.ts +6 -0
  234. package/dist/update-check/check.d.ts.map +1 -0
  235. package/dist/update-check/check.js +64 -0
  236. package/dist/update-check/check.js.map +1 -0
  237. package/dist/update-check/notify.d.ts +6 -0
  238. package/dist/update-check/notify.d.ts.map +1 -0
  239. package/dist/update-check/notify.js +40 -0
  240. package/dist/update-check/notify.js.map +1 -0
  241. package/dist/verify.d.ts +7 -0
  242. package/dist/verify.d.ts.map +1 -0
  243. package/dist/verify.js +40 -0
  244. package/dist/verify.js.map +1 -0
  245. package/dist/version.d.ts +9 -0
  246. package/dist/version.d.ts.map +1 -0
  247. package/dist/version.js +21 -0
  248. package/dist/version.js.map +1 -0
  249. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -113,7 +113,7 @@ function compareSemver(a, b) {
113
113
  }
114
114
  return 0;
115
115
  }
116
- var VERSION = "0.7.0";
116
+ var VERSION = "0.8.0";
117
117
 
118
118
  // src/auth/credentials.ts
119
119
  import { readFileSync, writeFileSync, rmSync, mkdirSync, chmodSync, renameSync } from "node:fs";
@@ -19231,7 +19231,7 @@ function readJsonLines(filePath) {
19231
19231
  }
19232
19232
  return results;
19233
19233
  }
19234
- function flattenContent(content) {
19234
+ function flattenContent(content, opts) {
19235
19235
  if (!content)
19236
19236
  return;
19237
19237
  if (typeof content === "string")
@@ -19241,8 +19241,15 @@ function flattenContent(content) {
19241
19241
  if (!item || typeof item !== "object")
19242
19242
  return "";
19243
19243
  const typed = item;
19244
- if (typed.type === "thinking" || typed.type === "tool_use" || typed.type === "reasoning")
19244
+ if (typed.type === "tool_use")
19245
19245
  return "";
19246
+ if (typed.type === "thinking" || typed.type === "reasoning") {
19247
+ if (opts?.includeThinking) {
19248
+ const thinkingText = typed.text ?? typed.input_text ?? typed.output_text ?? "";
19249
+ return thinkingText ? `[thinking]${thinkingText}[/thinking]` : "";
19250
+ }
19251
+ return "";
19252
+ }
19246
19253
  return typed.text ?? typed.input_text ?? typed.output_text ?? "";
19247
19254
  }).filter(Boolean);
19248
19255
  return parts.length ? parts.join(`
@@ -19257,6 +19264,102 @@ function estimateTokens(prompt, response) {
19257
19264
  }
19258
19265
  var init_parsing_utils = () => {};
19259
19266
 
19267
+ // src/agent-hooks/tool-extraction.ts
19268
+ function parseToolInput(toolName, input) {
19269
+ const isShell = SHELL_TOOL_NAMES.has(toolName);
19270
+ const truncLimit = isShell ? TRUNCATION_BASH : TRUNCATION_DEFAULT;
19271
+ let raw;
19272
+ let command;
19273
+ if (typeof input === "string") {
19274
+ raw = input.slice(0, truncLimit);
19275
+ if (isShell) {
19276
+ if (input.length > 0) {
19277
+ try {
19278
+ const parsed = JSON.parse(input);
19279
+ command = extractCommandFromObject(parsed);
19280
+ } catch {
19281
+ command = input.slice(0, truncLimit);
19282
+ }
19283
+ }
19284
+ }
19285
+ } else if (input !== null && typeof input === "object") {
19286
+ if (isShell) {
19287
+ command = extractCommandFromObject(input);
19288
+ }
19289
+ try {
19290
+ raw = JSON.stringify(input).slice(0, truncLimit);
19291
+ } catch {
19292
+ raw = String(input).slice(0, truncLimit);
19293
+ }
19294
+ } else {
19295
+ raw = String(input ?? "").slice(0, truncLimit);
19296
+ }
19297
+ if (command !== undefined) {
19298
+ command = command.slice(0, truncLimit);
19299
+ }
19300
+ const command_type = command !== undefined ? detectCommandType(command) : undefined;
19301
+ return { raw, command, command_type };
19302
+ }
19303
+ function extractCommandFromObject(obj) {
19304
+ const cmd = obj["command"] ?? obj["cmd"] ?? obj["shell_command"];
19305
+ return typeof cmd === "string" ? cmd : undefined;
19306
+ }
19307
+ function detectCommandType(command) {
19308
+ const first = command.trim().split(/\s+/)[0] ?? "";
19309
+ const basename = first.split("/").pop() ?? first;
19310
+ return COMMAND_TYPE_MAP[basename.toLowerCase()] ?? "shell";
19311
+ }
19312
+ var SHELL_TOOL_NAMES, TRUNCATION_BASH = 2000, TRUNCATION_DEFAULT = 500, COMMAND_TYPE_MAP;
19313
+ var init_tool_extraction = __esm(() => {
19314
+ SHELL_TOOL_NAMES = new Set([
19315
+ "Bash",
19316
+ "bash",
19317
+ "shell",
19318
+ "Shell",
19319
+ "exec",
19320
+ "run_command",
19321
+ "computer"
19322
+ ]);
19323
+ COMMAND_TYPE_MAP = {
19324
+ git: "git",
19325
+ npm: "npm",
19326
+ npx: "npm",
19327
+ bun: "bun",
19328
+ bunx: "bun",
19329
+ node: "node",
19330
+ python: "python",
19331
+ python3: "python",
19332
+ pip: "python",
19333
+ pip3: "python",
19334
+ tsc: "typescript",
19335
+ vitest: "test",
19336
+ jest: "test",
19337
+ pytest: "test",
19338
+ mocha: "test",
19339
+ curl: "http",
19340
+ wget: "http",
19341
+ cat: "file",
19342
+ ls: "file",
19343
+ find: "file",
19344
+ cp: "file",
19345
+ mv: "file",
19346
+ rm: "file",
19347
+ mkdir: "file",
19348
+ grep: "search",
19349
+ rg: "search",
19350
+ sed: "transform",
19351
+ awk: "transform",
19352
+ docker: "docker",
19353
+ "docker-compose": "docker",
19354
+ kubectl: "kubernetes",
19355
+ helm: "kubernetes",
19356
+ terraform: "infrastructure",
19357
+ gh: "github",
19358
+ supabase: "supabase",
19359
+ bd: "beads"
19360
+ };
19361
+ });
19362
+
19260
19363
  // src/platforms/parsers/claude-code.ts
19261
19364
  import fs7 from "node:fs";
19262
19365
  import path6 from "node:path";
@@ -19296,6 +19399,101 @@ function findConversationFiles(baseDir) {
19296
19399
  walk(baseDir);
19297
19400
  return results;
19298
19401
  }
19402
+ function extractToolCallTraces(entries) {
19403
+ const toolUseBlocks = new Map;
19404
+ for (const entry of entries) {
19405
+ const content = entry.message?.content;
19406
+ if (!Array.isArray(content))
19407
+ continue;
19408
+ if ((entry.message?.role ?? entry.type) !== "assistant")
19409
+ continue;
19410
+ for (const block of content) {
19411
+ if (!block || typeof block !== "object")
19412
+ continue;
19413
+ const b = block;
19414
+ if (b.type === "tool_use" && typeof b.id === "string") {
19415
+ toolUseBlocks.set(b.id, {
19416
+ id: b.id,
19417
+ name: b.name ?? "unknown",
19418
+ input: b.input,
19419
+ startTimestamp: b.start_timestamp,
19420
+ stopTimestamp: b.stop_timestamp,
19421
+ entryTimestamp: entry.timestamp
19422
+ });
19423
+ }
19424
+ }
19425
+ }
19426
+ const toolResults = new Map;
19427
+ for (const entry of entries) {
19428
+ const content = entry.message?.content ?? entry.content;
19429
+ if (!Array.isArray(content))
19430
+ continue;
19431
+ const role = entry.message?.role ?? entry.type;
19432
+ if (role !== "user" && role !== "human")
19433
+ continue;
19434
+ for (const block of content) {
19435
+ if (!block || typeof block !== "object")
19436
+ continue;
19437
+ const b = block;
19438
+ if (b.type === "tool_result" && typeof b.tool_use_id === "string") {
19439
+ const rawContent = b.content;
19440
+ let output = "";
19441
+ if (typeof rawContent === "string") {
19442
+ output = rawContent.slice(0, 500);
19443
+ } else if (Array.isArray(rawContent)) {
19444
+ output = rawContent.map((c) => {
19445
+ if (!c || typeof c !== "object")
19446
+ return "";
19447
+ return c.text ?? "";
19448
+ }).join(`
19449
+ `).slice(0, 500);
19450
+ }
19451
+ toolResults.set(b.tool_use_id, {
19452
+ toolUseId: b.tool_use_id,
19453
+ isError: Boolean(b.is_error),
19454
+ output
19455
+ });
19456
+ }
19457
+ }
19458
+ }
19459
+ const traces = [];
19460
+ for (const [id, toolUse] of toolUseBlocks) {
19461
+ const result = toolResults.get(id);
19462
+ const startTs = toolUse.startTimestamp ?? toolUse.entryTimestamp ?? new Date().toISOString();
19463
+ const stopTs = toolUse.stopTimestamp ?? startTs;
19464
+ const durationMs = startTs && stopTs ? Math.max(0, new Date(stopTs).getTime() - new Date(startTs).getTime()) : undefined;
19465
+ const parsed = parseToolInput(toolUse.name, toolUse.input);
19466
+ const startedMetadata = {
19467
+ tool: toolUse.name
19468
+ };
19469
+ if (parsed.command !== undefined)
19470
+ startedMetadata["command"] = parsed.command;
19471
+ if (parsed.command_type !== undefined)
19472
+ startedMetadata["command_type"] = parsed.command_type;
19473
+ traces.push({
19474
+ type: "step.started",
19475
+ step_id: id,
19476
+ name: toolUse.name,
19477
+ timestamp: startTs,
19478
+ input: parsed.raw,
19479
+ metadata: startedMetadata
19480
+ });
19481
+ traces.push({
19482
+ type: "step.completed",
19483
+ step_id: id,
19484
+ name: toolUse.name,
19485
+ timestamp: stopTs,
19486
+ output: result?.output,
19487
+ metadata: {
19488
+ tool: toolUse.name,
19489
+ success: result ? !result.isError : true,
19490
+ duration_ms: durationMs,
19491
+ ...parsed.command_type !== undefined ? { command_type: parsed.command_type } : {}
19492
+ }
19493
+ });
19494
+ }
19495
+ return traces;
19496
+ }
19299
19497
 
19300
19498
  class ClaudeCodeParser {
19301
19499
  info = {
@@ -19316,6 +19514,7 @@ class ClaudeCodeParser {
19316
19514
  const sessions = claudeState?.sessions ?? {};
19317
19515
  const warnings = [];
19318
19516
  const events = [];
19517
+ const toolCalls = [];
19319
19518
  const files = this.discover();
19320
19519
  for (const filePath of files) {
19321
19520
  const relative = path6.relative(this.baseDir, filePath);
@@ -19381,6 +19580,8 @@ class ClaudeCodeParser {
19381
19580
  events.push(event);
19382
19581
  }
19383
19582
  }
19583
+ const fileToolCalls = extractToolCallTraces(newEntries);
19584
+ toolCalls.push(...fileToolCalls);
19384
19585
  const lastEntry = newEntries[newEntries.length - 1];
19385
19586
  const lastEntryId = getEntryId(lastEntry);
19386
19587
  if (lastEntryId) {
@@ -19392,11 +19593,12 @@ class ClaudeCodeParser {
19392
19593
  sessions,
19393
19594
  lastSyncTimestamp: new Date().toISOString()
19394
19595
  };
19395
- return { events, newState, warnings };
19596
+ return { events, newState, warnings, ...toolCalls.length > 0 ? { toolCalls } : {} };
19396
19597
  }
19397
19598
  }
19398
19599
  var init_claude_code = __esm(() => {
19399
19600
  init_parsing_utils();
19601
+ init_tool_extraction();
19400
19602
  });
19401
19603
 
19402
19604
  // src/platforms/parsers/codex.ts
@@ -20093,6 +20295,9 @@ async function syncPlatforms(config, options = {}) {
20093
20295
  platformCost += event.cost ?? 0;
20094
20296
  await ingestor.track(event);
20095
20297
  }
20298
+ if (config.traceEnabled && config.traceUrl && result.toolCalls && result.toolCalls.length > 0) {
20299
+ await sendToolCallTraces(config, parser.info.id, result.toolCalls);
20300
+ }
20096
20301
  savePlatformState(config.stateDir, parser.info.id, result.newState);
20097
20302
  platformResults.push({
20098
20303
  id: parser.info.id,
@@ -20117,6 +20322,31 @@ async function syncPlatforms(config, options = {}) {
20117
20322
  ingestor.destroy();
20118
20323
  return { platforms: platformResults, totalEvents, totalCost };
20119
20324
  }
20325
+ async function sendToolCallTraces(config, platformId, toolCalls) {
20326
+ if (!config.traceUrl)
20327
+ return;
20328
+ const payload = {
20329
+ trace_id: `sync:${platformId}:${new Date().toISOString()}`,
20330
+ name: `${platformId}-tool-calls`,
20331
+ events: toolCalls
20332
+ };
20333
+ try {
20334
+ const response = await fetch(config.traceUrl, {
20335
+ method: "POST",
20336
+ headers: {
20337
+ "Content-Type": "application/json",
20338
+ Authorization: `Bearer ${config.apiKey}`
20339
+ },
20340
+ body: JSON.stringify(payload)
20341
+ });
20342
+ if (!response.ok) {
20343
+ const text = await response.text();
20344
+ console.warn(`[VerbalTrace] Tool call trace ingest failed (${response.status}): ${text}`);
20345
+ }
20346
+ } catch (error) {
20347
+ console.warn("[VerbalTrace] Tool call trace ingest failed:", error);
20348
+ }
20349
+ }
20120
20350
  function mapParsedEventToUsageEvent(event, project) {
20121
20351
  const cost = event.billing_type === "subscription" ? 0 : calculateCost(event.model, event.tokens_in, event.tokens_out, event.cache_read_tokens, event.cache_write_tokens);
20122
20352
  return {
@@ -20437,6 +20667,234 @@ var init_update = __esm(() => {
20437
20667
  import_picocolors7 = __toESM(require_picocolors(), 1);
20438
20668
  });
20439
20669
 
20670
+ // src/commands/keys.ts
20671
+ var exports_keys = {};
20672
+ __export(exports_keys, {
20673
+ runKeys: () => runKeys
20674
+ });
20675
+ import { createInterface as createInterface4 } from "node:readline/promises";
20676
+ function isValidProvider(value) {
20677
+ return VALID_PROVIDERS.includes(value);
20678
+ }
20679
+ async function keysAdd(provider) {
20680
+ if (!isValidProvider(provider)) {
20681
+ console.error(import_picocolors8.default.red(`✗ Invalid provider: ${import_picocolors8.default.bold(provider)}`));
20682
+ console.log(import_picocolors8.default.dim(` Valid providers: ${VALID_PROVIDERS.join(", ")}`));
20683
+ process.exitCode = 1;
20684
+ return;
20685
+ }
20686
+ const credentials = await readCredentials();
20687
+ if (!credentials) {
20688
+ console.error(import_picocolors8.default.red("✗ Not authenticated. Run `getverbal init` first."));
20689
+ process.exitCode = 1;
20690
+ return;
20691
+ }
20692
+ const displayName = PROVIDER_DISPLAY[provider];
20693
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
20694
+ try {
20695
+ const apiKey = await rl.question(`Enter your ${displayName} Admin API key: `);
20696
+ if (!apiKey.trim()) {
20697
+ console.error(import_picocolors8.default.red("✗ API key cannot be empty."));
20698
+ process.exitCode = 1;
20699
+ return;
20700
+ }
20701
+ const res = await fetch(`${credentials.api_url}/api/v1/provider-keys`, {
20702
+ method: "POST",
20703
+ headers: {
20704
+ Authorization: `Bearer ${credentials.api_key}`,
20705
+ "Content-Type": "application/json"
20706
+ },
20707
+ body: JSON.stringify({ provider, key: apiKey.trim() })
20708
+ });
20709
+ if (!res.ok) {
20710
+ const body = await res.text().catch(() => "");
20711
+ let errorMessage = `HTTP ${res.status}`;
20712
+ try {
20713
+ const json = JSON.parse(body);
20714
+ if (json.error)
20715
+ errorMessage = json.error;
20716
+ } catch {}
20717
+ console.error(import_picocolors8.default.red(`✗ Failed to add ${displayName} key: ${errorMessage}`));
20718
+ process.exitCode = 1;
20719
+ return;
20720
+ }
20721
+ const data = await res.json();
20722
+ console.log();
20723
+ console.log(import_picocolors8.default.green(`✓ ${displayName} API key added`));
20724
+ console.log(import_picocolors8.default.dim(` Prefix: ${data.key_prefix}`));
20725
+ if (data.verified) {
20726
+ console.log(import_picocolors8.default.green(` ✓ Key verified`));
20727
+ } else {
20728
+ console.log(import_picocolors8.default.dim(` Key stored (verification pending)`));
20729
+ }
20730
+ } finally {
20731
+ rl.close();
20732
+ }
20733
+ }
20734
+ async function keysList() {
20735
+ const credentials = await readCredentials();
20736
+ if (!credentials) {
20737
+ console.error(import_picocolors8.default.red("✗ Not authenticated. Run `getverbal init` first."));
20738
+ process.exitCode = 1;
20739
+ return;
20740
+ }
20741
+ const res = await fetch(`${credentials.api_url}/api/v1/provider-keys`, {
20742
+ method: "GET",
20743
+ headers: {
20744
+ Authorization: `Bearer ${credentials.api_key}`
20745
+ }
20746
+ });
20747
+ if (!res.ok) {
20748
+ const body = await res.text().catch(() => "");
20749
+ let errorMessage = `HTTP ${res.status}`;
20750
+ try {
20751
+ const json2 = JSON.parse(body);
20752
+ if (json2.error)
20753
+ errorMessage = json2.error;
20754
+ } catch {}
20755
+ console.error(import_picocolors8.default.red(`✗ Failed to list provider keys: ${errorMessage}`));
20756
+ process.exitCode = 1;
20757
+ return;
20758
+ }
20759
+ const json = await res.json();
20760
+ const data = json.keys ?? [];
20761
+ if (data.length === 0) {
20762
+ console.log(import_picocolors8.default.dim("No provider keys configured. Run `getverbal keys add <provider>` to add one."));
20763
+ return;
20764
+ }
20765
+ console.log();
20766
+ console.log(import_picocolors8.default.bold("Provider Keys:"));
20767
+ console.log();
20768
+ const COL_PROVIDER = 12;
20769
+ const COL_PREFIX = 20;
20770
+ const COL_STATUS = 16;
20771
+ const header = import_picocolors8.default.dim("Provider".padEnd(COL_PROVIDER)) + import_picocolors8.default.dim("Key Prefix".padEnd(COL_PREFIX)) + import_picocolors8.default.dim("Sync Status".padEnd(COL_STATUS)) + import_picocolors8.default.dim("Last Synced");
20772
+ console.log(" " + header);
20773
+ console.log(" " + import_picocolors8.default.dim("─".repeat(64)));
20774
+ for (const key of data) {
20775
+ const lastSync = key.last_sync_at ? new Date(key.last_sync_at).toLocaleDateString() : import_picocolors8.default.dim("Never");
20776
+ const statusColor = key.sync_status === "ok" || key.sync_status === "active" ? import_picocolors8.default.green(key.sync_status) : import_picocolors8.default.yellow(key.sync_status);
20777
+ const row = key.provider.padEnd(COL_PROVIDER) + key.key_prefix.padEnd(COL_PREFIX) + statusColor.padEnd(COL_STATUS + 10) + lastSync;
20778
+ console.log(" " + row);
20779
+ }
20780
+ console.log();
20781
+ }
20782
+ async function keysRemove(provider) {
20783
+ if (!isValidProvider(provider)) {
20784
+ console.error(import_picocolors8.default.red(`✗ Invalid provider: ${import_picocolors8.default.bold(provider)}`));
20785
+ console.log(import_picocolors8.default.dim(` Valid providers: ${VALID_PROVIDERS.join(", ")}`));
20786
+ process.exitCode = 1;
20787
+ return;
20788
+ }
20789
+ const credentials = await readCredentials();
20790
+ if (!credentials) {
20791
+ console.error(import_picocolors8.default.red("✗ Not authenticated. Run `getverbal init` first."));
20792
+ process.exitCode = 1;
20793
+ return;
20794
+ }
20795
+ const displayName = PROVIDER_DISPLAY[provider];
20796
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
20797
+ try {
20798
+ const answer = await rl.question(`Remove ${import_picocolors8.default.bold(displayName)} key? (y/N) `);
20799
+ const trimmed = answer.trim().toLowerCase();
20800
+ if (trimmed !== "y" && trimmed !== "yes") {
20801
+ console.log(import_picocolors8.default.dim("Cancelled."));
20802
+ return;
20803
+ }
20804
+ const url = new URL(`${credentials.api_url}/api/v1/provider-keys`);
20805
+ url.searchParams.set("provider", provider);
20806
+ const res = await fetch(url.toString(), {
20807
+ method: "DELETE",
20808
+ headers: {
20809
+ Authorization: `Bearer ${credentials.api_key}`
20810
+ }
20811
+ });
20812
+ if (!res.ok) {
20813
+ const body = await res.text().catch(() => "");
20814
+ let errorMessage = `HTTP ${res.status}`;
20815
+ try {
20816
+ const json = JSON.parse(body);
20817
+ if (json.error)
20818
+ errorMessage = json.error;
20819
+ } catch {}
20820
+ console.error(import_picocolors8.default.red(`✗ Failed to remove ${displayName} key: ${errorMessage}`));
20821
+ process.exitCode = 1;
20822
+ return;
20823
+ }
20824
+ console.log(import_picocolors8.default.green(`✓ ${displayName} key removed`));
20825
+ } finally {
20826
+ rl.close();
20827
+ }
20828
+ }
20829
+ async function runKeys(args) {
20830
+ const subcommand = args[0];
20831
+ switch (subcommand) {
20832
+ case "add": {
20833
+ const provider = args[1];
20834
+ if (!provider) {
20835
+ console.error(import_picocolors8.default.red("✗ Missing provider. Usage: getverbal keys add <provider>"));
20836
+ console.log(import_picocolors8.default.dim(` Valid providers: ${VALID_PROVIDERS.join(", ")}`));
20837
+ process.exitCode = 1;
20838
+ return;
20839
+ }
20840
+ await keysAdd(provider);
20841
+ break;
20842
+ }
20843
+ case "list":
20844
+ await keysList();
20845
+ break;
20846
+ case "remove": {
20847
+ const provider = args[1];
20848
+ if (!provider) {
20849
+ console.error(import_picocolors8.default.red("✗ Missing provider. Usage: getverbal keys remove <provider>"));
20850
+ console.log(import_picocolors8.default.dim(` Valid providers: ${VALID_PROVIDERS.join(", ")}`));
20851
+ process.exitCode = 1;
20852
+ return;
20853
+ }
20854
+ await keysRemove(provider);
20855
+ break;
20856
+ }
20857
+ case "--help":
20858
+ case "-h":
20859
+ case undefined:
20860
+ console.log(KEYS_HELP);
20861
+ break;
20862
+ default:
20863
+ console.error(`${import_picocolors8.default.red("Unknown subcommand:")} ${subcommand}
20864
+ `);
20865
+ console.log(KEYS_HELP);
20866
+ process.exitCode = 1;
20867
+ }
20868
+ }
20869
+ var import_picocolors8, VALID_PROVIDERS, PROVIDER_DISPLAY, KEYS_HELP;
20870
+ var init_keys = __esm(() => {
20871
+ init_credentials();
20872
+ import_picocolors8 = __toESM(require_picocolors(), 1);
20873
+ VALID_PROVIDERS = ["anthropic", "openai", "google"];
20874
+ PROVIDER_DISPLAY = {
20875
+ anthropic: "Anthropic",
20876
+ openai: "OpenAI",
20877
+ google: "Google"
20878
+ };
20879
+ KEYS_HELP = `
20880
+ ${import_picocolors8.default.bold("getverbal keys")} — Manage provider API keys
20881
+
20882
+ ${import_picocolors8.default.dim("Usage:")} getverbal keys <subcommand> [provider]
20883
+
20884
+ ${import_picocolors8.default.dim("Subcommands:")}
20885
+ add <provider> Add a provider API key
20886
+ list List all configured provider keys
20887
+ remove <provider> Remove a provider API key
20888
+
20889
+ ${import_picocolors8.default.dim("Providers:")} ${VALID_PROVIDERS.join(", ")}
20890
+
20891
+ ${import_picocolors8.default.dim("Examples:")}
20892
+ getverbal keys add anthropic
20893
+ getverbal keys list
20894
+ getverbal keys remove openai
20895
+ `.trim();
20896
+ });
20897
+
20440
20898
  // src/commands/sync.ts
20441
20899
  var exports_sync = {};
20442
20900
  __export(exports_sync, {
@@ -20445,12 +20903,12 @@ __export(exports_sync, {
20445
20903
  async function runSync() {
20446
20904
  const creds = await readCredentials();
20447
20905
  if (!creds) {
20448
- console.error(import_picocolors8.default.red("Not authenticated. Run `getverbal init` first."));
20906
+ console.error(import_picocolors9.default.red("Not authenticated. Run `getverbal init` first."));
20449
20907
  process.exitCode = 1;
20450
20908
  return;
20451
20909
  }
20452
20910
  console.log(`
20453
- ${import_picocolors8.default.bold("Syncing AI tool usage...")}
20911
+ ${import_picocolors9.default.bold("Syncing AI tool usage...")}
20454
20912
  `);
20455
20913
  const stateDir = getDefaultStateDir();
20456
20914
  let result;
@@ -20465,36 +20923,36 @@ ${import_picocolors8.default.bold("Syncing AI tool usage...")}
20465
20923
  stateDir
20466
20924
  });
20467
20925
  } catch (err) {
20468
- console.error(import_picocolors8.default.red(`Sync failed: ${err instanceof Error ? err.message : String(err)}`));
20926
+ console.error(import_picocolors9.default.red(`Sync failed: ${err instanceof Error ? err.message : String(err)}`));
20469
20927
  process.exitCode = 1;
20470
20928
  return;
20471
20929
  }
20472
20930
  for (const p of result.platforms) {
20473
20931
  if (p.error) {
20474
- console.log(` ${import_picocolors8.default.dim(p.id.padEnd(14))} ${import_picocolors8.default.yellow("⚠")} ${import_picocolors8.default.dim(p.error)}`);
20932
+ console.log(` ${import_picocolors9.default.dim(p.id.padEnd(14))} ${import_picocolors9.default.yellow("⚠")} ${import_picocolors9.default.dim(p.error)}`);
20475
20933
  } else if (p.events === 0) {
20476
- console.log(` ${import_picocolors8.default.dim(p.id.padEnd(14))} ${import_picocolors8.default.dim("0 events")} ${import_picocolors8.default.green("✓")}`);
20934
+ console.log(` ${import_picocolors9.default.dim(p.id.padEnd(14))} ${import_picocolors9.default.dim("0 events")} ${import_picocolors9.default.green("✓")}`);
20477
20935
  } else {
20478
- const costStr = p.totalCost > 0 ? `$${p.totalCost.toFixed(2)}` : import_picocolors8.default.dim("—");
20479
- const billingNote = p.totalCost === 0 && p.events > 0 ? import_picocolors8.default.dim(" (subscription)") : "";
20480
- console.log(` ${import_picocolors8.default.dim(p.id.padEnd(14))} ${import_picocolors8.default.bold(String(p.events))} events ${costStr}${billingNote} ${import_picocolors8.default.green("✓")}`);
20936
+ const costStr = p.totalCost > 0 ? `$${p.totalCost.toFixed(2)}` : import_picocolors9.default.dim("—");
20937
+ const billingNote = p.totalCost === 0 && p.events > 0 ? import_picocolors9.default.dim(" (subscription)") : "";
20938
+ console.log(` ${import_picocolors9.default.dim(p.id.padEnd(14))} ${import_picocolors9.default.bold(String(p.events))} events ${costStr}${billingNote} ${import_picocolors9.default.green("✓")}`);
20481
20939
  }
20482
20940
  }
20483
20941
  console.log();
20484
20942
  if (result.totalEvents === 0) {
20485
- console.log(import_picocolors8.default.dim("No new events since last sync."));
20943
+ console.log(import_picocolors9.default.dim("No new events since last sync."));
20486
20944
  } else {
20487
20945
  const costStr = result.totalCost > 0 ? `, $${result.totalCost.toFixed(2)} in spend` : "";
20488
- console.log(`${import_picocolors8.default.green("✓")} ${import_picocolors8.default.bold(String(result.totalEvents))} events synced${costStr}`);
20946
+ console.log(`${import_picocolors9.default.green("✓")} ${import_picocolors9.default.bold(String(result.totalEvents))} events synced${costStr}`);
20489
20947
  }
20490
20948
  console.log();
20491
20949
  }
20492
- var import_picocolors8, DEFAULT_BATCH_SIZE = 50, DEFAULT_FLUSH_MS = 0, DEFAULT_TIMEOUT_MS = 30000;
20950
+ var import_picocolors9, DEFAULT_BATCH_SIZE = 50, DEFAULT_FLUSH_MS = 0, DEFAULT_TIMEOUT_MS = 30000;
20493
20951
  var init_sync = __esm(() => {
20494
20952
  init_credentials();
20495
20953
  init_orchestrator();
20496
20954
  init_preflight_state();
20497
- import_picocolors8 = __toESM(require_picocolors(), 1);
20955
+ import_picocolors9 = __toESM(require_picocolors(), 1);
20498
20956
  });
20499
20957
 
20500
20958
  // node_modules/zod/v4/core/core.js
@@ -34918,102 +35376,6 @@ async function sendTrace(config3, traceId, name, events) {
34918
35376
  }
34919
35377
  }
34920
35378
 
34921
- // src/agent-hooks/tool-extraction.ts
34922
- function parseToolInput(toolName, input) {
34923
- const isShell = SHELL_TOOL_NAMES.has(toolName);
34924
- const truncLimit = isShell ? TRUNCATION_BASH : TRUNCATION_DEFAULT;
34925
- let raw;
34926
- let command;
34927
- if (typeof input === "string") {
34928
- raw = input.slice(0, truncLimit);
34929
- if (isShell) {
34930
- if (input.length > 0) {
34931
- try {
34932
- const parsed = JSON.parse(input);
34933
- command = extractCommandFromObject(parsed);
34934
- } catch {
34935
- command = input.slice(0, truncLimit);
34936
- }
34937
- }
34938
- }
34939
- } else if (input !== null && typeof input === "object") {
34940
- if (isShell) {
34941
- command = extractCommandFromObject(input);
34942
- }
34943
- try {
34944
- raw = JSON.stringify(input).slice(0, truncLimit);
34945
- } catch {
34946
- raw = String(input).slice(0, truncLimit);
34947
- }
34948
- } else {
34949
- raw = String(input ?? "").slice(0, truncLimit);
34950
- }
34951
- if (command !== undefined) {
34952
- command = command.slice(0, truncLimit);
34953
- }
34954
- const command_type = command !== undefined ? detectCommandType(command) : undefined;
34955
- return { raw, command, command_type };
34956
- }
34957
- function extractCommandFromObject(obj) {
34958
- const cmd = obj["command"] ?? obj["cmd"] ?? obj["shell_command"];
34959
- return typeof cmd === "string" ? cmd : undefined;
34960
- }
34961
- function detectCommandType(command) {
34962
- const first = command.trim().split(/\s+/)[0] ?? "";
34963
- const basename = first.split("/").pop() ?? first;
34964
- return COMMAND_TYPE_MAP[basename.toLowerCase()] ?? "shell";
34965
- }
34966
- var SHELL_TOOL_NAMES, TRUNCATION_BASH = 2000, TRUNCATION_DEFAULT = 500, COMMAND_TYPE_MAP;
34967
- var init_tool_extraction = __esm(() => {
34968
- SHELL_TOOL_NAMES = new Set([
34969
- "Bash",
34970
- "bash",
34971
- "shell",
34972
- "Shell",
34973
- "exec",
34974
- "run_command",
34975
- "computer"
34976
- ]);
34977
- COMMAND_TYPE_MAP = {
34978
- git: "git",
34979
- npm: "npm",
34980
- npx: "npm",
34981
- bun: "bun",
34982
- bunx: "bun",
34983
- node: "node",
34984
- python: "python",
34985
- python3: "python",
34986
- pip: "python",
34987
- pip3: "python",
34988
- tsc: "typescript",
34989
- vitest: "test",
34990
- jest: "test",
34991
- pytest: "test",
34992
- mocha: "test",
34993
- curl: "http",
34994
- wget: "http",
34995
- cat: "file",
34996
- ls: "file",
34997
- find: "file",
34998
- cp: "file",
34999
- mv: "file",
35000
- rm: "file",
35001
- mkdir: "file",
35002
- grep: "search",
35003
- rg: "search",
35004
- sed: "transform",
35005
- awk: "transform",
35006
- docker: "docker",
35007
- "docker-compose": "docker",
35008
- kubectl: "kubernetes",
35009
- helm: "kubernetes",
35010
- terraform: "infrastructure",
35011
- gh: "github",
35012
- supabase: "supabase",
35013
- bd: "beads"
35014
- };
35015
- });
35016
-
35017
35379
  // src/agent-hooks/claude.ts
35018
35380
  import fs13 from "node:fs";
35019
35381
  async function ingestClaudeHookInput(input, config3) {
@@ -35189,28 +35551,9 @@ function readJsonLines2(filePath) {
35189
35551
  function extractMessage3(entry) {
35190
35552
  const message = entry.message;
35191
35553
  const role = message?.role ?? entry.type;
35192
- const content = flattenContent2(message?.content ?? entry.content);
35554
+ const content = flattenContent(message?.content ?? entry.content);
35193
35555
  return { role, content };
35194
35556
  }
35195
- function flattenContent2(content) {
35196
- if (!content)
35197
- return;
35198
- if (typeof content === "string")
35199
- return content;
35200
- if (Array.isArray(content)) {
35201
- const parts = content.map((item) => {
35202
- if (!item || typeof item !== "object")
35203
- return "";
35204
- const typed = item;
35205
- if (typed.type === "thinking" || typed.type === "tool_use")
35206
- return "";
35207
- return typed.text ?? typed.input_text ?? typed.output_text ?? "";
35208
- }).filter(Boolean);
35209
- return parts.length ? parts.join(`
35210
- `) : undefined;
35211
- }
35212
- return;
35213
- }
35214
35557
  function getEntryId2(entry) {
35215
35558
  return entry.uuid ?? entry.message?.id ?? entry.id ?? entry.messageId;
35216
35559
  }
@@ -35222,6 +35565,7 @@ var init_claude = __esm(() => {
35222
35565
  init_ingest();
35223
35566
  init_state2();
35224
35567
  init_tool_extraction();
35568
+ init_parsing_utils();
35225
35569
  });
35226
35570
 
35227
35571
  // src/agent-hooks/codex.ts
@@ -35501,22 +35845,22 @@ function extractMessage4(entry) {
35501
35845
  const payload = entry.payload;
35502
35846
  if (payload.type === "message") {
35503
35847
  const role = payload.role;
35504
- const content = flattenContent3(payload.content);
35848
+ const content = flattenContent2(payload.content);
35505
35849
  if (role)
35506
35850
  return { role, content };
35507
35851
  }
35508
35852
  }
35509
35853
  if (entry.type === "message" && entry.role) {
35510
- return { role: entry.role, content: flattenContent3(entry.content) };
35854
+ return { role: entry.role, content: flattenContent2(entry.content) };
35511
35855
  }
35512
35856
  if (entry.type === "user" || entry.type === "assistant") {
35513
35857
  const role = entry.type;
35514
- const content = flattenContent3(entry.content ?? entry.message);
35858
+ const content = flattenContent2(entry.content ?? entry.message);
35515
35859
  return { role, content };
35516
35860
  }
35517
35861
  return null;
35518
35862
  }
35519
- function flattenContent3(content) {
35863
+ function flattenContent2(content) {
35520
35864
  if (!content)
35521
35865
  return;
35522
35866
  if (typeof content === "string")
@@ -36141,8 +36485,8 @@ function printUpdateNotice(currentVersion) {
36141
36485
  if (compareSemver(currentVersion, cache.latest) >= 0)
36142
36486
  return;
36143
36487
  const lines = [
36144
- `Update available: ${import_picocolors9.default.dim(currentVersion)} → ${import_picocolors9.default.green(cache.latest)}`,
36145
- `Run ${import_picocolors9.default.cyan("getverbal update")} to upgrade`
36488
+ `Update available: ${import_picocolors10.default.dim(currentVersion)} → ${import_picocolors10.default.green(cache.latest)}`,
36489
+ `Run ${import_picocolors10.default.cyan("getverbal update")} to upgrade`
36146
36490
  ];
36147
36491
  if (cache.message) {
36148
36492
  lines.push("");
@@ -36160,14 +36504,14 @@ function printUpdateNotice(currentVersion) {
36160
36504
  console.log(`└${border}┘`);
36161
36505
  } catch {}
36162
36506
  }
36163
- var import_picocolors9;
36507
+ var import_picocolors10;
36164
36508
  var init_notify = __esm(() => {
36165
36509
  init_credentials();
36166
- import_picocolors9 = __toESM(require_picocolors(), 1);
36510
+ import_picocolors10 = __toESM(require_picocolors(), 1);
36167
36511
  });
36168
36512
 
36169
36513
  // src/cli.ts
36170
- var import_picocolors10 = __toESM(require_picocolors(), 1);
36514
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
36171
36515
 
36172
36516
  // src/commands/init.ts
36173
36517
  var import_picocolors = __toESM(require_picocolors(), 1);
@@ -37095,6 +37439,58 @@ async function runInit() {
37095
37439
  console.log(import_picocolors.default.yellow(" Cursor sync requires Bun runtime. Run via: bunx getverbal sync"));
37096
37440
  }
37097
37441
  }
37442
+ {
37443
+ console.log();
37444
+ console.log(import_picocolors.default.bold("Connect provider billing") + import_picocolors.default.dim(" (optional)"));
37445
+ console.log(import_picocolors.default.dim(" Verbal can pull your API spend directly from providers."));
37446
+ console.log();
37447
+ const providers = [
37448
+ { id: "anthropic", label: "Anthropic" },
37449
+ { id: "openai", label: "OpenAI" }
37450
+ ];
37451
+ let keysAdded = 0;
37452
+ for (const provider of providers) {
37453
+ const connect = await confirm(rl, ` Connect ${provider.label} API billing?`, false);
37454
+ if (!connect) {
37455
+ console.log(import_picocolors.default.dim(` Skipped.`));
37456
+ continue;
37457
+ }
37458
+ const key = await rl.question(` Enter your ${provider.label} Admin API key: `);
37459
+ const trimmedKey = key.trim();
37460
+ if (!trimmedKey) {
37461
+ console.log(import_picocolors.default.dim(" No key entered. Skipped."));
37462
+ continue;
37463
+ }
37464
+ try {
37465
+ const res = await fetch(`${credentials.api_url}/api/v1/provider-keys`, {
37466
+ method: "POST",
37467
+ headers: {
37468
+ "Content-Type": "application/json",
37469
+ Authorization: `Bearer ${credentials.api_key}`
37470
+ },
37471
+ body: JSON.stringify({ provider: provider.id, key: trimmedKey })
37472
+ });
37473
+ if (res.ok) {
37474
+ const data = await res.json();
37475
+ const prefix = data.key_prefix ?? trimmedKey.slice(0, 10);
37476
+ console.log(import_picocolors.default.green(` ✓ ${provider.label} key added (prefix: ${prefix})`));
37477
+ keysAdded++;
37478
+ } else {
37479
+ const errData = await res.json().catch(() => ({}));
37480
+ const errMsg = errData.error ?? `HTTP ${res.status}`;
37481
+ console.log(import_picocolors.default.yellow(` ⚠ ${provider.label} key not saved: ${errMsg}`));
37482
+ }
37483
+ } catch (err) {
37484
+ const msg = err instanceof Error ? err.message : String(err);
37485
+ console.log(import_picocolors.default.yellow(` ⚠ Could not save ${provider.label} key: ${msg}`));
37486
+ }
37487
+ }
37488
+ if (keysAdded > 0) {
37489
+ console.log();
37490
+ console.log(import_picocolors.default.green(` ✓ ${keysAdded} provider key${keysAdded > 1 ? "s" : ""} connected`) + import_picocolors.default.dim(" — billing data will sync automatically"));
37491
+ }
37492
+ console.log();
37493
+ }
37098
37494
  process.stdout.write(import_picocolors.default.dim("Verifying connection..."));
37099
37495
  try {
37100
37496
  await verifyConnection(credentials);
@@ -37266,25 +37662,26 @@ async function runDashboard() {
37266
37662
 
37267
37663
  // src/cli.ts
37268
37664
  var HELP = `
37269
- ${import_picocolors10.default.bold("getverbal")} — AI Spend Tracker
37665
+ ${import_picocolors11.default.bold("getverbal")} — AI Spend Tracker
37270
37666
 
37271
- ${import_picocolors10.default.dim("Usage:")} getverbal <command> [options]
37667
+ ${import_picocolors11.default.dim("Usage:")} getverbal <command> [options]
37272
37668
 
37273
- ${import_picocolors10.default.dim("Commands:")}
37669
+ ${import_picocolors11.default.dim("Commands:")}
37274
37670
  init Authenticate, detect tools, configure, and import history
37275
37671
  status Show current configuration and connection status
37276
37672
  dashboard Open your Verbal dashboard in the browser
37277
37673
  import Bulk import from exports or local tool history
37674
+ keys Manage provider API keys (add, list, remove)
37278
37675
  sync Sync usage data from all detected AI tools
37279
37676
  update Update getverbal to the latest version
37280
37677
  logout Remove stored credentials
37281
37678
  uninstall Remove Verbal from all AI tools and delete credentials
37282
37679
 
37283
- ${import_picocolors10.default.dim("Machine commands (invoked by AI tools):")}
37680
+ ${import_picocolors11.default.dim("Machine commands (invoked by AI tools):")}
37284
37681
  mcp-serve Start MCP server (stdio)
37285
37682
  hooks Agent hook utilities
37286
37683
 
37287
- ${import_picocolors10.default.dim("Options:")}
37684
+ ${import_picocolors11.default.dim("Options:")}
37288
37685
  --help, -h Show this help message
37289
37686
  --version, -v Show version number
37290
37687
  `.trim();
@@ -37316,6 +37713,11 @@ async function main2() {
37316
37713
  await runUpdate2(VERSION);
37317
37714
  break;
37318
37715
  }
37716
+ case "keys": {
37717
+ const { runKeys: runKeys2 } = await Promise.resolve().then(() => (init_keys(), exports_keys));
37718
+ await runKeys2(process.argv.slice(3));
37719
+ break;
37720
+ }
37319
37721
  case "sync": {
37320
37722
  const { runSync: runSync2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
37321
37723
  await runSync2();
@@ -37347,7 +37749,7 @@ async function main2() {
37347
37749
  console.log(HELP);
37348
37750
  return;
37349
37751
  default:
37350
- console.error(`${import_picocolors10.default.red("Unknown command:")} ${command}
37752
+ console.error(`${import_picocolors11.default.red("Unknown command:")} ${command}
37351
37753
  `);
37352
37754
  console.log(HELP);
37353
37755
  process.exitCode = 1;
@@ -37360,6 +37762,6 @@ async function main2() {
37360
37762
  }
37361
37763
  }
37362
37764
  main2().catch((err) => {
37363
- console.error(import_picocolors10.default.red(err instanceof Error ? err.message : "An unexpected error occurred"));
37765
+ console.error(import_picocolors11.default.red(err instanceof Error ? err.message : "An unexpected error occurred"));
37364
37766
  process.exitCode = 1;
37365
37767
  });