-
Notifications
You must be signed in to change notification settings - Fork 127
{tool/claudecode, docs}: add Claude Code toolset #1656
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
6665afc
{tool/claudecode, docs/mkdocs}: add Claude Code toolset
Flash-LHR 91ca004
tool/claudecode: tidy module dependencies
Flash-LHR a120560
tool/claudecode: raise test coverage
Flash-LHR be2b295
tool/claudecode: stabilize and extend tests
Flash-LHR 66744f7
tool/claudecode: cover remaining helper branches
Flash-LHR eeecd8d
tool/claudecode: make ripgrep path test environment independent
Flash-LHR 332aba9
tool/claudecode: cover remaining patch branches
Flash-LHR d982a17
tool/claudecode: cover remaining helper paths
Flash-LHR b15cf0f
tool/claudecode: cover common and read helpers
Flash-LHR 2be7fa3
tool/claudecode: cover search helpers
Flash-LHR ae04f5b
tool/claudecode: cover helper branches for patch checks
Flash-LHR d4a9d66
{tool/claudecode, docs/mkdocs}: adopt focused review fixes
Flash-LHR ed32bcb
tool/claudecode: tighten web search and notebook edge cases
Flash-LHR 385abdc
tool/claudecode: align google search offset semantics
Flash-LHR fd3cd53
docs
Flash-LHR 6f1556c
remove tool search
Flash-LHR 2b4e018
del ask user
Flash-LHR 68413e4
tool/claudecode: add coverage for web fetch and bash branches
Flash-LHR 1a3bcb4
tool/claudecode: add coverage for additional web fetch branches
Flash-LHR d4cbcb6
tool/claudecode: stabilize web fetch coverage tests
Flash-LHR File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // | ||
| // Tencent is pleased to support the open source community by making | ||
| // trpc-agent-go available. | ||
| // | ||
| // Copyright (C) 2025 Tencent. All rights reserved. | ||
| // | ||
| // trpc-agent-go is licensed under the Apache License Version 2.0. | ||
| // | ||
|
|
||
| package claudecode | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strconv" | ||
| "time" | ||
|
|
||
| "github.com/google/uuid" | ||
| "trpc.group/trpc-go/trpc-agent-go/tool" | ||
| "trpc.group/trpc-go/trpc-agent-go/tool/function" | ||
| ) | ||
|
|
||
| func newBashTool(runtime *runtime) (tool.Tool, error) { | ||
| return function.NewFunctionTool( | ||
| func(ctx context.Context, in bashInput) (bashOutput, error) { | ||
| if in.RunInBackground { | ||
| return runBackgroundCommand(runtime, in.Command) | ||
| } | ||
| return runForegroundCommand(ctx, runtime, in) | ||
| }, | ||
| function.WithName(toolBash), | ||
| function.WithDescription(bashDescription()), | ||
| ), nil | ||
| } | ||
|
|
||
| func runForegroundCommand(ctx context.Context, runtime *runtime, in bashInput) (bashOutput, error) { | ||
| timeoutMs := bashTimeout(in.Timeout) | ||
| start := time.Now() | ||
| runCtx, cancel := context.WithTimeout(ctx, time.Duration(timeoutMs)*time.Millisecond) | ||
| defer cancel() | ||
| result, err := runCapturedProcess(runCtx, runtime.currentBaseDir(), nil, "bash", "-lc", in.Command) | ||
| durationMs := time.Since(start).Milliseconds() | ||
| timedOut := errorsIsDeadlineExceeded(runCtx.Err()) | ||
| exitCode := result.ExitCode | ||
| if err != nil && exitCode == 0 { | ||
| if timedOut { | ||
| exitCode = 124 | ||
| } else { | ||
| exitCode = 1 | ||
| } | ||
| } | ||
| stdout := string(result.Stdout) | ||
| stderr := string(result.Stderr) | ||
| return bashOutput{ | ||
| Command: in.Command, | ||
| ExitCode: exitCode, | ||
| Stdout: stdout, | ||
| Stderr: stderr, | ||
| Output: joinOutput(stdout, stderr), | ||
| DurationMs: durationMs, | ||
| TimedOut: timedOut, | ||
| }, nil | ||
| } | ||
|
|
||
| func bashTimeout(timeout *int) int { | ||
| timeoutMs := defaultBashTimeoutMs | ||
| if raw := os.Getenv("BASH_DEFAULT_TIMEOUT_MS"); raw != "" { | ||
| if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 { | ||
| timeoutMs = parsed | ||
| } | ||
| } | ||
| if timeout != nil { | ||
| timeoutMs = *timeout | ||
| } | ||
| if timeoutMs <= 0 { | ||
| timeoutMs = defaultBashTimeoutMs | ||
| } | ||
| if timeoutMs > maxBashTimeoutMs { | ||
| timeoutMs = maxBashTimeoutMs | ||
| } | ||
| return timeoutMs | ||
| } | ||
|
|
||
| func runBackgroundCommand(runtime *runtime, command string) (bashOutput, error) { | ||
| taskID := uuid.NewString() | ||
| outputDir := filepath.Join(os.TempDir(), "trpc-agent-go-claudecode") | ||
| if err := os.MkdirAll(outputDir, 0o755); err != nil { | ||
| return bashOutput{}, err | ||
| } | ||
| outputPath := filepath.Join(outputDir, taskID+".log") | ||
| outputFile, err := os.Create(outputPath) | ||
| if err != nil { | ||
| return bashOutput{}, err | ||
| } | ||
| process, err := startProcess(runtime.currentBaseDir(), nil, outputFile, outputFile, "bash", "-lc", command) | ||
| if err != nil { | ||
| _ = outputFile.Close() | ||
| return bashOutput{}, err | ||
| } | ||
| runtime.taskState.mu.Lock() | ||
| runtime.taskState.tasks[taskID] = &backgroundTask{ | ||
| ID: taskID, | ||
| Command: command, | ||
| Type: toolBash, | ||
| OutputPath: outputPath, | ||
| Process: process, | ||
| Status: "running", | ||
| } | ||
| runtime.taskState.mu.Unlock() | ||
| go func() { | ||
| state, waitErr := process.Wait() | ||
| _ = outputFile.Close() | ||
| runtime.taskState.mu.Lock() | ||
| task := runtime.taskState.tasks[taskID] | ||
| if task != nil && task.Status == "running" { | ||
| task.Status = backgroundTaskStatus(waitErr, state) | ||
| exitCode := backgroundTaskExitCode(waitErr, state) | ||
| task.ExitCode = &exitCode | ||
| } | ||
| runtime.taskState.mu.Unlock() | ||
| }() | ||
| return bashOutput{ | ||
| Command: command, | ||
| ExitCode: 0, | ||
| Output: fmt.Sprintf("Command is running in the background. Read %s later to inspect the output.", outputPath), | ||
| BackgroundTaskID: taskID, | ||
| OutputPath: outputPath, | ||
| }, nil | ||
| } | ||
|
Flash-LHR marked this conversation as resolved.
|
||
|
|
||
| func backgroundTaskStatus(waitErr error, state *os.ProcessState) string { | ||
| if waitErr != nil { | ||
| return "exited" | ||
| } | ||
| if state == nil || !state.Success() { | ||
| return "exited" | ||
| } | ||
| return "completed" | ||
| } | ||
|
|
||
| func backgroundTaskExitCode(waitErr error, state *os.ProcessState) int { | ||
| if state != nil { | ||
| return state.ExitCode() | ||
| } | ||
| if waitErr != nil { | ||
| return 1 | ||
| } | ||
| return 0 | ||
| } | ||
|
|
||
| func errorsIsDeadlineExceeded(err error) bool { | ||
| return err == context.DeadlineExceeded | ||
| } | ||
|
|
||
| func bashDescription() string { | ||
| return fmt.Sprintf(`Execute a local shell command. | ||
|
|
||
| Usage: | ||
| - Use %s for shell-native tasks such as git, build, test, lint, package managers, and project scripts. | ||
| - Prefer dedicated tools when they fit better: use %s to read files, %s to create or overwrite files, %s for targeted text replacements, %s for notebook cell edits, %s for filename search, %s for repository content search, %s to fetch a specific URL, and %s for broad web discovery. | ||
| - NEVER use bash grep or rg for repository search when %s can answer the question. | ||
| - Commands run from the current workspace base directory. | ||
| - Use run_in_background for long-running commands that do not need an immediate result. Inspect them later with %s or stop them with %s. | ||
| - timeout is measured in milliseconds and is capped at %d ms.`, toolBash, toolRead, toolWrite, toolEdit, toolNotebookEdit, toolGlob, toolGrep, toolWebFetch, toolWebSearch, toolGrep, toolTaskOutput, toolTaskStop, maxBashTimeoutMs) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.