Skip to content

Improve AI response parsing and sanitization for command suggestions#273

Merged
AnnatarHe merged 2 commits intomainfrom
claude/fix-cli-question-command-UeTGO
Apr 22, 2026
Merged

Improve AI response parsing and sanitization for command suggestions#273
AnnatarHe merged 2 commits intomainfrom
claude/fix-cli-question-command-UeTGO

Conversation

@AnnatarHe
Copy link
Copy Markdown
Contributor

@AnnatarHe AnnatarHe commented Apr 22, 2026

Summary

This PR enhances the AI service's Server-Sent Events (SSE) parsing and adds robust sanitization of AI-generated command suggestions to handle various formatting styles and edge cases.

Key Changes

SSE Parsing Improvements (model/ai_service.go)

  • Introduced stripSSEField() helper function that properly parses SSE field values according to the SSE specification, handling optional leading spaces
  • Refactored SSE event parsing to use the new helper, improving robustness for both event: and data: fields
  • Fixed event error state management: errors are now properly reset on blank lines between events, preventing false error propagation
  • Improved handling of [DONE] markers with or without leading spaces

Command Sanitization (commands/query.go)

  • Added sanitizeSuggestedCommand() function that normalizes raw AI output into executable commands
  • Handles triple-backtick code fences with optional language tags (bash, sh, shell, zsh, fish, pwsh, powershell)
  • Strips single backticks for single-line responses
  • Preserves comment-style refusals (lines starting with #) verbatim for user visibility
  • Trims surrounding whitespace while preserving intentional spacing in multi-line commands
  • Added validation to reject empty AI responses with user-friendly error messaging

Test Coverage

  • Added comprehensive SSE parsing tests (TestQueryCommandStream_SSEParsing) covering:
    • Various [DONE] marker formats
    • Multi-token streams with mixed spacing
    • Error event handling with and without spaces
    • Event state reset between messages
  • Added unit tests for stripSSEField() helper function
  • Added extensive tests for sanitizeSuggestedCommand() covering:
    • Code fence removal with various language tags
    • Single backtick stripping
    • Whitespace handling
    • Comment preservation
    • Edge cases (empty input, multiline without fences)
  • Updated existing empty response test to expect error instead of success

Notable Implementation Details

  • SSE field parsing now strictly follows the specification for space handling
  • Error state is properly scoped to individual events (reset on blank lines)
  • Command sanitization is defensive and handles multiple formatting styles that AI models might produce
  • Empty responses are now treated as errors rather than silently passing through

https://claude.ai/code/session_01Qa2FSi9HhA49Z4LTMmpxm9


Open in Devin Review

claude added 2 commits April 21, 2026 23:28
Per SSE spec §9.2 the value after "data:" or "event:" may have a single
optional leading space that parsers must strip. The previous parser
stripped the prefix but not the space, so Gin's "data: [DONE]" was
compared as " [DONE]" and the stream never terminated cleanly.
Forwarded tokens were also prefixed with stray whitespace.

- Extract stripSSEField helper that removes at most one leading space.
- Apply it to both event: and data: field names.
- Reset isError on blank-line dispatch (event boundary), not inside the
  data: branch.
- Recognize "event:error" in addition to "event: error".

https://claude.ai/code/session_01Qa2FSi9HhA49Z4LTMmpxm9
The suggested command often arrived wrapped in Markdown code fences or
surrounding single backticks, which broke shell execution. Extract a
sanitizeSuggestedCommand helper that strips triple-backtick fences
(with optional bash/sh/shell/zsh/fish/pwsh/powershell language tag) and
single-line backtick wrapping, then trims whitespace. Fail fast with a
clear user-facing error on empty output instead of classifying an empty
string as ActionOther and silently returning.

https://claude.ai/code/session_01Qa2FSi9HhA49Z4LTMmpxm9
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5373bf2b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread commands/query.go
}
}
s = strings.TrimRight(s, " \t\n")
s = strings.TrimSuffix(s, "```")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Truncate fenced output at first closing backticks

When the AI returns a fenced command followed by explanatory prose (for example bash\nls -la\n```\nThis lists files), sanitizeSuggestedCommand only removes a closing fence if it is the final suffix, so it leaves \n```\nThis lists files in the command. Because classification uses the first token, this can now be auto-run as a view/edit command while still containing non-command prose, causing unintended shell execution and failures whenever auto-run is enabled. The sanitizer should extract content up to the first closing fence and ignore trailing text before classification/execution.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a sanitizeSuggestedCommand utility to normalize AI-generated commands by stripping markdown fences and backticks, and refactors the SSE parsing logic to improve compliance with the specification. The changes include new unit tests for both the sanitization logic and the SSE stream processing. Review feedback suggests refining the SSE parser to ensure the error state is explicitly updated for all event fields and to support multi-line error messages.

Comment thread model/ai_service.go
Comment on lines +81 to +83
if v == "error" {
isError = true
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The isError state should be explicitly set based on whether the event type is exactly "error". Currently, if an event is marked as "error" and then a subsequent event in the same stream (before a blank line) has a different event type, isError will remain true. While SSE events are typically separated by blank lines, it's safer to update the state for every event: field encountered.

Suggested change
if v == "error" {
isError = true
}
if v, ok := stripSSEField(line, "event:"); ok {
isError = (v == "error")
continue
}

Comment thread model/ai_service.go
Comment on lines 88 to 90
if isError {
return fmt.Errorf("server error: %s", data)
return fmt.Errorf("server error: %s", v)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the server sends a multi-line error message (multiple data: lines for a single event: error), this implementation will return an error containing only the first line. Consider buffering the error message until the event is fully received (at the blank line) or concatenating it if multi-line errors are expected.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
unittests 39.57% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
commands/query.go 87.71% <100.00%> (+2.67%) ⬆️
model/ai_service.go 80.30% <100.00%> (+36.11%) ⬆️

... and 98 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AnnatarHe AnnatarHe merged commit 88967e8 into main Apr 22, 2026
9 checks passed
@AnnatarHe AnnatarHe deleted the claude/fix-cli-question-command-UeTGO branch April 22, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants