Skip to content

Commit a8ad74a

Browse files
committed
add basic session list command
1 parent e2e2b79 commit a8ad74a

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import type { Argv } from "yargs"
2+
import { cmd } from "./cmd"
3+
import { Session } from "../../session"
4+
import { bootstrap } from "../bootstrap"
5+
import { UI } from "../ui"
6+
import { Locale } from "../../util/locale"
7+
import { EOL } from "os"
8+
9+
export const SessionCommand = cmd({
10+
command: "session",
11+
describe: "manage sessions",
12+
builder: (yargs: Argv) => yargs.command(SessionListCommand).demandCommand(),
13+
async handler() {},
14+
})
15+
16+
export const SessionListCommand = cmd({
17+
command: "list",
18+
describe: "list sessions",
19+
builder: (yargs: Argv) => {
20+
return yargs
21+
.option("max-count", {
22+
alias: "n",
23+
describe: "limit to N most recent sessions",
24+
type: "number",
25+
})
26+
.option("format", {
27+
describe: "output format",
28+
type: "string",
29+
choices: ["table", "json"],
30+
default: "table",
31+
})
32+
},
33+
handler: async (args) => {
34+
await bootstrap(process.cwd(), async () => {
35+
const sessions = []
36+
for await (const session of Session.list()) {
37+
if (!session.parentID) {
38+
sessions.push(session)
39+
}
40+
}
41+
42+
sessions.sort((a, b) => b.time.updated - a.time.updated)
43+
44+
const limitedSessions = args.maxCount ? sessions.slice(0, args.maxCount) : sessions
45+
46+
if (limitedSessions.length === 0) {
47+
return
48+
}
49+
50+
let output: string
51+
if (args.format === "json") {
52+
output = formatSessionJSON(limitedSessions)
53+
} else {
54+
output = formatSessionTable(limitedSessions)
55+
}
56+
57+
const shouldPaginate = process.stdout.isTTY && !args.maxCount && args.format === "table"
58+
59+
if (shouldPaginate) {
60+
const proc = Bun.spawn({
61+
cmd: ["less", "-R", "-S"],
62+
stdin: "pipe",
63+
stdout: "inherit",
64+
stderr: "inherit",
65+
})
66+
67+
proc.stdin.write(output)
68+
proc.stdin.end()
69+
await proc.exited
70+
} else {
71+
console.log(output)
72+
}
73+
})
74+
},
75+
})
76+
77+
function formatSessionTable(sessions: Session.Info[]): string {
78+
const lines: string[] = []
79+
80+
const maxIdWidth = Math.max(20, ...sessions.map((s) => s.id.length))
81+
const maxTitleWidth = Math.max(25, ...sessions.map((s) => s.title.length))
82+
83+
const header = `Session ID${" ".repeat(maxIdWidth - 10)} Title${" ".repeat(maxTitleWidth - 5)} Updated`
84+
lines.push(header)
85+
lines.push("─".repeat(header.length))
86+
for (const session of sessions) {
87+
const truncatedTitle = Locale.truncate(session.title, maxTitleWidth)
88+
const timeStr = Locale.todayTimeOrDateTime(session.time.updated)
89+
const line = `${session.id.padEnd(maxIdWidth)} ${truncatedTitle.padEnd(maxTitleWidth)} ${timeStr}`
90+
lines.push(line)
91+
}
92+
93+
return lines.join(EOL)
94+
}
95+
96+
function formatSessionJSON(sessions: Session.Info[]): string {
97+
const jsonData = sessions.map((session) => ({
98+
id: session.id,
99+
title: session.title,
100+
updated: session.time.updated,
101+
created: session.time.created,
102+
projectId: session.projectID,
103+
directory: session.directory,
104+
}))
105+
return JSON.stringify(jsonData, null, 2)
106+
}

‎packages/opencode/src/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { AcpCommand } from "./cli/cmd/acp"
2525
import { EOL } from "os"
2626
import { WebCommand } from "./cli/cmd/web"
2727
import { PrCommand } from "./cli/cmd/pr"
28+
import { SessionCommand } from "./cli/cmd/session"
2829

2930
process.on("unhandledRejection", (e) => {
3031
Log.Default.error("rejection", {
@@ -93,6 +94,7 @@ const cli = yargs(hideBin(process.argv))
9394
.command(ImportCommand)
9495
.command(GithubCommand)
9596
.command(PrCommand)
97+
.command(SessionCommand)
9698
.fail((msg) => {
9799
if (
98100
msg.startsWith("Unknown argument") ||

0 commit comments

Comments
 (0)