opencode-agent[bot] 2 месяцев назад
Родитель
Сommit
3867fa2bad

+ 48 - 40
packages/opencode/src/cli/cmd/run/footer.command.tsx

@@ -298,7 +298,14 @@ function PanelShell(props: {
         </box>
       )}
       {minimal() ? (
-        <box id={`${props.id}-bottom`} width="100%" height={1} border={false} backgroundColor="transparent" flexShrink={0}>
+        <box
+          id={`${props.id}-bottom`}
+          width="100%"
+          height={1}
+          border={false}
+          backgroundColor="transparent"
+          flexShrink={0}
+        >
           <box
             width="100%"
             height={1}
@@ -368,17 +375,18 @@ export function RunCommandMenuBody(props: {
       },
       ...(props.subagents().length > 0
         ? [
-          {
-            action: "subagent" as const,
-            category: "Session",
-            display: "View subagents",
-            footer: activeSubagentCount() > 0 ? `${activeSubagentCount()} active` : `${props.subagents().length} recent`,
-            keywords: props
-              .subagents()
-              .map((item) => `${item.label} ${item.description} ${item.title ?? ""}`)
-              .join(" "),
-          },
-        ]
+            {
+              action: "subagent" as const,
+              category: "Session",
+              display: "View subagents",
+              footer:
+                activeSubagentCount() > 0 ? `${activeSubagentCount()} active` : `${props.subagents().length} recent`,
+              keywords: props
+                .subagents()
+                .map((item) => `${item.label} ${item.description} ${item.title ?? ""}`)
+                .join(" "),
+            },
+          ]
         : []),
       {
         action: "slash",
@@ -392,16 +400,16 @@ export function RunCommandMenuBody(props: {
     const prompt: CommandEntry[] =
       props.commands() === undefined || skills().length > 0
         ? [
-          {
-            action: "skill" as const,
-            category: "Prompt",
-            display: "Skills",
-            footer: "/skills",
-            keywords: `skill skills ${skills()
-              .map((item) => `${item.name} ${item.description ?? ""}`)
-              .join(" ")}`.trim(),
-          },
-        ]
+            {
+              action: "skill" as const,
+              category: "Prompt",
+              display: "Skills",
+              footer: "/skills",
+              keywords: `skill skills ${skills()
+                .map((item) => `${item.name} ${item.description ?? ""}`)
+                .join(" ")}`.trim(),
+            },
+          ]
         : []
     const agent: CommandEntry[] = [
       {
@@ -411,17 +419,17 @@ export function RunCommandMenuBody(props: {
       },
       ...(props.queued().length > 0
         ? [
-          {
-            action: "queued" as const,
-            category: "Agent",
-            display: "Manage queued prompts",
-            footer: `${props.queued().length} queued`,
-            keywords: props
-              .queued()
-              .map((item) => item.prompt.text)
-              .join(" "),
-          },
-        ]
+            {
+              action: "queued" as const,
+              category: "Agent",
+              display: "Manage queued prompts",
+              footer: `${props.queued().length} queued`,
+              keywords: props
+                .queued()
+                .map((item) => item.prompt.text)
+                .join(" "),
+            },
+          ]
         : []),
       {
         action: "variant.cycle",
@@ -432,13 +440,13 @@ export function RunCommandMenuBody(props: {
       },
       ...(props.variants().length > 0
         ? [
-          {
-            action: "variant.list" as const,
-            category: "Agent",
-            display: "Switch model variant",
-            keywords: `variant variants ${props.variants().join(" ")}`,
-          },
-        ]
+            {
+              action: "variant.list" as const,
+              category: "Agent",
+              display: "Switch model variant",
+              keywords: `variant variants ${props.variants().join(" ")}`,
+            },
+          ]
         : []),
     ]
     const commands = (props.commands() ?? [])

+ 7 - 6
packages/opencode/src/cli/cmd/run/footer.menu.tsx

@@ -264,7 +264,12 @@ export function RunFooterMenu(props: {
           if (row.type === "header") {
             return (
               <box paddingLeft={props.paddingLeft ?? 1} paddingRight={props.paddingRight ?? 1}>
-                <text fg={props.headerColor ?? props.theme().highlight} attributes={TextAttributes.BOLD} wrapMode="none" truncate>
+                <text
+                  fg={props.headerColor ?? props.theme().highlight}
+                  attributes={TextAttributes.BOLD}
+                  wrapMode="none"
+                  truncate
+                >
                   {row.label}
                 </text>
               </box>
@@ -281,11 +286,7 @@ export function RunFooterMenu(props: {
                 ? props.theme().shade
                 : transparent
           return (
-            <box
-              paddingRight={0}
-              flexDirection="row"
-              backgroundColor={background()}
-            >
+            <box paddingRight={0} flexDirection="row" backgroundColor={background()}>
               {border() ? (
                 <text fg={props.theme().highlight} bg={background()} wrapMode="none">
                   {active() ? "▌" : " "}

+ 6 - 2
packages/opencode/src/cli/cmd/run/footer.prompt.tsx

@@ -234,7 +234,7 @@ export function RunPromptBody(props: {
 
           props.onContentChange()
         })
-        .catch(() => { })
+        .catch(() => {})
     }, 0)
   }
 
@@ -1200,7 +1200,11 @@ export function createPromptState(input: PromptInput): PromptState {
       return
     }
 
-    const submit = command ? { ...next, command } : parsed?.type === "command" ? { ...next, command: parsed.command } : next
+    const submit = command
+      ? { ...next, command }
+      : parsed?.type === "command"
+        ? { ...next, command: parsed.command }
+        : next
     const shellMode = next.mode === "shell"
 
     resetDraft()

+ 7 - 7
packages/opencode/src/cli/cmd/run/footer.ts

@@ -720,14 +720,14 @@ export class RunFooter implements FooterApi {
               : this.promptRoute.type === "model"
                 ? 1 + MODEL_ROWS
                 : this.promptRoute.type === "variant"
-                ? 1 + VARIANT_ROWS
-                : this.promptRoute.type === "queued-menu"
-                  ? 1 + this.subagentMenuRows
-                  : this.promptRoute.type === "subagent-menu"
+                  ? 1 + VARIANT_ROWS
+                  : this.promptRoute.type === "queued-menu"
                     ? 1 + this.subagentMenuRows
-                    : this.promptRoute.type === "subagent"
-                      ? this.base + SUBAGENT_INSPECTOR_ROWS
-                      : this.base + Math.max(TEXTAREA_MIN_ROWS, Math.min(PROMPT_MAX_ROWS, this.rows))
+                    : this.promptRoute.type === "subagent-menu"
+                      ? 1 + this.subagentMenuRows
+                      : this.promptRoute.type === "subagent"
+                        ? this.base + SUBAGENT_INSPECTOR_ROWS
+                        : this.base + Math.max(TEXTAREA_MIN_ROWS, Math.min(PROMPT_MAX_ROWS, this.rows))
 
     if (height !== this.renderer.footerHeight) {
       this.renderer.footerHeight = height

+ 9 - 5
packages/opencode/src/cli/cmd/run/footer.view.tsx

@@ -166,7 +166,9 @@ export function RunFooterView(props: RunFooterViewProps) {
 
     return tabs().findIndex((item) => item.sessionID === sessionID) + 1
   })
-  const foregroundSubagents = createMemo(() => props.backgroundSubagents && activeTabs().some((item) => !item.background))
+  const foregroundSubagents = createMemo(
+    () => props.backgroundSubagents && activeTabs().some((item) => !item.background),
+  )
   const model = createMemo(() => {
     const current = props.currentModel()
     return current ? modelInfo(props.providers(), current) : { model: props.state().model, provider: undefined }
@@ -648,9 +650,9 @@ export function RunFooterView(props: RunFooterViewProps) {
                     panel() || prompt()
                       ? undefined
                       : {
-                        ...EMPTY_BORDER,
-                        vertical: "█",
-                      }
+                          ...EMPTY_BORDER,
+                          vertical: "█",
+                        }
                   }
                 >
                   <box
@@ -938,7 +940,9 @@ export function RunFooterView(props: RunFooterViewProps) {
                       maxWidth={18}
                     >
                       <text fg={theme().text} wrapMode="none" truncate>
-                        <Show when={hasActivityMeta() || hasModelStatus() || hasContextHints()}>{sectionSeparator()}</Show>
+                        <Show when={hasActivityMeta() || hasModelStatus() || hasContextHints()}>
+                          {sectionSeparator()}
+                        </Show>
                         <span style={{ fg: theme().text }}>{hint().key}</span>{" "}
                         <span style={{ fg: theme().muted }}>{hint().label}</span>
                       </text>

+ 1 - 6
packages/opencode/src/cli/cmd/run/prompt.editor.ts

@@ -96,12 +96,7 @@ function promptPartStart(part: Mention) {
   return part.source?.text.start ?? Number.POSITIVE_INFINITY
 }
 
-function findPromptPartIndex(
-  content: string,
-  text: string,
-  used: Array<{ start: number; end: number }>,
-  hint: number,
-) {
+function findPromptPartIndex(content: string, text: string, used: Array<{ start: number; end: number }>, hint: number) {
   let searchFrom = 0
   let best = -1
   let distance = Number.POSITIVE_INFINITY

+ 4 - 1
packages/opencode/src/cli/cmd/run/scrollback.writer.tsx

@@ -341,7 +341,10 @@ export function turnSummaryWriter(input: { agent: string; model: string; duratio
         <text wrapMode="none" truncate>
           <span style={{ fg: input.theme.block.highlight }}>▣ </span>
           <span style={{ fg: input.theme.block.text }}>{input.agent}</span>
-          <span style={{ fg: input.theme.block.muted }}> · {input.model} · {input.duration}</span>
+          <span style={{ fg: input.theme.block.muted }}>
+            {" "}
+            · {input.model} · {input.duration}
+          </span>
         </text>
       </box>
     ),

+ 3 - 1
packages/opencode/src/cli/cmd/run/session-replay.ts

@@ -217,7 +217,9 @@ function replayMessage(
     commits.push(...next.commits)
   }
 
-  const summary = config.summaries.has(message.info.id) ? messageTurnSummaryCommit(message, config.providers) : undefined
+  const summary = config.summaries.has(message.info.id)
+    ? messageTurnSummaryCommit(message, config.providers)
+    : undefined
   if (summary) {
     commits.push(summary)
   }

+ 4 - 4
packages/opencode/src/cli/cmd/run/stream.transport.ts

@@ -713,10 +713,10 @@ function createLayer(input: StreamInput) {
                 messages: messagesList,
                 permissions: sessionPermissions,
                 questions: sessionQuestions,
-              thinking: input.thinking,
-              limits: input.limits(),
-              providers: input.providers?.(),
-            })
+                thinking: input.thinking,
+                limits: input.limits(),
+                providers: input.providers?.(),
+              })
             : undefined
           const replay =
             history && input.replayLimit !== undefined && messagesList.length > input.replayLimit

+ 18 - 17
packages/opencode/src/cli/cmd/run/subagent-data.ts

@@ -829,9 +829,10 @@ export function reduceSubagentData(input: {
   }
 
   const detail = ensureDetail(input.data, sessionID)
-  const cancelled = event.type === "message.updated" && isAbortedAssistantMessage(event.properties.info)
-    ? cancelSubagentTab(input.data, sessionID)
-    : false
+  const cancelled =
+    event.type === "message.updated" && isAbortedAssistantMessage(event.properties.info)
+      ? cancelSubagentTab(input.data, sessionID)
+      : false
   if (event.type === "session.status") {
     if (event.properties.status.type !== "retry") {
       return cancelled
@@ -839,13 +840,13 @@ export function reduceSubagentData(input: {
 
     return (
       appendCommits(detail, [
-      {
-        kind: "error",
-        text: event.properties.status.message,
-        phase: "start",
-        source: "system",
-        messageID: `retry:${event.properties.status.attempt}`,
-      },
+        {
+          kind: "error",
+          text: event.properties.status.message,
+          phase: "start",
+          source: "system",
+          messageID: `retry:${event.properties.status.attempt}`,
+        },
       ]) || cancelled
     )
   }
@@ -853,13 +854,13 @@ export function reduceSubagentData(input: {
   if (event.type === "session.error" && event.properties.error) {
     return (
       appendCommits(detail, [
-      {
-        kind: "error",
-        text: formatError(event.properties.error),
-        phase: "start",
-        source: "system",
-        messageID: `session.error:${event.properties.sessionID}:${formatError(event.properties.error)}`,
-      },
+        {
+          kind: "error",
+          text: formatError(event.properties.error),
+          phase: "start",
+          source: "system",
+          messageID: `session.error:${event.properties.sessionID}:${formatError(event.properties.error)}`,
+        },
       ]) || cancelled
     )
   }

+ 51 - 52
packages/opencode/test/cli/run/session-replay.test.ts

@@ -102,63 +102,62 @@ function assistantMessage(
   }
 }
 
-const provider = (name: string): RunProvider =>
-  ({
-    id: "openai",
-    name: "OpenAI",
-    source: "api",
-    env: [],
-    options: {},
-    models: {
-      "gpt-5": {
-        id: "gpt-5",
-        providerID: "openai",
-        api: {
-          id: "openai",
-          url: "https://openai.test",
-          npm: "@ai-sdk/openai",
+const provider = (name: string): RunProvider => ({
+  id: "openai",
+  name: "OpenAI",
+  source: "api",
+  env: [],
+  options: {},
+  models: {
+    "gpt-5": {
+      id: "gpt-5",
+      providerID: "openai",
+      api: {
+        id: "openai",
+        url: "https://openai.test",
+        npm: "@ai-sdk/openai",
+      },
+      name,
+      capabilities: {
+        temperature: true,
+        reasoning: true,
+        attachment: true,
+        toolcall: true,
+        input: {
+          text: true,
+          audio: false,
+          image: false,
+          video: false,
+          pdf: false,
         },
-        name,
-        capabilities: {
-          temperature: true,
-          reasoning: true,
-          attachment: true,
-          toolcall: true,
-          input: {
-            text: true,
-            audio: false,
-            image: false,
-            video: false,
-            pdf: false,
-          },
-          output: {
-            text: true,
-            audio: false,
-            image: false,
-            video: false,
-            pdf: false,
-          },
-          interleaved: false,
+        output: {
+          text: true,
+          audio: false,
+          image: false,
+          video: false,
+          pdf: false,
         },
-        cost: {
-          input: 0,
-          output: 0,
-          cache: {
-            read: 0,
-            write: 0,
-          },
-        },
-        limit: {
-          context: 128000,
-          output: 8192,
+        interleaved: false,
+      },
+      cost: {
+        input: 0,
+        output: 0,
+        cache: {
+          read: 0,
+          write: 0,
         },
-        status: "active",
-        options: {},
-        headers: {},
-        release_date: "2026-01-01",
       },
+      limit: {
+        context: 128000,
+        output: 8192,
+      },
+      status: "active",
+      options: {},
+      headers: {},
+      release_date: "2026-01-01",
     },
-  })
+  },
+})
 
 function runningToolMessage(id: string): SessionMessages[number] {
   return {

+ 9 - 9
packages/tui/src/component/prompt/index.tsx

@@ -461,7 +461,7 @@ export function Prompt(props: PromptProps) {
 
               if (!virtualText) return part
 
-               const newStart = normalized.indexOf(virtualText)
+              const newStart = normalized.indexOf(virtualText)
               // if the virtual text is deleted, remove the part
               if (newStart === -1) return null
 
@@ -496,14 +496,14 @@ export function Prompt(props: PromptProps) {
             })
             .filter((part) => part !== null)
 
-           setStore("prompt", {
-             input: normalized,
-             // keep only the non-text parts because the text parts were
-             // already expanded inline
-             parts: updatedNonTextParts,
-           })
-           restoreExtmarksFromParts(updatedNonTextParts)
-           input.cursorOffset = Bun.stringWidth(normalized)
+          setStore("prompt", {
+            input: normalized,
+            // keep only the non-text parts because the text parts were
+            // already expanded inline
+            parts: updatedNonTextParts,
+          })
+          restoreExtmarksFromParts(updatedNonTextParts)
+          input.cursorOffset = Bun.stringWidth(normalized)
         },
       },
       {