mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
Session command with list subcommand added
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
This commit is contained in:
parent
8ab206b443
commit
d5c15f70d0
3 changed files with 225 additions and 0 deletions
206
packages/opencode/src/cli/cmd/session.ts
Normal file
206
packages/opencode/src/cli/cmd/session.ts
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/**
|
||||
* Session management commands
|
||||
*
|
||||
* Provides commands to list and manage opencode sessions.
|
||||
* The main use case is to help users find session IDs for use with --session flag.
|
||||
*
|
||||
* Examples:
|
||||
* opencode session list # List all sessions in default format
|
||||
* opencode session list --format ids # List only session IDs (CI-friendly)
|
||||
* opencode session list --format json # List sessions as JSON
|
||||
* opencode session list --limit 5 # Show only 5 most recent sessions
|
||||
*/
|
||||
|
||||
import type { Argv } from "yargs"
|
||||
import { Session } from "../../session"
|
||||
import { cmd } from "./cmd"
|
||||
import { bootstrap } from "../bootstrap"
|
||||
import { UI } from "../ui"
|
||||
import { EOL } from "os"
|
||||
|
||||
export const SessionListCommand = cmd({
|
||||
command: "list",
|
||||
describe: "list all sessions with their IDs",
|
||||
builder: (yargs: Argv) => {
|
||||
return yargs
|
||||
.option("format", {
|
||||
alias: "f",
|
||||
describe: "output format",
|
||||
type: "string",
|
||||
choices: ["default", "json", "ids"],
|
||||
default: "default",
|
||||
})
|
||||
.option("limit", {
|
||||
alias: "l",
|
||||
describe: "limit number of sessions shown",
|
||||
type: "number",
|
||||
})
|
||||
},
|
||||
handler: async (args) => {
|
||||
await bootstrap(process.cwd(), async () => {
|
||||
// Collect all sessions from storage
|
||||
const sessions = []
|
||||
for await (const session of Session.list()) {
|
||||
sessions.push(session)
|
||||
}
|
||||
|
||||
// Handle empty case
|
||||
if (sessions.length === 0) {
|
||||
UI.println("No sessions found")
|
||||
return
|
||||
}
|
||||
|
||||
// Sort by most recently updated
|
||||
sessions.sort((a, b) => b.time.updated - a.time.updated)
|
||||
|
||||
// Apply limit if specified
|
||||
if (args.limit) {
|
||||
sessions.splice(args.limit)
|
||||
}
|
||||
|
||||
// JSON format - full session data
|
||||
if (args.format === "json") {
|
||||
process.stdout.write(JSON.stringify(sessions, null, 2) + EOL)
|
||||
return
|
||||
}
|
||||
|
||||
// IDs format - just the session IDs (CI-friendly)
|
||||
if (args.format === "ids") {
|
||||
sessions.forEach(session => {
|
||||
process.stdout.write(session.id + EOL)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Default format - git log style table
|
||||
const terminalWidth = process.stdout.columns || 80
|
||||
const idWidth = Math.min(20, Math.max(8, Math.max(...sessions.map(s => s.id.length))))
|
||||
const timeWidth = 19
|
||||
const titleWidth = terminalWidth - idWidth - timeWidth - 4
|
||||
|
||||
// Header
|
||||
UI.println(
|
||||
UI.Style.TEXT_DIM + "Session ID".padEnd(idWidth) + " " +
|
||||
"Last Updated".padEnd(timeWidth) + " " +
|
||||
"Title"
|
||||
)
|
||||
|
||||
// Session rows
|
||||
sessions.forEach(session => {
|
||||
const shortId = session.id.slice(-idWidth).padStart(idWidth)
|
||||
const time = new Date(session.time.updated).toLocaleString().padEnd(timeWidth)
|
||||
const title = session.title.length > titleWidth
|
||||
? session.title.slice(0, titleWidth - 3) + "..."
|
||||
: session.title.padEnd(titleWidth)
|
||||
|
||||
UI.println(
|
||||
UI.Style.TEXT_INFO + shortId + " " +
|
||||
UI.Style.TEXT_NORMAL + time + " " +
|
||||
UI.Style.TEXT_HIGHLIGHT + title
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const SessionCommand = cmd({
|
||||
command: "session",
|
||||
describe: "manage sessions",
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.command(SessionListCommand)
|
||||
.demandCommand(),
|
||||
async handler() {},
|
||||
})
|
||||
.command({
|
||||
command: "list",
|
||||
describe: "list all sessions with their IDs",
|
||||
builder: (yargs: Argv) => {
|
||||
return yargs
|
||||
.option("format", {
|
||||
alias: "f",
|
||||
describe: "output format",
|
||||
type: "string",
|
||||
choices: ["default", "json", "ids"],
|
||||
default: "default",
|
||||
})
|
||||
.option("limit", {
|
||||
alias: "l",
|
||||
describe: "limit number of sessions shown",
|
||||
type: "number",
|
||||
})
|
||||
},
|
||||
handler: async (args) => {
|
||||
await bootstrap(process.cwd(), async () => {
|
||||
const sessions = []
|
||||
for await (const session of Session.list()) {
|
||||
sessions.push(session)
|
||||
}
|
||||
|
||||
if (sessions.length === 0) {
|
||||
UI.println("No sessions found")
|
||||
return
|
||||
}
|
||||
|
||||
sessions.sort((a, b) => b.time.updated - a.time.updated)
|
||||
|
||||
if (args.limit) {
|
||||
sessions.splice(args.limit)
|
||||
}
|
||||
|
||||
if (args.format === "json") {
|
||||
process.stdout.write(JSON.stringify(sessions, null, 2) + EOL)
|
||||
return
|
||||
}
|
||||
|
||||
if (args.format === "ids") {
|
||||
sessions.forEach((session) => {
|
||||
process.stdout.write(session.id + EOL)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Default format - similar to git log
|
||||
const terminalWidth = process.stdout.columns || 80
|
||||
const idWidth = Math.min(20, Math.max(8, Math.max(...sessions.map((s) => s.id.length))))
|
||||
const timeWidth = 19
|
||||
const titleWidth = terminalWidth - idWidth - timeWidth - 4
|
||||
|
||||
UI.println(
|
||||
UI.Style.TEXT_DIM +
|
||||
"Session ID".padEnd(idWidth) +
|
||||
" " +
|
||||
"Last Updated".padEnd(timeWidth) +
|
||||
" " +
|
||||
"Title",
|
||||
)
|
||||
|
||||
sessions.forEach((session) => {
|
||||
const shortId = session.id.slice(-idWidth).padStart(idWidth)
|
||||
const time = new Date(session.time.updated).toLocaleString().padEnd(timeWidth)
|
||||
const title =
|
||||
session.title.length > titleWidth
|
||||
? session.title.slice(0, titleWidth - 3) + "..."
|
||||
: session.title.padEnd(titleWidth)
|
||||
|
||||
UI.println(
|
||||
UI.Style.TEXT_INFO +
|
||||
shortId +
|
||||
" " +
|
||||
UI.Style.TEXT_NORMAL +
|
||||
time +
|
||||
" " +
|
||||
UI.Style.TEXT_HIGHLIGHT +
|
||||
title,
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
.demandCommand(1, "You need to specify a subcommand")
|
||||
.strict()
|
||||
},
|
||||
handler: async () => {
|
||||
// This handler won't be called due to demandCommand
|
||||
},
|
||||
})
|
||||
|
|
@ -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", {
|
||||
|
|
@ -92,6 +93,7 @@ const cli = yargs(hideBin(process.argv))
|
|||
.command(ImportCommand)
|
||||
.command(GithubCommand)
|
||||
.command(PrCommand)
|
||||
.command(SessionCommand)
|
||||
.fail((msg) => {
|
||||
if (
|
||||
msg.startsWith("Unknown argument") ||
|
||||
|
|
|
|||
17
packages/opencode/test/cli/session.test.ts
Normal file
17
packages/opencode/test/cli/session.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { describe, test, expect } from "bun:test"
|
||||
import { SessionListCommand } from "../../src/cli/cmd/session"
|
||||
|
||||
describe("SessionCommand", () => {
|
||||
test("should have correct command structure", () => {
|
||||
expect(SessionListCommand.command).toBe("list")
|
||||
expect(SessionListCommand.describe).toBe("list all sessions with their IDs")
|
||||
})
|
||||
|
||||
test("should have builder function", () => {
|
||||
expect(typeof SessionListCommand.builder).toBe("function")
|
||||
})
|
||||
|
||||
test("should have handler function", () => {
|
||||
expect(typeof SessionListCommand.handler).toBe("function")
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue