llm: make Tool.Run return ToolOut
This is preliminary work towards
allowing tools to add additional information.
No functional changes (at least, that's the intent).
diff --git a/claudetool/codereview/codereview_test.go b/claudetool/codereview/codereview_test.go
index f9d240e..4bba851 100644
--- a/claudetool/codereview/codereview_test.go
+++ b/claudetool/codereview/codereview_test.go
@@ -234,10 +234,11 @@
// runDifferentialTest runs the code review tool on the repository and returns the result.
func runDifferentialTest(reviewer *CodeReviewer) (string, error) {
ctx := context.Background()
- result, err := reviewer.Run(ctx, nil)
- if err != nil {
- return "", fmt.Errorf("error running code review: %w", err)
+ toolOut := reviewer.Run(ctx, nil)
+ if toolOut.Error != nil {
+ return "", fmt.Errorf("error running code review: %w", toolOut.Error)
}
+ result := toolOut.LLMContent
// Normalize paths in the result
resultStr := ""
diff --git a/claudetool/codereview/differential.go b/claudetool/codereview/differential.go
index 764f000..6c29d06 100644
--- a/claudetool/codereview/differential.go
+++ b/claudetool/codereview/differential.go
@@ -48,14 +48,14 @@
return spec
}
-func (r *CodeReviewer) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
+func (r *CodeReviewer) Run(ctx context.Context, m json.RawMessage) llm.ToolOut {
// Parse input to get timeout
var input struct {
Timeout string `json:"timeout"`
}
if len(m) > 0 {
if err := json.Unmarshal(m, &input); err != nil {
- return nil, fmt.Errorf("failed to parse input: %w", err)
+ return llm.ErrorfToolOut("failed to parse input: %w", err)
}
}
if input.Timeout == "" {
@@ -65,7 +65,7 @@
// Parse timeout duration
timeout, err := time.ParseDuration(input.Timeout)
if err != nil {
- return nil, fmt.Errorf("invalid timeout duration %q: %w", input.Timeout, err)
+ return llm.ErrorfToolOut("invalid timeout duration %q: %w", input.Timeout, err)
}
// Create timeout context
@@ -76,22 +76,22 @@
// webui/src/web-components/sketch-tool-card.ts (SketchToolCardCodeReview.getStatusIcon)
if err := r.RequireNormalGitState(timeoutCtx); err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to check for normal git state", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
if err := r.RequireNoUncommittedChanges(timeoutCtx); err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to check for uncommitted changes", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
// Check that the current commit is not the initial commit
currentCommit, err := r.CurrentCommit(timeoutCtx)
if err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to get current commit", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
if r.IsInitialCommit(currentCommit) {
slog.DebugContext(ctx, "CodeReviewer.Run: current commit is initial commit, nothing to review")
- return nil, fmt.Errorf("no new commits have been added, nothing to review")
+ return llm.ErrorToolOut(fmt.Errorf("no new commits have been added, nothing to review"))
}
// No matter what failures happen from here out, we will declare this to have been reviewed.
@@ -101,7 +101,7 @@
changedFiles, err := r.changedFiles(timeoutCtx, r.sketchBaseRef, currentCommit)
if err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to get changed files", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
// Prepare to analyze before/after for the impacted files.
@@ -113,7 +113,7 @@
if err != nil {
// TODO: log and skip to stuff that doesn't require packages
slog.DebugContext(ctx, "CodeReviewer.Run: failed to get packages for files", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
allPkgList := slices.Collect(maps.Keys(allPkgs))
@@ -152,7 +152,7 @@
testMsg, err := r.checkTests(timeoutCtx, allPkgList)
if err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to check tests", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
if testMsg != "" {
errorMessages = append(errorMessages, testMsg)
@@ -161,7 +161,7 @@
goplsMsg, err := r.checkGopls(timeoutCtx, changedFiles) // includes vet checks
if err != nil {
slog.DebugContext(ctx, "CodeReviewer.Run: failed to check gopls", "err", err)
- return nil, err
+ return llm.ErrorToolOut(err)
}
if goplsMsg != "" {
errorMessages = append(errorMessages, goplsMsg)
@@ -183,7 +183,7 @@
if buf.Len() == 0 {
buf.WriteString("OK")
}
- return llm.TextContent(buf.String()), nil
+ return llm.ToolOut{LLMContent: llm.TextContent(buf.String())}
}
func (r *CodeReviewer) initializeInitialCommitWorktree(ctx context.Context) error {