ai-spend-agent 0.2.0 → 0.2.1

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/index.js +38 -7
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -835,7 +835,7 @@ async function reportCommand(args) {
835
835
  return {
836
836
  exitCode: 1,
837
837
  stdout: "",
838
- stderr: `No local spend state found at ${stateDir}. Run scan --sample --path <dir> first. ${error instanceof Error ? error.message : ""}`
838
+ stderr: `Couldn't build a report: ${error instanceof Error ? error.message : String(error)}`
839
839
  };
840
840
  }
841
841
  }
@@ -897,15 +897,35 @@ async function applyArtifactCommand(args) {
897
897
  return {
898
898
  exitCode: 1,
899
899
  stdout: "",
900
- stderr: `No local spend state found at ${stateDir}. Run scan --sample --path <dir> first. ${error instanceof Error ? error.message : ""}`
900
+ stderr: `Couldn't build apply artifacts: ${error instanceof Error ? error.message : String(error)}`
901
901
  };
902
902
  }
903
903
  }
904
904
  async function buildReportInput(stateDir, rootPath) {
905
- const [spendState, discovery, mappings, sourceRegistry, missingSourcePrompts, confirmedMappings, providerRecordsState] = await Promise.all([
906
- readJson(join(stateDir, "spend.json")),
907
- readJson(join(stateDir, "discovery.json")),
908
- readJson(join(stateDir, "mappings.json")),
905
+ let spendState = await readOptionalJson(join(stateDir, "spend.json"), undefined);
906
+ let mappings = await readOptionalJson(join(stateDir, "mappings.json"), undefined);
907
+ // The quickstart intentionally writes nothing — so `report`/`apply-artifact`
908
+ // right after a first run must fall back to the SAME live local-log read
909
+ // (never sample data), then persist it so subsequent runs are consistent.
910
+ if (!spendState?.summary || !spendState.records || spendState.records.length === 0) {
911
+ const logs = await loadLocalAgentUsage({
912
+ claudeProjectsDir: process.env.AI_SPEND_CLAUDE_LOGS_DIR,
913
+ codexSessionsDir: process.env.AI_SPEND_CODEX_LOGS_DIR
914
+ }).catch(() => undefined);
915
+ if (!logs || logs.records.length === 0) {
916
+ throw new Error("no persisted spend state and no local Claude Code/Codex logs found. " +
917
+ "Run `npx ai-spend-agent` first (or `npx ai-spend-agent scan --sample --path <dir>` for a demo-data report).");
918
+ }
919
+ const records = logs.records;
920
+ const summary = analyzeSpend(records);
921
+ const liveMappings = attributeUsageRecords(records);
922
+ await mkdir(stateDir, { recursive: true });
923
+ await writeLocalSpendState(stateDir, records, summary, liveMappings, "local_logs");
924
+ spendState = { summary, records, mode: "local_logs" };
925
+ mappings = liveMappings;
926
+ }
927
+ const [discovery, sourceRegistry, missingSourcePrompts, confirmedMappings, providerRecordsState] = await Promise.all([
928
+ readOptionalJson(join(stateDir, "discovery.json"), emptyDiscovery(rootPath)),
909
929
  readSourceRegistry(stateDir, rootPath),
910
930
  readOptionalJson(join(stateDir, "missing-sources.json"), []),
911
931
  readConfirmedMappings(stateDir),
@@ -918,7 +938,7 @@ async function buildReportInput(stateDir, rootPath) {
918
938
  allRecords: spendState.records ?? [],
919
939
  dataMode: spendState.mode,
920
940
  discovery,
921
- mappings,
941
+ mappings: mappings ?? [],
922
942
  sourceRegistry,
923
943
  missingSourcePrompts,
924
944
  confirmedMappings,
@@ -926,6 +946,17 @@ async function buildReportInput(stateDir, rootPath) {
926
946
  providerQa: providerRecordsState.qa ? [providerRecordsState.qa] : []
927
947
  };
928
948
  }
949
+ function emptyDiscovery(rootPath) {
950
+ return {
951
+ rootPath,
952
+ scannedFiles: 0,
953
+ skippedDirectories: [],
954
+ unreadablePaths: [],
955
+ signals: [],
956
+ secretsDetected: [],
957
+ redactedEvidence: []
958
+ };
959
+ }
929
960
  async function writeApplyArtifacts(stateDir, reportInput) {
930
961
  const paths = {
931
962
  codingPrompt: join(stateDir, "ai-spend-coding-agent-prompt.md"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-spend-agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Your AI spend in one view, in 90 seconds — local-first CLI for OpenAI, Anthropic, Cursor, Copilot + Claude Code/Codex session logs, with a ranked savings cut list, and flags the tools your agent loads but never uses (dead context).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,8 +49,8 @@
49
49
  "prepack": "npm run build"
50
50
  },
51
51
  "dependencies": {
52
- "@agent-finops/core": "0.2.0",
53
- "@agent-finops/report": "0.2.0",
52
+ "@agent-finops/core": "0.2.1",
53
+ "@agent-finops/report": "0.2.1",
54
54
  "yocto-spinner": "^1.2.0"
55
55
  }
56
56
  }