@colinmollenhour/occtl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +290 -0
  2. package/SKILL.md +692 -0
  3. package/dist/client.d.ts +4 -0
  4. package/dist/client.js +64 -0
  5. package/dist/commands/session-abort.d.ts +2 -0
  6. package/dist/commands/session-abort.js +16 -0
  7. package/dist/commands/session-children.d.ts +2 -0
  8. package/dist/commands/session-children.js +30 -0
  9. package/dist/commands/session-create.d.ts +2 -0
  10. package/dist/commands/session-create.js +39 -0
  11. package/dist/commands/session-diff.d.ts +2 -0
  12. package/dist/commands/session-diff.js +35 -0
  13. package/dist/commands/session-get.d.ts +2 -0
  14. package/dist/commands/session-get.js +24 -0
  15. package/dist/commands/session-last.d.ts +2 -0
  16. package/dist/commands/session-last.js +39 -0
  17. package/dist/commands/session-list.d.ts +2 -0
  18. package/dist/commands/session-list.js +91 -0
  19. package/dist/commands/session-messages.d.ts +2 -0
  20. package/dist/commands/session-messages.js +44 -0
  21. package/dist/commands/session-respond.d.ts +2 -0
  22. package/dist/commands/session-respond.js +78 -0
  23. package/dist/commands/session-send.d.ts +2 -0
  24. package/dist/commands/session-send.js +114 -0
  25. package/dist/commands/session-share.d.ts +3 -0
  26. package/dist/commands/session-share.js +53 -0
  27. package/dist/commands/session-status.d.ts +2 -0
  28. package/dist/commands/session-status.js +45 -0
  29. package/dist/commands/session-summary.d.ts +2 -0
  30. package/dist/commands/session-summary.js +87 -0
  31. package/dist/commands/session-todo.d.ts +2 -0
  32. package/dist/commands/session-todo.js +41 -0
  33. package/dist/commands/session-wait-for-text.d.ts +2 -0
  34. package/dist/commands/session-wait-for-text.js +119 -0
  35. package/dist/commands/session-wait.d.ts +4 -0
  36. package/dist/commands/session-wait.js +85 -0
  37. package/dist/commands/session-watch.d.ts +2 -0
  38. package/dist/commands/session-watch.js +101 -0
  39. package/dist/commands/skill.d.ts +3 -0
  40. package/dist/commands/skill.js +55 -0
  41. package/dist/commands/worktree.d.ts +5 -0
  42. package/dist/commands/worktree.js +359 -0
  43. package/dist/format.d.ts +19 -0
  44. package/dist/format.js +115 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +63 -0
  47. package/dist/resolve.d.ts +6 -0
  48. package/dist/resolve.js +47 -0
  49. package/dist/sse.d.ts +40 -0
  50. package/dist/sse.js +128 -0
  51. package/dist/wait-util.d.ts +23 -0
  52. package/dist/wait-util.js +118 -0
  53. package/package.json +49 -0
package/dist/client.js ADDED
@@ -0,0 +1,64 @@
1
+ import { createOpencodeClient } from "@opencode-ai/sdk";
2
+ import { execSync } from "child_process";
3
+ let _client = null;
4
+ let _baseUrl = null;
5
+ /**
6
+ * Auto-detect the OpenCode server by looking at running processes.
7
+ * Falls back to env vars or defaults.
8
+ */
9
+ function detectServer() {
10
+ // Check env vars first
11
+ if (process.env.OPENCODE_SERVER_HOST || process.env.OPENCODE_SERVER_PORT) {
12
+ return {
13
+ host: process.env.OPENCODE_SERVER_HOST || "127.0.0.1",
14
+ port: process.env.OPENCODE_SERVER_PORT || "4096",
15
+ };
16
+ }
17
+ // Try to detect from running opencode process
18
+ try {
19
+ const output = execSync("ps aux | grep 'opencode serve' | grep -v grep", { encoding: "utf-8", timeout: 2000 });
20
+ const lines = output.trim().split("\n");
21
+ for (const line of lines) {
22
+ const portMatch = line.match(/--port\s+(\d+)/);
23
+ const hostMatch = line.match(/--hostname\s+([\w.:]+)/);
24
+ if (portMatch) {
25
+ return {
26
+ host: hostMatch?.[1] || "127.0.0.1",
27
+ port: portMatch[1],
28
+ };
29
+ }
30
+ }
31
+ }
32
+ catch {
33
+ // Process detection failed, fall through
34
+ }
35
+ return { host: "127.0.0.1", port: "4096" };
36
+ }
37
+ export function getBaseUrl() {
38
+ if (!_baseUrl) {
39
+ const { host, port } = detectServer();
40
+ _baseUrl = `http://${host}:${port}`;
41
+ }
42
+ return _baseUrl;
43
+ }
44
+ export function getClient() {
45
+ if (!_client) {
46
+ _client = createOpencodeClient({
47
+ baseUrl: getBaseUrl(),
48
+ });
49
+ }
50
+ return _client;
51
+ }
52
+ export async function ensureServer() {
53
+ const client = getClient();
54
+ try {
55
+ // Try listing sessions as a health check
56
+ await client.session.list();
57
+ }
58
+ catch {
59
+ console.error("Error: Cannot connect to OpenCode server at " + getBaseUrl());
60
+ console.error("Make sure OpenCode is running, or set OPENCODE_SERVER_HOST/OPENCODE_SERVER_PORT");
61
+ process.exit(1);
62
+ }
63
+ return client;
64
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionAbortCommand(): Command;
@@ -0,0 +1,16 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { resolveSession } from "../resolve.js";
4
+ export function sessionAbortCommand() {
5
+ return new Command("abort")
6
+ .description("Abort a running session")
7
+ .argument("[session-id]", "Session ID (defaults to most recent)")
8
+ .action(async (sessionId) => {
9
+ const client = await ensureServer();
10
+ const resolved = await resolveSession(client, sessionId);
11
+ await client.session.abort({
12
+ path: { id: resolved },
13
+ });
14
+ console.log(`Aborted session: ${resolved}`);
15
+ });
16
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionChildrenCommand(): Command;
@@ -0,0 +1,30 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { resolveSession } from "../resolve.js";
4
+ import { formatSession, formatJSON } from "../format.js";
5
+ export function sessionChildrenCommand() {
6
+ return new Command("children")
7
+ .description("List child sessions (sub-agents) of a session")
8
+ .argument("[session-id]", "Session ID (defaults to most recent)")
9
+ .option("-j, --json", "Output as JSON")
10
+ .action(async (sessionId, opts) => {
11
+ const client = await ensureServer();
12
+ const resolved = await resolveSession(client, sessionId);
13
+ const result = await client.session.children({
14
+ path: { id: resolved },
15
+ });
16
+ const children = result.data ?? [];
17
+ if (opts.json) {
18
+ console.log(formatJSON(children));
19
+ return;
20
+ }
21
+ if (children.length === 0) {
22
+ console.log("No child sessions.");
23
+ return;
24
+ }
25
+ console.log("ID\tTITLE\tUPDATED");
26
+ for (const s of children) {
27
+ console.log(formatSession(s));
28
+ }
29
+ });
30
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionCreateCommand(): Command;
@@ -0,0 +1,39 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { formatSessionDetailed, formatJSON } from "../format.js";
4
+ export function sessionCreateCommand() {
5
+ return new Command("create")
6
+ .alias("new")
7
+ .description("Create a new session")
8
+ .option("-t, --title <title>", "Session title")
9
+ .option("-p, --parent <id>", "Parent session ID (creates a child/sub-agent session)")
10
+ .option("-d, --dir <path>", "Project directory for the session (defaults to cwd)")
11
+ .option("-j, --json", "Output as JSON")
12
+ .option("-q, --quiet", "Only output the session ID")
13
+ .action(async (opts) => {
14
+ const client = await ensureServer();
15
+ const directory = opts.dir
16
+ ? (await import("path")).resolve(opts.dir)
17
+ : process.cwd();
18
+ const result = await client.session.create({
19
+ body: {
20
+ ...(opts.title && { title: opts.title }),
21
+ ...(opts.parent && { parentID: opts.parent }),
22
+ },
23
+ query: { directory },
24
+ });
25
+ if (!result.data) {
26
+ console.error("Failed to create session.");
27
+ process.exit(1);
28
+ }
29
+ if (opts.quiet) {
30
+ console.log(result.data.id);
31
+ return;
32
+ }
33
+ if (opts.json) {
34
+ console.log(formatJSON(result.data));
35
+ return;
36
+ }
37
+ console.log(formatSessionDetailed(result.data));
38
+ });
39
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionDiffCommand(): Command;
@@ -0,0 +1,35 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { resolveSession } from "../resolve.js";
4
+ import { formatJSON } from "../format.js";
5
+ export function sessionDiffCommand() {
6
+ return new Command("diff")
7
+ .description("Show the diff (file changes) for a session")
8
+ .argument("[session-id]", "Session ID (defaults to most recent)")
9
+ .option("-j, --json", "Output as JSON")
10
+ .action(async (sessionId, opts) => {
11
+ const client = await ensureServer();
12
+ const resolved = await resolveSession(client, sessionId);
13
+ const result = await client.session.diff({
14
+ path: { id: resolved },
15
+ });
16
+ const diffs = result.data ?? [];
17
+ if (opts.json) {
18
+ console.log(formatJSON(diffs));
19
+ return;
20
+ }
21
+ if (diffs.length === 0) {
22
+ console.log("No file changes in this session.");
23
+ return;
24
+ }
25
+ for (const d of diffs) {
26
+ console.log(`--- ${d.file}`);
27
+ console.log(`+${d.additions} -${d.deletions}`);
28
+ if (d.before !== d.after) {
29
+ console.log(`Before: ${d.before.slice(0, 100)}`);
30
+ console.log(`After: ${d.after.slice(0, 100)}`);
31
+ }
32
+ console.log("");
33
+ }
34
+ });
35
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionGetCommand(): Command;
@@ -0,0 +1,24 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { formatSessionDetailed, formatJSON } from "../format.js";
4
+ export function sessionGetCommand() {
5
+ return new Command("get")
6
+ .description("Get session details")
7
+ .argument("<session-id>", "Session ID")
8
+ .option("-j, --json", "Output as JSON")
9
+ .action(async (sessionId, opts) => {
10
+ const client = await ensureServer();
11
+ const result = await client.session.get({
12
+ path: { id: sessionId },
13
+ });
14
+ if (!result.data) {
15
+ console.error(`Session not found: ${sessionId}`);
16
+ process.exit(1);
17
+ }
18
+ if (opts.json) {
19
+ console.log(formatJSON(result.data));
20
+ return;
21
+ }
22
+ console.log(formatSessionDetailed(result.data));
23
+ });
24
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionLastCommand(): Command;
@@ -0,0 +1,39 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { formatMessage, formatJSON } from "../format.js";
4
+ import { resolveSession } from "../resolve.js";
5
+ export function sessionLastCommand() {
6
+ return new Command("last")
7
+ .description("Get the last message from a session")
8
+ .argument("[session-id]", "Session ID (defaults to most recent)")
9
+ .option("-j, --json", "Output as JSON")
10
+ .option("-v, --verbose", "Show tool calls and extra details")
11
+ .option("-t, --text-only", "Show only text content (default)")
12
+ .option("--role <role>", "Get last message of a specific role (user or assistant)")
13
+ .action(async (sessionId, opts) => {
14
+ const client = await ensureServer();
15
+ const resolved = await resolveSession(client, sessionId);
16
+ const result = await client.session.messages({
17
+ path: { id: resolved },
18
+ });
19
+ let messages = result.data ?? [];
20
+ if (opts.role) {
21
+ messages = messages.filter((m) => m.info.role === opts.role);
22
+ }
23
+ if (messages.length === 0) {
24
+ console.error("No messages in session.");
25
+ process.exit(1);
26
+ }
27
+ const last = messages[messages.length - 1];
28
+ if (opts.json) {
29
+ console.log(formatJSON(last));
30
+ return;
31
+ }
32
+ // Default to text-only for the 'last' command unless verbose
33
+ const textOnly = opts.verbose ? false : (opts.textOnly !== false);
34
+ console.log(formatMessage(last.info, last.parts, {
35
+ verbose: opts.verbose,
36
+ textOnly,
37
+ }));
38
+ });
39
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionListCommand(): Command;
@@ -0,0 +1,91 @@
1
+ import { Command } from "commander";
2
+ import path from "path";
3
+ import { ensureServer } from "../client.js";
4
+ import { formatSession, formatSessionDetailed, formatJSON } from "../format.js";
5
+ export function sessionListCommand() {
6
+ return new Command("list")
7
+ .alias("ls")
8
+ .description("List sessions. Default: current directory only. Pass a path to filter by directory, or --all for everything.")
9
+ .argument("[directory]", "Only show sessions for this directory")
10
+ .option("-j, --json", "Output as JSON")
11
+ .option("-d, --detailed", "Show detailed info for each session")
12
+ .option("-n, --limit <n>", "Limit number of results", parseInt)
13
+ .option("-a, --all", "Show sessions for all directories")
14
+ .option("-c, --children", "Include child sessions (sub-agents)")
15
+ .option("--sort <field>", "Sort by: updated (default), created, title", "updated")
16
+ .option("--asc", "Sort ascending instead of descending")
17
+ .action(async (directory, opts) => {
18
+ const client = await ensureServer();
19
+ // Determine which directory to filter by
20
+ let filterDir;
21
+ if (opts.all) {
22
+ filterDir = undefined;
23
+ }
24
+ else if (directory) {
25
+ filterDir = path.resolve(directory);
26
+ }
27
+ else {
28
+ filterDir = process.cwd();
29
+ }
30
+ const result = await client.session.list({
31
+ ...(filterDir && { query: { directory: filterDir } }),
32
+ });
33
+ let sessions = result.data ?? [];
34
+ // Client-side directory filtering as fallback
35
+ if (filterDir) {
36
+ sessions = sessions.filter((s) => s.directory === filterDir);
37
+ }
38
+ // Filter out child sessions unless --children
39
+ if (!opts.children) {
40
+ sessions = sessions.filter((s) => !s.parentID);
41
+ }
42
+ // Sort
43
+ const sortField = opts.sort;
44
+ const ascending = !!opts.asc;
45
+ sessions.sort((a, b) => {
46
+ let cmp = 0;
47
+ switch (sortField) {
48
+ case "created":
49
+ cmp = b.time.created - a.time.created;
50
+ break;
51
+ case "title":
52
+ cmp = (a.title || "").localeCompare(b.title || "");
53
+ break;
54
+ case "updated":
55
+ default:
56
+ cmp = b.time.updated - a.time.updated;
57
+ break;
58
+ }
59
+ return ascending ? -cmp : cmp;
60
+ });
61
+ // Apply limit
62
+ if (opts.limit && opts.limit > 0) {
63
+ sessions = sessions.slice(0, opts.limit);
64
+ }
65
+ if (opts.json) {
66
+ console.log(formatJSON(sessions));
67
+ return;
68
+ }
69
+ if (sessions.length === 0) {
70
+ if (filterDir) {
71
+ console.log(`No sessions found for ${filterDir}.`);
72
+ }
73
+ else {
74
+ console.log("No sessions found.");
75
+ }
76
+ return;
77
+ }
78
+ if (opts.detailed) {
79
+ for (const s of sessions) {
80
+ console.log(formatSessionDetailed(s));
81
+ console.log("");
82
+ }
83
+ }
84
+ else {
85
+ console.log("ID\tTITLE\tUPDATED");
86
+ for (const s of sessions) {
87
+ console.log(formatSession(s));
88
+ }
89
+ }
90
+ });
91
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionMessagesCommand(): Command;
@@ -0,0 +1,44 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { formatMessage, formatJSON } from "../format.js";
4
+ import { resolveSession } from "../resolve.js";
5
+ export function sessionMessagesCommand() {
6
+ return new Command("messages")
7
+ .alias("msgs")
8
+ .description("List messages in a session")
9
+ .argument("[session-id]", "Session ID (defaults to most recent)")
10
+ .option("-j, --json", "Output as JSON")
11
+ .option("-v, --verbose", "Show tool calls and extra details")
12
+ .option("-t, --text-only", "Show only text content")
13
+ .option("-n, --limit <n>", "Limit number of messages", parseInt)
14
+ .option("--role <role>", "Filter by role (user or assistant)")
15
+ .action(async (sessionId, opts) => {
16
+ const client = await ensureServer();
17
+ const resolved = await resolveSession(client, sessionId);
18
+ const result = await client.session.messages({
19
+ path: { id: resolved },
20
+ });
21
+ let messages = result.data ?? [];
22
+ if (opts.role) {
23
+ messages = messages.filter((m) => m.info.role === opts.role);
24
+ }
25
+ if (opts.limit && opts.limit > 0) {
26
+ messages = messages.slice(-opts.limit);
27
+ }
28
+ if (opts.json) {
29
+ console.log(formatJSON(messages));
30
+ return;
31
+ }
32
+ if (messages.length === 0) {
33
+ console.log("No messages in session.");
34
+ return;
35
+ }
36
+ for (const m of messages) {
37
+ console.log(formatMessage(m.info, m.parts, {
38
+ verbose: opts.verbose,
39
+ textOnly: opts.textOnly,
40
+ }));
41
+ console.log("");
42
+ }
43
+ });
44
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionRespondCommand(): Command;
@@ -0,0 +1,78 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer, getClient } from "../client.js";
3
+ import { resolveSession } from "../resolve.js";
4
+ import { formatJSON } from "../format.js";
5
+ import { streamEvents } from "../sse.js";
6
+ export function sessionRespondCommand() {
7
+ return new Command("respond")
8
+ .description("Respond to a permission request in a session")
9
+ .argument("[session-id]", "Session ID (defaults to most recent)")
10
+ .option("-r, --response <response>", "Response: once, always, or reject", "once")
11
+ .option("-j, --json", "Output as JSON")
12
+ .option("-p, --permission-id <id>", "Permission ID to respond to (auto-detects if omitted)")
13
+ .option("-w, --wait", "Wait for a permission request if none pending")
14
+ .option("--auto-approve", "Automatically approve all permission requests (implies --wait)")
15
+ .action(async (sessionId, opts) => {
16
+ const client = await ensureServer();
17
+ const resolved = await resolveSession(client, sessionId);
18
+ const validResponses = ["once", "always", "reject"];
19
+ if (!validResponses.includes(opts.response)) {
20
+ console.error(`Invalid response: ${opts.response}. Must be one of: ${validResponses.join(", ")}`);
21
+ process.exit(1);
22
+ }
23
+ if (opts.permissionId) {
24
+ await respondToPermission(resolved, opts.permissionId, opts.response);
25
+ if (opts.json) {
26
+ console.log(formatJSON({ success: true, permissionId: opts.permissionId }));
27
+ }
28
+ else {
29
+ console.log(`Responded to permission ${opts.permissionId} with: ${opts.response}`);
30
+ }
31
+ return;
32
+ }
33
+ if (opts.wait || opts.autoApprove) {
34
+ await waitAndRespond(resolved, opts);
35
+ return;
36
+ }
37
+ console.error("No --permission-id specified. Use --wait to wait for permission requests.");
38
+ process.exit(1);
39
+ });
40
+ }
41
+ async function respondToPermission(sessionId, permissionId, response) {
42
+ const client = getClient();
43
+ await client.postSessionIdPermissionsPermissionId({
44
+ path: { id: sessionId, permissionID: permissionId },
45
+ body: { response },
46
+ });
47
+ }
48
+ async function waitAndRespond(sessionId, opts) {
49
+ console.error(`Waiting for permission requests on session ${sessionId}...`);
50
+ console.error("Press Ctrl+C to stop.\n");
51
+ const result = await streamEvents(sessionId, async (event) => {
52
+ if (event.type !== "permission.updated")
53
+ return;
54
+ const props = event.properties;
55
+ // Skip permissions that are not pending (already resolved)
56
+ if (props.status && props.status !== "pending")
57
+ return;
58
+ console.error(`Permission request: ${props.title} (type: ${props.type}, id: ${props.id})`);
59
+ try {
60
+ if (opts.autoApprove) {
61
+ await respondToPermission(sessionId, props.id, "once");
62
+ console.error(`Auto-approved: ${props.id}`);
63
+ }
64
+ else {
65
+ await respondToPermission(sessionId, props.id, opts.response);
66
+ console.error(`Responded with "${opts.response}": ${props.id}`);
67
+ return "stop";
68
+ }
69
+ }
70
+ catch (err) {
71
+ console.error(`Failed to respond to ${props.id}: ${err instanceof Error ? err.message : String(err)}`);
72
+ }
73
+ });
74
+ if (result === "disconnected") {
75
+ console.error("Error: lost connection to OpenCode server.");
76
+ process.exit(1);
77
+ }
78
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionSendCommand(): Command;
@@ -0,0 +1,114 @@
1
+ import { Command } from "commander";
2
+ import { ensureServer } from "../client.js";
3
+ import { formatMessage, formatJSON } from "../format.js";
4
+ import { resolveSession } from "../resolve.js";
5
+ import { waitForIdle } from "../wait-util.js";
6
+ export function sessionSendCommand() {
7
+ return new Command("send")
8
+ .alias("prompt")
9
+ .description("Send a message to a session")
10
+ .argument("<message...>", "Message text to send")
11
+ .option("-s, --session <id>", "Session ID (defaults to most recent)")
12
+ .option("-j, --json", "Output response as JSON")
13
+ .option("-v, --verbose", "Show tool calls and extra details")
14
+ .option("-t, --text-only", "Show only text content in response")
15
+ .option("--no-reply", "Send as context injection (no AI response)")
16
+ .option("--async", "Send async and return immediately")
17
+ .option("-w, --wait", "Send async, block until session is idle, then show the last message")
18
+ .option("--agent <agent>", "Agent to use")
19
+ .option("--model <model>", "Model to use (format: provider/model)")
20
+ .option("--stdin", "Read message from stdin instead of arguments")
21
+ .action(async (messageParts, opts) => {
22
+ const client = await ensureServer();
23
+ const resolved = await resolveSession(client, opts.session);
24
+ let messageText;
25
+ if (opts.stdin) {
26
+ const chunks = [];
27
+ for await (const chunk of process.stdin) {
28
+ chunks.push(chunk);
29
+ }
30
+ messageText = Buffer.concat(chunks).toString("utf-8").trim();
31
+ }
32
+ else {
33
+ messageText = messageParts.join(" ");
34
+ }
35
+ if (!messageText) {
36
+ console.error("No message provided.");
37
+ process.exit(1);
38
+ }
39
+ // Parse model if provided
40
+ let model;
41
+ if (opts.model) {
42
+ const parts = opts.model.split("/");
43
+ if (parts.length === 2 && parts[0] && parts[1]) {
44
+ model = { providerID: parts[0], modelID: parts[1] };
45
+ }
46
+ }
47
+ const body = {
48
+ parts: [{ type: "text", text: messageText }],
49
+ ...(model && { model }),
50
+ ...(opts.agent && { agent: opts.agent }),
51
+ ...(opts.reply === false && { noReply: true }),
52
+ };
53
+ // --async: fire and forget
54
+ if (opts.async) {
55
+ await client.session.promptAsync({
56
+ path: { id: resolved },
57
+ body,
58
+ });
59
+ console.log("Message sent (async).");
60
+ return;
61
+ }
62
+ // --wait: send async, then block until session.idle, then show result
63
+ if (opts.wait) {
64
+ await client.session.promptAsync({
65
+ path: { id: resolved },
66
+ body,
67
+ });
68
+ const result = await waitForIdle(client, resolved);
69
+ if (!result.idle) {
70
+ if (result.reason === "disconnected") {
71
+ console.error("Error: lost connection to OpenCode server.");
72
+ }
73
+ process.exit(1);
74
+ }
75
+ // Fetch and display the last assistant message
76
+ const msgs = await client.session.messages({
77
+ path: { id: resolved },
78
+ });
79
+ const messages = msgs.data ?? [];
80
+ const last = messages.filter((m) => m.info.role === "assistant").pop();
81
+ if (!last) {
82
+ console.error("No assistant response found.");
83
+ process.exit(1);
84
+ }
85
+ if (opts.json) {
86
+ console.log(formatJSON(last));
87
+ return;
88
+ }
89
+ const textOnly = opts.verbose ? false : (opts.textOnly !== false);
90
+ console.log(formatMessage(last.info, last.parts, {
91
+ verbose: opts.verbose,
92
+ textOnly,
93
+ }));
94
+ return;
95
+ }
96
+ // Default: synchronous send (blocks until response)
97
+ const syncResult = await client.session.prompt({
98
+ path: { id: resolved },
99
+ body,
100
+ });
101
+ if (!syncResult.data) {
102
+ console.error("No response received.");
103
+ process.exit(1);
104
+ }
105
+ if (opts.json) {
106
+ console.log(formatJSON(syncResult.data));
107
+ return;
108
+ }
109
+ console.log(formatMessage(syncResult.data.info, syncResult.data.parts, {
110
+ verbose: opts.verbose,
111
+ textOnly: opts.textOnly ?? true,
112
+ }));
113
+ });
114
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function sessionShareCommand(): Command;
3
+ export declare function sessionUnshareCommand(): Command;