import type { AggregateRow, Page, SessionRow } from '../db/repositories/usage-repository.js'; import type { CostTotals } from '../models/usage-record.js'; import type { BreakdownReport, CacheHealthReport, ClientReport, DailyReport, ModelReport, PageInfo, ProjectReport, SessionDetail, SummaryReport, UnmatchedScope } from './aggregation-service.js'; import type { ContextReading } from './context-service.js'; import type { CostService } from './cost-service.js'; import type { BudgetReport } from './budget-service.js'; import type { UsageReport } from './report-service.js'; import type { PlanUsageReport, StatusLineReport } from './plan-limit-service.js'; import type { ImportResult, PruneResult, VacuumResult } from './lifecycle-service.js'; import type { TokenTotals } from '../models/usage-record.js'; import type { CounterfactualReport } from './counterfactual-service.js'; import type { PricingRefreshReport, StatusReport } from './usage-service.js'; import { type UpdateInfo } from './update-check.js'; import type { SyncReport } from './sync-service.js'; import type { VerifyReport } from './verify-service.js'; /** * Text rendering shared by the CLI and the MCP tools. * * Presentation only -- no aggregation happens here. Both frontends render through * these functions so `ai-usage stats --today` and the `usage_summary` tool cannot * describe the same numbers differently. */ export declare function int(n: number): string; /** Exact count plus a compact hint. Exactness first: rounded-only output is how * `opencode stats` ends up displaying 76.1M for two different real numbers. */ export declare function tokens(n: number): string; export declare function compact(n: number): string; export declare function usd(n: number): string; export declare function duration(seconds: number): string; /** * Renders cost as separate buckets, always labelled. Reported and estimated are * never added together -- that single blended number is the easiest way to lie * with this data. */ export declare function costLines(cost: CostTotals, costService: CostService, indent?: string): string[]; /** * The derived cache figures, printed beside the raw counts. * * Cache-read is the overwhelming majority of tokens on any real machine, and the * raw counts alone cannot say whether that is a cache paying for itself or a * write premium being burnt on sessions too short to reuse it. */ export declare function cacheLines(row: TokenTotals, costService: CostService, indent?: string): string[]; export declare function tokenLines(row: AggregateRow, indent?: string): string[]; /** * What this page is a page OF, and how to get the next one. * * A bare `--limit 5` says nothing about the other 275 rows, so "the top 5" was * indistinguishable from "all 5 there are". This line makes the difference * visible, and gives a caller the exact flag to walk the rest. */ export declare function pageLines(info: PageInfo, noun: string): string[]; /** * Scope values that match nothing anywhere in the database. * * Without this, a typo answers "No usage records for this period" and exits 0, * which is indistinguishable from a genuinely quiet period. */ export declare function unmatchedScopeLines(scope: UnmatchedScope | undefined): string[]; export declare function formatSummary(report: SummaryReport, costService: CostService): string; export declare function formatModels(report: ModelReport, costService: CostService): string; /** * A signed figure, so a delta reads as a direction rather than a quantity. * `+0` and `-0` both render as `0`: nothing changed is not a direction. */ export declare function signed(n: number, render?: (v: number) => string): string; /** * `undefined` renders as "n/a", never as 100% or Infinity: there is no * percentage change from zero. Going from $0 to $5 is a new thing happening, * not a rise of any particular size. */ export declare function percent(ratio: number | undefined): string; /** Compact one line per bucket. Reported and estimated costs stay separate, as everywhere else. */ export declare function formatDaily(report: DailyReport): string; /** * A tidy row set, one row per combination of the requested axes. * * Rendered as a table rather than the nested blocks the single-axis reports use, * because the whole point is to scan one dimension against another -- which * nested blocks make impossible. * * Cost columns show `--`, never `$0.00`, where a row has no records on that * basis. A rendered $0 would say "this cost nothing", which is a different claim * from "nothing here is priced this way". */ export declare function formatBreakdown(report: BreakdownReport): string; export declare function formatProjects(report: ProjectReport, costService: CostService): string; export declare function formatClients(report: ClientReport, costService: CostService): string; export declare function formatSessions(page: Page, costService: CostService): string; export declare function formatSessionDetail(detail: SessionDetail, costService: CostService): string; /** * Spend against a target. * * The two projections are rendered side by side, never merged. Extrapolating * month-end spend by hand meant picking a denominator -- calendar days or active * days -- and on a machine used on weekdays only those differ by more than 2x. * Showing one would be making that modelling choice silently on the user's * behalf; showing both makes the size of the assumption the visible thing. */ export declare function formatBudget(report: BudgetReport): string; export declare function formatPrune(result: PruneResult): string; export declare function formatVacuum(result: VacuumResult): string; export declare function formatImport(result: ImportResult): string; export declare function formatStatus(status: StatusReport, update?: UpdateInfo | null): string; /** * What a price refresh did, or nothing when it did nothing worth saying: a * fresh cache or an opt-out is the ordinary state, not news. */ export declare function formatPricingRefresh(refresh: PricingRefreshReport): string[]; export declare function formatSyncReport(report: SyncReport, refresh?: PricingRefreshReport): string; export declare function formatVerify(report: VerifyReport): string; /** * "These tokens on another model." * * Deliberately shaped so the comparison cannot be misread as a saving: the * actual figure keeps its own basis at the top, every scenario is labelled an * estimate, the model that actually ran is marked in the list rather than * subtracted from it, and the caveats print with the numbers instead of being * left to the README. */ export declare function formatCounterfactual(report: CounterfactualReport, costService: CostService): string; export declare function formatCacheHealth(report: CacheHealthReport, costService: CostService): string; /** * The report a new user runs first: a verdict, where the usage went, and what * to change. Shares lead and dollars follow, because on a subscription the * dollar figure is not money spent -- it is only the weight the shares use. */ export declare function formatUsageReport(report: UsageReport): string; export declare function formatPlanUsage(report: PlanUsageReport, costService: CostService): string; /** * One line for Claude Code's status bar. * * Kept short on purpose -- it shares a row with whatever the terminal is wide * enough for -- but never at the price of a blend: an estimated figure says so, * and a reported one is a separate segment. Colour marks only the things worth * looking up for: a limit past 70% (yellow) or 90% (red), a pace that reaches * 100% before the reset, and a cache break in this session. */ export declare function formatStatusLine(report: StatusLineReport, options?: { color?: boolean; now?: Date; }): string; /** * What the UserPromptSubmit hook prints, or undefined when it should stay quiet * (below the compact limit; the hook warns only when compacting is advised, and * the yellow band is for the status line). * * `additionalContext` is read by Claude, so it says what the size costs and what * to do about it; `systemMessage` is the one line the user sees. */ export declare function formatContextHook(reading: ContextReading | undefined): string | undefined;