summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-12-02 19:24:05 -0600
committerAiden Cline <[email protected]>2025-12-02 19:24:05 -0600
commita8ad74aef3f8b6cd84c76855cbbb30f4fb472b86 (patch)
treef0e8fb5e1ea663670bdfedcef5f0b567b02f1e18 /packages
parente2e2b7934e94606b376bee1c42911ace394535b6 (diff)
downloadopencode-a8ad74aef3f8b6cd84c76855cbbb30f4fb472b86.tar.gz
opencode-a8ad74aef3f8b6cd84c76855cbbb30f4fb472b86.zip
add basic session list command
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/cli/cmd/session.ts106
-rw-r--r--packages/opencode/src/index.ts2
2 files changed, 108 insertions, 0 deletions
diff --git a/packages/opencode/src/cli/cmd/session.ts b/packages/opencode/src/cli/cmd/session.ts
new file mode 100644
index 000000000..c8b5b0336
--- /dev/null
+++ b/packages/opencode/src/cli/cmd/session.ts
@@ -0,0 +1,106 @@
+import type { Argv } from "yargs"
+import { cmd } from "./cmd"
+import { Session } from "../../session"
+import { bootstrap } from "../bootstrap"
+import { UI } from "../ui"
+import { Locale } from "../../util/locale"
+import { EOL } from "os"
+
+export const SessionCommand = cmd({
+ command: "session",
+ describe: "manage sessions",
+ builder: (yargs: Argv) => yargs.command(SessionListCommand).demandCommand(),
+ async handler() {},
+})
+
+export const SessionListCommand = cmd({
+ command: "list",
+ describe: "list sessions",
+ builder: (yargs: Argv) => {
+ return yargs
+ .option("max-count", {
+ alias: "n",
+ describe: "limit to N most recent sessions",
+ type: "number",
+ })
+ .option("format", {
+ describe: "output format",
+ type: "string",
+ choices: ["table", "json"],
+ default: "table",
+ })
+ },
+ handler: async (args) => {
+ await bootstrap(process.cwd(), async () => {
+ const sessions = []
+ for await (const session of Session.list()) {
+ if (!session.parentID) {
+ sessions.push(session)
+ }
+ }
+
+ sessions.sort((a, b) => b.time.updated - a.time.updated)
+
+ const limitedSessions = args.maxCount ? sessions.slice(0, args.maxCount) : sessions
+
+ if (limitedSessions.length === 0) {
+ return
+ }
+
+ let output: string
+ if (args.format === "json") {
+ output = formatSessionJSON(limitedSessions)
+ } else {
+ output = formatSessionTable(limitedSessions)
+ }
+
+ const shouldPaginate = process.stdout.isTTY && !args.maxCount && args.format === "table"
+
+ if (shouldPaginate) {
+ const proc = Bun.spawn({
+ cmd: ["less", "-R", "-S"],
+ stdin: "pipe",
+ stdout: "inherit",
+ stderr: "inherit",
+ })
+
+ proc.stdin.write(output)
+ proc.stdin.end()
+ await proc.exited
+ } else {
+ console.log(output)
+ }
+ })
+ },
+})
+
+function formatSessionTable(sessions: Session.Info[]): string {
+ const lines: string[] = []
+
+ const maxIdWidth = Math.max(20, ...sessions.map((s) => s.id.length))
+ const maxTitleWidth = Math.max(25, ...sessions.map((s) => s.title.length))
+
+ const header = `Session ID${" ".repeat(maxIdWidth - 10)} Title${" ".repeat(maxTitleWidth - 5)} Updated`
+ lines.push(header)
+ lines.push("─".repeat(header.length))
+ for (const session of sessions) {
+ const truncatedTitle = Locale.truncate(session.title, maxTitleWidth)
+ const timeStr = Locale.todayTimeOrDateTime(session.time.updated)
+ const line = `${session.id.padEnd(maxIdWidth)} ${truncatedTitle.padEnd(maxTitleWidth)} ${timeStr}`
+ lines.push(line)
+ }
+
+ return lines.join(EOL)
+}
+
+function formatSessionJSON(sessions: Session.Info[]): string {
+ const jsonData = sessions.map((session) => ({
+ id: session.id,
+ title: session.title,
+ updated: session.time.updated,
+ created: session.time.created,
+ projectId: session.projectID,
+ directory: session.directory,
+ }))
+ return JSON.stringify(jsonData, null, 2)
+}
diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts
index 5fb6f966c..5ddf68e10 100644
--- a/packages/opencode/src/index.ts
+++ b/packages/opencode/src/index.ts
@@ -25,6 +25,7 @@ import { AcpCommand } from "./cli/cmd/acp"
import { EOL } from "os"
import { WebCommand } from "./cli/cmd/web"
import { PrCommand } from "./cli/cmd/pr"
+import { SessionCommand } from "./cli/cmd/session"
process.on("unhandledRejection", (e) => {
Log.Default.error("rejection", {
@@ -93,6 +94,7 @@ const cli = yargs(hideBin(process.argv))
.command(ImportCommand)
.command(GithubCommand)
.command(PrCommand)
+ .command(SessionCommand)
.fail((msg) => {
if (
msg.startsWith("Unknown argument") ||