@rely-ai/caliber 1.8.0 → 1.9.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 (2) hide show
  1. package/dist/bin.js +146 -10
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -627,6 +627,33 @@ init_config();
627
627
 
628
628
  // src/llm/anthropic.ts
629
629
  import Anthropic from "@anthropic-ai/sdk";
630
+
631
+ // src/llm/usage.ts
632
+ var usageByModel = /* @__PURE__ */ new Map();
633
+ function trackUsage(model, usage) {
634
+ const existing = usageByModel.get(model);
635
+ if (existing) {
636
+ existing.inputTokens += usage.inputTokens;
637
+ existing.outputTokens += usage.outputTokens;
638
+ existing.cacheReadTokens += usage.cacheReadTokens ?? 0;
639
+ existing.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
640
+ existing.calls += 1;
641
+ } else {
642
+ usageByModel.set(model, {
643
+ model,
644
+ inputTokens: usage.inputTokens,
645
+ outputTokens: usage.outputTokens,
646
+ cacheReadTokens: usage.cacheReadTokens ?? 0,
647
+ cacheWriteTokens: usage.cacheWriteTokens ?? 0,
648
+ calls: 1
649
+ });
650
+ }
651
+ }
652
+ function getUsageSummary() {
653
+ return Array.from(usageByModel.values());
654
+ }
655
+
656
+ // src/llm/anthropic.ts
630
657
  var AnthropicProvider = class {
631
658
  client;
632
659
  defaultModel;
@@ -641,6 +668,16 @@ var AnthropicProvider = class {
641
668
  system: [{ type: "text", text: options.system, cache_control: { type: "ephemeral" } }],
642
669
  messages: [{ role: "user", content: options.prompt }]
643
670
  });
671
+ const model = options.model || this.defaultModel;
672
+ if (response.usage) {
673
+ const u = response.usage;
674
+ trackUsage(model, {
675
+ inputTokens: u.input_tokens ?? 0,
676
+ outputTokens: u.output_tokens ?? 0,
677
+ cacheReadTokens: u.cache_read_input_tokens,
678
+ cacheWriteTokens: u.cache_creation_input_tokens
679
+ });
680
+ }
644
681
  const block = response.content[0];
645
682
  return block.type === "text" ? block.text : "";
646
683
  }
@@ -667,11 +704,24 @@ var AnthropicProvider = class {
667
704
  messages
668
705
  });
669
706
  let stopReason;
707
+ let usage;
708
+ const model = options.model || this.defaultModel;
670
709
  stream.on("message", (message) => {
671
- stopReason = message.stop_reason;
710
+ const msg = message;
711
+ stopReason = msg.stop_reason;
712
+ const u = msg.usage;
713
+ if (u) {
714
+ usage = {
715
+ inputTokens: u.input_tokens ?? 0,
716
+ outputTokens: u.output_tokens ?? 0,
717
+ cacheReadTokens: u.cache_read_input_tokens,
718
+ cacheWriteTokens: u.cache_creation_input_tokens
719
+ };
720
+ trackUsage(model, usage);
721
+ }
672
722
  });
673
723
  stream.on("text", (text) => callbacks.onText(text));
674
- stream.on("end", () => callbacks.onEnd({ stopReason }));
724
+ stream.on("end", () => callbacks.onEnd({ stopReason, usage }));
675
725
  stream.on("error", (error) => callbacks.onError(error));
676
726
  }
677
727
  };
@@ -722,6 +772,16 @@ var VertexProvider = class {
722
772
  system: [{ type: "text", text: options.system, cache_control: { type: "ephemeral" } }],
723
773
  messages: [{ role: "user", content: options.prompt }]
724
774
  });
775
+ const model = options.model || this.defaultModel;
776
+ if (response.usage) {
777
+ const u = response.usage;
778
+ trackUsage(model, {
779
+ inputTokens: u.input_tokens ?? 0,
780
+ outputTokens: u.output_tokens ?? 0,
781
+ cacheReadTokens: u.cache_read_input_tokens,
782
+ cacheWriteTokens: u.cache_creation_input_tokens
783
+ });
784
+ }
725
785
  const block = response.content[0];
726
786
  return block.type === "text" ? block.text : "";
727
787
  }
@@ -740,11 +800,24 @@ var VertexProvider = class {
740
800
  messages
741
801
  });
742
802
  let stopReason;
803
+ let usage;
804
+ const model = options.model || this.defaultModel;
743
805
  stream.on("message", (message) => {
744
- stopReason = message.stop_reason;
806
+ const msg = message;
807
+ stopReason = msg.stop_reason;
808
+ const u = msg.usage;
809
+ if (u) {
810
+ usage = {
811
+ inputTokens: u.input_tokens ?? 0,
812
+ outputTokens: u.output_tokens ?? 0,
813
+ cacheReadTokens: u.cache_read_input_tokens,
814
+ cacheWriteTokens: u.cache_creation_input_tokens
815
+ };
816
+ trackUsage(model, usage);
817
+ }
745
818
  });
746
819
  stream.on("text", (text) => callbacks.onText(text));
747
- stream.on("end", () => callbacks.onEnd({ stopReason }));
820
+ stream.on("end", () => callbacks.onEnd({ stopReason, usage }));
748
821
  stream.on("error", (error) => callbacks.onError(error));
749
822
  }
750
823
  };
@@ -770,6 +843,13 @@ var OpenAICompatProvider = class {
770
843
  { role: "user", content: options.prompt }
771
844
  ]
772
845
  });
846
+ const model = options.model || this.defaultModel;
847
+ if (response.usage) {
848
+ trackUsage(model, {
849
+ inputTokens: response.usage.prompt_tokens ?? 0,
850
+ outputTokens: response.usage.completion_tokens ?? 0
851
+ });
852
+ }
773
853
  return response.choices[0]?.message?.content || "";
774
854
  }
775
855
  async listModels() {
@@ -797,13 +877,23 @@ var OpenAICompatProvider = class {
797
877
  });
798
878
  try {
799
879
  let stopReason;
880
+ let usage;
881
+ const model = options.model || this.defaultModel;
800
882
  for await (const chunk of stream) {
801
883
  const delta = chunk.choices[0]?.delta?.content;
802
884
  if (delta != null) callbacks.onText(delta);
803
885
  const finishReason = chunk.choices[0]?.finish_reason;
804
886
  if (finishReason) stopReason = finishReason === "length" ? "max_tokens" : finishReason;
887
+ const chunkUsage = chunk.usage;
888
+ if (chunkUsage) {
889
+ usage = {
890
+ inputTokens: chunkUsage.prompt_tokens ?? 0,
891
+ outputTokens: chunkUsage.completion_tokens ?? 0
892
+ };
893
+ trackUsage(model, usage);
894
+ }
805
895
  }
806
- callbacks.onEnd({ stopReason });
896
+ callbacks.onEnd({ stopReason, usage });
807
897
  } catch (error) {
808
898
  callbacks.onError(error instanceof Error ? error : new Error(String(error)));
809
899
  }
@@ -1910,9 +2000,9 @@ async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failin
1910
2000
  }
1911
2001
  if (setup) {
1912
2002
  if (callbacks) callbacks.onComplete(setup, explanation);
1913
- resolve2({ setup, explanation });
2003
+ resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
1914
2004
  } else {
1915
- resolve2({ setup: null, explanation, raw: preJsonBuffer });
2005
+ resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
1916
2006
  }
1917
2007
  },
1918
2008
  onError: (error) => {
@@ -1922,12 +2012,12 @@ async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failin
1922
2012
  return;
1923
2013
  }
1924
2014
  if (callbacks) callbacks.onError(error.message);
1925
- resolve2({ setup: null, raw: error.message });
2015
+ resolve2({ setup: null, raw: error.message, stopReason: "error" });
1926
2016
  }
1927
2017
  }
1928
2018
  ).catch((error) => {
1929
2019
  if (callbacks) callbacks.onError(error.message);
1930
- resolve2({ setup: null, raw: error.message });
2020
+ resolve2({ setup: null, raw: error.message, stopReason: "error" });
1931
2021
  });
1932
2022
  });
1933
2023
  };
@@ -5558,6 +5648,7 @@ async function initCommand(options) {
5558
5648
  genMessages.start();
5559
5649
  let generatedSetup = null;
5560
5650
  let rawOutput;
5651
+ let genStopReason;
5561
5652
  try {
5562
5653
  const result = await generateSetup(
5563
5654
  fingerprint,
@@ -5583,15 +5674,18 @@ async function initCommand(options) {
5583
5674
  generatedSetup = result.setup;
5584
5675
  rawOutput = result.raw;
5585
5676
  }
5677
+ genStopReason = result.stopReason;
5586
5678
  } catch (err) {
5587
5679
  genMessages.stop();
5588
5680
  const msg = err instanceof Error ? err.message : "Unknown error";
5589
5681
  genSpinner.fail(`Generation failed: ${msg}`);
5682
+ writeErrorLog(config, void 0, msg, "exception");
5590
5683
  throw new Error("__exit__");
5591
5684
  }
5592
5685
  genMessages.stop();
5593
5686
  if (!generatedSetup) {
5594
5687
  genSpinner.fail("Failed to generate setup.");
5688
+ writeErrorLog(config, rawOutput, void 0, genStopReason);
5595
5689
  if (rawOutput) {
5596
5690
  console.log(chalk8.dim("\nRaw LLM output (JSON parse failed):"));
5597
5691
  console.log(chalk8.dim(rawOutput.slice(0, 500)));
@@ -5825,6 +5919,9 @@ async function initCommand(options) {
5825
5919
  console.log(` ${title("caliber skills")} Discover community skills for your stack`);
5826
5920
  console.log(` ${title("caliber undo")} Revert all changes from this run`);
5827
5921
  console.log("");
5922
+ if (options.showTokens) {
5923
+ displayTokenUsage();
5924
+ }
5828
5925
  if (report) {
5829
5926
  report.markStep("Finished");
5830
5927
  const reportPath = path19.join(process.cwd(), ".caliber", "debug-report.md");
@@ -6101,6 +6198,45 @@ function ensurePermissions() {
6101
6198
  if (!fs24.existsSync(".claude")) fs24.mkdirSync(".claude", { recursive: true });
6102
6199
  fs24.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6103
6200
  }
6201
+ function displayTokenUsage() {
6202
+ const summary = getUsageSummary();
6203
+ if (summary.length === 0) return;
6204
+ console.log(chalk8.bold(" Token usage:\n"));
6205
+ let totalIn = 0;
6206
+ let totalOut = 0;
6207
+ for (const m of summary) {
6208
+ totalIn += m.inputTokens;
6209
+ totalOut += m.outputTokens;
6210
+ const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk8.dim(` (cache: ${m.cacheReadTokens.toLocaleString()} read, ${m.cacheWriteTokens.toLocaleString()} write)`) : "";
6211
+ console.log(` ${chalk8.dim(m.model)}: ${m.inputTokens.toLocaleString()} in / ${m.outputTokens.toLocaleString()} out (${m.calls} call${m.calls === 1 ? "" : "s"})${cacheInfo}`);
6212
+ }
6213
+ if (summary.length > 1) {
6214
+ console.log(` ${chalk8.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`);
6215
+ }
6216
+ console.log("");
6217
+ }
6218
+ function writeErrorLog(config, rawOutput, error, stopReason) {
6219
+ try {
6220
+ const logPath = path19.join(process.cwd(), ".caliber", "error-log.md");
6221
+ const lines = [
6222
+ `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
6223
+ "",
6224
+ `**Provider**: ${config.provider}`,
6225
+ `**Model**: ${config.model}`,
6226
+ `**Stop reason**: ${stopReason || "unknown"}`,
6227
+ ""
6228
+ ];
6229
+ if (error) {
6230
+ lines.push("## Error", "```", error, "```", "");
6231
+ }
6232
+ lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
6233
+ fs24.mkdirSync(path19.join(process.cwd(), ".caliber"), { recursive: true });
6234
+ fs24.writeFileSync(logPath, lines.join("\n"));
6235
+ console.log(chalk8.dim(`
6236
+ Error log written to .caliber/error-log.md`));
6237
+ } catch {
6238
+ }
6239
+ }
6104
6240
 
6105
6241
  // src/commands/undo.ts
6106
6242
  import chalk9 from "chalk";
@@ -7265,7 +7401,7 @@ function parseAgentOption(value) {
7265
7401
  }
7266
7402
  return agents;
7267
7403
  }
7268
- program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").option("--debug-report", void 0, false).action(tracked("init", initCommand));
7404
+ program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").action(tracked("init", initCommand));
7269
7405
  program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
7270
7406
  program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
7271
7407
  program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Analyze your codebase and generate optimized AI agent configs (CLAUDE.md, .cursorrules, skills) — no API key needed",
5
5
  "type": "module",
6
6
  "bin": {