Skip to content

Commit f10f14c

Browse files
fix(agent): fix OpenCode double-prompt, Claude ConvertCommand, and parser event timing
- Remove duplicate stdin in OpenCode ConvertCommand and FixJSONCommand where the prompt was passed both as a CLI argument and via stdin - Revert Claude ConvertCommand to use `-p` with stdin instead of the incorrect `-p --tools ""` which passed an empty string as a separate arg - Fix OpenCode parser to emit EventToolStart when tools begin (not only on completion) and EventToolResult when completed, so the TUI shows activity during tool execution
1 parent bde875d commit f10f14c

3 files changed

Lines changed: 26 additions & 21 deletions

File tree

internal/agent/claude.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (p *ClaudeProvider) InteractiveCommand(workDir, prompt string) *exec.Cmd {
4949

5050
// ConvertCommand implements loop.Provider.
5151
func (p *ClaudeProvider) ConvertCommand(workDir, prompt string) (*exec.Cmd, loop.OutputMode, string, error) {
52-
cmd := exec.Command(p.cliPath, "-p", "--tools", "")
52+
cmd := exec.Command(p.cliPath, "-p")
5353
cmd.Dir = workDir
5454
cmd.Stdin = strings.NewReader(prompt)
5555
return cmd, loop.OutputStdout, "", nil

internal/agent/opencode.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@ func (p *OpenCodeProvider) InteractiveCommand(workDir, prompt string) *exec.Cmd
3939
func (p *OpenCodeProvider) ConvertCommand(workDir, prompt string) (*exec.Cmd, loop.OutputMode, string, error) {
4040
cmd := exec.Command(p.cliPath, "run", "--format", "json", "--", prompt)
4141
cmd.Dir = workDir
42-
cmd.Stdin = strings.NewReader(prompt)
4342
return cmd, loop.OutputStdout, "", nil
4443
}
4544

4645
func (p *OpenCodeProvider) FixJSONCommand(prompt string) (*exec.Cmd, loop.OutputMode, string, error) {
4746
cmd := exec.Command(p.cliPath, "run", "--format", "json", "--", prompt)
48-
cmd.Stdin = strings.NewReader(prompt)
4947
return cmd, loop.OutputStdout, "", nil
5048
}
5149

@@ -56,30 +54,32 @@ func (p *OpenCodeProvider) ParseLine(line string) *loop.Event {
5654
func (p *OpenCodeProvider) LogFileName() string { return "opencode.log" }
5755

5856
// CleanOutput extracts JSON from opencode's NDJSON output format.
57+
// It looks for the last "text" event line and returns its part.text content.
5958
func (p *OpenCodeProvider) CleanOutput(output string) string {
6059
output = strings.TrimSpace(output)
61-
62-
if !strings.Contains(output, "\n") || !strings.Contains(output, `"type":"step_start"`) || !strings.Contains(output, `"type":"text"`) {
60+
if !strings.Contains(output, "\n") {
6361
return output
6462
}
6563

66-
lines := strings.Split(output, "\n")
67-
for _, line := range lines {
64+
// Parse each line as JSON to find text events (last one wins).
65+
var lastText string
66+
for _, line := range strings.Split(output, "\n") {
6867
line = strings.TrimSpace(line)
6968
if line == "" {
7069
continue
7170
}
72-
if strings.Contains(line, `"type":"text"`) {
73-
var ev struct {
74-
Type string `json:"type"`
75-
Part struct {
76-
Text string `json:"text"`
77-
} `json:"part"`
78-
}
79-
if json.Unmarshal([]byte(line), &ev) == nil && ev.Part.Text != "" {
80-
return ev.Part.Text
81-
}
71+
var ev struct {
72+
Type string `json:"type"`
73+
Part struct {
74+
Text string `json:"text"`
75+
} `json:"part"`
76+
}
77+
if json.Unmarshal([]byte(line), &ev) == nil && ev.Type == "text" && ev.Part.Text != "" {
78+
lastText = ev.Part.Text
8279
}
8380
}
81+
if lastText != "" {
82+
return lastText
83+
}
8484
return output
8585
}

internal/loop/opencode_parser.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,21 @@ func ParseLineOpenCode(line string) *Event {
7878
return &Event{Type: EventIterationStart}
7979

8080
case "tool_use":
81-
if ev.Part == nil || ev.Part.State == nil {
81+
if ev.Part == nil {
8282
return nil
8383
}
84-
if ev.Part.State.Status == "completed" {
84+
if ev.Part.State != nil && ev.Part.State.Status == "completed" {
8585
return &Event{
86-
Type: EventToolStart,
86+
Type: EventToolResult,
8787
Tool: ev.Part.Tool,
88+
Text: ev.Part.State.Output,
8889
}
8990
}
90-
return nil
91+
// Tool starting or in-progress
92+
return &Event{
93+
Type: EventToolStart,
94+
Tool: ev.Part.Tool,
95+
}
9196

9297
case "text":
9398
if ev.Part == nil {

0 commit comments

Comments
 (0)