claudetool/codereview: new package extracted from claudetool

Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/claudetool/codereview.go b/claudetool/codereview/codereview.go
similarity index 98%
rename from claudetool/codereview.go
rename to claudetool/codereview/codereview.go
index 2a9f9ea..bf118ee 100644
--- a/claudetool/codereview.go
+++ b/claudetool/codereview/codereview.go
@@ -1,4 +1,4 @@
-package claudetool
+package codereview
 
 import (
 	"bytes"
@@ -9,6 +9,8 @@
 	"os/exec"
 	"path/filepath"
 	"strings"
+
+	"sketch.dev/claudetool"
 )
 
 // A CodeReviewer manages quality checks.
@@ -39,7 +41,7 @@
 		return nil, fmt.Errorf("NewCodeReviewer: initialCommit must be non-empty")
 	}
 	// Confirm that root is in fact the git repo root.
-	root, err := findRepoRoot(r.repoRoot)
+	root, err := claudetool.FindRepoRoot(r.repoRoot)
 	if err != nil {
 		return nil, err
 	}
@@ -106,7 +108,7 @@
 			slog.WarnContext(ctx, "CodeReviewer.Autoformat unable to get file content at head", "file", file, "err", err)
 			continue
 		}
-		if isAutogeneratedGoFile(code) { // leave autogenerated files alone
+		if claudetool.IsAutogeneratedGoFile(code) { // leave autogenerated files alone
 			continue
 		}
 		onDisk, err := os.ReadFile(file)
diff --git a/claudetool/codereview_test.go b/claudetool/codereview/codereview_test.go
similarity index 99%
rename from claudetool/codereview_test.go
rename to claudetool/codereview/codereview_test.go
index e189584..29e05ec 100644
--- a/claudetool/codereview_test.go
+++ b/claudetool/codereview/codereview_test.go
@@ -1,4 +1,4 @@
-package claudetool
+package codereview
 
 import (
 	"bytes"
diff --git a/claudetool/differential.go b/claudetool/codereview/differential.go
similarity index 99%
rename from claudetool/differential.go
rename to claudetool/codereview/differential.go
index 1d7ff6e..6aa8dfe 100644
--- a/claudetool/differential.go
+++ b/claudetool/codereview/differential.go
@@ -1,4 +1,4 @@
-package claudetool
+package codereview
 
 import (
 	"bytes"
@@ -313,9 +313,7 @@
 // Each line has the format: '/path/to/file.go:448:22-26: unused parameter: path'
 func parseGoplsOutput(root string, output []byte) []GoplsIssue {
 	var issues []GoplsIssue
-	lines := strings.Split(string(output), "\n")
-
-	for _, line := range lines {
+	for line := range strings.Lines(string(output)) {
 		line = strings.TrimSpace(line)
 		if line == "" {
 			continue
@@ -381,8 +379,7 @@
 
 	// Check if output has at least one line that looks like a gopls issue
 	// A gopls issue looks like: '/path/to/file.go:123:45-67: message'
-	lines := strings.Split(string(output), "\n")
-	for _, line := range lines {
+	for line := range strings.Lines(string(output)) {
 		line = strings.TrimSpace(line)
 		if line == "" {
 			continue
diff --git a/claudetool/llm_codereview_prompt.txt b/claudetool/codereview/llm_codereview_prompt.txt
similarity index 100%
rename from claudetool/llm_codereview_prompt.txt
rename to claudetool/codereview/llm_codereview_prompt.txt
diff --git a/claudetool/llm_review.go b/claudetool/codereview/llm_review.go
similarity index 98%
rename from claudetool/llm_review.go
rename to claudetool/codereview/llm_review.go
index 09d41b9..541ee52 100644
--- a/claudetool/llm_review.go
+++ b/claudetool/codereview/llm_review.go
@@ -1,4 +1,4 @@
-package claudetool
+package codereview
 
 import (
 	"context"
diff --git a/claudetool/testdata/add_skipped_test.txtar b/claudetool/codereview/testdata/add_skipped_test.txtar
similarity index 100%
rename from claudetool/testdata/add_skipped_test.txtar
rename to claudetool/codereview/testdata/add_skipped_test.txtar
diff --git a/claudetool/testdata/basic_gofmt.txtar b/claudetool/codereview/testdata/basic_gofmt.txtar
similarity index 100%
rename from claudetool/testdata/basic_gofmt.txtar
rename to claudetool/codereview/testdata/basic_gofmt.txtar
diff --git a/claudetool/testdata/empty_testdir_add_file.txtar b/claudetool/codereview/testdata/empty_testdir_add_file.txtar
similarity index 100%
rename from claudetool/testdata/empty_testdir_add_file.txtar
rename to claudetool/codereview/testdata/empty_testdir_add_file.txtar
diff --git a/claudetool/testdata/failing_to_failing.txtar b/claudetool/codereview/testdata/failing_to_failing.txtar
similarity index 100%
rename from claudetool/testdata/failing_to_failing.txtar
rename to claudetool/codereview/testdata/failing_to_failing.txtar
diff --git a/claudetool/testdata/fmt_hierarchy.txtar b/claudetool/codereview/testdata/fmt_hierarchy.txtar
similarity index 100%
rename from claudetool/testdata/fmt_hierarchy.txtar
rename to claudetool/codereview/testdata/fmt_hierarchy.txtar
diff --git a/claudetool/testdata/gopls_issue_unchanged.txtar b/claudetool/codereview/testdata/gopls_issue_unchanged.txtar
similarity index 100%
rename from claudetool/testdata/gopls_issue_unchanged.txtar
rename to claudetool/codereview/testdata/gopls_issue_unchanged.txtar
diff --git a/claudetool/testdata/gopls_issues.txtar b/claudetool/codereview/testdata/gopls_issues.txtar
similarity index 100%
rename from claudetool/testdata/gopls_issues.txtar
rename to claudetool/codereview/testdata/gopls_issues.txtar
diff --git a/claudetool/testdata/gopls_line_number_changed.txtar b/claudetool/codereview/testdata/gopls_line_number_changed.txtar
similarity index 100%
rename from claudetool/testdata/gopls_line_number_changed.txtar
rename to claudetool/codereview/testdata/gopls_line_number_changed.txtar
diff --git a/claudetool/testdata/gopls_new_issue_with_existing.txtar b/claudetool/codereview/testdata/gopls_new_issue_with_existing.txtar
similarity index 100%
rename from claudetool/testdata/gopls_new_issue_with_existing.txtar
rename to claudetool/codereview/testdata/gopls_new_issue_with_existing.txtar
diff --git a/claudetool/testdata/mark_test_skipped.txtar b/claudetool/codereview/testdata/mark_test_skipped.txtar
similarity index 100%
rename from claudetool/testdata/mark_test_skipped.txtar
rename to claudetool/codereview/testdata/mark_test_skipped.txtar
diff --git a/claudetool/testdata/multi_commit_review.txtar b/claudetool/codereview/testdata/multi_commit_review.txtar
similarity index 100%
rename from claudetool/testdata/multi_commit_review.txtar
rename to claudetool/codereview/testdata/multi_commit_review.txtar
diff --git a/claudetool/testdata/multiple_format_issues.txtar b/claudetool/codereview/testdata/multiple_format_issues.txtar
similarity index 100%
rename from claudetool/testdata/multiple_format_issues.txtar
rename to claudetool/codereview/testdata/multiple_format_issues.txtar
diff --git a/claudetool/testdata/new_test_build_error.txtar b/claudetool/codereview/testdata/new_test_build_error.txtar
similarity index 100%
rename from claudetool/testdata/new_test_build_error.txtar
rename to claudetool/codereview/testdata/new_test_build_error.txtar
diff --git a/claudetool/testdata/no_fmt_autogenerated.txtar b/claudetool/codereview/testdata/no_fmt_autogenerated.txtar
similarity index 100%
rename from claudetool/testdata/no_fmt_autogenerated.txtar
rename to claudetool/codereview/testdata/no_fmt_autogenerated.txtar
diff --git a/claudetool/testdata/no_tests.txtar b/claudetool/codereview/testdata/no_tests.txtar
similarity index 100%
rename from claudetool/testdata/no_tests.txtar
rename to claudetool/codereview/testdata/no_tests.txtar
diff --git a/claudetool/testdata/no_tests_to_failing_tests.txtar b/claudetool/codereview/testdata/no_tests_to_failing_tests.txtar
similarity index 100%
rename from claudetool/testdata/no_tests_to_failing_tests.txtar
rename to claudetool/codereview/testdata/no_tests_to_failing_tests.txtar
diff --git a/claudetool/testdata/passing_to_build_error.txtar b/claudetool/codereview/testdata/passing_to_build_error.txtar
similarity index 100%
rename from claudetool/testdata/passing_to_build_error.txtar
rename to claudetool/codereview/testdata/passing_to_build_error.txtar
diff --git a/claudetool/testdata/passing_to_failing.txtar b/claudetool/codereview/testdata/passing_to_failing.txtar
similarity index 100%
rename from claudetool/testdata/passing_to_failing.txtar
rename to claudetool/codereview/testdata/passing_to_failing.txtar
diff --git a/claudetool/testdata/passing_to_failing_subdir.txtar b/claudetool/codereview/testdata/passing_to_failing_subdir.txtar
similarity index 100%
rename from claudetool/testdata/passing_to_failing_subdir.txtar
rename to claudetool/codereview/testdata/passing_to_failing_subdir.txtar
diff --git a/claudetool/testdata/related_files_cooccurrence.txtar b/claudetool/codereview/testdata/related_files_cooccurrence.txtar
similarity index 100%
rename from claudetool/testdata/related_files_cooccurrence.txtar
rename to claudetool/codereview/testdata/related_files_cooccurrence.txtar
diff --git a/claudetool/testdata/skipped_to_build_error.txtar b/claudetool/codereview/testdata/skipped_to_build_error.txtar
similarity index 100%
rename from claudetool/testdata/skipped_to_build_error.txtar
rename to claudetool/codereview/testdata/skipped_to_build_error.txtar
diff --git a/claudetool/testdata/skipped_to_failing.txtar b/claudetool/codereview/testdata/skipped_to_failing.txtar
similarity index 100%
rename from claudetool/testdata/skipped_to_failing.txtar
rename to claudetool/codereview/testdata/skipped_to_failing.txtar
diff --git a/claudetool/testdata/vet_error_test.txtar b/claudetool/codereview/testdata/vet_error_test.txtar
similarity index 100%
rename from claudetool/testdata/vet_error_test.txtar
rename to claudetool/codereview/testdata/vet_error_test.txtar
diff --git a/claudetool/teststatus_string.go b/claudetool/codereview/teststatus_string.go
similarity index 97%
rename from claudetool/teststatus_string.go
rename to claudetool/codereview/teststatus_string.go
index a5b159b..6c9ac90 100644
--- a/claudetool/teststatus_string.go
+++ b/claudetool/codereview/teststatus_string.go
@@ -1,6 +1,6 @@
 // Code generated by "stringer -type=testStatus -trimprefix=testStatus"; DO NOT EDIT.
 
-package claudetool
+package codereview
 
 import "strconv"
 
diff --git a/claudetool/keyword.go b/claudetool/keyword.go
index 8c693be..5d9f155 100644
--- a/claudetool/keyword.go
+++ b/claudetool/keyword.go
@@ -71,8 +71,8 @@
 //go:embed keyword_system_prompt.txt
 var keywordSystemPrompt string
 
-// findRepoRoot attempts to find the git repository root from the current directory
-func findRepoRoot(wd string) (string, error) {
+// FindRepoRoot attempts to find the git repository root from the current directory
+func FindRepoRoot(wd string) (string, error) {
 	cmd := exec.Command("git", "rev-parse", "--show-toplevel")
 	cmd.Dir = wd
 	out, err := cmd.Output()
@@ -89,7 +89,7 @@
 		return "", err
 	}
 	wd := WorkingDir(ctx)
-	root, err := findRepoRoot(wd)
+	root, err := FindRepoRoot(wd)
 	if err == nil {
 		wd = root
 	}
diff --git a/claudetool/patch.go b/claudetool/patch.go
index 0886e66..419e966 100644
--- a/claudetool/patch.go
+++ b/claudetool/patch.go
@@ -127,7 +127,7 @@
 
 	likelyGoFile := strings.HasSuffix(input.Path, ".go")
 
-	autogenerated := likelyGoFile && isAutogeneratedGoFile(orig)
+	autogenerated := likelyGoFile && IsAutogeneratedGoFile(orig)
 	parsed := likelyGoFile && parseGo(orig) != nil
 
 	origStr := string(orig)
@@ -262,7 +262,8 @@
 	return err
 }
 
-func isAutogeneratedGoFile(buf []byte) bool {
+// IsAutogeneratedGoFile reports whether a Go file has markers indicating it was autogenerated.
+func IsAutogeneratedGoFile(buf []byte) bool {
 	for _, sig := range autogeneratedSignals {
 		if bytes.Contains(buf, []byte(sig)) {
 			return true