Pārlūkot izejas kodu

fix(tui): show terminal tool failure labels (#31934)

Aiden Cline 2 mēneši atpakaļ
vecāks
revīzija
a150424c16

+ 13 - 4
packages/tui/src/routes/session/index.tsx

@@ -1831,6 +1831,7 @@ function InlineTool(props: {
   color?: RGBA
   complete: unknown
   pending: string
+  failure?: string
   spinner?: boolean
   subagent?: boolean
   children: JSX.Element
@@ -1884,6 +1885,7 @@ function InlineTool(props: {
       errorExpanded={errorExpanded()}
       complete={props.complete}
       pending={props.pending}
+      failure={props.failure}
       spinner={props.spinner}
       subagent={props.subagent}
       separateAfter={(id) => id !== undefined && ctx.userMessageIDs().has(id)}
@@ -1915,6 +1917,7 @@ export function InlineToolRow(props: {
   errorExpanded?: boolean
   complete: unknown
   pending: string
+  failure?: string
   spinner?: boolean
   subagent?: boolean
   children: JSX.Element
@@ -1958,7 +1961,7 @@ export function InlineToolRow(props: {
                 ~ {props.pending}
               </text>
             }
-            when={props.complete}
+            when={props.complete || props.failed}
           >
             <box flexDirection="row">
               <text
@@ -1973,7 +1976,7 @@ export function InlineToolRow(props: {
                 fg={props.failed ? props.errorColor : props.color}
                 attributes={props.denied ? TextAttributes.STRIKETHROUGH : undefined}
               >
-                {props.children}
+                {props.failed && !props.complete ? (props.failure ?? props.children) : props.children}
               </text>
             </box>
           </Show>
@@ -2445,7 +2448,7 @@ function ApplyPatch(props: ToolProps) {
         </For>
       </Match>
       <Match when={true}>
-        <InlineTool icon="%" pending="Preparing patch..." complete={false} part={props.part}>
+        <InlineTool icon="%" pending="Preparing patch..." failure="Patch failed" complete={false} part={props.part}>
           Patch
         </InlineTool>
       </Match>
@@ -2465,7 +2468,13 @@ function TodoWrite(props: ToolProps) {
         </BlockTool>
       </Match>
       <Match when={true}>
-        <InlineTool icon="⚙" pending="Updating todos..." complete={false} part={props.part}>
+        <InlineTool
+          icon="⚙"
+          pending="Updating todos..."
+          failure="Todo update failed"
+          complete={false}
+          part={props.part}
+        >
           Updating todos...
         </InlineTool>
       </Match>

+ 40 - 0
packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx

@@ -155,6 +155,34 @@ function StickyScrollFixture(props: { separated: boolean; scroll: (scroll: Scrol
   )
 }
 
+function FailedPendingToolFixture() {
+  return (
+    <InlineToolRow
+      icon="%"
+      complete={false}
+      pending="Preparing patch..."
+      failed={true}
+      failure="Patch failed"
+    >
+      Patch
+    </InlineToolRow>
+  )
+}
+
+function FailedCompleteToolFixture() {
+  return (
+    <InlineToolRow
+      icon="→"
+      complete={true}
+      pending="Reading file..."
+      failed={true}
+      failure="Read failed"
+    >
+      Read src/index.ts
+    </InlineToolRow>
+  )
+}
+
 async function renderFrame(component: () => JSX.Element, options: { width: number; height: number }) {
   testSetup = await testRender(component, options)
   await testSetup.renderOnce()
@@ -173,6 +201,18 @@ describe("TUI inline tool wrapping", () => {
     expect(toolDisplay("plugin_tool")).toBe("generic")
   })
 
+  test("replaces pending copy when a tool fails before completion", async () => {
+    const frame = await renderFrame(() => <FailedPendingToolFixture />, { width: 72, height: 3 })
+    expect(frame).toContain("Patch failed")
+    expect(frame).not.toContain("Preparing patch")
+  })
+
+  test("preserves useful completed copy when a tool fails", async () => {
+    const frame = await renderFrame(() => <FailedCompleteToolFixture />, { width: 72, height: 3 })
+    expect(frame).toContain("Read src/index.ts")
+    expect(frame).not.toContain("Read failed")
+  })
+
   test("filters malformed nested tool wire data", () => {
     expect(
       parseApplyPatchFiles([