summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-05-02 18:01:06 -0400
committerGitHub <[email protected]>2026-05-02 18:01:06 -0400
commitc1686c6ddc8c05a452b8f5771f717c7bb1401114 (patch)
tree4dc7acbc4f2d13b01f078042a280e3518cf0bf21 /packages
parent79b6ce5db47d9107711dbc3b8bf02ebabe5b47ae (diff)
downloadopencode-c1686c6ddc8c05a452b8f5771f717c7bb1401114.tar.gz
opencode-c1686c6ddc8c05a452b8f5771f717c7bb1401114.zip
refactor(cli): convert stats command to effectCmd (#25474)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/cli/cmd/stats.ts61
1 files changed, 33 insertions, 28 deletions
diff --git a/packages/opencode/src/cli/cmd/stats.ts b/packages/opencode/src/cli/cmd/stats.ts
index 6d00fdf9f..9a8843160 100644
--- a/packages/opencode/src/cli/cmd/stats.ts
+++ b/packages/opencode/src/cli/cmd/stats.ts
@@ -1,11 +1,11 @@
-import type { Argv } from "yargs"
-import { cmd } from "./cmd"
+import { Effect } from "effect"
+import { effectCmd } from "../effect-cmd"
import { Session } from "@/session/session"
-import { bootstrap } from "../bootstrap"
import { Database } from "@/storage/db"
import { SessionTable } from "../../session/session.sql"
import { Project } from "@/project/project"
-import { Instance } from "../../project/instance"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
import { AppRuntime } from "@/effect/app-runtime"
interface SessionStats {
@@ -47,11 +47,11 @@ interface SessionStats {
medianTokensPerSession: number
}
-export const StatsCommand = cmd({
+export const StatsCommand = effectCmd({
command: "stats",
describe: "show token usage and cost statistics",
- builder: (yargs: Argv) => {
- return yargs
+ builder: (yargs) =>
+ yargs
.option("days", {
describe: "show stats for the last N days (default: all time)",
type: "number",
@@ -66,34 +66,39 @@ export const StatsCommand = cmd({
.option("project", {
describe: "filter by project (default: all projects, empty string: current project)",
type: "string",
- })
- },
- handler: async (args) => {
- await bootstrap(process.cwd(), async () => {
- const stats = await aggregateSessionStats(args.days, args.project)
-
- let modelLimit: number | undefined
- if (args.models === true) {
- modelLimit = Infinity
- } else if (typeof args.models === "number") {
- modelLimit = args.models
- }
-
- displayStats(stats, args.tools, modelLimit)
- })
- },
+ }),
+ handler: Effect.fn("Cli.stats")(function* (args) {
+ const ctx = yield* InstanceRef
+ if (!ctx) return
+ const store = yield* InstanceStore.Service
+ return yield* run(args, ctx.project).pipe(Effect.ensuring(store.dispose(ctx)))
+ }),
})
-async function getCurrentProject(): Promise<Project.Info> {
- return Instance.project
-}
+const run = (args: { days?: number; tools?: number; models?: unknown; project?: string }, currentProject: Project.Info) =>
+ Effect.promise(async () => {
+ const stats = await aggregateSessionStats(args.days, args.project, currentProject)
+
+ let modelLimit: number | undefined
+ if (args.models === true) {
+ modelLimit = Infinity
+ } else if (typeof args.models === "number") {
+ modelLimit = args.models
+ }
+
+ displayStats(stats, args.tools, modelLimit)
+ })
async function getAllSessions(): Promise<Session.Info[]> {
const rows = Database.use((db) => db.select().from(SessionTable).all())
return rows.map((row) => Session.fromRow(row))
}
-export async function aggregateSessionStats(days?: number, projectFilter?: string): Promise<SessionStats> {
+export async function aggregateSessionStats(
+ days?: number,
+ projectFilter?: string,
+ currentProject?: Project.Info,
+): Promise<SessionStats> {
const sessions = await getAllSessions()
const MS_IN_DAY = 24 * 60 * 60 * 1000
@@ -117,7 +122,7 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
if (projectFilter !== undefined) {
if (projectFilter === "") {
- const currentProject = await getCurrentProject()
+ if (!currentProject) throw new Error("currentProject required when projectFilter is empty string")
filteredSessions = filteredSessions.filter((session) => session.projectID === currentProject.id)
} else {
filteredSessions = filteredSessions.filter((session) => session.projectID === projectFilter)