From f4c3354faf072589070644d6c3daa8da4c6f3775 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sat, 20 Jun 2026 10:36:12 +0530 Subject: [PATCH 1/3] feat(storage): add storage policy cleanup and centralize hawk data paths Introduce internal/storage helpers with policy tests, route session and plugin persistence through the shared layout, and trim duplicated path handling across cmd and internal packages. --- cmd/agent.go | 2 +- cmd/audit.go | 8 +- cmd/bg_sessions.go | 5 +- cmd/chat.go | 13 +- cmd/chat_commands_session.go | 14 +- cmd/chat_commands_util.go | 2 +- cmd/chat_focus.go | 4 +- cmd/chat_subcommand_memory.go | 6 +- cmd/chat_subcommand_recipe.go | 2 +- cmd/chat_subcommand_simple.go | 11 +- cmd/chat_tools.go | 16 -- cmd/cmdhistory_cmd.go | 7 +- cmd/daemon.go | 10 +- cmd/diagnostics.go | 10 +- cmd/dx.go | 5 +- cmd/errors.go | 52 +++---- cmd/exec.go | 5 +- cmd/exec_test.go | 9 +- cmd/feedback.go | 10 +- cmd/history.go | 9 +- cmd/history_test.go | 6 +- cmd/manpage.go | 8 +- cmd/permissions_center.go | 15 +- cmd/plan.go | 28 ++-- cmd/review.go | 4 - cmd/review_store.go | 8 +- cmd/root.go | 4 +- cmd/rules.go | 4 +- cmd/storage_policy_test.go | 33 ++++ cmd/tips.go | 8 +- external/eyrie | 2 +- internal/auth/auth.go | 20 +-- internal/auth/auth_test.go | 26 +--- internal/autoinit/autoinit.go | 7 +- internal/config/config.go | 18 +-- internal/config/config_test.go | 84 ++-------- internal/config/credentials_store.go | 8 +- internal/config/deployments_ui.go | 25 +-- internal/config/settings.go | 54 +------ internal/config/xiaomi_setup_test.go | 2 + internal/context/rules.go | 10 +- internal/context/rules_test.go | 16 +- internal/daemon/daemon.go | 5 +- internal/daemon/routes_sessions.go | 13 +- internal/engine/adaptive_prompt.go | 7 +- internal/engine/bmad_features_test.go | 3 +- internal/engine/coding_soul.go | 16 +- internal/engine/compact/session_memory.go | 7 +- internal/engine/context_compaction.go | 6 +- internal/engine/context_governor_test.go | 3 +- internal/engine/cost/cost_tracker.go | 8 +- internal/engine/errs/error_context.go | 2 +- internal/engine/errs/error_patterns.go | 5 +- internal/engine/hints_loader.go | 2 +- internal/engine/integration.go | 10 +- internal/engine/lifecycle_service.go | 4 +- internal/engine/multi_repo.go | 6 +- internal/engine/project/project_context.go | 10 +- internal/engine/prompt/prompt_optimizer.go | 5 +- internal/engine/prompt/prompt_tuner.go | 5 +- internal/engine/scaffold/fewshot.go | 7 +- internal/engine/self_improve.go | 5 +- internal/engine/session.go | 2 +- internal/engine/stream_tool_exec.go | 2 +- internal/engine/tool_output_spill.go | 6 +- internal/engine/transfer.go | 7 +- internal/feature/eval/cache.go | 7 +- internal/feature/eval/store.go | 7 +- internal/feature/shellmode/modes.go | 14 +- internal/feature/taste/store.go | 13 +- internal/hooks/hooks.go | 18 +-- internal/intelligence/memory/auto_memory.go | 7 +- internal/intelligence/memory/autodream.go | 5 +- internal/intelligence/memory/autofile.go | 4 +- internal/intelligence/memory/evolving.go | 7 +- internal/intelligence/memory/memory.go | 5 +- internal/intelligence/memory/memory_test.go | 7 +- internal/intelligence/memory/zenbrain.go | 5 +- internal/intelligence/planner/planner.go | 8 +- .../intelligence/repomap/incremental_map.go | 11 +- internal/intelligence/repomap/patterns.go | 24 +-- internal/lsp/config.go | 11 +- internal/multiagent/agents/agents.go | 24 +-- internal/multiagent/agents/agents_test.go | 7 +- internal/multiagent/agents/persona.go | 7 +- .../agents/persona_registry_test.go | 10 +- .../multiagent/parallel/worktree_manager.go | 6 +- internal/observability/analytics.go | 5 +- internal/observability/analytics_test.go | 5 +- internal/observability/insights.go | 8 +- internal/permissions/persist.go | 4 +- internal/permissions/rules_test.go | 2 +- internal/plugin/auto_skill.go | 4 +- internal/plugin/auto_skill_audit_test.go | 6 +- internal/plugin/bundled_skills.go | 5 +- internal/plugin/dynamic.go | 9 +- internal/plugin/feedback.go | 7 +- internal/plugin/manager.go | 8 +- internal/plugin/manager_test.go | 12 +- internal/plugin/plugin.go | 5 +- internal/plugin/registry.go | 20 +-- internal/plugin/skills_auto.go | 12 +- internal/plugin/wizard.go | 6 +- internal/prompts/agents_accumulator.go | 13 +- internal/prompts/agents_accumulator_test.go | 4 +- internal/prompts/loader.go | 15 +- internal/recipe/providers.go | 7 +- internal/recipe/recipe.go | 7 +- internal/resilience/health/diagnostics.go | 41 +---- internal/rules/formats.go | 6 +- internal/rules/formats_test.go | 2 +- internal/rules/rules.go | 4 +- internal/rules/rules_test.go | 8 +- internal/sandbox/approval.go | 7 +- internal/sandbox/container.go | 12 +- internal/sandbox/container_test.go | 9 +- internal/sandbox/manager.go | 9 +- internal/sandbox/manager_test.go | 8 +- internal/sandbox/mode.go | 14 +- internal/sandbox/runtime_deps.go | 6 +- internal/sandbox/runtime_deps_test.go | 4 +- internal/session/autosave_test.go | 16 +- internal/session/benchmark_test.go | 4 +- internal/session/export_redact_test.go | 4 +- internal/session/persist.go | 6 +- internal/session/persist_integrity_test.go | 15 +- internal/session/session.go | 23 ++- internal/session/session_concurrent_test.go | 6 +- internal/session/session_safety_test.go | 18 ++- internal/session/snapshot.go | 5 +- internal/session/storage_policy_test.go | 25 +++ internal/session/storage_test_helpers_test.go | 13 ++ internal/snapshot/snapshot.go | 6 +- internal/snapshot/workspace.go | 6 +- internal/snapshot/workspace_test.go | 11 +- internal/storage/paths.go | 147 ++++++++++++++++++ internal/storage/paths_test.go | 58 +++++++ internal/tool/audit.go | 8 +- internal/tool/backup.go | 8 +- internal/tool/devenv.go | 5 +- internal/tool/safety.go | 6 + internal/tool/skill.go | 15 +- internal/tool/tool_test.go | 4 +- internal/tool/treesitter.go | 4 +- internal/tool/workflow.go | 34 ++-- lefthook.yml | 2 +- 146 files changed, 843 insertions(+), 843 deletions(-) create mode 100644 cmd/storage_policy_test.go create mode 100644 internal/session/storage_policy_test.go create mode 100644 internal/session/storage_test_helpers_test.go create mode 100644 internal/storage/paths.go create mode 100644 internal/storage/paths_test.go diff --git a/cmd/agent.go b/cmd/agent.go index ff4ecce7..8c4ad462 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -15,7 +15,7 @@ import ( var agentCmd = &cobra.Command{ Use: "agent", Short: "Manage custom agent personas", - Long: "Create, list, and manage custom agent personas stored in ~/.hawk/agents/.", + Long: "Create, list, and manage custom agent personas stored in Hawk user state.", } var agentListCmd = &cobra.Command{ diff --git a/cmd/audit.go b/cmd/audit.go index d3c9d537..e3332bf6 100644 --- a/cmd/audit.go +++ b/cmd/audit.go @@ -10,6 +10,7 @@ import ( "time" "github.com/GrayCodeAI/hawk/internal/hooks/audit" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -175,16 +176,11 @@ type SessionInfo struct { } func discoverSessions(days int, projectFilter string) ([]SessionInfo, error) { - home, err := os.UserHomeDir() - if err != nil { - return nil, err - } - cutoff := time.Now().AddDate(0, 0, -days) var sessions []SessionInfo // Scan hawk sessions directory - hawkDir := filepath.Join(home, ".hawk", "sessions") + hawkDir := storage.SessionsDir() entries, err := os.ReadDir(hawkDir) if err != nil && !os.IsNotExist(err) { return nil, err diff --git a/cmd/bg_sessions.go b/cmd/bg_sessions.go index 1ea3357b..c4822cd5 100644 --- a/cmd/bg_sessions.go +++ b/cmd/bg_sessions.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/home" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -19,8 +19,7 @@ import ( // ───────────────────────────────────────────────────────────────────────────── func bgSessionsDir() string { - home := home.Dir() - return filepath.Join(home, ".hawk", "bg-sessions") + return filepath.Join(storage.StateDir(), "bg-sessions") } // BGSessionInfo tracks a running background session. diff --git a/cmd/chat.go b/cmd/chat.go index c6c8bb22..2f658f3c 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -30,7 +30,6 @@ import ( "github.com/GrayCodeAI/hawk/internal/engine" "github.com/GrayCodeAI/hawk/internal/feature/shellmode" "github.com/GrayCodeAI/hawk/internal/feature/taste" - "github.com/GrayCodeAI/hawk/internal/home" "github.com/GrayCodeAI/hawk/internal/intelligence/memory" "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" "github.com/GrayCodeAI/hawk/internal/observability/logger" @@ -38,6 +37,7 @@ import ( "github.com/GrayCodeAI/hawk/internal/sandbox" "github.com/GrayCodeAI/hawk/internal/session" "github.com/GrayCodeAI/hawk/internal/startup" + hawkstorage "github.com/GrayCodeAI/hawk/internal/storage" "github.com/GrayCodeAI/hawk/internal/system/staleness" "github.com/GrayCodeAI/hawk/internal/ui/icons" ) @@ -152,11 +152,9 @@ func newChatModel(ref *progRef, systemPrompt string, settings hawkconfig.Setting // Initialize conversation DAG for branching support startup.MarkPhase("newChatModel:dag") - if home, err := os.UserHomeDir(); err == nil { - dagPath := filepath.Join(home, ".hawk", "sessions", "convo.db") - if dag, err := storage.NewDAG(dagPath, sid); err == nil { - sess.SetConvoDAG(dag) - } + dagPath := filepath.Join(hawkstorage.SessionsDir(), "convo.db") + if dag, err := storage.NewDAG(dagPath, sid); err == nil { + sess.SetConvoDAG(dag) } startup.EndPhase("newChatModel:dag") @@ -241,8 +239,7 @@ func newChatModel(ref *progRef, systemPrompt string, settings hawkconfig.Setting // Check for crash recovery startup.MarkPhase("newChatModel:crash-recovery") if recovered := session.CheckForRecovery(); len(recovered) > 0 { - home := home.Dir() - walDir := filepath.Join(home, ".hawk", "sessions") + walDir := hawkstorage.SessionsDir() for _, rid := range recovered { if rid == sid { continue // current session WAL diff --git a/cmd/chat_commands_session.go b/cmd/chat_commands_session.go index e1330f41..e2ea8680 100644 --- a/cmd/chat_commands_session.go +++ b/cmd/chat_commands_session.go @@ -12,8 +12,8 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/GrayCodeAI/eyrie/client" - "github.com/GrayCodeAI/hawk/internal/home" "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" ) // saveSession persists the current session to disk. @@ -234,8 +234,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string return m, nil case "/export": - homeDir := home.Dir() - exportDir := filepath.Join(homeDir, ".hawk", "exports") + exportDir := filepath.Join(storage.StateDir(), "exports") _ = os.MkdirAll(exportDir, 0o755) exportPath := filepath.Join(exportDir, m.sessionID+".md") var md strings.Builder @@ -258,8 +257,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string return m, nil case "/share": - homeDir := home.Dir() - exportDir := filepath.Join(homeDir, ".hawk", "exports") + exportDir := filepath.Join(storage.StateDir(), "exports") _ = os.MkdirAll(exportDir, 0o755) exportPath := filepath.Join(exportDir, m.sessionID+".md") var md strings.Builder @@ -286,8 +284,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string return m, nil } newName := parts[1] - homeDir := home.Dir() - sessDir := filepath.Join(homeDir, ".hawk", "sessions") + sessDir := storage.SessionsDir() oldPath := filepath.Join(sessDir, m.sessionID+".jsonl") newPath := filepath.Join(sessDir, newName+".jsonl") if err := os.Rename(oldPath, newPath); err != nil { @@ -303,8 +300,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /tag