devrage 0.5.4 → 0.5.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.
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/commands/scan.ts
4
4
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
5
- import { dirname as dirname2, join as join10 } from "node:path";
5
+ import { dirname as dirname2, join as join11 } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
8
8
  // src/adapters/amp.ts
@@ -569,11 +569,19 @@ function codexAdapter() {
569
569
  }
570
570
  },
571
571
  async *usage(options) {
572
+ const seenUsage = /* @__PURE__ */ new Set();
572
573
  for await (const file of discoverCodexSessionFiles(CODEX_SESSIONS_DIR)) {
573
- yield* parseCodexUsageJsonl(file.filePath, {
574
+ for await (const record of parseCodexUsageJsonl(file.filePath, {
574
575
  session: file.session,
575
576
  since: options?.since
576
- });
577
+ })) {
578
+ const key = codexUsageRecordKey(record);
579
+ if (seenUsage.has(key)) {
580
+ continue;
581
+ }
582
+ seenUsage.add(key);
583
+ yield record;
584
+ }
577
585
  }
578
586
  }
579
587
  };
@@ -591,10 +599,15 @@ async function* discoverCodexSessionFiles(dir) {
591
599
  if (entryStat.isDirectory()) {
592
600
  yield* discoverCodexSessionFiles(fullPath);
593
601
  } else if (entry.endsWith(".jsonl")) {
594
- yield { filePath: fullPath, session: entry.replace(".jsonl", "") };
602
+ yield { filePath: fullPath, session: sessionFromRolloutFileName(entry) };
595
603
  }
596
604
  }
597
605
  }
606
+ function sessionFromRolloutFileName(fileName) {
607
+ return fileName.match(
608
+ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i
609
+ )?.[1] ?? fileName.replace(".jsonl", "");
610
+ }
598
611
  async function* parseCodexJsonl(filePath, context) {
599
612
  const rl = createInterface2({
600
613
  input: createReadStream2(filePath, { encoding: "utf-8" }),
@@ -655,6 +668,8 @@ async function* parseCodexUsageJsonl(filePath, context) {
655
668
  let model;
656
669
  let previousTotal = null;
657
670
  let previousUsageSignature = null;
671
+ let session = context.session;
672
+ let sawSessionMeta = false;
658
673
  for await (const line of rl) {
659
674
  if (!line.trim()) {
660
675
  continue;
@@ -662,6 +677,14 @@ async function* parseCodexUsageJsonl(filePath, context) {
662
677
  try {
663
678
  const entry = JSON.parse(line);
664
679
  const payload = asRecord3(entry["payload"]);
680
+ if (entry["type"] === "session_meta") {
681
+ const metaSession = stringValue2(payload?.["id"]) ?? stringValue2(entry["id"]);
682
+ if (metaSession && !sawSessionMeta) {
683
+ session = metaSession;
684
+ sawSessionMeta = true;
685
+ }
686
+ continue;
687
+ }
665
688
  if (entry["type"] === "turn_context") {
666
689
  model = stringValue2(payload?.["model"]) ?? model;
667
690
  continue;
@@ -710,7 +733,7 @@ async function* parseCodexUsageJsonl(filePath, context) {
710
733
  provider: "openai",
711
734
  model,
712
735
  timestamp,
713
- session: context.session,
736
+ session,
714
737
  inputTokens: Math.max(usage2.inputTokens - usage2.cachedInputTokens, 0),
715
738
  outputTokens: Math.max(usage2.outputTokens - reasoningTokens, 0),
716
739
  reasoningTokens,
@@ -721,6 +744,19 @@ async function* parseCodexUsageJsonl(filePath, context) {
721
744
  }
722
745
  }
723
746
  }
747
+ function codexUsageRecordKey(record) {
748
+ return JSON.stringify([
749
+ record.session ?? "",
750
+ record.timestamp ?? "",
751
+ record.provider ?? "",
752
+ record.model ?? "",
753
+ record.inputTokens,
754
+ record.outputTokens,
755
+ record.reasoningTokens,
756
+ record.cacheReadTokens,
757
+ record.cacheWriteTokens
758
+ ]);
759
+ }
724
760
  function parseCodexTokenUsage(value) {
725
761
  const usage2 = asRecord3(value);
726
762
  if (!usage2) {
@@ -1653,23 +1689,444 @@ function asRecord5(value) {
1653
1689
  return value;
1654
1690
  }
1655
1691
 
1656
- // src/adapters/zed.ts
1657
- import { readdir as readdir7, readFile as readFile3 } from "node:fs/promises";
1692
+ // src/adapters/t3code.ts
1658
1693
  import { existsSync as existsSync4 } from "node:fs";
1659
1694
  import { homedir as homedir8 } from "node:os";
1660
- import { join as join8 } from "node:path";
1695
+ import { isAbsolute, join as join8, resolve } from "node:path";
1696
+ function t3codeAdapter() {
1697
+ return {
1698
+ name: "t3code",
1699
+ async *messages(options) {
1700
+ for (const location of discoverT3Databases()) {
1701
+ const db = await openT3Db(location.path);
1702
+ if (!db) {
1703
+ continue;
1704
+ }
1705
+ try {
1706
+ yield* queryUserMessages2(db, location, options);
1707
+ } finally {
1708
+ db.close();
1709
+ }
1710
+ }
1711
+ },
1712
+ async *usage(options) {
1713
+ const seen = /* @__PURE__ */ new Set();
1714
+ for (const location of discoverT3Databases()) {
1715
+ const db = await openT3Db(location.path);
1716
+ if (!db) {
1717
+ continue;
1718
+ }
1719
+ try {
1720
+ yield* queryUsageRecords2(db, location, seen, options);
1721
+ } finally {
1722
+ db.close();
1723
+ }
1724
+ }
1725
+ }
1726
+ };
1727
+ }
1728
+ function discoverT3Databases() {
1729
+ const locations = [];
1730
+ const seen = /* @__PURE__ */ new Set();
1731
+ const stateDir = stringValue6(process.env["T3CODE_STATE_DIR"]);
1732
+ if (stateDir) {
1733
+ addLocation(locations, seen, join8(resolveHomePath(stateDir), "state.sqlite"), "state");
1734
+ }
1735
+ for (const baseDir of uniqueStrings2([
1736
+ stringValue6(process.env["T3CODE_HOME"]),
1737
+ join8(homedir8(), ".t3")
1738
+ ])) {
1739
+ addLocation(
1740
+ locations,
1741
+ seen,
1742
+ join8(resolveHomePath(baseDir), "userdata", "state.sqlite"),
1743
+ "userdata"
1744
+ );
1745
+ addLocation(locations, seen, join8(resolveHomePath(baseDir), "dev", "state.sqlite"), "dev");
1746
+ }
1747
+ return locations;
1748
+ }
1749
+ function addLocation(locations, seen, dbPath, scope) {
1750
+ if (seen.has(dbPath) || !existsSync4(dbPath)) {
1751
+ return;
1752
+ }
1753
+ seen.add(dbPath);
1754
+ locations.push({ path: dbPath, scope });
1755
+ }
1756
+ function resolveHomePath(value) {
1757
+ if (value === "~") {
1758
+ return homedir8();
1759
+ }
1760
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
1761
+ return join8(homedir8(), value.slice(2));
1762
+ }
1763
+ return isAbsolute(value) ? value : resolve(value);
1764
+ }
1765
+ async function openT3Db(dbPath) {
1766
+ try {
1767
+ const BetterSqlite3 = await import("better-sqlite3");
1768
+ const Ctor = BetterSqlite3.default ?? BetterSqlite3;
1769
+ return new Ctor(
1770
+ dbPath,
1771
+ { readonly: true, fileMustExist: true }
1772
+ );
1773
+ } catch {
1774
+ return null;
1775
+ }
1776
+ }
1777
+ function* queryUserMessages2(db, location, options) {
1778
+ if (!hasColumns(db, "projection_thread_messages", ["thread_id", "role", "text", "created_at"])) {
1779
+ return;
1780
+ }
1781
+ const orderColumn = hasColumns(db, "projection_thread_messages", ["message_id"]) ? "message_id" : "created_at";
1782
+ let query = `
1783
+ SELECT thread_id, created_at, text
1784
+ FROM projection_thread_messages
1785
+ WHERE role = 'user'
1786
+ `;
1787
+ const params = [];
1788
+ if (options?.since) {
1789
+ query += ` AND created_at >= ?`;
1790
+ params.push(options.since.toISOString());
1791
+ }
1792
+ query += ` ORDER BY created_at ASC, ${orderColumn} ASC`;
1793
+ let rows;
1794
+ try {
1795
+ rows = db.prepare(query).all(...params);
1796
+ } catch {
1797
+ return;
1798
+ }
1799
+ for (const row of rows) {
1800
+ const text = stringValue6(row.text);
1801
+ if (!text) {
1802
+ continue;
1803
+ }
1804
+ yield {
1805
+ text,
1806
+ timestamp: stringValue6(row.created_at),
1807
+ session: stringValue6(row.thread_id),
1808
+ project: location.scope
1809
+ };
1810
+ }
1811
+ }
1812
+ function* queryUsageRecords2(db, location, seen, options) {
1813
+ if (!hasColumns(db, "orchestration_events", [
1814
+ "event_id",
1815
+ "stream_id",
1816
+ "event_type",
1817
+ "occurred_at",
1818
+ "payload_json"
1819
+ ])) {
1820
+ return;
1821
+ }
1822
+ const threadInfo = readThreadInfo(db);
1823
+ const orderColumn = hasColumns(db, "orchestration_events", ["sequence"]) ? "sequence" : "event_id";
1824
+ let query = `
1825
+ SELECT event_id, stream_id, occurred_at, payload_json
1826
+ FROM orchestration_events
1827
+ WHERE event_type = 'thread.activity-appended'
1828
+ `;
1829
+ const params = [];
1830
+ if (options?.since) {
1831
+ query += ` AND occurred_at >= ?`;
1832
+ params.push(options.since.toISOString());
1833
+ }
1834
+ query += ` ORDER BY occurred_at ASC, ${orderColumn} ASC`;
1835
+ let rows;
1836
+ try {
1837
+ rows = db.prepare(query).all(...params);
1838
+ } catch {
1839
+ return;
1840
+ }
1841
+ for (const row of rows) {
1842
+ const payload = asRecord6(parseJson(row.payload_json));
1843
+ const activity = asRecord6(payload?.["activity"]);
1844
+ if (activity?.["kind"] !== "context-window.updated") {
1845
+ continue;
1846
+ }
1847
+ const usage2 = parseUsageSnapshot(activity["payload"]);
1848
+ if (!usage2 || !hasBillableUsage2(usage2)) {
1849
+ continue;
1850
+ }
1851
+ const threadId = stringValue6(payload?.["threadId"]) ?? stringValue6(row.stream_id);
1852
+ const timestamp = stringValue6(activity["createdAt"]) ?? stringValue6(row.occurred_at);
1853
+ const turnId = stringValue6(activity["turnId"]);
1854
+ const info = threadId ? threadInfo.get(threadId) : void 0;
1855
+ const provider = normalizeT3Provider(info?.provider, info?.model);
1856
+ const dedupeKey = t3UsageDedupeKey({
1857
+ scope: location.scope,
1858
+ threadId,
1859
+ turnId,
1860
+ provider,
1861
+ model: info?.model,
1862
+ usage: usage2
1863
+ });
1864
+ if (seen.has(dedupeKey)) {
1865
+ continue;
1866
+ }
1867
+ seen.add(dedupeKey);
1868
+ yield {
1869
+ agent: "t3code",
1870
+ provider,
1871
+ model: info?.model,
1872
+ timestamp,
1873
+ session: threadId,
1874
+ inputTokens: usage2.inputTokens,
1875
+ outputTokens: usage2.outputTokens,
1876
+ reasoningTokens: usage2.reasoningTokens,
1877
+ cacheReadTokens: usage2.cacheReadTokens,
1878
+ cacheWriteTokens: usage2.cacheWriteTokens
1879
+ };
1880
+ }
1881
+ }
1882
+ function normalizeT3Provider(provider, model) {
1883
+ const normalized = provider?.trim().toLowerCase();
1884
+ const key = normalized?.replace(/[^a-z0-9]/g, "");
1885
+ switch (key) {
1886
+ case "codex":
1887
+ return "openai";
1888
+ case "claudeagent":
1889
+ case "claudecode":
1890
+ return "anthropic";
1891
+ case "cursor":
1892
+ case "opencode":
1893
+ return void 0;
1894
+ default:
1895
+ if (key?.includes("codex")) {
1896
+ return "openai";
1897
+ }
1898
+ if (key?.includes("claude")) {
1899
+ return "anthropic";
1900
+ }
1901
+ if (normalized === "openai" || normalized === "anthropic") {
1902
+ return normalized;
1903
+ }
1904
+ return providerFromModel(model);
1905
+ }
1906
+ }
1907
+ function providerFromModel(model) {
1908
+ const slash = model?.indexOf("/") ?? -1;
1909
+ if (!model || slash <= 0) {
1910
+ return void 0;
1911
+ }
1912
+ return model.slice(0, slash);
1913
+ }
1914
+ function readThreadInfo(db) {
1915
+ const info = /* @__PURE__ */ new Map();
1916
+ readProjectionThreadModels(db, info);
1917
+ readProjectionThreadProviders(db, info);
1918
+ return info;
1919
+ }
1920
+ function readProjectionThreadModels(db, info) {
1921
+ if (!tableExists(db, "projection_threads")) {
1922
+ return;
1923
+ }
1924
+ const columns = tableColumns(db, "projection_threads");
1925
+ if (!columns.has("thread_id")) {
1926
+ return;
1927
+ }
1928
+ try {
1929
+ if (columns.has("model_selection_json")) {
1930
+ const rows = db.prepare("SELECT thread_id, model_selection_json FROM projection_threads").all();
1931
+ for (const row of rows) {
1932
+ const threadId = stringValue6(row.thread_id);
1933
+ const modelSelection = asRecord6(parseJson(row.model_selection_json));
1934
+ if (!threadId || !modelSelection) {
1935
+ continue;
1936
+ }
1937
+ const entry = info.get(threadId) ?? {};
1938
+ entry.model = stringValue6(modelSelection["model"]) ?? entry.model;
1939
+ entry.provider = stringValue6(modelSelection["provider"]) ?? stringValue6(modelSelection["instanceId"]) ?? entry.provider;
1940
+ info.set(threadId, entry);
1941
+ }
1942
+ return;
1943
+ }
1944
+ if (columns.has("model")) {
1945
+ const rows = db.prepare("SELECT thread_id, model FROM projection_threads").all();
1946
+ for (const row of rows) {
1947
+ const threadId = stringValue6(row.thread_id);
1948
+ const model = stringValue6(row.model);
1949
+ if (threadId && model) {
1950
+ info.set(threadId, { ...info.get(threadId), model });
1951
+ }
1952
+ }
1953
+ }
1954
+ } catch {
1955
+ return;
1956
+ }
1957
+ }
1958
+ function readProjectionThreadProviders(db, info) {
1959
+ if (!hasColumns(db, "projection_thread_sessions", ["thread_id", "provider_name"])) {
1960
+ return;
1961
+ }
1962
+ try {
1963
+ const rows = db.prepare("SELECT thread_id, provider_name FROM projection_thread_sessions").all();
1964
+ for (const row of rows) {
1965
+ const threadId = stringValue6(row.thread_id);
1966
+ const provider = stringValue6(row.provider_name);
1967
+ if (!threadId || !provider) {
1968
+ continue;
1969
+ }
1970
+ info.set(threadId, { ...info.get(threadId), provider });
1971
+ }
1972
+ } catch {
1973
+ return;
1974
+ }
1975
+ }
1976
+ function parseUsageSnapshot(value) {
1977
+ const usage2 = asRecord6(value);
1978
+ if (!usage2) {
1979
+ return null;
1980
+ }
1981
+ const lastInputTokens = tokenValue(usage2["lastInputTokens"] ?? usage2["last_input_tokens"]);
1982
+ const lastCachedInputTokens = tokenValue(
1983
+ usage2["lastCachedInputTokens"] ?? usage2["last_cached_input_tokens"]
1984
+ );
1985
+ const lastOutputTokens = tokenValue(usage2["lastOutputTokens"] ?? usage2["last_output_tokens"]);
1986
+ const lastReasoningOutputTokens = tokenValue(
1987
+ usage2["lastReasoningOutputTokens"] ?? usage2["last_reasoning_output_tokens"]
1988
+ );
1989
+ const hasLastDetails = [
1990
+ lastInputTokens,
1991
+ lastCachedInputTokens,
1992
+ lastOutputTokens,
1993
+ lastReasoningOutputTokens
1994
+ ].some((token) => token !== void 0);
1995
+ if (hasLastDetails) {
1996
+ return splitTokenUsage({
1997
+ inputTokens: lastInputTokens ?? 0,
1998
+ cachedInputTokens: lastCachedInputTokens ?? 0,
1999
+ outputTokens: lastOutputTokens ?? 0,
2000
+ reasoningOutputTokens: lastReasoningOutputTokens ?? 0
2001
+ });
2002
+ }
2003
+ const inputTokens = tokenValue(usage2["inputTokens"] ?? usage2["input_tokens"]);
2004
+ const cachedInputTokens = tokenValue(usage2["cachedInputTokens"] ?? usage2["cached_input_tokens"]);
2005
+ const outputTokens = tokenValue(usage2["outputTokens"] ?? usage2["output_tokens"]);
2006
+ const reasoningOutputTokens = tokenValue(
2007
+ usage2["reasoningOutputTokens"] ?? usage2["reasoning_output_tokens"]
2008
+ );
2009
+ const hasSnapshotDetails = [
2010
+ inputTokens,
2011
+ cachedInputTokens,
2012
+ outputTokens,
2013
+ reasoningOutputTokens
2014
+ ].some((token) => token !== void 0);
2015
+ if (!hasSnapshotDetails) {
2016
+ return null;
2017
+ }
2018
+ return splitTokenUsage({
2019
+ inputTokens: inputTokens ?? 0,
2020
+ cachedInputTokens: cachedInputTokens ?? 0,
2021
+ outputTokens: outputTokens ?? 0,
2022
+ reasoningOutputTokens: reasoningOutputTokens ?? 0
2023
+ });
2024
+ }
2025
+ function splitTokenUsage(input) {
2026
+ const reasoningTokens = Math.min(input.reasoningOutputTokens, input.outputTokens);
2027
+ return {
2028
+ inputTokens: Math.max(input.inputTokens - input.cachedInputTokens, 0),
2029
+ outputTokens: Math.max(input.outputTokens - reasoningTokens, 0),
2030
+ reasoningTokens,
2031
+ cacheReadTokens: input.cachedInputTokens,
2032
+ cacheWriteTokens: 0
2033
+ };
2034
+ }
2035
+ function hasBillableUsage2(usage2) {
2036
+ return usage2.inputTokens + usage2.outputTokens + usage2.reasoningTokens + usage2.cacheReadTokens + usage2.cacheWriteTokens > 0;
2037
+ }
2038
+ function t3UsageDedupeKey(input) {
2039
+ return JSON.stringify([
2040
+ input.scope,
2041
+ input.threadId ?? "",
2042
+ input.turnId ?? "",
2043
+ input.provider ?? "",
2044
+ input.model ?? "",
2045
+ input.usage.inputTokens,
2046
+ input.usage.outputTokens,
2047
+ input.usage.reasoningTokens,
2048
+ input.usage.cacheReadTokens,
2049
+ input.usage.cacheWriteTokens
2050
+ ]);
2051
+ }
2052
+ function hasColumns(db, table, requiredColumns) {
2053
+ const columns = tableColumns(db, table);
2054
+ return requiredColumns.every((column) => columns.has(column));
2055
+ }
2056
+ function tableExists(db, table) {
2057
+ try {
2058
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1").get(table);
2059
+ return Boolean(row);
2060
+ } catch {
2061
+ return false;
2062
+ }
2063
+ }
2064
+ function tableColumns(db, table) {
2065
+ if (!tableExists(db, table)) {
2066
+ return /* @__PURE__ */ new Set();
2067
+ }
2068
+ try {
2069
+ const rows = db.prepare(`PRAGMA table_info("${table}")`).all();
2070
+ return new Set(rows.flatMap((row) => stringValue6(row.name) ?? []));
2071
+ } catch {
2072
+ return /* @__PURE__ */ new Set();
2073
+ }
2074
+ }
2075
+ function parseJson(value) {
2076
+ if (typeof value !== "string") {
2077
+ return null;
2078
+ }
2079
+ try {
2080
+ return JSON.parse(value);
2081
+ } catch {
2082
+ return null;
2083
+ }
2084
+ }
2085
+ function tokenValue(value) {
2086
+ if (typeof value !== "number" || !Number.isFinite(value)) {
2087
+ return void 0;
2088
+ }
2089
+ return Math.max(Math.round(value), 0);
2090
+ }
2091
+ function uniqueStrings2(values) {
2092
+ const seen = /* @__PURE__ */ new Set();
2093
+ const unique = [];
2094
+ for (const value of values) {
2095
+ if (!value || seen.has(value)) {
2096
+ continue;
2097
+ }
2098
+ seen.add(value);
2099
+ unique.push(value);
2100
+ }
2101
+ return unique;
2102
+ }
2103
+ function stringValue6(value) {
2104
+ return typeof value === "string" && value.trim() ? value : void 0;
2105
+ }
2106
+ function asRecord6(value) {
2107
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2108
+ return null;
2109
+ }
2110
+ return value;
2111
+ }
2112
+
2113
+ // src/adapters/zed.ts
2114
+ import { readdir as readdir7, readFile as readFile3 } from "node:fs/promises";
2115
+ import { existsSync as existsSync5 } from "node:fs";
2116
+ import { homedir as homedir9 } from "node:os";
2117
+ import { join as join9 } from "node:path";
1661
2118
  function getZedPaths() {
1662
2119
  if (process.platform === "darwin") {
1663
- const base2 = join8(homedir8(), "Library", "Application Support", "Zed");
2120
+ const base2 = join9(homedir9(), "Library", "Application Support", "Zed");
1664
2121
  return {
1665
- conversations: join8(base2, "conversations"),
1666
- db: join8(base2, "db")
2122
+ conversations: join9(base2, "conversations"),
2123
+ db: join9(base2, "db")
1667
2124
  };
1668
2125
  }
1669
- const base = join8(process.env["XDG_DATA_HOME"] ?? join8(homedir8(), ".local", "share"), "zed");
2126
+ const base = join9(process.env["XDG_DATA_HOME"] ?? join9(homedir9(), ".local", "share"), "zed");
1670
2127
  return {
1671
- conversations: join8(base, "conversations"),
1672
- db: join8(base, "db")
2128
+ conversations: join9(base, "conversations"),
2129
+ db: join9(base, "db")
1673
2130
  };
1674
2131
  }
1675
2132
  function zedAdapter() {
@@ -1683,7 +2140,7 @@ function zedAdapter() {
1683
2140
  };
1684
2141
  }
1685
2142
  async function* parseTextThreads(dir, _options) {
1686
- if (!existsSync4(dir)) {
2143
+ if (!existsSync5(dir)) {
1687
2144
  return;
1688
2145
  }
1689
2146
  let files;
@@ -1694,7 +2151,7 @@ async function* parseTextThreads(dir, _options) {
1694
2151
  }
1695
2152
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
1696
2153
  for (const file of jsonFiles) {
1697
- const filePath = join8(dir, file);
2154
+ const filePath = join9(dir, file);
1698
2155
  const session = file.replace(".json", "");
1699
2156
  try {
1700
2157
  const raw = await readFile3(filePath, "utf-8");
@@ -1720,7 +2177,7 @@ async function* parseTextThreads(dir, _options) {
1720
2177
  }
1721
2178
  }
1722
2179
  async function* parseAgentThreads(dbDir, _options) {
1723
- if (!existsSync4(dbDir)) {
2180
+ if (!existsSync5(dbDir)) {
1724
2181
  return;
1725
2182
  }
1726
2183
  let dbFiles;
@@ -1741,7 +2198,7 @@ async function* parseAgentThreads(dbDir, _options) {
1741
2198
  return;
1742
2199
  }
1743
2200
  for (const dbFile of dbFiles) {
1744
- const dbPath = join8(dbDir, dbFile);
2201
+ const dbPath = join9(dbDir, dbFile);
1745
2202
  let db;
1746
2203
  try {
1747
2204
  db = new Database(dbPath, {
@@ -1792,6 +2249,7 @@ var ADAPTERS = {
1792
2249
  amp: ampAdapter,
1793
2250
  cline: clineAdapter,
1794
2251
  pi: piAdapter,
2252
+ t3code: t3codeAdapter,
1795
2253
  zed: zedAdapter
1796
2254
  };
1797
2255
  function createAdapter(name) {
@@ -1961,8 +2419,8 @@ function runPattern(_originalText, searchText, matches, seen) {
1961
2419
 
1962
2420
  // src/pricing/index.ts
1963
2421
  import { mkdir, readFile as readFile4, writeFile } from "node:fs/promises";
1964
- import { homedir as homedir9 } from "node:os";
1965
- import { dirname, join as join9 } from "node:path";
2422
+ import { homedir as homedir10 } from "node:os";
2423
+ import { dirname, join as join10 } from "node:path";
1966
2424
  var MODELS_DEV_URL = "https://models.dev/api.json";
1967
2425
  var CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1968
2426
  var FETCH_TIMEOUT_MS = 2e3;
@@ -2062,16 +2520,16 @@ async function summarizeUsage(records, pricing) {
2062
2520
  }
2063
2521
  function getPricingCachePath() {
2064
2522
  if (process.env["XDG_CACHE_HOME"]) {
2065
- return join9(process.env["XDG_CACHE_HOME"], "devrage", "models.dev.json");
2523
+ return join10(process.env["XDG_CACHE_HOME"], "devrage", "models.dev.json");
2066
2524
  }
2067
2525
  if (process.platform === "darwin") {
2068
- return join9(homedir9(), "Library", "Caches", "devrage", "models.dev.json");
2526
+ return join10(homedir10(), "Library", "Caches", "devrage", "models.dev.json");
2069
2527
  }
2070
2528
  if (process.platform === "win32") {
2071
- const localAppData = process.env["LOCALAPPDATA"] ?? join9(homedir9(), "AppData", "Local");
2072
- return join9(localAppData, "devrage", "models.dev.json");
2529
+ const localAppData = process.env["LOCALAPPDATA"] ?? join10(homedir10(), "AppData", "Local");
2530
+ return join10(localAppData, "devrage", "models.dev.json");
2073
2531
  }
2074
- return join9(homedir9(), ".cache", "devrage", "models.dev.json");
2532
+ return join10(homedir10(), ".cache", "devrage", "models.dev.json");
2075
2533
  }
2076
2534
  function createCostAccumulator() {
2077
2535
  return {
@@ -2180,7 +2638,7 @@ async function readPricingCache(cachePath) {
2180
2638
  try {
2181
2639
  const raw = await readFile4(cachePath, "utf-8");
2182
2640
  const parsed = JSON.parse(raw);
2183
- const cache = asRecord6(parsed);
2641
+ const cache = asRecord7(parsed);
2184
2642
  if (cache?.["source"] !== "models.dev" || cache["schemaVersion"] !== 1 || typeof cache["fetchedAt"] !== "string" || !isModelsDevCatalog(cache["catalog"])) {
2185
2643
  return null;
2186
2644
  }
@@ -2321,10 +2779,10 @@ function inferProvider(model) {
2321
2779
  return void 0;
2322
2780
  }
2323
2781
  function getCatalogRates(catalog, provider, model) {
2324
- const root = asRecord6(catalog);
2325
- const providerEntry = asRecord6(root?.[provider]);
2326
- const models = asRecord6(providerEntry?.["models"]);
2327
- const modelEntry = asRecord6(models?.[model]);
2782
+ const root = asRecord7(catalog);
2783
+ const providerEntry = asRecord7(root?.[provider]);
2784
+ const models = asRecord7(providerEntry?.["models"]);
2785
+ const modelEntry = asRecord7(models?.[model]);
2328
2786
  return toRateTable(modelEntry?.["cost"]);
2329
2787
  }
2330
2788
  function selectContextRates(rates, record) {
@@ -2332,8 +2790,8 @@ function selectContextRates(rates, record) {
2332
2790
  let selected = rates;
2333
2791
  let selectedSize = 0;
2334
2792
  for (const tier of rates.tiers ?? []) {
2335
- const tierRecord = asRecord6(tier);
2336
- const tierInfo = asRecord6(tierRecord?.["tier"]);
2793
+ const tierRecord = asRecord7(tier);
2794
+ const tierInfo = asRecord7(tierRecord?.["tier"]);
2337
2795
  const size = typeof tierInfo?.["size"] === "number" ? tierInfo["size"] : 0;
2338
2796
  if (tierInfo?.["type"] !== "context" || contextTokens < size || size < selectedSize) {
2339
2797
  continue;
@@ -2350,7 +2808,7 @@ function selectContextRates(rates, record) {
2350
2808
  return selected;
2351
2809
  }
2352
2810
  function toRateTable(value) {
2353
- const record = asRecord6(value);
2811
+ const record = asRecord7(value);
2354
2812
  if (!record) {
2355
2813
  return null;
2356
2814
  }
@@ -2384,10 +2842,10 @@ function numberValue6(value) {
2384
2842
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2385
2843
  }
2386
2844
  function isModelsDevCatalog(value) {
2387
- const catalog = asRecord6(value);
2388
- const openai = asRecord6(catalog?.["openai"]);
2389
- const anthropic = asRecord6(catalog?.["anthropic"]);
2390
- return Boolean(asRecord6(openai?.["models"]) || asRecord6(anthropic?.["models"]));
2845
+ const catalog = asRecord7(value);
2846
+ const openai = asRecord7(catalog?.["openai"]);
2847
+ const anthropic = asRecord7(catalog?.["anthropic"]);
2848
+ return Boolean(asRecord7(openai?.["models"]) || asRecord7(anthropic?.["models"]));
2391
2849
  }
2392
2850
  function isFresh(fetchedAt, ttlMs) {
2393
2851
  const fetchedTime = new Date(fetchedAt).getTime();
@@ -2396,7 +2854,7 @@ function isFresh(fetchedAt, ttlMs) {
2396
2854
  function mergePricingSource(left, right) {
2397
2855
  return left === right ? left : "mixed";
2398
2856
  }
2399
- function asRecord6(value) {
2857
+ function asRecord7(value) {
2400
2858
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2401
2859
  return null;
2402
2860
  }
@@ -2501,7 +2959,7 @@ function parseArgs(args) {
2501
2959
  console.log(`devrage scan \u2014 scan sessions for profanity
2502
2960
 
2503
2961
  Options:
2504
- --agent, -a <name> Scan only a specific agent (claude, codex, cursor, opencode, amp, cline, pi, zed)
2962
+ --agent, -a <name> Scan only a specific agent (claude, codex, cursor, opencode, amp, cline, pi, t3code, zed)
2505
2963
  --since, -s <date> Only scan messages after this date (ISO 8601)
2506
2964
  --day, --days [n] Only scan the last n days (default: 1)
2507
2965
  --week Only scan the last 7 days
@@ -2542,7 +3000,7 @@ Usage:
2542
3000
  devrage cost [options]
2543
3001
 
2544
3002
  Options:
2545
- --agent, -a <name> Show only a specific agent (claude, codex, cursor, opencode, amp, pi)
3003
+ --agent, -a <name> Show only a specific agent (claude, codex, cursor, opencode, amp, pi, t3code)
2546
3004
  --refresh-prices Refresh models.dev pricing before estimating cost
2547
3005
  --since, -s <date> Only include usage after this date (ISO 8601)
2548
3006
  --day, --days [n] Only include the last n days (default: 1)
@@ -2756,7 +3214,7 @@ function printCostCommandUnavailable(options) {
2756
3214
  }
2757
3215
  async function writeCostHtmlReport(totals, options) {
2758
3216
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
2759
- const reportPath = join10(
3217
+ const reportPath = join11(
2760
3218
  dirname2(getPricingCachePath()),
2761
3219
  `cost-report-${safeTimestamp(generatedAt)}.html`
2762
3220
  );
@@ -3209,7 +3667,7 @@ async function main() {
3209
3667
  process.exit(0);
3210
3668
  }
3211
3669
  if (command === "--version") {
3212
- console.log("0.5.4");
3670
+ console.log("0.5.6");
3213
3671
  process.exit(0);
3214
3672
  }
3215
3673
  const parsed = parseCommand(args);