Merge pull request #11752 from cli/kw/run-view-pr-feedback
`gh agent-task view`: Follow-up to #11743
This commit is contained in:
commit
6eafe9687c
10 changed files with 288 additions and 107 deletions
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
|
|
@ -133,15 +134,16 @@ func renderLogEntry(entry chatCompletionChunkEntry, w io.Writer, io *iostreams.I
|
|||
case "view":
|
||||
args := viewToolArgs{}
|
||||
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
|
||||
return false, fmt.Errorf("failed to parse 'view' tool call arguments: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to parse 'view' tool call arguments: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "View %s\n", cs.Bold(relativeFilePath(args.Path)))
|
||||
renderToolCallTitle(w, cs, fmt.Sprintf("View %s", cs.Bold(relativeFilePath(args.Path))), "")
|
||||
|
||||
content := stripDiffFormat(choice.Delta.Content)
|
||||
|
||||
// TODO: Strip the diff formatting from this, but for now render as it is.
|
||||
if err := renderFileContentAsMarkdown(args.Path, content, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render viewed file content: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render viewed file content: %v\n\n", err)
|
||||
fmt.Fprintln(io.ErrOut, content) // raw fallback
|
||||
}
|
||||
case "bash":
|
||||
if v := unmarshal[bashToolArgs](args); v != nil {
|
||||
|
|
@ -153,10 +155,11 @@ func renderLogEntry(entry chatCompletionChunkEntry, w io.Writer, io *iostreams.I
|
|||
|
||||
contentWithCommand := choice.Delta.Content
|
||||
if v.Command != "" {
|
||||
contentWithCommand = fmt.Sprintf("%s\n%s", v.Command, choice.Delta.Content)
|
||||
contentWithCommand = fmt.Sprintf("$ %s\n%s", v.Command, choice.Delta.Content)
|
||||
}
|
||||
if err := renderFileContentAsMarkdown("commands.sh", contentWithCommand, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render bash command output: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render bash command output: %v\n\n", err)
|
||||
fmt.Fprintln(io.ErrOut, contentWithCommand)
|
||||
}
|
||||
}
|
||||
// TODO: consider including more details for these bash-related tool calls.
|
||||
|
|
@ -193,24 +196,26 @@ func renderLogEntry(entry chatCompletionChunkEntry, w io.Writer, io *iostreams.I
|
|||
case "think":
|
||||
args := thinkToolArgs{}
|
||||
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
|
||||
return false, fmt.Errorf("failed to parse 'think' tool call arguments: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to parse 'think' tool call arguments: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// NOTE: omit the delta.content since it's the same as thought
|
||||
renderToolCallTitle(w, cs, "Thought", "")
|
||||
if err := renderRawMarkdown(args.Thought, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render thought: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render thought: %v\n", err)
|
||||
}
|
||||
case "report_progress":
|
||||
args := reportProgressToolArgs{}
|
||||
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
|
||||
return false, fmt.Errorf("failed to parse 'report_progress' tool call arguments: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to parse 'report_progress' tool call arguments: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
renderToolCallTitle(w, cs, "Progress update", cs.Bold(args.CommitMessage))
|
||||
if args.PrDescription != "" {
|
||||
if err := renderRawMarkdown(args.PrDescription, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render PR description: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render PR description: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,29 +223,33 @@ func renderLogEntry(entry chatCompletionChunkEntry, w io.Writer, io *iostreams.I
|
|||
if choice.Delta.Content != "" {
|
||||
// Try to treat this as JSON
|
||||
if err := renderContentAsJSONMarkdown("", choice.Delta.Content, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render progress update content: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render progress update content: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
case "create":
|
||||
args := createToolArgs{}
|
||||
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
|
||||
return false, fmt.Errorf("failed to parse 'create' tool call arguments: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to parse 'create' tool call arguments: %v\n", err)
|
||||
continue
|
||||
}
|
||||
renderToolCallTitle(w, cs, "Create", cs.Bold(relativeFilePath(args.Path)))
|
||||
|
||||
if err := renderFileContentAsMarkdown(args.Path, args.FileText, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render created file content: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render created file content: %v\n\n", err)
|
||||
fmt.Fprintln(io.ErrOut, args.FileText)
|
||||
}
|
||||
case "str_replace":
|
||||
args := strReplaceToolArgs{}
|
||||
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
|
||||
return false, fmt.Errorf("failed to parse 'str_replace' tool call arguments: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to parse 'str_replace' tool call arguments: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
renderToolCallTitle(w, cs, "Edit", cs.Bold(relativeFilePath(args.Path)))
|
||||
if err := renderFileContentAsMarkdown("output.diff", choice.Delta.Content, w, io); err != nil {
|
||||
return false, fmt.Errorf("failed to render str_replace diff: %w", err)
|
||||
fmt.Fprintf(io.ErrOut, "\nfailed to render str_replace diff: %v\n\n", err)
|
||||
fmt.Fprintln(io.ErrOut, choice.Delta.Content)
|
||||
}
|
||||
default:
|
||||
// Unknown tool call. For example for "codeql_checker":
|
||||
|
|
@ -333,14 +342,15 @@ func stripDiffFormat(diff string) string {
|
|||
}
|
||||
}
|
||||
|
||||
// If we found the hunk header end, we strip everything before it.
|
||||
if hunkEndIndex != -1 {
|
||||
lines = lines[hunkEndIndex+1:]
|
||||
} else {
|
||||
// This isn't a diff, so we defensively just return the original string.
|
||||
// Guard clause: if we didn't find a hunk header, this isn't a diff, so
|
||||
// we defensively just return the original string.
|
||||
if hunkEndIndex == -1 {
|
||||
return diff
|
||||
}
|
||||
|
||||
// We found the hunk header end; strip everything before it.
|
||||
lines = lines[hunkEndIndex+1:]
|
||||
|
||||
// Now we strip the leading + and - from lines, if they exist.
|
||||
// Note: most of the time, but not all the time, we get a diff without
|
||||
// these prefixes.
|
||||
|
|
@ -358,10 +368,9 @@ func stripDiffFormat(diff string) string {
|
|||
// renderFileContentAsMarkdown renders the given content as markdown
|
||||
// based on the file extension of the path.
|
||||
func renderFileContentAsMarkdown(path, content string, w io.Writer, io *iostreams.IOStreams) error {
|
||||
parts := strings.Split(path, ".")
|
||||
lang := parts[len(parts)-1]
|
||||
lang := filepath.Ext(filepath.ToSlash(path))
|
||||
|
||||
if lang == "md" {
|
||||
if lang == ".md" {
|
||||
return renderRawMarkdown(content, w, io)
|
||||
}
|
||||
|
||||
|
|
@ -422,62 +431,63 @@ func renderToolCallTitle(w io.Writer, cs *iostreams.ColorScheme, toolName, title
|
|||
}
|
||||
}
|
||||
|
||||
// genericToolCallNamesToTitles maps known generic tool call identifiers to human-friendly titles.
|
||||
var genericToolCallNamesToTitles = map[string]string{
|
||||
// Custom tools, the GitHub UI doesn't currently have these.
|
||||
"codeql_checker": "Run CodeQL analysis",
|
||||
|
||||
// Playwright tools.
|
||||
"playwright-browser_navigate": "Navigate Playwright web browser to a URL",
|
||||
"playwright-browser_navigate_back": "Navigate back in Playwright web browser",
|
||||
"playwright-browser_navigate_forward": "Navigate forward in Playwright web browser",
|
||||
"playwright-browser_click": "Click element in Playwright web browser",
|
||||
"playwright-browser_take_screenshot": "Take screenshot of Playwright web browser",
|
||||
"playwright-browser_type": "Type in Playwright web browser",
|
||||
"playwright-browser_wait_for": "Wait for text to appear/disappear in Playwright web browser",
|
||||
"playwright-browser_evaluate": "Run JavaScript in Playwright web browser",
|
||||
"playwright-browser_snapshot": "Take snapshot of page in Playwright web browser",
|
||||
"playwright-browser_resize": "Resize Playwright web browser window",
|
||||
"playwright-browser_close": "Close Playwright web browser",
|
||||
"playwright-browser_press_key": "Press key in Playwright web browser",
|
||||
"playwright-browser_select_option": "Select option in Playwright web browser",
|
||||
"playwright-browser_handle_dialog": "Interact with dialog in Playwright web browser",
|
||||
"playwright-browser_console_messages": "Get console messages from Playwright web browser",
|
||||
"playwright-browser_drag": "Drag mouse between elements in Playwright web browser",
|
||||
"playwright-browser_file_upload": "Upload file in Playwright web browser",
|
||||
"playwright-browser_hover": "Hover mouse over element in Playwright web browser",
|
||||
"playwright-browser_network_requests": "Get network requests from Playwright web browser",
|
||||
|
||||
// GitHub MCP server common tools
|
||||
"github-mcp-server-get_file_contents": "Get file contents from GitHub",
|
||||
"github-mcp-server-get_pull_request": "Get pull request from GitHub",
|
||||
"github-mcp-server-get_issue": "Get issue from GitHub",
|
||||
"github-mcp-server-get_pull_request_files": "Get pull request changed files from GitHub",
|
||||
"github-mcp-server-list_pull_requests": "List pull requests on GitHub",
|
||||
"github-mcp-server-list_branches": "List branches on GitHub",
|
||||
"github-mcp-server-get_pull_request_diff": "Get pull request diff from GitHub",
|
||||
"github-mcp-server-get_pull_request_comments": "Get pull request comments from GitHub",
|
||||
"github-mcp-server-get_commit": "Get commit from GitHub",
|
||||
"github-mcp-server-search_repositories": "Search repositories on GitHub",
|
||||
"github-mcp-server-search_code": "Search code on GitHub",
|
||||
"github-mcp-server-get_issue_comments": "Get issue comments from GitHub",
|
||||
"github-mcp-server-list_issues": "List issues on GitHub",
|
||||
"github-mcp-server-search_pull_requests": "Search pull requests on GitHub",
|
||||
"github-mcp-server-list_commits": "List commits on GitHub",
|
||||
"github-mcp-server-get_pull_request_status": "Get pull request status from GitHub",
|
||||
"github-mcp-server-search_issues": "Search issues on GitHub",
|
||||
"github-mcp-server-get_pull_request_reviews": "Get pull request reviews from GitHub",
|
||||
"github-mcp-server-download_workflow_run_artifact": "Download GitHub Actions workflow run artifact",
|
||||
"github-mcp-server-get_job_logs": "Get GitHub Actions job logs",
|
||||
"github-mcp-server-get_workflow_run": "Get GitHub Actions workflow run",
|
||||
"github-mcp-server-get_workflow_run_logs": "Get GitHub Actions workflow run logs",
|
||||
"github-mcp-server-get_workflow_run_usage": "Get GitHub Actions workflow usage",
|
||||
"github-mcp-server-list_workflow_jobs": "List GitHub Actions workflow jobs",
|
||||
"github-mcp-server-list_workflow_run_artifacts": "List GitHub Actions workflow run artifacts",
|
||||
"github-mcp-server-list_workflow_runs": "List GitHub Actions workflow runs",
|
||||
"github-mcp-server-list_workflows": "List GitHub Actions workflows",
|
||||
}
|
||||
|
||||
func renderGenericToolCall(w io.Writer, cs *iostreams.ColorScheme, name string) {
|
||||
genericToolCallNamesToTitles := map[string]string{
|
||||
// Custom tools, the GitHub UI doesn't currently have these.
|
||||
"codeql_checker": "Run CodeQL analysis",
|
||||
|
||||
// Playwright tools.
|
||||
"playwright-browser_navigate": "Navigate Playwright web browser to a URL",
|
||||
"playwright-browser_navigate_back": "Navigate back in Playwright web browser",
|
||||
"playwright-browser_navigate_forward": "Navigate forward in Playwright web browser",
|
||||
"playwright-browser_click": "Click element in Playwright web browser",
|
||||
"playwright-browser_take_screenshot": "Take screenshot of Playwright web browser",
|
||||
"playwright-browser_type": "Type in Playwright web browser",
|
||||
"playwright-browser_wait_for": "Wait for text to appear/disappear in Playwright web browser",
|
||||
"playwright-browser_evaluate": "Run JavaScript in Playwright web browser",
|
||||
"playwright-browser_snapshot": "Take snapshot of page in Playwright web browser",
|
||||
"playwright-browser_resize": "Resize Playwright web browser window",
|
||||
"playwright-browser_close": "Close Playwright web browser",
|
||||
"playwright-browser_press_key": "Press key in Playwright web browser",
|
||||
"playwright-browser_select_option": "Select option in Playwright web browser",
|
||||
"playwright-browser_handle_dialog": "Interact with dialog in Playwright web browser",
|
||||
"playwright-browser_console_messages": "Get console messages from Playwright web browser",
|
||||
"playwright-browser_drag": "Drag mouse between elements in Playwright web browser",
|
||||
"playwright-browser_file_upload": "Upload file in Playwright web browser",
|
||||
"playwright-browser_hover": "Hover mouse over element in Playwright web browser",
|
||||
"playwright-browser_network_requests": "Get network requests from Playwright web browser",
|
||||
|
||||
// GitHub MCP server common tools
|
||||
"github-mcp-server-get_file_contents": "Get file contents from GitHub",
|
||||
"github-mcp-server-get_pull_request": "Get pull request from GitHub",
|
||||
"github-mcp-server-get_issue": "Get issue from GitHub",
|
||||
"github-mcp-server-get_pull_request_files": "Get pull request changed files from GitHub",
|
||||
"github-mcp-server-list_pull_requests": "List pull requests on GitHub",
|
||||
"github-mcp-server-list_branches": "List branches on GitHub",
|
||||
"github-mcp-server-get_pull_request_diff": "Get pull request diff from GitHub",
|
||||
"github-mcp-server-get_pull_request_comments": "Get pull request comments from GitHub",
|
||||
"github-mcp-server-get_commit": "Get commit from GitHub",
|
||||
"github-mcp-server-search_repositories": "Search repositories on GitHub",
|
||||
"github-mcp-server-search_code": "Search code on GitHub",
|
||||
"github-mcp-server-get_issue_comments": "Get issue comments from GitHub",
|
||||
"github-mcp-server-list_issues": "List issues on GitHub",
|
||||
"github-mcp-server-search_pull_requests": "Search pull requests on GitHub",
|
||||
"github-mcp-server-list_commits": "List commits on GitHub",
|
||||
"github-mcp-server-get_pull_request_status": "Get pull request status from GitHub",
|
||||
"github-mcp-server-search_issues": "Search issues on GitHub",
|
||||
"github-mcp-server-get_pull_request_reviews": "Get pull request reviews from GitHub",
|
||||
"github-mcp-server-download_workflow_run_artifact": "Download GitHub Actions workflow run artifact",
|
||||
"github-mcp-server-get_job_logs": "Get GitHub Actions job logs",
|
||||
"github-mcp-server-get_workflow_run": "Get GitHub Actions workflow run",
|
||||
"github-mcp-server-get_workflow_run_logs": "Get GitHub Actions workflow run logs",
|
||||
"github-mcp-server-get_workflow_run_usage": "Get GitHub Actions workflow usage",
|
||||
"github-mcp-server-list_workflow_jobs": "List GitHub Actions workflow jobs",
|
||||
"github-mcp-server-list_workflow_run_artifacts": "List GitHub Actions workflow run artifacts",
|
||||
"github-mcp-server-list_workflow_runs": "List GitHub Actions workflow runs",
|
||||
"github-mcp-server-list_workflows": "List GitHub Actions workflows",
|
||||
}
|
||||
|
||||
toolName, ok := genericToolCallNamesToTitles[name]
|
||||
if !ok {
|
||||
toolName = fmt.Sprintf("Call to %s", name)
|
||||
|
|
|
|||
|
|
@ -13,19 +13,26 @@ import (
|
|||
|
||||
func TestFollow(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
log string
|
||||
want string
|
||||
name string
|
||||
log string
|
||||
wantStdoutFile string
|
||||
wantStderrFile string
|
||||
}{
|
||||
{
|
||||
name: "sample log 1",
|
||||
log: "testdata/log-1-input.txt",
|
||||
want: "testdata/log-1-want.txt",
|
||||
name: "sample log 1",
|
||||
log: "testdata/log-1-input.txt",
|
||||
wantStdoutFile: "testdata/log-1-want.txt",
|
||||
},
|
||||
{
|
||||
name: "sample log 2",
|
||||
log: "testdata/log-2-input.txt",
|
||||
want: "testdata/log-2-want.txt",
|
||||
name: "sample log 2",
|
||||
log: "testdata/log-2-input.txt",
|
||||
wantStdoutFile: "testdata/log-2-want.txt",
|
||||
},
|
||||
{
|
||||
name: "sample log 3 (tolerant parse failures)",
|
||||
log: "testdata/log-3-synthetic-failures-input.txt",
|
||||
wantStdoutFile: "testdata/log-3-synthetic-failures-want.txt",
|
||||
wantStderrFile: "testdata/log-3-synthetic-failures-want-stderr.txt",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +41,7 @@ func TestFollow(t *testing.T) {
|
|||
raw, err := os.ReadFile(tt.log)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete all the `/r` to make the tests OS-agnostic.
|
||||
// Normalize CRLF to LF to make the tests OS-agnostic.
|
||||
raw = []byte(strings.ReplaceAll(string(raw), "\r\n", "\n"))
|
||||
|
||||
lines := slices.DeleteFunc(strings.Split(string(raw), "\n"), func(line string) bool {
|
||||
|
|
@ -50,7 +57,7 @@ func TestFollow(t *testing.T) {
|
|||
return []byte(strings.Join(lines[0:hits], "\n\n")), nil
|
||||
}
|
||||
|
||||
ios, _, stdout, _ := iostreams.Test()
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
|
||||
err = NewLogRenderer().Follow(fetcher, stdout, ios)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -58,15 +65,31 @@ func TestFollow(t *testing.T) {
|
|||
// Handy note for updating the testdata files when they change:
|
||||
// ext := filepath.Ext(tt.log)
|
||||
// stripped := strings.TrimSuffix(tt.log, ext)
|
||||
// os.WriteFile(stripped+".want"+ext, stdout.Bytes(), 0644)
|
||||
// stripped = strings.TrimSuffix(stripped, "-input")
|
||||
// os.WriteFile(stripped+"-want"+ext, stdout.Bytes(), 0644)
|
||||
// if tt.wantStderrFile != "" {
|
||||
// os.WriteFile(stripped+"-want-stderr"+ext, stderr.Bytes(), 0644)
|
||||
// }
|
||||
|
||||
want, err := os.ReadFile(tt.want)
|
||||
wantStdout, err := os.ReadFile(tt.wantStdoutFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete all the `/r` to make the tests OS-agnostic.
|
||||
want = []byte(strings.ReplaceAll(string(want), "\r\n", "\n"))
|
||||
// Normalize CRLF to LF to make the tests OS-agnostic.
|
||||
wantStdout = []byte(strings.ReplaceAll(string(wantStdout), "\r\n", "\n"))
|
||||
|
||||
assert.Equal(t, string(want), stdout.String())
|
||||
assert.Equal(t, string(wantStdout), stdout.String())
|
||||
|
||||
if tt.wantStderrFile != "" {
|
||||
wantStderr, err := os.ReadFile(tt.wantStderrFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Normalize CRLF to LF to make the tests OS-agnostic.
|
||||
wantStderr = []byte(strings.ReplaceAll(string(wantStderr), "\r\n", "\n"))
|
||||
|
||||
assert.Equal(t, string(wantStderr), stderr.String())
|
||||
} else {
|
||||
require.Empty(t, stderr, "expected no stderr output")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ View repository
|
|||
README.md
|
||||
|
||||
Bash: Check current directory and list all files
|
||||
cd /home/runner/work/foo/foo && pwd && ls -la
|
||||
$ cd /home/runner/work/foo/foo && pwd && ls -la
|
||||
/home/runner/work/foo/foo
|
||||
total 20
|
||||
drwxr-xr-x 4 runner docker 4096 Sep 9 10:17 .
|
||||
|
|
@ -36,8 +36,8 @@ View README.md
|
|||
# foo
|
||||
|
||||
Bash: Check if there are any existing scripts or build files
|
||||
cd /home/runner/work/foo/foo && find . -name "*.sh" -o -name "Makefile" -o -
|
||||
name "package.json" -o -name "*.yml" -o -name "*.yaml"
|
||||
$ cd /home/runner/work/foo/foo && find . -name "*.sh" -o -name "Makefile" -
|
||||
o -name "package.json" -o -name "*.yml" -o -name "*.yaml"
|
||||
./.github/workflows/upload-artifact.yml
|
||||
./.github/workflows/manual-dispatch.yml
|
||||
./.github/workflows/cache.yml
|
||||
|
|
@ -195,7 +195,7 @@ Create: os_info.sh
|
|||
Now let me make the script executable and test it:
|
||||
|
||||
Bash: Make script executable and test it
|
||||
cd /home/runner/work/foo/foo && chmod +x os_info.sh && ./os_info.sh
|
||||
$ cd /home/runner/work/foo/foo && chmod +x os_info.sh && ./os_info.sh
|
||||
==================================================
|
||||
OPERATING SYSTEM INFORMATION
|
||||
==================================================
|
||||
|
|
@ -270,7 +270,7 @@ diff --git a/home/runner/work/foo/foo/README.md
|
|||
Let me verify the changes look good:
|
||||
|
||||
Bash: Check file permissions and final status
|
||||
cd /home/runner/work/foo/foo && ls -la os_info.sh && echo "---" && git
|
||||
$ cd /home/runner/work/foo/foo && ls -la os_info.sh && echo "---" && git
|
||||
status
|
||||
-rwxr-xr-x 1 runner docker 2139 Sep 9 10:18 os_info.sh
|
||||
---
|
||||
|
|
@ -323,7 +323,7 @@ Perfect! Let me test the script one more time to make sure everything is
|
|||
working correctly and commit the changes:
|
||||
|
||||
Bash: Final test of the script
|
||||
cd /home/runner/work/foo/foo && echo "Testing script execution:" && .
|
||||
$ cd /home/runner/work/foo/foo && echo "Testing script execution:" && .
|
||||
/os_info.sh | head -10
|
||||
Testing script execution:
|
||||
==================================================
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ I'll help you create a .yaml file with random fields in the repository. Let
|
|||
and then create a plan.
|
||||
|
||||
Bash: Find documentation and config files to understand the repository
|
||||
cd /home/runner/work/foo/foo && find . -type f -name "*.md" -o -name "*.
|
||||
$ cd /home/runner/work/foo/foo && find . -type f -name "*.md" -o -name "*.
|
||||
txt" -o -name "*.json" -o -name "*.yaml" -o -name "*.yml" | head -20
|
||||
./README.md
|
||||
./.github/workflows/upload-artifact.yml
|
||||
|
|
@ -23,7 +23,7 @@ View repository
|
|||
README.md
|
||||
|
||||
Bash: List all files in the root directory
|
||||
cd /home/runner/work/foo/foo && ls -la
|
||||
$ cd /home/runner/work/foo/foo && ls -la
|
||||
total 20
|
||||
drwxr-xr-x 4 runner runner 4096 Sep 11 13:35 .
|
||||
drwxr-xr-x 3 runner runner 4096 Sep 11 13:34 ..
|
||||
|
|
@ -37,7 +37,7 @@ View README.md
|
|||
# foo
|
||||
|
||||
Bash: Check git status to understand current state
|
||||
cd /home/runner/work/foo/foo && git --no-pager status
|
||||
$ cd /home/runner/work/foo/foo && git --no-pager status
|
||||
On branch copilot/fix-5b1c2fcb-9248-492c-84fa-04a3e0304d25
|
||||
Your branch is up to date with 'origin/copilot/fix-5b1c2fcb-9248-492c-84fa-
|
||||
04a3e0304d25'.
|
||||
|
|
@ -197,13 +197,13 @@ Now let me verify that the YAML file is valid by testing it with a YAML
|
|||
parser:
|
||||
|
||||
Bash: Validate the YAML file syntax
|
||||
cd /home/runner/work/foo/foo && python3 -c "import yaml; yaml.
|
||||
$ cd /home/runner/work/foo/foo && python3 -c "import yaml; yaml.
|
||||
safe_load(open('sample-data.yaml', 'r')); print('YAML file is valid!')"
|
||||
|
||||
<exited with exit code 0>
|
||||
|
||||
Bash: Validate YAML and show structure
|
||||
cd /home/runner/work/foo/foo && python3 -c "import yaml; data = yaml.
|
||||
$ cd /home/runner/work/foo/foo && python3 -c "import yaml; data = yaml.
|
||||
safe_load(open('sample-data.yaml', 'r')); print('YAML file is valid!');
|
||||
print(f'Loaded {len(data)} top-level keys: {list(data.keys())}')"
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ cd /home/runner/work/foo/foo && python3 -c "import yaml; data = yaml.
|
|||
Let me try a different approach to verify the YAML:
|
||||
|
||||
Bash: Detailed YAML validation with content verification
|
||||
cd /home/runner/work/foo/foo && python3 -c "
|
||||
$ cd /home/runner/work/foo/foo && python3 -c "
|
||||
import yaml
|
||||
try:
|
||||
with open('sample-data.yaml', 'r') as f:
|
||||
|
|
@ -237,7 +237,7 @@ cd /home/runner/work/foo/foo && python3 -c "
|
|||
Perfect! Let me also check the file contents and verify the current status:
|
||||
|
||||
Bash: Check git status to see the new file
|
||||
cd /home/runner/work/foo/foo && git --no-pager status
|
||||
$ cd /home/runner/work/foo/foo && git --no-pager status
|
||||
On branch copilot/fix-5b1c2fcb-9248-492c-84fa-04a3e0304d25
|
||||
Your branch is up to date with 'origin/copilot/fix-5b1c2fcb-9248-492c-84fa-
|
||||
04a3e0304d25'.
|
||||
|
|
|
|||
27
pkg/cmd/agent-task/shared/testdata/log-3-input.txt
vendored
Normal file
27
pkg/cmd/agent-task/shared/testdata/log-3-input.txt
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
data: {"id": "bad1", "object": "chat.completion.chunk", "choices": [ { "delta": { "tool_calls": [ { "function": { "name": "view", "arguments": "{bad json" } } ] } } ] }
|
||||
|
||||
data: {"id":"v1","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line\nINSIDE A VIEW CALL","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md\"}"},"id":"tc1","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"v1b","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md\"}"},"id":"tc1b","index":0}]}],"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"v1","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line\nINSIDE A VIEW CALL","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md"},"id":"tc1","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"t1","object":"chat.completion.chunk","choices":[{"delta":{"content":"THINK","tool_calls":[{"function":{"name":"think","arguments":"{\"thought\":123"},"id":"tc2","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"t2","object":"chat.completion.chunk","choices":[{"delta":{"content":"A valid thought to render.","reasoning_text":"Interim reasoning that should show as raw markdown.","tool_calls":[{"function":{"name":"think","arguments":"{\"thought\":\"A valid thought to render.\"}"},"id":"tc3","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"rp1","object":"chat.completion.chunk","choices":[{"delta":{"content":"RP","tool_calls":[{"function":{"name":"report_progress","arguments":"{\"commitMessage\": 5"},"id":"tc4","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"rp2","object":"chat.completion.chunk","choices":[{"delta":{"content":"not-json","tool_calls":[{"function":{"name":"report_progress","arguments":"{\"commitMessage\":\"Valid commit msg\"}"},"id":"tc5","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"c1","object":"chat.completion.chunk","choices":[{"delta":{"content":"CREATE","tool_calls":[{"function":{"name":"create","arguments":"{\"path\":\"/abs/path/file.txt\""},"id":"tc6","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"c2","object":"chat.completion.chunk","choices":[{"delta":{"content":"CREATE2","tool_calls":[{"function":{"name":"create","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/new.txt\",\"file_text\":\"hello world\"}"},"id":"tc7","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"sr1","object":"chat.completion.chunk","choices":[{"delta":{"content":"SR","tool_calls":[{"function":{"name":"str_replace","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/file.diff"},"id":"tc8","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"sr2","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line","tool_calls":[{"function":{"name":"str_replace","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/file.diff\"}"},"id":"tc9","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"u1","object":"chat.completion.chunk","choices":[{"delta":{"content":"{\"foo\":1}","tool_calls":[{"function":{"name":"mystery_tool","arguments":"{\"bar\":2}"},"id":"tc10","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"end","object":"chat.completion.chunk","choices":[{"delta":{"content":"","tool_calls":[],"role":"assistant"},"finish_reason":"stop","index":0}]}
|
||||
27
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-input.txt
vendored
Normal file
27
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-input.txt
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
data: {"id": "bad1", "object": "chat.completion.chunk", "choices": [ { "delta": { "tool_calls": [ { "function": { "name": "view", "arguments": "{bad json" } } ] } } ] }
|
||||
|
||||
data: {"id":"v1","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line\nINSIDE A VIEW CALL","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md\"}"},"id":"tc1","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"v1b","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md\"}"},"id":"tc1b","index":0}]}],"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"v1","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line\nINSIDE A VIEW CALL","tool_calls":[{"function":{"name":"view","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/README.md"},"id":"tc1","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"t1","object":"chat.completion.chunk","choices":[{"delta":{"content":"THINK","tool_calls":[{"function":{"name":"think","arguments":"{\"thought\":123"},"id":"tc2","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"t2","object":"chat.completion.chunk","choices":[{"delta":{"content":"A valid thought to render.","reasoning_text":"Interim reasoning that should show as raw markdown.","tool_calls":[{"function":{"name":"think","arguments":"{\"thought\":\"A valid thought to render.\"}"},"id":"tc3","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"rp1","object":"chat.completion.chunk","choices":[{"delta":{"content":"RP","tool_calls":[{"function":{"name":"report_progress","arguments":"{\"commitMessage\": 5"},"id":"tc4","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"rp2","object":"chat.completion.chunk","choices":[{"delta":{"content":"not-json","tool_calls":[{"function":{"name":"report_progress","arguments":"{\"commitMessage\":\"Valid commit msg\"}"},"id":"tc5","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"c1","object":"chat.completion.chunk","choices":[{"delta":{"content":"CREATE","tool_calls":[{"function":{"name":"create","arguments":"{\"path\":\"/abs/path/file.txt\""},"id":"tc6","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"c2","object":"chat.completion.chunk","choices":[{"delta":{"content":"CREATE2","tool_calls":[{"function":{"name":"create","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/new.txt\",\"file_text\":\"hello world\"}"},"id":"tc7","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"sr1","object":"chat.completion.chunk","choices":[{"delta":{"content":"SR","tool_calls":[{"function":{"name":"str_replace","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/file.diff"},"id":"tc8","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"sr2","object":"chat.completion.chunk","choices":[{"delta":{"content":"@@ -1,2 +1,2 @@\n-old line\n+new line\nunchanged line","tool_calls":[{"function":{"name":"str_replace","arguments":"{\"path\":\"/home/runner/work/repo/owner/repo/file.diff\"}"},"id":"tc9","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"u1","object":"chat.completion.chunk","choices":[{"delta":{"content":"{\"foo\":1}","tool_calls":[{"function":{"name":"mystery_tool","arguments":"{\"bar\":2}"},"id":"tc10","index":0}]},"finish_reason":"tool_calls","index":0}]}
|
||||
|
||||
data: {"id":"end","object":"chat.completion.chunk","choices":[{"delta":{"content":"","tool_calls":[],"role":"assistant"},"finish_reason":"stop","index":0}]}
|
||||
10
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-want-stderr.txt
vendored
Normal file
10
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-want-stderr.txt
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
failed to parse 'view' tool call arguments: unexpected end of JSON input
|
||||
|
||||
failed to parse 'think' tool call arguments: unexpected end of JSON input
|
||||
|
||||
failed to parse 'report_progress' tool call arguments: unexpected end of JSON input
|
||||
|
||||
failed to parse 'create' tool call arguments: unexpected end of JSON input
|
||||
|
||||
failed to parse 'str_replace' tool call arguments: unexpected end of JSON input
|
||||
39
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-want.txt
vendored
Normal file
39
pkg/cmd/agent-task/shared/testdata/log-3-synthetic-failures-want.txt
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
View repo/README.md
|
||||
|
||||
old line
|
||||
new line
|
||||
unchanged line
|
||||
INSIDE A VIEW CALL
|
||||
|
||||
|
||||
Interim reasoning that should show as raw markdown.
|
||||
|
||||
Thought
|
||||
|
||||
A valid thought to render.
|
||||
|
||||
Progress update: Valid commit msg
|
||||
Create: repo/new.txt
|
||||
hello world
|
||||
|
||||
Edit: repo/file.diff
|
||||
@@ -1,2 +1,2 @@
|
||||
-old line
|
||||
+new line
|
||||
unchanged line
|
||||
|
||||
Call to mystery_tool
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"foo": 1
|
||||
}
|
||||
|
||||
|
||||
Input:
|
||||
|
||||
{
|
||||
"bar": 2
|
||||
}
|
||||
|
||||
39
pkg/cmd/agent-task/shared/testdata/log-3-want.txt
vendored
Normal file
39
pkg/cmd/agent-task/shared/testdata/log-3-want.txt
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
View repo/README.md
|
||||
|
||||
old line
|
||||
new line
|
||||
unchanged line
|
||||
INSIDE A VIEW CALL
|
||||
|
||||
|
||||
Interim reasoning that should show as raw markdown.
|
||||
|
||||
Thought
|
||||
|
||||
A valid thought to render.
|
||||
|
||||
Progress update: Valid commit msg
|
||||
Create: repo/new.txt
|
||||
hello world
|
||||
|
||||
Edit: repo/file.diff
|
||||
@@ -1,2 +1,2 @@
|
||||
-old line
|
||||
+new line
|
||||
unchanged line
|
||||
|
||||
Call to mystery_tool
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"foo": 1
|
||||
}
|
||||
|
||||
|
||||
Input:
|
||||
|
||||
{
|
||||
"bar": 2
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +325,12 @@ func printLogs(opts *ViewOptions, capiClient capi.CapiClient, sessionID string)
|
|||
|
||||
renderer := opts.LogRenderer()
|
||||
|
||||
if err := opts.IO.StartPager(); err == nil {
|
||||
defer opts.IO.StopPager()
|
||||
} else {
|
||||
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
|
||||
}
|
||||
|
||||
if opts.Follow {
|
||||
var called bool
|
||||
fetcher := func() ([]byte, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue