Skip to content

Commit eb64ca7

Browse files
Apply PR #23557: feat(opencode): add interactive split-footer mode to run
2 parents 15e7784 + 4ed9610 commit eb64ca7

51 files changed

Lines changed: 18054 additions & 336 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/opencode/src/cli/cmd/run.ts

Lines changed: 400 additions & 329 deletions
Large diffs are not rendered by default.

packages/opencode/src/cli/cmd/run/demo.ts

Lines changed: 1302 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { toolEntryBody } from "./tool"
2+
import type { RunEntryBody, StreamCommit } from "./types"
3+
4+
export type EntryFlags = {
5+
startOnNewLine: boolean
6+
trailingNewline: boolean
7+
}
8+
9+
export const RUN_ENTRY_NONE: RunEntryBody = {
10+
type: "none",
11+
}
12+
13+
export function cleanRunText(text: string): string {
14+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n")
15+
}
16+
17+
function textBody(content: string): RunEntryBody {
18+
if (!content) {
19+
return RUN_ENTRY_NONE
20+
}
21+
22+
return {
23+
type: "text",
24+
content,
25+
}
26+
}
27+
28+
function codeBody(content: string, filetype?: string): RunEntryBody {
29+
if (!content) {
30+
return RUN_ENTRY_NONE
31+
}
32+
33+
return {
34+
type: "code",
35+
content,
36+
filetype,
37+
}
38+
}
39+
40+
function markdownBody(content: string): RunEntryBody {
41+
if (!content) {
42+
return RUN_ENTRY_NONE
43+
}
44+
45+
return {
46+
type: "markdown",
47+
content,
48+
}
49+
}
50+
51+
function userBody(raw: string): RunEntryBody {
52+
if (!raw.trim()) {
53+
return RUN_ENTRY_NONE
54+
}
55+
56+
const lead = raw.match(/^\n+/)?.[0] ?? ""
57+
const body = lead ? raw.slice(lead.length) : raw
58+
return textBody(`${lead}${body}`)
59+
}
60+
61+
function reasoningBody(raw: string): RunEntryBody {
62+
const clean = raw.replace(/\[REDACTED\]/g, "")
63+
if (!clean) {
64+
return RUN_ENTRY_NONE
65+
}
66+
67+
const lead = clean.match(/^\n+/)?.[0] ?? ""
68+
const body = lead ? clean.slice(lead.length) : clean
69+
const mark = "Thinking:"
70+
if (body.startsWith(mark)) {
71+
return codeBody(`${lead}_Thinking:_ ${body.slice(mark.length).trimStart()}`, "markdown")
72+
}
73+
74+
return codeBody(clean, "markdown")
75+
}
76+
77+
function systemBody(raw: string, phase: StreamCommit["phase"]): RunEntryBody {
78+
return textBody(phase === "progress" ? raw : raw.trim())
79+
}
80+
81+
export function entryFlags(commit: StreamCommit): EntryFlags {
82+
if (commit.kind === "user") {
83+
return {
84+
startOnNewLine: true,
85+
trailingNewline: false,
86+
}
87+
}
88+
89+
if (commit.kind === "tool") {
90+
if (commit.phase === "progress") {
91+
return {
92+
startOnNewLine: false,
93+
trailingNewline: false,
94+
}
95+
}
96+
97+
return {
98+
startOnNewLine: true,
99+
trailingNewline: true,
100+
}
101+
}
102+
103+
if (commit.kind === "assistant" || commit.kind === "reasoning") {
104+
if (commit.phase === "progress") {
105+
return {
106+
startOnNewLine: false,
107+
trailingNewline: false,
108+
}
109+
}
110+
111+
return {
112+
startOnNewLine: true,
113+
trailingNewline: true,
114+
}
115+
}
116+
117+
return {
118+
startOnNewLine: true,
119+
trailingNewline: true,
120+
}
121+
}
122+
123+
export function entryDone(commit: StreamCommit): boolean {
124+
if (commit.kind === "assistant" || commit.kind === "reasoning") {
125+
return commit.phase === "final"
126+
}
127+
128+
if (commit.kind === "tool") {
129+
return commit.phase === "final" || (commit.phase === "progress" && commit.toolState === "completed")
130+
}
131+
132+
return true
133+
}
134+
135+
export function entryCanStream(commit: StreamCommit, body: RunEntryBody): boolean {
136+
if (commit.phase !== "progress") {
137+
return false
138+
}
139+
140+
if (body.type === "none") {
141+
return false
142+
}
143+
144+
return commit.kind === "assistant" || commit.kind === "reasoning" || commit.kind === "tool"
145+
}
146+
147+
export function entryBody(commit: StreamCommit): RunEntryBody {
148+
const raw = cleanRunText(commit.text)
149+
150+
if (commit.kind === "user") {
151+
return userBody(raw)
152+
}
153+
154+
if (commit.kind === "tool") {
155+
return toolEntryBody(commit, raw) ?? RUN_ENTRY_NONE
156+
}
157+
158+
if (commit.kind === "assistant") {
159+
if (commit.phase === "start") {
160+
return RUN_ENTRY_NONE
161+
}
162+
163+
if (commit.phase === "final") {
164+
return commit.interrupted ? textBody("assistant interrupted") : RUN_ENTRY_NONE
165+
}
166+
167+
return markdownBody(raw)
168+
}
169+
170+
if (commit.kind === "reasoning") {
171+
if (commit.phase === "start") {
172+
return RUN_ENTRY_NONE
173+
}
174+
175+
if (commit.phase === "final") {
176+
return commit.interrupted ? textBody("reasoning interrupted") : RUN_ENTRY_NONE
177+
}
178+
179+
return reasoningBody(raw)
180+
}
181+
182+
return systemBody(raw, commit.phase)
183+
}

0 commit comments

Comments
 (0)