From 7f741cac3ddc48ed321c247718d61ad177db1638 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 12 Jun 2026 12:16:23 +0530 Subject: [PATCH 1/3] refactor: replace context.TODO() with context.Background() and harden CI/CD - Replace all context.TODO() calls in production and test code with context.Background() or proper context propagation - Enable shadow and nilness govet linters; re-enable select staticcheck checks (SA4006, S1011, S1034) - Fix CI coverage threshold comment (50% -> 60%) - Extract markdownlint config to .markdownlint-cli2.jsonc - Pin actions/setup-go to SHA in setup-deps and hawk actions - Change setup-deps default branch from dev to main - Add tracking issue references to all t.Skip() calls --- .github/actions/setup-deps/action.yml | 16 ++++++++-------- .github/workflows/ci.yml | 3 +-- .golangci.yml | 5 ----- .markdownlint-cli2.jsonc | 21 +++++++++++++++++++++ cmd/chat_config_save_flow_test.go | 2 +- cmd/chat_model_test.go | 4 ++-- cmd/chat_welcome.go | 4 ++-- cmd/dx.go | 4 ++-- cmd/ocg_live_test.go | 2 +- cmd/path_test.go | 2 +- internal/codegraph/codegraph_cgo.go | 2 +- internal/config/validator.go | 4 ++-- internal/engine/quality_gate.go | 2 +- 13 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 .markdownlint-cli2.jsonc diff --git a/.github/actions/setup-deps/action.yml b/.github/actions/setup-deps/action.yml index b91abce1..777735ef 100644 --- a/.github/actions/setup-deps/action.yml +++ b/.github/actions/setup-deps/action.yml @@ -18,7 +18,7 @@ runs: GH_TOKEN: ${{ inputs.token }} run: | clone_with_retry() { - local repo=$1 dest=$2 branch=${3:-dev} + local repo=$1 dest=$2 branch=${3:-main} for i in 1 2 3; do git clone --depth=1 --branch "$branch" "https://x-access-token:${GH_TOKEN}@github.com/GrayCodeAI/${repo}.git" "$dest" && return 0 echo "Retry $i for $repo..." && sleep $((i * 5)) @@ -26,15 +26,15 @@ runs: echo "Failed to clone $repo after 3 attempts" && return 1 } mkdir -p external - clone_with_retry eyrie external/eyrie dev - clone_with_retry tok external/tok dev - clone_with_retry yaad external/yaad dev - clone_with_retry inspect external/inspect dev - clone_with_retry sight external/sight dev - clone_with_retry trace external/trace dev + clone_with_retry eyrie external/eyrie main + clone_with_retry tok external/tok main + clone_with_retry yaad external/yaad main + clone_with_retry inspect external/inspect main + clone_with_retry sight external/sight main + clone_with_retry trace external/trace main - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: ${{ inputs.go-version }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d62c2932..43e70b15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,7 +159,7 @@ jobs: coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%' | tail -1) echo "Coverage: ${coverage}%" echo "COVERAGE=${coverage}" >> "$GITHUB_ENV" - - name: Coverage threshold (minimum 50%) + - name: Coverage threshold (minimum 60%) run: | if (( $(echo "${COVERAGE} < 60" | bc -l) )); then echo "::error::Coverage ${COVERAGE}% is below minimum 60%" @@ -253,7 +253,6 @@ jobs: - name: Run markdownlint-cli2 run: | npm install -g markdownlint-cli2 - printf '%s\n' '{"config":{"default":true,"line-length":false,"no-inline-html":false,"first-line-h1":false,"no-duplicate-heading":false,"no-emphasis-as-heading":false,"blanks-around-headings":false,"blanks-around-lists":false,"blanks-around-fences":false,"fenced-code-language":false,"table-column-style":false,"no-space-in-emphasis":false,"ol-prefix":false,"link-fragments":false,"blanks-around-tables":false,"table-column-count":false,"single-trailing-newline":false}}' > .markdownlint-cli2.jsonc markdownlint-cli2 '**/*.md' # ------------------------------------------------------------------------- diff --git a/.golangci.yml b/.golangci.yml index cf12174d..f652489e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,9 +21,7 @@ linters: enable-all: true disable: - fieldalignment - - shadow - unusedwrite - - nilness staticcheck: checks: - all @@ -32,14 +30,11 @@ linters: - -ST1020 - -ST1021 - -ST1018 - - -SA4006 - -SA5011 - -SA1012 - -SA2001 - -SA4011 - -S1039 - - -S1011 - - -S1034 - -QF1003 - -QF1011 - -S1008 diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..d62067db --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,21 @@ +{ + "config": { + "default": true, + "line-length": false, + "no-inline-html": false, + "first-line-h1": false, + "no-duplicate-heading": false, + "no-emphasis-as-heading": false, + "blanks-around-headings": false, + "blanks-around-lists": false, + "blanks-around-fences": false, + "fenced-code-language": false, + "table-column-style": false, + "no-space-in-emphasis": false, + "ol-prefix": false, + "link-fragments": false, + "blanks-around-tables": false, + "table-column-count": false, + "single-trailing-newline": false + } +} diff --git a/cmd/chat_config_save_flow_test.go b/cmd/chat_config_save_flow_test.go index 917c7366..d91b3fce 100644 --- a/cmd/chat_config_save_flow_test.go +++ b/cmd/chat_config_save_flow_test.go @@ -109,7 +109,7 @@ func TestConfigGatewaysSelect_AddKeyOpensPaste(t *testing.T) { } } if sel < 0 { - t.Skip("all gateways already have keys in this environment") + t.Skip("all gateways already have keys in this environment") // TODO: https://github.com/GrayCodeAI/hawk/issues/28 } m.configSel = sel next, _ := m.handleConfigGatewaysSelect() diff --git a/cmd/chat_model_test.go b/cmd/chat_model_test.go index 1c4fff13..62a781b0 100644 --- a/cmd/chat_model_test.go +++ b/cmd/chat_model_test.go @@ -150,7 +150,7 @@ func TestChatModel_SlashUnknown(t *testing.T) { } func TestChatModel_ManyCommands(t *testing.T) { - t.Skip("flaky: race condition with global state access") + t.Skip("flaky: race condition with global state access") // TODO: https://github.com/GrayCodeAI/hawk/issues/26 commands := []string{ "/context", "/env", "/hooks", "/stats", "/compact", "/diff", "/branch", "/vim", @@ -224,7 +224,7 @@ func TestChatModel_SlashExport(t *testing.T) { } func TestChatModel_StreamingCommands(t *testing.T) { - t.Skip("flaky: race condition with startStream goroutines") + t.Skip("flaky: race condition with startStream goroutines") // TODO: https://github.com/GrayCodeAI/hawk/issues/27 // These trigger startStream but progRef is nil-safe so they won't panic commands := []string{ "/doctor", "/commit", "/review", diff --git a/cmd/chat_welcome.go b/cmd/chat_welcome.go index 2883a55b..f918f456 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -313,8 +313,8 @@ func envSummaryWithSelection(provider, model string, includeSelection bool) stri func configCommandSummary(settings hawkconfig.Settings) string { _ = settings - provider := displayConfigValue(hawkconfig.ActiveProvider(context.TODO())) - model := displayConfigValue(hawkconfig.ActiveModel(context.TODO())) + provider := displayConfigValue(hawkconfig.ActiveProvider(context.Background())) + model := displayConfigValue(hawkconfig.ActiveModel(context.Background())) return fmt.Sprintf(`Setup (eyrie) /config → paste API key (OS keychain) + pick model diff --git a/cmd/dx.go b/cmd/dx.go index 29eaf055..0c640d9b 100644 --- a/cmd/dx.go +++ b/cmd/dx.go @@ -67,10 +67,10 @@ func doctorOutput(settings hawkconfig.Settings) string { } b.WriteString("\nProvider:\n") b.WriteString(fmt.Sprintf(" Provider: %s\n", effectiveProvider)) - b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(hawkconfig.ActiveProvider(context.TODO())))) + b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(hawkconfig.ActiveProvider(context.Background())))) // Model configured (eyrie provider.json) - effectiveModel := strings.TrimSpace(hawkconfig.ActiveModel(context.TODO())) + effectiveModel := strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" } diff --git a/cmd/ocg_live_test.go b/cmd/ocg_live_test.go index 245825a0..8c88458c 100644 --- a/cmd/ocg_live_test.go +++ b/cmd/ocg_live_test.go @@ -14,7 +14,7 @@ import ( func TestLiveOpenCodeGoMiniMaxM3FullHawkPath(t *testing.T) { if credentials.LookupSecret(context.Background(), "OPENCODEGO_API_KEY") == "" { - t.Skip("OPENCODEGO_API_KEY not configured") + t.Skip("OPENCODEGO_API_KEY not configured") // TODO: https://github.com/GrayCodeAI/hawk/issues/29 } settings, err := loadEffectiveSettings() if err != nil { diff --git a/cmd/path_test.go b/cmd/path_test.go index 63591e4a..7ee853d2 100644 --- a/cmd/path_test.go +++ b/cmd/path_test.go @@ -10,7 +10,7 @@ import ( func TestPathCmdRuns(t *testing.T) { if err := pathCmd.RunE(pathCmd, nil); err == nil { - t.Skip("machine has full developer path setup") + t.Skip("machine has full developer path setup") // TODO: https://github.com/GrayCodeAI/hawk/issues/30 } } diff --git a/internal/codegraph/codegraph_cgo.go b/internal/codegraph/codegraph_cgo.go index d928b40d..6e3ef86f 100644 --- a/internal/codegraph/codegraph_cgo.go +++ b/internal/codegraph/codegraph_cgo.go @@ -290,7 +290,7 @@ func (cg *CodeGraph) IndexFile(filePath string) error { return err } - tree, err := cg.parser.ParseCtx(context.TODO(), nil, source) + tree, err := cg.parser.ParseCtx(context.Background(), nil, source) if err != nil { return err } diff --git a/internal/config/validator.go b/internal/config/validator.go index 735bf077..893404ee 100644 --- a/internal/config/validator.go +++ b/internal/config/validator.go @@ -50,7 +50,7 @@ func ValidateSettings(s Settings) ValidationResult { // Validate model selection (stored in eyrie provider.json) activeModel := strings.TrimSpace(s.Model) if activeModel == "" { - activeModel = ActiveModel(context.TODO()) + activeModel = ActiveModel(context.Background()) } if activeModel != "" && strings.Contains(activeModel, " ") { errors = append(errors, ValidationError{ @@ -62,7 +62,7 @@ func ValidateSettings(s Settings) ValidationResult { activeProvider := strings.TrimSpace(s.Provider) if activeProvider == "" { - activeProvider = ActiveProvider(context.TODO()) + activeProvider = ActiveProvider(context.Background()) } // Hawk: validate API key is in the OS secret store (not in settings) if activeProvider != "" { diff --git a/internal/engine/quality_gate.go b/internal/engine/quality_gate.go index d7bdeeed..c4633ac1 100644 --- a/internal/engine/quality_gate.go +++ b/internal/engine/quality_gate.go @@ -87,7 +87,7 @@ func ImplementGate(validateCmd string, workDir string) QualityGate { Phase: GateImplement, Check: func() GateResult { el := &ExperimentLoop{WorkDir: workDir, ValidateCmd: validateCmd, Timeout: 60_000_000_000} - passed, output := el.validate(context.TODO()) + passed, output := el.validate(context.Background()) if passed { return GateResult{Phase: GateImplement, Passed: true, Reason: "build/tests pass"} } From 285a5cc545d9b69f6c1306c10f48fd35380f4a74 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 12 Jun 2026 12:49:14 +0530 Subject: [PATCH 2/3] refactor: centralize env access via internal/env package - Create internal/env/env.go with zero-dependency Getenv wrapper - Update config.Getenv to delegate to env.Getenv - Migrate os.Getenv calls across internal/ to env.Getenv or config.Getenv - Update AGENTS.md policy: env.Getenv for simple reads, config.EnvManager for profile/secret management - Add exceptions for runtime probes and telemetry --- AGENTS.md | 2 +- internal/auth/auth.go | 15 +++++++++++++-- internal/autoinit/autoinit.go | 6 ++++-- internal/config/envmanager.go | 8 ++++++++ internal/env/env.go | 14 ++++++++++++++ internal/resilience/health/diagnostics.go | 3 ++- internal/tool/safety.go | 4 ++-- internal/tool/web_search_brave.go | 5 +++-- internal/tool/web_search_searxng.go | 5 +++-- 9 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 internal/env/env.go diff --git a/AGENTS.md b/AGENTS.md index 6bd4415f..0e5d18e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -246,7 +246,7 @@ test: add coverage for guardian ## Anti-Patterns -- **No `os.Getenv` in `internal/`** — use `config.EnvManager` to centralize env access. Exception: `internal/observability/oteltrace/` for telemetry env vars. +- **No `os.Getenv` in `internal/`** — use `env.Getenv` (in `internal/env/`) for simple reads, or `config.Getenv` if the package can import `internal/config` without cycles. `config.EnvManager` is for profile/secret management. Exceptions: `internal/observability/oteltrace/` for telemetry env vars; runtime environment probes (e.g. `TMUX`, `STY`, `TERM_PROGRAM`, `SHELL`, `GOPATH`) which are set by the OS/terminal and not by config. - **No `panic()` for error handling** — return `error` values. Exception: `init()` functions for package-level assertions. - **No `fmt.Print` for logging** — use `logger.Logger` with structured fields. Exception: `internal/onboarding/` and `internal/engine/scaffold/` for user-facing CLI output. - **No API keys in settings.json** — use OS secret store via `credentials` package and `/config` command. diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 2ec8b6da..509ea84e 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -93,8 +93,19 @@ func (s *SecureStorage) setMacOS(account, token string) error { return err } +func homeDir() string { + if runtime.GOOS == "windows" { + if d := os.Getenv("USERPROFILE"); d != "" { + return d + } + return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + } + home, _ := os.UserHomeDir() + return home +} + func (s *SecureStorage) getFile(account string) (string, error) { - path := filepath.Join(os.Getenv("HOME"), ".hawk", ".tokens") + path := filepath.Join(homeDir(), ".hawk", ".tokens") data, err := os.ReadFile(path) if err != nil { return "", err @@ -107,7 +118,7 @@ func (s *SecureStorage) getFile(account string) (string, error) { } func (s *SecureStorage) setFile(account, token string) error { - path := filepath.Join(os.Getenv("HOME"), ".hawk", ".tokens") + path := filepath.Join(homeDir(), ".hawk", ".tokens") var tokens map[string]string if data, err := os.ReadFile(path); err == nil { _ = json.Unmarshal(data, &tokens) diff --git a/internal/autoinit/autoinit.go b/internal/autoinit/autoinit.go index e469673c..80f890c4 100644 --- a/internal/autoinit/autoinit.go +++ b/internal/autoinit/autoinit.go @@ -17,6 +17,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/GrayCodeAI/hawk/internal/config" ) // markerName is the file written under the project's .hawk directory once an @@ -64,7 +66,7 @@ type Decision struct { // Disabled reports whether auto-init is disabled via the environment. func Disabled() bool { - return isTruthy(os.Getenv(disableEnv)) + return isTruthy(config.Getenv(disableEnv)) } // HasContext reports whether root already contains a project context file. @@ -105,7 +107,7 @@ func MaybeRun(ctx context.Context, opts Options) (Decision, error) { disabled := opts.disableEnvValue if disabled == "" { - disabled = os.Getenv(disableEnv) + disabled = config.Getenv(disableEnv) } if isTruthy(disabled) { return Decision{Skipped: "disabled via " + disableEnv}, nil diff --git a/internal/config/envmanager.go b/internal/config/envmanager.go index d72a139a..bc021daa 100644 --- a/internal/config/envmanager.go +++ b/internal/config/envmanager.go @@ -12,6 +12,7 @@ import ( "sync" "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/hawk/internal/env" ) // EnvVar represents a single environment variable with metadata. @@ -32,6 +33,13 @@ type EnvManager struct { mu sync.RWMutex } +// Getenv returns the value of an environment variable. +// Delegates to internal/env to avoid import cycles for callers in packages +// that already import internal/config. +func Getenv(key string) string { + return env.Getenv(key) +} + // NewEnvManager creates a new EnvManager with initialized maps. func NewEnvManager() *EnvManager { return &EnvManager{ diff --git a/internal/env/env.go b/internal/env/env.go new file mode 100644 index 00000000..3bfc12d2 --- /dev/null +++ b/internal/env/env.go @@ -0,0 +1,14 @@ +package env + +import "os" + +// Getenv returns the value of an environment variable. +// This is the centralized access point for all env var reads in internal/ +// packages. Packages that cannot import internal/config due to import cycles +// use this package instead. The config package also delegates to this function. +// +// Using this function instead of os.Getenv makes env access grep-able and +// allows future migration to a more sophisticated env management layer. +func Getenv(key string) string { + return os.Getenv(key) +} diff --git a/internal/resilience/health/diagnostics.go b/internal/resilience/health/diagnostics.go index 355a6e5a..ddb0de67 100644 --- a/internal/resilience/health/diagnostics.go +++ b/internal/resilience/health/diagnostics.go @@ -13,6 +13,7 @@ import ( "time" "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/hawk/internal/config" ) // DiagnosticResult holds the outcome of a single diagnostic check. @@ -367,7 +368,7 @@ func checkAPIKeySet() DiagnosticResult { func checkModelConfigured() DiagnosticResult { start := time.Now() - model := os.Getenv("HAWK_MODEL") + model := config.Getenv("HAWK_MODEL") if model == "" { return DiagnosticResult{ Name: "model_configured", diff --git a/internal/tool/safety.go b/internal/tool/safety.go index a45a15d8..58c14618 100644 --- a/internal/tool/safety.go +++ b/internal/tool/safety.go @@ -6,12 +6,12 @@ import ( "net" "net/http" "net/url" - "os" "path/filepath" "regexp" "strings" "time" + "github.com/GrayCodeAI/hawk/internal/env" "github.com/GrayCodeAI/hawk/internal/home" ) @@ -231,7 +231,7 @@ func IsSensitivePath(path string) string { } } - if cfgDir := strings.TrimSpace(os.Getenv("HAWK_CONFIG_DIR")); cfgDir != "" { + if cfgDir := strings.TrimSpace(env.Getenv("HAWK_CONFIG_DIR")); cfgDir != "" { customProv := filepath.Clean(filepath.Join(cfgDir, "provider.json")) if clean == customProv { return "access to provider.json is blocked for security (API credentials)" diff --git a/internal/tool/web_search_brave.go b/internal/tool/web_search_brave.go index a0e1e579..ce344a59 100644 --- a/internal/tool/web_search_brave.go +++ b/internal/tool/web_search_brave.go @@ -7,8 +7,9 @@ import ( "io" "net/http" "net/url" - "os" "time" + + "github.com/GrayCodeAI/hawk/internal/env" ) // braveClient is a Brave Search API client. @@ -24,7 +25,7 @@ type braveClient struct { // the BRAVE_SEARCH_API_KEY environment variable. func newBraveClient() *braveClient { return &braveClient{ - apiKey: os.Getenv("BRAVE_SEARCH_API_KEY"), + apiKey: env.Getenv("BRAVE_SEARCH_API_KEY"), http: &http.Client{ Timeout: 15 * time.Second, }, diff --git a/internal/tool/web_search_searxng.go b/internal/tool/web_search_searxng.go index da16d431..27bfa1f1 100644 --- a/internal/tool/web_search_searxng.go +++ b/internal/tool/web_search_searxng.go @@ -7,9 +7,10 @@ import ( "io" "net/http" "net/url" - "os" "strings" "time" + + "github.com/GrayCodeAI/hawk/internal/env" ) // searxngClient is a SearXNG API client. @@ -23,7 +24,7 @@ type searxngClient struct { // newSearxngClient creates a new SearXNG client, reading the instance URL from // the SEARXNG_URL environment variable. func newSearxngClient() *searxngClient { - baseURL := os.Getenv("SEARXNG_URL") + baseURL := env.Getenv("SEARXNG_URL") // Ensure no trailing slash baseURL = strings.TrimRight(baseURL, "/") return &searxngClient{ From 03e8081f16dedf25fc424da0e03e5a151eabf701 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 12 Jun 2026 13:31:32 +0530 Subject: [PATCH 3/3] fix: resolve 145 golangci-lint shadow and nilness issues This commit resolves all 145 golangci-lint issues that surfaced when the shadow, nilness, and additional staticcheck checks were enabled: - 142 shadow (govet): renamed inner shadowed variables to context-specific names (statErr, writeErr, unmarshalErr, etc.) - 22 model/provider shadows: renamed to modelName/providerName - 2 nilness tautologies (non-nil != nil): removed redundant nil checks - 1 staticcheck SA4006 (unused value): removed dead assignment No function signatures, exported types, or behavior changed. No linters were disabled or skipped. .golangci.yml is unchanged. Build and lint pass cleanly. The CI lint check on PR #26 will now pass. --- cmd/autoinit_test.go | 2 +- cmd/chat.go | 26 +++++----- cmd/chat_commands.go | 22 ++++---- cmd/chat_config_keys.go | 36 ++++++------- cmd/chat_config_panel.go | 50 +++++++++---------- cmd/chat_config_ui.go | 6 +-- cmd/chat_print.go | 8 +-- cmd/chat_status.go | 20 ++++---- cmd/chat_welcome.go | 16 +++--- cmd/cmdhistory_cmd.go | 8 +-- cmd/daemon.go | 6 +-- cmd/daemon_ready_test.go | 2 +- cmd/diagnostics.go | 8 +-- cmd/ecosystem.go | 8 +-- cmd/errors.go | 12 ++--- cmd/eval.go | 10 ++-- cmd/eval_tools.go | 4 +- cmd/exec.go | 24 ++++----- cmd/feedback.go | 4 +- cmd/models.go | 24 ++++----- cmd/ocg_live_test.go | 4 +- cmd/review_fix.go | 6 +-- cmd/review_refine.go | 6 +-- internal/cmdhistory/history.go | 8 +-- internal/codegraph/algorithms_cgo.go | 8 +-- internal/config/migrate.go | 4 +- internal/config/migrate_test.go | 22 ++++---- internal/config/xiaomi_setup.go | 4 +- internal/diffsandbox/sandbox_test.go | 4 +- internal/engine/branching/branching_test.go | 4 +- internal/engine/errs/error_recovery.go | 2 +- internal/engine/git/git_context.go | 12 ++--- internal/engine/git/git_context_test.go | 8 +-- .../engine/memory/memory_consolidator_test.go | 2 +- .../observability/feedback_collector_test.go | 2 +- .../engine/observability/structured_log.go | 4 +- .../observability/structured_log_test.go | 6 +-- internal/engine/planning/suggested_tasks.go | 2 +- internal/engine/project/release.go | 4 +- internal/engine/scaffold/scaffold_test.go | 8 +-- internal/engine/stream.go | 7 +-- internal/engine/validation/gen_validator.go | 4 +- internal/feature/eval/lmeval_test.go | 4 +- internal/feature/taste/store_test.go | 16 +++--- internal/intelligence/memory/autodream.go | 4 +- internal/intelligence/memory/yaad_bridge.go | 2 +- internal/intelligence/planner/planner_test.go | 2 +- internal/intelligence/repomap/depgraph.go | 4 +- .../intelligence/repomap/incremental_test.go | 20 ++++---- .../intelligence/repomap/semantic_search.go | 6 +-- .../intelligence/repomap/semantic_test.go | 4 +- internal/intelligence/repomap/summary.go | 2 +- internal/lsp/client.go | 4 +- internal/mcp/mcp.go | 4 +- internal/mcp/server_test.go | 2 +- internal/mcp/ws.go | 4 +- internal/multiagent/parallel/parallel_test.go | 10 ++-- .../parallel/worktree_manager_test.go | 8 +-- internal/observability/insights.go | 8 +-- internal/observability/metrics/metrics.go | 12 ++--- internal/permissions/guardian_test.go | 6 +-- internal/plugin/plugin_test.go | 4 +- internal/plugin/registry.go | 6 +-- internal/resilience/circuit.go | 4 +- internal/resilience/ratelimit/ratelimit.go | 4 +- internal/rules/rules_test.go | 24 ++++----- internal/sandbox/devenv_test.go | 4 +- internal/sandbox/isolation_verify_test.go | 10 ++-- internal/sandbox/netproxy_test.go | 4 +- internal/sandbox/snapshot_sandbox_test.go | 8 +-- internal/session/named_checkpoint.go | 8 +-- internal/session/sqlite_store.go | 8 +-- internal/snapshot/workspace_test.go | 6 ++- internal/tool/api_compat.go | 4 +- internal/tool/api_compat_test.go | 4 +- internal/tool/backup.go | 12 ++--- internal/tool/codegraph.go | 2 +- internal/tool/git_commit_test.go | 4 +- internal/tool/git_fs.go | 6 +-- internal/tool/lsp.go | 8 +-- internal/tool/tool_integration_test.go | 4 +- internal/tool/transaction.go | 8 +-- 82 files changed, 345 insertions(+), 346 deletions(-) diff --git a/cmd/autoinit_test.go b/cmd/autoinit_test.go index 0ab53a43..efcbd50c 100644 --- a/cmd/autoinit_test.go +++ b/cmd/autoinit_test.go @@ -37,7 +37,7 @@ func TestAutoInitRunner_WritesContextFileOnce(t *testing.T) { } ctxFile := filepath.Join(root, autoInitContextFile) - if _, err := os.Stat(ctxFile); err != nil { + if _, statErr := os.Stat(ctxFile); statErr != nil { t.Fatalf("expected context file %s to be written: %v", autoInitContextFile, err) } diff --git a/cmd/chat.go b/cmd/chat.go index b4c0ee86..6966a440 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -273,8 +273,8 @@ func newChatModel(ref *progRef, systemPrompt string, settings hawkconfig.Setting startup.MarkPhase("newChatModel:configureSession") syncSessionFromPersistedSelection(sess, settings) sess.SetLogger(logger.New(io.Discard, logger.Error)) - if err := configureSession(sess, settings); err != nil { - return chatModel{}, err + if cfgErr := configureSession(sess, settings); cfgErr != nil { + return chatModel{}, cfgErr } startup.EndPhase("newChatModel:configureSession") @@ -399,15 +399,15 @@ func newChatModel(ref *progRef, systemPrompt string, settings hawkconfig.Setting // Prefetch live models for the active provider so footer ctx/pricing stay current. go func() { - provider := effectiveProvider - entries, _ := runtime.ListModels(context.Background(), runtime.ListModelsOpts{ProviderID: provider, Source: runtime.ListSourceAuto}) + providerName := effectiveProvider + entries, _ := runtime.ListModels(context.Background(), runtime.ListModelsOpts{ProviderID: providerName, Source: runtime.ListSourceAuto}) opts := configModelOptionsFromEyrie(entries) if len(opts) > 0 { modelCacheMu.Lock() - modelCache[provider] = opts + modelCache[providerName] = opts modelCacheMu.Unlock() if ref != nil { - ref.Send(modelsFetchedMsg{options: opts, provider: provider}) + ref.Send(modelsFetchedMsg{options: opts, provider: providerName}) } } }() @@ -1068,11 +1068,11 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.viewDirty = true m.updateViewportContent() - cmds := []tea.Cmd{compactTickCmd()} + localCmds := []tea.Cmd{compactTickCmd()} if !m.input.Focused() { - cmds = append(cmds, m.input.Focus()) + localCmds = append(localCmds, m.input.Focus()) } - return m, tea.Batch(cmds...) + return m, tea.Batch(localCmds...) } return m, nil @@ -1306,7 +1306,7 @@ func autoIndexCodegraph() { } dbPath := filepath.Join(cwd, ".codegraph", "codegraph.db") - if _, err := os.Stat(dbPath); os.IsNotExist(err) { + if _, statErr := os.Stat(dbPath); os.IsNotExist(statErr) { return // Not initialized, skip } @@ -1367,9 +1367,9 @@ func runChat() error { ctx, cancel := context.WithCancel(context.Background()) _ = cancel // will be cancelled when program exits go func() { - ch, err := sess.Stream(ctx) - if err != nil { - p.Send(streamErrMsg{err: err}) + ch, streamErr := sess.Stream(ctx) + if streamErr != nil { + p.Send(streamErrMsg{err: streamErr}) return } pumpStreamEvents(ref, ch) diff --git a/cmd/chat_commands.go b/cmd/chat_commands.go index 1e2184cc..d558ff2b 100644 --- a/cmd/chat_commands.go +++ b/cmd/chat_commands.go @@ -802,9 +802,9 @@ Generate the recap:`, summary.String()) case "/check": return m.startPromptCommand("/check", buildCheckPrompt()) case "/design": - parts := strings.Fields(text) - if len(parts) >= 2 { - switch parts[1] { + fields := strings.Fields(text) + if len(fields) >= 2 { + switch fields[1] { case "screenshot": path := strings.TrimSpace(strings.TrimPrefix(text, "/design screenshot")) if path == "" { @@ -922,11 +922,11 @@ Generate the recap:`, summary.String()) m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } - model, provider := effectiveModelAndProvider(settings) - if provider == "" { - provider = "auto" + modelName, providerName := effectiveModelAndProvider(settings) + if providerName == "" { + providerName = "auto" } - m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.FormatEcosystemPanel(context.Background(), provider, model)}) + m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)}) return m, nil case "/path": m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.FormatDeveloperPathReport(context.Background())}) @@ -1294,11 +1294,11 @@ Generate the recap:`, summary.String()) return } - text := strings.TrimSpace(string(transcription)) - if text != "" { - m.input.SetValue(text) + transcript := strings.TrimSpace(string(transcription)) + if transcript != "" { + m.input.SetValue(transcript) m.input.CursorEnd() - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Voice input: %s", text)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Voice input: %s", transcript)}) } }() } diff --git a/cmd/chat_config_keys.go b/cmd/chat_config_keys.go index d5c6d849..a02d3250 100644 --- a/cmd/chat_config_keys.go +++ b/cmd/chat_config_keys.go @@ -37,16 +37,16 @@ func (m chatModel) configKeyDetailView() string { mutedStyle := configMutedStyle() accentStyle := configAccentStyle() activeStyle := configActiveStyle() - provider := strings.TrimSpace(m.configProvider) - displayName := hawkconfig.GatewayDisplayName(provider) - masked := hawkconfig.MaskCredentialForProvider(context.Background(), provider) + providerName := strings.TrimSpace(m.configProvider) + displayName := hawkconfig.GatewayDisplayName(providerName) + masked := hawkconfig.MaskCredentialForProvider(context.Background(), providerName) var b strings.Builder b.WriteString(renderConfigBreadcrumb(displayName+" key") + "\n\n") b.WriteString(mutedStyle.Render(" Gateway: ") + accentStyle.Render(displayName) + "\n") b.WriteString(mutedStyle.Render(" Key: ") + activeStyle.Render(masked) + "\n") b.WriteString(mutedStyle.Render(" Stored in: "+credentialsStoreLabel()) + "\n") - if provider == hawkconfig.ProviderXiaomiTokenPlan { + if providerName == hawkconfig.ProviderXiaomiTokenPlan { reg := hawkconfig.XiaomiTokenPlanRegionLabel() if reg == "" { reg = "(not set — press g)" @@ -111,13 +111,13 @@ func (m chatModel) clearConfigGatewayKeyRemove() chatModel { } func (m chatModel) advanceConfigGatewayKeyRemove() (chatModel, tea.Cmd) { - provider := strings.TrimSpace(m.configKeysPendingRemove) - if provider == "" { + trimmedProvider := strings.TrimSpace(m.configKeysPendingRemove) + if trimmedProvider == "" { return m, nil } if m.configKeysRemoveStep < 2 { m.configKeysRemoveStep = 2 - name := hawkconfig.GatewayDisplayName(provider) + name := hawkconfig.GatewayDisplayName(trimmedProvider) m.configNotice = configGatewayRemoveNotice(2, name) return m, nil } @@ -125,47 +125,47 @@ func (m chatModel) advanceConfigGatewayKeyRemove() (chatModel, tea.Cmd) { } func (m chatModel) confirmConfigGatewayKeyRemove() (chatModel, tea.Cmd) { - provider := strings.TrimSpace(m.configKeysPendingRemove) - if provider == "" { + trimmedProvider := strings.TrimSpace(m.configKeysPendingRemove) + if trimmedProvider == "" { return m, nil } m.configKeysPendingRemove = "" m.configKeysRemoveStep = 0 m.configSaving = true - m.configNotice = fmt.Sprintf("Removing key for %s…", hawkconfig.GatewayDisplayName(provider)) + m.configNotice = fmt.Sprintf("Removing key for %s…", hawkconfig.GatewayDisplayName(trimmedProvider)) if m.configEntry == configEntryKeyView { m.configEntry = configEntryNone m.configProvider = "" } - return m, removeCredentialAsync(provider) + return m, removeCredentialAsync(trimmedProvider) } func (m chatModel) handleConfigKeyViewKey(msg tea.KeyMsg) (chatModel, tea.Cmd) { - provider := strings.TrimSpace(m.configProvider) + trimmedProvider := strings.TrimSpace(m.configProvider) switch msg.Type { case tea.KeyEsc: m.configEntry = configEntryNone m.configProvider = "" m = m.clearConfigGatewayKeyRemove() - if idx := m.configGatewayRowIndex(provider); idx >= 0 { + if idx := m.configGatewayRowIndex(trimmedProvider); idx >= 0 { m.configSel = idx } return m, nil case tea.KeyDelete, tea.KeyBackspace: - if provider == "" { + if trimmedProvider == "" { return m, nil } - return m.beginConfigGatewayKeyRemove(provider), nil + return m.beginConfigGatewayKeyRemove(trimmedProvider), nil case tea.KeyEnter: if m.configKeysPendingRemove != "" { return m.advanceConfigGatewayKeyRemove() } - if provider == "" { + if trimmedProvider == "" { return m, nil } - return m.startConfigKeyReplace(provider) + return m.startConfigKeyReplace(trimmedProvider) case tea.KeyRunes: - if provider == hawkconfig.ProviderXiaomiTokenPlan && strings.EqualFold(string(msg.Runes), "g") { + if trimmedProvider == hawkconfig.ProviderXiaomiTokenPlan && strings.EqualFold(string(msg.Runes), "g") { return m.startConfigXiaomiTokenPlanRegion(), nil } default: diff --git a/cmd/chat_config_panel.go b/cmd/chat_config_panel.go index b092da54..718e7bae 100644 --- a/cmd/chat_config_panel.go +++ b/cmd/chat_config_panel.go @@ -77,14 +77,14 @@ func (m chatModel) configPanelView() string { func (m chatModel) configProviderKeyView() string { titleStyle := configTitleStyle() mutedStyle := configMutedStyle() - provider := strings.TrimSpace(m.configProvider) + providerName := strings.TrimSpace(m.configProvider) title := "🔑 Paste API key" hint := "validates with provider API · stored in " + credentialsStoreLabel() - if provider != "" { - title = "🔑 " + hawkconfig.GatewayDisplayName(provider) + if providerName != "" { + title = "🔑 " + hawkconfig.GatewayDisplayName(providerName) hint = "paste key for this gateway only · " + hint } - if provider == hawkconfig.ProviderXiaomiTokenPlan { + if providerName == hawkconfig.ProviderXiaomiTokenPlan { reg := hawkconfig.XiaomiTokenPlanRegionLabel() if reg == "" { reg = "not set — esc and pick region with g or enter on gateway row" @@ -243,8 +243,8 @@ func (m chatModel) configPanelViewWidth() int { func (m chatModel) configActiveModelID() string { if m.session != nil { - if model := strings.TrimSpace(m.session.Model()); model != "" { - return model + if modelName := strings.TrimSpace(m.session.Model()); modelName != "" { + return modelName } } return strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) @@ -448,37 +448,37 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { return m, saveOllamaAsync(value) case configEntryAPIKeyPaste: if value == "" { - provider := strings.TrimSpace(m.configProvider) + providerName := strings.TrimSpace(m.configProvider) m.configEntry = configEntryNone m.configProvider = "" m.wipeConfigKeyInput() m.restoreChatInput() m.configTab = configTabGateways m.configNotice = "No API key entered — paste your key, then press enter" - if provider != "" { - if idx := m.configGatewayRowIndex(provider); idx >= 0 { + if providerName != "" { + if idx := m.configGatewayRowIndex(providerName); idx >= 0 { m.configSel = idx } } return m, nil } - provider := strings.TrimSpace(m.configReplaceProvider) - if provider == "" { - provider = strings.TrimSpace(m.configProvider) + providerName := strings.TrimSpace(m.configReplaceProvider) + if providerName == "" { + providerName = strings.TrimSpace(m.configProvider) } m.configReplaceProvider = "" - if provider == "" { + if providerName == "" { m.configNotice = "Select a gateway on the Gateways tab first" m.configEntry = configEntryNone m.wipeConfigKeyInput() m.restoreChatInput() return m, nil } - if provider == hawkconfig.ProviderXiaomiTokenPlan && hawkconfig.NeedsXiaomiTokenPlanRegion(provider) { + if providerName == hawkconfig.ProviderXiaomiTokenPlan && hawkconfig.NeedsXiaomiTokenPlanRegion(providerName) { m.configEntry = configEntryNone m.wipeConfigKeyInput() m.restoreChatInput() - m.configPostSaveKeysProvider = provider + m.configPostSaveKeysProvider = providerName m.configNotice = "Pick Token Plan region before pasting key" return m.startConfigXiaomiTokenPlanRegion(), nil } @@ -486,21 +486,21 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { m.configProvider = "" m.wipeConfigKeyInput() m.restoreChatInput() - inference, err := hawkconfig.CredentialInferenceForProvider(provider) + inference, err := hawkconfig.CredentialInferenceForProvider(providerName) if err != nil { m.configTab = configTabGateways m.configNotice = "Could not save key: " + sanitizeConfigNotice(err.Error()) - if idx := m.configGatewayRowIndex(provider); idx >= 0 { + if idx := m.configGatewayRowIndex(providerName); idx >= 0 { m.configSel = idx } return m, nil } - m.configPostSaveKeysProvider = provider + m.configPostSaveKeysProvider = providerName m.configSaving = true notice := fmt.Sprintf("Validating key for %s…", inference.DisplayName) - if hint := xiaomi.KeyMismatchHint(xiaomi.BillingTokenPlan, value); provider == hawkconfig.ProviderXiaomiTokenPlan && hint != "" { + if hint := xiaomi.KeyMismatchHint(xiaomi.BillingTokenPlan, value); providerName == hawkconfig.ProviderXiaomiTokenPlan && hint != "" { notice = hint + " · " + notice - } else if hint := xiaomi.KeyMismatchHint(xiaomi.BillingPayAsYouGo, value); provider == xiaomi.ProviderPayAsYouGo && hint != "" { + } else if hint := xiaomi.KeyMismatchHint(xiaomi.BillingPayAsYouGo, value); providerName == xiaomi.ProviderPayAsYouGo && hint != "" { notice = hint + " · " + notice } m.configNotice = notice @@ -528,17 +528,17 @@ func (m chatModel) handleConfigEntryKey(msg tea.KeyMsg) (chatModel, tea.Cmd) { m.restoreChatInput() return m, nil case configEntryAPIKeyPaste: - provider := strings.TrimSpace(m.configReplaceProvider) - if provider == "" { - provider = strings.TrimSpace(m.configProvider) + providerName := strings.TrimSpace(m.configReplaceProvider) + if providerName == "" { + providerName = strings.TrimSpace(m.configProvider) } m.configReplaceProvider = "" m.configEntry = configEntryNone m.configProvider = "" m.wipeConfigKeyInput() m.restoreChatInput() - if provider != "" { - if idx := m.configGatewayRowIndex(provider); idx >= 0 { + if providerName != "" { + if idx := m.configGatewayRowIndex(providerName); idx >= 0 { m.configSel = idx } } diff --git a/cmd/chat_config_ui.go b/cmd/chat_config_ui.go index 7778d17c..5a965635 100644 --- a/cmd/chat_config_ui.go +++ b/cmd/chat_config_ui.go @@ -47,7 +47,7 @@ func renderConfigGatewayLine(displayName string) string { } func renderConfigStatusLine(m chatModel) string { - gateway, model, configured := m.configStatus() + gateway, modelName, configured := m.configStatus() muted := configMutedStyle().Inline(true) accent := configAccentStyle().Inline(true) active := configActiveStyle().Inline(true) @@ -70,10 +70,10 @@ func renderConfigStatusLine(m chatModel) string { muted.Render("Gateway: "), gatewayStyle.Render(gateway), } - if model == "" { + if modelName == "" { parts = append(parts, muted.Render(" · no model selected")) } else { - parts = append(parts, muted.Render(" · Model: "), active.Render(model)) + parts = append(parts, muted.Render(" · Model: "), active.Render(modelName)) } return lipgloss.JoinHorizontal(lipgloss.Left, parts...) } diff --git a/cmd/chat_print.go b/cmd/chat_print.go index a20b07b3..5e552f12 100644 --- a/cmd/chat_print.go +++ b/cmd/chat_print.go @@ -39,8 +39,8 @@ func runPrint(text string) error { sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) sess.SetLogger(logger.New(io.Discard, logger.Error)) - if err := configureSession(sess, settings); err != nil { - return err + if cfgErr := configureSession(sess, settings); cfgErr != nil { + return cfgErr } reader := bufio.NewReader(os.Stdin) @@ -261,8 +261,8 @@ func runRepl() error { sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) sess.SetLogger(logger.New(io.Discard, logger.Error)) - if err := configureSession(sess, settings); err != nil { - return err + if cfgErr := configureSession(sess, settings); cfgErr != nil { + return cfgErr } reader := bufio.NewReader(os.Stdin) diff --git a/cmd/chat_status.go b/cmd/chat_status.go index b3353384..8a312eeb 100644 --- a/cmd/chat_status.go +++ b/cmd/chat_status.go @@ -59,14 +59,14 @@ func (m *chatModel) invalidateConnStatus() { } func (m chatModel) connStatusFingerprint() string { - gw, model := m.sessionGatewayModel() + gw, modelName := m.sessionGatewayModel() creds := strings.Join(hawkconfig.ConfiguredCredentialProviders(), ",") api := 0 if m.session != nil { api = m.session.LastPromptTokens() } used := sessionContextUsedTokens(m.session) - return gw + "\x00" + model + "\x00" + creds + "\x00" + fmt.Sprintf("%d", used) + "\x00" + fmt.Sprintf("%d", api) + return gw + "\x00" + modelName + "\x00" + creds + "\x00" + fmt.Sprintf("%d", used) + "\x00" + fmt.Sprintf("%d", api) } func (m chatModel) sessionGatewayModel() (gateway, model string) { @@ -103,11 +103,11 @@ func (m *chatModel) chatConnectionStatus() string { } func (m chatModel) buildConnectionStatusPlain() string { - gw, model, ctxLabel := m.connectionStatusParts() - if gw == "" && model == "" { + gw, modelName, ctxLabel := m.connectionStatusParts() + if gw == "" && modelName == "" { return "pick model" } - if model == "" { + if modelName == "" { if gw == "" { return "pick model" } @@ -116,13 +116,13 @@ func (m chatModel) buildConnectionStatusPlain() string { if ctxLabel != "" && ctxLabel != "—" { ctxText := formatConnectionContextLabel(m, ctxLabel) if ctxText != "" { - return fmt.Sprintf("%s · %s · %s", gw, model, ctxText) + return fmt.Sprintf("%s · %s · %s", gw, modelName, ctxText) } } if gw == "" { - return model + return modelName } - return gw + " · " + model + return gw + " · " + modelName } func (m chatModel) connectionStatusParts() (gateway, model, contextLabel string) { @@ -167,9 +167,9 @@ func (m chatModel) renderConnectionStatusSplit() (modelRendered string, modelVis return "", 0, "", 0 } - gw, model, ctxLabel := m.connectionStatusParts() + gw, modelName, ctxLabel := m.connectionStatusParts() ctxText := formatConnectionContextLabel(m, ctxLabel) - modelRendered, modelVis = renderChatConnectionModel(gw, model) + modelRendered, modelVis = renderChatConnectionModel(gw, modelName) if ctxText != "" { ctxRendered, ctxVis = renderChatConnectionContext(ctxText, contextUsagePercent(m, ctxLabel)) } diff --git a/cmd/chat_welcome.go b/cmd/chat_welcome.go index f918f456..82501ce4 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -157,13 +157,13 @@ func buildWelcomeMessage(sess *engine.Session, sessionID string, registry *tool. } if forGate { - if model, provider := effectiveModelAndProvider(settings); model != "" { + if modelName, providerName := effectiveModelAndProvider(settings); modelName != "" { var plainParts, styledParts []string - if provider != "" { - plainParts = append(plainParts, provider) - styledParts = append(styledParts, mutedC+provider+rst) + if providerName != "" { + plainParts = append(plainParts, providerName) + styledParts = append(styledParts, mutedC+providerName+rst) } - short := normalizeModelDisplayName(model, model) + short := normalizeModelDisplayName(modelName, modelName) plainParts = append(plainParts, short) styledParts = append(styledParts, bodyC+short+rst) mode := permissionModeLabel(sess) @@ -313,8 +313,8 @@ func envSummaryWithSelection(provider, model string, includeSelection bool) stri func configCommandSummary(settings hawkconfig.Settings) string { _ = settings - provider := displayConfigValue(hawkconfig.ActiveProvider(context.Background())) - model := displayConfigValue(hawkconfig.ActiveModel(context.Background())) + providerName := displayConfigValue(hawkconfig.ActiveProvider(context.Background())) + modelName := displayConfigValue(hawkconfig.ActiveModel(context.Background())) return fmt.Sprintf(`Setup (eyrie) /config → paste API key (OS keychain) + pick model @@ -326,7 +326,7 @@ Current: model: %s keys: %s -Model catalog and routing live in eyrie — hawk is the UI only.`, provider, model, configuredKeyList()) +Model catalog and routing live in eyrie — hawk is the UI only.`, providerName, modelName, configuredKeyList()) } func apiKeyConfigSummary() string { diff --git a/cmd/cmdhistory_cmd.go b/cmd/cmdhistory_cmd.go index ffccf9fc..a7416fd6 100644 --- a/cmd/cmdhistory_cmd.go +++ b/cmd/cmdhistory_cmd.go @@ -83,8 +83,8 @@ var cmdHistoryRecentCmd = &cobra.Command{ n := 20 if len(args) > 0 { - parsed, err := strconv.Atoi(args[0]) - if err != nil { + parsed, parseErr := strconv.Atoi(args[0]) + if parseErr != nil { return fmt.Errorf("invalid number: %s", args[0]) } n = parsed @@ -165,8 +165,8 @@ func openCmdHistoryStore() (*cmdhistory.Store, error) { dbPath := filepath.Join(home, ".hawk", "cmd-history.db") // Ensure the directory exists. - if err := os.MkdirAll(filepath.Dir(dbPath), 0o755); err != nil { - return nil, fmt.Errorf("cannot create history directory: %w", err) + if mkErr := os.MkdirAll(filepath.Dir(dbPath), 0o755); mkErr != nil { + return nil, fmt.Errorf("cannot create history directory: %w", mkErr) } store, err := cmdhistory.New(dbPath) diff --git a/cmd/daemon.go b/cmd/daemon.go index 1da9fac2..c93f4a58 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -213,8 +213,8 @@ func runDaemonStop(_ *cobra.Command, _ []string) error { PID int `json:"pid"` Addr string `json:"addr"` } - if err := json.Unmarshal(data, &info); err != nil { - return fmt.Errorf("invalid PID file: %w", err) + if unmarshalErr := json.Unmarshal(data, &info); unmarshalErr != nil { + return fmt.Errorf("invalid PID file: %w", unmarshalErr) } proc, err := os.FindProcess(info.PID) @@ -246,7 +246,7 @@ func runDaemonStatus(_ *cobra.Command, _ []string) error { Addr string `json:"addr"` StartedAt string `json:"started_at"` } - if err := json.Unmarshal(data, &info); err != nil { + if unmarshalErr := json.Unmarshal(data, &info); unmarshalErr != nil { fmt.Println("Status: unknown (invalid PID file)") return nil } diff --git a/cmd/daemon_ready_test.go b/cmd/daemon_ready_test.go index 778bd67c..fb1feddf 100644 --- a/cmd/daemon_ready_test.go +++ b/cmd/daemon_ready_test.go @@ -55,7 +55,7 @@ func TestDaemonReadyProbe_AffectsReadyEndpoint(t *testing.T) { // Wait for the listener. deadline := time.Now().Add(2 * time.Second) for time.Now().Before(deadline) { - if c, err := net.DialTimeout("tcp", addr, 50*time.Millisecond); err == nil { + if c, dialErr := net.DialTimeout("tcp", addr, 50*time.Millisecond); dialErr == nil { c.Close() break } diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go index 7766a57b..c94f7b69 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -23,9 +23,9 @@ import ( ) func doctorReport(settings hawkconfig.Settings) string { - modelName, provider := effectiveModelAndProvider(settings) - if provider == "" { - provider = "auto" + modelName, providerName := effectiveModelAndProvider(settings) + if providerName == "" { + providerName = "auto" } if modelName == "" { modelName = "default" @@ -37,7 +37,7 @@ func doctorReport(settings hawkconfig.Settings) string { b.WriteString(fmt.Sprintf("Version: %s\n", version)) b.WriteString(fmt.Sprintf("Go version: %s\n", runtime.Version())) b.WriteString(fmt.Sprintf("Directory: %s\n", cwd)) - b.WriteString(fmt.Sprintf("Provider: %s\n", provider)) + b.WriteString(fmt.Sprintf("Provider: %s\n", providerName)) b.WriteString(fmt.Sprintf("Model: %s\n", modelName)) // Binary size diff --git a/cmd/ecosystem.go b/cmd/ecosystem.go index 21cb32f1..168e63f0 100644 --- a/cmd/ecosystem.go +++ b/cmd/ecosystem.go @@ -16,11 +16,11 @@ var ecosystemCmd = &cobra.Command{ if err != nil { return err } - model, provider := effectiveModelAndProvider(settings) - if provider == "" { - provider = "auto" + modelName, providerName := effectiveModelAndProvider(settings) + if providerName == "" { + providerName = "auto" } - cmd.Println(hawkconfig.FormatEcosystemPanel(context.Background(), provider, model)) + cmd.Println(hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)) return nil }, } diff --git a/cmd/errors.go b/cmd/errors.go index 7cafb9e4..83a5c42e 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -377,20 +377,20 @@ func validateStartup(settings hawkconfig.Settings) []StartupWarning { var warnings []StartupWarning // 1. Check API key for configured provider - provider := hawkconfig.NormalizeProviderForEngine(settings.Provider) - if provider != "" && provider != "ollama" { - envKey := hawkconfig.ProviderAPIKeyEnv(provider) + providerName := hawkconfig.NormalizeProviderForEngine(settings.Provider) + if providerName != "" && providerName != "ollama" { + envKey := hawkconfig.ProviderAPIKeyEnv(providerName) if envKey != "" && os.Getenv(envKey) == "" { warnings = append(warnings, StartupWarning{ Check: "api_key", - Message: fmt.Sprintf("No API key found for %s. Set %s in your environment or run /config.", provider, envKey), + Message: fmt.Sprintf("No API key found for %s. Set %s in your environment or run /config.", providerName, envKey), }) } } // 2. Quick network reachability check (DNS lookup, no full HTTP request) - if provider != "" && provider != "ollama" { - host := providerDNSHost(provider) + if providerName != "" && providerName != "ollama" { + host := providerDNSHost(providerName) if host != "" { if _, err := net.LookupHost(host); err != nil { warnings = append(warnings, StartupWarning{ diff --git a/cmd/eval.go b/cmd/eval.go index ddd981a8..ea358595 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -124,15 +124,15 @@ func runEval(_ *cobra.Command, _ []string) error { return fmt.Errorf("no tasks matched the given filters") } - model := evalModel - if model == "" { - model = "default" + modelName := evalModel + if modelName == "" { + modelName = "default" } - fmt.Printf("Running %d tasks with model %s...\n", len(tasks), model) + fmt.Printf("Running %d tasks with model %s...\n", len(tasks), modelName) suite := &eval.BenchmarkSuite{Name: "hawk-eval", Tasks: tasks} - runner := eval.NewRunner(model, "") + runner := eval.NewRunner(modelName, "") runner.NoCache = evalNoCache if !evalNoCache { runner.Cache = eval.DefaultCache() diff --git a/cmd/eval_tools.go b/cmd/eval_tools.go index 9a207816..98e6b12c 100644 --- a/cmd/eval_tools.go +++ b/cmd/eval_tools.go @@ -78,8 +78,8 @@ func runEvalTools(cmd *cobra.Command, _ []string) error { if err != nil { return err } - model, provider := effectiveModelAndProvider(settings) - sess := newHawkSession(settings, provider, model, systemPrompt, registry) + modelName, providerName := effectiveModelAndProvider(settings) + sess := newHawkSession(settings, providerName, modelName, systemPrompt, registry) if err := configureSession(sess, settings); err != nil { return err } diff --git a/cmd/exec.go b/cmd/exec.go index 5a080963..ed869112 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -145,8 +145,8 @@ func runExec(_ *cobra.Command, args []string) error { } if execCWD != "" { - if err := os.Chdir(execCWD); err != nil { - return fmt.Errorf("chdir %s: %w", execCWD, err) + if chdirErr := os.Chdir(execCWD); chdirErr != nil { + return fmt.Errorf("chdir %s: %w", execCWD, chdirErr) } } @@ -166,8 +166,8 @@ func runExec(_ *cobra.Command, args []string) error { } wtBranch = branch defer cleanupExecWorktree(cwd, wtPath) - if err := os.Chdir(wtPath); err != nil { - return fmt.Errorf("chdir worktree: %w", err) + if chdirErr := os.Chdir(wtPath); chdirErr != nil { + return fmt.Errorf("chdir worktree: %w", chdirErr) } } @@ -183,9 +183,9 @@ func runExec(_ *cobra.Command, args []string) error { // If --agent is specified, prepend the agent persona var agentModel string if execAgent != "" { - agentDef, err := agents.Get(execAgent) - if err != nil { - return fmt.Errorf("agent %q: %w", execAgent, err) + agentDef, lookupErr := agents.Get(execAgent) + if lookupErr != nil { + return fmt.Errorf("agent %q: %w", execAgent, lookupErr) } systemPrompt = agentDef.Prompt + "\n\n" + systemPrompt agentModel = agentDef.Model @@ -211,8 +211,8 @@ func runExec(_ *cobra.Command, args []string) error { sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) sess.SetLogger(logger.New(io.Discard, logger.Error)) - if err := configureSession(sess, settings, execMaxTurns); err != nil { - return err + if cfgErr := configureSession(sess, settings, execMaxTurns); cfgErr != nil { + return cfgErr } // Apply autonomy level @@ -231,9 +231,9 @@ func runExec(_ *cobra.Command, args []string) error { // Resume existing session if --session-id provided if execSessionID != "" { - saved, err := session.Load(execSessionID) - if err != nil { - return fmt.Errorf("resume session %s: %w", execSessionID, err) + saved, lookupErr := session.Load(execSessionID) + if lookupErr != nil { + return fmt.Errorf("resume session %s: %w", execSessionID, lookupErr) } sess.LoadMessages(toEyrieMessages(saved.Messages)) } diff --git a/cmd/feedback.go b/cmd/feedback.go index 73db104f..82819b72 100644 --- a/cmd/feedback.go +++ b/cmd/feedback.go @@ -95,8 +95,8 @@ func saveFeedbackLocal(report FeedbackReport) error { } dir := filepath.Join(home, ".hawk", "feedback") - if err := os.MkdirAll(dir, 0o755); err != nil { - return fmt.Errorf("create feedback directory: %w", err) + if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil { + return fmt.Errorf("create feedback directory: %w", mkErr) } filename := fmt.Sprintf("feedback-%s.json", time.Now().Format("20060102-150405")) diff --git a/cmd/models.go b/cmd/models.go index 7235f712..234c9548 100644 --- a/cmd/models.go +++ b/cmd/models.go @@ -55,11 +55,11 @@ var modelsStatusCmd = &cobra.Command{ if err != nil { return err } - model, _ := effectiveModelAndProvider(settings) + modelName, _ := effectiveModelAndProvider(settings) if len(args) > 0 { - model = args[0] + modelName = args[0] } - report, err := hawkconfig.DeploymentStatusReport(ctx, model) + report, err := hawkconfig.DeploymentStatusReport(ctx, modelName) if err != nil { return err } @@ -73,8 +73,8 @@ var modelsRoutingPreviewCmd = &cobra.Command{ Short: "Print effective deployment routing JSON for a model", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - model := args[0] - out, err := hawkconfig.RoutingPreviewJSON(context.Background(), model) + modelName := args[0] + out, err := hawkconfig.RoutingPreviewJSON(context.Background(), modelName) if err != nil { return err } @@ -87,20 +87,20 @@ var modelsListCmd = &cobra.Command{ Use: "list [provider]", Short: "List models from the eyrie catalog cache (or live provider API)", RunE: func(cmd *cobra.Command, args []string) error { - provider := "" + providerName := "" if len(args) > 0 { - provider = args[0] + providerName = args[0] } ctx := context.Background() var models []catalog.ModelCatalogEntry var err error if modelsListLive { - if provider == "" { + if providerName == "" { return fmt.Errorf("provider required with --live (e.g. hawk models list canopywave --live --json)") } - models, err = catalog.FetchLiveModelEntriesForProvider(eyriecfg.DiscoveryEnvMap(ctx), hawkconfig.NormalizeProviderForEngine(provider)) + models, err = catalog.FetchLiveModelEntriesForProvider(eyriecfg.DiscoveryEnvMap(ctx), hawkconfig.NormalizeProviderForEngine(providerName)) } else { - models, err = hawkconfig.FetchModelsForProvider(provider) + models, err = hawkconfig.FetchModelsForProvider(providerName) } if err != nil { return err @@ -137,8 +137,8 @@ var modelsListCmd = &cobra.Command{ return nil } cmd.Printf("%d models", len(models)) - if provider != "" { - cmd.Printf(" for provider %q", provider) + if providerName != "" { + cmd.Printf(" for provider %q", providerName) } cmd.Println() rows := make([]modelTableRow, len(models)) diff --git a/cmd/ocg_live_test.go b/cmd/ocg_live_test.go index 8c88458c..96548190 100644 --- a/cmd/ocg_live_test.go +++ b/cmd/ocg_live_test.go @@ -36,8 +36,8 @@ func TestLiveOpenCodeGoMiniMaxM3FullHawkPath(t *testing.T) { sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) sess.SetLogger(logger.New(ioDiscard{}, logger.Info)) - if err := configureSession(sess, settings); err != nil { - t.Fatal(err) + if cfgErr := configureSession(sess, settings); cfgErr != nil { + t.Fatal(cfgErr) } sess.AddUser("Hi") diff --git a/cmd/review_fix.go b/cmd/review_fix.go index 898f9a70..7ad72684 100644 --- a/cmd/review_fix.go +++ b/cmd/review_fix.go @@ -44,9 +44,9 @@ func runReviewFix(_ *cobra.Command, args []string) error { if len(args) > 0 { for _, ref := range args { - r, err := resolveReview(store, ref) - if err != nil { - return err + r, resolveErr := resolveReview(store, ref) + if resolveErr != nil { + return resolveErr } reviews = append(reviews, r) } diff --git a/cmd/review_refine.go b/cmd/review_refine.go index e371c3c8..537744df 100644 --- a/cmd/review_refine.go +++ b/cmd/review_refine.go @@ -46,9 +46,9 @@ func runReviewRefine(_ *cobra.Command, args []string) error { var reviews []*ReviewRecord if len(args) > 0 { for _, ref := range args { - r, err := resolveReview(store, ref) - if err != nil { - return err + r, resolveErr := resolveReview(store, ref) + if resolveErr != nil { + return resolveErr } reviews = append(reviews, r) } diff --git a/internal/cmdhistory/history.go b/internal/cmdhistory/history.go index ad21a475..1c0db829 100644 --- a/internal/cmdhistory/history.go +++ b/internal/cmdhistory/history.go @@ -248,13 +248,13 @@ func (s *Store) Stats() (*HistoryStats, error) { for cmdRows.Next() { var cc CommandCount - if err := cmdRows.Scan(&cc.Command, &cc.Count); err != nil { - return nil, fmt.Errorf("scan command count: %w", err) + if scanErr := cmdRows.Scan(&cc.Command, &cc.Count); scanErr != nil { + return nil, fmt.Errorf("scan command count: %w", scanErr) } stats.TopCommands = append(stats.TopCommands, cc) } - if err := cmdRows.Err(); err != nil { - return nil, err + if iterErr := cmdRows.Err(); iterErr != nil { + return nil, iterErr } // Top 10 directories by frequency. diff --git a/internal/codegraph/algorithms_cgo.go b/internal/codegraph/algorithms_cgo.go index 1d8f3401..acf79e07 100644 --- a/internal/codegraph/algorithms_cgo.go +++ b/internal/codegraph/algorithms_cgo.go @@ -490,15 +490,15 @@ func (cg *CodeGraph) SnapshotGraph() (nodes map[string]bool, edges map[string]bo } for rows.Next() { var id string - if err := rows.Scan(&id); err != nil { + if scanErr := rows.Scan(&id); scanErr != nil { rows.Close() - return nil, nil, fmt.Errorf("scanning node row: %w", err) + return nil, nil, fmt.Errorf("scanning node row: %w", scanErr) } nodes[id] = true } - if err := rows.Err(); err != nil { + if iterErr := rows.Err(); iterErr != nil { rows.Close() - return nil, nil, fmt.Errorf("iterating nodes: %w", err) + return nil, nil, fmt.Errorf("iterating nodes: %w", iterErr) } rows.Close() diff --git a/internal/config/migrate.go b/internal/config/migrate.go index 6d7bae42..ade48d9e 100644 --- a/internal/config/migrate.go +++ b/internal/config/migrate.go @@ -343,8 +343,8 @@ func (r *MigrationRegistry) MigrateFile(path string) error { } var data map[string]interface{} - if err := json.Unmarshal(rawData, &data); err != nil { - return fmt.Errorf("failed to parse config JSON: %w", err) + if unmarshalErr := json.Unmarshal(rawData, &data); unmarshalErr != nil { + return fmt.Errorf("failed to parse config JSON: %w", unmarshalErr) } if !r.NeedsMigration(data) { diff --git a/internal/config/migrate_test.go b/internal/config/migrate_test.go index 118a0bac..3c3020a5 100644 --- a/internal/config/migrate_test.go +++ b/internal/config/migrate_test.go @@ -466,7 +466,7 @@ func TestBackupCreation(t *testing.T) { } // Verify backup file exists - if _, err := os.Stat(backupPath); os.IsNotExist(err) { + if _, statErr := os.Stat(backupPath); os.IsNotExist(statErr) { t.Fatal("backup file should exist") } @@ -802,13 +802,13 @@ func TestMigrateFileEndToEnd(t *testing.T) { if err != nil { t.Fatal(err) } - if err := os.WriteFile(configPath, data, 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(configPath, data, 0o644); writeErr != nil { + t.Fatal(writeErr) } r := NewMigrationRegistry() - if err := r.MigrateFile(configPath); err != nil { - t.Fatalf("MigrateFile failed: %v", err) + if migrateErr := r.MigrateFile(configPath); migrateErr != nil { + t.Fatalf("MigrateFile failed: %v", migrateErr) } // Read back the migrated config @@ -818,8 +818,8 @@ func TestMigrateFileEndToEnd(t *testing.T) { } var result map[string]interface{} - if err := json.Unmarshal(migratedData, &result); err != nil { - t.Fatalf("failed to parse migrated config: %v", err) + if unmarshalErr := json.Unmarshal(migratedData, &result); unmarshalErr != nil { + t.Fatalf("failed to parse migrated config: %v", unmarshalErr) } // Verify version @@ -877,13 +877,13 @@ func TestMigrateFileAlreadyCurrent(t *testing.T) { if err != nil { t.Fatal(err) } - if err := os.WriteFile(configPath, data, 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(configPath, data, 0o644); writeErr != nil { + t.Fatal(writeErr) } r := NewMigrationRegistry() - if err := r.MigrateFile(configPath); err != nil { - t.Fatalf("MigrateFile should succeed for current config: %v", err) + if migrateErr := r.MigrateFile(configPath); migrateErr != nil { + t.Fatalf("MigrateFile should succeed for current config: %v", migrateErr) } // Verify no backup was created (no migration needed) diff --git a/internal/config/xiaomi_setup.go b/internal/config/xiaomi_setup.go index cfdbf502..3819c0b4 100644 --- a/internal/config/xiaomi_setup.go +++ b/internal/config/xiaomi_setup.go @@ -35,8 +35,8 @@ func SetXiaomiTokenPlanRegion(region string) error { cfg = &eyriecfg.ProviderConfig{} } cfg.XiaomiMimoTokenPlanRegion = string(normalized) - if err := eyriecfg.SaveProviderConfig(cfg, ""); err != nil { - return err + if saveErr := eyriecfg.SaveProviderConfig(cfg, ""); saveErr != nil { + return saveErr } _ = os.Setenv(eyriecfg.EnvXiaomiTokenPlanRegion, string(normalized)) base, err := eyriecfg.ResolveXiaomiOpenAIBase(ProviderXiaomiTokenPlan, cfg) diff --git a/internal/diffsandbox/sandbox_test.go b/internal/diffsandbox/sandbox_test.go index 43870c8d..eb09a72a 100644 --- a/internal/diffsandbox/sandbox_test.go +++ b/internal/diffsandbox/sandbox_test.go @@ -60,8 +60,8 @@ func TestProposeModifyAndApply(t *testing.T) { t.Errorf("expected original content, got %q", c.Original) } - if err := sb.Apply(); err != nil { - t.Fatalf("Apply error: %v", err) + if applyErr := sb.Apply(); applyErr != nil { + t.Fatalf("Apply error: %v", applyErr) } data, err := os.ReadFile(origPath) diff --git a/internal/engine/branching/branching_test.go b/internal/engine/branching/branching_test.go index 15f4db30..9e8c234c 100644 --- a/internal/engine/branching/branching_test.go +++ b/internal/engine/branching/branching_test.go @@ -517,8 +517,8 @@ func TestExportImportRoundTrip(t *testing.T) { // Verify it's valid JSON. var raw map[string]interface{} - if err := json.Unmarshal(data, &raw); err != nil { - t.Fatalf("exported data is not valid JSON: %v", err) + if unmarshalErr := json.Unmarshal(data, &raw); unmarshalErr != nil { + t.Fatalf("exported data is not valid JSON: %v", unmarshalErr) } // Import into a new manager. diff --git a/internal/engine/errs/error_recovery.go b/internal/engine/errs/error_recovery.go index f2af43ae..5f360188 100644 --- a/internal/engine/errs/error_recovery.go +++ b/internal/engine/errs/error_recovery.go @@ -166,7 +166,7 @@ func (er *ErrorRecovery) Recover(err error, ctx *RecoveryContext) (*RecoveryResu if ctx == nil { ctx = &RecoveryContext{} } - if ctx.ErrorMsg == "" && err != nil { + if ctx.ErrorMsg == "" { ctx.ErrorMsg = err.Error() } if ctx.Error == nil { diff --git a/internal/engine/git/git_context.go b/internal/engine/git/git_context.go index c7ed56fc..6f158cf8 100644 --- a/internal/engine/git/git_context.go +++ b/internal/engine/git/git_context.go @@ -86,7 +86,7 @@ func (gc *GitContext) GetFileInfo(path string) (*GitFileInfo, error) { lines := strings.Split(logOut, "\n") if len(lines) >= 4 { info.LastAuthor = lines[1] - if t, err := time.Parse(time.RFC3339, lines[2]); err == nil { + if t, parseErr := time.Parse(time.RFC3339, lines[2]); parseErr == nil { info.LastModified = t } info.LastCommitMsg = lines[3] @@ -95,7 +95,7 @@ func (gc *GitContext) GetFileInfo(path string) (*GitFileInfo, error) { // Count commits touching this file countOut, err := gc.runGit("rev-list", "--count", "HEAD", "--", path) if err == nil { - if n, err := strconv.Atoi(countOut); err == nil { + if n, parseErr := strconv.Atoi(countOut); parseErr == nil { info.CommitCount = n } } @@ -392,8 +392,8 @@ func (gc *GitContext) BuildContextForFile(path string) string { commitCount := 0 for _, c := range recentCommits { // Check if this commit touched the file - filesOut, err := gc.runGit("diff-tree", "--no-commit-id", "-r", "--name-only", c.Hash) - if err == nil { + filesOut, diffErr := gc.runGit("diff-tree", "--no-commit-id", "-r", "--name-only", c.Hash) + if diffErr == nil { for _, f := range strings.Split(filesOut, "\n") { if strings.TrimSpace(f) == path { commitCount++ @@ -434,8 +434,8 @@ func (gc *GitContext) BuildContextForSession() string { // Check how far ahead of main aheadBehind := "" for _, base := range []string{"main", "master"} { - out, err := gc.runGit("rev-list", "--count", base+"..HEAD") - if err == nil { + out, revErr := gc.runGit("rev-list", "--count", base+"..HEAD") + if revErr == nil { if n, _ := strconv.Atoi(out); n > 0 { aheadBehind = fmt.Sprintf(" (ahead of %s by %d commits)", base, n) } diff --git a/internal/engine/git/git_context_test.go b/internal/engine/git/git_context_test.go index d82842cf..5db6b0c9 100644 --- a/internal/engine/git/git_context_test.go +++ b/internal/engine/git/git_context_test.go @@ -159,8 +159,8 @@ func TestGetUncommitted(t *testing.T) { } // Modify a file - if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\n// modified\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\n// modified\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } files, err = gc.GetUncommitted() @@ -425,8 +425,8 @@ func TestGetDiffSummary(t *testing.T) { } // Modify a file - if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\n// changed\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\n// changed\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } summary, err = gc.GetDiffSummary() diff --git a/internal/engine/memory/memory_consolidator_test.go b/internal/engine/memory/memory_consolidator_test.go index 44eabfdf..77e061b0 100644 --- a/internal/engine/memory/memory_consolidator_test.go +++ b/internal/engine/memory/memory_consolidator_test.go @@ -303,7 +303,7 @@ func TestSaveLoad(t *testing.T) { // Verify file exists path := filepath.Join(dir, "consolidated_memory.json") - if _, err := os.Stat(path); os.IsNotExist(err) { + if _, statErr := os.Stat(path); os.IsNotExist(statErr) { t.Fatal("expected consolidated_memory.json to exist") } diff --git a/internal/engine/observability/feedback_collector_test.go b/internal/engine/observability/feedback_collector_test.go index cb1c9d57..f4999155 100644 --- a/internal/engine/observability/feedback_collector_test.go +++ b/internal/engine/observability/feedback_collector_test.go @@ -419,7 +419,7 @@ func TestFeedbackCollectorSaveAndLoad(t *testing.T) { // Verify file exists path := filepath.Join(dir, "feedback.json") - if _, err := os.Stat(path); os.IsNotExist(err) { + if _, statErr := os.Stat(path); os.IsNotExist(statErr) { t.Fatal("feedback.json was not created") } diff --git a/internal/engine/observability/structured_log.go b/internal/engine/observability/structured_log.go index 91daeb7e..645bacf7 100644 --- a/internal/engine/observability/structured_log.go +++ b/internal/engine/observability/structured_log.go @@ -312,8 +312,8 @@ func (rw *RotatingWriter) Write(p []byte) (n int, err error) { defer rw.mu.Unlock() if rw.size+int64(len(p)) > rw.MaxSize { - if err := rw.rotate(); err != nil { - return 0, err + if rotateErr := rw.rotate(); rotateErr != nil { + return 0, rotateErr } } diff --git a/internal/engine/observability/structured_log_test.go b/internal/engine/observability/structured_log_test.go index 1350850c..3cccb3d0 100644 --- a/internal/engine/observability/structured_log_test.go +++ b/internal/engine/observability/structured_log_test.go @@ -338,9 +338,9 @@ func TestRotatingWriter(t *testing.T) { // Write enough data to trigger rotation. data := bytes.Repeat([]byte("A"), 60) for i := 0; i < 5; i++ { - _, err := rw.Write(data) - if err != nil { - t.Fatalf("write %d failed: %v", i, err) + _, writeErr := rw.Write(data) + if writeErr != nil { + t.Fatalf("write %d failed: %v", i, writeErr) } } diff --git a/internal/engine/planning/suggested_tasks.go b/internal/engine/planning/suggested_tasks.go index 1c52112d..692334bc 100644 --- a/internal/engine/planning/suggested_tasks.go +++ b/internal/engine/planning/suggested_tasks.go @@ -378,7 +378,7 @@ func ScanTestFailures(projectDir string) []*SuggestedTask { } // If we detected failures but couldn't parse test names - if len(failedTests) == 0 && err != nil { + if len(failedTests) == 0 { tasks = append(tasks, &SuggestedTask{ ID: generateTaskID(), Title: "Fix failing tests", diff --git a/internal/engine/project/release.go b/internal/engine/project/release.go index e9f0a04d..2795b54a 100644 --- a/internal/engine/project/release.go +++ b/internal/engine/project/release.go @@ -402,11 +402,11 @@ func (rm *ReleaseManager) PrepareRelease() (*Release, error) { sinceTag = "v" + currentVersion cmd := exec.CommandContext(context.Background(), "git", "rev-parse", sinceTag) cmd.Dir = rm.ProjectDir - if err := cmd.Run(); err != nil { + if runErr := cmd.Run(); runErr != nil { sinceTag = currentVersion cmd2 := exec.CommandContext(context.Background(), "git", "rev-parse", sinceTag) cmd2.Dir = rm.ProjectDir - if err := cmd2.Run(); err != nil { + if runErr := cmd2.Run(); runErr != nil { sinceTag = "" } } diff --git a/internal/engine/scaffold/scaffold_test.go b/internal/engine/scaffold/scaffold_test.go index c7e4484c..52ccbb6a 100644 --- a/internal/engine/scaffold/scaffold_test.go +++ b/internal/engine/scaffold/scaffold_test.go @@ -144,7 +144,7 @@ func TestGenerateConditionEvaluation(t *testing.T) { } dockerPath := filepath.Join(outputDir1, "myapi/Dockerfile") - if _, err := os.Stat(dockerPath); os.IsNotExist(err) { + if _, statErr := os.Stat(dockerPath); os.IsNotExist(statErr) { t.Error("Dockerfile should be created when WithDocker is true") } @@ -387,8 +387,8 @@ func TestLoadTemplateFromJSON(t *testing.T) { if err != nil { t.Fatalf("marshaling template: %v", err) } - if err := os.WriteFile(jsonPath, data, 0o644); err != nil { - t.Fatalf("writing template file: %v", err) + if writeErr := os.WriteFile(jsonPath, data, 0o644); writeErr != nil { + t.Fatalf("writing template file: %v", writeErr) } // Load it @@ -581,7 +581,7 @@ func TestGeneratePythonCLITemplate(t *testing.T) { } // Check tests are created - if _, err := os.Stat(filepath.Join(outputDir, "mycli/tests/test_cli.py")); os.IsNotExist(err) { + if _, statErr := os.Stat(filepath.Join(outputDir, "mycli/tests/test_cli.py")); os.IsNotExist(statErr) { t.Error("test_cli.py should be created when WithTests is true") } diff --git a/internal/engine/stream.go b/internal/engine/stream.go index 73ea2b35..1cb52760 100644 --- a/internal/engine/stream.go +++ b/internal/engine/stream.go @@ -362,8 +362,8 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { // Rate limit: wait for a token before making the LLM call if s.RateLimiter != nil { - if err := s.RateLimiter.Wait(ctx); err != nil { - ch <- StreamEvent{Type: "error", Content: err.Error()} + if waitErr := s.RateLimiter.Wait(ctx); waitErr != nil { + ch <- StreamEvent{Type: "error", Content: waitErr.Error()} return } } @@ -464,9 +464,6 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { break } if streamAttempt >= maxStreamRetries { - if thinkingOnly { - streamErr = fmt.Errorf("error_only_reasoning: model produced reasoning but no answer") - } break } retryReason := "transient stream error" diff --git a/internal/engine/validation/gen_validator.go b/internal/engine/validation/gen_validator.go index bc58043c..4121bb39 100644 --- a/internal/engine/validation/gen_validator.go +++ b/internal/engine/validation/gen_validator.go @@ -822,14 +822,14 @@ func checkGoCompilation(code string) []GenIssue { defer func() { _ = os.RemoveAll(tmpDir) }() tmpFile := filepath.Join(tmpDir, "generated.go") - if err := os.WriteFile(tmpFile, []byte(code), 0o644); err != nil { + if writeErr := os.WriteFile(tmpFile, []byte(code), 0o644); writeErr != nil { return nil } // Initialize a module in the temp directory cmd := exec.CommandContext(context.Background(), "go", "mod", "init", "temp") cmd.Dir = tmpDir - if err := cmd.Run(); err != nil { + if runErr := cmd.Run(); runErr != nil { return nil } diff --git a/internal/feature/eval/lmeval_test.go b/internal/feature/eval/lmeval_test.go index 4f1233cb..a8e6442c 100644 --- a/internal/feature/eval/lmeval_test.go +++ b/internal/feature/eval/lmeval_test.go @@ -30,8 +30,8 @@ func TestResultStore_SaveLoad(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err := os.Stat(path); err != nil { - t.Fatalf("file not created: %v", err) + if _, statErr := os.Stat(path); statErr != nil { + t.Fatalf("file not created: %v", statErr) } loaded, err := store.Load(path) diff --git a/internal/feature/taste/store_test.go b/internal/feature/taste/store_test.go index 94e03999..ffa82cfb 100644 --- a/internal/feature/taste/store_test.go +++ b/internal/feature/taste/store_test.go @@ -15,8 +15,8 @@ func TestStore_SaveAndLoad(t *testing.T) { profile.Update(CategoryNaming, Signal{Value: "camelCase", Confidence: 0.8}) profile.Update(CategoryComments, Signal{Value: "minimal", Confidence: 0.6}) - if err := store.Save("test-project", profile); err != nil { - t.Fatalf("Save: %v", err) + if saveErr := store.Save("test-project", profile); saveErr != nil { + t.Fatalf("Save: %v", saveErr) } loaded, err := store.Load("test-project") @@ -70,8 +70,8 @@ func TestStore_ExportAndImport(t *testing.T) { profile := NewProfile("export-test") profile.Update(CategoryNaming, Signal{Value: "snake_case", Confidence: 0.9}) - if err := store.Save("export-test", profile); err != nil { - t.Fatalf("Save: %v", err) + if saveErr := store.Save("export-test", profile); saveErr != nil { + t.Fatalf("Save: %v", saveErr) } data, err := store.Export("export-test") @@ -86,8 +86,8 @@ func TestStore_ExportAndImport(t *testing.T) { t.Fatalf("NewStore2: %v", err) } - if err := store2.Import(data); err != nil { - t.Fatalf("Import: %v", err) + if importErr := store2.Import(data); importErr != nil { + t.Fatalf("Import: %v", importErr) } loaded, err := store2.Load("export-test") @@ -130,8 +130,8 @@ func TestStore_Delete(t *testing.T) { store.Save("delete-me", NewProfile("delete-me")) - if err := store.Delete("delete-me"); err != nil { - t.Fatalf("Delete: %v", err) + if deleteErr := store.Delete("delete-me"); deleteErr != nil { + t.Fatalf("Delete: %v", deleteErr) } // Load should return a fresh profile after deletion. diff --git a/internal/intelligence/memory/autodream.go b/internal/intelligence/memory/autodream.go index 73315089..98e2d03f 100644 --- a/internal/intelligence/memory/autodream.go +++ b/internal/intelligence/memory/autodream.go @@ -109,8 +109,8 @@ func RunDream(ctx context.Context, cfg AutoDreamConfig, agentFn func(ctx context if e.IsDir() || filepath.Ext(e.Name()) != ".md" { continue } - data, err := os.ReadFile(filepath.Join(memDir, e.Name())) - if err != nil { + data, readErr := os.ReadFile(filepath.Join(memDir, e.Name())) + if readErr != nil { continue } memoryContent += fmt.Sprintf("--- %s ---\n%s\n\n", e.Name(), string(data)) diff --git a/internal/intelligence/memory/yaad_bridge.go b/internal/intelligence/memory/yaad_bridge.go index 36fb6754..2b61bc57 100644 --- a/internal/intelligence/memory/yaad_bridge.go +++ b/internal/intelligence/memory/yaad_bridge.go @@ -40,7 +40,7 @@ func (b *YaadBridge) init() { return } dbDir := filepath.Join(home, ".yaad", "data") - if err := os.MkdirAll(dbDir, 0o755); err != nil { + if mkErr := os.MkdirAll(dbDir, 0o755); mkErr != nil { return } dbPath := filepath.Join(dbDir, "yaad.db") diff --git a/internal/intelligence/planner/planner_test.go b/internal/intelligence/planner/planner_test.go index fa94a863..327b4e4e 100644 --- a/internal/intelligence/planner/planner_test.go +++ b/internal/intelligence/planner/planner_test.go @@ -161,7 +161,7 @@ func TestSaveAndLoad(t *testing.T) { } // Verify file exists - if _, err := os.Stat(path); os.IsNotExist(err) { + if _, statErr := os.Stat(path); os.IsNotExist(statErr) { t.Fatalf("saved file does not exist: %s", path) } diff --git a/internal/intelligence/repomap/depgraph.go b/internal/intelligence/repomap/depgraph.go index 63742bd0..952d5078 100644 --- a/internal/intelligence/repomap/depgraph.go +++ b/internal/intelligence/repomap/depgraph.go @@ -265,8 +265,8 @@ func (dg *DepGraph) BuildFromPackageJSON(projectDir string) error { Dependencies map[string]string `json:"dependencies"` DevDependencies map[string]string `json:"devDependencies"` } - if err := json.Unmarshal(data, &pkgJSON); err != nil { - return fmt.Errorf("depgraph: parse package.json: %w", err) + if unmarshalErr := json.Unmarshal(data, &pkgJSON); unmarshalErr != nil { + return fmt.Errorf("depgraph: parse package.json: %w", unmarshalErr) } dg.Root = pkgJSON.Name diff --git a/internal/intelligence/repomap/incremental_test.go b/internal/intelligence/repomap/incremental_test.go index 2a64a3c1..e778717d 100644 --- a/internal/intelligence/repomap/incremental_test.go +++ b/internal/intelligence/repomap/incremental_test.go @@ -157,8 +157,8 @@ func TestIncrementalMap_DetectsModifiedFiles(t *testing.T) { } // Modify the file (add a new function) - if err := os.WriteFile(goFile, []byte("package main\n\nfunc main() {}\n\nfunc helper() {}\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(goFile, []byte("package main\n\nfunc main() {}\n\nfunc helper() {}\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } // Second update: should detect the change @@ -207,8 +207,8 @@ func TestIncrementalMap_RemovesDeletedFiles(t *testing.T) { } // Delete helper.go - if err := os.Remove(helperFile); err != nil { - t.Fatal(err) + if removeErr := os.Remove(helperFile); removeErr != nil { + t.Fatal(removeErr) } // Second update: should detect deletion @@ -264,8 +264,8 @@ func TestIncrementalMap_SymbolPreservation(t *testing.T) { } // Modify only b.go - if err := os.WriteFile(filepath.Join(rootDir, "b.go"), []byte("package main\n\nfunc BetaV2() {}\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(rootDir, "b.go"), []byte("package main\n\nfunc BetaV2() {}\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } changed, err := im.Update(rootDir) @@ -304,11 +304,11 @@ func TestIncrementalMap_SaveAndReload(t *testing.T) { if err != nil { t.Fatal(err) } - if _, err := im1.Update(rootDir); err != nil { - t.Fatal(err) + if _, updateErr := im1.Update(rootDir); updateErr != nil { + t.Fatal(updateErr) } - if err := im1.Save(); err != nil { - t.Fatalf("Save failed: %v", err) + if saveErr := im1.Save(); saveErr != nil { + t.Fatalf("Save failed: %v", saveErr) } // Reload from cache diff --git a/internal/intelligence/repomap/semantic_search.go b/internal/intelligence/repomap/semantic_search.go index 5b9a9b1f..35aa23fc 100644 --- a/internal/intelligence/repomap/semantic_search.go +++ b/internal/intelligence/repomap/semantic_search.go @@ -399,13 +399,13 @@ func splitIntoDocuments(path, content string) []*Document { // Convert builders to documents for _, b := range builders { - content := strings.Join(b.lines, "\n") - terms := buildTermFrequency(content) + blockContent := strings.Join(b.lines, "\n") + terms := buildTermFrequency(blockContent) docID := fmt.Sprintf("%s:%s", path, b.name) docs = append(docs, &Document{ ID: docID, Path: path, - Content: content, + Content: blockContent, Terms: terms, Length: countTerms(terms), Type: b.docType, diff --git a/internal/intelligence/repomap/semantic_test.go b/internal/intelligence/repomap/semantic_test.go index 543a9b48..19668220 100644 --- a/internal/intelligence/repomap/semantic_test.go +++ b/internal/intelligence/repomap/semantic_test.go @@ -73,8 +73,8 @@ func TestSemanticIndexSaveLoad(t *testing.T) { } savePath := filepath.Join(dir, "index.gob") - if err := idx.Save(savePath); err != nil { - t.Fatal(err) + if saveErr := idx.Save(savePath); saveErr != nil { + t.Fatal(saveErr) } loaded, err := LoadSemanticIndex(savePath) diff --git a/internal/intelligence/repomap/summary.go b/internal/intelligence/repomap/summary.go index 10b134f4..90837bb0 100644 --- a/internal/intelligence/repomap/summary.go +++ b/internal/intelligence/repomap/summary.go @@ -888,7 +888,7 @@ func summaryFindJSEntryPoints(packageJSONPath string, projectDir string) []strin var pkg struct { Main string `json:"main"` } - if err := json.Unmarshal(data, &pkg); err != nil { + if unmarshalErr := json.Unmarshal(data, &pkg); unmarshalErr != nil { return nil } diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 6d5f26ee..6e447875 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -99,8 +99,8 @@ func NewLSPClient(ctx context.Context, lang string, cfg ServerConfig) (*LSPClien if err != nil { return nil, fmt.Errorf("lsp: stdout pipe: %w", err) } - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("lsp: start %s: %w", cfg.Command, err) + if startErr := cmd.Start(); startErr != nil { + return nil, fmt.Errorf("lsp: start %s: %w", cfg.Command, startErr) } c := &LSPClient{ diff --git a/internal/mcp/mcp.go b/internal/mcp/mcp.go index 25c20ea1..8d388daa 100644 --- a/internal/mcp/mcp.go +++ b/internal/mcp/mcp.go @@ -83,8 +83,8 @@ func Connect(ctx context.Context, name, command string, args ...string) (*Server if err != nil { return nil, fmt.Errorf("mcp: stdout pipe: %w", err) } - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("mcp: start %s: %w", command, err) + if startErr := cmd.Start(); startErr != nil { + return nil, fmt.Errorf("mcp: start %s: %w", command, startErr) } scanner := bufio.NewScanner(stdout) diff --git a/internal/mcp/server_test.go b/internal/mcp/server_test.go index 69d7cb8f..0288cb97 100644 --- a/internal/mcp/server_test.go +++ b/internal/mcp/server_test.go @@ -39,7 +39,7 @@ func TestInitializeHandshake(t *testing.T) { } // Check protocol version - if pv, ok := result["protocolVersion"].(string); !ok || pv != "2025-03-26" { + if pv, typeOK := result["protocolVersion"].(string); !typeOK || pv != "2025-03-26" { t.Errorf("expected protocolVersion 2025-03-26, got %v", result["protocolVersion"]) } diff --git a/internal/mcp/ws.go b/internal/mcp/ws.go index d4411716..54728173 100644 --- a/internal/mcp/ws.go +++ b/internal/mcp/ws.go @@ -174,9 +174,9 @@ func wsDial(ctx context.Context, rawURL string, headers map[string]string) (net. } b.WriteString("\r\n") - if _, err := io.WriteString(conn, b.String()); err != nil { + if _, writeErr := io.WriteString(conn, b.String()); writeErr != nil { _ = conn.Close() - return nil, nil, err + return nil, nil, writeErr } rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) diff --git a/internal/multiagent/parallel/parallel_test.go b/internal/multiagent/parallel/parallel_test.go index 832bf114..0766a26a 100644 --- a/internal/multiagent/parallel/parallel_test.go +++ b/internal/multiagent/parallel/parallel_test.go @@ -253,18 +253,18 @@ func TestCleanupRemovesAllWorktrees(t *testing.T) { // All worktree paths should exist before cleanup. for _, p := range paths { - if _, err := os.Stat(p); err != nil { - t.Fatalf("worktree %s should exist before cleanup: %v", p, err) + if _, statErr := os.Stat(p); statErr != nil { + t.Fatalf("worktree %s should exist before cleanup: %v", p, statErr) } } - if err := pool.Cleanup(); err != nil { - t.Fatalf("Cleanup: %v", err) + if cleanupErr := pool.Cleanup(); cleanupErr != nil { + t.Fatalf("Cleanup: %v", cleanupErr) } // All worktree paths should be gone after cleanup. for _, p := range paths { - if _, err := os.Stat(p); !os.IsNotExist(err) { + if _, statErr := os.Stat(p); !os.IsNotExist(statErr) { t.Errorf("worktree %s should not exist after cleanup", p) } } diff --git a/internal/multiagent/parallel/worktree_manager_test.go b/internal/multiagent/parallel/worktree_manager_test.go index 83e65cf5..c9642c20 100644 --- a/internal/multiagent/parallel/worktree_manager_test.go +++ b/internal/multiagent/parallel/worktree_manager_test.go @@ -397,8 +397,8 @@ func TestIsClean(t *testing.T) { // Create an uncommitted file. dirty := filepath.Join(wt.Path, "dirty.txt") - if err := os.WriteFile(dirty, []byte("uncommitted\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(dirty, []byte("uncommitted\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } clean, err = wm.IsClean(wt.ID) @@ -428,8 +428,8 @@ func TestGetDiff(t *testing.T) { // Make a commit in the worktree. newFile := filepath.Join(wt.Path, "diffed.txt") - if err := os.WriteFile(newFile, []byte("new content\n"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(newFile, []byte("new content\n"), 0o644); writeErr != nil { + t.Fatal(writeErr) } runGitIn(t, wt.Path, "add", "diffed.txt") runGitIn(t, wt.Path, "commit", "-m", "add diffed.txt") diff --git a/internal/observability/insights.go b/internal/observability/insights.go index 9c8ee048..994713e3 100644 --- a/internal/observability/insights.go +++ b/internal/observability/insights.go @@ -45,16 +45,16 @@ func GenerateInsights(days int, analysisFn func(content string) ([]InsightsFacet if e.IsDir() { continue } - info, err := e.Info() - if err != nil || info.ModTime().Before(cutoff) { + info, infoErr := e.Info() + if infoErr != nil || info.ModTime().Before(cutoff) { continue } if filepath.Ext(e.Name()) != ".jsonl" { continue } - data, err := os.ReadFile(filepath.Join(sessDir, e.Name())) - if err != nil { + data, readErr := os.ReadFile(filepath.Join(sessDir, e.Name())) + if readErr != nil { continue } transcripts = append(transcripts, string(data)) diff --git a/internal/observability/metrics/metrics.go b/internal/observability/metrics/metrics.go index 3220aa33..21f7829f 100644 --- a/internal/observability/metrics/metrics.go +++ b/internal/observability/metrics/metrics.go @@ -151,8 +151,8 @@ func (r *Registry) Counter(name string) *Counter { r.mu.Lock() defer r.mu.Unlock() - if c, ok := r.counters[name]; ok { - return c + if existingC, ok := r.counters[name]; ok { + return existingC } c = &Counter{} r.counters[name] = c @@ -170,8 +170,8 @@ func (r *Registry) Gauge(name string) *Gauge { r.mu.Lock() defer r.mu.Unlock() - if g, ok := r.gauges[name]; ok { - return g + if existingG, ok := r.gauges[name]; ok { + return existingG } g = &Gauge{} r.gauges[name] = g @@ -189,8 +189,8 @@ func (r *Registry) Timer(name string) *Timer { r.mu.Lock() defer r.mu.Unlock() - if t, ok := r.timers[name]; ok { - return t + if existingT, ok := r.timers[name]; ok { + return existingT } t = NewTimer() r.timers[name] = t diff --git a/internal/permissions/guardian_test.go b/internal/permissions/guardian_test.go index 0847ce23..68d39b26 100644 --- a/internal/permissions/guardian_test.go +++ b/internal/permissions/guardian_test.go @@ -126,9 +126,9 @@ func TestGuardian_CircuitBreakerResetsOnAllow(t *testing.T) { g.ChatFn = chatFn2 for i := 0; i < 3; i++ { - _, err := g.Review(ctx, req) - if err != nil { - t.Fatalf("unexpected circuit breaker on denial %d after reset: %v", i+1, err) + _, reviewErr := g.Review(ctx, req) + if reviewErr != nil { + t.Fatalf("unexpected circuit breaker on denial %d after reset: %v", i+1, reviewErr) } } diff --git a/internal/plugin/plugin_test.go b/internal/plugin/plugin_test.go index 9eb903fc..94e141c6 100644 --- a/internal/plugin/plugin_test.go +++ b/internal/plugin/plugin_test.go @@ -84,8 +84,8 @@ func TestInstallAndUninstall(t *testing.T) { t.Fatalf("expected 1 plugin, got %d", len(plugins)) } - if err := Uninstall("test-plugin"); err != nil { - t.Fatal(err) + if uninstallErr := Uninstall("test-plugin"); uninstallErr != nil { + t.Fatal(uninstallErr) } plugins, err = List() diff --git a/internal/plugin/registry.go b/internal/plugin/registry.go index 0675980d..1ba6b078 100644 --- a/internal/plugin/registry.go +++ b/internal/plugin/registry.go @@ -213,14 +213,14 @@ func (rc *RegistryClient) Install(repo, skillName, scope string) (string, error) url := "https://github.com/" + repo + ".git" cmd := exec.CommandContext(context.Background(), "git", "clone", "--depth", "1", "--single-branch", url, tmpDir) - if out, err := cmd.CombinedOutput(); err != nil { - return "", fmt.Errorf("git clone failed: %s\n%s", err, string(out)) + if out, cloneErr := cmd.CombinedOutput(); cloneErr != nil { + return "", fmt.Errorf("git clone failed: %s\n%s", cloneErr, string(out)) } // Discover skills in the cloned repo. skillsRoot := tmpDir // Check for skills/ subdirectory (agentskills.io convention). - if info, err := os.Stat(filepath.Join(tmpDir, "skills")); err == nil && info.IsDir() { + if info, statErr := os.Stat(filepath.Join(tmpDir, "skills")); statErr == nil && info.IsDir() { skillsRoot = filepath.Join(tmpDir, "skills") } diff --git a/internal/resilience/circuit.go b/internal/resilience/circuit.go index be6665a5..1300cd57 100644 --- a/internal/resilience/circuit.go +++ b/internal/resilience/circuit.go @@ -218,8 +218,8 @@ func (m *Manager) Get(name string) *Breaker { m.mu.Lock() defer m.mu.Unlock() // Double-check - if b, ok := m.breakers[name]; ok { - return b + if existingB, ok := m.breakers[name]; ok { + return existingB } b = New(m.config) m.breakers[name] = b diff --git a/internal/resilience/ratelimit/ratelimit.go b/internal/resilience/ratelimit/ratelimit.go index 4a178750..7cb99328 100644 --- a/internal/resilience/ratelimit/ratelimit.go +++ b/internal/resilience/ratelimit/ratelimit.go @@ -136,8 +136,8 @@ func (m *Manager) Get(name string, cfg Config) *Limiter { m.mu.Lock() defer m.mu.Unlock() - if l, ok := m.limiters[name]; ok { - return l + if existingL, ok := m.limiters[name]; ok { + return existingL } l = New(cfg) m.limiters[name] = l diff --git a/internal/rules/rules_test.go b/internal/rules/rules_test.go index 774de377..cf6f215a 100644 --- a/internal/rules/rules_test.go +++ b/internal/rules/rules_test.go @@ -372,8 +372,8 @@ func TestRoundTrip_ClaudeCodeToHawk(t *testing.T) { } // Export to hawk. - if err := Export(dir, FormatHawk, imported); err != nil { - t.Fatal(err) + if exportErr := Export(dir, FormatHawk, imported); exportErr != nil { + t.Fatal(exportErr) } // Re-import from hawk. @@ -415,15 +415,15 @@ func TestRoundTrip_CursorToHawkToClaudeCode(t *testing.T) { } // Export to hawk, then to Claude Code. - if err := Export(dir, FormatHawk, imported); err != nil { - t.Fatal(err) + if exportErr := Export(dir, FormatHawk, imported); exportErr != nil { + t.Fatal(exportErr) } hawkRules, err := Import(dir, FormatHawk) if err != nil { t.Fatal(err) } - if err := Export(dir, FormatClaudeCode, hawkRules); err != nil { - t.Fatal(err) + if exportErr := Export(dir, FormatClaudeCode, hawkRules); exportErr != nil { + t.Fatal(exportErr) } // Re-import from Claude Code. @@ -456,15 +456,15 @@ func TestRoundTrip_HawkToCursorToHawk(t *testing.T) { } // Export to hawk, import, export to cursor, import from cursor, export back to hawk. - if err := Export(dir, FormatHawk, original); err != nil { - t.Fatal(err) + if exportErr := Export(dir, FormatHawk, original); exportErr != nil { + t.Fatal(exportErr) } fromHawk, err := Import(dir, FormatHawk) if err != nil { t.Fatal(err) } - if err := Export(dir, FormatCursor, fromHawk); err != nil { - t.Fatal(err) + if exportErr := Export(dir, FormatCursor, fromHawk); exportErr != nil { + t.Fatal(exportErr) } fromCursor, err := Import(dir, FormatCursor) if err != nil { @@ -473,8 +473,8 @@ func TestRoundTrip_HawkToCursorToHawk(t *testing.T) { // Re-export back to hawk in a fresh directory. dir2 := t.TempDir() - if err := Export(dir2, FormatHawk, fromCursor); err != nil { - t.Fatal(err) + if exportErr := Export(dir2, FormatHawk, fromCursor); exportErr != nil { + t.Fatal(exportErr) } final, err := Import(dir2, FormatHawk) if err != nil { diff --git a/internal/sandbox/devenv_test.go b/internal/sandbox/devenv_test.go index 2b0f14b0..0c10fb1b 100644 --- a/internal/sandbox/devenv_test.go +++ b/internal/sandbox/devenv_test.go @@ -69,8 +69,8 @@ func TestDevEnvManager_GetOrBuild_RebuildsOnChange(t *testing.T) { } // Modify Dockerfile - if err := os.WriteFile(dockerfile, []byte("FROM alpine:latest\nRUN echo v2"), 0o644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(dockerfile, []byte("FROM alpine:latest\nRUN echo v2"), 0o644); writeErr != nil { + t.Fatal(writeErr) } tag2, err := mgr.GetOrBuild(ctx, dockerfile) diff --git a/internal/sandbox/isolation_verify_test.go b/internal/sandbox/isolation_verify_test.go index 97df0b4b..8acf65e7 100644 --- a/internal/sandbox/isolation_verify_test.go +++ b/internal/sandbox/isolation_verify_test.go @@ -47,11 +47,11 @@ func TestVerify_ContainerDoesNotExposeHostHawkHome(t *testing.T) { t.Fatal(err) } hawkEnv := filepath.Join(home, ".hawk", "env") - if _, err := os.Stat(hawkEnv); err != nil { + if _, statErr := os.Stat(hawkEnv); statErr != nil { // Create a marker file so we can detect accidental host mount exposure. _ = os.MkdirAll(filepath.Dir(hawkEnv), 0o700) - if err := os.WriteFile(hawkEnv, []byte("export VERIFY_HAWK_HOME_SECRET=1\n"), 0o600); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(hawkEnv, []byte("export VERIFY_HAWK_HOME_SECRET=1\n"), 0o600); writeErr != nil { + t.Fatal(writeErr) } t.Cleanup(func() { _ = os.Remove(hawkEnv) }) } @@ -63,8 +63,8 @@ func TestVerify_ContainerDoesNotExposeHostHawkHome(t *testing.T) { } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() - if err := cs.Start(ctx); err != nil { - t.Fatalf("container start: %v", err) + if startErr := cs.Start(ctx); startErr != nil { + t.Fatalf("container start: %v", startErr) } t.Cleanup(func() { _ = cs.Stop() }) diff --git a/internal/sandbox/netproxy_test.go b/internal/sandbox/netproxy_test.go index c5637a5a..80d7affc 100644 --- a/internal/sandbox/netproxy_test.go +++ b/internal/sandbox/netproxy_test.go @@ -409,8 +409,8 @@ func TestStop_Clean(t *testing.T) { conn.Close() // Stop the proxy. - if err := proxy.Stop(); err != nil { - t.Fatalf("Stop() error = %v", err) + if stopErr := proxy.Stop(); stopErr != nil { + t.Fatalf("Stop() error = %v", stopErr) } // Give it a moment to close. diff --git a/internal/sandbox/snapshot_sandbox_test.go b/internal/sandbox/snapshot_sandbox_test.go index 7de38d3e..f765b1f1 100644 --- a/internal/sandbox/snapshot_sandbox_test.go +++ b/internal/sandbox/snapshot_sandbox_test.go @@ -101,8 +101,8 @@ func TestPauseAndResume(t *testing.T) { os.WriteFile(filepath.Join(workDir, "new.txt"), []byte("new file"), 0o644) // Pause. - if err := mgr.Pause(sb.ID); err != nil { - t.Fatalf("Pause failed: %v", err) + if pauseErr := mgr.Pause(sb.ID); pauseErr != nil { + t.Fatalf("Pause failed: %v", pauseErr) } if sb.Status != "paused" { t.Errorf("expected status paused, got %q", sb.Status) @@ -113,8 +113,8 @@ func TestPauseAndResume(t *testing.T) { // Verify persisted to disk. path := filepath.Join(dir, sb.ID+".json") - if _, err := os.Stat(path); err != nil { - t.Errorf("expected persisted file at %s: %v", path, err) + if _, statErr := os.Stat(path); statErr != nil { + t.Errorf("expected persisted file at %s: %v", path, statErr) } // Wipe work dir to simulate environment teardown. diff --git a/internal/session/named_checkpoint.go b/internal/session/named_checkpoint.go index fbd093d3..e48e593e 100644 --- a/internal/session/named_checkpoint.go +++ b/internal/session/named_checkpoint.go @@ -74,8 +74,8 @@ func SaveNamedCheckpoint(name string, s *Session) (*NamedCheckpoint, error) { return nil, fmt.Errorf("marshal session: %w", err) } var snap Session - if err := json.Unmarshal(raw, &snap); err != nil { - return nil, fmt.Errorf("copy session: %w", err) + if unmarshalErr := json.Unmarshal(raw, &snap); unmarshalErr != nil { + return nil, fmt.Errorf("copy session: %w", unmarshalErr) } cp := &NamedCheckpoint{ @@ -85,8 +85,8 @@ func SaveNamedCheckpoint(name string, s *Session) (*NamedCheckpoint, error) { } dir := namedCheckpointsDir() - if err := os.MkdirAll(dir, 0o755); err != nil { - return nil, fmt.Errorf("create checkpoints directory: %w", err) + if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil { + return nil, fmt.Errorf("create checkpoints directory: %w", mkErr) } data, err := json.MarshalIndent(cp, "", " ") diff --git a/internal/session/sqlite_store.go b/internal/session/sqlite_store.go index 6aec9ce2..d5788e9b 100644 --- a/internal/session/sqlite_store.go +++ b/internal/session/sqlite_store.go @@ -478,8 +478,8 @@ func (s *SQLiteStore) DeleteSession(id string) error { defer func() { _ = tx.Rollback() }() // Delete messages first (FK constraint). - if _, err := tx.ExecContext(context.Background(), "DELETE FROM messages WHERE session_id = ?", id); err != nil { - return fmt.Errorf("delete messages: %w", err) + if _, execErr := tx.ExecContext(context.Background(), "DELETE FROM messages WHERE session_id = ?", id); execErr != nil { + return fmt.Errorf("delete messages: %w", execErr) } result, err := tx.ExecContext(context.Background(), "DELETE FROM sessions WHERE id = ?", id) @@ -636,8 +636,8 @@ func (s *SQLiteStore) Compact(sessionID string, keepLast int) error { // Recalculate total tokens. var totalTokens int row := tx.QueryRowContext(context.Background(), "SELECT COALESCE(SUM(tokens), 0) FROM messages WHERE session_id = ?", sessionID) - if err := row.Scan(&totalTokens); err != nil { - return fmt.Errorf("sum tokens: %w", err) + if scanErr := row.Scan(&totalTokens); scanErr != nil { + return fmt.Errorf("sum tokens: %w", scanErr) } _, err = tx.ExecContext(context.Background(), "UPDATE sessions SET total_tokens = ?, updated_at = ? WHERE id = ?", diff --git a/internal/snapshot/workspace_test.go b/internal/snapshot/workspace_test.go index cb498fe8..ac41ccb7 100644 --- a/internal/snapshot/workspace_test.go +++ b/internal/snapshot/workspace_test.go @@ -135,7 +135,9 @@ func TestRestore_BringsBackOriginalState(t *testing.T) { // Make changes os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\n// CHANGED\nfunc main() {}\n"), 0o644) os.WriteFile(filepath.Join(dir, "new_file.go"), []byte("package main\n"), 0o644) - os.Remove(filepath.Join(dir, "README.md")) + if removeErr := os.Remove(filepath.Join(dir, "README.md")); removeErr != nil { + t.Fatalf("remove README: %v", removeErr) + } // Verify changes exist content, _ := os.ReadFile(filepath.Join(dir, "main.go")) @@ -159,7 +161,7 @@ func TestRestore_BringsBackOriginalState(t *testing.T) { } // Verify new file was deleted - if _, err := os.Stat(filepath.Join(dir, "new_file.go")); !os.IsNotExist(err) { + if _, statErr := os.Stat(filepath.Join(dir, "new_file.go")); !os.IsNotExist(statErr) { t.Error("new_file.go should be deleted after restore") } diff --git a/internal/tool/api_compat.go b/internal/tool/api_compat.go index ab932978..203c64c3 100644 --- a/internal/tool/api_compat.go +++ b/internal/tool/api_compat.go @@ -688,8 +688,8 @@ func (t *APICompatTool) Execute(ctx context.Context, input json.RawMessage) (str if savePath == "" { savePath = params.PackagePath + "/.api_baseline.json" } - if err := t.checker.SaveSnapshot(current, savePath); err != nil { - return "", err + if saveErr := t.checker.SaveSnapshot(current, savePath); saveErr != nil { + return "", saveErr } return fmt.Sprintf("Saved API baseline to %s (%d functions, %d types, %d interfaces)", savePath, len(current.Functions), len(current.Types), len(current.Interfaces)), nil diff --git a/internal/tool/api_compat_test.go b/internal/tool/api_compat_test.go index 52ff090f..f5e8aeb3 100644 --- a/internal/tool/api_compat_test.go +++ b/internal/tool/api_compat_test.go @@ -410,8 +410,8 @@ func TestSaveAndLoadSnapshot(t *testing.T) { } savePath := filepath.Join(t.TempDir(), "baseline.json") - if err := cc.SaveSnapshot(snap, savePath); err != nil { - t.Fatalf("SaveSnapshot failed: %v", err) + if saveErr := cc.SaveSnapshot(snap, savePath); saveErr != nil { + t.Fatalf("SaveSnapshot failed: %v", saveErr) } loaded, err := cc.LoadSnapshot(savePath) diff --git a/internal/tool/backup.go b/internal/tool/backup.go index c6569244..a7cd890e 100644 --- a/internal/tool/backup.go +++ b/internal/tool/backup.go @@ -67,8 +67,8 @@ func RestoreFromBackup(path string) error { for _, e := range entries { name := e.Name() if len(name) > len(baseName)+1 && name[:len(baseName)] == baseName { - info, err := e.Info() - if err != nil { + info, infoErr := e.Info() + if infoErr != nil { continue } if info.ModTime().After(latestTime) { @@ -124,16 +124,16 @@ func UndoLatest() (string, error) { continue } subDir := filepath.Join(backupsRoot, d.Name()) - entries, err := os.ReadDir(subDir) - if err != nil { + entries, readErr := os.ReadDir(subDir) + if readErr != nil { continue } for _, e := range entries { if strings.HasPrefix(e.Name(), ".") { continue } - info, err := e.Info() - if err != nil { + info, infoErr := e.Info() + if infoErr != nil { continue } if info.ModTime().After(bestTime) { diff --git a/internal/tool/codegraph.go b/internal/tool/codegraph.go index 2464baf6..fd6d6501 100644 --- a/internal/tool/codegraph.go +++ b/internal/tool/codegraph.go @@ -700,7 +700,7 @@ func crossRepoCodeGraph(query string, maxNodes int) (string, error) { for _, entry := range entries { if entry.IsDir() { dbPath := filepath.Join(parentDir, entry.Name(), ".codegraph", "codegraph.db") - if _, err := os.Stat(dbPath); err == nil { + if _, statErr := os.Stat(dbPath); statErr == nil { repos = append(repos, filepath.Join(parentDir, entry.Name())) } } diff --git a/internal/tool/git_commit_test.go b/internal/tool/git_commit_test.go index dbf2377a..e9e50d48 100644 --- a/internal/tool/git_commit_test.go +++ b/internal/tool/git_commit_test.go @@ -85,8 +85,8 @@ func TestAutoCommitAndRevert(t *testing.T) { } // Revert the auto-commit. - if err := RevertLastAutoCommit(); err != nil { - t.Fatalf("RevertLastAutoCommit: %v", err) + if revertErr := RevertLastAutoCommit(); revertErr != nil { + t.Fatalf("RevertLastAutoCommit: %v", revertErr) } // After revert, HEAD should point to the commit before auto-commit. diff --git a/internal/tool/git_fs.go b/internal/tool/git_fs.go index 38a41ae5..163995a0 100644 --- a/internal/tool/git_fs.go +++ b/internal/tool/git_fs.go @@ -30,9 +30,9 @@ func ReadGitState(dir string) (*GitState, error) { // If .git is a file, it's a worktree reference worktree := false if !info.IsDir() { - data, err := os.ReadFile(gitDir) - if err != nil { - return nil, err + data, readErr := os.ReadFile(gitDir) + if readErr != nil { + return nil, readErr } line := strings.TrimSpace(string(data)) if strings.HasPrefix(line, "gitdir: ") { diff --git a/internal/tool/lsp.go b/internal/tool/lsp.go index 7cfbc989..2a3da586 100644 --- a/internal/tool/lsp.go +++ b/internal/tool/lsp.go @@ -143,12 +143,12 @@ func lspReferences(root, filePath string, line int, symbol string) (string, erro // Get symbol name sym := symbol if sym == "" && line > 0 { - absPath, err := filepath.Abs(filePath) - if err != nil { + absPath, absErr := filepath.Abs(filePath) + if absErr != nil { absPath = filePath } - source, err := os.ReadFile(absPath) - if err == nil { + source, readErr := os.ReadFile(absPath) + if readErr == nil { lines := strings.Split(string(source), "\n") if line <= len(lines) { sym = extractSymbolFromLine(lines[line-1]) diff --git a/internal/tool/tool_integration_test.go b/internal/tool/tool_integration_test.go index a8b6db3e..1c030f43 100644 --- a/internal/tool/tool_integration_test.go +++ b/internal/tool/tool_integration_test.go @@ -27,8 +27,8 @@ func TestIntegration_BashThenRead(t *testing.T) { } // Verify the file exists on disk. - if _, err := os.Stat(filePath); err != nil { - t.Fatalf("expected file to exist after bash: %v", err) + if _, statErr := os.Stat(filePath); statErr != nil { + t.Fatalf("expected file to exist after bash: %v", statErr) } // Read reads it back. diff --git a/internal/tool/transaction.go b/internal/tool/transaction.go index ac45d6f0..31d06524 100644 --- a/internal/tool/transaction.go +++ b/internal/tool/transaction.go @@ -115,12 +115,12 @@ func (tx *Transaction) Add(op FileOperation) error { if info.IsDir() { return fmt.Errorf("cannot rename %s: is a directory", op.OldPath) } - if _, err := os.Stat(op.Path); err == nil { + if _, statErr := os.Stat(op.Path); statErr == nil { return fmt.Errorf("cannot rename to %s: file already exists", op.Path) } - data, err := os.ReadFile(op.OldPath) - if err != nil { - return fmt.Errorf("cannot read %s for backup: %w", op.OldPath, err) + data, readErr := os.ReadFile(op.OldPath) + if readErr != nil { + return fmt.Errorf("cannot read %s for backup: %w", op.OldPath, readErr) } op.OldContent = data if op.Mode == 0 {