From b89c357de0f3f03bcc9d373d9c8b09e21d21d750 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 11 Sep 2026 11:28:18 +0530 Subject: [PATCH 1/4] chore!: rename graycode back to hawk The GitHub repository is GrayCodeAI/hawk again, so the module, binary and config surface follow it: github.com/GrayCodeAI/hawk, hawk binary, HAWK_* env vars, ~/.hawk, cmd/hawk, internal/hawkerr, npm/hawk*. The router dependency is github.com/GrayCodeAI/eyrie again (Eyrie* types, eyrieengine, EYRIE_* env vars). graycode-skills, graycode-platform, graycode-cloud and the graycode-cloud.graph/v1 schema keep their names. Hard break with no ~/.graycode migration. Module-mode builds need a new eyrie release: the eyrie v0.0.1 tag declares the graycode-router module path. Co-Authored-By: Claude Opus 5 --- .dockerignore | 4 +- .env.example | 14 +- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 12 +- .github/ISSUE_TEMPLATE/config.yml | 4 +- .github/ISSUE_TEMPLATE/feature_request.yml | 12 +- .../action.yml | 4 +- .github/actions/{graycode => hawk}/action.yml | 154 ++++++------ .github/actions/setup-deps/action.yml | 2 +- .github/workflows/ci.yml | 58 ++--- .github/workflows/compatibility-matrix.yml | 2 +- .github/workflows/daemon-image.yml | 12 +- .github/workflows/docker.yml | 34 +-- .github/workflows/release.yml | 2 +- .gitignore | 10 +- .golangci.yml | 2 +- .goreleaser.yml | 24 +- .shared-templates/.goreleaser.yml.tmpl | 4 +- .shared-templates/Makefile.binary.tmpl | 10 +- .shared-templates/Makefile.library.tmpl | 2 +- .shared-templates/Makefile.python.tmpl | 2 +- .shared-templates/README.md | 14 +- .shared-templates/docs/coverage-matrix.md | 4 +- .../check-ecosystem-boundaries.sh.tmpl | 26 +- .shared-templates/scripts/sync-external.sh | 4 +- .../workflows/compatibility-test.yml.tmpl | 4 +- .../workflows/go-release.yml.tmpl | 4 +- .../workflows/python-ci.yml.tmpl | 2 +- .trivyignore | 6 +- AGENTS.md | 104 ++++---- CHANGELOG.md | 1 + CLAUDE.md | 10 +- CODE_OF_CONDUCT.md | 4 +- CONTRIBUTING.md | 8 +- Dockerfile | 14 +- Dockerfile.daemon | 22 +- Makefile | 36 +-- README.md | 222 +++++++++--------- SECURITY.md | 26 +- api/openapi.yaml | 26 +- cmd/acp.go | 20 +- cmd/agent.go | 10 +- cmd/agent_grid.go | 4 +- cmd/agent_grid_test.go | 2 +- cmd/agent_test.go | 6 +- cmd/ai_comments.go | 2 +- cmd/audit.go | 16 +- cmd/autoinit.go | 14 +- cmd/autoinit_test.go | 4 +- cmd/autonomy_picker.go | 2 +- cmd/autonomy_picker_test.go | 2 +- cmd/autonomy_tiers.go | 2 +- cmd/autonomy_tiers_copy_test.go | 2 +- cmd/autonomy_tiers_test.go | 2 +- cmd/bg_sessions.go | 18 +- cmd/braille_spinner.go | 64 ++--- cmd/braille_spinner_test.go | 28 +-- cmd/cascade_diag_test.go | 6 +- cmd/catalog_startup.go | 10 +- cmd/chat.go | 90 +++---- cmd/chat_commands.go | 14 +- cmd/chat_commands_config.go | 16 +- cmd/chat_commands_image.go | 4 +- cmd/chat_commands_session.go | 10 +- cmd/chat_commands_skills.go | 6 +- cmd/chat_commands_test.go | 14 +- cmd/chat_commands_tools.go | 12 +- cmd/chat_commands_util.go | 16 +- cmd/chat_config_deployment.go | 74 +++--- cmd/chat_config_gateways.go | 30 +-- cmd/chat_config_gateways_test.go | 52 ++-- cmd/chat_config_hub.go | 6 +- cmd/chat_config_keys.go | 26 +- cmd/chat_config_keys_test.go | 32 +-- cmd/chat_config_models.go | 34 +-- cmd/chat_config_models_test.go | 4 +- cmd/chat_config_panel.go | 64 ++--- cmd/chat_config_region.go | 28 +-- cmd/chat_config_remove.go | 16 +- cmd/chat_config_remove_test.go | 24 +- cmd/chat_config_save_flow_test.go | 46 ++-- cmd/chat_config_security.go | 2 +- cmd/chat_config_tabs.go | 24 +- cmd/chat_config_tabs_test.go | 14 +- cmd/chat_config_ui.go | 10 +- cmd/chat_copy_e2e_test.go | 12 +- cmd/chat_copy_test.go | 14 +- cmd/chat_errors_test.go | 2 +- cmd/chat_export.go | 6 +- cmd/chat_focus.go | 6 +- cmd/chat_history_search.go | 4 +- cmd/chat_journey_e2e_test.go | 10 +- cmd/chat_layout_mouse_test.go | 2 +- cmd/chat_layout_test.go | 10 +- cmd/chat_model.go | 46 ++-- cmd/chat_model_test.go | 30 +-- cmd/chat_mouse.go | 18 +- cmd/chat_multiturn_e2e_test.go | 18 +- cmd/chat_permission_keys_test.go | 4 +- cmd/chat_platform_ctx.go | 4 +- cmd/chat_print.go | 38 +-- cmd/chat_print_usage_test.go | 8 +- cmd/chat_prompt_timeout_test.go | 4 +- cmd/chat_scrollbar.go | 2 +- cmd/chat_select.go | 6 +- cmd/chat_session_picker.go | 4 +- cmd/chat_status.go | 18 +- cmd/chat_status_metadata_test.go | 8 +- cmd/chat_status_test.go | 48 ++-- cmd/chat_stream.go | 2 +- cmd/chat_subcommand_brainstorm.go | 2 +- cmd/chat_subcommand_branch_agent.go | 8 +- cmd/chat_subcommand_checkpoint.go | 2 +- cmd/chat_subcommand_context.go | 6 +- cmd/chat_subcommand_council.go | 4 +- cmd/chat_subcommand_dream.go | 2 +- cmd/chat_subcommand_ecosystem.go | 4 +- cmd/chat_subcommand_harness.go | 4 +- cmd/chat_subcommand_harrier.go | 2 +- cmd/chat_subcommand_investigate.go | 2 +- cmd/chat_subcommand_isolation.go | 2 +- cmd/chat_subcommand_memory.go | 4 +- cmd/chat_subcommand_mode.go | 6 +- cmd/chat_subcommand_model.go | 18 +- cmd/chat_subcommand_party.go | 2 +- cmd/chat_subcommand_path.go | 4 +- cmd/chat_subcommand_recipe.go | 4 +- cmd/chat_subcommand_reflect.go | 2 +- cmd/chat_subcommand_render.go | 2 +- cmd/chat_subcommand_review.go | 2 +- cmd/chat_subcommand_shell_test.go | 2 +- cmd/chat_subcommand_simple.go | 70 +++--- cmd/chat_subcommand_soul.go | 2 +- cmd/chat_subcommand_spec.go | 4 +- cmd/chat_subcommand_start.go | 4 +- cmd/chat_subcommand_status.go | 6 +- cmd/chat_subcommand_trust.go | 4 +- cmd/chat_subcommand_version.go | 6 +- cmd/chat_subcommand_voice.go | 2 +- cmd/chat_submit.go | 12 +- cmd/chat_terminal_mouse.go | 2 +- cmd/chat_tools.go | 12 +- cmd/chat_tools_lsp_test.go | 6 +- cmd/chat_tools_test.go | 14 +- cmd/chat_turn.go | 2 +- cmd/chat_update.go | 26 +- cmd/chat_update_test.go | 4 +- cmd/chat_view.go | 20 +- cmd/chat_view_test.go | 6 +- cmd/chat_viewport.go | 2 +- cmd/chat_viewport_render.go | 8 +- cmd/chat_viewport_test.go | 10 +- cmd/chat_welcome.go | 62 ++--- cmd/chat_yolo_confirm_test.go | 2 +- cmd/checkpoint.go | 14 +- cmd/cli_contracts_test.go | 18 +- cmd/clipboard.go | 4 +- cmd/clipboard_test.go | 2 +- cmd/cloud.go | 48 ++-- cmd/cloud_context_test.go | 4 +- cmd/cloud_graph.go | 16 +- cmd/cmdhistory_cmd.go | 4 +- cmd/command_history.go | 2 +- cmd/compact_ui.go | 4 +- cmd/compact_ui_test.go | 4 +- cmd/compat-test/drift.go | 22 +- cmd/compat-test/drift_test.go | 14 +- cmd/compat-test/main.go | 6 +- cmd/completions.go | 88 +++---- cmd/completions_test.go | 36 +-- cmd/config_table.go | 2 +- cmd/container_boot.go | 12 +- cmd/container_boot_test.go | 6 +- cmd/context_export.go | 8 +- cmd/contextual_help.go | 42 ++-- cmd/control_plane_hints.go | 4 +- cmd/cost.go | 4 +- cmd/cost_test.go | 2 +- cmd/credential_gate.go | 2 +- cmd/credentials.go | 16 +- cmd/daemon.go | 78 +++--- cmd/daemon_ready_test.go | 36 +-- cmd/diagnostics.go | 60 ++--- cmd/diagnostics_test.go | 38 +-- cmd/dx.go | 40 ++-- cmd/dx_test.go | 24 +- cmd/ecosystem.go | 10 +- cmd/ecosystem_test.go | 8 +- cmd/error_classify.go | 26 +- cmd/error_classify_test.go | 2 +- cmd/errors.go | 40 ++-- cmd/errors_test.go | 10 +- cmd/eval.go | 18 +- cmd/eval_tools.go | 16 +- cmd/exec.go | 78 +++--- cmd/exec_test.go | 22 +- cmd/execution_graph.go | 26 +- cmd/execution_graph_test.go | 54 ++--- cmd/features.go | 14 +- cmd/feedback.go | 16 +- cmd/fingerprint.go | 10 +- cmd/footer_layout_clip_test.go | 4 +- cmd/footer_layout_width_test.go | 2 +- cmd/formatter.go | 4 +- cmd/formatter_test.go | 2 +- cmd/golden_test.go | 4 +- cmd/governance_cmd.go | 10 +- cmd/harness.go | 12 +- cmd/{graycode => hawk}/integration_test.go | 6 +- cmd/{graycode => hawk}/main.go | 22 +- cmd/help_template.go | 2 +- cmd/history.go | 6 +- cmd/history_test.go | 4 +- cmd/hud_panel.go | 2 +- cmd/image.go | 6 +- cmd/input_indicator.go | 4 +- cmd/input_indicator_test.go | 2 +- cmd/issue.go | 2 +- cmd/learn_cmd.go | 16 +- cmd/main_test.go | 4 +- cmd/manpage.go | 34 +-- cmd/manpage_test.go | 2 +- cmd/markdown.go | 8 +- cmd/markdown_renderer.go | 4 +- cmd/markdown_test.go | 6 +- cmd/mcp_serve.go | 44 ++-- cmd/mcp_serve_test.go | 12 +- cmd/mentions.go | 4 +- cmd/mentions_test.go | 2 +- cmd/merlin_pipeline.go | 12 +- cmd/merlin_pipeline_test.go | 4 +- cmd/migrate_secrets_test.go | 6 +- cmd/mission.go | 30 +-- cmd/mission_graph.go | 6 +- cmd/mission_graph_test.go | 6 +- cmd/model_table.go | 18 +- cmd/model_table_test.go | 6 +- cmd/models.go | 42 ++-- cmd/models_test.go | 6 +- cmd/notifications.go | 6 +- cmd/notify.go | 6 +- cmd/notify_test.go | 4 +- cmd/ocg_live_test.go | 20 +- cmd/options.go | 84 +++---- cmd/options_test.go | 2 +- cmd/options_welcome_test.go | 34 +-- cmd/pager.go | 2 +- cmd/path.go | 12 +- cmd/path_test.go | 8 +- cmd/permissions.go | 4 +- cmd/permissions_center.go | 20 +- cmd/permissions_center_test.go | 10 +- cmd/plan.go | 22 +- cmd/plugin_dynamic.go | 20 +- cmd/power.go | 4 +- cmd/pr.go | 8 +- cmd/preflight_test.go | 6 +- cmd/progress_cli.go | 6 +- cmd/progress_debug_test.go | 2 +- cmd/progress_tracker.go | 2 +- cmd/progress_tracker_test.go | 2 +- cmd/progressive_disclosure.go | 14 +- cmd/prompt_input.go | 2 +- cmd/record.go | 2 +- cmd/record_test.go | 2 +- cmd/replay.go | 4 +- cmd/resize_unix.go | 2 +- cmd/resize_windows.go | 2 +- cmd/review.go | 24 +- cmd/review_analyze.go | 28 +-- cmd/review_fix.go | 16 +- cmd/review_read.go | 6 +- cmd/review_refine.go | 18 +- cmd/review_run.go | 18 +- cmd/review_run_test.go | 4 +- cmd/review_store.go | 4 +- cmd/review_test.go | 34 +-- cmd/review_tui.go | 6 +- cmd/root.go | 182 +++++++------- cmd/rules.go | 30 +-- cmd/sandbox.go | 4 +- cmd/schema.go | 10 +- cmd/search.go | 12 +- cmd/security_verify_governance_cli_test.go | 6 +- cmd/securitylog_cmd.go | 10 +- cmd/session_export.go | 2 +- cmd/session_migrate.go | 2 +- cmd/session_migrate_test.go | 4 +- cmd/session_share.go | 8 +- cmd/session_sync.go | 10 +- cmd/session_sync_test.go | 44 ++-- cmd/skills_cmd.go | 4 +- cmd/skills_create.go | 10 +- cmd/skills_curator_cmd.go | 6 +- cmd/sleep_prevent.go | 2 +- cmd/snapshot_cmd.go | 6 +- cmd/spec_picker.go | 2 +- cmd/spec_picker_test.go | 2 +- cmd/spinner_wave.go | 10 +- cmd/spinner_wave_test.go | 6 +- cmd/stats.go | 6 +- cmd/status_snapshot.go | 18 +- cmd/status_snapshot_test.go | 4 +- cmd/statusbar.go | 6 +- cmd/statusbar_test.go | 6 +- cmd/storage_policy_test.go | 14 +- cmd/swift.go | 4 +- cmd/swift_correlation.go | 24 +- cmd/swift_correlation_test.go | 20 +- cmd/swift_report.go | 8 +- cmd/tabcomplete.go | 2 +- cmd/tabcomplete_test.go | 2 +- cmd/tape.go | 4 +- cmd/taste.go | 10 +- cmd/terminal_notify.go | 2 +- cmd/textutil_shim.go | 2 +- cmd/theme.go | 26 +- cmd/theme_picker.go | 12 +- cmd/tips.go | 10 +- cmd/tips_test.go | 4 +- cmd/toolset_cmd.go | 6 +- cmd/trust.go | 12 +- cmd/usage.go | 4 +- cmd/verify_cmd.go | 8 +- cmd/version_display.go | 4 +- cmd/version_display_test.go | 12 +- cmd/vibe.go | 2 +- cmd/visual_diff.go | 2 +- cmd/welcome_banner.go | 30 +-- cmd/welcome_banner_test.go | 8 +- cmd/welcome_inline_test.go | 28 +-- deploy/docker/docker-compose.yml | 12 +- docs/BENCHMARKS.md | 2 +- docs/COMPETITIVE.md | 20 +- docs/DAEMON-PORT-THREAT-MODEL.md | 30 +-- docs/DEVELOPER-PATH.md | 90 +++---- docs/DYNAMIC-MODELS.md | 78 +++--- docs/ECOSYSTEM-CONFIG.md | 76 +++--- docs/ECOSYSTEM-ROADMAP.md | 28 +-- docs/ECOSYSTEM-WIRING.md | 86 +++---- docs/IMPLEMENTATION-ROADMAP.md | 66 +++--- docs/OTEL-CONVENTIONS.md | 40 ++-- docs/PERMISSION-MODEL-IMPROVEMENTS.md | 6 +- docs/RESEARCH.md | 8 +- docs/SECURITY-DEVELOPER.md | 70 +++--- docs/architecture.md | 24 +- docs/architecture/README.md | 36 +-- ...R-0001-graycode-platform-telemetry-edge.md | 32 +-- .../ADR-0002-graycode-cloud-control-plane.md | 24 +- ...-0003-grok-behavioral-port-go-multirepo.md | 20 +- .../ADR-0004-file-first-session-history.md | 6 +- docs/architecture/control-plane.md | 16 +- docs/architecture/ecosystem-architecture.md | 56 ++--- docs/architecture/ecosystem-design.md | 50 ++-- .../ecosystem-implementation-plan.md | 76 +++--- docs/architecture/execution-graph.md | 100 ++++---- .../graycode-ecosystem-summary.md | 46 ++-- ...eline.md => hawk-architecture-baseline.md} | 58 ++--- ...awk-architecture-v1-definition-of-done.md} | 48 ++-- ...lity-seams.md => hawk-capability-seams.md} | 2 +- ...contract-spec.md => hawk-contract-spec.md} | 22 +- ...roposed.md => hawk-current-vs-proposed.md} | 58 ++--- ...ency-rules.md => hawk-dependency-rules.md} | 46 ++-- .../{graycode-harness.md => hawk-harness.md} | 10 +- ...ecture.md => hawk-product-architecture.md} | 108 ++++----- ...action.md => hawk-provider-abstraction.md} | 32 +-- ...ycode-repo-roles.md => hawk-repo-roles.md} | 48 ++-- ...cle.md => hawk-review-verify-lifecycle.md} | 6 +- ...ent-model.md => hawk-swift-event-model.md} | 4 +- docs/architecture/plan.md | 6 +- docs/architecture/spec.md | 26 +- docs/architecture/tasks.md | 4 +- docs/compatibility.md | 20 +- docs/design/ECOSYSTEM-MARKETPLACE.md | 152 ++++++------ ...YCODE-CLOUD-SAAS.md => HAWK-CLOUD-SAAS.md} | 82 +++---- docs/design/three-repo-architecture.md | 14 +- docs/ecosystem-message-flow.md | 48 ++-- docs/ecosystem-remediation-plan.md | 24 +- docs/intelligent-cli.md | 6 +- docs/mcp-servers.md | 24 +- docs/monitoring-guide.md | 70 +++--- docs/operations-checklist.md | 34 +-- ...=> FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md} | 202 ++++++++-------- ...GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md | 132 +++++------ docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md | 2 +- docs/plans/YEAR-0-ACTIVE.md | 18 +- docs/plans/codex-adoption-plan.md | 36 +-- docs/plans/commandcodeai-adoption-plan.md | 42 ++-- .../competitive-gap-01-docker-onboarding.md | 8 +- .../competitive-gap-02-share-multisession.md | 6 +- .../competitive-gap-03-kitty-graphics.md | 2 +- ...competitive-gap-04-published-benchmarks.md | 4 +- .../competitive-gap-05-backend-wiring.md | 16 +- docs/plans/dsh-harness-gap-port-plan.md | 26 +- docs/plans/dsh-harness-port-plan.md | 20 +- docs/plans/dsh-harness-rfc-port-plan.md | 26 +- docs/plans/engine-refactor-plan.md | 24 +- docs/plans/fix-critical-and-high-review.md | 30 +-- docs/plans/fx-adoption-plan.md | 90 +++---- docs/plans/goose-adoption-plan.md | 40 ++-- ...md => hawk-contracts-migration-backlog.md} | 82 +++---- docs/plans/minimax-adoption-plan.md | 104 ++++---- docs/plans/pi-adoption-plan.md | 64 ++--- docs/plans/pi-renderer-and-eval-reporting.md | 8 +- docs/plans/qwen-code-adoption-plan.md | 20 +- docs/plans/toolbench-comparison-vs-top20.md | 32 +-- docs/platform-capabilities.json | 14 +- docs/plugin-development.md | 70 +++--- docs/session-decomposition.md | 36 +-- docs/terminal-icons.md | 10 +- docs/troubleshooting-guide.md | 108 ++++----- docs/user-guide/01-getting-started.md | 70 +++--- docs/user-guide/02-authentication.md | 48 ++-- docs/user-guide/03-keyboard-shortcuts.md | 6 +- docs/user-guide/04-slash-commands.md | 8 +- docs/user-guide/05-configuration.md | 36 +-- docs/user-guide/06-theming.md | 20 +- docs/user-guide/07-mcp-servers.md | 30 +-- docs/user-guide/08-skills.md | 36 +-- docs/user-guide/09-plugins.md | 32 +-- docs/user-guide/10-hooks.md | 30 +-- docs/user-guide/11-custom-models.md | 18 +- docs/user-guide/12-project-rules.md | 18 +- docs/user-guide/13-memory.md | 36 +-- docs/user-guide/14-headless-mode.md | 42 ++-- docs/user-guide/15-agent-mode.md | 18 +- docs/user-guide/16-subagents.md | 4 +- docs/user-guide/17-sessions.md | 16 +- docs/user-guide/18-sandbox.md | 24 +- docs/user-guide/19-plan-mode.md | 10 +- docs/user-guide/20-background-tasks.md | 2 +- docs/user-guide/21-terminal-support.md | 8 +- docs/user-guide/22-permissions-and-safety.md | 2 +- docs/user-guide/23-dashboard.md | 10 +- docs/user-guide/24-monitoring-usage.md | 14 +- docs/user-guide/25-shell-completions.md | 38 +-- docs/user-guide/26-learned-preferences.md | 2 +- docs/user-guide/27-workflows.md | 8 +- docs/user-guide/28-workflow-budgets.md | 2 +- docs/versioning.md | 6 +- ecosystem.yaml | 22 +- examples/README.md | 32 +-- examples/github/graycode-delivery-context.yml | 47 ---- ...{graycode-ci-exec.yml => hawk-ci-exec.yml} | 38 +-- examples/github/hawk-delivery-context.yml | 47 ++++ ...e-resolver.yml => hawk-issue-resolver.yml} | 40 ++-- flake.nix | 22 +- go.mod | 4 +- go.sum | 2 - install.sh | 36 +-- internal/acp/acp_extra_test.go | 2 +- internal/acp/client_test.go | 2 +- internal/acp/content.go | 2 +- internal/acp/content_test.go | 2 +- internal/acp/server.go | 20 +- internal/acp/server_test.go | 10 +- internal/appverify/appverify_test.go | 2 +- internal/appverify/manifest.go | 6 +- internal/appverify/prompt.go | 2 +- internal/auth/auth.go | 6 +- internal/auth/auth_extra_test.go | 2 +- internal/auth/auth_test.go | 18 +- internal/auth/storage_extra_test.go | 10 +- internal/autoinit/autoinit.go | 18 +- internal/autoinit/autoinit_test.go | 6 +- internal/bench/bench_test.go | 34 +-- internal/bench/suite.go | 22 +- internal/bridge/kestrel/bridge.go | 46 ++-- internal/bridge/kestrel/bridge_test.go | 14 +- internal/bridge/merlin/bridge.go | 14 +- internal/bridge/merlin/bridge_test.go | 6 +- internal/bridge/swift/lib_bridge.go | 4 +- internal/catalogtest/install.go | 12 +- internal/circuitbreaker/circuitbreaker.go | 2 +- internal/codegraph/algorithms_cgo_more.go | 6 +- internal/config/catalog_api.go | 44 ++-- internal/config/catalog_compat_test.go | 8 +- internal/config/catalog_gateways_test.go | 16 +- internal/config/catalog_health.go | 20 +- internal/config/catalog_health_test.go | 6 +- internal/config/catalog_startup.go | 22 +- .../config/catalog_startup_robust_test.go | 12 +- internal/config/catalog_startup_test.go | 8 +- internal/config/config.go | 2 +- internal/config/config_test.go | 20 +- internal/config/credentials_store.go | 36 +-- internal/config/credentials_store_test.go | 2 +- internal/config/deployment.go | 4 +- internal/config/deployment_status.go | 12 +- internal/config/deployments_ui_test.go | 2 +- internal/config/developer_path.go | 56 ++--- internal/config/developer_path_test.go | 4 +- internal/config/ecosystem_report.go | 44 ++-- internal/config/ecosystem_report_test.go | 4 +- internal/config/envmanager.go | 2 +- internal/config/envmanager_test.go | 8 +- ...raycode_router_apply.go => eyrie_apply.go} | 20 +- ...ycode_router_engine.go => eyrie_engine.go} | 42 ++-- ...er_engine_test.go => eyrie_engine_test.go} | 10 +- ...router_selection.go => eyrie_selection.go} | 32 +-- internal/config/main_test.go | 4 +- internal/config/milestone_verify_test.go | 40 ++-- internal/config/provider_filter.go | 6 +- internal/config/sandbox_checklist.go | 6 +- internal/config/security_test.go | 4 +- internal/config/settings.go | 64 ++--- internal/config/settings_status_test.go | 4 +- internal/config/settings_test.go | 2 +- internal/config/setup_status.go | 6 +- internal/config/setup_status_test.go | 6 +- internal/config/ui_cache.go | 2 +- internal/config/validator.go | 6 +- internal/config/validator_test.go | 6 +- internal/config/xiaomi_setup_test.go | 18 +- internal/container/lifecycle.go | 2 +- internal/context/repomap/repomap.go | 4 +- internal/context/repomap/scan.go | 2 +- internal/context/rules.go | 10 +- internal/context/rules_test.go | 26 +- internal/context/walkup.go | 6 +- internal/context/walkup_test.go | 2 +- internal/contracts/agent/hooks.go | 10 +- internal/contracts/agent/spawn.go | 2 +- internal/contracts/events/events.go | 4 +- internal/contracts/graph/graph.go | 2 +- internal/contracts/harness/harness.go | 6 +- internal/contracts/policy/policy.go | 2 +- internal/contracts/review/review.go | 6 +- internal/contracts/types/finding.go | 4 +- internal/contracts/types/severity.go | 2 +- internal/contracts/verify/verify.go | 6 +- internal/crash/crash.go | 4 +- internal/crash/crash_extra_test.go | 4 +- internal/crash/crash_test.go | 12 +- internal/crash/crash_unix.go | 2 +- internal/daemon/contract_parity_test.go | 4 +- internal/daemon/daemon.go | 62 ++--- internal/daemon/daemon_test.go | 30 +-- internal/daemon/discord.go | 8 +- internal/daemon/e2e_test.go | 12 +- internal/daemon/gateway.go | 6 +- internal/daemon/gateway_test.go | 22 +- internal/daemon/graph_ledger_sqlite_test.go | 8 +- internal/daemon/h9_h10_test.go | 4 +- internal/daemon/middleware.go | 2 +- internal/daemon/middleware_test.go | 4 +- internal/daemon/routes_agent_status_test.go | 2 +- internal/daemon/routes_graph.go | 6 +- internal/daemon/routes_graph_read.go | 2 +- internal/daemon/routes_graph_read_test.go | 6 +- internal/daemon/routes_graph_sync.go | 6 +- internal/daemon/routes_graph_sync_test.go | 6 +- internal/daemon/routes_graph_test.go | 10 +- internal/daemon/routes_lease.go | 2 +- internal/daemon/routes_lease_test.go | 6 +- internal/daemon/routes_metrics.go | 14 +- internal/daemon/routes_review.go | 10 +- internal/daemon/routes_review_test.go | 4 +- internal/daemon/routes_sessions.go | 4 +- internal/daemon/routes_sessions_test.go | 14 +- internal/daemon/routes_stats.go | 2 +- internal/daemon/routes_stats_test.go | 10 +- internal/daemon/routes_status.go | 4 +- internal/daemon/slack.go | 6 +- internal/daemon/telegram.go | 20 +- internal/daemon/telegram_test.go | 46 ++-- internal/deps/deps.go | 2 +- internal/diffsandbox/sandbox.go | 2 +- internal/engine/adaptive_prompt.go | 6 +- internal/engine/adaptive_prompt_test.go | 2 +- internal/engine/adaptive_system_prompt.go | 6 +- .../engine/adaptive_system_prompt_test.go | 12 +- internal/engine/agent/background_agent.go | 4 +- internal/engine/agent_reexports.go | 2 +- internal/engine/agent_resume_test.go | 8 +- internal/engine/agent_session_tool.go | 20 +- internal/engine/arc_test.go | 2 +- internal/engine/architect.go | 14 +- internal/engine/assumptions.go | 2 +- internal/engine/async/engine.go | 2 +- internal/engine/async/engine_test.go | 40 ++-- internal/engine/auto_commit.go | 4 +- internal/engine/auto_commit_wire_test.go | 4 +- internal/engine/background_runner.go | 6 +- internal/engine/branching/cascade.go | 8 +- internal/engine/branching/cascade_test.go | 4 +- internal/engine/branching/shadow.go | 2 +- internal/engine/cache_gate.go | 2 +- internal/engine/cache_gate_test.go | 4 +- internal/engine/cache_planner.go | 8 +- internal/engine/cache_planner_test.go | 4 +- internal/engine/chat_provider.go | 16 +- internal/engine/chat_provider_test.go | 12 +- internal/engine/chat_replay.go | 14 +- internal/engine/chat_replay_test.go | 20 +- internal/engine/chat_service.go | 42 ++-- internal/engine/chat_service_test.go | 44 ++-- internal/engine/client_interface.go | 24 +- internal/engine/code/code_actions.go | 2 +- internal/engine/code_index_adapter.go | 4 +- internal/engine/coding_soul.go | 6 +- internal/engine/compact.go | 24 +- internal/engine/compact/aliases_extra_test.go | 6 +- internal/engine/compact/api.go | 12 +- internal/engine/compact/files.go | 4 +- internal/engine/compact/files_test.go | 6 +- internal/engine/compact/incremental_test.go | 8 +- internal/engine/compact/micro.go | 12 +- internal/engine/compact/prompt.go | 6 +- internal/engine/compact/session_memory.go | 14 +- internal/engine/compact/split_test.go | 18 +- internal/engine/compact/strategy.go | 8 +- internal/engine/compact/strategy_test.go | 26 +- .../engine/compact/transcript_segments.go | 10 +- .../compact/transcript_segments_test.go | 8 +- internal/engine/compact_auto.go | 8 +- internal/engine/compact_clear.go | 12 +- internal/engine/compact_clear_test.go | 14 +- internal/engine/compact_micro_engine.go | 6 +- internal/engine/compact_provider_native.go | 14 +- internal/engine/compact_reexports.go | 12 +- internal/engine/compact_regression_test.go | 32 +-- internal/engine/compact_relevance.go | 20 +- internal/engine/compact_relevance_test.go | 10 +- .../engine/compact_session_memory_engine.go | 12 +- internal/engine/compact_split.go | 24 +- internal/engine/compact_strategy_engine.go | 6 +- internal/engine/compact_strategy_test.go | 16 +- internal/engine/compression/cross_session.go | 4 +- .../engine/compression/session_timeline.go | 2 +- .../compression/session_timeline_test.go | 2 +- internal/engine/context_compaction.go | 12 +- internal/engine/context_compaction_test.go | 14 +- internal/engine/context_governor.go | 4 +- internal/engine/context_governor_test.go | 6 +- internal/engine/control/backtrack.go | 18 +- internal/engine/control/backtrack_test.go | 8 +- internal/engine/control/stall_detector.go | 2 +- .../engine/control/stall_detector_test.go | 2 +- internal/engine/control_plane_test.go | 20 +- internal/engine/control_reexports.go | 2 +- internal/engine/conversation_adapter.go | 4 +- internal/engine/cost/aliases.go | 2 +- internal/engine/cost/cost_optimizer.go | 2 +- internal/engine/cost/cost_optimizer_test.go | 2 +- internal/engine/cost/cost_table.go | 2 +- internal/engine/cost/cost_tracker.go | 4 +- internal/engine/cost/cost_tracker_test.go | 4 +- internal/engine/cost_reexports.go | 4 +- internal/engine/council.go | 2 +- internal/engine/ctxmgr/context_budget.go | 2 +- internal/engine/ctxmgr/context_collapse.go | 18 +- .../engine/ctxmgr/context_collapse_test.go | 12 +- internal/engine/ctxmgr/context_decay.go | 2 +- internal/engine/ctxmgr/context_decay_test.go | 2 +- internal/engine/ctxmgr/context_viz.go | 2 +- internal/engine/ctxmgr/context_viz_test.go | 2 +- internal/engine/ctxmgr/incremental.go | 8 +- internal/engine/delegated_subagent_test.go | 8 +- internal/engine/diff/diff_staging.go | 2 +- internal/engine/diff_reexports.go | 2 +- internal/engine/directive_scanner.go | 12 +- internal/engine/elision.go | 2 +- internal/engine/elision_test.go | 2 +- internal/engine/engine.go | 6 +- internal/engine/engine_full_loop_test.go | 6 +- internal/engine/engine_integration_test.go | 14 +- internal/engine/engine_stage2_test_helpers.go | 2 +- internal/engine/engine_token_helpers.go | 8 +- internal/engine/errs/aliases.go | 2 +- internal/engine/errs/error_context.go | 14 +- internal/engine/errs/error_context_test.go | 6 +- internal/engine/errs/error_patterns.go | 2 +- internal/engine/errs_reexports.go | 2 +- internal/engine/event_bus.go | 2 +- .../engine/execution_graph_observations.go | 52 ++-- .../execution_graph_observations_test.go | 30 +-- internal/engine/experiment_loop.go | 2 +- internal/engine/extract_targets_test.go | 2 +- internal/engine/git/git_provider.go | 2 +- internal/engine/git/git_provider_test.go | 2 +- internal/engine/git/provider_extra_test.go | 12 +- internal/engine/git_safety.go | 10 +- internal/engine/git_safety_test.go | 2 +- internal/engine/goose_extras_test.go | 4 +- internal/engine/hints_loader.go | 4 +- internal/engine/history/annotations.go | 14 +- internal/engine/history/annotations_test.go | 40 ++-- .../history/conversation_summarizer_test.go | 2 +- internal/engine/history/file_mentions.go | 4 +- internal/engine/history/file_mentions_test.go | 6 +- internal/engine/history_reexports.go | 2 +- internal/engine/incremental.go | 4 +- internal/engine/incremental_test.go | 8 +- internal/engine/integration.go | 24 +- internal/engine/integration_graph_test.go | 4 +- internal/engine/integration_test.go | 4 +- internal/engine/intelligence/capabilities.go | 2 +- .../engine/intelligence/capabilities_test.go | 2 +- .../engine/intelligence/command_suggest.go | 2 +- .../intelligence/command_suggest_test.go | 2 +- internal/engine/intelligence/tool_selector.go | 2 +- internal/engine/intelligence_reexports.go | 2 +- internal/engine/io/ai_watch.go | 2 +- internal/engine/io/clipboard_bridge.go | 2 +- internal/engine/io/clipboard_test.go | 4 +- internal/engine/io_reexports.go | 2 +- internal/engine/isolation_profile.go | 4 +- internal/engine/journal.go | 26 +- internal/engine/journal_test.go | 20 +- internal/engine/jsonl_events.go | 2 +- internal/engine/l2_home_paths_test.go | 10 +- internal/engine/lifecycle/lifecycle.go | 2 +- .../engine/lifecycle/lifecycle_adapters.go | 2 +- internal/engine/lifecycle/sleeptime_ops.go | 2 +- .../engine/lifecycle/sleeptime_ops_test.go | 2 +- internal/engine/lifecycle/timeout.go | 2 +- internal/engine/lifecycle/timeout_test.go | 2 +- internal/engine/lifecycle_finalize_test.go | 6 +- internal/engine/lifecycle_reexports.go | 2 +- internal/engine/lifecycle_service.go | 18 +- internal/engine/llm_client.go | 10 +- internal/engine/main_test.go | 4 +- internal/engine/memory/knowledge.go | 2 +- internal/engine/memory/memory_consolidator.go | 2 +- internal/engine/memory_reexports.go | 2 +- internal/engine/memory_service.go | 8 +- internal/engine/mock_client_test.go | 26 +- internal/engine/model_switch_test.go | 4 +- .../observability/feedback_collector.go | 2 +- .../observability/structured_log_test.go | 6 +- internal/engine/observability_reexports.go | 2 +- internal/engine/party_mode.go | 2 +- internal/engine/permission_exactrules_test.go | 4 +- internal/engine/permission_service.go | 18 +- .../permission_service_spec_journal_test.go | 2 +- internal/engine/permission_service_test.go | 2 +- internal/engine/permission_session_methods.go | 4 +- internal/engine/persistence_service.go | 50 ++-- .../persistence_service_deadlock_test.go | 10 +- internal/engine/persistence_trim_test.go | 20 +- internal/engine/planning/goals.go | 2 +- internal/engine/planning/goals_test.go | 2 +- internal/engine/planning/suggested_tasks.go | 22 +- .../engine/planning/suggested_tasks_test.go | 10 +- internal/engine/planning_reexports.go | 2 +- .../engine/project/convention_enforcer.go | 2 +- .../project/convention_enforcer_test.go | 2 +- internal/engine/project/dep_updater.go | 4 +- .../engine/project/impact_analyzer_test.go | 4 +- internal/engine/project/migration_planner.go | 2 +- .../engine/project/project_analyzer_test.go | 6 +- internal/engine/project/project_context.go | 4 +- internal/engine/project/project_metrics.go | 2 +- internal/engine/project/release.go | 2 +- internal/engine/project_reexports.go | 2 +- internal/engine/project_trust.go | 8 +- internal/engine/prompt/prompt_extra_test.go | 6 +- internal/engine/prompt/prompt_optimizer.go | 8 +- internal/engine/prompt/prompt_tuner.go | 2 +- internal/engine/prompt/prompt_tuner_test.go | 2 +- internal/engine/provider_chat_client.go | 10 +- internal/engine/redaction.go | 2 +- internal/engine/redaction_test.go | 2 +- internal/engine/reflect_test.go | 10 +- internal/engine/resilience_boundary_test.go | 68 +++--- internal/engine/review/consensus.go | 2 +- internal/engine/review/quality_scorer.go | 2 +- internal/engine/review/review_bot.go | 2 +- internal/engine/review/review_bot_rules.go | 2 +- internal/engine/review_reexports.go | 2 +- internal/engine/safety/autonomy.go | 2 +- internal/engine/safety/permission.go | 6 +- internal/engine/safety/permission_engine.go | 28 +-- .../permission_engine_governance_test.go | 2 +- .../safety/permission_engine_pretool_test.go | 2 +- .../engine/safety/permission_engine_test.go | 4 +- internal/engine/safety/risk_assessor.go | 2 +- .../safety/spec_approval_summary_test.go | 2 +- internal/engine/safety_reexports.go | 2 +- internal/engine/sandbox_policy_test.go | 2 +- internal/engine/scaffold/fewshot.go | 4 +- internal/engine/scaffold/fewshot_test.go | 2 +- internal/engine/scaffold/patterns.go | 30 +-- internal/engine/scaffold/recipe.go | 12 +- internal/engine/scaffold/recipe_test.go | 16 +- internal/engine/scaffold_reexports.go | 2 +- internal/engine/scale_adaptive.go | 2 +- internal/engine/search/url_scraper.go | 2 +- internal/engine/search/url_scraper_test.go | 12 +- internal/engine/search_reexports.go | 2 +- internal/engine/security_events.go | 4 +- internal/engine/security_events_test.go | 10 +- internal/engine/selective_rag.go | 2 +- internal/engine/self_heal.go | 6 +- internal/engine/self_heal_test.go | 2 +- internal/engine/self_improve.go | 4 +- internal/engine/self_review.go | 6 +- internal/engine/semantic_diff.go | 2 +- internal/engine/session.go | 66 +++--- internal/engine/session_factory.go | 32 +-- internal/engine/session_factory_test.go | 14 +- internal/engine/session_h3_h4_test.go | 8 +- internal/engine/session_mock_test.go | 4 +- internal/engine/session_reexports.go | 2 +- internal/engine/skill_catalog_test.go | 4 +- internal/engine/smartrouting_test.go | 2 +- internal/engine/spawn_controller.go | 4 +- internal/engine/spec_mode_test.go | 6 +- internal/engine/stage2_move.sh | 4 +- internal/engine/stream.go | 70 +++--- internal/engine/stream_exhaustion.go | 4 +- internal/engine/stream_exhaustion_test.go | 10 +- internal/engine/stream_guards.go | 6 +- internal/engine/stream_helpers.go | 8 +- internal/engine/stream_prompt_test.go | 6 +- internal/engine/stream_tool_exec.go | 8 +- internal/engine/stream_tool_exec_test.go | 12 +- internal/engine/stream_usage.go | 18 +- internal/engine/stream_usage_test.go | 12 +- internal/engine/streaming_reexports.go | 2 +- internal/engine/structured_output.go | 14 +- internal/engine/structured_output_test.go | 10 +- internal/engine/sub_service_wiring_test.go | 10 +- internal/engine/subagent_synthesis.go | 8 +- internal/engine/subagent_synthesis_test.go | 18 +- internal/engine/system_context_test.go | 2 +- internal/engine/teach.go | 2 +- internal/engine/title.go | 4 +- internal/engine/title_test.go | 4 +- internal/engine/token/aliases.go | 2 +- internal/engine/token/shrike_facade.go | 48 ++-- internal/engine/token/token_budget.go | 8 +- internal/engine/token/token_budget_test.go | 44 ++-- internal/engine/token/token_estimates.go | 6 +- internal/engine/token/token_predictor.go | 2 +- internal/engine/token/token_predictor_test.go | 2 +- internal/engine/token/tokenizer.go | 8 +- internal/engine/token_reexports.go | 10 +- internal/engine/tool_catalog_shrink.go | 20 +- internal/engine/tool_catalog_shrink_test.go | 32 +-- internal/engine/tool_output_spill.go | 4 +- internal/engine/tool_pipeline.go | 2 +- internal/engine/tool_service.go | 24 +- internal/engine/tool_service_test.go | 4 +- internal/engine/tool_timeout_test.go | 4 +- internal/engine/trajectory.go | 6 +- internal/engine/trajectory/trajectory.go | 12 +- internal/engine/trajectory/trajectory_test.go | 14 +- internal/engine/trajectory_test.go | 18 +- internal/engine/transfer.go | 4 +- internal/engine/transfer_test.go | 6 +- internal/engine/validation/gen_validator.go | 2 +- internal/engine/validation/test_loop.go | 2 +- internal/engine/validation_reexports.go | 2 +- internal/engine/vision.go | 14 +- internal/engine/work_mode.go | 2 +- .../engine/workflow/trajectory_inspector.go | 2 +- .../workflow/trajectory_inspector_test.go | 2 +- internal/engine/workflow/workspace_state.go | 4 +- .../engine/workflow/workspace_state_test.go | 2 +- internal/engine/workflow_reexports.go | 2 +- internal/env/scrub.go | 8 +- internal/env/scrub_test.go | 6 +- internal/errhint/errhint.go | 4 +- internal/eventlog/boundary.go | 6 +- internal/eventlog/chunk_rows.go | 2 +- internal/eventlog/event.go | 6 +- internal/eventlog/lifecycle.go | 10 +- internal/eventlog/projection_test.go | 2 +- internal/executiongraph/execution_graph.go | 144 ++++++------ .../executiongraph/execution_graph_test.go | 38 +-- internal/fastcopy/fastcopy.go | 2 +- internal/feature/daemon_flags.go | 14 +- internal/feature/eval/cache.go | 4 +- internal/feature/eval/coverage.go | 2 +- internal/feature/eval/coverage_test.go | 2 +- internal/feature/eval/eval.go | 6 +- internal/feature/eval/parallel_runner.go | 2 +- internal/feature/eval/parallel_runner_test.go | 2 +- internal/feature/eval/skills/run_eval.sh | 4 +- internal/feature/eval/skills/scenarios.md | 4 +- internal/feature/eval/store.go | 4 +- internal/feature/eval/tasks_go_more.go | 8 +- internal/feature/eval/toolmatrix.go | 2 +- internal/feature/evalloop/evalloop_test.go | 4 +- internal/feature/evalloop/runtime.go | 2 +- internal/feature/evalloop/session.go | 4 +- internal/feature/feature.go | 8 +- internal/feature/feature_test.go | 12 +- .../feature/fingerprint/fingerprint_test.go | 8 +- internal/feature/fingerprint/project.go | 12 +- internal/feature/fingerprint/project_test.go | 2 +- internal/feature/ide/ide.go | 36 +-- internal/feature/ide/ide_test.go | 4 +- internal/feature/shellmode/modes.go | 2 +- internal/feature/taste/store.go | 4 +- internal/feature/voice/voice.go | 4 +- internal/feature/voice/voice_extra_test.go | 2 +- internal/filestate/filestate.go | 8 +- internal/filestate/filestate_test.go | 8 +- internal/flags/y0.go | 10 +- internal/fuzzyfind/fuzzyfind.go | 2 +- internal/gitworktree/worktree.go | 6 +- internal/governance/governance.go | 4 +- internal/graphjournal/journal.go | 16 +- internal/graphjournal/journal_test.go | 12 +- internal/harness/evaluator.go | 36 +-- internal/harness/fix.go | 6 +- internal/harness/journal.go | 6 +- internal/harness/render.go | 6 +- internal/harness/workloop.go | 6 +- .../bridge_error_test.go | 2 +- internal/{graycodeerr => hawkerr}/classify.go | 8 +- .../classify_extra_test.go | 6 +- internal/{graycodeerr => hawkerr}/errors.go | 2 +- internal/{graycodeerr => hawkerr}/exitcode.go | 8 +- .../{graycodeerr => hawkerr}/exitcode_test.go | 2 +- internal/hooks/aliases.go | 4 +- internal/hooks/audit/detectors.go | 2 +- internal/hooks/discover.go | 4 +- internal/hooks/file_watcher.go | 2 +- internal/hooks/hooks.go | 8 +- internal/hooks/http_hooks.go | 4 +- internal/identity/identity.go | 12 +- internal/identity/identity_test.go | 18 +- internal/installtxn/installtxn.go | 8 +- internal/intelligence/memory/auto_memory.go | 4 +- internal/intelligence/memory/autodream.go | 2 +- internal/intelligence/memory/autofile.go | 2 +- internal/intelligence/memory/evolving.go | 6 +- .../memory/evolving_additional_test.go | 12 +- .../intelligence/memory/harrier_bridge.go | 44 ++-- .../memory/harrier_bridge_integration_test.go | 12 +- internal/intelligence/memory/memory.go | 2 +- internal/intelligence/memory/memory_test.go | 2 +- internal/intelligence/memory/proactive.go | 2 +- internal/intelligence/memory/shared_memory.go | 2 +- internal/intelligence/memory/zenbrain.go | 2 +- internal/intelligence/planner/planner.go | 8 +- internal/intelligence/repomap/README.md | 8 +- internal/intelligence/repomap/complexity.go | 2 +- .../intelligence/repomap/complexity_test.go | 2 +- internal/intelligence/repomap/doc.go | 10 +- internal/intelligence/repomap/doclint.go | 2 +- internal/intelligence/repomap/health_score.go | 2 +- internal/intelligence/repomap/imports.go | 2 +- internal/intelligence/repomap/incremental.go | 2 +- .../intelligence/repomap/incremental_map.go | 4 +- .../intelligence/repomap/incremental_test.go | 2 +- internal/intelligence/repomap/navigation.go | 2 +- .../intelligence/repomap/navigation_test.go | 2 +- internal/intelligence/repomap/rerank.go | 2 +- .../intelligence/repomap/semantic_search.go | 4 +- internal/intelligence/repomap/summary_test.go | 10 +- internal/intelligence/skillcurator/record.go | 2 +- .../intelligence/skillcurator/skillcurator.go | 2 +- internal/jsonc/jsonc_test.go | 2 +- internal/jsonc/validate_test.go | 2 +- internal/lint/lint.go | 2 +- internal/localize/localize_test.go | 4 +- internal/lockutil/lockutil.go | 2 +- internal/lsp/config.go | 4 +- internal/lsp/env_test.go | 16 +- internal/lsp/lsp_extra_test.go | 2 +- internal/mcp/http.go | 4 +- internal/mcp/mcp.go | 10 +- internal/mcp/oauth.go | 6 +- internal/mcp/oauth_store.go | 4 +- internal/mcp/schema.go | 2 +- internal/mcp/server.go | 10 +- internal/mcp/server_test.go | 36 +-- internal/mcp/server_tools.go | 84 +++---- internal/mcp/server_tools_test.go | 152 ++++++------ internal/mcp/ws.go | 2 +- internal/multiagent/agents/agents.go | 8 +- internal/multiagent/agents/agents_test.go | 2 +- internal/multiagent/agents/persona.go | 4 +- .../multiagent/agents/persona_builtins.go | 4 +- .../agents/persona_registry_test.go | 6 +- internal/multiagent/approval.go | 2 +- internal/multiagent/continuable.go | 6 +- internal/multiagent/continuable_test.go | 4 +- internal/multiagent/delegation_test.go | 4 +- internal/multiagent/mission.go | 8 +- internal/multiagent/mission_test.go | 4 +- internal/multiagent/parallel/parallel.go | 4 +- internal/multiagent/parallel/parallel_test.go | 18 +- internal/multiagent/parallel/worktree.go | 2 +- .../multiagent/parallel/worktree_manager.go | 4 +- .../parallel/worktree_manager_test.go | 14 +- internal/multiagent/portable_graph.go | 42 ++-- internal/multiagent/portable_graph_test.go | 2 +- internal/multiagent/provider.go | 2 +- internal/multiagent/reflexion_test.go | 10 +- internal/multiagent/shared_memory_test.go | 10 +- internal/multiagent/watchdog.go | 4 +- internal/multiagent/worker.go | 28 +-- internal/multiagent/worker_cleanup_test.go | 18 +- internal/multiagent/worker_transcript.go | 14 +- internal/multiagent/worker_transcript_test.go | 6 +- internal/notify/notify.go | 12 +- internal/notify/notify_test.go | 10 +- internal/observability/analytics.go | 2 +- internal/observability/analytics_test.go | 2 +- .../conformance/conformance_test.go | 16 +- internal/observability/conformance/harness.go | 2 +- internal/observability/conformance/schema.go | 10 +- internal/observability/dashboard.go | 4 +- internal/observability/dashboard_test.go | 2 +- internal/observability/insights.go | 6 +- internal/observability/otellog/otellog.go | 2 +- internal/observability/otellog/otellog_sdk.go | 12 +- .../observability/otellog/otellog_test.go | 4 +- internal/observability/oteltrace/langfuse.go | 2 +- internal/observability/oteltrace/otel.go | 10 +- internal/observability/oteltrace/otel_test.go | 14 +- internal/observability/oteltrace/spans.go | 2 +- internal/observability/oteltrace/trace.go | 2 +- internal/observability/perf_budget.go | 2 +- internal/observability/testmain_test.go | 2 +- internal/onboarding/onboarding.go | 56 ++--- internal/onboarding/onboarding_test.go | 2 +- internal/permissions/approval_workflow.go | 2 +- .../permissions/approval_workflow_test.go | 2 +- internal/permissions/boundary.go | 2 +- internal/permissions/boundary_test.go | 2 +- internal/permissions/egress.go | 2 +- internal/permissions/guardian.go | 2 +- internal/permissions/persist.go | 2 +- internal/permissions/rules.go | 8 +- internal/permissions/rules_test.go | 4 +- internal/permissions/stableid/state.go | 2 +- internal/permissions/stablerules.go | 4 +- .../permissions/stablerules_reset_test.go | 2 +- internal/permissions/stablerules_test.go | 2 +- internal/permissions/verdict.go | 2 +- internal/permissions/verdict_test.go | 2 +- internal/planning/model_test.go | 12 +- internal/planning/search.go | 6 +- internal/platform/cloud/client.go | 20 +- internal/platform/cloud/client_test.go | 4 +- internal/platform/cloud/config.go | 6 +- internal/platform/cloud/graph.go | 14 +- internal/platform/cloud/graph_test.go | 14 +- internal/plugin/audit.go | 6 +- internal/plugin/auto_skill.go | 6 +- internal/plugin/auto_skill_audit_test.go | 10 +- internal/plugin/bridge.go | 2 +- internal/plugin/components.go | 2 +- internal/plugin/dynamic.go | 18 +- internal/plugin/env.go | 10 +- internal/plugin/feedback.go | 4 +- internal/plugin/learn.go | 4 +- internal/plugin/manager.go | 38 +-- internal/plugin/manager_test.go | 8 +- internal/plugin/manifest_hardening.go | 4 +- internal/plugin/manifest_v2.go | 28 +-- internal/plugin/marketplace.go | 12 +- internal/plugin/marketplace_test.go | 4 +- internal/plugin/plugin.go | 4 +- internal/plugin/plugin_test.go | 6 +- internal/plugin/plugin_tool.go | 2 +- internal/plugin/provider.go | 4 +- internal/plugin/registry.go | 8 +- internal/plugin/registry_test.go | 20 +- internal/plugin/runtime.go | 6 +- internal/plugin/scopes.go | 14 +- internal/plugin/skills_auto.go | 22 +- internal/plugin/skillslock.go | 4 +- internal/plugin/testmain_test.go | 4 +- internal/plugin/wizard.go | 6 +- internal/prompt/prompt.go | 4 +- internal/prompt/prompt_test.go | 2 +- internal/prompts/agents_accumulator.go | 8 +- internal/prompts/agents_accumulator_test.go | 2 +- internal/prompts/loader.go | 6 +- internal/prompts/loader_test.go | 20 +- internal/prompts/templates/examples.md | 2 +- internal/prompts/templates/practices.md | 2 +- internal/prompts/templates/role.md | 2 +- internal/prompts/templates/subagent.md | 4 +- internal/prompts/templates/tools.md | 2 +- internal/prompts/testmain_test.go | 2 +- internal/prompts/workspace.go | 2 +- internal/provider/gateway/engine_client.go | 206 ++++++++-------- .../provider/gateway/engine_client_test.go | 38 +-- internal/provider/gateway/gateway.go | 180 +++++++------- internal/provider/gateway/media_backends.go | 44 ++-- .../provider/gateway/media_backends_stub.go | 6 +- .../provider/gateway/provider_stub_test.go | 112 ++++----- internal/provider/routing/catalog.go | 12 +- internal/provider/routing/health_router.go | 2 +- internal/provider/routing/main_test.go | 2 +- internal/provider/routing/roles.go | 6 +- internal/provider/routing/tiers.go | 2 +- internal/providers/providers.go | 6 +- internal/providers/providers_test.go | 2 +- internal/recipe/providers.go | 4 +- internal/recipe/recipe.go | 2 +- internal/replaycache/replaycache.go | 18 +- internal/replaycache/replaycache_test.go | 22 +- internal/resilience/health/diagnostics.go | 28 +-- .../resilience/health/diagnostics_test.go | 16 +- internal/rules/formats.go | 8 +- internal/rules/formats_test.go | 30 +-- internal/rules/rules.go | 18 +- internal/rules/rules_test.go | 62 ++--- internal/safewrite/safewrite_test.go | 2 +- internal/sandbox/approval.go | 4 +- internal/sandbox/code_verifier.go | 2 +- internal/sandbox/config_toml.go | 10 +- internal/sandbox/container.go | 18 +- internal/sandbox/container_test.go | 48 ++-- internal/sandbox/devenv.go | 6 +- internal/sandbox/image.go | 34 +-- internal/sandbox/isolation_verify_test.go | 22 +- internal/sandbox/landlock.go | 2 +- internal/sandbox/manager.go | 2 +- internal/sandbox/mode.go | 14 +- internal/sandbox/mode_extra_test.go | 2 +- internal/sandbox/mode_network_test.go | 12 +- internal/sandbox/netproxy.go | 2 +- internal/sandbox/netproxy_test.go | 2 +- internal/sandbox/policy.go | 2 +- internal/sandbox/policy_test.go | 2 +- internal/sandbox/runtime_deps_test.go | 36 +-- internal/sandbox/sandbox.go | 10 +- internal/sandbox/sandbox_tier_test.go | 36 +-- internal/sandbox/seatbelt.go | 8 +- internal/sandbox/seatbelt_test.go | 24 +- internal/sandbox/testmain_test.go | 2 +- internal/schedule/fold.go | 2 +- internal/schedule/manager.go | 2 +- internal/schedule/schedule_test.go | 2 +- internal/securitylog/securitylog.go | 4 +- internal/session/branch.go | 2 +- internal/session/branch_test.go | 2 +- internal/session/checkpoint.go | 4 +- internal/session/conversation_graph.go | 4 +- internal/session/conversion.go | 22 +- internal/session/conversion_test.go | 4 +- internal/session/export.go | 4 +- internal/session/export_test.go | 2 +- internal/session/handover.go | 2 +- internal/session/named_checkpoint.go | 4 +- internal/session/persist.go | 4 +- internal/session/persist_integrity_test.go | 8 +- internal/session/preparations.go | 2 +- internal/session/preparations_test.go | 2 +- internal/session/recovery.go | 4 +- internal/session/search.go | 4 +- internal/session/session.go | 14 +- internal/session/session_concurrent_test.go | 12 +- internal/session/session_test.go | 4 +- internal/session/snapshot.go | 4 +- internal/session/snapshot_test.go | 2 +- internal/session/storage_policy_test.go | 12 +- internal/session/storage_test_helpers_test.go | 2 +- internal/session/write_behind.go | 2 +- internal/session/write_behind_test.go | 2 +- internal/session/zstd_persist_test.go | 4 +- internal/sessionquery/db.go | 2 +- internal/sessionquery/indexer.go | 4 +- internal/sessionquery/search.go | 2 +- internal/sessionquery/service.go | 2 +- internal/sessionquery/sessionquery_test.go | 6 +- internal/snapshot/l2_home_paths_test.go | 10 +- internal/snapshot/snapshot.go | 12 +- internal/snapshot/workspace.go | 8 +- internal/snapshot/workspace_test.go | 4 +- internal/spec/config.go | 4 +- internal/spec/dag.go | 4 +- internal/spec/schema.go | 2 +- internal/spec/state.go | 6 +- internal/spec/state_security_test.go | 2 +- internal/spec/validator.go | 2 +- internal/status/snapshot.go | 38 +-- internal/storage/paths.go | 26 +- internal/storage/paths_extra_test.go | 36 +-- internal/storage/paths_test.go | 30 +-- internal/storage/testdirs.go | 4 +- internal/stt/stt.go | 4 +- internal/swift/file.go | 2 +- internal/swift/file_test.go | 4 +- internal/swift/report.go | 6 +- internal/swift/report_test.go | 6 +- internal/system/cron/cron.go | 2 +- internal/system/cron/cron_test.go | 2 +- internal/system/staleness/staleness_test.go | 8 +- internal/taskruntime/runtime.go | 2 +- internal/taskruntime/runtime_extra_test.go | 2 +- internal/taskruntime/runtime_test.go | 2 +- internal/terminal/store.go | 2 +- internal/terminal/tape/smoke_test.go | 4 +- internal/terminal/tape/store.go | 6 +- internal/terminal/tape/store_test.go | 2 +- internal/terminal/terminal_test.go | 10 +- internal/testaudit/audit_test.go | 42 ++-- internal/testaudit/docs_audit_test.go | 12 +- internal/testaudit/emoji_audit_test.go | 12 +- internal/testaudit/helpers.go | 4 +- internal/testaudit/package_boundaries_test.go | 36 +-- internal/testrunner/testrunner.go | 2 +- internal/testutil/network.go | 2 +- internal/testutil/storage.go | 18 +- internal/theme/capabilities.go | 2 +- internal/theme/theme.go | 4 +- internal/theme/theme_palettes.go | 2 +- internal/token/shrike.go | 6 +- internal/token/shrike_test.go | 2 +- internal/tool/agent.go | 2 +- internal/tool/agent_limits_test.go | 2 +- internal/tool/agent_test.go | 2 +- internal/tool/agentic_fetch.go | 2 +- internal/tool/agentic_fetch_test.go | 2 +- internal/tool/announcements.go | 8 +- internal/tool/api_compat.go | 4 +- internal/tool/app_verify.go | 6 +- internal/tool/audit.go | 2 +- internal/tool/audit_test.go | 2 +- internal/tool/background.go | 4 +- internal/tool/backup.go | 2 +- internal/tool/bash.go | 12 +- internal/tool/bash_injection_test.go | 6 +- internal/tool/bash_sandbox_test.go | 2 +- internal/tool/batch_exec.go | 6 +- internal/tool/browser.go | 2 +- internal/tool/browser_ax.go | 2 +- internal/tool/browser_live_test.go | 8 +- internal/tool/change_validator.go | 2 +- internal/tool/change_validator_test.go | 2 +- internal/tool/code_match.go | 2 +- internal/tool/code_match_cgo.go | 2 +- internal/tool/codegraph.go | 2 +- internal/tool/commitlint.go | 2 +- internal/tool/commitlint_test.go | 2 +- internal/tool/devenv.go | 6 +- internal/tool/download_test.go | 2 +- internal/tool/extra.go | 4 +- internal/tool/extra_test.go | 4 +- internal/tool/file_read.go | 2 +- internal/tool/file_write.go | 2 +- internal/tool/fuzzy_find_tool.go | 2 +- internal/tool/git_commit.go | 6 +- internal/tool/git_commit_test.go | 22 +- internal/tool/git_hooks.go | 60 ++--- internal/tool/git_hooks_test.go | 62 ++--- internal/tool/import_organizer_test.go | 8 +- internal/tool/intents.go | 2 +- internal/tool/interceptor.go | 2 +- internal/tool/interceptor_test.go | 2 +- internal/tool/jobs_tool.go | 4 +- internal/tool/jobs_tool_test.go | 2 +- internal/tool/lint_hook.go | 2 +- internal/tool/lsp.go | 4 +- internal/tool/mcp_auth.go | 8 +- internal/tool/mcp_auth_test.go | 4 +- internal/tool/mcp_lsp.go | 2 +- internal/tool/mcp_resources.go | 2 +- internal/tool/mcp_tool.go | 8 +- internal/tool/media_generation.go | 8 +- internal/tool/path_guard.go | 2 +- internal/tool/path_resolver.go | 2 +- internal/tool/powershell.go | 2 +- internal/tool/powershell_test.go | 2 +- internal/tool/project_verify.go | 2 +- internal/tool/prompt_queue.go | 2 +- internal/tool/refactor.go | 2 +- internal/tool/registry_lazy.go | 6 +- internal/tool/safety.go | 48 ++-- internal/tool/safety_test.go | 74 +++--- internal/tool/schedule.go | 2 +- internal/tool/schedule_test.go | 4 +- internal/tool/screenshot.go | 2 +- internal/tool/search_x.go | 2 +- internal/tool/search_x_test.go | 2 +- internal/tool/session_query.go | 2 +- internal/tool/session_query_test.go | 6 +- internal/tool/skill.go | 10 +- internal/tool/skill_policy_test.go | 2 +- internal/tool/smart_reader.go | 4 +- internal/tool/spec.go | 14 +- internal/tool/spec_analyze.go | 2 +- internal/tool/spec_bdd.go | 2 +- internal/tool/spec_clarify.go | 2 +- internal/tool/spec_constitution.go | 2 +- internal/tool/spec_converge.go | 2 +- internal/tool/spec_drift.go | 2 +- internal/tool/spec_ground.go | 4 +- internal/tool/spec_links.go | 2 +- internal/tool/spec_master.go | 2 +- internal/tool/spec_parallel.go | 4 +- internal/tool/spec_plan_variations.go | 2 +- internal/tool/spec_progress.go | 2 +- internal/tool/spec_properties.go | 2 +- internal/tool/spec_review.go | 2 +- internal/tool/spec_test.go | 10 +- internal/tool/spec_test_first.go | 2 +- internal/tool/spec_testgen.go | 2 +- internal/tool/spec_trace.go | 2 +- internal/tool/spec_version.go | 4 +- internal/tool/sql.go | 8 +- internal/tool/task_control.go | 2 +- internal/tool/task_control_test.go | 2 +- internal/tool/task_tools.go | 4 +- internal/tool/terminal.go | 4 +- internal/tool/terminal_test.go | 2 +- internal/tool/testmain_test.go | 2 +- internal/tool/ticket_compliance.go | 8 +- internal/tool/ticket_compliance_test.go | 42 ++-- internal/tool/tool.go | 26 +- internal/tool/tool_health.go | 2 +- internal/tool/tool_integration_test.go | 12 +- internal/tool/tool_metadata_test.go | 12 +- internal/tool/tool_test.go | 6 +- internal/tool/toolset_tool.go | 2 +- internal/tool/transaction.go | 2 +- internal/tool/treesitter.go | 2 +- internal/tool/web_fetch.go | 2 +- internal/tool/web_search.go | 2 +- internal/tool/web_search_brave.go | 4 +- internal/tool/web_search_deepseek.go | 4 +- internal/tool/web_search_exa.go | 4 +- internal/tool/web_search_perplexity.go | 4 +- internal/tool/web_search_searxng.go | 4 +- internal/tool/web_test.go | 2 +- internal/tool/workflow.go | 2 +- internal/tool/workflow_test.go | 2 +- internal/tools/lsp_tools.go | 2 +- internal/tools/lsp_tools_execute_test.go | 2 +- internal/tools/lsp_tools_test.go | 2 +- internal/trust/store.go | 28 +-- internal/trust/store_test.go | 8 +- internal/tui/graphics.go | 4 +- internal/types/client.go | 58 ++--- internal/types/client_test.go | 6 +- internal/types/severity.go | 4 +- internal/ui/icons/codepoints.go | 8 +- internal/ui/icons/detect.go | 6 +- internal/ui/icons/detect_test.go | 28 +-- internal/ui/icons/icons.go | 2 +- internal/update/update.go | 6 +- internal/update/update_test.go | 6 +- internal/usage/ledger.go | 2 +- lefthook.yml | 8 +- npm/graycode/package.json | 33 --- .../bin/.keep | 0 .../package.json | 6 +- .../bin/.keep | 0 .../package.json | 6 +- .../bin/.keep | 0 .../package.json | 6 +- .../bin/.keep | 0 .../package.json | 6 +- .../bin/.keep | 0 .../package.json | 6 +- .../bin/.keep | 0 .../package.json | 6 +- npm/{graycode => hawk}/bin/postinstall.js | 48 ++-- npm/hawk/package.json | 33 +++ .../scripts/assemble-platform-packages.js | 56 ++--- ...ode-daemon.service => hawk-daemon.service} | 22 +- scripts/check-ecosystem-boundaries.sh | 8 +- scripts/check-eyrie-client-imports.sh | 27 +++ ...dary.sh => check-eyrie-engine-boundary.sh} | 20 +- .../check-graycode-router-client-imports.sh | 27 --- scripts/check-internal-layer-imports.sh | 6 +- scripts/check-module-release-parity.sh | 10 +- scripts/check-shared-types-imports.sh | 6 +- scripts/check-support-repo-coupling.sh | 2 +- scripts/e2e-macos.sh | 32 +-- scripts/fresh-setup-test.sh | 24 +- scripts/generate-workspace.sh | 4 +- scripts/{smoke-graycode.sh => smoke-hawk.sh} | 10 +- scripts/stage3_analyze.py | 2 +- scripts/verify-developer-path.sh | 4 +- scripts/verify-milestone.sh | 2 +- spec/README.md | 8 +- testdata/compatibility-matrix.json | 22 +- testdata/compatibility-matrix.schema.json | 2 +- testdata/golden/help_root.txt | 62 ++--- 1382 files changed, 9071 insertions(+), 9072 deletions(-) rename .github/actions/{checkout-graycode-router => checkout-eyrie}/action.yml (95%) rename .github/actions/{graycode => hawk}/action.yml (59%) rename cmd/{graycode => hawk}/integration_test.go (98%) rename cmd/{graycode => hawk}/main.go (84%) rename docs/architecture/{graycode-architecture-baseline.md => hawk-architecture-baseline.md} (78%) rename docs/architecture/{graycode-architecture-v1-definition-of-done.md => hawk-architecture-v1-definition-of-done.md} (69%) rename docs/architecture/{graycode-capability-seams.md => hawk-capability-seams.md} (96%) rename docs/architecture/{graycode-contract-spec.md => hawk-contract-spec.md} (76%) rename docs/architecture/{graycode-current-vs-proposed.md => hawk-current-vs-proposed.md} (70%) rename docs/architecture/{graycode-dependency-rules.md => hawk-dependency-rules.md} (61%) rename docs/architecture/{graycode-harness.md => hawk-harness.md} (76%) rename docs/architecture/{graycode-product-architecture.md => hawk-product-architecture.md} (61%) rename docs/architecture/{graycode-provider-abstraction.md => hawk-provider-abstraction.md} (58%) rename docs/architecture/{graycode-repo-roles.md => hawk-repo-roles.md} (58%) rename docs/architecture/{graycode-review-verify-lifecycle.md => hawk-review-verify-lifecycle.md} (84%) rename docs/architecture/{graycode-swift-event-model.md => hawk-swift-event-model.md} (95%) rename docs/design/{GRAYCODE-CLOUD-SAAS.md => HAWK-CLOUD-SAAS.md} (89%) rename docs/plans/{FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md => FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md} (73%) rename docs/plans/{graycode-contracts-migration-backlog.md => hawk-contracts-migration-backlog.md} (61%) delete mode 100644 examples/github/graycode-delivery-context.yml rename examples/github/{graycode-ci-exec.yml => hawk-ci-exec.yml} (56%) create mode 100644 examples/github/hawk-delivery-context.yml rename examples/github/{graycode-issue-resolver.yml => hawk-issue-resolver.yml} (55%) rename internal/config/{graycode_router_apply.go => eyrie_apply.go} (69%) rename internal/config/{graycode_router_engine.go => eyrie_engine.go} (79%) rename internal/config/{graycode_router_engine_test.go => eyrie_engine_test.go} (83%) rename internal/config/{graycode_router_selection.go => eyrie_selection.go} (75%) rename internal/{graycodeerr => hawkerr}/bridge_error_test.go (96%) rename internal/{graycodeerr => hawkerr}/classify.go (97%) rename internal/{graycodeerr => hawkerr}/classify_extra_test.go (98%) rename internal/{graycodeerr => hawkerr}/errors.go (97%) rename internal/{graycodeerr => hawkerr}/exitcode.go (86%) rename internal/{graycodeerr => hawkerr}/exitcode_test.go (99%) delete mode 100644 npm/graycode/package.json rename npm/{graycode-darwin-arm64 => hawk-darwin-arm64}/bin/.keep (100%) rename npm/{graycode-darwin-arm64 => hawk-darwin-arm64}/package.json (57%) rename npm/{graycode-darwin-x64 => hawk-darwin-x64}/bin/.keep (100%) rename npm/{graycode-darwin-x64 => hawk-darwin-x64}/package.json (57%) rename npm/{graycode-linux-arm64 => hawk-linux-arm64}/bin/.keep (100%) rename npm/{graycode-linux-arm64 => hawk-linux-arm64}/package.json (57%) rename npm/{graycode-linux-x64 => hawk-linux-x64}/bin/.keep (100%) rename npm/{graycode-linux-x64 => hawk-linux-x64}/package.json (57%) rename npm/{graycode-win32-arm64 => hawk-win32-arm64}/bin/.keep (100%) rename npm/{graycode-win32-arm64 => hawk-win32-arm64}/package.json (56%) rename npm/{graycode-win32-x64 => hawk-win32-x64}/bin/.keep (100%) rename npm/{graycode-win32-x64 => hawk-win32-x64}/package.json (57%) rename npm/{graycode => hawk}/bin/postinstall.js (79%) create mode 100644 npm/hawk/package.json rename npm/{graycode => hawk}/scripts/assemble-platform-packages.js (74%) rename packaging/systemd/{graycode-daemon.service => hawk-daemon.service} (51%) create mode 100755 scripts/check-eyrie-client-imports.sh rename scripts/{check-graycode-router-engine-boundary.sh => check-eyrie-engine-boundary.sh} (55%) delete mode 100755 scripts/check-graycode-router-client-imports.sh rename scripts/{smoke-graycode.sh => smoke-hawk.sh} (82%) diff --git a/.dockerignore b/.dockerignore index 2f2b517c7..da96199ca 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,8 +5,8 @@ .factory Dockerfile .dockerignore -graycode_bin -graycode_test_bin +hawk_bin +hawk_test_bin coverage.out coverage.html go.work diff --git a/.env.example b/.env.example index 49f8e7f14..991d5b848 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,10 @@ -# graycode daemon environment variables — copy to .env and fill in. -# Only GRAYCODE_DAEMON_API_KEY is consumed from the environment. -GRAYCODE_DAEMON_API_KEY= +# hawk daemon environment variables — copy to .env and fill in. +# Only HAWK_DAEMON_API_KEY is consumed from the environment. +HAWK_DAEMON_API_KEY= -# The following are NOT read by graycode-cli — setting them has no effect: -# GRAYCODE_DAEMON_PORT / GRAYCODE_DAEMON_HOST — daemon binds 127.0.0.1:4590; -# override with the --host / --port flags (see `graycode daemon start --help`) -# GRAYCODE_ROUTER_API_KEY / GRAYCODE_ROUTER_BASE_URL, HARRIER_API_KEY / HARRIER_ADDR — +# The following are NOT read by hawk — setting them has no effect: +# HAWK_DAEMON_PORT / HAWK_DAEMON_HOST — daemon binds 127.0.0.1:4590; +# override with the --host / --port flags (see `hawk daemon start --help`) +# EYRIE_API_KEY / EYRIE_BASE_URL, HARRIER_API_KEY / HARRIER_ADDR — # consumed by sibling services, not by this binary # See docs/user-guide/05-configuration.md for the full precedence chain. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 474819e4e..a19380813 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -# CODEOWNERS for graycode +# CODEOWNERS for hawk # # These owners will be the default owners for everything in the repo. Unless # a later match takes precedence, they will be requested for review when diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e0a4255ca..bd16c3cbe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -11,7 +11,7 @@ body: of the form as you can — the more we know, the faster we can fix it. Before submitting: - - Search [existing issues](https://github.com/GrayCodeAI/graycode-cli/issues) to avoid duplicates. + - Search [existing issues](https://github.com/GrayCodeAI/hawk/issues) to avoid duplicates. - If this is a security issue, please **do not** file a public issue. See `SECURITY.md`. - type: textarea @@ -19,7 +19,7 @@ body: attributes: label: What happened? description: A clear, concise description of the bug. - placeholder: When I run `graycode ...`, I expected X but got Y. + placeholder: When I run `hawk ...`, I expected X but got Y. validations: required: true @@ -29,7 +29,7 @@ body: label: Steps to reproduce description: Minimal steps that reliably reproduce the problem. placeholder: | - 1. Run `graycode ...` + 1. Run `hawk ...` 2. Type `...` 3. See error `...` validations: @@ -44,10 +44,10 @@ body: required: true - type: input - id: graycode-version + id: hawk-version attributes: - label: graycode version - description: Output of `graycode version` (or `graycode --version`). + label: hawk version + description: Output of `hawk version` (or `hawk --version`). placeholder: "0.1.0" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4a730ef66..a1885141b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - name: Security vulnerability - url: https://github.com/GrayCodeAI/graycode-cli/security/advisories/new + url: https://github.com/GrayCodeAI/hawk/security/advisories/new about: Please report security issues privately via a GitHub Security Advisory. See SECURITY.md. - name: Question / discussion - url: https://github.com/GrayCodeAI/graycode-cli/discussions + url: https://github.com/GrayCodeAI/hawk/discussions about: Have a question or want to discuss an idea? Open a discussion instead of an issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 62e4245df..6f62dab5c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,5 +1,5 @@ name: Feature request -description: Suggest an improvement or new capability for graycode. +description: Suggest an improvement or new capability for hawk. title: "feat: " labels: ["enhancement", "triage"] @@ -7,13 +7,13 @@ body: - type: markdown attributes: value: | - Thanks for proposing a feature. graycode is built **for developers first** + Thanks for proposing a feature. hawk is built **for developers first** (one machine, local config, keychain credentials) — teams and enterprise come later. We evaluate every request against: _"would a single developer on their own machine benefit from this?"_ Before submitting: - - Search [existing issues](https://github.com/GrayCodeAI/graycode-cli/issues) to avoid duplicates. + - Search [existing issues](https://github.com/GrayCodeAI/hawk/issues) to avoid duplicates. - For large changes, consider opening a discussion first. - type: textarea @@ -21,7 +21,7 @@ body: attributes: label: What problem are you trying to solve? description: Describe the user problem first. Solutions can come later. - placeholder: When I'm doing X, graycode makes me do Y, which is painful because Z. + placeholder: When I'm doing X, hawk makes me do Y, which is painful because Z. validations: required: true @@ -29,7 +29,7 @@ body: id: proposal attributes: label: Proposed solution - description: How would you like graycode to behave? CLI flags, output, config, etc. + description: How would you like hawk to behave? CLI flags, output, config, etc. validations: required: true @@ -55,7 +55,7 @@ body: id: principles attributes: label: Developer fit - description: graycode avoids enterprise scope for now. Confirm this feature respects that. + description: hawk avoids enterprise scope for now. Confirm this feature respects that. options: - label: Works with zero configuration (sensible defaults). - label: Works offline / does not require a cloud account. diff --git a/.github/actions/checkout-graycode-router/action.yml b/.github/actions/checkout-eyrie/action.yml similarity index 95% rename from .github/actions/checkout-graycode-router/action.yml rename to .github/actions/checkout-eyrie/action.yml index 987441e3b..f37455399 100644 --- a/.github/actions/checkout-graycode-router/action.yml +++ b/.github/actions/checkout-eyrie/action.yml @@ -1,5 +1,5 @@ name: Checkout ecosystem -description: Clone graycode-eco sibling repos into the workspace parent for graycode's go.work +description: Clone graycode-eco sibling repos into the workspace parent for hawk's go.work inputs: ref: @@ -24,7 +24,7 @@ runs: # ecosystem migration; downstream guards skip absent checkouts too). ws_parent="$(cd "${GITHUB_WORKSPACE}/.." && pwd)" while IFS= read -r repo; do - [ "$repo" = graycode-cli ] && continue + [ "$repo" = hawk ] && continue dest="${ws_parent}/${repo}" if [ -d "$dest/.git" ]; then echo "$repo already present at $dest" diff --git a/.github/actions/graycode/action.yml b/.github/actions/hawk/action.yml similarity index 59% rename from .github/actions/graycode/action.yml rename to .github/actions/hawk/action.yml index f60cae4fc..42f5c8031 100644 --- a/.github/actions/graycode/action.yml +++ b/.github/actions/hawk/action.yml @@ -1,7 +1,7 @@ -name: Graycode +name: Hawk description: >- - Run the Graycode coding agent headlessly in a GitHub workflow. Graycode is + Run the Hawk coding agent headlessly in a GitHub workflow. Hawk is model- and provider-agnostic: you supply the provider and API key as inputs. author: GrayCodeAI @@ -12,7 +12,7 @@ branding: inputs: prompt: - description: The instruction for Graycode to execute. Mutually exclusive with prompt-file. + description: The instruction for Hawk to execute. Mutually exclusive with prompt-file. required: false default: "" prompt-file: @@ -29,20 +29,20 @@ inputs: api-key: description: >- The provider API key. Pass it from a repository or organization secret; - it is exported only for the Graycode step and never written to the log. + it is exported only for the Hawk step and never written to the log. required: false default: "" api-key-env: description: >- Name of the environment variable the chosen provider reads its key from (for example OPENAI_API_KEY or ANTHROPIC_API_KEY). When set together with - api-key, that variable is exported only for the Graycode step. Leave the action + api-key, that variable is exported only for the Hawk step. Leave the action provider-agnostic by passing the env name your provider expects rather than hardcoding one here. required: false default: "" model: - description: Model id to use for the run. Passed to `graycode exec --model`. + description: Model id to use for the run. Passed to `hawk exec --model`. required: false default: "" auto: @@ -54,19 +54,19 @@ inputs: default: supervised agent: description: >- - Agent persona to use (from Graycode user state). Maps to `graycode exec --agent`. + Agent persona to use (from Hawk user state). Maps to `hawk exec --agent`. required: false default: "" cwd: description: >- - Working directory override. Maps to `graycode exec --cwd`. + Working directory override. Maps to `hawk exec --cwd`. Defaults to the action's working-directory input. required: false default: "" ephemeral: description: >- When true, skip session persistence. Ideal for CI runs. - Maps to `graycode exec --ephemeral`. + Maps to `hawk exec --ephemeral`. required: false default: "false" worktree: @@ -80,7 +80,7 @@ inputs: default: text post-to: description: >- - Optional destination for a run summary after Graycode finishes: `pr-comment` + Optional destination for a run summary after Hawk finishes: `pr-comment` posts to the triggering pull request, `slack` posts to slack-webhook-url, or `none` (default) posts nowhere. required: false @@ -98,26 +98,26 @@ inputs: required: false default: ${{ github.token }} working-directory: - description: Directory to run Graycode in. Defaults to ${{ github.workspace }}. + description: Directory to run Hawk in. Defaults to ${{ github.workspace }}. required: false default: ${{ github.workspace }} - graycode-version: + hawk-version: description: >- - Graycode release version/tag to install (e.g. v1.2.3 or latest). + Hawk release version/tag to install (e.g. v1.2.3 or latest). Defaults to the ref this action was resolved at. required: false default: "" - graycode-repo: - description: Repository to install Graycode from (owner/repo). + hawk-repo: + description: Repository to install Hawk from (owner/repo). required: false - default: GrayCodeAI/graycode-cli + default: GrayCodeAI/hawk outputs: exit-code: - description: The exit code Graycode returned (0 success, 2 usage, 3 provider, non-zero otherwise). + description: The exit code Hawk returned (0 success, 2 usage, 3 provider, non-zero otherwise). value: ${{ steps.run.outputs.exit-code }} output-file: - description: Path to the captured Graycode stdout (the raw output-format stream). + description: Path to the captured Hawk stdout (the raw output-format stream). value: ${{ steps.run.outputs.output-file }} summary: description: A short human-readable summary line parsed from the run, when available. @@ -126,24 +126,24 @@ outputs: runs: using: composite steps: - - name: Install Graycode + - name: Install Hawk id: install shell: bash env: - GRAYCODE_REPO: ${{ inputs.graycode-repo }} - GRAYCODE_VERSION_INPUT: ${{ inputs.graycode-version }} + HAWK_REPO: ${{ inputs.hawk-repo }} + HAWK_VERSION_INPUT: ${{ inputs.hawk-version }} ACTION_REF: ${{ github.action_ref }} ACTION_PATH: ${{ github.action_path }} RUNNER_TEMP: ${{ runner.temp }} run: | set -euo pipefail if [ "${RUNNER_OS}" = "Windows" ]; then - echo "::error::The Graycode action currently supports Linux and macOS runners only." >&2 + echo "::error::The Hawk action currently supports Linux and macOS runners only." >&2 exit 1 fi # Resolve the version to install: explicit input wins; otherwise the ref # this action was checked out at (a tag); otherwise the latest release. - version="${GRAYCODE_VERSION_INPUT}" + version="${HAWK_VERSION_INPUT}" if [ -z "${version}" ]; then version="${ACTION_REF}" fi @@ -155,46 +155,46 @@ runs: if printf '%s' "${version}" | grep -qiE '^[0-9a-f]{40}$'; then version="latest" fi - install_dir="${RUNNER_TEMP}/graycode-bin" - src_dir="${RUNNER_TEMP}/graycode-src" + install_dir="${RUNNER_TEMP}/hawk-bin" + src_dir="${RUNNER_TEMP}/hawk-src" mkdir -p "${install_dir}" "${src_dir}" - # Download the Graycode source archive and extract it + # Download the Hawk source archive and extract it if [ "${version}" = "latest" ]; then - tarball_url=$(curl -sL "https://api.github.com/repos/${GRAYCODE_REPO}/releases/latest" \ + tarball_url=$(curl -sL "https://api.github.com/repos/${HAWK_REPO}/releases/latest" \ | grep '"tarball_url":' | sed 's/.*"\([^"]*\)".*/\1/') else - tarball_url="https://github.com/${GRAYCODE_REPO}/archive/refs/tags/v${version}.tar.gz" + tarball_url="https://github.com/${HAWK_REPO}/archive/refs/tags/v${version}.tar.gz" fi curl -sL "${tarball_url}" | tar -xz -C "${src_dir}" --strip-components=1 - # Build the Graycode binary from source (Go must be available on the runner) + # Build the Hawk binary from source (Go must be available on the runner) if ! command -v go &>/dev/null; then echo "::error::Go is required but not found on the runner PATH. Add actions/setup-go before this action, or install Go manually." >&2 exit 1 fi - go build -o "${install_dir}/graycode" "${src_dir}/cmd/graycode" - chmod +x "${install_dir}/graycode" + go build -o "${install_dir}/hawk" "${src_dir}/cmd/hawk" + chmod +x "${install_dir}/hawk" echo "${install_dir}" >> "${GITHUB_PATH}" - echo "Installed Graycode from ${GRAYCODE_REPO}@${version}" + echo "Installed Hawk from ${HAWK_REPO}@${version}" - - name: Run Graycode + - name: Run Hawk id: run shell: bash working-directory: ${{ inputs.working-directory }} env: # Secrets are passed via the environment and never echoed. `set +x` and # the absence of any `echo` of these values keep them out of the log. - GRAYCODE_INPUT_API_KEY: ${{ inputs.api-key }} - GRAYCODE_INPUT_API_KEY_ENV: ${{ inputs.api-key-env }} - GRAYCODE_INPUT_PROVIDER: ${{ inputs.provider }} - GRAYCODE_INPUT_PROMPT: ${{ inputs.prompt }} - GRAYCODE_INPUT_PROMPT_FILE: ${{ inputs.prompt-file }} - GRAYCODE_INPUT_MODEL: ${{ inputs.model }} - GRAYCODE_INPUT_AUTO: ${{ inputs.auto }} - GRAYCODE_INPUT_AGENT: ${{ inputs.agent }} - GRAYCODE_INPUT_CWD: ${{ inputs.cwd }} - GRAYCODE_INPUT_EPHEMERAL: ${{ inputs.ephemeral }} - GRAYCODE_INPUT_WORKTREE: ${{ inputs.worktree }} - GRAYCODE_INPUT_OUTPUT_FORMAT: ${{ inputs.output-format }} + HAWK_INPUT_API_KEY: ${{ inputs.api-key }} + HAWK_INPUT_API_KEY_ENV: ${{ inputs.api-key-env }} + HAWK_INPUT_PROVIDER: ${{ inputs.provider }} + HAWK_INPUT_PROMPT: ${{ inputs.prompt }} + HAWK_INPUT_PROMPT_FILE: ${{ inputs.prompt-file }} + HAWK_INPUT_MODEL: ${{ inputs.model }} + HAWK_INPUT_AUTO: ${{ inputs.auto }} + HAWK_INPUT_AGENT: ${{ inputs.agent }} + HAWK_INPUT_CWD: ${{ inputs.cwd }} + HAWK_INPUT_EPHEMERAL: ${{ inputs.ephemeral }} + HAWK_INPUT_WORKTREE: ${{ inputs.worktree }} + HAWK_INPUT_OUTPUT_FORMAT: ${{ inputs.output-format }} ACTION_PATH: ${{ github.action_path }} RUNNER_TEMP: ${{ runner.temp }} run: | @@ -203,50 +203,50 @@ runs: # Export the provider key under the env name the provider expects. This # keeps the action provider-agnostic: no provider->env mapping is baked # in. The value is never printed. - if [ -n "${GRAYCODE_INPUT_API_KEY}" ] && [ -n "${GRAYCODE_INPUT_API_KEY_ENV}" ]; then - if ! printf '%s' "${GRAYCODE_INPUT_API_KEY_ENV}" | grep -qE '^[A-Za-z_][A-Za-z0-9_]*$'; then + if [ -n "${HAWK_INPUT_API_KEY}" ] && [ -n "${HAWK_INPUT_API_KEY_ENV}" ]; then + if ! printf '%s' "${HAWK_INPUT_API_KEY_ENV}" | grep -qE '^[A-Za-z_][A-Za-z0-9_]*$'; then echo "::error::api-key-env must be a valid environment variable name." >&2 exit 2 fi - export "${GRAYCODE_INPUT_API_KEY_ENV}=${GRAYCODE_INPUT_API_KEY}" + export "${HAWK_INPUT_API_KEY_ENV}=${HAWK_INPUT_API_KEY}" fi - if [ -n "${GRAYCODE_INPUT_PROVIDER}" ]; then - export GRAYCODE_PROVIDER="${GRAYCODE_INPUT_PROVIDER}" + if [ -n "${HAWK_INPUT_PROVIDER}" ]; then + export HAWK_PROVIDER="${HAWK_INPUT_PROVIDER}" fi args=(exec) - if [ -n "${GRAYCODE_INPUT_PROMPT_FILE}" ] && [ -n "${GRAYCODE_INPUT_PROMPT}" ]; then + if [ -n "${HAWK_INPUT_PROMPT_FILE}" ] && [ -n "${HAWK_INPUT_PROMPT}" ]; then echo "::error::The 'prompt' and 'prompt-file' inputs are mutually exclusive." >&2 exit 2 fi - if [ -n "${GRAYCODE_INPUT_PROMPT_FILE}" ]; then - args+=(--file "${GRAYCODE_INPUT_PROMPT_FILE}") - elif [ -n "${GRAYCODE_INPUT_PROMPT}" ]; then - args+=("${GRAYCODE_INPUT_PROMPT}") + if [ -n "${HAWK_INPUT_PROMPT_FILE}" ]; then + args+=(--file "${HAWK_INPUT_PROMPT_FILE}") + elif [ -n "${HAWK_INPUT_PROMPT}" ]; then + args+=("${HAWK_INPUT_PROMPT}") else echo "::error::Provide either the 'prompt' or 'prompt-file' input." >&2 exit 2 fi - [ -n "${GRAYCODE_INPUT_MODEL}" ] && args+=(--model "${GRAYCODE_INPUT_MODEL}") - [ -n "${GRAYCODE_INPUT_AUTO}" ] && args+=(--auto "${GRAYCODE_INPUT_AUTO}") - [ -n "${GRAYCODE_INPUT_AGENT}" ] && args+=(--agent "${GRAYCODE_INPUT_AGENT}") - [ -n "${GRAYCODE_INPUT_CWD}" ] && args+=(--cwd "${GRAYCODE_INPUT_CWD}") - if [ "${GRAYCODE_INPUT_EPHEMERAL}" = "true" ]; then + [ -n "${HAWK_INPUT_MODEL}" ] && args+=(--model "${HAWK_INPUT_MODEL}") + [ -n "${HAWK_INPUT_AUTO}" ] && args+=(--auto "${HAWK_INPUT_AUTO}") + [ -n "${HAWK_INPUT_AGENT}" ] && args+=(--agent "${HAWK_INPUT_AGENT}") + [ -n "${HAWK_INPUT_CWD}" ] && args+=(--cwd "${HAWK_INPUT_CWD}") + if [ "${HAWK_INPUT_EPHEMERAL}" = "true" ]; then args+=(--ephemeral) fi - if [ "${GRAYCODE_INPUT_WORKTREE}" = "true" ]; then + if [ "${HAWK_INPUT_WORKTREE}" = "true" ]; then args+=(--worktree) fi - [ -n "${GRAYCODE_INPUT_OUTPUT_FORMAT}" ] && args+=(--output-format "${GRAYCODE_INPUT_OUTPUT_FORMAT}") + [ -n "${HAWK_INPUT_OUTPUT_FORMAT}" ] && args+=(--output-format "${HAWK_INPUT_OUTPUT_FORMAT}") - output_file="${RUNNER_TEMP}/graycode-output.txt" + output_file="${RUNNER_TEMP}/hawk-output.txt" - # Run Graycode. Capture stdout to a file AND echo it to the log. Surface the + # Run Hawk. Capture stdout to a file AND echo it to the log. Surface the # real exit code as the step's status. set +e - graycode "${args[@]}" | tee "${output_file}" + hawk "${args[@]}" | tee "${output_file}" code=${PIPESTATUS[0]} set -e @@ -255,14 +255,14 @@ runs: # Best-effort one-line summary if [ "${code}" = "0" ]; then - summary="Graycode run succeeded" + summary="Hawk run succeeded" else - summary="Graycode run failed (exit ${code})" + summary="Hawk run failed (exit ${code})" fi { - echo "summary<> "${GITHUB_OUTPUT}" exit "${code}" @@ -273,13 +273,13 @@ runs: env: GH_TOKEN: ${{ inputs.github-token }} PR_NUMBER: ${{ github.event.pull_request.number }} - GRAYCODE_EXIT: ${{ steps.run.outputs.exit-code }} - GRAYCODE_SUMMARY: ${{ steps.run.outputs.summary }} + HAWK_EXIT: ${{ steps.run.outputs.exit-code }} + HAWK_SUMMARY: ${{ steps.run.outputs.summary }} run: | set -euo pipefail status="succeeded" - [ "${GRAYCODE_EXIT}" != "0" ] && status="failed (exit ${GRAYCODE_EXIT})" - body="$(printf 'Graycode run %s.\n\n%s' "${status}" "${GRAYCODE_SUMMARY}")" + [ "${HAWK_EXIT}" != "0" ] && status="failed (exit ${HAWK_EXIT})" + body="$(printf 'Hawk run %s.\n\n%s' "${status}" "${HAWK_SUMMARY}")" gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ -f body="${body}" >/dev/null @@ -288,14 +288,14 @@ runs: shell: bash env: SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }} - GRAYCODE_EXIT: ${{ steps.run.outputs.exit-code }} - GRAYCODE_SUMMARY: ${{ steps.run.outputs.summary }} + HAWK_EXIT: ${{ steps.run.outputs.exit-code }} + HAWK_SUMMARY: ${{ steps.run.outputs.summary }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail status="succeeded" - [ "${GRAYCODE_EXIT}" != "0" ] && status="failed (exit ${GRAYCODE_EXIT})" - text="Graycode run ${status} for ${GITHUB_REPOSITORY}@${GITHUB_REF_NAME}. ${GRAYCODE_SUMMARY} ${RUN_URL}" + [ "${HAWK_EXIT}" != "0" ] && status="failed (exit ${HAWK_EXIT})" + text="Hawk run ${status} for ${GITHUB_REPOSITORY}@${GITHUB_REF_NAME}. ${HAWK_SUMMARY} ${RUN_URL}" payload="$(jq -n --arg text "${text}" '{text: $text}')" curl --fail --silent --show-error -X POST -H 'Content-Type: application/json' \ -d "${payload}" "${SLACK_WEBHOOK_URL}" >/dev/null || \ diff --git a/.github/actions/setup-deps/action.yml b/.github/actions/setup-deps/action.yml index 80610ede9..805b02191 100644 --- a/.github/actions/setup-deps/action.yml +++ b/.github/actions/setup-deps/action.yml @@ -28,7 +28,7 @@ runs: ws_parent="$(cd "${GITHUB_WORKSPACE}/.." && pwd)" mkdir -p "$ws_parent" while IFS= read -r repo; do - [ "$repo" = graycode-cli ] && continue + [ "$repo" = hawk ] && continue dest="$ws_parent/$repo" if [ -d "$dest/.git" ]; then echo "$repo already present at $dest" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2eabb8e70..c8f09b1f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ env: # Go's reference is explicit that a committed go.work "may cause a continuous # integration (CI) system to select and thus test the wrong versions of a # module's dependencies" and that "CI systems should generally not be allowed to - # use the go.work file". Because ./.github/actions/checkout-graycode-router clones the + # use the go.work file". Because ./.github/actions/checkout-eyrie clones the # siblings at branch HEAD into the workspace parent, workspace mode would silently # hide version skew (e.g. a shared pin that no longer matches). # The `module` job opts back in (GOWORK: "auto") since `go work sync` and the @@ -77,13 +77,13 @@ jobs: run: bash ./scripts/check-ecosystem-boundaries.sh - name: internal layer boundary guard run: bash ./scripts/check-internal-layer-imports.sh - - name: graycode-router client boundary guard - run: bash ./scripts/check-graycode-router-client-imports.sh - - name: graycode-router engine facade boundary guard - run: bash ./scripts/check-graycode-router-engine-boundary.sh + - name: eyrie client boundary guard + run: bash ./scripts/check-eyrie-client-imports.sh + - name: eyrie engine facade boundary guard + run: bash ./scripts/check-eyrie-engine-boundary.sh # ------------------------------------------------------------------------- - # 2. Module hygiene — tidy, verify Graycode plus the sibling Go modules via go.work. + # 2. Module hygiene — tidy, verify Hawk plus the sibling Go modules via go.work. # ------------------------------------------------------------------------- module: name: module hygiene @@ -98,7 +98,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref == 'feat/ecosystem-wiring-pr' && 'feat/ecosystem-wiring' || github.head_ref || github.ref_name }} - name: Generate ecosystem workspace @@ -113,7 +113,7 @@ jobs: # go mod tidy can mis-resolve workspace modules here; go work sync is # the supported workspace hygiene step. go work sync - go build -mod=readonly -o /dev/null ./cmd/graycode + go build -mod=readonly -o /dev/null ./cmd/hawk if ! git diff --quiet -- go.mod go.sum go.work go.work.sum; then echo "::error::go.mod / go.sum / go.work files out of date — run 'go work sync' locally and commit" git diff -- go.mod go.sum go.work go.work.sum @@ -125,7 +125,7 @@ jobs: run: | workspace_file="${GITHUB_WORKSPACE}/../go.work" while IFS= read -r module; do - [ "$module" = graycode ] && continue + [ "$module" = hawk ] && continue if ! grep -q "./${module}" "$workspace_file"; then echo "::error::go.work must include ./${module}." cat "$workspace_file" @@ -149,7 +149,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -162,7 +162,7 @@ jobs: run: | go mod download go mod verify - go build -mod=readonly ./cmd/graycode + go build -mod=readonly ./cmd/hawk go test ./... -count=1 -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E' release-parity: @@ -172,7 +172,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -191,7 +191,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -200,8 +200,8 @@ jobs: cache: true - name: go vet run: go vet ./... - - name: graycode harness evaluation audit - run: go run ./cmd/graycode harness --out-dir .graycode/harness + - name: hawk harness evaluation audit + run: go run ./cmd/hawk harness --out-dir .hawk/harness - name: support repo coupling guard run: bash ./scripts/check-support-repo-coupling.sh @@ -216,7 +216,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -236,7 +236,7 @@ jobs: runs-on: ubuntu-latest # NOTE: depends only on `format`, deliberately NOT on `vet`. # A `vet` (or upstream) failure must never skip the test job — that - # historically masked real test regressions (see graycode PR #155, where + # historically masked real test regressions (see hawk PR #155, where # DrainAlerts code shipped with failing tests because `test` was skipped # when `vet` failed on the trailer-strip force-push). Tests must always # run so regressions surface instead of being hidden. @@ -245,7 +245,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -300,7 +300,7 @@ jobs: echo "" echo "Policy: Every t.Skip() should have a comment linking to a GitHub issue." echo "Example:" - echo " // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/123" + echo " // TODO: https://github.com/GrayCodeAI/hawk/issues/123" echo " t.Skip(\"not yet implemented\")" fi @@ -315,7 +315,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -423,7 +423,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c @@ -481,7 +481,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -499,7 +499,7 @@ jobs: run: | # Use -ldflags="-s -w" to match release build size (Makefile LDFLAGS). # This catches binary bloat regressions against the actual shipped artifact size. - size=$(go build -trimpath -ldflags="-s -w" -o /tmp/graycode-bin ./cmd/graycode && wc -c < /tmp/graycode-bin) + size=$(go build -trimpath -ldflags="-s -w" -o /tmp/hawk-bin ./cmd/hawk && wc -c < /tmp/hawk-bin) size_mb=$((size / 1024 / 1024)) echo "Binary size: ${size_mb}MB" # Threshold history: 110MB → 80MB (binary was ~76MB) → 98MB. @@ -513,7 +513,7 @@ jobs: if [ "$size_mb" -gt 98 ]; then echo "::warning::Binary size ${size_mb}MB exceeds 98MB threshold" fi - rm -f /tmp/graycode-bin + rm -f /tmp/hawk-bin # ------------------------------------------------------------------------- # Fuzz — short corpus runs to catch panics in fuzz targets. @@ -526,7 +526,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -543,7 +543,7 @@ jobs: go test -fuzz=FuzzParseSessionMeta -fuzztime=60s ./internal/session/... || true # ------------------------------------------------------------------------- - # 10. Smoke — build graycode and verify ecosystem CLI wiring. + # 10. Smoke — build hawk and verify ecosystem CLI wiring. # ------------------------------------------------------------------------- smoke: name: smoke @@ -553,12 +553,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: ${{ env.GO_VERSION }} cache: true - - name: smoke-graycode.sh - run: ./scripts/smoke-graycode.sh + - name: smoke-hawk.sh + run: ./scripts/smoke-hawk.sh diff --git a/.github/workflows/compatibility-matrix.yml b/.github/workflows/compatibility-matrix.yml index e72f72362..e52499ae1 100644 --- a/.github/workflows/compatibility-matrix.yml +++ b/.github/workflows/compatibility-matrix.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.head_ref || github.ref_name }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.github/workflows/daemon-image.yml b/.github/workflows/daemon-image.yml index 4bf4100f4..71fdf0c60 100644 --- a/.github/workflows/daemon-image.yml +++ b/.github/workflows/daemon-image.yml @@ -8,7 +8,7 @@ on: branches: [main] paths: - "Dockerfile.daemon" - - "packaging/systemd/graycode-daemon.service" + - "packaging/systemd/hawk-daemon.service" - "internal/**" - "cmd/**" - "go.mod" @@ -21,7 +21,7 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: graycodeai/graycode-daemon + IMAGE_NAME: graycodeai/hawk-daemon jobs: build: @@ -51,8 +51,8 @@ jobs: push: false load: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan - cache-from: type=gha,scope=graycode-daemon - cache-to: type=gha,mode=max,scope=graycode-daemon + cache-from: type=gha,scope=hawk-daemon + cache-to: type=gha,mode=max,scope=hawk-daemon build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} @@ -106,8 +106,8 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=graycode-daemon - cache-to: type=gha,mode=max,scope=graycode-daemon + cache-from: type=gha,scope=hawk-daemon + cache-to: type=gha,mode=max,scope=hawk-daemon build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d0c85a8b0..b66304296 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: graycodeai/graycode + IMAGE_NAME: graycodeai/hawk jobs: # Build each platform natively on its own runner (arm64 natively via the @@ -59,8 +59,8 @@ jobs: push: false load: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan - cache-from: type=gha,scope=graycode-amd64 - cache-to: type=gha,mode=max,scope=graycode-amd64 + cache-from: type=gha,scope=hawk-amd64 + cache-to: type=gha,mode=max,scope=hawk-amd64 build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} @@ -98,8 +98,8 @@ jobs: platforms: linux/amd64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:amd64-${{ github.sha }} - cache-from: type=gha,scope=graycode-amd64 - cache-to: type=gha,mode=max,scope=graycode-amd64 + cache-from: type=gha,scope=hawk-amd64 + cache-to: type=gha,mode=max,scope=hawk-amd64 build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} @@ -142,8 +142,8 @@ jobs: push: false load: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan - cache-from: type=gha,scope=graycode-arm64 - cache-to: type=gha,mode=max,scope=graycode-arm64 + cache-from: type=gha,scope=hawk-arm64 + cache-to: type=gha,mode=max,scope=hawk-arm64 build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} @@ -181,8 +181,8 @@ jobs: platforms: linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:arm64-${{ github.sha }} - cache-from: type=gha,scope=graycode-arm64 - cache-to: type=gha,mode=max,scope=graycode-arm64 + cache-from: type=gha,scope=hawk-arm64 + cache-to: type=gha,mode=max,scope=hawk-arm64 build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} @@ -267,9 +267,9 @@ jobs: platforms: linux/amd64 push: false load: true - tags: ${{ env.REGISTRY }}/graycodeai/graycode-sandbox:scan - cache-from: type=gha,scope=graycode-sandbox - cache-to: type=gha,mode=max,scope=graycode-sandbox + tags: ${{ env.REGISTRY }}/graycodeai/hawk-sandbox:scan + cache-from: type=gha,scope=hawk-sandbox + cache-to: type=gha,mode=max,scope=hawk-sandbox - name: Scan sandbox image uses: aquasecurity/setup-trivy@3fb12ec12f41e471780db15c232d5dd185dcb514 @@ -290,7 +290,7 @@ jobs: --format sarif \ --output trivy-sandbox-image.sarif \ --exit-code 1 \ - ${{ env.REGISTRY }}/graycodeai/graycode-sandbox:scan + ${{ env.REGISTRY }}/graycodeai/hawk-sandbox:scan - name: Build and publish public sandbox image uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 @@ -300,10 +300,10 @@ jobs: platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: | - ${{ env.REGISTRY }}/graycodeai/graycode-sandbox:${{ steps.sandbox-version.outputs.tag }} - ${{ env.REGISTRY }}/graycodeai/graycode-sandbox:latest - cache-from: type=gha,scope=graycode-sandbox - cache-to: type=gha,mode=max,scope=graycode-sandbox + ${{ env.REGISTRY }}/graycodeai/hawk-sandbox:${{ steps.sandbox-version.outputs.tag }} + ${{ env.REGISTRY }}/graycodeai/hawk-sandbox:latest + cache-from: type=gha,scope=hawk-sandbox + cache-to: type=gha,mode=max,scope=hawk-sandbox # Publish the sandbox scan to GitHub code scanning. PRs still run the # scan; they just do not publish the release image artifacts. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec7818f4a..c84872ba7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: 0 # goreleaser needs full history for changelog # Releases build against the sibling repos resolved via the workspace go.work. - - uses: ./.github/actions/checkout-graycode-router + - uses: ./.github/actions/checkout-eyrie with: ref: ${{ github.ref_name }} diff --git a/.gitignore b/.gitignore index ddd214f19..314e07a1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ # Binaries -/graycode +/hawk # Ignore build output in bin/, but track committed source scripts like bin/buf. bin/* !bin/buf -graycode_bin +hawk_bin *.exe *.dll *.so @@ -12,7 +12,7 @@ graycode_bin .claude/ .codegraph/ .commandcode/ -.graycode/ +.hawk/ .harrier/ .openclaude/ .pi/ @@ -45,8 +45,8 @@ go.work.sum # Lefthook-generated git hook wrappers .githooks/ __pycache__/ -graycode_bin +hawk_bin # Logs *.log -graycode-sec105b.log +hawk-sec105b.log diff --git a/.golangci.yml b/.golangci.yml index 7259c9745..9358dd360 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,7 @@ linters: # production code with this set). Deliberately excluded floods: # G104 — unhandled errors, already covered by errcheck # G301/G306 — 0755/0644 perms are intentional for user-visible files - # G304 — graycode reads user-supplied file paths by design (CLI agent) + # G304 — hawk reads user-supplied file paths by design (CLI agent) includes: - G101 # hardcoded credentials - G102 # bind to all interfaces diff --git a/.goreleaser.yml b/.goreleaser.yml index d07766c4c..0afb1d9fb 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,17 +2,17 @@ # Source of truth: .shared-templates/.goreleaser.yml.tmpl # # Placeholders rendered per repo: -# graycode — short repo name (e.g. graycode, harrier, swift) +# hawk — short repo name (e.g. hawk, harrier, swift) # ./ — main package path (e.g. ./ or ./cmd/harrier) # AI coding agent — reads, writes, and runs code in your terminal — short single-line description for brew/nfpms # main — Go package path holding Version vars -# (e.g. main or github.com/GrayCodeAI/graycode-cli/internal/version) +# (e.g. main or github.com/GrayCodeAI/hawk/internal/version) # # Repos with PRO/special features (e.g. swift's macOS notarization, shrike's # nfpms) extend this template with extra sections instead of replacing it. version: 2 -project_name: graycode +project_name: hawk # --------------------------------------------------------------------------- # Pre-build hooks — keep go.mod tidy and verified. @@ -27,9 +27,9 @@ before: # rather than the build host's clock. # --------------------------------------------------------------------------- builds: - - id: graycode - main: ./cmd/graycode - binary: graycode + - id: hawk + main: ./cmd/hawk + binary: hawk env: - CGO_ENABLED=0 goos: @@ -121,7 +121,7 @@ changelog: # --------------------------------------------------------------------------- # Homebrew — publishes a formula to GrayCodeAI/homebrew-tap so users can -# `brew install graycodeai/tap/graycode`. Requires HOMEBREW_TAP_TOKEN or +# `brew install graycodeai/tap/hawk`. Requires HOMEBREW_TAP_TOKEN or # TAP_GITHUB_TOKEN in the release workflow (both already wired there). # --------------------------------------------------------------------------- brews: @@ -130,13 +130,13 @@ brews: name: homebrew-tap token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" directory: Formula - homepage: "https://github.com/GrayCodeAI/graycode-cli" + homepage: "https://github.com/GrayCodeAI/hawk" description: "AI coding agent — reads, writes, and runs code in your terminal" license: "MIT" install: | - bin.install "graycode" + bin.install "hawk" test: | - system "#{bin}/graycode", "--version" + system "#{bin}/hawk", "--version" # --------------------------------------------------------------------------- # Release — auto-detect prereleases (rc/beta tags). Created on the repo @@ -158,9 +158,9 @@ release: prerelease: auto name_template: "v{{ .Version }}" header: | - ## graycode v{{ .Version }} + ## hawk v{{ .Version }} AI coding agent — reads, writes, and runs code in your terminal footer: | - **Full changelog:** https://github.com/GrayCodeAI/graycode-cli/compare/{{ .PreviousTag }}...{{ .Tag }} + **Full changelog:** https://github.com/GrayCodeAI/hawk/compare/{{ .PreviousTag }}...{{ .Tag }} diff --git a/.shared-templates/.goreleaser.yml.tmpl b/.shared-templates/.goreleaser.yml.tmpl index a8b12a439..9edda1bb9 100644 --- a/.shared-templates/.goreleaser.yml.tmpl +++ b/.shared-templates/.goreleaser.yml.tmpl @@ -3,11 +3,11 @@ # # Placeholders rendered per repo (ALL-CAPS, distinct from goreleaser's own # `{{.Version}}`-style Go template fields, which are left as-is below): -# {{NAME}} — short repo name (e.g. graycode, harrier, swift) +# {{NAME}} — short repo name (e.g. hawk, harrier, swift) # {{MAIN_PKG}} — main package path (e.g. ./ or ./cmd/harrier) # {{DESCRIPTION}} — short single-line description for brew/nfpms # {{VERSION_PKG}} — Go package path holding Version vars -# (e.g. main or github.com/GrayCodeAI/graycode-cli/internal/version) +# (e.g. main or github.com/GrayCodeAI/hawk/internal/version) # # Repos with PRO/special features (e.g. swift's macOS notarization, shrike's # nfpms) extend this template with extra sections instead of replacing it. diff --git a/.shared-templates/Makefile.binary.tmpl b/.shared-templates/Makefile.binary.tmpl index 6728120c9..f9403f077 100644 --- a/.shared-templates/Makefile.binary.tmpl +++ b/.shared-templates/Makefile.binary.tmpl @@ -2,13 +2,13 @@ # Source of truth: .shared-templates/Makefile.binary.tmpl at the eco root. # Placeholders rendered per repo: {{NAME}}, {{MAIN_PKG}}. # -# graycode is currently the only Go binary repo in the ecosystem, and its real -# Makefile extends this core with graycode-specific targets (workspace `setup`, +# hawk is currently the only Go binary repo in the ecosystem, and its real +# Makefile extends this core with hawk-specific targets (workspace `setup`, # `compat-test`/`compat-check`, `sync-submodules`/`sync-external`, # `build-all`/`build-static`/`size-check`, and split `*-guard` boundary # checks instead of the single generic `boundaries` below). Treat this file # as the floor every Go binary repo starts from, not a literal copy of -# graycode/Makefile. +# hawk/Makefile. # --------------------------------------------------------------------------- # Project metadata @@ -18,7 +18,7 @@ MAIN_PKG := {{MAIN_PKG}} # --------------------------------------------------------------------------- # Versioning — sourced from VERSION file; falls back to git describe. -# See https://github.com/GrayCodeAI/graycode-cli/blob/main/docs/versioning.md. +# See https://github.com/GrayCodeAI/hawk/blob/main/docs/versioning.md. # --------------------------------------------------------------------------- VERSION ?= $(shell v=$$(cat VERSION 2>/dev/null | head -n1 | tr -d '[:space:]'); if [ -n "$$v" ]; then echo "$$v"; else git describe --tags --always --dirty 2>/dev/null || echo "dev"; fi) COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") @@ -138,5 +138,5 @@ hooks: ## Install git hooks via lefthook (formatting, linting, conventional comm # --------------------------------------------------------------------------- # Repo-specific extensions go below this line — workspace setup, submodule # sync, cross-platform build matrices, compatibility-matrix checks, etc. -# See graycode/Makefile for the real, extended reference implementation. +# See hawk/Makefile for the real, extended reference implementation. # --------------------------------------------------------------------------- diff --git a/.shared-templates/Makefile.library.tmpl b/.shared-templates/Makefile.library.tmpl index f93961e1b..3ed3921a1 100644 --- a/.shared-templates/Makefile.library.tmpl +++ b/.shared-templates/Makefile.library.tmpl @@ -9,7 +9,7 @@ NAME := {{NAME}} # --------------------------------------------------------------------------- # Versioning — sourced from VERSION file; falls back to git describe. -# See https://github.com/GrayCodeAI/graycode-cli/blob/main/VERSIONING.md. +# See https://github.com/GrayCodeAI/hawk/blob/main/VERSIONING.md. # --------------------------------------------------------------------------- VERSION ?= $(shell v=$$(cat VERSION 2>/dev/null | head -n1 | tr -d '[:space:]'); if [ -n "$$v" ]; then echo "$$v"; else git describe --tags --always --dirty 2>/dev/null || echo "dev"; fi) COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") diff --git a/.shared-templates/Makefile.python.tmpl b/.shared-templates/Makefile.python.tmpl index 2dfd5f3c0..76e029d4d 100644 --- a/.shared-templates/Makefile.python.tmpl +++ b/.shared-templates/Makefile.python.tmpl @@ -63,7 +63,7 @@ fmt: ## Format with ruff. vet: ## Type-check with mypy. $(PYTHON) -m mypy src/ -boundary-guard: ## Fail if the SDK references support engines or Graycode private packages. +boundary-guard: ## Fail if the SDK references support engines or Hawk private packages. bash ./scripts/check-consumer-boundaries.sh lint: ## Lint with ruff. diff --git a/.shared-templates/README.md b/.shared-templates/README.md index fffa3875d..1a3617a95 100644 --- a/.shared-templates/README.md +++ b/.shared-templates/README.md @@ -7,17 +7,17 @@ Every graycode-ecosystem repo's `Makefile`, `lefthook.yml`, and # Source of truth: .shared-templates/Makefile.library.tmpl at the eco root. ``` -This directory is that source of truth. It lives here, in `graycode-cli`, because -`graycode-cli` is the one repo every consumer already depends on or references — +This directory is that source of truth. It lives here, in `hawk`, because +`hawk` is the one repo every consumer already depends on or references — there is no separate monorepo at the workspace root to hold it. -**This directory is not built or run by graycode itself.** It is a template +**This directory is not built or run by hawk itself.** It is a template library that other graycode-ecosystem repos copy from and diff against. ## Layout - `Makefile.library.tmpl` — Go library repos (engines, SDKs, foundation repos) -- `Makefile.binary.tmpl` — Go binary repos (currently only `graycode`) +- `Makefile.binary.tmpl` — Go binary repos (currently only `hawk`) - `Makefile.python.tmpl` — Python repos (`robin`) - `lefthook.yml.tmpl` — git hooks config, identical across all Go repos - `.goreleaser.yml.tmpl` — goreleaser config for Go binary repos @@ -28,7 +28,7 @@ library that other graycode-ecosystem repos copy from and diff against. - `workflows/python-release.yml.tmpl` — PyPI publish workflow (Trusted Publishing) - `workflows/compatibility-test.yml.tmpl` — cross-repo compatibility matrix check (see `docs/compatibility.md`) - `scripts/check-ecosystem-boundaries.sh.tmpl` — the import-boundary guard, parameterized per repo role -- `scripts/sync-external.sh` — read-only drift report for `graycode`'s `external/` submodule pins (graycode-specific, not templated elsewhere) +- `scripts/sync-external.sh` — read-only drift report for `hawk`'s `external/` submodule pins (hawk-specific, not templated elsewhere) - `docs/coverage-matrix.md` — per-repo test coverage thresholds enforced in CI, kept in one place so they don't silently drift out of sync with each repo's `go-ci.yml` ## How repos use this @@ -37,9 +37,9 @@ There is currently no rendering tool — repos copy a template, replace the placeholders marked `{{LIKE_THIS}}`, and keep the "Source of truth" header comment pointing back here. When you change a template, the repos that copied it are now stale; update them in the same PR or file a follow-up -per repo. `graycode`'s own `Makefile`/`lefthook.yml`/CI predate this directory +per repo. `hawk`'s own `Makefile`/`lefthook.yml`/CI predate this directory and intentionally diverge in a few binary-specific ways (see -`Makefile.binary.tmpl`, which documents the real deltas against `graycode`'s +`Makefile.binary.tmpl`, which documents the real deltas against `hawk`'s Makefile). ## Boundary rule diff --git a/.shared-templates/docs/coverage-matrix.md b/.shared-templates/docs/coverage-matrix.md index dae904282..445ce6ac5 100644 --- a/.shared-templates/docs/coverage-matrix.md +++ b/.shared-templates/docs/coverage-matrix.md @@ -8,8 +8,8 @@ repo's threshold, update both the CI file and this table in the same PR.** | Repo | Threshold | Mechanism | |---|---|---| -| `graycode-cli` | 65% | inline `bc` check in `ci.yml` | -| `graycode-router` | 60% | inline `bc` check in `ci.yml` | +| `hawk` | 65% | inline `bc` check in `ci.yml` | +| `eyrie` | 60% | inline `bc` check in `ci.yml` | | `graycode-skills` | n/a | no Go/Python test suite (skill/content registry) | ## Why thresholds differ per repo diff --git a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl index 3d16ff814..1d5211bf9 100644 --- a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl +++ b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl @@ -12,29 +12,29 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" # ============================================================================= -# VARIANT 1 — Support engine (graycode-router, harrier, shrike, swift, kestrel, merlin). +# VARIANT 1 — Support engine (eyrie, harrier, shrike, swift, kestrel, merlin). # Engines are peers: they may depend on falcon, -# but never on graycode/internal/* or another engine. +# but never on hawk/internal/* or another engine. # ============================================================================= -FORBIDDEN_GRAYCODE='github\.com/GrayCodeAI/graycode-cli/(internal/|shared/types)' +FORBIDDEN_HAWK='github\.com/GrayCodeAI/hawk/(internal/|shared/types)' FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(harrier|shrike|swift|kestrel|merlin)(/|")' # ^ list every OTHER engine here — never include yourself. exit_code=0 if command -v rg >/dev/null 2>&1; then - violations="$(rg -n "$FORBIDDEN_GRAYCODE" --glob '*.go' . || true)" + violations="$(rg -n "$FORBIDDEN_HAWK" --glob '*.go' . || true)" engine_violations="$(rg -n "$FORBIDDEN_ENGINES" --glob '*.go' . || true)" else - violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_GRAYCODE" . || true)" + violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_HAWK" . || true)" engine_violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_ENGINES" . || true)" fi if [[ -n "${violations}" ]]; then - echo "forbidden Graycode imports found:" + echo "forbidden Hawk imports found:" echo "${violations}" echo - echo "support repos must use local contracts, not graycode/internal or removed graycode/shared/types" + echo "support repos must use local contracts, not hawk/internal or removed hawk/shared/types" exit_code=1 fi @@ -70,19 +70,19 @@ echo "ecosystem boundary guard passed" # echo "forbidden graycode-eco imports found in {{OWN_MODULE}}:" # echo "${violations}" # echo -# echo "{{OWN_MODULE}} is a foundation repo — it must not depend on graycode, engines, or any other GrayCodeAI/* package" +# echo "{{OWN_MODULE}} is a foundation repo — it must not depend on hawk, engines, or any other GrayCodeAI/* package" # exit 1 # fi # # echo "ecosystem boundary guard passed" # ============================================================================= -# VARIANT 3 — SDK / skills consumer (graycode-skills). Consumers may depend on graycode's public surfaces -# only — never on a support engine directly, and never on graycode/internal. +# VARIANT 3 — SDK / skills consumer (graycode-skills). Consumers may depend on hawk's public surfaces +# only — never on a support engine directly, and never on hawk/internal. # ============================================================================= # -# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(graycode-router|harrier|shrike|swift|kestrel|merlin)(/|")' -# FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/graycode-cli/internal' +# FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(eyrie|harrier|shrike|swift|kestrel|merlin)(/|")' +# FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/hawk/internal' # # (same rg/grep + report pattern as Variant 1, naming "SDKs/skills must go -# through graycode's public API, not engines directly" as the violation message) +# through hawk's public API, not engines directly" as the violation message) diff --git a/.shared-templates/scripts/sync-external.sh b/.shared-templates/scripts/sync-external.sh index 55157c939..8f6fc20c6 100755 --- a/.shared-templates/scripts/sync-external.sh +++ b/.shared-templates/scripts/sync-external.sh @@ -5,7 +5,7 @@ # the submodule checkout), this makes no changes — it only reports. # # Typical drift: you commit changes in ../shrike, but forget `make -# sync-submodules` + a commit in graycode to bump the external/tok pin. This +# sync-submodules` + a commit in hawk to bump the external/tok pin. This # script catches that before it becomes a stale-dependency surprise in CI. set -euo pipefail @@ -62,7 +62,7 @@ done < <(git config -f .gitmodules --get-regexp path | awk '{print $2}') if [[ $exit_code -ne 0 ]]; then echo - echo "drift detected — run 'make sync-submodules' in graycode after confirming the sibling repos are what you expect, then commit the updated external/ pins" + echo "drift detected — run 'make sync-submodules' in hawk after confirming the sibling repos are what you expect, then commit the updated external/ pins" fi exit $exit_code diff --git a/.shared-templates/workflows/compatibility-test.yml.tmpl b/.shared-templates/workflows/compatibility-test.yml.tmpl index aaf63c9fa..5182755c5 100644 --- a/.shared-templates/workflows/compatibility-test.yml.tmpl +++ b/.shared-templates/workflows/compatibility-test.yml.tmpl @@ -1,7 +1,7 @@ -# Cross-repo compatibility matrix check, referenced from graycode/docs/compatibility.md. +# Cross-repo compatibility matrix check, referenced from hawk/docs/compatibility.md. # Source of truth: .shared-templates/workflows/compatibility-test.yml.tmpl # -# Lives (and runs) in graycode only — graycode owns testdata/compatibility-matrix.json +# Lives (and runs) in hawk only — hawk owns testdata/compatibility-matrix.json # and the `compat-test`/`compat-check` Make targets that validate it. Other # repos don't need a copy of this workflow; they're read as data, not as a # workflow trigger. diff --git a/.shared-templates/workflows/go-release.yml.tmpl b/.shared-templates/workflows/go-release.yml.tmpl index 91a938684..cd043fec7 100644 --- a/.shared-templates/workflows/go-release.yml.tmpl +++ b/.shared-templates/workflows/go-release.yml.tmpl @@ -23,8 +23,8 @@ jobs: fetch-depth: 0 # goreleaser needs full history for changelog # Only needed if this repo has local workspace dependencies on other - # graycode-eco repos at build time (graycode itself clones graycode-router this way via - # ./.github/actions/checkout-graycode-router). Omit for repos with none. + # graycode-eco repos at build time (hawk itself clones eyrie this way via + # ./.github/actions/checkout-eyrie). Omit for repos with none. - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 diff --git a/.shared-templates/workflows/python-ci.yml.tmpl b/.shared-templates/workflows/python-ci.yml.tmpl index 1e470fcde..8a62864a1 100644 --- a/.shared-templates/workflows/python-ci.yml.tmpl +++ b/.shared-templates/workflows/python-ci.yml.tmpl @@ -2,7 +2,7 @@ # Source of truth: .shared-templates/workflows/python-ci.yml.tmpl # # Placeholders rendered per repo: -# {{PACKAGE}} — importable package name for --cov (e.g. graycode) +# {{PACKAGE}} — importable package name for --cov (e.g. hawk) # {{THRESHOLD}} — minimum coverage percentage, kept in sync with # docs/coverage-matrix.md diff --git a/.trivyignore b/.trivyignore index 297bfc82d..8dca84a98 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,10 +1,10 @@ -# Trivy OS-package ignore list for Graycode Docker images. +# Trivy OS-package ignore list for Hawk Docker images. # # CVE-2026-14456 — OpenSSL DoS via unbounded memory (libcrypto3/libssl3). # Fixed upstream in OpenSSL 3.5.8-r0, but that package is NOT yet published in # the Alpine 3.23 repository (the latest alpine:3.23 still ships 3.5.7-r0, as -# of 2026-08-27). Graycode is a Go binary and does not link libcrypto; this affects -# only the base OS TLS stack and is not reachable from Graycode's runtime. Re-add a +# of 2026-08-27). Hawk is a Go binary and does not link libcrypto; this affects +# only the base OS TLS stack and is not reachable from Hawk's runtime. Re-add a # base-image bump to remove this entry once Alpine 3.23 publishes OpenSSL # 3.5.8-r0. CVE-2026-14456 diff --git a/AGENTS.md b/AGENTS.md index 642f1f941..f29b339ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,15 @@ --- -description: Extending graycode — how to write AGENTS.md files, custom specialists, skills, hooks, MCP servers, and plugins. +description: Extending hawk — how to write AGENTS.md files, custom specialists, skills, hooks, MCP servers, and plugins. globs: "*.go, *.js, *.md, *.json, *.toml, *.yaml, *.yml" alwaysApply: false --- -# Extending graycode +# Extending hawk -graycode is an open-source code intelligence platform. It lives in the `graycode-eco` -workspace alongside the ecosystem repos that power it (`graycode-router`, +hawk is an open-source code intelligence platform. It lives in the `graycode-eco` +workspace alongside the ecosystem repos that power it (`eyrie`, `shrike`, `harrier`, `swift`, `kestrel`, `merlin`). This document describes how to extend -graycode with custom tools, skills, hooks, and integrations. +hawk with custom tools, skills, hooks, and integrations. ## Development workflow @@ -17,7 +17,7 @@ When starting any new work (feature, fix, refactor, chore), always create a feat ## 1. Drop a project `AGENTS.md` -When graycode starts in a directory, it looks for project-level instructions and injects them into the system prompt. The lookup walks from your current working directory **up to the nearest git root** and reads the first matching file at each level — general rules at the repo root, more specific rules in sub-trees. Files are labeled with their directory in the prompt (e.g. `## Project guidelines (services/api/AGENTS.md)`). +When hawk starts in a directory, it looks for project-level instructions and injects them into the system prompt. The lookup walks from your current working directory **up to the nearest git root** and reads the first matching file at each level — general rules at the repo root, more specific rules in sub-trees. Files are labeled with their directory in the prompt (e.g. `## Project guidelines (services/api/AGENTS.md)`). Accepted file names, in priority order at each level: @@ -29,7 +29,7 @@ Accepted file names, in priority order at each level: Matching is **case-insensitive** on the basename, so `AGENTS.md`, `Agents.md`, and `agents.md` resolve to the same file on Windows and macOS. The git-tracked filename in this repo is `AGENTS.md` — keep that on case-sensitive filesystems (Linux, the WSL filesystem, or a CI runner) to match what the loader looks for. -Both files use the same format. YAML frontmatter is optional; the markdown body is loaded as instructions for the agent. graycode reads the file once at session start, so changes take effect on the next launch — not mid-session. +Both files use the same format. YAML frontmatter is optional; the markdown body is loaded as instructions for the agent. hawk reads the file once at session start, so changes take effect on the next launch — not mid-session. ```markdown # Project conventions for @@ -42,26 +42,26 @@ Both files use the same format. YAML frontmatter is optional; the markdown body Tips: -- Keep each file under ~8 KiB. graycode caps the **total** across all matched files at 32 KiB; everything past the cap is dropped. +- Keep each file under ~8 KiB. hawk caps the **total** across all matched files at 32 KiB; everything past the cap is dropped. - Re-state rules in the imperative voice: "Run `make lint`", not "you should consider running the linter". - Don't put secrets, model IDs, or environment-specific paths in `AGENTS.md`. Use config files for those. -- In a monorepo, drop a narrower `AGENTS.md` in each sub-tree (e.g. `services/api/AGENTS.md`). graycode picks those up automatically when you launch from inside the sub-tree. +- In a monorepo, drop a narrower `AGENTS.md` in each sub-tree (e.g. `services/api/AGENTS.md`). hawk picks those up automatically when you launch from inside the sub-tree. - A YAML frontmatter block (`---\n...\n---`) at the top is preserved verbatim in the injected prompt but is not parsed for `globs:` or `alwaysApply:` scoping today — keep the body self-contained. ### Personal guidelines, across every project -For preferences that follow *you*, not a specific repo (tone, tooling habits, workflow), drop a `ZERO.md` in your user config directory: `~/.graycode/ZERO.md` on Linux/macOS, `%AppData%\graycode\ZERO.md` on Windows — the same directory as config files and your personal specialists. Same format and 8 KiB cap as the project files above, and the same case-insensitive basename match. +For preferences that follow *you*, not a specific repo (tone, tooling habits, workflow), drop a `ZERO.md` in your user config directory: `~/.hawk/ZERO.md` on Linux/macOS, `%AppData%\hawk\ZERO.md` on Windows — the same directory as config files and your personal specialists. Same format and 8 KiB cap as the project files above, and the same case-insensitive basename match. This file is injected as its own `## User guidelines` section, before the project's `AGENTS.md`/`ZERO.md`, and is labeled as personal preference in the prompt: project guidelines are the later, more specific instruction and take precedence over it when the two conflict. ## 2. Custom specialists -Specialists are graycode's sub-agents. Three scopes, in priority order: +Specialists are hawk's sub-agents. Three scopes, in priority order: | Scope | Path | Shared? | | --- | --- | --- | -| Built-in | compiled into graycode | yes | -| User | `~/.graycode/specialists/*.md` | no — your machine only | +| Built-in | compiled into hawk | yes | +| User | `~/.hawk/specialists/*.md` | no — your machine only | | Project | `./.zero/specialists/*.md` | yes — the repo team | Project overrides user overrides built-in when names collide. @@ -89,33 +89,33 @@ Reply with one JSON object per finding: `{"file", "line", "severity", "message", CLI management: ```bash -graycode specialist list -graycode specialist show api-reviewer -graycode specialist create api-reviewer \ +hawk specialist list +hawk specialist show api-reviewer +hawk specialist create api-reviewer \ --project \ --description "Reviews API changes" \ --tools read-only,plan \ --prompt "$(cat api-reviewer.md)" -graycode specialist edit api-reviewer --project -graycode specialist delete api-reviewer --project -graycode specialist path # prints the resolved specialists directory +hawk specialist edit api-reviewer --project +hawk specialist delete api-reviewer --project +hawk specialist path # prints the resolved specialists directory ``` ## 3. Skills -graycode ships **no bundled skills** by default. Skills are markdown instruction +hawk ships **no bundled skills** by default. Skills are markdown instruction files that extend agent capabilities, sourced from the separate `GrayCodeAI/graycode-skills` repo and installed on demand: ```bash -graycode skills search # find skills in graycode-skills -graycode skills install [skill-name] # install after user approval -graycode skills list # list installed skills -graycode skills remove +hawk skills search # find skills in graycode-skills +hawk skills install [skill-name] # install after user approval +hawk skills list # list installed skills +hawk skills remove ``` Installed skills live in user or project scope: -- User-scoped: `~/.graycode/skills/` +- User-scoped: `~/.hawk/skills/` - Project-scoped: `./.zero/skills/` or `./skills/` A skill manifest: @@ -144,56 +144,56 @@ Hooks allow custom commands to run at specific lifecycle points: - `sessionEnd` — runs at session teardown ```bash -graycode hook add beforeReview --command "lint-check" -graycode hook remove beforeReview -graycode hook list +hawk hook add beforeReview --command "lint-check" +hawk hook remove beforeReview +hawk hook list ``` ## 5. MCP integration -MCP (Model Context Protocol) servers can expose tools to graycode: +MCP (Model Context Protocol) servers can expose tools to hawk: ```bash -graycode mcp add --name server --url http://localhost:8080 -graycode mcp remove server -graycode mcp list +hawk mcp add --name server --url http://localhost:8080 +hawk mcp remove server +hawk mcp list ``` ## 6. Plugins -Plugins extend graycode with custom tools and capabilities: +Plugins extend hawk with custom tools and capabilities: ```bash -graycode plugin add --name my-plugin --path ./my-plugin -graycode plugin remove my-plugin -graycode plugin list +hawk plugin add --name my-plugin --path ./my-plugin +hawk plugin remove my-plugin +hawk plugin list ``` ## 7. Verification -graycode includes a self-verification system to validate local changes before contributing: +hawk includes a self-verification system to validate local changes before contributing: ```bash -graycode verify -graycode verify --fix +hawk verify +hawk verify --fix ``` ## Development ```bash make lint -graycode verify +hawk verify ``` ### Architecture note: cross-repo contracts -Legacy `graycode/shared/types` has been removed. Cross-repo severity and finding contracts now live in graycode-cli's `internal/contracts` (vendored from the removed `github.com/GrayCodeAI/eagle` module) — extensions and support repos must vendor the needed DTOs instead of Graycode internals until a published contracts module exists. +Legacy `hawk/shared/types` has been removed. Cross-repo severity and finding contracts now live in hawk's `internal/contracts` (vendored from the removed `github.com/GrayCodeAI/eagle` module) — extensions and support repos must vendor the needed DTOs instead of Hawk internals until a published contracts module exists. ### Architecture note: provider ownership Implement provider protocols, adapters, catalog metadata, credential mappings, and -provider contract tests in `../graycode-router` (the graycode-router engine's repo) first. Graycode consumes providers only -through GraycodeRouter's stable engine facade; Graycode changes should be limited to host UX +provider contract tests in `../eyrie` (the eyrie engine's repo) first. Hawk consumes providers only +through Eyrie's stable engine facade; Hawk changes should be limited to host UX and facade integration. Concentrate AI is a pay-as-you-go gateway implemented with its native Responses API (`/v1/responses`) under the `concentrate-payg` deployment. @@ -201,7 +201,7 @@ with its native Responses API (`/v1/responses`) under the ## GitNexus — Code Intelligence -This project is indexed by GitNexus as **graycode** (97743 symbols, 322940 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **hawk** (97743 symbols, 322940 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939). @@ -225,10 +225,10 @@ This project is indexed by GitNexus as **graycode** (97743 symbols, 322940 relat | Resource | Use for | |----------|---------| -| `gitnexus://repo/graycode/context` | Codebase overview, check index freshness | -| `gitnexus://repo/graycode/clusters` | All functional areas | -| `gitnexus://repo/graycode/processes` | All execution flows | -| `gitnexus://repo/graycode/process/{name}` | Step-by-step execution trace | +| `gitnexus://repo/hawk/context` | Codebase overview, check index freshness | +| `gitnexus://repo/hawk/clusters` | All functional areas | +| `gitnexus://repo/hawk/processes` | All execution flows | +| `gitnexus://repo/hawk/process/{name}` | Step-by-step execution trace | ## CLI @@ -245,10 +245,10 @@ This project is indexed by GitNexus as **graycode** (97743 symbols, 322940 relat ### Workspace workflow (sibling repos) -graycode depends on ecosystem repos (`graycode-router`, etc.) as independent sibling repos in the `graycode-eco` workspace. Graycode's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by graycode. Each sibling is its own git repo, versioned and released independently. +hawk depends on ecosystem repos (`eyrie`, etc.) as independent sibling repos in the `graycode-eco` workspace. Hawk's `go.work` lists them as `../`, so local changes in any sibling are automatically picked up by hawk. Each sibling is its own git repo, versioned and released independently. -1. Edit + test in `../` — run its tests, run `make test` in graycode +1. Edit + test in `../` — run its tests, run `make test` in hawk 2. Push from the sibling: `git push origin ` 3. Open a PR in the sibling repo → merge to `main` -4. Ensure graycode's `go.mod` pins a version that resolves to (or is an ancestor of) the sibling's `main` — run `make sync` to verify parity -5. No pointer commits: graycode resolves the sibling via `go.work` for local dev and via the pinned `go.mod` version for standalone/module-mode builds (Docker, released consumers) +4. Ensure hawk's `go.mod` pins a version that resolves to (or is an ancestor of) the sibling's `main` — run `make sync` to verify parity +5. No pointer commits: hawk resolves the sibling via `go.work` for local dev and via the pinned `go.mod` version for standalone/module-mode builds (Docker, released consumers) diff --git a/CHANGELOG.md b/CHANGELOG.md index d479daa96..710267771 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Memory and config failures no longer silently dropped**: stream-loop memory persists (assistant learnings, skills, conversation summaries, insights) and the self-improve lesson store log failures via slog, corrupt lesson stores are reported, and the config panel surfaces failed `provider` setting saves instead of ignoring them. ### Changed +- **Renamed graycode back to hawk**: module `github.com/GrayCodeAI/hawk`, binary `hawk`, `HAWK_*` env vars, `~/.hawk` config, `cmd/hawk`, `internal/hawkerr`, and `npm/hawk*` packages, matching the `GrayCodeAI/hawk` repository name. The router dependency is `github.com/GrayCodeAI/eyrie` again (`Eyrie*` types, `eyrieengine`, `EYRIE_*` env vars, `~/.eyrie`). Hard break with no `~/.graycode` migration. `graycode-skills`, `graycode-platform`, `graycode-cloud` and the `graycode-cloud.graph/v1` schema keep their names. - **Makefile lint pin matches CI**: `make lint`/`lint-fix`/`setup` install `golangci-lint@v2.1.0` (was `@latest`), the same version CI enforces. - **Docs truth and housekeeping**: SECURITY.md/CONTRIBUTING.md now describe the actual Go toolchain (golangci-lint, go vet, govulncheck) instead of the polyglot template's ruff/mypy/pip-audit/pnpm-lock language, CONTRIBUTING documents `make setup`/`boundaries`/`test-10x`/`smoke`, and the planning docs (`SPEC_DRIVEN_PLAN.md`, `SPEC_DRIVEN_PHASE2_PLAN.md`, `internal/engine/REFACTOR_PLAN.md`) moved to `docs/plans/`. diff --git a/CLAUDE.md b/CLAUDE.md index c3396ac33..4bd51dd5d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # GitNexus — Code Intelligence -This project is indexed by GitNexus as **graycode** (97743 symbols, 322940 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **hawk** (97743 symbols, 322940 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939). @@ -25,10 +25,10 @@ This project is indexed by GitNexus as **graycode** (97743 symbols, 322940 relat | Resource | Use for | |----------|---------| -| `gitnexus://repo/graycode/context` | Codebase overview, check index freshness | -| `gitnexus://repo/graycode/clusters` | All functional areas | -| `gitnexus://repo/graycode/processes` | All execution flows | -| `gitnexus://repo/graycode/process/{name}` | Step-by-step execution trace | +| `gitnexus://repo/hawk/context` | Codebase overview, check index freshness | +| `gitnexus://repo/hawk/clusters` | All functional areas | +| `gitnexus://repo/hawk/processes` | All execution flows | +| `gitnexus://repo/hawk/process/{name}` | Step-by-step execution trace | ## CLI diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3bfa3f3f1..bc0e9e3a4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ ## Our pledge -We — the maintainers and contributors of the graycode project — pledge to +We — the maintainers and contributors of the hawk project — pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, @@ -45,7 +45,7 @@ offensive, or harmful. Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported to the maintainers via the contact in `SECURITY.md` or by opening a confidential GitHub Security Advisory at -. All +. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac602c816..5c1db48e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,8 @@ -# Contributing to graycode +# Contributing to hawk Thanks for your interest! This guide covers the conventions used across the GrayCodeAI repositories. The shared standards (versioning, release tooling, repo layout) -are defined in . +are defined in . ## Quick start @@ -139,8 +139,8 @@ Before requesting review: ## Reporting bugs -Open an issue using the bug-report template. Include the `graycode` -version (`graycode --version` for binaries, `graycode.Version` for +Open an issue using the bug-report template. Include the `hawk` +version (`hawk --version` for binaries, `hawk.Version` for libraries — see this repo's `VERSION` file), reproduction steps, expected behaviour, and actual behaviour. diff --git a/Dockerfile b/Dockerfile index f6fc28e94..3df4d1e11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ ENV GOPROXY=file:///build/third_party/modproxy,https://proxy.golang.org,direct # Build-time provenance (passed by .github/workflows/docker.yml or `docker build # --build-arg VERSION=... --build-arg COMMIT=... --build-arg BUILD_DATE=...`). # Default to "dev"/"none"/"unknown" so plain `docker build .` still produces a -# runnable image — matching the cmd/graycode/main.go ldflags fallbacks. +# runnable image — matching the cmd/hawk/main.go ldflags fallbacks. ARG VERSION=dev ARG COMMIT=none ARG BUILD_DATE=unknown @@ -46,20 +46,20 @@ RUN --mount=type=cache,target=/go/pkg/mod \ -X main.Version=${VERSION} \ -X main.Commit=${COMMIT} \ -X main.BuildDate=${BUILD_DATE}" \ - -o graycode ./cmd/graycode + -o hawk ./cmd/hawk -# Runtime stage — Alpine (graycode requires git + bash for workspace operations; distroless excluded) +# Runtime stage — Alpine (hawk requires git + bash for workspace operations; distroless excluded) FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b RUN apk upgrade --no-cache && \ apk add --no-cache ca-certificates git bash tini && \ - adduser -D -u 1000 -h /home/graycode graycode + adduser -D -u 1000 -h /home/hawk hawk -COPY --from=builder /build/graycode /usr/local/bin/graycode +COPY --from=builder /build/hawk /usr/local/bin/hawk COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo -USER graycode +USER hawk WORKDIR /workspace -ENTRYPOINT ["tini", "--", "graycode"] +ENTRYPOINT ["tini", "--", "hawk"] CMD ["--help"] diff --git a/Dockerfile.daemon b/Dockerfile.daemon index e975f10f4..4a8a7a7e2 100644 --- a/Dockerfile.daemon +++ b/Dockerfile.daemon @@ -1,9 +1,9 @@ -# Dockerfile for the Graycode daemon (background HTTP server). +# Dockerfile for the Hawk daemon (background HTTP server). # The binary is identical to the CLI image — this Dockerfile just sets the # daemon as the default entrypoint and exposes the daemon port. # -# Build: docker build -f Dockerfile.daemon -t graycode-daemon . -# Run: docker run -p 4590:4590 -e GRAYCODE_DAEMON_API_KEY=... graycode-daemon +# Build: docker build -f Dockerfile.daemon -t hawk-daemon . +# Run: docker run -p 4590:4590 -e HAWK_DAEMON_API_KEY=... hawk-daemon FROM golang:1.26.6-alpine@sha256:af8d6740070b8906d12eae1c3e3ea0957fb63f492051ea05e354c38ef9fe88df AS builder RUN apk upgrade --no-cache && \ @@ -28,23 +28,23 @@ RUN --mount=type=cache,target=/go/pkg/mod \ -X main.Version=${VERSION} \ -X main.Commit=${COMMIT} \ -X main.BuildDate=${BUILD_DATE}" \ - -o graycode ./cmd/graycode + -o hawk ./cmd/hawk FROM alpine:3.24@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b RUN apk upgrade --no-cache && \ apk add --no-cache ca-certificates git bash curl tini && \ - adduser -D -u 1000 -h /home/graycode graycode + adduser -D -u 1000 -h /home/hawk hawk # Create state directory for daemon logs, API key, and audit log. -RUN mkdir -p /home/graycode/.graycode/state && \ - chown -R graycode:graycode /home/graycode/.graycode +RUN mkdir -p /home/hawk/.hawk/state && \ + chown -R hawk:hawk /home/hawk/.hawk -COPY --from=builder /build/graycode /usr/local/bin/graycode +COPY --from=builder /build/hawk /usr/local/bin/hawk COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo -COPY packaging/systemd/graycode-daemon.service /etc/systemd/system/graycode-daemon.service +COPY packaging/systemd/hawk-daemon.service /etc/systemd/system/hawk-daemon.service -USER graycode +USER hawk WORKDIR /workspace EXPOSE 4590 @@ -53,5 +53,5 @@ EXPOSE 4590 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -ksf https://127.0.0.1:4590/v1/health || curl -sf http://127.0.0.1:4590/v1/health || exit 1 -ENTRYPOINT ["tini", "--", "graycode", "daemon", "start"] +ENTRYPOINT ["tini", "--", "hawk", "daemon", "start"] CMD ["--host", "0.0.0.0", "--port", "4590"] diff --git a/Makefile b/Makefile index 5c6a6272a..68e298d19 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ # Canonical GrayCodeAI Makefile for Go binary repos. # Source of truth: .shared-templates/Makefile.binary.tmpl at the eco root. -# Placeholders rendered per repo: graycode, .. +# Placeholders rendered per repo: hawk, .. # --------------------------------------------------------------------------- # Project metadata # --------------------------------------------------------------------------- -NAME := graycode -MAIN_PKG := ./cmd/graycode +NAME := hawk +MAIN_PKG := ./cmd/hawk # --------------------------------------------------------------------------- # Versioning — sourced from VERSION file; falls back to git describe. -# See https://github.com/GrayCodeAI/graycode-cli/blob/main/docs/versioning.md. +# See https://github.com/GrayCodeAI/hawk/blob/main/docs/versioning.md. # --------------------------------------------------------------------------- VERSION ?= $(shell v=$$(cat VERSION 2>/dev/null | head -n1 | tr -d '[:space:]'); if [ -n "$$v" ]; then echo "$$v"; else git describe --tags --always --dirty 2>/dev/null || echo "dev"; fi) COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") @@ -36,7 +36,7 @@ GORELEASER := $(GOBIN_DIR)/goreleaser # --------------------------------------------------------------------------- # Phony declarations (alphabetical). # --------------------------------------------------------------------------- -.PHONY: all bench boundaries build check-replace ci clean contracts-guard ecosystem-guard graycode-router-client-guard graycode-router-engine-guard manifest-guard peer-guard internal-layers-guard package-boundaries-guard release-parity cover cover-new fmt help install lint lint-fix \ +.PHONY: all bench boundaries build check-replace ci clean contracts-guard ecosystem-guard eyrie-client-guard eyrie-engine-guard manifest-guard peer-guard internal-layers-guard package-boundaries-guard release-parity cover cover-new fmt help install lint lint-fix \ release security setup smoke path sync test test-10x test-live test-new test-race tidy version vet api-docs api-validate workspace check-replace: ## Fail if go.mod has local replace directives (run before tagging) @@ -112,28 +112,28 @@ fmt: ## Format source files (gofumpt + goimports). vet: ## Run go vet. go vet ./... -contracts-guard: ## Fail on any legacy imports of removed graycode/shared/types. +contracts-guard: ## Fail on any legacy imports of removed hawk/shared/types. bash ./scripts/check-shared-types-imports.sh -ecosystem-guard: ## Fail if external ecosystem repos import graycode/internal or removed graycode/shared/types. +ecosystem-guard: ## Fail if external ecosystem repos import hawk/internal or removed hawk/shared/types. bash ./scripts/check-ecosystem-boundaries.sh -graycode-router-client-guard: ## Fail on any production graycode-router/client import. - bash ./scripts/check-graycode-router-client-imports.sh +eyrie-client-guard: ## Fail on any production eyrie/client import. + bash ./scripts/check-eyrie-client-imports.sh -graycode-router-engine-guard: ## Require all production GraycodeRouter imports to use the stable engine facade. - bash ./scripts/check-graycode-router-engine-boundary.sh +eyrie-engine-guard: ## Require all production Eyrie imports to use the stable engine facade. + bash ./scripts/check-eyrie-engine-boundary.sh -peer-guard: ## Fail if support engines import each other instead of depending only on Graycode contracts. +peer-guard: ## Fail if support engines import each other instead of depending only on Hawk contracts. bash ./scripts/check-support-repo-coupling.sh -internal-layers-guard: ## Enforce one-way dependencies across stable Graycode internal layers. +internal-layers-guard: ## Enforce one-way dependencies across stable Hawk internal layers. bash ./scripts/check-internal-layer-imports.sh package-boundaries-guard: ## Enforce AST/package-graph boundaries with file/line diagnostics. bash ./scripts/check-package-boundaries.sh -boundaries: manifest-guard check-replace contracts-guard ecosystem-guard graycode-router-client-guard graycode-router-engine-guard peer-guard internal-layers-guard package-boundaries-guard ## Alias for all boundary guards (matches `make boundaries` in engine repos). +boundaries: manifest-guard check-replace contracts-guard ecosystem-guard eyrie-client-guard eyrie-engine-guard peer-guard internal-layers-guard package-boundaries-guard ## Alias for all boundary guards (matches `make boundaries` in engine repos). release-parity: ## Verify every go.mod ecosystem version resolves to a reachable remote commit. bash ./scripts/check-module-release-parity.sh @@ -161,7 +161,7 @@ ci: tidy fmt vet boundaries lint test-race security api-validate ## Run everythi @echo "All CI checks passed." smoke: ## Quick build + doctor + ecosystem verification. - ./scripts/smoke-graycode.sh + ./scripts/smoke-hawk.sh path: ## Verify developer path (setup, security, milestone tests). ./scripts/verify-developer-path.sh @@ -185,7 +185,7 @@ workspace: ## Regenerate the ecosystem root go.work from ecosystem.yaml. @bash ./scripts/generate-workspace.sh setup: workspace ## Set up local development environment and development tools. - @echo "=== Setting up graycode development environment ===" + @echo "=== Setting up hawk development environment ===" @echo "✓ go.work generated and synced from ecosystem.yaml" @echo "" @echo "=== Environment check ===" @@ -211,7 +211,7 @@ help: ## Show this help. @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' # --------------------------------------------------------------------------- -# Compatibility matrix (graycode-specific extension to the canonical template). +# Compatibility matrix (hawk-specific extension to the canonical template). # Validates compatibility-matrix.json and reports the resolved versions for # a chosen matrix entry. Wired into the compatibility-test workflow. # --------------------------------------------------------------------------- @@ -223,7 +223,7 @@ compat-test: ## Validate testdata/compatibility-matrix.json and report the 'next compat-check: ## Strict validation — non-zero exit if any component lacks a version. @go run ./cmd/compat-test -matrix=next -strict -file=testdata/compatibility-matrix.json -compat-drift: ## Advisory: report pin drift between Graycode and sibling repositories. Never fails. +compat-drift: ## Advisory: report pin drift between Hawk and sibling repositories. Never fails. @go run ./cmd/compat-test -check-external -file=testdata/compatibility-matrix.json .PHONY: hooks sync diff --git a/README.md b/README.md index 2d4ebd6e6..e913769c7 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@

Go License - CI - Release - GoDoc + CI + Release + GoDoc

@@ -25,13 +25,13 @@ --- -## Why graycode +## Why hawk -graycode is an AI-powered coding agent that lives in your terminal. It reads your codebase, writes and edits files, runs tests, and manages git — all through natural language. Unlike IDE-bound tools, graycode works over SSH, in containers, and on any machine with a shell. +hawk is an AI-powered coding agent that lives in your terminal. It reads your codebase, writes and edits files, runs tests, and manages git — all through natural language. Unlike IDE-bound tools, hawk works over SSH, in containers, and on any machine with a shell. -**Developer path:** one machine, keychain credentials, local memory. Run `graycode path` to check readiness. +**Developer path:** one machine, keychain credentials, local memory. Run `hawk path` to check readiness. -- **Model-agnostic** — supports many first-class providers through [graycode-router](https://github.com/GrayCodeAI/graycode-router) (the exact count is dynamic — see `graycode --help`), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama +- **Model-agnostic** — supports many first-class providers through [eyrie](https://github.com/GrayCodeAI/eyrie) (the exact count is dynamic — see `hawk --help`), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama - **Zero CGO** — single static binary, cross-compiled for linux/darwin/windows on amd64/arm64 - **Privacy-first** — your code never leaves your machine except to the LLM API you choose - **Docker-only execution** — agent commands run in an isolated container and @@ -40,62 +40,62 @@ graycode is an AI-powered coding agent that lives in your terminal. It reads you ## Status -**Graycode is in active development.** Contributor source builds are the primary path today while we keep hardening the product in the open. Tagged releases and install assets may exist for validation, but they are not the recommended first path yet. +**Hawk is in active development.** Contributor source builds are the primary path today while we keep hardening the product in the open. Tagged releases and install assets may exist for validation, but they are not the recommended first path yet. -Follow [GrayCode](https://github.com/GrayCodeAI) for progress. When Graycode is ready to try, we will announce it on [graycodeai.com](https://graycodeai.com/changelog). +Follow [GrayCode](https://github.com/GrayCodeAI) for progress. When Hawk is ready to try, we will announce it on [graycodeai.com](https://graycodeai.com/changelog). ## Install (60 seconds) -Pick one — all install the same `graycode` binary (versioned into `~/.graycode/bin`, symlinked as `graycode`): +Pick one — all install the same `hawk` binary (versioned into `~/.hawk/bin`, symlinked as `hawk`): ```bash # 1. Script (any shell, verifies checksum; cosign signature when available) -curl -fsSL https://raw.githubusercontent.com/GrayCodeAI/graycode-cli/main/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/GrayCodeAI/hawk/main/install.sh | sh # 2. Homebrew (macOS / Linuxbrew) — after the next tagged release -brew install graycodeai/tap/graycode +brew install graycodeai/tap/hawk # 3. npm (wraps the same release binaries) -npm install -g @graycodeai/graycode +npm install -g @graycodeai/hawk ``` -If `~/.graycode/bin` is not on your `PATH`, add it to your shell profile. +If `~/.hawk/bin` is not on your `PATH`, add it to your shell profile. Then: ```bash -graycode # interactive REPL (/config on first run: API key + model) -graycode path # verify readiness +hawk # interactive REPL (/config on first run: API key + model) +hawk path # verify readiness ``` ## Quick Start (contributors — from source) ```bash -git clone https://github.com/GrayCodeAI/graycode-cli && cd graycode-cli +git clone https://github.com/GrayCodeAI/hawk && cd hawk make setup # generates go.work referencing sibling support repos in the graycode-eco workspace -go build -o graycode ./cmd/graycode -./graycode +go build -o hawk ./cmd/hawk +./hawk # First run — paste API key in /config (stored in macOS Keychain / Linux keyring) # Verify readiness -./graycode path +./hawk path ``` -**Docker is required before your first run.** Graycode executes agent commands inside a +**Docker is required before your first run.** Hawk executes agent commands inside a container — there is no host-execution fallback (fail-closed). Start the Docker daemon -first, then run `graycode path` (or `graycode doctor`) to see an ordered onboarding +first, then run `hawk path` (or `hawk doctor`) to see an ordered onboarding checklist: daemon running → sandbox image cached → registry reachable → local build. -Graycode automatically uses the versioned public `graycodeai/graycode-sandbox` image; -when it is not local, Graycode pulls it anonymously, and if the registry is unavailable +Hawk automatically uses the versioned public `graycodeai/hawk-sandbox` image; +when it is not local, Hawk pulls it anonymously, and if the registry is unavailable it builds the bundled sandbox image locally through Docker. See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md) for the credential model and -the sandbox checklist. Do not put API keys in shell env or `.env` for graycode. +the sandbox checklist. Do not put API keys in shell env or `.env` for hawk. Optional for contributors: ```bash -go install github.com/GrayCodeAI/graycode-cli/cmd/graycode@latest +go install github.com/GrayCodeAI/hawk/cmd/hawk@latest ``` ## Features @@ -118,31 +118,31 @@ Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea) for a smooth ### Portable Execution Graph -Export the latest or a selected Graycode session as validated graph nodes, edges, +Export the latest or a selected Hawk session as validated graph nodes, edges, and lifecycle events: ```bash -graycode graph export -graycode graph export -graycode graph export --swift-checkpoint abc123def456 -graycode graph export --mission-dir /path/to/mission +hawk graph export +hawk graph export +hawk graph export --swift-checkpoint abc123def456 +hawk graph export --mission-dir /path/to/mission # Explicitly privacy-normalize and sync the graph for a connected cloud project -graycode cloud graph sync -graycode cloud graph sync --mission-dir /path/to/mission +hawk cloud graph sync +hawk cloud graph sync --mission-dir /path/to/mission ``` Cloud commands require an endpoint. There is no default: pass `--endpoint` or -set `GRAYCODE_CLOUD_URL` to your Graycode Cloud worker URL before running -`graycode cloud login`. `https://api.graycodeai.com` is the browser BFF and +set `HAWK_CLOUD_URL` to your Hawk Cloud worker URL before running +`hawk cloud login`. `https://api.graycodeai.com` is the browser BFF and will reject a device token. The export contains metadata and hashes, not prompts, tool arguments/results, policy reasons, verification evidence, or runtime output. Swift remains -available separately as `graycode swift graph export`. Persisted chat sessions +available separately as `hawk swift graph export`. Persisted chat sessions automatically append privacy-safe permission, enabled approval-gate, and `VerifyPlanExecution` summaries for subsequent graph exports. Harrier memory -subgraphs and Graycode code-index chunks actually selected for inference are also +subgraphs and Hawk code-index chunks actually selected for inference are also journaled as metadata-only knowledge nodes and linked to the session. Merlin's observed bridge path similarly journals bounded, metadata-only report/finding quality subgraphs. Kestrel exposes the same observed bridge boundary for @@ -155,7 +155,7 @@ explicitly with the `--mission-dir` variants above. For larger tasks, decompose work into parallel feature branches (power-user / future team workflows): ```bash -graycode mission "Add auth, rate limiting, and logging" +hawk mission "Add auth, rate limiting, and logging" ``` Each sub-agent runs in its own git worktree with full autonomy. @@ -165,14 +165,14 @@ Each sub-agent runs in its own git worktree with full autonomy. Discover and install modular instruction packages for specialized workflows: ```bash -graycode skills search api # Search community registry -graycode skills install go-review # Install from GitHub -graycode skills audit # Security scan installed skills +hawk skills search api # Search community registry +hawk skills install go-review # Install from GitHub +hawk skills audit # Security scan installed skills ``` ### Permission Center -graycode exposes two independent chat command centers — trust tier and the +hawk exposes two independent chat command centers — trust tier and the spec-driven workflow gate — rather than one merged permission mode: ```text @@ -209,7 +209,7 @@ The model is: - `Rules` control explicit allow/deny exceptions. - `Spec` is a separate, independent workflow gate (bare `/spec` opens a picker): starting it walks the model through `Specify → Plan → Tasks`, - writing real files to `.graycode/specs//`, and blocks Write/Edit/Bash + writing real files to `.hawk/specs//`, and blocks Write/Edit/Bash until you approve moving to implementation — at **any** trust tier, including Autonomous. @@ -223,23 +223,23 @@ Connect external tools via [Model Context Protocol](https://modelcontextprotocol ### Watch Mode (AI-comment loop) -`graycode --watch` watches your tree for `AI!` (do it now) and `AI?` (answer my question) comments and acts on them automatically — leave a directive in code, save, and graycode responds. Off by default; enabled via the `--watch` flag. +`hawk --watch` watches your tree for `AI!` (do it now) and `AI?` (answer my question) comments and acts on them automatically — leave a directive in code, save, and hawk responds. Off by default; enabled via the `--watch` flag. ### CI / GitHub Action -A bundled GitHub Action (`.github/actions/graycode`) runs graycode in your pipeline: interactive mode on `@graycode` mentions in issue/PR comments, automation mode on labeled issues/PRs, and skill dispatch when a prompt begins with `/` (e.g. `/code-review`). +A bundled GitHub Action (`.github/actions/hawk`) runs hawk in your pipeline: interactive mode on `@hawk` mentions in issue/PR comments, automation mode on labeled issues/PRs, and skill dispatch when a prompt begins with `/` (e.g. `/code-review`). ### Messaging Gateways (opt-in) -The daemon exposes Telegram, Discord, and Slack gateways so you can chat with graycode from your messaging app. Disabled by default; enabled per-channel via daemon config. +The daemon exposes Telegram, Discord, and Slack gateways so you can chat with hawk from your messaging app. Disabled by default; enabled per-channel via daemon config. ### AST Repo-Map & Codebase Analysis -An AST-based repository map (`internal/context/repomap`) gives the model a structural overview of your code. On first run, graycode can auto-analyze the codebase to seed context (default-off, opt-in). +An AST-based repository map (`internal/context/repomap`) gives the model a structural overview of your code. On first run, hawk can auto-analyze the codebase to seed context (default-off, opt-in). ### Auto-Lint / Auto-Fix Cycle -After edits, graycode can run the matching linter and iterate on fixes (bounded retries) before handing back. Opt-in; preserves existing behavior when disabled. +After edits, hawk can run the matching linter and iterate on fixes (bounded retries) before handing back. Opt-in; preserves existing behavior when disabled. ### Image / Multimodal Context @@ -275,17 +275,17 @@ Features adopted from open-source agent projects. All are off by default unless | Feature | Flag / Command | What it does | |---|---|---| -| Best-of-N fan-out | `graycode exec --fanout N` | Run the same prompt in N isolated worktrees, compare, merge winner | -| Completion notifications | `GRAYCODE_NOTIFY_WEBHOOK_URL` / `GRAYCODE_NOTIFY_TELEGRAM_TOKEN` + `_CHAT_ID` | Webhook or Telegram ping when a run finishes | -| Incremental system-context | `GRAYCODE_INCREMENTAL_CONTEXT=1` | Reconcile dynamic sections instead of rebuilding the prompt | -| Tool-catalog shrink | `GRAYCODE_TOOL_SHRINK=1` | Compress the tool catalog sent on every request | -| Compaction segments | `GRAYCODE_COMPACTION_SEGMENT_DETAIL=verbose\|balanced\|minimal\|none` | Persist verbatim compacted turns to disk | -| Skill curator | `graycode skills curator status/run/pin/unpin/archive` + `GRAYCODE_SKILL_CURATOR=1` | Auto-archive cold agent-created skills (recoverable) | +| Best-of-N fan-out | `hawk exec --fanout N` | Run the same prompt in N isolated worktrees, compare, merge winner | +| Completion notifications | `HAWK_NOTIFY_WEBHOOK_URL` / `HAWK_NOTIFY_TELEGRAM_TOKEN` + `_CHAT_ID` | Webhook or Telegram ping when a run finishes | +| Incremental system-context | `HAWK_INCREMENTAL_CONTEXT=1` | Reconcile dynamic sections instead of rebuilding the prompt | +| Tool-catalog shrink | `HAWK_TOOL_SHRINK=1` | Compress the tool catalog sent on every request | +| Compaction segments | `HAWK_COMPACTION_SEGMENT_DETAIL=verbose\|balanced\|minimal\|none` | Persist verbatim compacted turns to disk | +| Skill curator | `hawk skills curator status/run/pin/unpin/archive` + `HAWK_SKILL_CURATOR=1` | Auto-archive cold agent-created skills (recoverable) | | Structural code match | `CodeMatch` tool | Tree-sitter query search over Go/Python/TS/TSX | -| Composable toolsets | `graycode toolset [name]` + `Toolset` tool | Named tool groups (research, dev, ops, full_stack) | +| Composable toolsets | `hawk toolset [name]` + `Toolset` tool | Named tool groups (research, dev, ops, full_stack) | | App verification | `AppVerify` tool | Boot-smoke check with readiness polling and evidence artifacts | -| Media generation | `GenerateMedia` tool | Image/video generation with local persistence. Backend via `tool.SetMediaEngine`; an OpenAI-compatible client ships in `graycode-router/client` (`ImageClient`), wired by the host (boundary-guarded — graycode routes through the graycode-router facade) | -| Voice transcription | Telegram voice notes + `stt` package | Transcribe Telegram voice/audio into the prompt. Backend via `stt.SetTranscriber`; an OpenAI-compatible client ships in `graycode-router/client` (`AudioClient`), wired by the host | +| Media generation | `GenerateMedia` tool | Image/video generation with local persistence. Backend via `tool.SetMediaEngine`; an OpenAI-compatible client ships in `eyrie/client` (`ImageClient`), wired by the host (boundary-guarded — hawk routes through the eyrie facade) | +| Voice transcription | Telegram voice notes + `stt` package | Transcribe Telegram voice/audio into the prompt. Backend via `stt.SetTranscriber`; an OpenAI-compatible client ships in `eyrie/client` (`AudioClient`), wired by the host | | Git-tree file snapshots | `internal/gitsnapshot` | Content-addressed tree capture/diff/preview/restore | | Turn-boundary rewind | `internal/filestate` | Per-prompt before/after snapshots with durable store | | Continual harness | `internal/intelligence/harness` | Versioned, evidence-backed refinement of supplemental prompts/memories/skills/subagents with rollback | @@ -296,28 +296,28 @@ Features adopted from open-source agent projects. All are off by default unless | X/Twitter search | `SearchX` tool | Live X search by forwarding a query to an xAI endpoint with server-side search; returns a cited summary. Requires `XAI_API_KEY` (or `GROK_API_KEY`) | | Desktop computer-use | `ComputerUse` tool | snapshot/click/type/scroll/press/screenshot via a pluggable `tool.SetComputerBackend` seam (host wires a native macOS accessibility backend) | | Token-cheaper file views | `Read` tool `--minify` | Read-only, comment-stripped, whitespace-dense file view (Go via `go/parser`; other languages string-aware; never touches disk) — fewer tokens per read | -| Classified provider hints | `internal/errhint` | Buckets provider errors (Auth/RateLimit/Connectivity/ModelNotFound/ContextOverflow) into a one-line fixable next step. Wired into TUI error rows (`friendlyErrorMessage`) and `graycode exec` CLI errors | +| Classified provider hints | `internal/errhint` | Buckets provider errors (Auth/RateLimit/Connectivity/ModelNotFound/ContextOverflow) into a one-line fixable next step. Wired into TUI error rows (`friendlyErrorMessage`) and `hawk exec` CLI errors | | Atomic install transactions | `internal/installtxn` | Cross-process staged install/remove with rollback. Wired into skill install (atomic `SKILL.md` publish) | | Stale-lock reclaim | `internal/lockutil` | Race-correct atomic reclaim of O_EXCL lock files with live-restore (ready for O_EXCL lock sites) | -| Test command discovery | `internal/testrunner` | Auto-detect test/verify commands (Go/npm/bun/pnpm/yarn/pytest/cargo) and parse runner output into structured results. Wired into `graycode verify` | +| Test command discovery | `internal/testrunner` | Auto-detect test/verify commands (Go/npm/bun/pnpm/yarn/pytest/cargo) and parse runner output into structured results. Wired into `hawk verify` | | Circuit breaker | `internal/circuitbreaker` | Closed/open/half-open retry-storm protection with cooldown. Wired into auto-compaction (cooldown + half-open auto-retry) | | Smart turn routing | `internal/smartrouting` | Deterministic simple/strong turn classifier with fail-toward-strong safety. Wired into per-turn model selection (`settings.smart_routing`) | | Conversation arc | `internal/conversationarc` | Durable sidecar memory of goals/decisions/milestones/phase with a byte-stable summary. Wired into sessions (loaded on open, saved on close, injected into the system prompt) | | Relevance pruning | `internal/relevanceprune` | Token-budgeted context pruning preserving recent turns/tool calls/errors. Wired into compaction as a `relevance` strategy | | Tool-result clearing | `internal/engine` (`ClearOldToolResults`) | Two-tier context management: at 80% of the context window, stale tool-result content is replaced with `[output cleared]` placeholders (tool_use kept intact) before compacting — a gentler tier below compaction | | Approval pause timing | `internal/permissions` | Approval requests record decision timestamp + human deliberation duration (`DecisionAt`/`PauseDuration`) for approval-latency observability | -| Graceful exhaustion | `internal/engine` (`SynthesisForExhaustion`) | When turn/token/time limits hit, one final tools-disabled LLM call synthesizes a coherent completion (accomplished/remaining/next steps) instead of a bare stop line. Opt-in via `GRAYCODE_GRACEFUL_EXHAUSTION=1` | -| Deterministic replay cache | `internal/replaycache` (`GRAYCODE_REPLAY_CACHE_DIR`) | Disk-persisted SHA-256-keyed cache of completions; identical requests replay stored responses for reproducible regression runs | +| Graceful exhaustion | `internal/engine` (`SynthesisForExhaustion`) | When turn/token/time limits hit, one final tools-disabled LLM call synthesizes a coherent completion (accomplished/remaining/next steps) instead of a bare stop line. Opt-in via `HAWK_GRACEFUL_EXHAUSTION=1` | +| Deterministic replay cache | `internal/replaycache` (`HAWK_REPLAY_CACHE_DIR`) | Disk-persisted SHA-256-keyed cache of completions; identical requests replay stored responses for reproducible regression runs | ## Usage ### Interactive Mode ```bash -graycode # Start REPL -graycode -r abc123 # Resume session -graycode -c # Continue latest session -graycode --provider openai --model gpt-4o # Override provider +hawk # Start REPL +hawk -r abc123 # Resume session +hawk -c # Continue latest session +hawk --provider openai --model gpt-4o # Override provider ``` ### Permission Examples @@ -337,40 +337,40 @@ graycode --provider openai --model gpt-4o # Override provider ### Non-Interactive Mode ```bash -graycode -p "explain this repo" # Print response, exit -graycode -p "fix tests" --allowed-tools "Bash(go test:*) Edit Read" -graycode -p "review this repo" --permission-mode plan --sandbox workspace -graycode exec "refactor auth module" # Full engine, non-interactive -graycode exec --auto full "add error handling" # Full autonomy -graycode exec --worktree "add rate limiting" # Isolated branch -graycode exec --agent reviewer "review last commit" # Custom persona +hawk -p "explain this repo" # Print response, exit +hawk -p "fix tests" --allowed-tools "Bash(go test:*) Edit Read" +hawk -p "review this repo" --permission-mode plan --sandbox workspace +hawk exec "refactor auth module" # Full engine, non-interactive +hawk exec --auto full "add error handling" # Full autonomy +hawk exec --worktree "add rate limiting" # Isolated branch +hawk exec --agent reviewer "review last commit" # Custom persona ``` ### Diagnostics & ecosystem ```bash -graycode path # Developer path readiness (setup + security + sandbox) -graycode doctor # Full health report (graycode-router + harrier + shrike panel) -graycode ecosystem # Ecosystem panel only -graycode harrier # Persistent memory graph -graycode harrier search # Search harrier memories -graycode preflight # Quick ready-to-chat check +hawk path # Developer path readiness (setup + security + sandbox) +hawk doctor # Full health report (eyrie + harrier + shrike panel) +hawk ecosystem # Ecosystem panel only +hawk harrier # Persistent memory graph +hawk harrier search # Search harrier memories +hawk preflight # Quick ready-to-chat check make path # Developer path verification make smoke # Build + quick verification script ``` See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md). -See [docs/ecosystem-message-flow.md](docs/ecosystem-message-flow.md) for how graycode-router, harrier, and shrike connect during a chat session, and [docs/ECOSYSTEM-WIRING.md](docs/ECOSYSTEM-WIRING.md) for the current-to-proposed architecture and repository boundaries. +See [docs/ecosystem-message-flow.md](docs/ecosystem-message-flow.md) for how eyrie, harrier, and shrike connect during a chat session, and [docs/ECOSYSTEM-WIRING.md](docs/ECOSYSTEM-WIRING.md) for the current-to-proposed architecture and repository boundaries. In the TUI: `/path`, `/ecosystem`, `/harrier`, `/harrier search `, `/memory` (AGENTS.md). ### Daemon Mode ```bash -graycode daemon start # Background HTTP server on port 4590 -graycode daemon status # Check if running -graycode daemon stop # Graceful shutdown +hawk daemon start # Background HTTP server on port 4590 +hawk daemon status # Check if running +hawk daemon stop # Graceful shutdown ``` Endpoints: `GET /v1/health`, `GET /v1/ready` (dependency-aware readiness), `POST /v1/chat` (JSON or SSE streaming) @@ -378,15 +378,15 @@ Endpoints: `GET /v1/health`, `GET /v1/ready` (dependency-aware readiness), `POST ### Mission Mode ```bash -graycode mission "Add auth, rate limiting, and logging" -graycode mission --workers 6 "Refactor into microservices" -graycode mission --dry-run "What would this decompose into?" -graycode mission --from-tasks # Execute validated dependency waves +hawk mission "Add auth, rate limiting, and logging" +hawk mission --workers 6 "Refactor into microservices" +hawk mission --dry-run "What would this decompose into?" +hawk mission --from-tasks # Execute validated dependency waves ``` ## Providers -graycode works with any LLM provider. **Developer path:** paste keys in `/config` (stored in OS keychain) — not shell env or `.env`. Use `graycode credentials status` to verify. +hawk works with any LLM provider. **Developer path:** paste keys in `/config` (stored in OS keychain) — not shell env or `.env`. Use `hawk credentials status` to verify. | Provider | ID | Key (via `/config`) | |---|---|---| @@ -404,20 +404,20 @@ graycode works with any LLM provider. **Developer path:** paste keys in `/config | Xiaomi (MiMo) Token Plan | `xiaomi_mimo_token_plan` | `XIAOMI_MIMO_TOKEN_PLAN_API_KEY` (pick region in `/config`) | | Ollama (local) | `ollama` | `OLLAMA_BASE_URL` (no API key) | -Provider routing, model resolution, and retries are handled by [graycode-router](https://github.com/GrayCodeAI/graycode-router). -For deployment-aware routing, set `"deployment_routing": true` in `.graycode/settings.json` -or export `GRAYCODE_DEPLOYMENT_ROUTING=true`. Graycode will route canonical model IDs through -GraycodeRouter's deployment catalog, so new models can be exposed by refreshing the catalog -instead of changing Graycode. In chat, run `/refresh-model-catalog` to fetch the latest -deployment-aware catalog into `~/.graycode-router/model_catalog.json`. +Provider routing, model resolution, and retries are handled by [eyrie](https://github.com/GrayCodeAI/eyrie). +For deployment-aware routing, set `"deployment_routing": true` in `.hawk/settings.json` +or export `HAWK_DEPLOYMENT_ROUTING=true`. Hawk will route canonical model IDs through +Eyrie's deployment catalog, so new models can be exposed by refreshing the catalog +instead of changing Hawk. In chat, run `/refresh-model-catalog` to fetch the latest +deployment-aware catalog into `~/.eyrie/model_catalog.json`. ## Architecture -graycode is built in Go with a modular, layered architecture: +hawk is built in Go with a modular, layered architecture: ``` -graycode/ -├── bin/ # Built binaries (graycode, graycode_bin) +hawk/ +├── bin/ # Built binaries (hawk, hawk_bin) ├── cmd/ # CLI entry point (Cobra + Bubble Tea TUI) ├── internal/ │ ├── engine/ # Agent loop, compaction, self-improvement @@ -445,23 +445,23 @@ graycode/ Ecosystem sibling repos (independent Git repos in the `graycode-eco` parent folder): -├── graycode-router/ # LLM provider runtime +├── eyrie/ # LLM provider runtime ``` ### Ecosystem -graycode is the main CLI/product and integrates these GrayCodeAI repositories in +hawk is the main CLI/product and integrates these GrayCodeAI repositories in three runtime layers plus optional tooling/platform services: -- **Primary product:** **graycode** is the only end-user product surface in this ecosystem. -- **Support engines mounted by Graycode:** **graycode-router**, **harrier**, **shrike**, **swift**, **kestrel**, **merlin**. Graycode imports or shells into these engines behind its own command surface. +- **Primary product:** **hawk** is the only end-user product surface in this ecosystem. +- **Support engines mounted by Hawk:** **eyrie**, **harrier**, **shrike**, **swift**, **kestrel**, **merlin**. Hawk imports or shells into these engines behind its own command surface. - **Shared foundations:** **falcon** provides shared MCP server scaffolding. -- **API consumers/extensions:** **graycode-skills** provides Graycode skills - installed on demand (`graycode skills install`). +- **API consumers/extensions:** **graycode-skills** provides Hawk skills + installed on demand (`hawk skills install`). - **Tooling/platform:** **owl** visualizes the generated ecosystem graph; - **graycode-platform** contains the optional web/BFF/Graycode Cloud plane and is - outside the Graycode Go runtime graph. + **graycode-platform** contains the optional web/BFF/Hawk Cloud plane and is + outside the Hawk Go runtime graph. Local development uses: @@ -471,7 +471,7 @@ Local development uses: Cross-repo contracts now live in `internal/contracts` (vendored from the removed `github.com/GrayCodeAI/eagle` module) so support repos do not depend -on Graycode internals. The old `graycode/shared/types` path has been removed; +on Hawk internals. The old `hawk/shared/types` path has been removed; external consumers should vendor the needed DTOs from `internal/contracts` until a published contracts module exists. @@ -490,10 +490,10 @@ You may keep a **personal** parent **`go.work`** that lists alternate clones on | Component | Repository | Purpose | |---|---|---| -| **graycode** | This repo | AI coding agent | -| **graycode-router** | [GrayCodeAI/graycode-router](https://github.com/GrayCodeAI/graycode-router) | LLM provider runtime | +| **hawk** | This repo | AI coding agent | +| **eyrie** | [GrayCodeAI/eyrie](https://github.com/GrayCodeAI/eyrie) | LLM provider runtime | | **graycode-skills** | [GrayCodeAI/graycode-skills](https://github.com/GrayCodeAI/graycode-skills) | Community skill registry | -| **graycode-platform** | [GrayCodeAI/graycode-platform](https://github.com/GrayCodeAI/graycode-platform) | Web, BFF, and Graycode Cloud | +| **graycode-platform** | [GrayCodeAI/graycode-platform](https://github.com/GrayCodeAI/graycode-platform) | Web, BFF, and Hawk Cloud | `ecosystem.yaml` is the canonical inventory of repositories cloned as siblings in this local workspace; tooling reads it rather than carrying its @@ -501,7 +501,7 @@ own repo-name list. `harrier`, `shrike`, `swift`, `kestrel`, `merlin` and `falcon` above are consumed as pinned `go.mod` module dependencies rather than local workspace clones, so they are not listed there. -For the consolidated repo map and the current-vs-proposed architecture diagrams, see [docs/architecture/graycode-current-vs-proposed.md](docs/architecture/graycode-current-vs-proposed.md). +For the consolidated repo map and the current-vs-proposed architecture diagrams, see [docs/architecture/hawk-current-vs-proposed.md](docs/architecture/hawk-current-vs-proposed.md). For execution-graph ownership, automatic capture seams, export/sync commands, and the Swift correlation contract, see [docs/architecture/execution-graph.md](docs/architecture/execution-graph.md). @@ -515,7 +515,7 @@ and the Swift correlation contract, see ### Build & Test ```bash -go build ./cmd/graycode # Build binary +go build ./cmd/hawk # Build binary go test -race ./... # Run all tests with race detector make ci # Run full CI suite (lint, test, security) make cover # Generate coverage report @@ -539,7 +539,7 @@ Headline numbers (Go 1.26.6, AMD EPYC 7543P, `go test -bench -benchmem -count=3` ### Project Structure -graycode follows Go conventions: `cmd/` for entry points, `internal/` for private code, tests alongside source files. See [docs/architecture.md](docs/architecture.md) for details. +hawk follows Go conventions: `cmd/` for entry points, `internal/` for private code, tests alongside source files. See [docs/architecture.md](docs/architecture.md) for details. ## Contributing diff --git a/SECURITY.md b/SECURITY.md index cc0835aa0..7fecadd6b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,4 @@ -# Security Policy — graycode +# Security Policy — hawk ## Supported versions @@ -7,15 +7,15 @@ minor versions once `1.x` ships. Older versions receive critical-severity fixes only on a best-effort basis. The current canonical version is the contents of the [`VERSION`](./VERSION) -file at the repo root. See [`docs/versioning.md`](https://github.com/GrayCodeAI/graycode-cli/blob/main/docs/versioning.md) +file at the repo root. See [`docs/versioning.md`](https://github.com/GrayCodeAI/hawk/blob/main/docs/versioning.md) for the eco-wide versioning scheme. ## Reporting a vulnerability **Do not open a public GitHub issue for security vulnerabilities.** Instead: -1. Open a private [GitHub Security Advisory](https://github.com/GrayCodeAI/graycode-cli/security/advisories/new), **or** -2. Email `security@graycode.ai` with the details below. +1. Open a private [GitHub Security Advisory](https://github.com/GrayCodeAI/hawk/security/advisories/new), **or** +2. Email `security@hawk.ai` with the details below. Include in your report: @@ -63,28 +63,28 @@ This policy covers the code in this repository and the release artefacts published from it. It does not cover: - Third-party dependencies (report to upstream). -- LLM provider services that graycode integrates with (report to the +- LLM provider services that hawk integrates with (report to the provider). - Local filesystem misuse where an attacker already has shell access (out of threat model). -For graycode-specific threat-model notes, see the README and any docs in +For hawk-specific threat-model notes, see the README and any docs in this repo. ## Config security model ### Clone-and-load attack defense -Project-level `.graycode/settings.json` can be committed to a git repository. +Project-level `.hawk/settings.json` can be committed to a git repository. An attacker who controls a repository could define MCP servers that execute -arbitrary commands when a developer clones and runs graycode in that directory. +arbitrary commands when a developer clones and runs hawk in that directory. **Mitigation:** Project-level MCP servers are stripped from project config (`projectSafeSettings` in `internal/config/settings.go`) and project hooks/MCP/plugins/LSP additionally require folder trust: the project root -must be trusted via `graycode trust add` (`AllowProjectAutomation` in +must be trusted via `hawk trust add` (`AllowProjectAutomation` in `internal/trust/store.go`). There is no `--allow-project-mcp` flag. -Global MCP servers (from `~/.graycode/settings.json`) are always loaded. +Global MCP servers (from `~/.hawk/settings.json`) are always loaded. ### Security-sensitive fields @@ -107,8 +107,8 @@ Highest priority first (`LoadSettings` / `LoadSettingsWithOverride` in 1. CLI `--settings` JSON override 2. Per-command CLI flags (e.g., `--model`, `--provider`) 3. Environment variables (only where explicitly read; there is no global env layer) -4. Project `.graycode/settings.json` (repository-safe subset only — see above) -5. Global `~/.graycode/settings.json` +4. Project `.hawk/settings.json` (repository-safe subset only — see above) +5. Global `~/.hawk/settings.json` 6. Built-in defaults (lowest priority) Project-level config CANNOT escalate permissions beyond what global config @@ -118,7 +118,7 @@ implements field-by-field merging with explicit precedence rules. ### Credential storage API keys and secrets are stored in the OS secret store (macOS Keychain, -Linux secret service, Windows Credential Manager) via the `graycode-router/credentials` +Linux secret service, Windows Credential Manager) via the `eyrie/credentials` package. They are never written to `settings.json`, `.env`, or any file in the repository. The `/config` command manages credential storage interactively. diff --git a/api/openapi.yaml b/api/openapi.yaml index 86a4ac579..e52930c47 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1,19 +1,19 @@ openapi: "3.1.0" info: - title: Graycode Daemon API + title: Hawk Daemon API description: | - HTTP API served by the graycode daemon on port 4590. + HTTP API served by the hawk daemon on port 4590. Used by sparrow, robin, and external integrations. - The daemon must be running (`graycode daemon start`) before calling these endpoints. + The daemon must be running (`hawk daemon start`) before calling these endpoints. This spec is kept in lockstep with the registered routes in internal/daemon; the daemon test suite fails if they drift. version: "0.1.0" license: name: MIT - url: https://github.com/GrayCodeAI/graycode-cli/blob/main/LICENSE + url: https://github.com/GrayCodeAI/hawk/blob/main/LICENSE contact: - url: https://github.com/GrayCodeAI/graycode-cli + url: https://github.com/GrayCodeAI/hawk servers: - url: http://127.0.0.1:4590 @@ -60,7 +60,7 @@ components: description: Existing directory associated with session metadata. The daemon executes tools from its startup working directory; it never performs a process-wide chdir per request. agent: type: string - description: Named Graycode agent persona to apply. Continuations inherit the persisted persona when omitted. + description: Named Hawk agent persona to apply. Continuations inherit the persisted persona when omitted. ChatResponse: type: object @@ -254,7 +254,7 @@ components: properties: schema_version: type: string - enum: [graycode.graph/v1] + enum: [hawk.graph/v1] generated_at: type: string format: date-time @@ -457,7 +457,7 @@ components: properties: status: type: string - description: Output of `graycode review status` + description: Output of `hawk review status` Error: type: object @@ -565,7 +565,7 @@ paths: tags: [system] summary: Readiness probe description: | - Returns 200 only when a session factory is configured and GraycodeRouter's local + Returns 200 only when a session factory is configured and Eyrie's local preflight confirms provider state, catalog, credentials, and model selection are ready. Returns 503 with the failed dependency otherwise. security: [] @@ -600,7 +600,7 @@ paths: Omit `session_id` to create and durably persist a new session. Supply a previously returned `session_id` to load its transcript, append a turn, and update that same durable session. The returned JSON field and the - `X-Graycode-Session-ID` header identify a retrievable `/v1/sessions/{id}`. + `X-Hawk-Session-ID` header identify a retrievable `/v1/sessions/{id}`. requestBody: required: true content: @@ -611,7 +611,7 @@ paths: "200": description: Agent response (or SSE stream) headers: - X-Graycode-Session-ID: + X-Hawk-Session-ID: description: Durable session ID created or continued by this request. schema: type: string @@ -873,10 +873,10 @@ paths: tags: [graphs] summary: Project a persisted session as a portable execution graph description: | - Returns Graycode's privacy-safe, read-only `graycode.graph/v1` projection. + Returns Hawk's privacy-safe, read-only `hawk.graph/v1` projection. Prompt text, tool arguments, tool output, and verification details are excluded. Explicit Swift checkpoint IDs are additive to authoritative - Swift session correlation performed by Graycode. + Swift session correlation performed by Hawk. parameters: - name: id in: path diff --git a/cmd/acp.go b/cmd/acp.go index 16e882203..f0cc3f3c2 100644 --- a/cmd/acp.go +++ b/cmd/acp.go @@ -7,19 +7,19 @@ import ( "path/filepath" "syscall" - "github.com/GrayCodeAI/graycode-cli/internal/acp" - "github.com/GrayCodeAI/graycode-cli/internal/attachment" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/acp" + "github.com/GrayCodeAI/hawk/internal/attachment" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) var acpCmd = &cobra.Command{ Use: "acp", - Short: "Run graycode as an Agent Client Protocol (ACP) server", - Long: "Run graycode as an ACP server over stdio (JSON-RPC 2.0) so editors such as " + + Short: "Run hawk as an Agent Client Protocol (ACP) server", + Long: "Run hawk as an ACP server over stdio (JSON-RPC 2.0) so editors such as " + "Zed can drive it. Tool-permission prompts are routed back to the client " + "via session/request_permission.", RunE: runACP, @@ -30,8 +30,8 @@ func init() { } func runACP(cmd *cobra.Command, _ []string) error { - settings := graycodeconfig.LoadSettings() - newSession := newConfiguredGraycodeSessionFactory(settings, logger.New(io.Discard, logger.Error)) + settings := hawkconfig.LoadSettings() + newSession := newConfiguredHawkSessionFactory(settings, logger.New(io.Discard, logger.Error)) factory := func() (*engine.Session, error) { systemPrompt, err := buildSystemPrompt() diff --git a/cmd/agent.go b/cmd/agent.go index 0497e189a..2f0dab515 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -6,8 +6,8 @@ import ( "os" "path/filepath" - "github.com/GrayCodeAI/graycode-cli/internal/multiagent/agents" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/multiagent/agents" + "github.com/GrayCodeAI/hawk/internal/theme" "github.com/spf13/cobra" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -16,7 +16,7 @@ import ( var agentCmd = &cobra.Command{ Use: "agent", Short: "Manage custom agent personas", - Long: "Create, list, and manage custom agent personas stored in Graycode user state.", + Long: "Create, list, and manage custom agent personas stored in Hawk user state.", } var agentListJSON bool @@ -81,7 +81,7 @@ func runAgentList(cmd *cobra.Command, _ []string) error { return nil } if len(all) == 0 { - fmt.Printf("%s\n", auditTint("No agents found. Create one with: graycode agent create ", textMuted)) + fmt.Printf("%s\n", auditTint("No agents found. Create one with: hawk agent create ", textMuted)) fmt.Printf("%s\n", auditTint("Agent directory: "+agents.DefaultDir(), textPrimary)) return nil } @@ -164,7 +164,7 @@ func runAgentShow(_ *cobra.Command, args []string) error { } fmt.Printf("%s %s\n", auditTint("Model:", textMuted), auditTint(model, textPrimary)) fmt.Printf("%s %s\n", auditTint("File:", textMuted), auditTint(a.FilePath, textPrimary)) - fmt.Printf("\n%s\n%s\n", auditTint("--- Prompt ---", graycodeColor), a.Prompt) + fmt.Printf("\n%s\n%s\n", auditTint("--- Prompt ---", hawkColor), a.Prompt) return nil } diff --git a/cmd/agent_grid.go b/cmd/agent_grid.go index d5fddb4a0..30cacfc15 100644 --- a/cmd/agent_grid.go +++ b/cmd/agent_grid.go @@ -9,7 +9,7 @@ import ( "charm.land/bubbles/v2/viewport" tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var ( @@ -17,7 +17,7 @@ var ( // can read agent state at a glance. Active matches Talon Gold, // done matches the success palette, fail matches error, idle // matches disabled. - agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(graycodeColor).Padding(0, 1) + agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(hawkColor).Padding(0, 1) agentDoneStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(doneGreen).Padding(0, 1) agentFailStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(errorCoral).Padding(0, 1) agentIdleStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(textDisabled).Padding(0, 1) diff --git a/cmd/agent_grid_test.go b/cmd/agent_grid_test.go index 699822cec..8ece3017c 100644 --- a/cmd/agent_grid_test.go +++ b/cmd/agent_grid_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestAgentStateString(t *testing.T) { diff --git a/cmd/agent_test.go b/cmd/agent_test.go index 3a4989078..ef4bad437 100644 --- a/cmd/agent_test.go +++ b/cmd/agent_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/multiagent/agents" + "github.com/GrayCodeAI/hawk/internal/multiagent/agents" ) // sampleAgentMarkdown returns a minimal valid agent definition. @@ -17,7 +17,7 @@ func sampleAgentMarkdown(name, description, model string) string { func TestAgentList_JSON_Empty(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(dir, "state")) if err := os.MkdirAll(filepath.Join(dir, "state", "agents"), 0o755); err != nil { t.Fatal(err) } @@ -49,7 +49,7 @@ func TestAgentList_JSON_Empty(t *testing.T) { func TestAgentList_JSON(t *testing.T) { dir := t.TempDir() stateDir := filepath.Join(dir, "state") - t.Setenv("GRAYCODE_STATE_DIR", stateDir) + t.Setenv("HAWK_STATE_DIR", stateDir) agentDir := filepath.Join(stateDir, "agents") if err := os.MkdirAll(agentDir, 0o755); err != nil { t.Fatal(err) diff --git a/cmd/ai_comments.go b/cmd/ai_comments.go index 9cf661f3a..617206c97 100644 --- a/cmd/ai_comments.go +++ b/cmd/ai_comments.go @@ -9,7 +9,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/fsutil" + "github.com/GrayCodeAI/hawk/internal/fsutil" ) // AIDirective represents a found AI comment directive in a source file. diff --git a/cmd/audit.go b/cmd/audit.go index 34bc9f486..e400c856d 100644 --- a/cmd/audit.go +++ b/cmd/audit.go @@ -11,8 +11,8 @@ import ( "time" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/hooks/audit" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/hooks/audit" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -31,7 +31,7 @@ var auditCmd = &cobra.Command{ redundant cd commands, unnecessary cat/head usage, long sleep loops, and other patterns that waste tokens and wall-clock time. -Reports what graycode would have caught with current policies enabled, +Reports what hawk would have caught with current policies enabled, plus audit-only detectors that identify optimization opportunities.`, RunE: runAudit, } @@ -213,9 +213,9 @@ func discoverSessions(days int, projectFilter string) ([]SessionInfo, error) { cutoff := time.Now().AddDate(0, 0, -days) var sessions []SessionInfo - // Scan graycode sessions directory - graycodeDir := storage.SessionsDir() - entries, err := os.ReadDir(graycodeDir) + // Scan hawk sessions directory + hawkDir := storage.SessionsDir() + entries, err := os.ReadDir(hawkDir) if err != nil && !os.IsNotExist(err) { return nil, err } @@ -224,7 +224,7 @@ func discoverSessions(days int, projectFilter string) ([]SessionInfo, error) { if e.IsDir() || !strings.HasSuffix(e.Name(), ".jsonl") { continue } - path := filepath.Join(graycodeDir, e.Name()) + path := filepath.Join(hawkDir, e.Name()) info, err := os.Stat(path) if err != nil { continue @@ -311,7 +311,7 @@ func printAuditText(cmd *cobra.Command, result AuditResult) { _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, "═══════════════════════════════════════════════════════════════\n") - _, _ = fmt.Fprintf(w, " %s\n", auditTint("Graycode Audit Report", graycodeColor)) + _, _ = fmt.Fprintf(w, " %s\n", auditTint("Hawk Audit Report", hawkColor)) _, _ = fmt.Fprintf(w, "═══════════════════════════════════════════════════════════════\n") _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, " %s %d sessions (last %d days)\n", diff --git a/cmd/autoinit.go b/cmd/autoinit.go index 29e826e9d..ddff08eb1 100644 --- a/cmd/autoinit.go +++ b/cmd/autoinit.go @@ -8,8 +8,8 @@ import ( "path/filepath" "time" - "github.com/GrayCodeAI/graycode-cli/internal/autoinit" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" + "github.com/GrayCodeAI/hawk/internal/autoinit" + "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" ) // autoInitContextFile is the project-level context file the auto-init runner @@ -22,9 +22,9 @@ const autoInitContextFile = "AGENTS.md" // project. It is intentionally additive and safe-by-default: // // - It runs in a background goroutine so it never blocks chat startup. -// - It is a no-op unless the project has NO context file (AGENTS.md / GRAYCODE.md +// - It is a no-op unless the project has NO context file (AGENTS.md / HAWK.md // / CLAUDE.md / CONTEXT.md), no auto-init marker exists yet, and the -// GRAYCODE_DISABLE_AUTO_INIT kill switch is unset — all enforced by +// HAWK_DISABLE_AUTO_INIT kill switch is unset — all enforced by // autoinit.MaybeRun. // - Any failure (analysis error, write error) is swallowed; startup proceeds. // @@ -50,7 +50,7 @@ func maybeAutoInit(ctx context.Context) { // autoInitRunner drives the existing codebase-analysis machinery (the same // hierarchical repomap summary that powers init/repomap) and writes a starter // AGENTS.md context file for the project. It mirrors the intent of the -// init-deep skill / `graycode init` without coupling to the agentic flow: a cheap, +// init-deep skill / `hawk init` without coupling to the agentic flow: a cheap, // deterministic, dependency-free pass that gives the project a baseline context // file. The function is only ever invoked by autoinit.MaybeRun, which has // already confirmed the project has no context file. @@ -103,8 +103,8 @@ func autoInitContextContent(projectName, summary string) string { } return fmt.Sprintf(`# %s -> Auto-generated by graycode on first run. Edit freely or delete to regenerate. -> Set GRAYCODE_DISABLE_AUTO_INIT=1 to opt out of automatic context generation. +> Auto-generated by hawk on first run. Edit freely or delete to regenerate. +> Set HAWK_DISABLE_AUTO_INIT=1 to opt out of automatic context generation. ## Package map diff --git a/cmd/autoinit_test.go b/cmd/autoinit_test.go index ff7f35e72..12bb09776 100644 --- a/cmd/autoinit_test.go +++ b/cmd/autoinit_test.go @@ -6,14 +6,14 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/autoinit" + "github.com/GrayCodeAI/hawk/internal/autoinit" ) // TestAutoInitRunner_WritesContextFileOnce verifies the cmd-layer runner // produces a recognized context file and that MaybeRun invokes it exactly once // for a fresh project, then gates on the marker thereafter. func TestAutoInitRunner_WritesContextFileOnce(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) root := t.TempDir() // A trivial Go file so BuildHierarchy has something to summarize. if err := os.WriteFile(filepath.Join(root, "main.go"), []byte("package main\n\nfunc Foo() {}\n"), 0o644); err != nil { diff --git a/cmd/autonomy_picker.go b/cmd/autonomy_picker.go index 17647c7d8..daf529115 100644 --- a/cmd/autonomy_picker.go +++ b/cmd/autonomy_picker.go @@ -6,7 +6,7 @@ import ( "charm.land/bubbles/v2/textinput" tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/autonomy_picker_test.go b/cmd/autonomy_picker_test.go index fbe6ed078..fc06a75c2 100644 --- a/cmd/autonomy_picker_test.go +++ b/cmd/autonomy_picker_test.go @@ -4,7 +4,7 @@ import ( "testing" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/autonomy_tiers.go b/cmd/autonomy_tiers.go index de0d87827..ed2a9cce0 100644 --- a/cmd/autonomy_tiers.go +++ b/cmd/autonomy_tiers.go @@ -6,7 +6,7 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // Five container autonomy tiers (Scout → Builder → Operator → Autonomous → Always Ask). diff --git a/cmd/autonomy_tiers_copy_test.go b/cmd/autonomy_tiers_copy_test.go index 135c9bf2f..045f63a56 100644 --- a/cmd/autonomy_tiers_copy_test.go +++ b/cmd/autonomy_tiers_copy_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestAutonomyTierDescriptions_PlainLanguage(t *testing.T) { diff --git a/cmd/autonomy_tiers_test.go b/cmd/autonomy_tiers_test.go index 7ce1eb03c..f129b9e3b 100644 --- a/cmd/autonomy_tiers_test.go +++ b/cmd/autonomy_tiers_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestAutonomyTierNames(t *testing.T) { diff --git a/cmd/bg_sessions.go b/cmd/bg_sessions.go index 878687d7f..6859cb538 100644 --- a/cmd/bg_sessions.go +++ b/cmd/bg_sessions.go @@ -11,12 +11,12 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) // ───────────────────────────────────────────────────────────────────────────── -// Background Sessions — run graycode sessions in the background and manage them. +// Background Sessions — run hawk sessions in the background and manage them. // ───────────────────────────────────────────────────────────────────────────── func bgSessionsDir() string { @@ -119,15 +119,15 @@ func KillBGSession(id string) error { return SaveBGSession(info) } -// StartBGSession launches graycode in background mode. +// StartBGSession launches hawk in background mode. func StartBGSession(prompt string, args []string) (*BGSessionInfo, error) { id := genID() cwd, _ := os.Getwd() logFile := filepath.Join(bgSessionsDir(), id+".log") - // Build command: graycode --print with all inherited flags + // Build command: hawk --print with all inherited flags cmdArgs := append([]string{"--print", "--session-id", id, prompt}, args...) - cmd := exec.CommandContext(context.Background(), "graycode", cmdArgs...) // #nosec G204 -- fixed command 'graycode' relaunching self with internal flags + cmd := exec.CommandContext(context.Background(), "hawk", cmdArgs...) // #nosec G204 -- fixed command 'hawk' relaunching self with internal flags cmd.Dir = cwd // 0600: the log captures full session output (private user state, matching @@ -207,11 +207,11 @@ func FormatBGSessions(sessions []*BGSessionInfo) string { var bgCmd = &cobra.Command{ Use: "bg [prompt]", Short: "Run a session in the background", - Long: `Start graycode in the background and continue working in your terminal. + Long: `Start hawk in the background and continue working in your terminal. Examples: - graycode bg "Refactor the auth module" - graycode bg "Run tests and fix failures"`, + hawk bg "Refactor the auth module" + hawk bg "Run tests and fix failures"`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return fmt.Errorf("prompt required") @@ -229,7 +229,7 @@ Examples: if len(attachID) > 8 { attachID = attachID[:8] } - cmd.Printf("%s\n", auditTint("Attach: graycode attach "+attachID, textMuted)) + cmd.Printf("%s\n", auditTint("Attach: hawk attach "+attachID, textMuted)) return nil }, } diff --git a/cmd/braille_spinner.go b/cmd/braille_spinner.go index 3080f4803..4429358b7 100644 --- a/cmd/braille_spinner.go +++ b/cmd/braille_spinner.go @@ -10,43 +10,43 @@ import ( type SpinnerStyle string const ( - SpinnerBraille SpinnerStyle = "braille" - SpinnerBrailleWave SpinnerStyle = "braillewave" - SpinnerGraycode SpinnerStyle = "graycode" - SpinnerGraycodeQuad SpinnerStyle = "graycodequad" - SpinnerDNA SpinnerStyle = "dna" - SpinnerScan SpinnerStyle = "scan" - SpinnerPulse SpinnerStyle = "pulse" - SpinnerSnake SpinnerStyle = "snake" - SpinnerOrbit SpinnerStyle = "orbit" - SpinnerWing SpinnerStyle = "wing" // ⫷⫸ — two-frame wing flap - SpinnerTalons SpinnerStyle = "talons" // ⩤⩥⩦⩧ — four-frame talon cycle - SpinnerRandom SpinnerStyle = "random" + SpinnerBraille SpinnerStyle = "braille" + SpinnerBrailleWave SpinnerStyle = "braillewave" + SpinnerHawk SpinnerStyle = "hawk" + SpinnerHawkQuad SpinnerStyle = "hawkquad" + SpinnerDNA SpinnerStyle = "dna" + SpinnerScan SpinnerStyle = "scan" + SpinnerPulse SpinnerStyle = "pulse" + SpinnerSnake SpinnerStyle = "snake" + SpinnerOrbit SpinnerStyle = "orbit" + SpinnerWing SpinnerStyle = "wing" // ⫷⫸ — two-frame wing flap + SpinnerTalons SpinnerStyle = "talons" // ⩤⩥⩦⩧ — four-frame talon cycle + SpinnerRandom SpinnerStyle = "random" ) -// graycodeSpinnerGlyphs is the default TUI spinner — partial-circle compass (smooth, readable). -var graycodeSpinnerGlyphs = []string{"◐", "◓", "◑", "◒"} +// hawkSpinnerGlyphs is the default TUI spinner — partial-circle compass (smooth, readable). +var hawkSpinnerGlyphs = []string{"◐", "◓", "◑", "◒"} -// graycodeQuadBlockGlyphs is the legacy QUADBLOCK animation (kept for tests / bubbles compat). -var graycodeQuadBlockGlyphs = []string{"▛", "▜", "▟", "▙"} +// hawkQuadBlockGlyphs is the legacy QUADBLOCK animation (kept for tests / bubbles compat). +var hawkQuadBlockGlyphs = []string{"▛", "▜", "▟", "▙"} // spinnerFrames maps style names to their animation frames. var spinnerFrames = map[SpinnerStyle][]string{ - SpinnerBraille: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, - SpinnerBrailleWave: {"⠁⠂⠄⡀", "⠂⠄⡀⢀", "⠄⡀⢀⠠", "⡀⢀⠠⠐", "⢀⠠⠐⠈", "⠠⠐⠈⠁", "⠐⠈⠁⠂", "⠈⠁⠂⠄"}, - SpinnerGraycode: graycodeSpinnerGlyphs, - SpinnerGraycodeQuad: graycodeQuadBlockGlyphs, - SpinnerDNA: {"⠋⠉⠙⠚", "⠉⠙⠚⠒", "⠙⠚⠒⠂", "⠚⠒⠂⠂", "⠒⠂⠂⠒", "⠂⠂⠒⠲", "⠂⠒⠲⠴", "⠒⠲⠴⠤", "⠲⠴⠤⠄", "⠴⠤⠄⠋", "⠤⠄⠋⠉", "⠄⠋⠉⠙"}, - SpinnerScan: {"⡇⠀⠀⠀", "⣿⠀⠀⠀", "⢸⡇⠀⠀", "⠀⣿⠀⠀", "⠀⢸⡇⠀", "⠀⠀⣿⠀", "⠀⠀⢸⡇", "⠀⠀⠀⣿", "⠀⠀⠀⢸", "⠀⠀⠀⠀"}, - SpinnerPulse: {"⠀", "⠄", "⠆", "⠇", "⡇", "⣇", "⣧", "⣷", "⣿", "⣷", "⣧", "⣇", "⡇", "⠇", "⠆", "⠄"}, - SpinnerSnake: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢁⡱", "⢁⡰", "⢁⡠", "⢁⡀", "⢁⠀", "⠁⠀"}, - SpinnerOrbit: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠", "⣀", "⢠", "⢐", "⢈", "⢁"}, - SpinnerWing: {"⫷", "⫸"}, - SpinnerTalons: {"⩤", "⩥", "⩦", "⩧"}, + SpinnerBraille: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, + SpinnerBrailleWave: {"⠁⠂⠄⡀", "⠂⠄⡀⢀", "⠄⡀⢀⠠", "⡀⢀⠠⠐", "⢀⠠⠐⠈", "⠠⠐⠈⠁", "⠐⠈⠁⠂", "⠈⠁⠂⠄"}, + SpinnerHawk: hawkSpinnerGlyphs, + SpinnerHawkQuad: hawkQuadBlockGlyphs, + SpinnerDNA: {"⠋⠉⠙⠚", "⠉⠙⠚⠒", "⠙⠚⠒⠂", "⠚⠒⠂⠂", "⠒⠂⠂⠒", "⠂⠂⠒⠲", "⠂⠒⠲⠴", "⠒⠲⠴⠤", "⠲⠴⠤⠄", "⠴⠤⠄⠋", "⠤⠄⠋⠉", "⠄⠋⠉⠙"}, + SpinnerScan: {"⡇⠀⠀⠀", "⣿⠀⠀⠀", "⢸⡇⠀⠀", "⠀⣿⠀⠀", "⠀⢸⡇⠀", "⠀⠀⣿⠀", "⠀⠀⢸⡇", "⠀⠀⠀⣿", "⠀⠀⠀⢸", "⠀⠀⠀⠀"}, + SpinnerPulse: {"⠀", "⠄", "⠆", "⠇", "⡇", "⣇", "⣧", "⣷", "⣿", "⣷", "⣧", "⣇", "⡇", "⠇", "⠆", "⠄"}, + SpinnerSnake: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢁⡱", "⢁⡰", "⢁⡠", "⢁⡀", "⢁⠀", "⠁⠀"}, + SpinnerOrbit: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠", "⣀", "⢠", "⢐", "⢈", "⢁"}, + SpinnerWing: {"⫷", "⫸"}, + SpinnerTalons: {"⩤", "⩥", "⩦", "⩧"}, } -// graycodeTypingDots is the number of trailing typing-indicator dots. -const graycodeTypingDots = 3 +// hawkTypingDots is the number of trailing typing-indicator dots. +const hawkTypingDots = 3 // BrailleSpinner renders the glyph frame (◐◓◑◒) and a 20-color wave on the // whole status strip: glyph → verb → ▪▫▫. @@ -57,7 +57,7 @@ type BrailleSpinner struct { frame int // glyph animation frame (mod len(frames)) wavePhase int // 0..19 flowing color wave (glyph + verb + dots) text string - dots int // 0..graycodeTypingDots-1 — position of the highlighted dot + dots int // 0..hawkTypingDots-1 — position of the highlighted dot running bool stopCh chan struct{} } @@ -65,7 +65,7 @@ type BrailleSpinner struct { // NewBrailleSpinner creates a spinner with the given style and label text. func NewBrailleSpinner(style SpinnerStyle, text string) *BrailleSpinner { if style == SpinnerRandom { - styles := []SpinnerStyle{SpinnerGraycode, SpinnerBraille, SpinnerBrailleWave, SpinnerDNA, SpinnerScan, SpinnerPulse, SpinnerSnake, SpinnerOrbit} + styles := []SpinnerStyle{SpinnerHawk, SpinnerBraille, SpinnerBrailleWave, SpinnerDNA, SpinnerScan, SpinnerPulse, SpinnerSnake, SpinnerOrbit} style = styles[rand.Intn(len(styles))] // #nosec G404 -- non-cryptographic use (random spinner style selection) } frames := spinnerFrames[style] @@ -110,7 +110,7 @@ func (s *BrailleSpinner) Tick() string { s.mu.Lock() s.frame++ s.wavePhase = (s.wavePhase + 1) % spinnerWaveLen - s.dots = (s.dots + 1) % graycodeTypingDots + s.dots = (s.dots + 1) % hawkTypingDots s.mu.Unlock() return s.Frame() } diff --git a/cmd/braille_spinner_test.go b/cmd/braille_spinner_test.go index e326eb02e..96c1c6f05 100644 --- a/cmd/braille_spinner_test.go +++ b/cmd/braille_spinner_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestBrailleSpinner_Tick(t *testing.T) { @@ -29,7 +29,7 @@ func TestBrailleSpinner_Tick(t *testing.T) { func TestBrailleSpinner_AllStyles(t *testing.T) { styles := []SpinnerStyle{ - SpinnerBraille, SpinnerBrailleWave, SpinnerGraycode, SpinnerDNA, + SpinnerBraille, SpinnerBrailleWave, SpinnerHawk, SpinnerDNA, SpinnerScan, SpinnerPulse, SpinnerSnake, SpinnerOrbit, SpinnerWing, SpinnerTalons, } @@ -50,14 +50,14 @@ func TestBrailleSpinner_Random(t *testing.T) { } } -func TestGraycodeSpinner_Frames(t *testing.T) { - if len(graycodeSpinnerGlyphs) != 4 { - t.Fatalf("expected 4 compass glyphs, got %d", len(graycodeSpinnerGlyphs)) +func TestHawkSpinner_Frames(t *testing.T) { + if len(hawkSpinnerGlyphs) != 4 { + t.Fatalf("expected 4 compass glyphs, got %d", len(hawkSpinnerGlyphs)) } - if graycodeSpinnerGlyphs[0] != "◐" { - t.Fatalf("expected first compass frame ◐, got %q", graycodeSpinnerGlyphs[0]) + if hawkSpinnerGlyphs[0] != "◐" { + t.Fatalf("expected first compass frame ◐, got %q", hawkSpinnerGlyphs[0]) } - s := NewBrailleSpinner(SpinnerGraycode, "Working") + s := NewBrailleSpinner(SpinnerHawk, "Working") f0 := s.Frame() if !strings.Contains(f0, "◐") { t.Fatalf("expected compass glyph, got %q", f0) @@ -70,20 +70,20 @@ func TestGraycodeSpinner_Frames(t *testing.T) { } } -func TestGraycodeQuadBlock_LegacyFrames(t *testing.T) { - s := NewBrailleSpinner(SpinnerGraycodeQuad, "Working") +func TestHawkQuadBlock_LegacyFrames(t *testing.T) { + s := NewBrailleSpinner(SpinnerHawkQuad, "Working") if !strings.Contains(s.Frame(), "▛") { t.Fatalf("expected QuadBlock glyph, got %q", s.Frame()) } } -func TestGraycodeAnimatedDots_PresentInFrame(t *testing.T) { - s := NewBrailleSpinner(SpinnerGraycode, "Crafting") +func TestHawkAnimatedDots_PresentInFrame(t *testing.T) { + s := NewBrailleSpinner(SpinnerHawk, "Crafting") f := s.Frame() // Three progress dots ride after the verb: one bright, two dim. total := strings.Count(f, icons.CircleFilled()) + strings.Count(f, icons.CircleOutline()) - if total != graycodeTypingDots { - t.Errorf("expected %d trailing circle-dots, got %d in %q", graycodeTypingDots, total, f) + if total != hawkTypingDots { + t.Errorf("expected %d trailing circle-dots, got %d in %q", hawkTypingDots, total, f) } // Tick advances the highlighted dot position. idxBefore := s.dots diff --git a/cmd/cascade_diag_test.go b/cmd/cascade_diag_test.go index bfd211b46..55aaee42f 100644 --- a/cmd/cascade_diag_test.go +++ b/cmd/cascade_diag_test.go @@ -3,14 +3,14 @@ package cmd import ( "testing" - graycodebranch "github.com/GrayCodeAI/graycode-cli/internal/engine/branching" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + hawkbranch "github.com/GrayCodeAI/hawk/internal/engine/branching" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) func TestCascadeSelectsForOpenCodeGoHi(t *testing.T) { roles := routing.DefaultRoles("opencodego/minimax-m2.5") t.Logf("cheapest=%s commit=%s", routing.CheapestForProvider("opencodego", "minimax-m2.5"), roles.Commit) - cr := graycodebranch.NewCascadeRouter("opencodego/minimax-m2.5", roles) + cr := hawkbranch.NewCascadeRouter("opencodego/minimax-m2.5", roles) cr.Enabled = true got := cr.SelectModel("Hi", "opencodego/minimax-m2.5", "") t.Logf("selected=%s", got) diff --git a/cmd/catalog_startup.go b/cmd/catalog_startup.go index e4be71b35..b69aeac86 100644 --- a/cmd/catalog_startup.go +++ b/cmd/catalog_startup.go @@ -4,7 +4,7 @@ import ( "context" "os" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) var ( @@ -13,15 +13,15 @@ var ( ) func ensureCatalogBeforeAgent(ctx context.Context, strict bool) error { - opts := graycodeconfig.CatalogStartupOptions{ + opts := hawkconfig.CatalogStartupOptions{ ForceRefresh: refreshCatalogFlag, SkipAutoRefresh: skipCatalogRefreshFlag, VerboseOutput: refreshCatalogFlag, } if strict { - return graycodeconfig.PrepareCatalogForSession(ctx, os.Stderr, opts) + return hawkconfig.PrepareCatalogForSession(ctx, os.Stderr, opts) } - graycodeconfig.StartupCatalogPrefetch(ctx) + hawkconfig.StartupCatalogPrefetch(ctx) return nil } @@ -29,5 +29,5 @@ func startBackgroundCatalogRefresh(ctx context.Context) { if skipCatalogRefreshFlag { return } - graycodeconfig.ScheduleBackgroundCatalogRefresh(ctx) + hawkconfig.ScheduleBackgroundCatalogRefresh(ctx) } diff --git a/cmd/chat.go b/cmd/chat.go index 69e823986..d2c7720a0 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -25,24 +25,24 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/bridge/sessioncapture" - "github.com/GrayCodeAI/graycode-cli/internal/codegraph" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/feature/taste" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/startup" - graycodestorage "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/system/staleness" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" - - "github.com/GrayCodeAI/graycode-cli/internal/conversationarc" + "github.com/GrayCodeAI/hawk/internal/bridge/sessioncapture" + "github.com/GrayCodeAI/hawk/internal/codegraph" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "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/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" + "github.com/GrayCodeAI/hawk/internal/plugin" + "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/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" + + "github.com/GrayCodeAI/hawk/internal/conversationarc" ) // Types, styles, and model struct are in chat_model.go @@ -51,7 +51,7 @@ import ( // Tool-registry construction (essential/optional tools) is in chat_tools.go // The Bubble Tea event loop (Update, applyPromptArrowKey) is in chat_update.go -const workInputPlaceholder = `Ask Graycode to inspect, edit, or run something... (Shift+Enter for newline, ? for help)` +const workInputPlaceholder = `Ask Hawk to inspect, edit, or run something... (Shift+Enter for newline, ? for help)` func genID() string { b := make([]byte, 8) @@ -72,7 +72,7 @@ func prepareSession(sess *engine.Session) (string, *session.Session, error) { } if sessionIDFlag != "" && (resumeID != "" || continueFlag) { // --session-id is ignored when --resume or --continue is also given. - fmt.Fprintf(os.Stderr, "%s\n", auditTint("graycode: --session-id ignored during resume/continue", textMuted)) + fmt.Fprintf(os.Stderr, "%s\n", auditTint("hawk: --session-id ignored during resume/continue", textMuted)) } if resumeID == "" && !continueFlag { return id, nil, nil @@ -106,7 +106,7 @@ func prepareSession(sess *engine.Session) (string, *session.Session, error) { return saved.ID, saved, nil } -func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings graycodeconfig.Settings, registry *tool.Registry) (chatModel, error) { +func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkconfig.Settings, registry *tool.Registry) (chatModel, error) { startup.MarkPhase("newChatModel:total") startup.MarkPhase("newChatModel:ui-init") @@ -126,15 +126,15 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco CursorLine: lipgloss.NewStyle(), Base: lipgloss.NewStyle().Foreground(textPrimary), Placeholder: lipgloss.NewStyle().Foreground(textPlaceholder), - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), }, Blurred: textarea.StyleState{ Base: lipgloss.NewStyle().Foreground(textPlaceholder), Placeholder: lipgloss.NewStyle().Foreground(textPlaceholder), - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), }, Cursor: textarea.CursorStyle{ - Color: graycodeColor, + Color: hawkColor, }, }) ta.Prompt = icons.ChevronRight() + " " @@ -146,8 +146,8 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco ci.EchoMode = textinput.EchoNormal sp := spinner.New() - sp.Spinner = spinner.Spinner{Frames: graycodeSpinnerFrames, FPS: graycodeSpinnerFrameInterval} - sp.Style = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + sp.Spinner = spinner.Spinner{Frames: hawkSpinnerFrames, FPS: hawkSpinnerFrameInterval} + sp.Style = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) startup.EndPhase("newChatModel:ui-init") startup.MarkPhase("newChatModel:effectiveModelAndProvider") @@ -158,9 +158,9 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco startup.MarkPhase("newChatModel:defaultRegistry") startup.EndPhase("newChatModel:defaultRegistry") - startup.MarkPhase("newChatModel:newGraycodeSession") - sess := newStartupGraycodeSession(selection, systemPrompt, registry) - startup.EndPhase("newChatModel:newGraycodeSession") + startup.MarkPhase("newChatModel:newHawkSession") + sess := newStartupHawkSession(selection, systemPrompt, registry) + startup.EndPhase("newChatModel:newHawkSession") startup.MarkPhase("newChatModel:configureSession") if cfgErr := prepareInteractiveSessionStartup(sess, settings); cfgErr != nil { @@ -184,7 +184,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco // Initialize conversation DAG for branching support startup.MarkPhase("newChatModel:dag") - graphPath := filepath.Join(graycodestorage.SessionsDir(), "conversations", sid+".json") + graphPath := filepath.Join(hawkstorage.SessionsDir(), "conversations", sid+".json") if graph, err := session.OpenConversationGraph(graphPath, sid); err == nil { sess.SetConversationGraph(graph) } @@ -246,7 +246,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco m.contextualHelp = NewContextualHelp() m.modeManager = shellmode.NewModeManager() m.modeManager.LoadPersistedMode() - m.brailleSpinner = NewBrailleSpinner(SpinnerGraycode, "") + m.brailleSpinner = NewBrailleSpinner(SpinnerHawk, "") m.brailleSpinner.SetLabel(m.spinnerVerb) startup.EndPhase("newChatModel:lacy-features") @@ -297,8 +297,8 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco // Prefetch live models for the active provider so footer ctx/pricing stay current. go func() { providerName := effectiveProvider - entries, _ := graycodeconfig.ListEngineModels(context.Background(), providerName, false) - opts := configModelOptionsFromGraycodeRouter(entries) + entries, _ := hawkconfig.ListEngineModels(context.Background(), providerName, false) + opts := configModelOptionsFromEyrie(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[providerName] = opts @@ -324,7 +324,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco m.welcomeCache = buildWelcomeMessageWithSnapshot(sess, sid, registry, saved, settings, 0, connectedMCPCount(registry), 0, initWidth, initHeight, nil, quickSnapshot, false, "") m.messages = append(m.messages, displayMsg{role: "welcome", content: m.welcomeCache}) // First-session control-plane tip (skip when resuming history or when quiet env var is set). - if saved == nil && os.Getenv("GRAYCODE_QUIET_START") == "" && os.Getenv("GRAYCODE_SUPPRESS_HINTS") == "" && os.Getenv("GRAYCODE_QUIET") == "" { + if saved == nil && os.Getenv("HAWK_QUIET_START") == "" && os.Getenv("HAWK_SUPPRESS_HINTS") == "" && os.Getenv("HAWK_QUIET") == "" { m.messages = append(m.messages, displayMsg{role: "system", content: controlPlaneOnboardingHint(sess)}) } startup.EndPhase("newChatModel:welcome") @@ -401,7 +401,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco // the initial UI. go func(currentSessionID string) { if recovered := session.CheckForRecovery(); len(recovered) > 0 { - walDir := graycodestorage.SessionsDir() + walDir := hawkstorage.SessionsDir() for _, rid := range recovered { if rid == currentSessionID { continue // current session WAL @@ -417,7 +417,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco // Warm footer data and the model catalog after the first frame. go func(model chatModel) { startup.MarkPhase("newChatModel:ui-cache-warm") - graycodeconfig.RefreshConfigCredSnapshot(context.Background()) + hawkconfig.RefreshConfigCredSnapshot(context.Background()) // Network reachability runs off the startup critical path: an offline // machine stalls here (background) instead of before first paint. if msg := checkNetworkReachability(model.settings); msg != "" { @@ -537,7 +537,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings grayco } // refreshInputPlaceholder updates the input placeholder based on the current -// container lifecycle. Graycode never executes agent tools directly on the host. +// container lifecycle. Hawk never executes agent tools directly on the host. func (m *chatModel) refreshInputPlaceholder() { work := engine.WorkModeAct if m.session != nil { @@ -638,7 +638,7 @@ func runChat() error { // One-time, gated codebase analysis for projects with no context file. // Runs in the background and never blocks startup; fully opt-out via - // GRAYCODE_DISABLE_AUTO_INIT. No-op for projects that already have context. + // HAWK_DISABLE_AUTO_INIT. No-op for projects that already have context. maybeAutoInit(context.Background()) ref := &progRef{} @@ -647,7 +647,7 @@ func runChat() error { err error } type startupSettingsResult struct { - settings graycodeconfig.Settings + settings hawkconfig.Settings err error } type startupRegistryResult struct { @@ -713,7 +713,7 @@ func runChat() error { m.waiting = true } - // Suppress library log output (e.g. graycode-router retry warnings) from corrupting the TUI. + // Suppress library log output (e.g. eyrie retry warnings) from corrupting the TUI. // Must be set BEFORE tea.NewProgram so no initialization logs leak through. log.SetOutput(io.Discard) @@ -772,7 +772,7 @@ func runChat() error { fmt.Print(formatQuitResumeMessage(fm.sessionID)) return nil } - graycodeC := ansiOrange + hawkC := ansiOrange rst := ansiReset fmt.Print(fm.welcomeCache) @@ -780,10 +780,10 @@ func runChat() error { for _, msg := range fm.messages { switch msg.role { case "user": - fmt.Println(graycodeC + "█" + rst + " " + msg.content) + fmt.Println(hawkC + "█" + rst + " " + msg.content) fmt.Println() case "assistant": - fmt.Println(graycodeC + icons.Robot() + " " + rst + msg.content) + fmt.Println(hawkC + icons.Robot() + " " + rst + msg.content) fmt.Println() case "system": fmt.Println(dimStyle.Render("● " + msg.content)) @@ -815,12 +815,12 @@ func runChat() error { border := strings.Repeat("─", viewWidth) borderStyle := lipgloss.NewStyle().Foreground(borderDim) fmt.Println(borderStyle.Render(border)) - fmt.Println(lipgloss.NewStyle().Foreground(graycodeColor).Bold(true).Render(">") + " ") + fmt.Println(lipgloss.NewStyle().Foreground(hawkColor).Bold(true).Render(">") + " ") fmt.Println(borderStyle.Render(border)) fmt.Println(dimStyle.Render("? for help")) if fm.sessionID != "" { - fmt.Println(dimStyle.Render(fmt.Sprintf("To resume this session, run: graycode --resume %s", fm.sessionID))) + fmt.Println(dimStyle.Render(fmt.Sprintf("To resume this session, run: hawk --resume %s", fm.sessionID))) } return nil } diff --git a/cmd/chat_commands.go b/cmd/chat_commands.go index 354b44600..33ccf5ffd 100644 --- a/cmd/chat_commands.go +++ b/cmd/chat_commands.go @@ -11,8 +11,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/multiagent/parallel" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/multiagent/parallel" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // slashCmdCache caches the slash commands list to avoid rebuilding. @@ -202,7 +202,7 @@ var slashDescriptions = map[string]string{ "/export": "Export session", "/follow": "Toggle stream follow (auto-scroll)", "/home": "Jump to top of chat and welcome header", - "/feedback": "Submit feedback about graycode", + "/feedback": "Submit feedback about hawk", "/fast": "Toggle fast mode", "/files": "Show modified files", "/focus": "Narrow agent attention to specific files/dirs", @@ -245,7 +245,7 @@ var slashDescriptions = map[string]string{ "/start": "Guided setup: trust, mode, branch, first tasks", "/trust": "Folder trust status / add / remove", "/isolation": "Isolation profile: dev|workspace|strict|container", - "/branch-agent": "Create graycode/agent-* branch if on main/master", + "/branch-agent": "Create hawk/agent-* branch if on main/master", "/auto-commit": "Toggle git auto-commit after Write/Edit (on|off)", "/summary": "Summarize the session", "/tasks": "Show task list", @@ -254,10 +254,10 @@ var slashDescriptions = map[string]string{ "/tools": "List enabled tools", "/undo": "Undo the most recent file change", "/usage": "Show cost summary", - "/version": "Show graycode version", + "/version": "Show hawk version", "/vim": "Toggle vim mode", "/welcome": "Re-print the welcome header", - "/ecosystem": "Show graycode-router, harrier, and shrike integration status", + "/ecosystem": "Show eyrie, harrier, and shrike integration status", "/path": "Developer path readiness (setup, security, sandbox)", "/harrier": "Show harrier memory (use /harrier search to search)", "/cron": "Show scheduled jobs", @@ -303,7 +303,7 @@ var slashDescriptions = map[string]string{ "/refresh-model-catalog": "Refresh the model catalog from providers", "/image": "Generate or process images", "/recipe": "Run a saved recipe (command template)", - "/soul": "Show or update graycode's personality/soul", + "/soul": "Show or update hawk's personality/soul", "/mode": "Switch interaction mode", "/party": "Start a multi-agent party session", } diff --git a/cmd/chat_commands_config.go b/cmd/chat_commands_config.go index c121b61d2..6d6251847 100644 --- a/cmd/chat_commands_config.go +++ b/cmd/chat_commands_config.go @@ -6,14 +6,14 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // handleConfigCommand handles the /config command and all its subcommands. func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, tea.Cmd) { if len(parts) >= 3 && parts[1] == "provider" { value := strings.TrimSpace(strings.Join(parts[2:], " ")) - if err := graycodeconfig.SetGlobalSetting("provider", value); err != nil { + if err := hawkconfig.SetGlobalSetting("provider", value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } @@ -24,9 +24,9 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, modelCacheMu.RUnlock() if cacheHit && len(cached) > 0 { m.session.SetModel(cached[0].ID) - _ = graycodeconfig.SetGlobalSetting("model", cached[0].ID) + _ = hawkconfig.SetGlobalSetting("model", cached[0].ID) } - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Provider set to: %s\nModel: %s\nSaved in graycode-router (provider.json).", value, m.session.Model())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Provider set to: %s\nModel: %s\nSaved in eyrie (provider.json).", value, m.session.Model())}) return m, nil } if len(parts) >= 3 && parts[1] == "model" { @@ -47,12 +47,12 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, return m, nil } } - if err := graycodeconfig.SetGlobalSetting("model", value); err != nil { + if err := hawkconfig.SetGlobalSetting("model", value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } m.syncSessionSelection() - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Model switched to: %s\nSaved in graycode-router (provider.json).", m.session.Model())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Model switched to: %s\nSaved in eyrie (provider.json).", m.session.Model())}) return m, nil } if len(parts) >= 2 && parts[1] == "keys" { @@ -72,7 +72,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } - value, ok := graycodeconfig.SettingValue(settings, parts[2]) + value, ok := hawkconfig.SettingValue(settings, parts[2]) if !ok { m.messages = append(m.messages, displayMsg{role: "error", content: fmt.Sprintf("Unsupported setting key %q", parts[2])}) return m, nil @@ -86,7 +86,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, if len(parts) >= 4 && parts[1] == "set" { key := parts[2] value := strings.TrimSpace(strings.Join(parts[3:], " ")) - if err := graycodeconfig.SetGlobalSetting(key, value); err != nil { + if err := hawkconfig.SetGlobalSetting(key, value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } diff --git a/cmd/chat_commands_image.go b/cmd/chat_commands_image.go index 5bc1ae84a..079218b28 100644 --- a/cmd/chat_commands_image.go +++ b/cmd/chat_commands_image.go @@ -8,7 +8,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // handleImageCommand implements the /image slash command: @@ -17,7 +17,7 @@ import ( // // It reads an image (or PDF) file, builds a multimodal user message gated on the // active model's vision capability, and starts a turn. For PDFs the text is -// extracted and injected inline (graycode-router has no native document block). Without a +// extracted and injected inline (eyrie has no native document block). Without a // vision-capable model the image degrades to a text-only note. func (m *chatModel) handleImageCommand(parts []string, text string) (tea.Model, tea.Cmd) { if len(parts) < 2 { diff --git a/cmd/chat_commands_session.go b/cmd/chat_commands_session.go index dd33b424f..446ac1b56 100644 --- a/cmd/chat_commands_session.go +++ b/cmd/chat_commands_session.go @@ -11,8 +11,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" ) type sessionSaveResultMsg struct { @@ -85,9 +85,9 @@ func (m *chatModel) saveSessionCmd() tea.Cmd { func formatQuitResumeMessage(sessionID string) string { if strings.TrimSpace(sessionID) == "" { - return "Thank you for using Graycode!\n" + return "Thank you for using Hawk!\n" } - return fmt.Sprintf("Thank you for using Graycode!\n\nTo resume this session, run: graycode --resume %s\n", sessionID) + return fmt.Sprintf("Thank you for using Hawk!\n\nTo resume this session, run: hawk --resume %s\n", sessionID) } // handleSessionCommand dispatches session-management slash commands. @@ -534,7 +534,7 @@ func (m *chatModel) handleSessionCommand(cmd string, parts []string, text string case "/session": info := fmt.Sprintf("Session: %s\nModel: %s/%s\nSpec stage: %s\nMessages: %d\nTools: %d\n%s", m.sessionID, m.session.Provider(), m.session.Model(), - specStageLabel(m.session), m.session.MessageCount(), len(m.registry.GraycodeRouterTools()), m.session.CostValue().Summary()) + specStageLabel(m.session), m.session.MessageCount(), len(m.registry.EyrieTools()), m.session.CostValue().Summary()) m.messages = append(m.messages, displayMsg{role: "system", content: info}) return m, nil diff --git a/cmd/chat_commands_skills.go b/cmd/chat_commands_skills.go index 069b8e886..eb1a09e62 100644 --- a/cmd/chat_commands_skills.go +++ b/cmd/chat_commands_skills.go @@ -10,9 +10,9 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // handleSkillsCommand handles the /skills command and all its subcommands. diff --git a/cmd/chat_commands_test.go b/cmd/chat_commands_test.go index 4f94da28a..791f3ed20 100644 --- a/cmd/chat_commands_test.go +++ b/cmd/chat_commands_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestAdditionalDirContextLoadsInstructions(t *testing.T) { @@ -69,7 +69,7 @@ func TestLocalSlashCommands(t *testing.T) { m := &chatModel{ session: sess, registry: tool.NewRegistry(tool.LSTool{}), - settings: graycodeconfig.Settings{MCPServers: []graycodeconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}}, + settings: hawkconfig.Settings{MCPServers: []hawkconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}}, sessionID: "test", width: 80, height: 24, @@ -94,16 +94,16 @@ func TestLocalSlashCommands(t *testing.T) { func TestDiagnosticSummaries(t *testing.T) { preserveCLICompilerVersionState(t) version = "test-version" - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Provider: "openai", Model: "gpt-4o", - MCPServers: []graycodeconfig.MCPServerConfig{ + MCPServers: []hawkconfig.MCPServerConfig{ {Name: "demo", Command: "demo-mcp", Args: []string{"--stdio"}}, }, } report := doctorReport(settings) - if !strings.Contains(report, "Graycode doctor") || !strings.Contains(report, "Built-in tools") { + if !strings.Contains(report, "Hawk doctor") || !strings.Contains(report, "Built-in tools") { t.Fatalf("unexpected doctor report: %s", report) } if summary := mcpConfigSummary(settings); !strings.Contains(summary, "demo") { diff --git a/cmd/chat_commands_tools.go b/cmd/chat_commands_tools.go index b3d50e354..e53bb2cd5 100644 --- a/cmd/chat_commands_tools.go +++ b/cmd/chat_commands_tools.go @@ -8,9 +8,9 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // explainCode traces a file/line back to the git commit and session that created it. @@ -109,12 +109,12 @@ func (m *chatModel) handleShellEscape(command string) (tea.Model, tea.Cmd) { // handleNamespacedSkill handles /vendor:skill-name invocations. func (m *chatModel) handleNamespacedSkill(cmd, fullText string) (tea.Model, tea.Cmd) { // Parse /vendor:skill-name - invoke := cmd // e.g. "/graycode:go-review" + invoke := cmd // e.g. "/hawk:go-review" // Search active and installed skills for matching invoke pattern var matched *plugin.SmartSkill for name, skill := range m.activeSkills { - if skill.Invoke == invoke || "/graycode:"+name == invoke { + if skill.Invoke == invoke || "/hawk:"+name == invoke { matched = &skill break } @@ -124,7 +124,7 @@ func (m *chatModel) handleNamespacedSkill(cmd, fullText string) (tea.Model, tea. // Try loading from installed skills skills := plugin.LoadSmartSkills(plugin.DefaultSkillDirs()) for i := range skills { - if skills[i].Invoke == invoke || "/graycode:"+skills[i].Name == invoke { + if skills[i].Invoke == invoke || "/hawk:"+skills[i].Name == invoke { matched = &skills[i] break } diff --git a/cmd/chat_commands_util.go b/cmd/chat_commands_util.go index 6e84a4f73..d0352176c 100644 --- a/cmd/chat_commands_util.go +++ b/cmd/chat_commands_util.go @@ -8,11 +8,11 @@ import ( "path/filepath" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/taste" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/system/staleness" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/feature/taste" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/system/staleness" ) func gitOutput(args ...string) (string, error) { @@ -73,7 +73,7 @@ func additionalDirContext(dir string) (string, string, error) { } var b strings.Builder b.WriteString("Additional directory: " + abs) - if md := graycodeconfig.LoadAgentsMDFrom(abs); md != "" { + if md := hawkconfig.LoadAgentsMDFrom(abs); md != "" { b.WriteString("\nAdditional directory instructions (" + abs + "):\n" + md) } return abs, b.String(), nil @@ -108,7 +108,7 @@ func (m *chatModel) mcpSummary() string { } if m.registry != nil { var toolNames []string - for _, t := range m.registry.GraycodeRouterTools() { + for _, t := range m.registry.EyrieTools() { if strings.HasPrefix(t.Name, "mcp__") { toolNames = append(toolNames, t.Name) } @@ -129,7 +129,7 @@ func sessionStats(sess *engine.Session, id string) string { } func hooksSummary() string { - return "Hooks: pre_query, post_query, pre_tool, post_tool, session_start, session_end, permission_ask, error\nConfigure in Graycode user settings" + return "Hooks: pre_query, post_query, pre_tool, post_tool, session_start, session_end, permission_ask, error\nConfigure in Hawk user settings" } func pluginsSummary(rt *plugin.Runtime) string { diff --git a/cmd/chat_config_deployment.go b/cmd/chat_config_deployment.go index 36844308a..7c4718ba5 100644 --- a/cmd/chat_config_deployment.go +++ b/cmd/chat_config_deployment.go @@ -9,9 +9,9 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) type configApplyCredentialsMsg struct { @@ -24,39 +24,39 @@ type configApplyCredentialsMsg struct { func firstRunModelProvider(m chatModel) string { ctx := context.Background() - if p := strings.TrimSpace(m.configModelProvider); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(m.configModelProvider); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { return p } if m.session != nil { - if p := strings.TrimSpace(m.session.Provider()); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(m.session.Provider()); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { return p } } - if p := strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(hawkconfig.ActiveGateway(ctx)); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { return p } - if p := graycodeconfig.DefaultModelProviderFilter(ctx); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { + if p := hawkconfig.DefaultModelProviderFilter(ctx); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { return p } - for _, p := range graycodeconfig.AllSetupGateways() { - if graycodeconfig.HasStoredCredentialForProvider(ctx, p) { + for _, p := range hawkconfig.AllSetupGateways() { + if hawkconfig.HasStoredCredentialForProvider(ctx, p) { return p } } return "" } -func saveProviderKeyAsync(inference graycodeconfig.CredentialInference, secret string) tea.Cmd { +func saveProviderKeyAsync(inference hawkconfig.CredentialInference, secret string) tea.Cmd { return saveCredentialAsync(inference, secret) } func saveOllamaAsync(baseURL string) tea.Cmd { return func() tea.Msg { - inference, err := graycodeconfig.LocalCredentialInference(configProviderOllama) + inference, err := hawkconfig.LocalCredentialInference(configProviderOllama) if err != nil { return configApplyCredentialsMsg{err: err} } - inf := graycodeconfig.CredentialInference{ + inf := hawkconfig.CredentialInference{ ProviderID: inference.ProviderID, DeploymentID: inference.DeploymentID, EnvVar: inference.EnvVar, @@ -66,22 +66,22 @@ func saveOllamaAsync(baseURL string) tea.Cmd { } } -func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret string) tea.Cmd { +func saveCredentialAsync(inference hawkconfig.CredentialInference, secret string) tea.Cmd { return func() tea.Msg { ctx := context.Background() - if err := graycodeconfig.SaveCredential(ctx, inference, secret); err != nil { + if err := hawkconfig.SaveCredential(ctx, inference, secret); err != nil { return configApplyCredentialsMsg{ err: err, providerID: inference.ProviderID, deploymentID: inference.DeploymentID, } } - graycodeconfig.InvalidateConfigUICache() - graycodeconfig.RefreshConfigCredSnapshot(ctx) - result, err := graycodeconfig.ApplyGraycodeRouterCredentialsForProvider(ctx, inference.ProviderID) - if err != nil && graycodeconfig.IsCatalogCacheRequired(err) { - if refreshErr := graycodeconfig.RefreshCatalogAfterCredentials(ctx, nil); refreshErr == nil { - result, err = graycodeconfig.ApplyGraycodeRouterCredentialsForProvider(ctx, inference.ProviderID) + hawkconfig.InvalidateConfigUICache() + hawkconfig.RefreshConfigCredSnapshot(ctx) + result, err := hawkconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + if err != nil && hawkconfig.IsCatalogCacheRequired(err) { + if refreshErr := hawkconfig.RefreshCatalogAfterCredentials(ctx, nil); refreshErr == nil { + result, err = hawkconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) } else { err = fmt.Errorf("%w; automatic catalog refresh failed: %w", err, refreshErr) } @@ -94,7 +94,7 @@ func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret st } } - entries, listErr := graycodeconfig.ListEngineModels(ctx, inference.ProviderID, false) + entries, listErr := hawkconfig.ListEngineModels(ctx, inference.ProviderID, false) if listErr != nil { return configApplyCredentialsMsg{ err: listErr, @@ -102,9 +102,9 @@ func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret st deploymentID: inference.DeploymentID, } } - opts := configModelOptionsFromGraycodeRouter(entries) + opts := configModelOptionsFromEyrie(entries) return configApplyCredentialsMsg{ - summary: graycodeconfig.FormatApplyCredentialsSummary(result), + summary: hawkconfig.FormatApplyCredentialsSummary(result), providerID: inference.ProviderID, deploymentID: inference.DeploymentID, modelOptions: opts, @@ -134,15 +134,15 @@ func (m chatModel) startConfigURLInput(defaultURL string) (chatModel, tea.Cmd) { m.configInput.EchoMode = textinput.EchoNormal m.configInput.SetStyles(textinput.Styles{ Focused: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: graycodeColor, + Color: hawkColor, }, }) m.configInput.Focus() @@ -152,27 +152,27 @@ func (m chatModel) startConfigURLInput(defaultURL string) (chatModel, tea.Cmd) { func (m chatModel) handleConfigApplyCredentialsMsg(msg configApplyCredentialsMsg) (chatModel, tea.Cmd) { m.configSaving = false ctx := context.Background() - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() if msg.err != nil { m.invalidateConnStatus() if msg.providerID == configProviderOllama { return m.returnToOllamaURLAfterError(msg.err) } - notice := sanitizeConfigNotice(graycodeconfig.FormatConfigProviderError(msg.providerID, msg.err)) - saved := graycodeconfig.HasStoredCredentialForProvider(ctx, msg.providerID) || + notice := sanitizeConfigNotice(hawkconfig.FormatConfigProviderError(msg.providerID, msg.err)) + saved := hawkconfig.HasStoredCredentialForProvider(ctx, msg.providerID) || strings.Contains(strings.ToLower(msg.err.Error()), "key saved in keychain") if saved { - if graycodeconfig.IsCatalogCacheRequired(msg.err) { + if hawkconfig.IsCatalogCacheRequired(msg.err) { notice = "Key saved in " + credentialsStoreLabel() + " — model catalog unavailable: " + notice - notice += " · run graycode models refresh" + notice += " · run hawk models refresh" } else if configCredentialRejected(msg.err) { notice = "Key saved in " + credentialsStoreLabel() + " — provider rejected this key: " + notice } else { notice = "Key saved in " + credentialsStoreLabel() + " — catalog refresh failed: " + notice } - if !graycodeconfig.IsCatalogCacheRequired(msg.err) && !strings.Contains(strings.ToLower(notice), "refresh") { - notice += " · press r on " + graycodeconfig.GatewayDisplayName(msg.providerID) + " to retry" + if !hawkconfig.IsCatalogCacheRequired(msg.err) && !strings.Contains(strings.ToLower(notice), "refresh") { + notice += " · press r on " + hawkconfig.GatewayDisplayName(msg.providerID) + " to retry" } } else { notice = "Could not save key — " + notice @@ -206,11 +206,11 @@ func (m chatModel) handleConfigApplyCredentialsMsg(msg configApplyCredentialsMsg if idx := next.configGatewayRowIndex(post); idx >= 0 { next.configSel = idx } - next.configNotice = "Key updated for " + graycodeconfig.GatewayDisplayName(post) + next.configNotice = "Key updated for " + hawkconfig.GatewayDisplayName(post) return next, cmd } if msg.providerID == configProviderOllama { - _ = graycodeconfig.SetGlobalSetting("provider", configProviderOllama) + _ = hawkconfig.SetGlobalSetting("provider", configProviderOllama) next.syncSessionSelection() } next.configGuideAfterKey = false @@ -253,9 +253,9 @@ func configCredentialRejected(err error) bool { } func (m chatModel) rebuildSessionTransport() (chatModel, tea.Cmd) { - m.settings = graycodeconfig.LoadSettings() + m.settings = hawkconfig.LoadSettings() m.syncSessionSelection() - selection := graycodeconfig.EffectiveSelectionWithSettings(context.Background(), m.settings, graycodeconfig.SelectionOptions{ + selection := hawkconfig.EffectiveSelectionWithSettings(context.Background(), m.settings, hawkconfig.SelectionOptions{ ProviderOverride: firstNonEmptyTrimmed(m.session.Provider(), m.settings.Provider), ModelOverride: firstNonEmptyTrimmed(m.session.Model(), m.settings.Model), }) diff --git a/cmd/chat_config_gateways.go b/cmd/chat_config_gateways.go index 335b1b6d4..23c086616 100644 --- a/cmd/chat_config_gateways.go +++ b/cmd/chat_config_gateways.go @@ -7,8 +7,8 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) type configGatewayRow struct { @@ -47,7 +47,7 @@ func (m chatModel) loadConfigGatewayRows() []configGatewayRow { if m.session != nil { activeModel = strings.TrimSpace(m.session.Model()) } - statuses := graycodeconfig.GatewayStatuses(ctx, active, activeModel) + statuses := hawkconfig.GatewayStatuses(ctx, active, activeModel) rows := make([]configGatewayRow, 0, len(statuses)) for _, status := range statuses { if status.ID == "" { @@ -64,10 +64,10 @@ func (m chatModel) loadConfigGatewayRows() []configGatewayRow { hasKey := status.HasStoredCredential credentialEnv, keyConflict := "", false if hasKey { - credentialEnv, keyConflict = graycodeconfig.CredentialEnvironmentConflict(ctx, status.ID) + credentialEnv, keyConflict = hawkconfig.CredentialEnvironmentConflict(ctx, status.ID) } display := status.DisplayName - if status.RegionRequired || status.RegionLabel != "" || graycodeconfig.HasRegionOptions(status.ID) { + if status.RegionRequired || status.RegionLabel != "" || hawkconfig.HasRegionOptions(status.ID) { if reg := status.RegionLabel; reg != "" { display += " · " + reg } else if status.RegionRequired { @@ -82,7 +82,7 @@ func (m chatModel) loadConfigGatewayRows() []configGatewayRow { HasKey: hasKey, Configured: status.HasConfiguredDeployment || hasKey, ModelCount: count, - Active: status.Active || graycodeconfig.ActiveProviderID(status.ID) == graycodeconfig.ActiveProviderID(active), + Active: status.Active || hawkconfig.ActiveProviderID(status.ID) == hawkconfig.ActiveProviderID(active), RegionLabel: status.RegionLabel, RegionRequired: status.RegionRequired, CredentialEnv: credentialEnv, @@ -266,18 +266,18 @@ func (m chatModel) configGatewaysView() string { ctx := context.Background() indent := strings.Repeat(" ", configTableIndent) if m.configKeysPendingRemove != "" { - name := graycodeconfig.GatewayDisplayName(m.configKeysPendingRemove) + name := hawkconfig.GatewayDisplayName(m.configKeysPendingRemove) b.WriteString("\n" + mutedStyle.Render(indent+configGatewayRemovePrompt(m.configKeysRemoveStep, name))) - } else if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + } else if !hawkconfig.HasConfiguredDeploymentCached(ctx) { hint := "Select a gateway · enter · paste API key · then Models tab" - if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || graycodeconfig.HasRegionOptions(rows[targetIdx].ID)) { + if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) { hint = rows[targetIdx].DisplayName + ": enter pick region then key · g change region" } b.WriteString("\n" + mutedStyle.Render(indent+hint)) } else { hints := "enter use gateway · k view key · delete remove · r refresh" - if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || graycodeconfig.HasRegionOptions(rows[targetIdx].ID)) { + if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) { hints = "enter · g region · k key · delete · r refresh" } @@ -321,7 +321,7 @@ func (m chatModel) handleConfigGatewaysSelect() (chatModel, tea.Cmd) { return m, nil } row := rows[m.configSel] - if (row.RegionRequired || graycodeconfig.HasRegionOptions(row.ID)) && (!row.HasKey || row.RegionRequired) { + if (row.RegionRequired || hawkconfig.HasRegionOptions(row.ID)) && (!row.HasKey || row.RegionRequired) { m.configGatewayFocus = m.configSel return m.startConfigGatewayRegion(row.ID), nil } @@ -336,8 +336,8 @@ func (m chatModel) handleConfigGatewaysSelect() (chatModel, tea.Cmd) { gw := row.ID m.configGatewayFocus = m.configSel m.configModelProvider = gw - _ = graycodeconfig.SetGlobalSetting("provider", gw) - if active := graycodeconfig.ActiveProvider(context.Background()); active != "" { + _ = hawkconfig.SetGlobalSetting("provider", gw) + if active := hawkconfig.ActiveProvider(context.Background()); active != "" { m.session.SetProvider(active) } m.configTab = configTabModels @@ -349,7 +349,7 @@ func (m chatModel) handleConfigGatewaysSelect() (chatModel, tea.Cmd) { func refreshGatewayAsync(providerID string) tea.Cmd { return func() tea.Msg { - summary, err := graycodeconfig.RefreshGatewayCatalog(context.Background(), providerID) + summary, err := hawkconfig.RefreshGatewayCatalog(context.Background(), providerID) return configGatewayRefreshMsg{providerID: providerID, summary: summary, err: err} } } @@ -359,7 +359,7 @@ func (m chatModel) handleConfigGatewayRefreshMsg(msg configGatewayRefreshMsg) ch InvalidateModelCacheProvider(msg.providerID) m = m.refreshConfigGatewayRows() if msg.err != nil { - m.configNotice = sanitizeConfigNotice(graycodeconfig.FormatConfigProviderError(msg.providerID, msg.err)) + m.configNotice = sanitizeConfigNotice(hawkconfig.FormatConfigProviderError(msg.providerID, msg.err)) return m } m.configNotice = msg.summary diff --git a/cmd/chat_config_gateways_test.go b/cmd/chat_config_gateways_test.go index 57e585cbe..9de3552e6 100644 --- a/cmd/chat_config_gateways_test.go +++ b/cmd/chat_config_gateways_test.go @@ -7,9 +7,9 @@ import ( "charm.land/bubbles/v2/textarea" "charm.land/bubbles/v2/textinput" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func chatModelForConfigPasteTest() chatModel { @@ -37,12 +37,12 @@ func TestConfigGatewayRows_UsesPanelCacheUntilInvalidated(t *testing.T) { func TestConfigGatewayRowsKeepRegistryOrderAfterKeySaved(t *testing.T) { isolateCredentialHome(t) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{} @@ -50,7 +50,7 @@ func TestConfigGatewayRowsKeepRegistryOrderAfterKeySaved(t *testing.T) { if err := store.Set(t.Context(), gateway.AccountForEnv("CONCENTRATE_API_KEY"), "test-key-1234567890"); err != nil { t.Fatal(err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() after := m.loadConfigGatewayRows() if len(after) != len(before) { @@ -65,12 +65,12 @@ func TestConfigGatewayRowsKeepRegistryOrderAfterKeySaved(t *testing.T) { func TestConfigGatewaysView_RequiresKeyForModelCounts(t *testing.T) { isolateCredentialHome(t) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{configTab: configTabGateways} @@ -87,12 +87,12 @@ func TestConfigGatewaysView_RequiresKeyForModelCounts(t *testing.T) { } func TestConfigGatewaysView_ShowsSaveOrProbeNotice(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{ @@ -109,17 +109,17 @@ func TestConfigGatewaysView_ShowsSaveOrProbeNotice(t *testing.T) { } func TestConfigGatewayRefreshTargetIndex_UsesSelectedRow(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() sess := engine.NewSession("", "", "", nil) sess.SetProvider("openrouter") @@ -138,17 +138,17 @@ func TestConfigGatewayRefreshTargetIndex_UsesSelectedRow(t *testing.T) { } func TestConfigGatewayRefreshTargetIndex_UsesFocusOnRefreshRow(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() rows := []configGatewayRow{ {ID: "openai", DisplayName: "OpenAI", HasKey: false}, @@ -162,17 +162,17 @@ func TestConfigGatewayRefreshTargetIndex_UsesFocusOnRefreshRow(t *testing.T) { } func TestFocusConfigActiveGateway_SelectsActiveRow(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() sess := engine.NewSession("", "", "", nil) sess.SetProvider("openrouter") @@ -196,19 +196,19 @@ func TestFocusConfigActiveGateway_SelectsActiveRow(t *testing.T) { } func TestHandleConfigGatewaysSelect_TokenPlanNoKeyShowsRegion(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{configTab: configTabGateways} rows := m.configGatewayRows() idx := -1 for i, row := range rows { - if row.ID == graycodeconfig.ProviderXiaomiTokenPlan { + if row.ID == hawkconfig.ProviderXiaomiTokenPlan { idx = i break } @@ -221,18 +221,18 @@ func TestHandleConfigGatewaysSelect_TokenPlanNoKeyShowsRegion(t *testing.T) { if next.configEntry != configEntryXiaomiRegion { t.Fatalf("entry = %q, want xiaomi region picker", next.configEntry) } - if next.configProvider != graycodeconfig.ProviderXiaomiTokenPlan { + if next.configProvider != hawkconfig.ProviderXiaomiTokenPlan { t.Fatalf("provider = %q", next.configProvider) } } func TestHandleConfigGatewaysSelect_NoKeyStartsPaste(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModelForConfigPasteTest() @@ -243,7 +243,7 @@ func TestHandleConfigGatewaysSelect_NoKeyStartsPaste(t *testing.T) { } sel := 0 for i, row := range gwRows { - if row.ID == graycodeconfig.ProviderXiaomiTokenPlan { + if row.ID == hawkconfig.ProviderXiaomiTokenPlan { continue } sel = i diff --git a/cmd/chat_config_hub.go b/cmd/chat_config_hub.go index 6741f151d..58ca7431f 100644 --- a/cmd/chat_config_hub.go +++ b/cmd/chat_config_hub.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func (m chatModel) openConfigPanel() (chatModel, tea.Cmd) { @@ -69,7 +69,7 @@ func catalogPricesAreStale(opts []configModelOption) bool { } func providerHasLiveFetcher(providerID string) bool { - return graycodeconfig.GatewaySupportsLiveDiscovery(providerID) + return hawkconfig.GatewaySupportsLiveDiscovery(providerID) } func (m chatModel) returnToOllamaURLAfterError(err error) (chatModel, tea.Cmd) { @@ -80,7 +80,7 @@ func (m chatModel) returnToOllamaURLAfterError(err error) (chatModel, tea.Cmd) { url = configDefaultOllamaURL } if err != nil { - m.configNotice = graycodeconfig.FormatConfigProviderError(configProviderOllama, err) + m.configNotice = hawkconfig.FormatConfigProviderError(configProviderOllama, err) } return m.startConfigOllamaURLWithValue(url) } diff --git a/cmd/chat_config_keys.go b/cmd/chat_config_keys.go index 6033a0e4f..d766204de 100644 --- a/cmd/chat_config_keys.go +++ b/cmd/chat_config_keys.go @@ -7,11 +7,11 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func credentialsStoreLabel() string { - return graycodeconfig.CredentialStoreName() + return hawkconfig.CredentialStoreName() } func configGatewayRemovePrompt(step int, gatewayName string) string { @@ -37,15 +37,15 @@ func (m chatModel) configKeyDetailView() string { accentStyle := configAccentStyle() activeStyle := configActiveStyle() providerName := strings.TrimSpace(m.configProvider) - displayName := graycodeconfig.GatewayDisplayName(providerName) - masked := graycodeconfig.MaskCredentialForProvider(context.Background(), providerName) + 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 reg := graycodeconfig.GatewayRegionLabel(providerName); reg != "" || graycodeconfig.NeedsGatewayRegion(providerName) || graycodeconfig.HasRegionOptions(providerName) { + if reg := hawkconfig.GatewayRegionLabel(providerName); reg != "" || hawkconfig.NeedsGatewayRegion(providerName) || hawkconfig.HasRegionOptions(providerName) { if reg == "" { reg = "(not set — press g)" } @@ -68,32 +68,32 @@ func (m chatModel) startConfigKeyForProvider(provider string) (chatModel, tea.Cm if provider == "" { return m, nil } - if graycodeconfig.NeedsGatewayRegion(provider) { + if hawkconfig.NeedsGatewayRegion(provider) { m.configPostSaveKeysProvider = provider return m.startConfigGatewayRegion(provider), nil } - name := graycodeconfig.GatewayDisplayName(provider) + name := hawkconfig.GatewayDisplayName(provider) m.configNotice = "Paste API key for " + name return m.startConfigEntry(configEntryAPIKeyPaste, provider) } func (m chatModel) startConfigKeyReplace(provider string) (chatModel, tea.Cmd) { - if graycodeconfig.NeedsGatewayRegion(provider) { + if hawkconfig.NeedsGatewayRegion(provider) { m.configPostSaveKeysProvider = provider return m.startConfigGatewayRegion(provider), nil } m.configReplaceProvider = provider m.configEntry = configEntryNone - m.configNotice = "Paste replacement API key for " + graycodeconfig.GatewayDisplayName(provider) + m.configNotice = "Paste replacement API key for " + hawkconfig.GatewayDisplayName(provider) return m.startConfigEntry(configEntryAPIKeyPaste, provider) } func (m chatModel) beginConfigGatewayKeyRemove(provider string) chatModel { m.configKeysPendingRemove = provider m.configKeysRemoveStep = 1 - name := graycodeconfig.GatewayDisplayName(provider) + name := hawkconfig.GatewayDisplayName(provider) m.configNotice = configGatewayRemoveNotice(1, name) return m } @@ -116,7 +116,7 @@ func (m chatModel) advanceConfigGatewayKeyRemove() (chatModel, tea.Cmd) { } if m.configKeysRemoveStep < 2 { m.configKeysRemoveStep = 2 - name := graycodeconfig.GatewayDisplayName(trimmedProvider) + name := hawkconfig.GatewayDisplayName(trimmedProvider) m.configNotice = configGatewayRemoveNotice(2, name) return m, nil } @@ -131,7 +131,7 @@ func (m chatModel) confirmConfigGatewayKeyRemove() (chatModel, tea.Cmd) { m.configKeysPendingRemove = "" m.configKeysRemoveStep = 0 m.configSaving = true - m.configNotice = fmt.Sprintf("Removing key for %s…", graycodeconfig.GatewayDisplayName(trimmedProvider)) + m.configNotice = fmt.Sprintf("Removing key for %s…", hawkconfig.GatewayDisplayName(trimmedProvider)) if m.configEntry == configEntryKeyView { m.configEntry = configEntryNone m.configProvider = "" @@ -164,7 +164,7 @@ func (m chatModel) handleConfigKeyViewKey(msg tea.KeyMsg) (chatModel, tea.Cmd) { } return m.startConfigKeyReplace(trimmedProvider) default: - if (graycodeconfig.HasRegionOptions(trimmedProvider) || graycodeconfig.GatewayRegionLabel(trimmedProvider) != "") && strings.EqualFold(key.Text, "g") { + if (hawkconfig.HasRegionOptions(trimmedProvider) || hawkconfig.GatewayRegionLabel(trimmedProvider) != "") && strings.EqualFold(key.Text, "g") { return m.startConfigGatewayRegion(trimmedProvider), nil } return m, nil diff --git a/cmd/chat_config_keys_test.go b/cmd/chat_config_keys_test.go index 3f3931a70..15aee677e 100644 --- a/cmd/chat_config_keys_test.go +++ b/cmd/chat_config_keys_test.go @@ -5,22 +5,22 @@ import ( "testing" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestConfigGatewaysView_KeyHintsWithCredentials(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() m := chatModel{configTab: configTabGateways} view := m.configGatewaysView() @@ -30,17 +30,17 @@ func TestConfigGatewaysView_KeyHintsWithCredentials(t *testing.T) { } func TestConfigGatewaysKeyView_OpenWithK(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -61,17 +61,17 @@ func TestConfigGatewaysKeyView_OpenWithK(t *testing.T) { } func TestConfigGatewaysDelete_PendingRemove(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -92,17 +92,17 @@ func TestConfigGatewaysDelete_PendingRemove(t *testing.T) { } func TestConfigGatewaysDelete_DoubleConfirm(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -131,12 +131,12 @@ func TestConfigGatewaysDelete_DoubleConfirm(t *testing.T) { } func TestOpenConfigRemoveKeyPanel_OpensGatewaysTab(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{} diff --git a/cmd/chat_config_models.go b/cmd/chat_config_models.go index 2ba2328ca..44522f1f0 100644 --- a/cmd/chat_config_models.go +++ b/cmd/chat_config_models.go @@ -7,11 +7,11 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" ) -// configModelOption is one row in the /config model picker (display from graycode-router, id for settings). +// configModelOption is one row in the /config model picker (display from eyrie, id for settings). type configModelOption struct { ID string CanonicalID string @@ -42,7 +42,7 @@ func InvalidateModelCache() { modelSyncAttempted = make(map[string]bool) modelSyncMu.Unlock() invalidatePlatformContextCache() - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() } // InvalidateModelCacheProvider drops one gateway's cached picker rows. @@ -54,7 +54,7 @@ func InvalidateModelCacheProvider(provider string) { modelSyncMu.Lock() delete(modelSyncAttempted, provider) modelSyncMu.Unlock() - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() } func fetchModelsAsync(provider string) tea.Cmd { @@ -62,19 +62,19 @@ func fetchModelsAsync(provider string) tea.Cmd { ctx := context.Background() provider = strings.TrimSpace(provider) if provider == "" { - provider = graycodeconfig.DefaultModelProviderFilter(ctx) + provider = hawkconfig.DefaultModelProviderFilter(ctx) } - entries, err := graycodeconfig.ListEngineModels(ctx, provider, false) + entries, err := hawkconfig.ListEngineModels(ctx, provider, false) if err != nil { - if _, derr := graycodeconfig.ListEngineModels(ctx, provider, true); derr == nil { + if _, derr := hawkconfig.ListEngineModels(ctx, provider, true); derr == nil { InvalidateModelCacheProvider(provider) - entries, err = graycodeconfig.ListEngineModels(ctx, provider, false) + entries, err = hawkconfig.ListEngineModels(ctx, provider, false) } } if err != nil { return modelsFetchedMsg{provider: provider, err: err} } - opts := configModelOptionsFromGraycodeRouter(entries) + opts := configModelOptionsFromEyrie(entries) if len(opts) > 0 { modelCacheMu.Lock() modelCache[provider] = opts @@ -84,7 +84,7 @@ func fetchModelsAsync(provider string) tea.Cmd { } } -func configModelOptionsFromGraycodeRouter(entries []graycodeconfig.EngineModel) []configModelOption { +func configModelOptionsFromEyrie(entries []hawkconfig.EngineModel) []configModelOption { opts := make([]configModelOption, len(entries)) for i, e := range entries { opts[i] = configModelOption{ @@ -168,10 +168,10 @@ func ensureModelCacheLoaded(provider string) { modelSyncMu.Unlock() ctx := context.Background() - entries, err := graycodeconfig.ListEngineModels(ctx, provider, false) + entries, err := hawkconfig.ListEngineModels(ctx, provider, false) if err != nil { - if _, derr := graycodeconfig.ListEngineModels(ctx, provider, true); derr == nil { - entries, err = graycodeconfig.ListEngineModels(ctx, provider, false) + if _, derr := hawkconfig.ListEngineModels(ctx, provider, true); derr == nil { + entries, err = hawkconfig.ListEngineModels(ctx, provider, false) } } if err != nil || len(entries) == 0 { @@ -180,7 +180,7 @@ func ensureModelCacheLoaded(provider string) { modelSyncMu.Unlock() return } - opts := configModelOptionsFromGraycodeRouter(entries) + opts := configModelOptionsFromEyrie(entries) modelCacheMu.Lock() modelCache[provider] = opts modelCacheMu.Unlock() @@ -250,9 +250,9 @@ func loadConfigModelOptions(provider string) []configModelOption { return cached } modelCacheMu.RUnlock() - entries, err := graycodeconfig.ListEngineModels(context.Background(), provider, false) + entries, err := hawkconfig.ListEngineModels(context.Background(), provider, false) if err == nil && len(entries) > 0 { - opts := configModelOptionsFromGraycodeRouter(entries) + opts := configModelOptionsFromEyrie(entries) modelCacheMu.Lock() modelCache[provider] = opts modelCacheMu.Unlock() diff --git a/cmd/chat_config_models_test.go b/cmd/chat_config_models_test.go index 95ae657d5..c23732e1b 100644 --- a/cmd/chat_config_models_test.go +++ b/cmd/chat_config_models_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestFilterConfigModelOptions(t *testing.T) { @@ -45,7 +45,7 @@ func TestModelOptionIsActive(t *testing.T) { } func TestConfigModelOptionsCarryResolvedEngineIdentity(t *testing.T) { - opts := configModelOptionsFromGraycodeRouter([]graycodeconfig.EngineModel{{ + opts := configModelOptionsFromEyrie([]hawkconfig.EngineModel{{ ID: "models/gemini-pro", CanonicalID: "google/gemini-pro", ProviderID: "google", GatewayID: "gemini", Capabilities: []string{"tools", "vision"}, }}) diff --git a/cmd/chat_config_panel.go b/cmd/chat_config_panel.go index 0c1ea4881..006940f5c 100644 --- a/cmd/chat_config_panel.go +++ b/cmd/chat_config_panel.go @@ -9,8 +9,8 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func configModelChoices(opts []configModelOption, showProvider bool) []string { @@ -87,11 +87,11 @@ func (m chatModel) configProviderKeyView() string { title := icons.Key() + " Paste API key" hint := "validates with provider API · stored in " + credentialsStoreLabel() if providerName != "" { - title = icons.Key() + " " + graycodeconfig.GatewayDisplayName(providerName) + title = icons.Key() + " " + hawkconfig.GatewayDisplayName(providerName) hint = "paste key for this gateway only · " + hint } - if providerName == graycodeconfig.ProviderXiaomiTokenPlan { - reg := graycodeconfig.GatewayRegionLabel(graycodeconfig.ProviderXiaomiTokenPlan) + if providerName == hawkconfig.ProviderXiaomiTokenPlan { + reg := hawkconfig.GatewayRegionLabel(hawkconfig.ProviderXiaomiTokenPlan) if reg == "" { reg = "not set — esc and pick region with g or enter on gateway row" } @@ -116,7 +116,7 @@ func (m chatModel) configOllamaURLView() string { var b strings.Builder b.WriteString(titleStyle.Render(icons.Llama()+" Ollama local") + "\n") - b.WriteString(mutedStyle.Render("no API key · graycode-router discovers installed models") + "\n\n") + b.WriteString(mutedStyle.Render("no API key · eyrie discovers installed models") + "\n\n") if notice := strings.TrimSpace(m.configNotice); notice != "" { b.WriteString(mutedStyle.Render(sanitizeConfigNotice(notice)) + "\n\n") } @@ -198,7 +198,7 @@ func (m chatModel) configModelsTabView() string { gw = strings.TrimSpace(m.session.Provider()) } if gw != "" { - body.WriteString(renderConfigGatewayLine(graycodeconfig.GatewayDisplayName(gw)) + "\n\n") + body.WriteString(renderConfigGatewayLine(hawkconfig.GatewayDisplayName(gw)) + "\n\n") } if len(m.configModelOptions) > 0 || m.configModelSearchActive { @@ -245,15 +245,15 @@ func (m chatModel) startConfigModelSearch() (chatModel, tea.Cmd) { m.configInput.EchoCharacter = 0 m.configInput.SetStyles(textinput.Styles{ Focused: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: graycodeColor, + Color: hawkColor, }, }) m.configInput.Focus() @@ -333,11 +333,11 @@ func (m chatModel) configActiveModelID() string { return modelName } } - return strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) + return strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) } func modelOptionIsActive(opt configModelOption, activeModelID string) bool { - return modelOptionIsActiveResolved(opt, activeModelID, graycodeconfig.CanonicalModelID(context.Background(), activeModelID)) + return modelOptionIsActiveResolved(opt, activeModelID, hawkconfig.CanonicalModelID(context.Background(), activeModelID)) } func modelOptionIsActiveResolved(opt configModelOption, activeModelID, activeCanonicalID string) bool { @@ -364,7 +364,7 @@ func (m chatModel) focusConfigActiveModelSelection() chatModel { return m } activeID := m.configActiveModelID() - activeCanonicalID := graycodeconfig.CanonicalModelID(context.Background(), activeID) + activeCanonicalID := hawkconfig.CanonicalModelID(context.Background(), activeID) windowSize := m.configVisibleRows() for i, opt := range opts { if modelOptionIsActiveResolved(opt, activeID, activeCanonicalID) { @@ -405,7 +405,7 @@ func (m chatModel) configModelsBody() string { total := len(opts) allTotal := len(m.configModelOptions) activeModelID := m.configActiveModelID() - activeCanonicalID := graycodeconfig.CanonicalModelID(context.Background(), activeModelID) + activeCanonicalID := hawkconfig.CanonicalModelID(context.Background(), activeModelID) windowSize := m.configVisibleRows() maxScroll := maxInt(0, total-windowSize) if m.configScroll > maxScroll { @@ -446,7 +446,7 @@ func (m chatModel) configModelsBody() string { return b.String() } b.WriteString(mutedStyle.Render(" No models available.") + "\n") - if hint := graycodeconfig.CatalogEmptyHint(context.Background()); hint != "" { + if hint := hawkconfig.CatalogEmptyHint(context.Background()); hint != "" { b.WriteString(mutedStyle.Render(" "+hint) + "\n") } if gw == configProviderOllama { @@ -523,15 +523,15 @@ func (m chatModel) startConfigEntry(kind, provider string) (chatModel, tea.Cmd) m.configInput.EchoCharacter = '*' m.configInput.SetStyles(textinput.Styles{ Focused: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: graycodeColor, + Color: hawkColor, }, }) m.configInput.Focus() @@ -580,7 +580,7 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { m.restoreChatInput() return m, nil } - if providerName == graycodeconfig.ProviderXiaomiTokenPlan && graycodeconfig.NeedsGatewayRegion(providerName) { + if providerName == hawkconfig.ProviderXiaomiTokenPlan && hawkconfig.NeedsGatewayRegion(providerName) { m.configEntry = configEntryNone m.wipeConfigKeyInput() m.restoreChatInput() @@ -592,7 +592,7 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { m.configProvider = "" m.wipeConfigKeyInput() m.restoreChatInput() - inference, err := graycodeconfig.CredentialInferenceForProvider(providerName) + inference, err := hawkconfig.CredentialInferenceForProvider(providerName) if err != nil { m.configTab = configTabGateways m.configNotice = "Could not save key: " + sanitizeConfigNotice(err.Error()) @@ -604,7 +604,7 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { m.configPostSaveKeysProvider = providerName m.configSaving = true notice := fmt.Sprintf("Validating key for %s…", inference.DisplayName) - if hint := graycodeconfig.CredentialGuidance(providerName, value); hint != "" { + if hint := hawkconfig.CredentialGuidance(providerName, value); hint != "" { notice = hint + " · " + notice } m.configNotice = notice @@ -867,7 +867,7 @@ func (m chatModel) handleConfigKey(msg tea.KeyMsg) (chatModel, tea.Cmd) { return m.startConfigKeyView(row.ID), nil } case "g", "G": - if row, ok := m.selectedConfigGateway(); ok && row.ID == graycodeconfig.ProviderXiaomiTokenPlan { + if row, ok := m.selectedConfigGateway(); ok && row.ID == hawkconfig.ProviderXiaomiTokenPlan { return m.startConfigGatewayRegion(row.ID), nil } } @@ -960,7 +960,7 @@ func (m chatModel) selectConfigModelFromOptions(opts []configModelOption) (chatM } selected := opts[m.configSel] modelID := selected.ID - if err := graycodeconfig.SetGlobalSetting("model", modelID); err != nil { + if err := hawkconfig.SetGlobalSetting("model", modelID); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m.closeConfigPanel(), nil } @@ -976,7 +976,7 @@ func (m chatModel) selectConfigModelFromOptions(opts []configModelOption) (chatM provider = prov } if provider != "" { - if err := graycodeconfig.SetGlobalSetting("provider", provider); err != nil { + if err := hawkconfig.SetGlobalSetting("provider", provider); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: "model set, but saving provider failed: " + err.Error()}) } } @@ -986,7 +986,7 @@ func (m chatModel) selectConfigModelFromOptions(opts []configModelOption) (chatM next.invalidateConnStatus() next = next.stopConfigModelSearch(true) next = next.closeConfigPanel() - if !graycodeconfig.EvaluateSetupCached(context.Background()).NeedsSetup { + if !hawkconfig.EvaluateSetupCached(context.Background()).NeedsSetup { next.messages = append(next.messages, displayMsg{ role: "setup_complete", content: next.session.Model(), @@ -1001,19 +1001,19 @@ func (m chatModel) toggleConfigModelThinking() (chatModel, tea.Cmd) { return m, nil } selected := opts[m.configSel] - if !graycodeconfig.ModelCapabilitySupportsThinking(selected.Capabilities) { + if !hawkconfig.ModelCapabilitySupportsThinking(selected.Capabilities) { m.configNotice = "Thinking not supported for this model" return m, nil } - settings := graycodeconfig.LoadSettings() - pref := graycodeconfig.ThinkingPrefForModel(settings, selected.ID) + settings := hawkconfig.LoadSettings() + pref := hawkconfig.ThinkingPrefForModel(settings, selected.ID) // Current effective display: unset → off. Toggle flips that. currentlyOn := false if pref != nil { currentlyOn = *pref } next := !currentlyOn - if err := graycodeconfig.SetModelThinking(selected.ID, next); err != nil { + if err := hawkconfig.SetModelThinking(selected.ID, next); err != nil { m.configNotice = err.Error() return m, nil } @@ -1027,7 +1027,7 @@ func (m chatModel) toggleConfigModelThinking() (chatModel, tea.Cmd) { m.configNotice = label + " thinking → off" } // If this is the active model, apply immediately. - if m.session != nil && modelOptionIsActiveResolved(selected, m.configActiveModelID(), graycodeconfig.CanonicalModelID(context.Background(), m.configActiveModelID())) { + if m.session != nil && modelOptionIsActiveResolved(selected, m.configActiveModelID(), hawkconfig.CanonicalModelID(context.Background(), m.configActiveModelID())) { m.session.SetThinkingEnabled(&next) } return m, nil @@ -1037,7 +1037,7 @@ func (m chatModel) applyModelThinkingPref(selected configModelOption) { if m.session == nil { return } - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() provider := strings.TrimSpace(m.configModelProvider) if provider == "" { provider = strings.TrimSpace(selected.GatewayID) @@ -1045,5 +1045,5 @@ func (m chatModel) applyModelThinkingPref(selected configModelOption) { if provider == "" { provider = strings.TrimSpace(selected.ProviderID) } - m.session.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(settings, selected.ID, provider)) + m.session.SetThinkingEnabled(hawkconfig.ResolveThinkingForModel(settings, selected.ID, provider)) } diff --git a/cmd/chat_config_region.go b/cmd/chat_config_region.go index c8c22898a..418842569 100644 --- a/cmd/chat_config_region.go +++ b/cmd/chat_config_region.go @@ -7,7 +7,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func gatewayRegionOptionIndex(providerID, region string) int { @@ -15,7 +15,7 @@ func gatewayRegionOptionIndex(providerID, region string) int { if region == "" { return 0 } - opts := graycodeconfig.GatewayRegionOptions(providerID) + opts := hawkconfig.GatewayRegionOptions(providerID) for i, r := range opts { if strings.EqualFold(r.Value, region) { return i @@ -32,23 +32,23 @@ func (m chatModel) closeConfigEntry() chatModel { func (m chatModel) startConfigGatewayRegion(providerID string) chatModel { switch providerID { - case graycodeconfig.ProviderXiaomiTokenPlan: + case hawkconfig.ProviderXiaomiTokenPlan: m.configEntry = configEntryXiaomiRegion - case graycodeconfig.ProviderZAICoding, graycodeconfig.ProviderZAIPayg: + case hawkconfig.ProviderZAICoding, hawkconfig.ProviderZAIPayg: m.configEntry = configEntryZAIRegion default: m.configEntry = configEntryGatewayRegion } m.configProvider = providerID idx := 0 - if !graycodeconfig.NeedsGatewayRegion(providerID) { - idx = gatewayRegionOptionIndex(providerID, graycodeconfig.GatewayRegionLabel(providerID)) + if !hawkconfig.NeedsGatewayRegion(providerID) { + idx = gatewayRegionOptionIndex(providerID, hawkconfig.GatewayRegionLabel(providerID)) } m.configGatewayRegionSel = idx m.configZAIRegionSel = idx - name := graycodeconfig.GatewayDisplayName(providerID) + name := hawkconfig.GatewayDisplayName(providerID) notice := fmt.Sprintf("Select %s region (↑↓ · enter · esc cancel)", name) - if saved := graycodeconfig.GatewayRegionLabel(providerID); saved != "" { + if saved := hawkconfig.GatewayRegionLabel(providerID); saved != "" { notice = fmt.Sprintf("%s region · current %s (↑↓ · enter · esc cancel)", name, saved) } m.configNotice = notice @@ -61,9 +61,9 @@ func (m chatModel) configGatewayRegionView() string { rowStyle := configRowStyle() var b strings.Builder prov := m.configProvider - name := graycodeconfig.GatewayDisplayName(prov) + name := hawkconfig.GatewayDisplayName(prov) b.WriteString(renderConfigBreadcrumb(name+" region") + "\n\n") - opts := graycodeconfig.GatewayRegionOptions(prov) + opts := hawkconfig.GatewayRegionOptions(prov) for i, r := range opts { prefix := " " if i == m.configGatewayRegionSel { @@ -84,7 +84,7 @@ func (m chatModel) configGatewayRegionView() string { } func (m chatModel) handleConfigGatewayRegionKey(msg tea.KeyMsg) (chatModel, tea.Cmd) { - opts := graycodeconfig.GatewayRegionOptions(m.configProvider) + opts := hawkconfig.GatewayRegionOptions(m.configProvider) count := len(opts) if count == 0 { return m.closeConfigEntry(), nil @@ -105,7 +105,7 @@ func (m chatModel) handleConfigGatewayRegionKey(msg tea.KeyMsg) (chatModel, tea. case "enter": if m.configGatewayRegionSel >= 0 && m.configGatewayRegionSel < count { chosen := opts[m.configGatewayRegionSel] - if err := graycodeconfig.SetGatewayRegion(m.configProvider, chosen.Value); err != nil { + if err := hawkconfig.SetGatewayRegion(m.configProvider, chosen.Value); err != nil { m.configNotice = "Error saving region: " + err.Error() return m, nil } @@ -116,8 +116,8 @@ func (m chatModel) handleConfigGatewayRegionKey(msg tea.KeyMsg) (chatModel, tea. m.configPostSaveKeysProvider = "" return m.startConfigKeyReplace(m.configProvider) } - if graycodeconfig.HasStoredCredentialForProvider(ctx, m.configProvider) { - m.configNotice = "Saved region for " + graycodeconfig.GatewayDisplayName(m.configProvider) + if hawkconfig.HasStoredCredentialForProvider(ctx, m.configProvider) { + m.configNotice = "Saved region for " + hawkconfig.GatewayDisplayName(m.configProvider) if idx := m.configGatewayRowIndex(m.configProvider); idx >= 0 { m.configSel = idx } diff --git a/cmd/chat_config_remove.go b/cmd/chat_config_remove.go index 8c3c6cbc3..41b53ead5 100644 --- a/cmd/chat_config_remove.go +++ b/cmd/chat_config_remove.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) type configRemoveCredentialMsg struct { @@ -17,7 +17,7 @@ type configRemoveCredentialMsg struct { func removeCredentialAsync(provider string) tea.Cmd { return func() tea.Msg { - removed, err := graycodeconfig.RemoveStoredCredential(context.Background(), provider) + removed, err := hawkconfig.RemoveStoredCredential(context.Background(), provider) return configRemoveCredentialMsg{ provider: provider, removed: removed, @@ -34,10 +34,10 @@ func (m chatModel) handleConfigRemoveCredentialMsg(msg configRemoveCredentialMsg } delete(modelCache, msg.provider) ctx := context.Background() - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() - if graycodeconfig.ShouldClearSelectionAfterCredentialRemove(ctx, msg.provider) { - _ = graycodeconfig.ClearActiveSelection(ctx) + if hawkconfig.ShouldClearSelectionAfterCredentialRemove(ctx, msg.provider) { + _ = hawkconfig.ClearActiveSelection(ctx) m.configModelProvider = "" m.configModelOptions = nil m.session.SetProvider("") @@ -46,8 +46,8 @@ func (m chatModel) handleConfigRemoveCredentialMsg(msg configRemoveCredentialMsg m.configTab = configTabGateways m.configSel = 0 m.configScroll = 0 - m.configNotice = fmt.Sprintf("Removed API key for %s", graycodeconfig.GatewayDisplayName(msg.provider)) - if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + m.configNotice = fmt.Sprintf("Removed API key for %s", hawkconfig.GatewayDisplayName(msg.provider)) + if !hawkconfig.HasConfiguredDeploymentCached(ctx) { m.configNotice += " — add an API key to continue" } next, cmd := m.rebuildSessionTransport() @@ -59,7 +59,7 @@ func (m chatModel) handleConfigRemoveCredentialMsg(msg configRemoveCredentialMsg func (m chatModel) openConfigRemoveKeyPanel() (chatModel, tea.Cmd) { next, cmd := m.openConfigAtTab(configTabGateways) - if len(graycodeconfig.ConfiguredCredentialProviders()) == 0 { + if len(hawkconfig.ConfiguredCredentialProviders()) == 0 { next.configNotice = "No stored API keys" } return next, cmd diff --git a/cmd/chat_config_remove_test.go b/cmd/chat_config_remove_test.go index 45cefedbb..ae83e0fde 100644 --- a/cmd/chat_config_remove_test.go +++ b/cmd/chat_config_remove_test.go @@ -4,22 +4,22 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestConfigGatewayRows_ShowsSavedKey(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() var found bool @@ -35,36 +35,36 @@ func TestConfigGatewayRows_ShowsSavedKey(t *testing.T) { } func TestConfiguredCredentialProviders_UsedByGatewaysTab(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() - got := graycodeconfig.ConfiguredCredentialProviders() + got := hawkconfig.ConfiguredCredentialProviders() if len(got) != 1 || got[0] != "openrouter" { t.Fatalf("configured = %v", got) } } func TestRemoveCredentialAsync(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() cmd := removeCredentialAsync("openrouter") if cmd == nil { diff --git a/cmd/chat_config_save_flow_test.go b/cmd/chat_config_save_flow_test.go index 7ddb41d48..3a4442614 100644 --- a/cmd/chat_config_save_flow_test.go +++ b/cmd/chat_config_save_flow_test.go @@ -8,25 +8,25 @@ import ( "testing" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-router/catalog" - "github.com/GrayCodeAI/graycode-router/credentials" + "github.com/GrayCodeAI/eyrie/catalog" + "github.com/GrayCodeAI/eyrie/credentials" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestFinishConfigEntry_APIKeyPaste_SavesBeforeProbe(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) oldTransport := http.DefaultTransport http.DefaultTransport = configSaveTestTransport(http.StatusUnauthorized) t.Cleanup(func() { http.DefaultTransport = oldTransport }) - if _, err := graycodeconfig.CredentialInferenceForProvider("xiaomi_mimo_payg"); err != nil { + if _, err := hawkconfig.CredentialInferenceForProvider("xiaomi_mimo_payg"); err != nil { t.Fatalf("CredentialInferenceForProvider: %v", err) } @@ -111,7 +111,7 @@ func TestConfigGatewaysSelect_AddKeyOpensPaste(t *testing.T) { } } if sel < 0 { - t.Skip("all gateways already have keys in this environment") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/28 + t.Skip("all gateways already have keys in this environment") // TODO: https://github.com/GrayCodeAI/hawk/issues/28 } m.configSel = sel next, _ := m.handleConfigGatewaysSelect() @@ -134,7 +134,7 @@ func configSaveTestTransport(status int) http.RoundTripper { }) } -// roundTripFunc is defined in graycode-router/runtime tests; duplicate here for graycode cmd tests. +// roundTripFunc is defined in eyrie/runtime tests; duplicate here for hawk cmd tests. type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { @@ -142,12 +142,12 @@ func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { } func TestHandleConfigKey_EnterOnPasteSubmits(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) oldTransport := http.DefaultTransport http.DefaultTransport = configSaveTestTransport(http.StatusUnauthorized) @@ -197,12 +197,12 @@ func TestStartConfigEntry_APIKeyPasteHasNoPlaceholder(t *testing.T) { } func TestHandleConfigApplyCredentialsMsg_CatalogFailureDoesNotBlameProvider(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), credentials.AccountForEnv("POOLSIDE_API_KEY"), "poolside-test-key"); err != nil { t.Fatalf("store key: %v", err) @@ -218,21 +218,21 @@ func TestHandleConfigApplyCredentialsMsg_CatalogFailureDoesNotBlameProvider(t *t t.Fatalf("catalog failure blamed provider key: %q", next.configNotice) } if !strings.Contains(next.configNotice, "model catalog unavailable") || - !strings.Contains(next.configNotice, "graycode models refresh") { + !strings.Contains(next.configNotice, "hawk models refresh") { t.Fatalf("catalog recovery guidance missing: %q", next.configNotice) } } -// Skipped: integration test requiring specific graycode-router model catalog state +// Skipped: integration test requiring specific eyrie model catalog state func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t *testing.T) { - // TODO: enable once graycode-router catalog fixtures pin the claude-fable-5 model state. - t.Skip("requires specific graycode-router model catalog state (claude-fable-5)") - graycodeconfig.InvalidateConfigUICache() + // TODO: enable once eyrie catalog fixtures pin the claude-fable-5 model state. + t.Skip("requires specific eyrie model catalog state (claude-fable-5)") + hawkconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), credentials.AccountForEnv("CONCENTRATE_API_KEY"), "concentrate-test-key"); err != nil { t.Fatalf("store key: %v", err) @@ -252,16 +252,16 @@ func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t } } -// Skipped: integration test requiring specific graycode-router model catalog state +// Skipped: integration test requiring specific eyrie model catalog state func TestHandleConfigApplyCredentialsMsg_AuthenticationFailureBlamesKey(t *testing.T) { - // TODO: enable once graycode-router catalog fixtures pin the auth-failure model state. - t.Skip("requires specific graycode-router model catalog state") - graycodeconfig.InvalidateConfigUICache() + // TODO: enable once eyrie catalog fixtures pin the auth-failure model state. + t.Skip("requires specific eyrie model catalog state") + hawkconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), credentials.AccountForEnv("CONCENTRATE_API_KEY"), "concentrate-test-key"); err != nil { t.Fatalf("store key: %v", err) diff --git a/cmd/chat_config_security.go b/cmd/chat_config_security.go index 685984038..aa8d10e41 100644 --- a/cmd/chat_config_security.go +++ b/cmd/chat_config_security.go @@ -4,7 +4,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) var ( diff --git a/cmd/chat_config_tabs.go b/cmd/chat_config_tabs.go index 01c58d2e1..7ac1a93e5 100644 --- a/cmd/chat_config_tabs.go +++ b/cmd/chat_config_tabs.go @@ -7,20 +7,20 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func (m chatModel) configStatus() (gateway, model string, configured bool) { ctx := context.Background() - if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + if !hawkconfig.HasConfiguredDeploymentCached(ctx) { return "none", "", false } gw := strings.TrimSpace(m.configModelProvider) - if gw != "" && graycodeconfig.IsSetupGateway(gw) { - gw = graycodeconfig.GatewayDisplayName(gw) - } else if active := graycodeconfig.ActiveGateway(ctx); active != "" { - gw = graycodeconfig.GatewayDisplayName(active) + if gw != "" && hawkconfig.IsSetupGateway(gw) { + gw = hawkconfig.GatewayDisplayName(gw) + } else if active := hawkconfig.ActiveGateway(ctx); active != "" { + gw = hawkconfig.GatewayDisplayName(active) } else { gw = "none" } @@ -28,7 +28,7 @@ func (m chatModel) configStatus() (gateway, model string, configured bool) { model = strings.TrimSpace(m.session.Model()) } if model == "" { - model = strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) + model = strings.TrimSpace(hawkconfig.ActiveModel(ctx)) } return gw, model, true } @@ -44,7 +44,7 @@ func configTabDot(active bool) string { func configTabLabelStyle(active bool) lipgloss.Style { if active { - return lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + return lipgloss.NewStyle().Foreground(hawkColor).Bold(true) } return configMutedStyle() } @@ -90,7 +90,7 @@ func (m chatModel) switchConfigTab(tab int) (chatModel, tea.Cmd) { m = m.stopConfigModelSearch(true) } ctx := context.Background() - if tab == configTabModels && !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + if tab == configTabModels && !hawkconfig.HasConfiguredDeploymentCached(ctx) { tab = configTabGateways m.configNotice = "Select a gateway first · enter · paste API key" m = m.focusConfigActiveGateway() @@ -122,9 +122,9 @@ func (m chatModel) openConfigAtTab(tab int) (chatModel, tea.Cmd) { m.configSel = 0 m.configScroll = 0 m.viewDirty = true - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() - setup := graycodeconfig.EvaluateSetupCached(ctx) + setup := hawkconfig.EvaluateSetupCached(ctx) if tab < 0 { if setup.HasCredentials { diff --git a/cmd/chat_config_tabs_test.go b/cmd/chat_config_tabs_test.go index fd2974e1e..743e77873 100644 --- a/cmd/chat_config_tabs_test.go +++ b/cmd/chat_config_tabs_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestRenderConfigTabBar_DotIndicators(t *testing.T) { @@ -28,12 +28,12 @@ func TestRenderConfigTabBar_DotIndicators(t *testing.T) { } func TestOpenConfigPanel_FirstRunOpensGateways(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{} @@ -47,12 +47,12 @@ func TestOpenConfigPanel_FirstRunOpensGateways(t *testing.T) { } func TestOpenConfigAtTab_ModelsWithoutCredentialsFallsBackToGateways(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{} diff --git a/cmd/chat_config_ui.go b/cmd/chat_config_ui.go index e107c0cb2..30cdbaa86 100644 --- a/cmd/chat_config_ui.go +++ b/cmd/chat_config_ui.go @@ -4,7 +4,7 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func configMutedStyle() lipgloss.Style { @@ -13,19 +13,19 @@ func configMutedStyle() lipgloss.Style { func configTitleStyle() lipgloss.Style { // Talon Gold — title is the voice of the config panel. - return lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + return lipgloss.NewStyle().Foreground(hawkColor).Bold(true) } func configSelectedStyle() lipgloss.Style { // Talon Gold (bold) marks the focused row. Keep it distinct from // the active/current value, which uses configActiveStyle. - return lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + return lipgloss.NewStyle().Foreground(hawkColor).Bold(true) } func configAccentStyle() lipgloss.Style { // Accent for inline highlights (breadcrumb, status line values). // Same as title — both are the panel's voice. - return lipgloss.NewStyle().Foreground(graycodeColor) + return lipgloss.NewStyle().Foreground(hawkColor) } func renderConfigBreadcrumb(title string) string { @@ -109,7 +109,7 @@ func configNoticeStyle(notice string) lipgloss.Style { strings.Contains(n, "unauthorized"): return lipgloss.NewStyle().Foreground(errorCoral) case strings.HasPrefix(notice, "Refreshed"), - strings.HasPrefix(notice, "GraycodeRouter:"), + strings.HasPrefix(notice, "Eyrie:"), strings.Contains(notice, "Removed API key"), strings.Contains(notice, "Setup complete"), strings.Contains(n, "key saved"), diff --git a/cmd/chat_copy_e2e_test.go b/cmd/chat_copy_e2e_test.go index 27c2ad78a..94a776eda 100644 --- a/cmd/chat_copy_e2e_test.go +++ b/cmd/chat_copy_e2e_test.go @@ -8,7 +8,7 @@ import ( "charm.land/bubbles/v2/viewport" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // runCopySelectionE2EPass exercises chat + input copy/select/mouse flows in one pass. @@ -88,7 +88,7 @@ func runCopySelectionE2EPass(t *testing.T, pass int) { } // --- Mouse toggle (OpenCode-style) --- - t.Setenv("GRAYCODE_MOUSE", "") + t.Setenv("HAWK_MOUSE", "") m.handleMouseCommand([]string{"/mouse", "off"}) if m.mouseEnabled() { t.Fatalf("pass %d: expected mouse off after /mouse off", pass) @@ -109,13 +109,13 @@ func runCopySelectionE2EPass(t *testing.T, pass int) { } // --- Pass B: assistant reply path --- - m.messages = append(m.messages, displayMsg{role: "assistant", content: "Hello from graycode"}) + m.messages = append(m.messages, displayMsg{role: "assistant", content: "Hello from hawk"}) m.input.SetValue("") - if content, _, got := m.copyContent(copyModeAssistant); !got || content != "Hello from graycode" { + if content, _, got := m.copyContent(copyModeAssistant); !got || content != "Hello from hawk" { t.Fatalf("pass %d: /copy assistant content = %q got=%v", pass, content, got) } - if line, got := m.lastMessageContent(); !got || !strings.Contains(line, "Hello from graycode") { + if line, got := m.lastMessageContent(); !got || !strings.Contains(line, "Hello from hawk") { t.Fatalf("pass %d: last message = %q got=%v", pass, line, got) } @@ -128,7 +128,7 @@ func runCopySelectionE2EPass(t *testing.T, pass int) { // Settings-backed mouse default disabled := false - m2 := chatModel{settings: graycodeconfig.Settings{TuiMouse: &disabled}} + m2 := chatModel{settings: hawkconfig.Settings{TuiMouse: &disabled}} if m2.mouseEnabled() { t.Fatalf("pass %d: settings tui_mouse=false should disable capture", pass) } diff --git a/cmd/chat_copy_test.go b/cmd/chat_copy_test.go index 36b72c158..f143ee374 100644 --- a/cmd/chat_copy_test.go +++ b/cmd/chat_copy_test.go @@ -7,7 +7,7 @@ import ( "charm.land/bubbles/v2/textarea" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestCopyableTranscript_IncludesInputDraft(t *testing.T) { @@ -80,24 +80,24 @@ func TestIsCopyToClipboardKey(t *testing.T) { } func TestMouseEnabled_SettingsAndEnv(t *testing.T) { - t.Setenv("GRAYCODE_MOUSE", "") + t.Setenv("HAWK_MOUSE", "") if !(chatModel{}).mouseEnabled() { t.Fatal("expected mouse capture to default on") } disabled := false - m := chatModel{settings: graycodeconfig.Settings{TuiMouse: &disabled}} + m := chatModel{settings: hawkconfig.Settings{TuiMouse: &disabled}} if m.mouseEnabled() { t.Fatal("expected settings tui_mouse=false to disable capture") } - t.Setenv("GRAYCODE_MOUSE", "0") + t.Setenv("HAWK_MOUSE", "0") if m.mouseEnabled() { - t.Fatal("expected GRAYCODE_MOUSE=0 to disable capture") + t.Fatal("expected HAWK_MOUSE=0 to disable capture") } - t.Setenv("GRAYCODE_MOUSE", "1") + t.Setenv("HAWK_MOUSE", "1") if !m.mouseEnabled() { - t.Fatal("expected GRAYCODE_MOUSE=1 to enable capture") + t.Fatal("expected HAWK_MOUSE=1 to enable capture") } } diff --git a/cmd/chat_errors_test.go b/cmd/chat_errors_test.go index a26650a92..8d20c8c30 100644 --- a/cmd/chat_errors_test.go +++ b/cmd/chat_errors_test.go @@ -12,7 +12,7 @@ func TestFriendlyError(t *testing.T) { err string contains string }{ - {"rate limit 429", "graycode-router: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, + {"rate limit 429", "eyrie: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, {"unauthorized 401", "HTTP 401 Unauthorized", "Authentication failed"}, {"forbidden 403", "HTTP 403 Forbidden", "Access denied"}, {"not found 404", "model not found", "/model"}, diff --git a/cmd/chat_export.go b/cmd/chat_export.go index dafafdd8a..54b51a3fe 100644 --- a/cmd/chat_export.go +++ b/cmd/chat_export.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" ) func writeRedactedChatMarkdownExport(m *chatModel) (string, error) { @@ -160,7 +160,7 @@ func exportSessionTxt(m *chatModel) (string, error) { return "", fmt.Errorf("no active session") } var b strings.Builder - b.WriteString(fmt.Sprintf("Graycode Session: %s\n", m.sessionID)) + b.WriteString(fmt.Sprintf("Hawk Session: %s\n", m.sessionID)) b.WriteString(fmt.Sprintf("Model: %s/%s\n", m.session.Provider(), m.session.Model())) b.WriteString(fmt.Sprintf("Exported: %s\n\n", time.Now().Format(time.RFC3339))) b.WriteString(strings.Repeat("=", 60) + "\n\n") diff --git a/cmd/chat_focus.go b/cmd/chat_focus.go index dd5c78984..09a0ec282 100644 --- a/cmd/chat_focus.go +++ b/cmd/chat_focus.go @@ -9,9 +9,9 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // UI focus areas — Grok Build cycles Prompt ↔ Scrollback (Tab). diff --git a/cmd/chat_history_search.go b/cmd/chat_history_search.go index d4dfb4da8..d9dafe061 100644 --- a/cmd/chat_history_search.go +++ b/cmd/chat_history_search.go @@ -5,8 +5,8 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/textutil" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/textutil" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/mattn/go-runewidth" ) diff --git a/cmd/chat_journey_e2e_test.go b/cmd/chat_journey_e2e_test.go index e67b1366e..019b5df60 100644 --- a/cmd/chat_journey_e2e_test.go +++ b/cmd/chat_journey_e2e_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func requireChatModel(t *testing.T, model any) *chatModel { @@ -23,20 +23,20 @@ func requireChatModel(t *testing.T, model any) *chatModel { } func TestChatJourney_ConfigPermissionsAndCoreCommands(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatal(err) } - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() m := newTestChatModel() diff --git a/cmd/chat_layout_mouse_test.go b/cmd/chat_layout_mouse_test.go index 287022a10..35201db84 100644 --- a/cmd/chat_layout_mouse_test.go +++ b/cmd/chat_layout_mouse_test.go @@ -14,7 +14,7 @@ func TestView_LineCountMatchesHeight(t *testing.T) { m := chatModel{ height: 24, width: 80, - welcomeCache: "GRAYCODE LOGO\nv0.1.0", + welcomeCache: "HAWK LOGO\nv0.1.0", input: textarea.New(), viewport: viewport.New(viewport.WithWidth(80), viewport.WithHeight(8)), ghostText: NewGhostText(), diff --git a/cmd/chat_layout_test.go b/cmd/chat_layout_test.go index 0b30e1241..9a84817a8 100644 --- a/cmd/chat_layout_test.go +++ b/cmd/chat_layout_test.go @@ -12,7 +12,7 @@ func TestView_PinsWelcomeAboveViewport(t *testing.T) { m := chatModel{ height: 24, width: 80, - welcomeCache: "GRAYCODE LOGO\nv0.1.0", + welcomeCache: "HAWK LOGO\nv0.1.0", input: textarea.New(), viewport: viewport.New(viewport.WithWidth(80), viewport.WithHeight(8)), ghostText: NewGhostText(), @@ -21,7 +21,7 @@ func TestView_PinsWelcomeAboveViewport(t *testing.T) { m.viewDirty = true m.updateViewportContent() got := m.View().Content - if !strings.Contains(got, "GRAYCODE LOGO") { + if !strings.Contains(got, "HAWK LOGO") { t.Fatalf("welcome should be pinned at top, got prefix: %q", got[:min(40, len(got))]) } if !strings.Contains(got, "Docker:") { @@ -33,20 +33,20 @@ func TestPrimeInitialViewportContent_RendersWelcomeBeforeFirstFrame(t *testing.T m := chatModel{ height: 24, width: 80, - welcomeCache: "GRAYCODE LOGO\nv0.1.0", + welcomeCache: "HAWK LOGO\nv0.1.0", input: textarea.New(), viewport: viewport.New(viewport.WithWidth(80), viewport.WithHeight(8)), ghostText: NewGhostText(), } m = m.withSyncedLayout() - if strings.Contains(m.viewport.View(), "GRAYCODE LOGO") { + if strings.Contains(m.viewport.View(), "HAWK LOGO") { t.Fatal("expected empty initial viewport before priming") } m.primeInitialViewportContent() - if !strings.Contains(m.viewport.View(), "GRAYCODE LOGO") { + if !strings.Contains(m.viewport.View(), "HAWK LOGO") { t.Fatalf("expected primed viewport to include welcome content, got %q", m.viewport.View()) } } diff --git a/cmd/chat_model.go b/cmd/chat_model.go index 5eb41e130..389b4ae60 100644 --- a/cmd/chat_model.go +++ b/cmd/chat_model.go @@ -14,16 +14,16 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/bridge/sessioncapture" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/feature/taste" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/system/staleness" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/bridge/sessioncapture" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "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/plugin" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/system/staleness" + "github.com/GrayCodeAI/hawk/internal/tool" ) // sessionWAL is the durability surface the chat model needs from its @@ -55,10 +55,10 @@ func (m *chatModel) recordWALError(err error) { if err == nil || m.durabilityWarning != "" { return } - m.durabilityWarning = "Warning: session persistence is failing — recent messages may be lost if graycode crashes. Check disk space and permissions." + m.durabilityWarning = "Warning: session persistence is failing — recent messages may be lost if hawk crashes. Check disk space and permissions." } -// All graycode color/icon/glyph constants live in theme.go. This file holds +// All hawk color/icon/glyph constants live in theme.go. This file holds // the pre-built lipgloss styles that combine a color with attributes // (bold, italic, border, etc.) for the most common patterns. @@ -74,8 +74,8 @@ var ( slashCmdStyle = lipgloss.NewStyle().Foreground(textDisabled) slashDescStyle = lipgloss.NewStyle().Foreground(textDisabled) - slashSelCmdStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) - slashSelDescStyle = lipgloss.NewStyle().Foreground(graycodeColor) + slashSelCmdStyle = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) + slashSelDescStyle = lipgloss.NewStyle().Foreground(hawkColor) inputBorderStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false, true, false).BorderForeground(borderDim) ghostHintStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("238")).Italic(true) containerErrStyle = lipgloss.NewStyle().Foreground(errorCoral) @@ -86,13 +86,13 @@ var ( dimColor = textDisabled ) -// graycodeSpinnerFrames feeds the bubbles spinner (matches BrailleSpinner default). -var graycodeSpinnerFrames = graycodeSpinnerGlyphs +// hawkSpinnerFrames feeds the bubbles spinner (matches BrailleSpinner default). +var hawkSpinnerFrames = hawkSpinnerGlyphs -// graycodeSpinnerFrameInterval — compass frame cadence. -const graycodeSpinnerFrameInterval = 80 * time.Millisecond +// hawkSpinnerFrameInterval — compass frame cadence. +const hawkSpinnerFrameInterval = 80 * time.Millisecond -// Spinner verbs (from graycode-archive) — picked randomly per session +// Spinner verbs (from hawk-archive) — picked randomly per session var spinnerVerbs = []string{ "Abstracting", "Architecting", "Brewing", "Calculating", "Cogitating", "Compiling", "Computing", "Conjuring", "Contemplating", "Cooking", @@ -152,7 +152,7 @@ type ( statusLeftBranch string connStatusVal string connStatusKey string - welcomeSetup graycodeconfig.SetupState + welcomeSetup hawkconfig.SetupState welcomeAgentsOK bool } processArrowTickMsg struct { @@ -209,7 +209,7 @@ type chatModel struct { viewport viewport.Model session *engine.Session registry *tool.Registry - settings graycodeconfig.Settings + settings hawkconfig.Settings ref *progRef cancel context.CancelFunc // cancel current stream sessionID string @@ -252,7 +252,7 @@ type chatModel struct { configNotice string configEntry string // configEntryNone, configEntryAPIKeyPaste, configEntryOllamaURL configProvider string // e.g. configProviderOllama while entry overlay is open - configModelOptions []configModelOption // labels + ids from graycode-router catalog + configModelOptions []configModelOption // labels + ids from eyrie catalog configModelProvider string // filter models after API key paste configModelSearch string // active model filter query configModelSearchActive bool // typing into model search input @@ -307,7 +307,7 @@ type chatModel struct { sessionBootstrapDone bool toolStartTime time.Time welcomeCache string - welcomeSetupState graycodeconfig.SetupState + welcomeSetupState hawkconfig.SetupState welcomeAgentsOK bool viewDirty bool layoutKey int // input lines + slash menu height fingerprint diff --git a/cmd/chat_model_test.go b/cmd/chat_model_test.go index 16ed411de..8bc4b7e5d 100644 --- a/cmd/chat_model_test.go +++ b/cmd/chat_model_test.go @@ -11,14 +11,14 @@ import ( "charm.land/bubbles/v2/textarea" "charm.land/bubbles/v2/viewport" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/bridge/sessioncapture" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/bridge/sessioncapture" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/tool" ) func newTestChatModel() *chatModel { @@ -43,7 +43,7 @@ func newTestChatModel() *chatModel { hintsLoader: engine.NewHintsLoader(), selfImprover: engine.NewSelfImprover(), codingSoul: engine.LoadCodingSoul(), - brailleSpinner: NewBrailleSpinner(SpinnerGraycode, "Thinking"), + brailleSpinner: NewBrailleSpinner(SpinnerHawk, "Thinking"), testStreamStarter: func() {}, } return m @@ -62,12 +62,12 @@ func isolateChatCommandSweepEnv(t *testing.T) { root := t.TempDir() storage.SetTestDirs(t, root) isolateCredentialHome(t) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) restoreThemeGlobals(t) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) } @@ -77,7 +77,7 @@ func isolateChatCommandSweepEnv(t *testing.T) { // (e.g. TestAdaptiveNeutralsPreserveDarkAppearance). func restoreThemeGlobals(t *testing.T) { t.Helper() - savedGraycode, savedSuccess, savedWarn, savedErr, savedInfo := graycodeColor, successTeal, warnAmber, errorCoral, infoSky + savedHawk, savedSuccess, savedWarn, savedErr, savedInfo := hawkColor, successTeal, warnAmber, errorCoral, infoSky savedTool, savedAgent, savedDone, savedContainer := toolGold, agentGold, doneGreen, containerBlue savedInspect, savedEdit, savedRun, savedTrust := tierInspect, tierEdit, tierRun, tierTrust savedHudBorder, savedHudLabel := hudBorderPurple, hudLabelPink @@ -86,7 +86,7 @@ func restoreThemeGlobals(t *testing.T) { savedBorderDim, savedBgCode := borderDim, bgCode hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout) t.Cleanup(func() { - graycodeColor, successTeal, warnAmber, errorCoral, infoSky = savedGraycode, savedSuccess, savedWarn, savedErr, savedInfo + hawkColor, successTeal, warnAmber, errorCoral, infoSky = savedHawk, savedSuccess, savedWarn, savedErr, savedInfo toolGold, agentGold, doneGreen, containerBlue = savedTool, savedAgent, savedDone, savedContainer tierInspect, tierEdit, tierRun, tierTrust = savedInspect, savedEdit, savedRun, savedTrust hudBorderPurple, hudLabelPink = savedHudBorder, savedHudLabel @@ -141,7 +141,7 @@ func TestChatModel_SlashClear(t *testing.T) { func TestFormatQuitResumeMessage(t *testing.T) { got := formatQuitResumeMessage("44418bdd52745678") - want := "Thank you for using Graycode!\n\nTo resume this session, run: graycode --resume 44418bdd52745678\n" + want := "Thank you for using Hawk!\n\nTo resume this session, run: hawk --resume 44418bdd52745678\n" if got != want { t.Fatalf("quit message mismatch:\nwant %q\ngot %q", want, got) } @@ -149,7 +149,7 @@ func TestFormatQuitResumeMessage(t *testing.T) { func TestFormatQuitResumeMessage_NoSession(t *testing.T) { got := formatQuitResumeMessage("") - want := "Thank you for using Graycode!\n" + want := "Thank you for using Hawk!\n" if got != want { t.Fatalf("quit message mismatch:\nwant %q\ngot %q", want, got) } diff --git a/cmd/chat_mouse.go b/cmd/chat_mouse.go index 32e9a9c5f..0be52a4b1 100644 --- a/cmd/chat_mouse.go +++ b/cmd/chat_mouse.go @@ -5,11 +5,11 @@ import ( "os" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func mouseEnabledFromEnv() bool { - v := strings.TrimSpace(os.Getenv("GRAYCODE_MOUSE")) + v := strings.TrimSpace(os.Getenv("HAWK_MOUSE")) if v == "0" || strings.EqualFold(v, "false") || strings.EqualFold(v, "off") { return false } @@ -17,13 +17,13 @@ func mouseEnabledFromEnv() bool { } func envOverridesMouse() bool { - v := strings.TrimSpace(os.Getenv("GRAYCODE_MOUSE")) + v := strings.TrimSpace(os.Getenv("HAWK_MOUSE")) return v != "" } // mouseEnabled reports whether the TUI should capture mouse events for chat wheel // scroll. When false, the terminal handles click-drag selection natively (OpenCode -// "mouse": false). Priority: GRAYCODE_MOUSE env → runtime override → settings → default on. +// "mouse": false). Priority: HAWK_MOUSE env → runtime override → settings → default on. func (m chatModel) mouseEnabled() bool { if envOverridesMouse() { return mouseEnabledFromEnv() @@ -54,7 +54,7 @@ func (m *chatModel) handleMouseCommand(parts []string) { source := "default" switch { case envOverridesMouse(): - source = "GRAYCODE_MOUSE env" + source = "HAWK_MOUSE env" case m.mouseOverride != nil: source = "session" case m.settings.TuiMouse != nil: @@ -76,7 +76,7 @@ func (m *chatModel) handleMouseCommand(parts []string) { if envOverridesMouse() { m.messages = append(m.messages, displayMsg{ role: "system", - content: "Mouse is controlled by GRAYCODE_MOUSE env in this session. " + + content: "Mouse is controlled by HAWK_MOUSE env in this session. " + "Unset it to use /mouse or settings.json tui_mouse.", }) return @@ -85,11 +85,11 @@ func (m *chatModel) handleMouseCommand(parts []string) { switch strings.ToLower(parts[1]) { case "on", "true", "1", "enable": m.setMouseEnabled(true) - _ = graycodeconfig.SetGlobalSetting("tui_mouse", "true") + _ = hawkconfig.SetGlobalSetting("tui_mouse", "true") m.messages = append(m.messages, displayMsg{role: "system", content: "Mouse capture on — chat wheel scroll enabled."}) case "off", "false", "0", "disable": m.setMouseEnabled(false) - _ = graycodeconfig.SetGlobalSetting("tui_mouse", "false") + _ = hawkconfig.SetGlobalSetting("tui_mouse", "false") m.messages = append(m.messages, displayMsg{ role: "system", content: "Mouse capture off — use click-drag to select text. /copy and Ctrl+Shift+C still work.", @@ -103,7 +103,7 @@ func (m *chatModel) handleMouseCommand(parts []string) { val = "true" msg = "Mouse capture on — chat wheel scroll enabled." } - _ = graycodeconfig.SetGlobalSetting("tui_mouse", val) + _ = hawkconfig.SetGlobalSetting("tui_mouse", val) m.messages = append(m.messages, displayMsg{role: "system", content: msg}) default: m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /mouse [on|off|toggle]"}) diff --git a/cmd/chat_multiturn_e2e_test.go b/cmd/chat_multiturn_e2e_test.go index 913c15480..c1eb67a05 100644 --- a/cmd/chat_multiturn_e2e_test.go +++ b/cmd/chat_multiturn_e2e_test.go @@ -6,35 +6,35 @@ import ( "testing" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func configureReadyChatState(t *testing.T) { t.Helper() isolateChatCommandSweepEnv(t) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - graycodeconfig.InvalidateConfigUICache() - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.InvalidateConfigUICache() + hawkconfig.RefreshConfigCredSnapshot(ctx) } func countMessagesByRole(msgs []displayMsg, role string) int { diff --git a/cmd/chat_permission_keys_test.go b/cmd/chat_permission_keys_test.go index ed239c364..552b24215 100644 --- a/cmd/chat_permission_keys_test.go +++ b/cmd/chat_permission_keys_test.go @@ -6,8 +6,8 @@ import ( "time" tea "charm.land/bubbletea/v2" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestPermissionAlwaysAllowDoesNotNilDeref(t *testing.T) { diff --git a/cmd/chat_platform_ctx.go b/cmd/chat_platform_ctx.go index 2a8fdbcf6..0ced98469 100644 --- a/cmd/chat_platform_ctx.go +++ b/cmd/chat_platform_ctx.go @@ -8,7 +8,7 @@ import ( "time" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) var platformCtxCache struct { @@ -51,7 +51,7 @@ type platformContextIndexMsg struct { func fetchPlatformContextIndexCmd() tea.Cmd { return func() tea.Msg { - models, err := graycodeconfig.ListPublicEngineModels(context.Background(), "xiaomi_mimo_payg") + models, err := hawkconfig.ListPublicEngineModels(context.Background(), "xiaomi_mimo_payg") if err != nil { return platformContextIndexMsg{err: err} } diff --git a/cmd/chat_print.go b/cmd/chat_print.go index 916d3aff8..87184d6e5 100644 --- a/cmd/chat_print.go +++ b/cmd/chat_print.go @@ -14,12 +14,12 @@ import ( "unicode/utf8" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - aiwatch "github.com/GrayCodeAI/graycode-cli/internal/engine/io" - "github.com/GrayCodeAI/graycode-cli/internal/engine/lifecycle" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/session" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + aiwatch "github.com/GrayCodeAI/hawk/internal/engine/io" + "github.com/GrayCodeAI/hawk/internal/engine/lifecycle" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/session" ) // Print mode and session persistence functions extracted from chat.go @@ -40,7 +40,7 @@ func runPrint(text string) error { return err } - sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) if cfgErr != nil { return cfgErr } @@ -144,7 +144,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveGraycodeRouterSession(sessionID, sess) + saveEyrieSession(sessionID, sess) } return nil } @@ -159,7 +159,7 @@ func runPrint(text string) error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveGraycodeRouterSession(sessionID, sess) + saveEyrieSession(sessionID, sess) } return nil } @@ -294,7 +294,7 @@ func writePrintEvent(sessionID, eventType, content, toolName string) { fmt.Println(string(data)) } -func saveGraycodeRouterSession(id string, sess *engine.Session) { +func saveEyrieSession(id string, sess *engine.Session) { raw := sess.RawMessages() if len(raw) == 0 { return @@ -310,7 +310,7 @@ func saveGraycodeRouterSession(id string, sess *engine.Session) { // runRepl starts an interactive REPL mode for multi-turn conversation without TUI. func runRepl() error { - fmt.Fprintln(os.Stderr, auditTint("Graycode REPL", textPrimary)+auditTint(" — type 'exit' or 'quit' to leave, 'help' for commands", textMuted)) + fmt.Fprintln(os.Stderr, auditTint("Hawk REPL", textPrimary)+auditTint(" — type 'exit' or 'quit' to leave, 'help' for commands", textMuted)) fmt.Fprintln(os.Stderr) systemPrompt, err := buildSystemPrompt() @@ -329,7 +329,7 @@ func runRepl() error { return err } - sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) if cfgErr != nil { return cfgErr } @@ -474,14 +474,14 @@ func runRepl() error { writePrintResult(printed.String(), sessionID, sess, false, nil) } if !noSessionPersistence { - saveGraycodeRouterSession(sessionID, sess) + saveEyrieSession(sessionID, sess) } } } } } -func replBuiltinResponse(input string, sess *engine.Session, settings graycodeconfig.Settings, sessionID string) (string, bool, error) { +func replBuiltinResponse(input string, sess *engine.Session, settings hawkconfig.Settings, sessionID string) (string, bool, error) { switch strings.TrimSpace(input) { case "/tools": return builtInToolsSummary(), true, nil @@ -500,12 +500,12 @@ func replBuiltinResponse(input string, sess *engine.Session, settings graycodeco } } -func replModelsSummary(settings graycodeconfig.Settings, sessionProvider string) (string, bool, error) { +func replModelsSummary(settings hawkconfig.Settings, sessionProvider string) (string, bool, error) { providerName := effectiveProviderForREPL(settings, sessionProvider) if providerName == "" { - return "No active provider selected. Set one with `graycode config provider ` or start REPL with `--provider`.", true, nil + return "No active provider selected. Set one with `hawk config provider ` or start REPL with `--provider`.", true, nil } - models, err := graycodeconfig.FetchModelsForProvider(providerName) + models, err := hawkconfig.FetchModelsForProvider(providerName) if err != nil { return "", true, err } @@ -531,7 +531,7 @@ func replModelsSummary(settings graycodeconfig.Settings, sessionProvider string) return b.String(), true, nil } -func effectiveProviderForREPL(settings graycodeconfig.Settings, sessionProvider string) string { +func effectiveProviderForREPL(settings hawkconfig.Settings, sessionProvider string) string { if provider != "" { return strings.TrimSpace(provider) } @@ -556,7 +556,7 @@ func formatModelTablePlain(rows []modelTableRow) string { } // watchIgnoreDirs are directory names skipped when scanning for AI directives. -var watchIgnoreDirs = []string{".git", "node_modules", "vendor", "__pycache__", ".graycode"} +var watchIgnoreDirs = []string{".git", "node_modules", "vendor", "__pycache__", ".hawk"} // runWatch watches the working directory for AI!/AI? comment directives and // dispatches a targeted LLM edit for each one as files change (Aider-style diff --git a/cmd/chat_print_usage_test.go b/cmd/chat_print_usage_test.go index dca4f0bec..ca4c1b556 100644 --- a/cmd/chat_print_usage_test.go +++ b/cmd/chat_print_usage_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // captureStderr runs fn with os.Stderr redirected to a pipe and returns the @@ -38,7 +38,7 @@ func TestPrintTextUsageFooter(t *testing.T) { printTextUsageFooter(&engine.StreamUsage{ PromptTokens: 100, CompletionTokens: 50, - }, started, 3, "graycode-pro") + }, started, 3, "hawk-pro") }) if !strings.Contains(got, "100 in · 50 out") { t.Errorf("footer missing token counts: %q", got) @@ -49,7 +49,7 @@ func TestPrintTextUsageFooter(t *testing.T) { if !strings.Contains(got, "3 turn(s)") { t.Errorf("footer missing turn count: %q", got) } - if !strings.Contains(got, "graycode-pro") { + if !strings.Contains(got, "hawk-pro") { t.Errorf("footer missing model: %q", got) } if !strings.Contains(got, "2s") { @@ -90,7 +90,7 @@ func TestPrintTextUsageFooter(t *testing.T) { CompletionTokens: 5, }, started, 1, "") }) - if strings.Contains(got, "graycode-pro") { + if strings.Contains(got, "hawk-pro") { t.Errorf("footer should omit empty model: %q", got) } }) diff --git a/cmd/chat_prompt_timeout_test.go b/cmd/chat_prompt_timeout_test.go index 83aac204c..3920bc925 100644 --- a/cmd/chat_prompt_timeout_test.go +++ b/cmd/chat_prompt_timeout_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestPermissionPromptTimeoutClearsStaleState(t *testing.T) { diff --git a/cmd/chat_scrollbar.go b/cmd/chat_scrollbar.go index 538c75f69..8ca748ebc 100644 --- a/cmd/chat_scrollbar.go +++ b/cmd/chat_scrollbar.go @@ -19,7 +19,7 @@ const ( // scrollbarThumbStyle — Talon Gold thumb so it reads as a brand control. var ( - scrollbarThumbStyle = lipgloss.NewStyle().Foreground(graycodeColor) + scrollbarThumbStyle = lipgloss.NewStyle().Foreground(hawkColor) scrollbarTrackStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("238")) ) diff --git a/cmd/chat_select.go b/cmd/chat_select.go index 1376d0841..499da7b29 100644 --- a/cmd/chat_select.go +++ b/cmd/chat_select.go @@ -55,7 +55,7 @@ func enterSelectionMode(ref *progRef, transcript string, restoreMouse bool) tea. fmt.Fprintln(os.Stderr, " Click and drag to select text in this terminal.") fmt.Fprintln(os.Stderr, " Copy with your terminal's normal copy shortcut (e.g. Cmd+C,") fmt.Fprintln(os.Stderr, " Ctrl+Shift+C, or Ctrl+Insert).") - fmt.Fprintln(os.Stderr, " Press any key to return to graycode.") + fmt.Fprintln(os.Stderr, " Press any key to return to hawk.") fmt.Fprintln(os.Stderr, "────────────────────────────────────────────────────────────") fmt.Fprintln(os.Stderr, "") // Block on stdin in raw mode so any single keypress resumes the @@ -109,7 +109,7 @@ func plainTranscript(messages []displayMsg, partial string) string { b.WriteString("\n\n") } if partial != "" { - b.WriteString("graycode: ") + b.WriteString("hawk: ") b.WriteString(partial) b.WriteString("\n\n") } @@ -127,7 +127,7 @@ func plainTranscriptLine(msg displayMsg) (string, bool) { case "user": return "You: " + content, true case "assistant": - return "graycode: " + content, true + return "hawk: " + content, true case "error": return "Error: " + content, true case "system": diff --git a/cmd/chat_session_picker.go b/cmd/chat_session_picker.go index 302da64c9..d4b5f762b 100644 --- a/cmd/chat_session_picker.go +++ b/cmd/chat_session_picker.go @@ -8,10 +8,10 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/mattn/go-runewidth" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" ) // sessionPickerStyles holds the lipgloss styles for the session picker overlay. diff --git a/cmd/chat_status.go b/cmd/chat_status.go index 739e71ffb..7014878dc 100644 --- a/cmd/chat_status.go +++ b/cmd/chat_status.go @@ -8,8 +8,8 @@ import ( lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" ) func modelStatusMeta(gateway, modelID string) (displayName, contextLabel string) { @@ -61,7 +61,7 @@ func (m *chatModel) invalidateConnStatus() { func (m chatModel) connStatusFingerprint() string { gw, modelName := m.sessionGatewayModel() - creds := strings.Join(graycodeconfig.ConfiguredCredentialProviders(), ",") + creds := strings.Join(hawkconfig.ConfiguredCredentialProviders(), ",") api := 0 if m.session != nil { api = m.session.LastPromptTokens() @@ -83,7 +83,7 @@ func (m chatModel) sessionGatewayModel() (gateway, model string) { model = explicitModel } if gateway == "" && model != "" { - gateway = strings.TrimSpace(graycodeconfig.ProviderOfModel(model)) + gateway = strings.TrimSpace(hawkconfig.ProviderOfModel(model)) } if explicitGateway == "" && explicitModel == "" && model == "" { switch strings.TrimSpace(gateway) { @@ -96,10 +96,10 @@ func (m chatModel) sessionGatewayModel() (gateway, model string) { func (m *chatModel) chatConnectionStatus() string { ctx := context.Background() - if !graycodeconfig.CredentialSnapshotReady() { + if !hawkconfig.CredentialSnapshotReady() { return "" } - if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + if !hawkconfig.HasConfiguredDeploymentCached(ctx) { return "" } m.syncSessionSelection() @@ -138,7 +138,7 @@ func (m chatModel) buildConnectionStatusPlain() string { func (m chatModel) connectionStatusParts() (gateway, model, contextLabel string) { gw, modelID := m.sessionGatewayModel() - gateway = graycodeconfig.GatewayDisplayName(gw) + gateway = hawkconfig.GatewayDisplayName(gw) if gateway == "" { gateway = gw } @@ -196,10 +196,10 @@ func trimRepeatedGatewayPrefix(gateway, model string) string { // footer segments so context can sit flush on the right edge. func (m chatModel) renderConnectionStatusSplit() (modelRendered string, modelVis int, ctxRendered string, ctxVis int) { ctx := context.Background() - if !graycodeconfig.CredentialSnapshotReady() { + if !hawkconfig.CredentialSnapshotReady() { return "", 0, "", 0 } - if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { + if !hawkconfig.HasConfiguredDeploymentCached(ctx) { return "", 0, "", 0 } diff --git a/cmd/chat_status_metadata_test.go b/cmd/chat_status_metadata_test.go index 5f50fa7b1..4a0660f7a 100644 --- a/cmd/chat_status_metadata_test.go +++ b/cmd/chat_status_metadata_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" ) // seedPlatformContextCacheForTest primes the platform context cache so tests @@ -133,10 +133,10 @@ func TestConnectionStatusParts_MimoShowsPlatformContext_HyphenProvider(t *testin } func TestGatewayDisplayName_XiaomiTokenPlanHyphen(t *testing.T) { - if got := graycodeconfig.GatewayDisplayName("xiaomi-mimo-token-plan"); got != "Xiaomi MiMo — Token Plan" { + if got := hawkconfig.GatewayDisplayName("xiaomi-mimo-token-plan"); got != "Xiaomi MiMo — Token Plan" { t.Fatalf("GatewayDisplayName(hyphen) = %q, want nice name", got) } - if got := graycodeconfig.GatewayDisplayName("xiaomi_mimo_token_plan"); got != "Xiaomi MiMo — Token Plan" { + if got := hawkconfig.GatewayDisplayName("xiaomi_mimo_token_plan"); got != "Xiaomi MiMo — Token Plan" { t.Fatalf("GatewayDisplayName(underscore) = %q, want nice name", got) } } diff --git a/cmd/chat_status_test.go b/cmd/chat_status_test.go index cbf8e79f6..d456149cf 100644 --- a/cmd/chat_status_test.go +++ b/cmd/chat_status_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" "github.com/charmbracelet/x/ansi" ) @@ -92,23 +92,23 @@ func TestFormatContextUsedLabel(t *testing.T) { } func TestChatConnectionStatus_WithModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") - _ = graycodeconfig.SetActiveModel(ctx, "moonshotai/kimi-k2.6") - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.SetActiveProvider(ctx, "openrouter") + _ = hawkconfig.SetActiveModel(ctx, "moonshotai/kimi-k2.6") + hawkconfig.RefreshConfigCredSnapshot(ctx) sess := engine.NewSession("openrouter", "moonshotai/kimi-k2.6", "", nil) @@ -126,23 +126,23 @@ func TestChatConnectionStatus_WithModel(t *testing.T) { } func TestChatConnectionStatus_KeyNoModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.ClearActiveSelection(ctx) - _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.ClearActiveSelection(ctx) + _ = hawkconfig.SetActiveProvider(ctx, "openrouter") + hawkconfig.RefreshConfigCredSnapshot(ctx) m := chatModel{session: &engine.Session{}} got := m.chatConnectionStatus() @@ -152,22 +152,22 @@ func TestChatConnectionStatus_KeyNoModel(t *testing.T) { } func TestChatConnectionStatus_NoGatewayNoModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("ANTHROPIC_API_KEY"), "sk-ant-test-key-long-enough"); err != nil { t.Fatalf("store.Set: %v", err) } - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.ClearActiveSelection(ctx) - graycodeconfig.RefreshConfigCredSnapshot(ctx) + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.ClearActiveSelection(ctx) + hawkconfig.RefreshConfigCredSnapshot(ctx) m := chatModel{session: &engine.Session{}} got := m.chatConnectionStatus() @@ -210,7 +210,7 @@ func TestStartupWarmMsg_RefreshesFooterCache(t *testing.T) { statusLeftBranch: "main", connStatusVal: "OpenRouter · gpt-4", connStatusKey: "cache-key", - welcomeSetup: graycodeconfig.SetupState{NeedsSetup: true}, + welcomeSetup: hawkconfig.SetupState{NeedsSetup: true}, welcomeAgentsOK: true, }) next := nextModel.(chatModel) @@ -233,14 +233,14 @@ func TestStartupWarmMsg_RefreshesFooterCache(t *testing.T) { func TestBuildWelcomeMessage_IncludesDockerWhenEnabled(t *testing.T) { running := true - msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 80, 24, &running) + msg := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 80, 24, &running) if !strings.Contains(msg, "Container") { t.Fatalf("expected container execution badge in welcome, got:\n%s", msg) } } func TestBuildWelcomeMessage_OmitsDockerWhenDisabled(t *testing.T) { - msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 80, 24, nil) + msg := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 80, 24, nil) if !strings.Contains(msg, "Container Starting") || strings.Contains(msg, "HOST") { t.Fatalf("expected mandatory container startup badge, got:\n%s", msg) } @@ -300,7 +300,7 @@ func TestShowWelcomeBanner_WithMessages(t *testing.T) { func TestBuildWelcomeMessage_UsesDisplayVersion(t *testing.T) { SetVersion("dev") - msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 80, 24, nil) + msg := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 80, 24, nil) if strings.Contains(msg, "vdev") { t.Fatal("welcome should not show vdev; DisplayVersion should read VERSION file or dev") } diff --git a/cmd/chat_stream.go b/cmd/chat_stream.go index 942418ba8..d46f209f3 100644 --- a/cmd/chat_stream.go +++ b/cmd/chat_stream.go @@ -8,7 +8,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // Streaming and prompt command functions extracted from chat.go diff --git a/cmd/chat_subcommand_brainstorm.go b/cmd/chat_subcommand_brainstorm.go index 9610f7fab..c9062d4cd 100644 --- a/cmd/chat_subcommand_brainstorm.go +++ b/cmd/chat_subcommand_brainstorm.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // brainstormSubcommand implements the /brainstorm slash command. It diff --git a/cmd/chat_subcommand_branch_agent.go b/cmd/chat_subcommand_branch_agent.go index 196cca5c2..6ece33d58 100644 --- a/cmd/chat_subcommand_branch_agent.go +++ b/cmd/chat_subcommand_branch_agent.go @@ -5,17 +5,17 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) -// branchAgentSubcommand creates a graycode/agent-* branch from main/master. +// branchAgentSubcommand creates a hawk/agent-* branch from main/master. type branchAgentSubcommand struct{} func (c *branchAgentSubcommand) Name() string { return "branch-agent" } func (c *branchAgentSubcommand) Aliases() []string { return []string{"agent-branch"} } func (c *branchAgentSubcommand) Description() string { - return "create graycode/agent-* branch if on main/master" + return "create hawk/agent-* branch if on main/master" } func (c *branchAgentSubcommand) Usage() string { return "/branch-agent" } diff --git a/cmd/chat_subcommand_checkpoint.go b/cmd/chat_subcommand_checkpoint.go index 3f346fecd..bf5ae7402 100644 --- a/cmd/chat_subcommand_checkpoint.go +++ b/cmd/chat_subcommand_checkpoint.go @@ -3,7 +3,7 @@ package cmd import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // checkpointSubcommand implements the /checkpoint slash command. diff --git a/cmd/chat_subcommand_context.go b/cmd/chat_subcommand_context.go index 4144b18d2..76b04e866 100644 --- a/cmd/chat_subcommand_context.go +++ b/cmd/chat_subcommand_context.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine/project" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine/project" ) // contextSubcommand implements the /context slash command. It @@ -37,7 +37,7 @@ func (c *contextSubcommand) Handle(m *chatModel, args []string, text string) (te } return m, nil } - m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.BuildContextWithDirs(addDirs)}) + m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.BuildContextWithDirs(addDirs)}) return m, nil } diff --git a/cmd/chat_subcommand_council.go b/cmd/chat_subcommand_council.go index 9bac3eaf5..57db644f3 100644 --- a/cmd/chat_subcommand_council.go +++ b/cmd/chat_subcommand_council.go @@ -7,8 +7,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // councilSubcommand implements the /council slash command. It diff --git a/cmd/chat_subcommand_dream.go b/cmd/chat_subcommand_dream.go index 064cc5eb1..4a8902d40 100644 --- a/cmd/chat_subcommand_dream.go +++ b/cmd/chat_subcommand_dream.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" ) // dreamSubcommand implements the /dream slash command. It runs diff --git a/cmd/chat_subcommand_ecosystem.go b/cmd/chat_subcommand_ecosystem.go index 6f3c99bea..ec4288b94 100644 --- a/cmd/chat_subcommand_ecosystem.go +++ b/cmd/chat_subcommand_ecosystem.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // ecosystemSubcommand implements the /ecosystem slash command. It @@ -28,7 +28,7 @@ func (e *ecosystemSubcommand) Handle(m *chatModel, args []string, text string) ( if providerName == "" { providerName = "auto" } - m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)}) + m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)}) return m, nil } diff --git a/cmd/chat_subcommand_harness.go b/cmd/chat_subcommand_harness.go index 5d390abd2..3861e4640 100644 --- a/cmd/chat_subcommand_harness.go +++ b/cmd/chat_subcommand_harness.go @@ -6,7 +6,7 @@ import ( "os" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/harness" + "github.com/GrayCodeAI/hawk/internal/harness" ) type harnessSubcommand struct{} @@ -29,7 +29,7 @@ func (h *harnessSubcommand) Handle(m *chatModel, args []string, text string) (te return m.startPromptCommand("/harness", prompt) } - prompt := fmt.Sprintf("I ran a Graycode Agent Harness Review on this repository (%s).\n\nOverall Score: %d/100 (%s)\nPrioritized Findings (%d):\n\n%s\n\nPlease help me address the highest priority findings to improve our AI coding harness.", + prompt := fmt.Sprintf("I ran a Hawk Agent Harness Review on this repository (%s).\n\nOverall Score: %d/100 (%s)\nPrioritized Findings (%d):\n\n%s\n\nPlease help me address the highest priority findings to improve our AI coding harness.", report.TargetPath, report.OverallScore, report.OverallStatus, len(report.Findings), harness.RenderMarkdown(report)) return m.startPromptCommand("/harness", prompt) diff --git a/cmd/chat_subcommand_harrier.go b/cmd/chat_subcommand_harrier.go index b675d667f..d1482ba2c 100644 --- a/cmd/chat_subcommand_harrier.go +++ b/cmd/chat_subcommand_harrier.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" ) // harrierSubcommand implements the /harrier slash command. It shows a diff --git a/cmd/chat_subcommand_investigate.go b/cmd/chat_subcommand_investigate.go index 8738633f2..afd05bda6 100644 --- a/cmd/chat_subcommand_investigate.go +++ b/cmd/chat_subcommand_investigate.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // investigateSubcommand implements the /investigate slash command. diff --git a/cmd/chat_subcommand_isolation.go b/cmd/chat_subcommand_isolation.go index 457df94f2..fbcac867f 100644 --- a/cmd/chat_subcommand_isolation.go +++ b/cmd/chat_subcommand_isolation.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // isolationSubcommand sets the unified IsolationProfile: OS sandbox + container story. diff --git a/cmd/chat_subcommand_memory.go b/cmd/chat_subcommand_memory.go index 59c5561ef..92bacee41 100644 --- a/cmd/chat_subcommand_memory.go +++ b/cmd/chat_subcommand_memory.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // memorySubcommand implements the /memory slash command. @@ -17,7 +17,7 @@ func (m *memorySubcommand) Aliases() []string { return nil } func (m *memorySubcommand) Description() string { return "print project instructions (AGENTS.md)" } func (m *memorySubcommand) Usage() string { return "" } func (m *memorySubcommand) Handle(ml *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - md := strings.TrimSpace(graycodeconfig.LoadAgentsMD()) + md := strings.TrimSpace(hawkconfig.LoadAgentsMD()) if md == "" { ml.messages = append(ml.messages, displayMsg{role: "system", content: "No AGENTS.md project instructions found.\nUse /harrier for persistent graph memory."}) } else { diff --git a/cmd/chat_subcommand_mode.go b/cmd/chat_subcommand_mode.go index cba5da018..a47a5c470 100644 --- a/cmd/chat_subcommand_mode.go +++ b/cmd/chat_subcommand_mode.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" ) // modeSubcommand implements the /mode slash command. @@ -45,7 +45,7 @@ func (mo *modeSubcommand) Handle(m *chatModel, args []string, text string) (tea. } visible := 0 if m.session != nil && m.session.Tools() != nil && m.session.Tools().Registry() != nil { - visible = len(m.session.Tools().Registry().GraycodeRouterTools()) + visible = len(m.session.Tools().Registry().EyrieTools()) } m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( "Work mode: %s (plan | act | review)\nShell mode: %s (auto | shell | agent)\nIsolation: %s\nTrust: %s\nAuto-commit: %s\nTools visible: %d\n\n/start · /isolation · /trust · /branch-agent · /auto-commit", diff --git a/cmd/chat_subcommand_model.go b/cmd/chat_subcommand_model.go index 8a9bfd0b3..b37f6bd13 100644 --- a/cmd/chat_subcommand_model.go +++ b/cmd/chat_subcommand_model.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // modelSubcommand implements the /model slash command. It shows the @@ -60,15 +60,15 @@ func (mo *modelSubcommand) Handle(m *chatModel, args []string, text string) (tea return m, nil } } - if graycodeconfig.DeploymentRoutingEnabled(m.settings) { - arg = graycodeconfig.ResolveCanonicalModel(arg) + if hawkconfig.DeploymentRoutingEnabled(m.settings) { + arg = hawkconfig.ResolveCanonicalModel(arg) } prevModel := m.session.Model() if strings.EqualFold(strings.TrimSpace(prevModel), strings.TrimSpace(arg)) { m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Already using %s — no change.", prevModel)}) return m, nil } - if err := graycodeconfig.SetGlobalSetting("model", arg); err != nil { + if err := hawkconfig.SetGlobalSetting("model", arg); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } @@ -81,15 +81,15 @@ func (mo *modelSubcommand) Handle(m *chatModel, args []string, text string) (tea if m.session != nil { provider = m.session.Provider() } - m.session.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(graycodeconfig.LoadSettings(), arg, provider)) + m.session.SetThinkingEnabled(hawkconfig.ResolveThinkingForModel(hawkconfig.LoadSettings(), arg, provider)) } - thinkLabel := graycodeconfig.FormatModelThinkingLabel( - selected != nil && graycodeconfig.ModelCapabilitySupportsThinking(selected.Capabilities), - graycodeconfig.ThinkingPrefForModel(graycodeconfig.LoadSettings(), arg), + thinkLabel := hawkconfig.FormatModelThinkingLabel( + selected != nil && hawkconfig.ModelCapabilitySupportsThinking(selected.Capabilities), + hawkconfig.ThinkingPrefForModel(hawkconfig.LoadSettings(), arg), m.session.Provider(), ) m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( - "Model switched: %s → %s (Think: %s)\nConversation history preserved (%d messages); new requests use the new model.\nSaved in graycode-router (provider.json). Use /model and press t to toggle Think.", + "Model switched: %s → %s (Think: %s)\nConversation history preserved (%d messages); new requests use the new model.\nSaved in eyrie (provider.json). Use /model and press t to toggle Think.", prevModel, m.session.Model(), thinkLabel, msgCount, )}) return m, nil diff --git a/cmd/chat_subcommand_party.go b/cmd/chat_subcommand_party.go index 192f869c3..898d0cd7b 100644 --- a/cmd/chat_subcommand_party.go +++ b/cmd/chat_subcommand_party.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // partySubcommand implements the /party slash command. It starts a diff --git a/cmd/chat_subcommand_path.go b/cmd/chat_subcommand_path.go index 606a9c334..1fd773f57 100644 --- a/cmd/chat_subcommand_path.go +++ b/cmd/chat_subcommand_path.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) // pathSubcommand implements the /path slash command. It prints @@ -17,7 +17,7 @@ func (p *pathSubcommand) Aliases() []string { return nil } func (p *pathSubcommand) Description() string { return "show developer path (HOME, GOROOT, etc.)" } func (p *pathSubcommand) Usage() string { return "" } func (p *pathSubcommand) Handle(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.FormatDeveloperPathReport(context.Background())}) + m.messages = append(m.messages, displayMsg{role: "system", content: hawkconfig.FormatDeveloperPathReport(context.Background())}) return m, nil } diff --git a/cmd/chat_subcommand_recipe.go b/cmd/chat_subcommand_recipe.go index bb5ad474a..7bed6af84 100644 --- a/cmd/chat_subcommand_recipe.go +++ b/cmd/chat_subcommand_recipe.go @@ -7,7 +7,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/recipe" + "github.com/GrayCodeAI/hawk/internal/recipe" ) // recipeSubcommand implements the /recipe slash command. It @@ -25,7 +25,7 @@ func (r *recipeSubcommand) Handle(m *chatModel, args []string, text string) (tea rn := recipe.NewRunner() recipes := rn.List() if len(recipes) == 0 { - m.messages = append(m.messages, displayMsg{role: "system", content: "No recipes found in Graycode user state or .agents/recipes/"}) + m.messages = append(m.messages, displayMsg{role: "system", content: "No recipes found in Hawk user state or .agents/recipes/"}) } else { var list string for _, r := range recipes { diff --git a/cmd/chat_subcommand_reflect.go b/cmd/chat_subcommand_reflect.go index b5f0198ae..9b69ab5f2 100644 --- a/cmd/chat_subcommand_reflect.go +++ b/cmd/chat_subcommand_reflect.go @@ -3,7 +3,7 @@ package cmd import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // reflectSubcommand implements the /reflect slash command. It diff --git a/cmd/chat_subcommand_render.go b/cmd/chat_subcommand_render.go index e175c6355..90fbf48ac 100644 --- a/cmd/chat_subcommand_render.go +++ b/cmd/chat_subcommand_render.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // renderSubcommand implements the /render slash command. It diff --git a/cmd/chat_subcommand_review.go b/cmd/chat_subcommand_review.go index 5f66d5a0b..84fc9cdef 100644 --- a/cmd/chat_subcommand_review.go +++ b/cmd/chat_subcommand_review.go @@ -3,7 +3,7 @@ package cmd import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // reviewSubcommand implements the /review slash command. It diff --git a/cmd/chat_subcommand_shell_test.go b/cmd/chat_subcommand_shell_test.go index c8e6dbab3..7bd5564e2 100644 --- a/cmd/chat_subcommand_shell_test.go +++ b/cmd/chat_subcommand_shell_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestRunSubcommand_UsesBashToolForSafeCommand(t *testing.T) { diff --git a/cmd/chat_subcommand_simple.go b/cmd/chat_subcommand_simple.go index 8f3f6af8b..c666ec80b 100644 --- a/cmd/chat_subcommand_simple.go +++ b/cmd/chat_subcommand_simple.go @@ -11,13 +11,13 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/theme" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + analytics "github.com/GrayCodeAI/hawk/internal/observability" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/theme" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // init() registers a large batch of simple /slash commands via @@ -88,7 +88,7 @@ func init() { m.themePicker = NewThemePicker() } // Pre-select the current saved theme. - current := graycodeconfig.LoadGlobalSettings().Theme + current := hawkconfig.LoadGlobalSettings().Theme m.themePicker.OpenWithCurrent(current) m.viewDirty = true m.updateViewportContent() @@ -96,7 +96,7 @@ func init() { } // Inline: /theme themeName := args[0] - if err := graycodeconfig.SetGlobalSetting("theme", themeName); err != nil { + if err := hawkconfig.SetGlobalSetting("theme", themeName); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { // Apply immediately — full palette swap, no restart needed. @@ -117,7 +117,7 @@ func init() { m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /color "}) return m, nil } - if err := graycodeconfig.SetGlobalSetting("agentColor", args[0]); err != nil { + if err := hawkconfig.SetGlobalSetting("agentColor", args[0]); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Agent color set to: %s", args[0])}) @@ -132,12 +132,12 @@ func init() { description: "toggle fast mode (cheapest model for this provider)", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - savedModel := graycodeconfig.ActiveModel(context.Background()) + savedModel := hawkconfig.ActiveModel(context.Background()) if m.session.Model() == savedModel { providerName := strings.TrimSpace(m.session.Provider()) - fastModel := graycodeconfig.CheapestModelForProvider(providerName, m.session.Model()) + fastModel := hawkconfig.CheapestModelForProvider(providerName, m.session.Model()) if strings.TrimSpace(fastModel) == "" { - fastModel = graycodeconfig.DefaultModelForProvider(providerName) + fastModel = hawkconfig.DefaultModelForProvider(providerName) } if strings.TrimSpace(fastModel) == "" { m.messages = append(m.messages, displayMsg{role: "error", content: "Fast mode: no catalog model resolved for this provider"}) @@ -166,7 +166,7 @@ func init() { level := strings.ToLower(args[0]) switch level { case "low", "medium", "high": - _ = graycodeconfig.SetGlobalSetting("reasoningEffort", level) + _ = hawkconfig.SetGlobalSetting("reasoningEffort", level) m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Reasoning effort → %s", level)}) default: m.messages = append(m.messages, displayMsg{role: "error", content: "Valid levels: low, medium, high"}) @@ -320,14 +320,14 @@ func init() { description: "toggle compact mode (removes outer padding)", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - settings := graycodeconfig.LoadGlobalSettings() + settings := hawkconfig.LoadGlobalSettings() current := settings.CompactMode newVal := !current valStr := "false" if newVal { valStr = "true" } - if err := graycodeconfig.SetGlobalSetting("compact_mode", valStr); err != nil { + if err := hawkconfig.SetGlobalSetting("compact_mode", valStr); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { state := "disabled" @@ -349,7 +349,7 @@ func init() { usage: "/scroll-speed <1-100>", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { if len(args) < 1 { - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-speed <1-100>\nCurrent: %d", graycodeconfig.LoadGlobalSettings().ScrollSpeed)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-speed <1-100>\nCurrent: %d", hawkconfig.LoadGlobalSettings().ScrollSpeed)}) return m, nil } speed, err := strconv.Atoi(args[0]) @@ -357,7 +357,7 @@ func init() { m.messages = append(m.messages, displayMsg{role: "error", content: "Scroll speed must be 1-100"}) return m, nil } - if err := graycodeconfig.SetGlobalSetting("scroll_speed", args[0]); err != nil { + if err := hawkconfig.SetGlobalSetting("scroll_speed", args[0]); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: "Scroll speed → " + args[0]}) @@ -372,7 +372,7 @@ func init() { description: "toggle natural scrolling (invert direction)", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - settings := graycodeconfig.LoadGlobalSettings() + settings := hawkconfig.LoadGlobalSettings() current := settings.InvertScroll newVal := !current valStr := "false" @@ -383,7 +383,7 @@ func init() { if newVal { enabled = "enabled" } - if err := graycodeconfig.SetGlobalSetting("invert_scroll", valStr); err != nil { + if err := hawkconfig.SetGlobalSetting("invert_scroll", valStr); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: "Natural scrolling " + enabled}) @@ -399,13 +399,13 @@ func init() { usage: "/scroll-mode ", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { if len(args) < 1 { - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-mode \nCurrent: %s", graycodeconfig.LoadGlobalSettings().ScrollMode)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-mode \nCurrent: %s", hawkconfig.LoadGlobalSettings().ScrollMode)}) return m, nil } mode := strings.ToLower(args[0]) switch mode { case "auto", "wheel", "trackpad": - if err := graycodeconfig.SetGlobalSetting("scrollmode", mode); err != nil { + if err := hawkconfig.SetGlobalSetting("scrollmode", mode); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Scroll mode → %s", mode)}) @@ -444,7 +444,7 @@ func init() { level = "256-color" } - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Terminal Setup Recommendations:\n\nColor Support: %s\nScroll Mode: %s (use /scroll-mode to change)\nScroll Speed: %d (use /scroll-speed to change)\nCompact Mode: %v (use /compact-mode to toggle)\n\nTips:\n- Set COLORTERM=truecolor for best color experience\n- Use tmux with set -g default-terminal \"tmux-256color\" for 256-color support\n- Enable mouse reporting in your terminal for full TUI interaction", level, graycodeconfig.LoadGlobalSettings().ScrollMode, graycodeconfig.LoadGlobalSettings().ScrollSpeed, graycodeconfig.LoadGlobalSettings().CompactMode)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Terminal Setup Recommendations:\n\nColor Support: %s\nScroll Mode: %s (use /scroll-mode to change)\nScroll Speed: %d (use /scroll-speed to change)\nCompact Mode: %v (use /compact-mode to toggle)\n\nTips:\n- Set COLORTERM=truecolor for best color experience\n- Use tmux with set -g default-terminal \"tmux-256color\" for 256-color support\n- Enable mouse reporting in your terminal for full TUI interaction", level, hawkconfig.LoadGlobalSettings().ScrollMode, hawkconfig.LoadGlobalSettings().ScrollSpeed, hawkconfig.LoadGlobalSettings().CompactMode)}) return m, nil }, }) @@ -456,7 +456,7 @@ func init() { usage: "/pager-config ", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { if len(args) < 2 { - s := graycodeconfig.LoadGlobalSettings() + s := hawkconfig.LoadGlobalSettings() ln := false messages := fmt.Sprintf("Pager Configuration:\n lines: %d (0 = unlimited)\n linenumbers: %v\n\nUsage: /pager-config ", s.PaginatorLines, ln) if s.PaginatorShowLineNums != nil { @@ -474,7 +474,7 @@ func init() { m.messages = append(m.messages, displayMsg{role: "error", content: "Lines must be a positive number (0 = unlimited)"}) return m, nil } - if err := graycodeconfig.SetGlobalSetting("paginatorlines", value); err != nil { + if err := hawkconfig.SetGlobalSetting("paginatorlines", value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Pager lines → %d", lines)}) @@ -483,13 +483,13 @@ func init() { case "linenumbers", "linenums", "ln": switch strings.ToLower(value) { case "1", "true", "yes", "on": - if err := graycodeconfig.SetGlobalSetting("paginatorshowlinenumbers", "true"); err != nil { + if err := hawkconfig.SetGlobalSetting("paginatorshowlinenumbers", "true"); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: "Pager line numbers → enabled"}) } case "0", "false", "no", "off": - if err := graycodeconfig.SetGlobalSetting("paginatorshowlinenumbers", "false"); err != nil { + if err := hawkconfig.SetGlobalSetting("paginatorshowlinenumbers", "false"); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { m.messages = append(m.messages, displayMsg{role: "system", content: "Pager line numbers → disabled"}) @@ -530,10 +530,10 @@ func init() { // /upgrade — check for updates subcommandRegistry.Register(&delegatingCommand{ name: "upgrade", - description: "check for graycode updates", + description: "check for hawk updates", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - return m.startPromptCommand("/upgrade", "Check for graycode updates and show the latest available version.") + return m.startPromptCommand("/upgrade", "Check for hawk updates and show the latest available version.") }, }) @@ -783,7 +783,7 @@ func init() { style := strings.ToLower(args[0]) switch style { case "concise", "normal", "detailed": - _ = graycodeconfig.SetGlobalSetting("outputStyle", style) + _ = hawkconfig.SetGlobalSetting("outputStyle", style) m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Output style → %s", style)}) default: m.messages = append(m.messages, displayMsg{role: "error", content: "Valid styles: concise, normal, detailed"}) @@ -917,15 +917,15 @@ func init() { }, }) - // /feedback — submit feedback saved to Graycode user state. + // /feedback — submit feedback saved to Hawk user state. subcommandRegistry.Register(&delegatingCommand{ name: "feedback", - description: "submit feedback (saved to Graycode user state)", + description: "submit feedback (saved to Hawk user state)", usage: "/feedback ", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { body := strings.TrimSpace(strings.TrimPrefix(text, "/feedback")) if body == "" { - m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /feedback \nCaptures session context and saves feedback to Graycode user state."}) + m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /feedback \nCaptures session context and saves feedback to Hawk user state."}) return m, nil } feedDir := filepath.Join(storage.StateDir(), "feedback") @@ -1030,7 +1030,7 @@ func init() { description: "show provider deployment status", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - report, err := graycodeconfig.DeploymentStatusReport(context.Background(), m.session.Model()) + report, err := hawkconfig.DeploymentStatusReport(context.Background(), m.session.Model()) if err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: fmt.Sprintf("Provider status failed: %v", err)}) return m, nil @@ -1046,7 +1046,7 @@ func init() { description: "refresh the model catalog", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - summary, err := graycodeconfig.RefreshModelCatalogV1(context.Background()) + summary, err := hawkconfig.RefreshModelCatalogV1(context.Background()) if err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: fmt.Sprintf("Model catalog refresh failed: %v", err)}) return m, nil diff --git a/cmd/chat_subcommand_soul.go b/cmd/chat_subcommand_soul.go index c4710ce40..0dd3f3bc6 100644 --- a/cmd/chat_subcommand_soul.go +++ b/cmd/chat_subcommand_soul.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // soulSubcommand implements the /soul slash command. It shows the diff --git a/cmd/chat_subcommand_spec.go b/cmd/chat_subcommand_spec.go index c6266fb03..7f2f2efee 100644 --- a/cmd/chat_subcommand_spec.go +++ b/cmd/chat_subcommand_spec.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/spec" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/spec" ) // specSubcommand implements the /spec slash command: starts (or reports diff --git a/cmd/chat_subcommand_start.go b/cmd/chat_subcommand_start.go index 858f91dab..0e2375bfd 100644 --- a/cmd/chat_subcommand_start.go +++ b/cmd/chat_subcommand_start.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // startSubcommand is the first-run / guided success path. @@ -32,7 +32,7 @@ func (c *startSubcommand) Handle(m *chatModel, args []string, text string) (tea. } var b strings.Builder - b.WriteString("## Graycode quick start\n\n") + b.WriteString("## Hawk quick start\n\n") // 1. Model / session if m.session != nil { diff --git a/cmd/chat_subcommand_status.go b/cmd/chat_subcommand_status.go index a07063d93..dd4cc3838 100644 --- a/cmd/chat_subcommand_status.go +++ b/cmd/chat_subcommand_status.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // statusSubcommand implements the /status slash command. It prints @@ -35,7 +35,7 @@ func buildStatusInfo(m *chatModel) string { visible := 0 if m.registry != nil { toolCount = len(m.registry.PrimaryTools()) - visible = len(m.registry.GraycodeRouterTools()) + visible = len(m.registry.EyrieTools()) } work := m.session.WorkMode() if work == "" { diff --git a/cmd/chat_subcommand_trust.go b/cmd/chat_subcommand_trust.go index ebcd3acbd..f205b8ccd 100644 --- a/cmd/chat_subcommand_trust.go +++ b/cmd/chat_subcommand_trust.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // trustSubcommand manages folder trust from the chat TUI. diff --git a/cmd/chat_subcommand_version.go b/cmd/chat_subcommand_version.go index 4b3c6b1e0..98ff13528 100644 --- a/cmd/chat_subcommand_version.go +++ b/cmd/chat_subcommand_version.go @@ -7,16 +7,16 @@ import ( ) // versionSubcommand implements the /version slash command. It prints -// the running graycode version. Follows the SubcommandRegistry pattern +// the running hawk version. Follows the SubcommandRegistry pattern // demonstrated in chat_subcommand_branch.go. type versionSubcommand struct{} func (v *versionSubcommand) Name() string { return "version" } func (v *versionSubcommand) Aliases() []string { return nil } -func (v *versionSubcommand) Description() string { return "print the running graycode version" } +func (v *versionSubcommand) Description() string { return "print the running hawk version" } func (v *versionSubcommand) Usage() string { return "" } func (v *versionSubcommand) Handle(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("graycode v%s", DisplayVersion())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("hawk v%s", DisplayVersion())}) return m, nil } diff --git a/cmd/chat_subcommand_voice.go b/cmd/chat_subcommand_voice.go index acc95f7d2..0c16b47e7 100644 --- a/cmd/chat_subcommand_voice.go +++ b/cmd/chat_subcommand_voice.go @@ -47,7 +47,7 @@ func (v *voiceSubcommand) Handle(m *chatModel, args []string, text string) (tea. // outcome as a voiceResultMsg handled in the update loop. func (v *voiceSubcommand) recordAndTranscribe() tea.Cmd { return func() tea.Msg { - tmpFile := filepath.Join(os.TempDir(), "graycode_voice_input.wav") + tmpFile := filepath.Join(os.TempDir(), "hawk_voice_input.wav") var recordCmd *exec.Cmd if _, err := exec.LookPath("sox"); err == nil { recordCmd = exec.Command("sox", "-d", tmpFile, "trim", "0", "10") // #nosec G204 -- fixed command 'sox' resolved via exec.LookPath diff --git a/cmd/chat_submit.go b/cmd/chat_submit.go index 19e91d394..6b04039ff 100644 --- a/cmd/chat_submit.go +++ b/cmd/chat_submit.go @@ -11,11 +11,11 @@ import ( tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // submitUserMessage handles Enter on a non-empty prompt (slash commands, shell, or agent turn). @@ -93,7 +93,7 @@ func (m chatModel) submitUserMessage() (chatModel, tea.Cmd) { } return m, cmd } - if setup := graycodeconfig.EvaluateSetupCached(context.Background()); setup.NeedsSetup { + if setup := hawkconfig.EvaluateSetupCached(context.Background()); setup.NeedsSetup { hint := setup.Hint if hint == "" { hint = "Complete setup in /config (keychain + model)." diff --git a/cmd/chat_terminal_mouse.go b/cmd/chat_terminal_mouse.go index 47b0e0a03..7221aace6 100644 --- a/cmd/chat_terminal_mouse.go +++ b/cmd/chat_terminal_mouse.go @@ -11,7 +11,7 @@ import ( // scroll events to arrive as literal "[<65;99;16M" KeyRunes in the input. const ( disableMouseCSI = "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l" - // Explicitly disable any-motion mode left by an older Graycode process, then + // Explicitly disable any-motion mode left by an older Hawk process, then // enable button/cell tracking. Wheel and click events still arrive, while // ordinary pointer movement no longer floods the Bubble Tea update loop. enableMouseCSI = "\x1b[?1003l\x1b[?1006h\x1b[?1002h" diff --git a/cmd/chat_tools.go b/cmd/chat_tools.go index 829aa5280..bb8a64bd9 100644 --- a/cmd/chat_tools.go +++ b/cmd/chat_tools.go @@ -6,9 +6,9 @@ import ( "sync" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/lsp" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/lsp" + "github.com/GrayCodeAI/hawk/internal/tool" ) // This file holds the tool-registry construction used by the chat TUI: @@ -192,7 +192,7 @@ func optionalTools() []tool.Tool { } } -func configuredStartupMCPServers(settings graycodeconfig.Settings) []startupMCPServerSpec { +func configuredStartupMCPServers(settings hawkconfig.Settings) []startupMCPServerSpec { servers := make([]startupMCPServerSpec, 0, len(settings.MCPServers)+len(mcpServers)) for _, cfg := range settings.MCPServers { if cfg.Name == "" { @@ -241,7 +241,7 @@ func configuredStartupMCPServers(settings graycodeconfig.Settings) []startupMCPS // non-expired OAuth token is stored for this server (see // internal/tool/mcp_auth.go). A configured static Authorization header, if // any, takes precedence over the auto-injected one. -func mergedMCPHeaders(cfg graycodeconfig.MCPServerConfig) map[string]string { +func mergedMCPHeaders(cfg hawkconfig.MCPServerConfig) map[string]string { headers := make(map[string]string, len(cfg.Headers)+1) for k, v := range cfg.Headers { headers[k] = v @@ -290,7 +290,7 @@ func loadStartupMCPToolSetsWith(loadMCP func(context.Context, string, string, .. return results } -func defaultRegistry(settings graycodeconfig.Settings) (*tool.Registry, error) { +func defaultRegistry(settings hawkconfig.Settings) (*tool.Registry, error) { // Load essential tools first for fast startup tools := essentialTools() if tool.IsPowerShellAvailable() { diff --git a/cmd/chat_tools_lsp_test.go b/cmd/chat_tools_lsp_test.go index ab29d5b5d..373efeb5c 100644 --- a/cmd/chat_tools_lsp_test.go +++ b/cmd/chat_tools_lsp_test.go @@ -3,12 +3,12 @@ package cmd import ( "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestDefaultRegistryWiresLanguageServerManager(t *testing.T) { - registry, err := defaultRegistry(graycodeconfig.Settings{}) + registry, err := defaultRegistry(hawkconfig.Settings{}) if err != nil { t.Fatal(err) } diff --git a/cmd/chat_tools_test.go b/cmd/chat_tools_test.go index e98dfca4e..5e226751e 100644 --- a/cmd/chat_tools_test.go +++ b/cmd/chat_tools_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/tool" ) type registryTestTool struct { @@ -72,8 +72,8 @@ func TestLoadStartupMCPToolSets_UsesTimeoutAndPreservesOrder(t *testing.T) { } func TestConfiguredStartupMCPServers_DispatchesByType(t *testing.T) { - settings := graycodeconfig.Settings{ - MCPServers: []graycodeconfig.MCPServerConfig{ + settings := hawkconfig.Settings{ + MCPServers: []hawkconfig.MCPServerConfig{ {Name: "stdio-default", Command: "stdio-mcp"}, {Name: "stdio-explicit", Type: "stdio", Command: "stdio-mcp-2"}, {Name: "http-server", Type: "http", URL: "https://example.com/mcp"}, @@ -176,7 +176,7 @@ func TestLoadStartupMCPToolSets_DispatchesRemoteSpecsToRemoteLoader(t *testing.T } func TestMergedMCPHeaders_ConfiguredAuthorizationTakesPrecedence(t *testing.T) { - cfg := graycodeconfig.MCPServerConfig{ + cfg := hawkconfig.MCPServerConfig{ Name: "svc", Headers: map[string]string{"Authorization": "Bearer static-token", "X-Other": "1"}, } @@ -220,8 +220,8 @@ func TestDefaultRegistry_SkipsFailedStartupMCPServers(t *testing.T) { return nil, errors.New("boom") } - registry, err := defaultRegistry(graycodeconfig.Settings{ - MCPServers: []graycodeconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}, + registry, err := defaultRegistry(hawkconfig.Settings{ + MCPServers: []hawkconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}, }) if err != nil { t.Fatalf("defaultRegistry returned error: %v", err) diff --git a/cmd/chat_turn.go b/cmd/chat_turn.go index 1dbe0d04e..048e991cc 100644 --- a/cmd/chat_turn.go +++ b/cmd/chat_turn.go @@ -2,7 +2,7 @@ package cmd // turnHadThinkingOnly reports whether the latest user turn ended with internal // reasoning visible but no assistant reply or tool activity. This is the TUI -// symptom of graycode-router's ResponseErrorOnlyReasoning health check. +// symptom of eyrie's ResponseErrorOnlyReasoning health check. func turnHadThinkingOnly(messages []displayMsg) bool { if len(messages) == 0 { return false diff --git a/cmd/chat_update.go b/cmd/chat_update.go index 37807d4c2..10ea470fc 100644 --- a/cmd/chat_update.go +++ b/cmd/chat_update.go @@ -12,12 +12,12 @@ import ( "charm.land/bubbles/v2/spinner" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/spec" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/spec" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // This file holds the Bubble Tea event loop for the chat TUI: the central @@ -584,7 +584,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { chosen, handled := m.themePicker.Update(msg) if handled { if chosen != nil { - if err := graycodeconfig.SetGlobalSetting("theme", chosen.Name); err != nil { + if err := hawkconfig.SetGlobalSetting("theme", chosen.Name); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) } else { // Apply immediately — repaints with full palette on next frame. @@ -801,7 +801,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } - // Container failed and is retryable. Graycode is fail-closed: the only + // Container failed and is retryable. Hawk is fail-closed: the only // recovery path is to restore Docker isolation. if m.containerRetryable { switch msg.String() { @@ -1099,7 +1099,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.configSaving = false if msg.err != nil { if m.configOpen { - m.configNotice = sanitizeConfigNotice(graycodeconfig.FormatConfigProviderError(msg.provider, msg.err)) + m.configNotice = sanitizeConfigNotice(hawkconfig.FormatConfigProviderError(msg.provider, msg.err)) m.viewDirty = true m.updateViewportContent() } @@ -1116,7 +1116,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.configNotice = "" } } else if m.configOpen { - m.configNotice = graycodeconfig.CatalogEmptyHint(context.Background()) + m.configNotice = hawkconfig.CatalogEmptyHint(context.Background()) } if m.session != nil && msg.provider != "" { gw, _ := m.sessionGatewayModel() @@ -1411,8 +1411,8 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { line := fmt.Sprintf( "Context compacted (%s): ~%s → ~%s tokens", msg.strategy, - formatGraycodeTokenCount(msg.tokensBefore), - formatGraycodeTokenCount(msg.tokensAfter), + formatHawkTokenCount(msg.tokensBefore), + formatHawkTokenCount(msg.tokensAfter), ) m.messages = append(m.messages, displayMsg{role: "system", content: line}) m.invalidateConnStatus() @@ -1496,7 +1496,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Send terminal notification if terminal was not focused during the // turn and the agent produced output (not just tool activity). if m.backgrounded && !wasCancelled && hadOutput { - sendTerminalNotification("graycode", "Agent turn complete") + sendTerminalNotification("hawk", "Agent turn complete") } m.backgrounded = false m.notifiedComplete = false diff --git a/cmd/chat_update_test.go b/cmd/chat_update_test.go index a0a4ce2e2..28645a38e 100644 --- a/cmd/chat_update_test.go +++ b/cmd/chat_update_test.go @@ -6,8 +6,8 @@ import ( "time" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/sandbox" ) // TestContainerStatusErrFallsBackToHostAutonomy preserves the historical test diff --git a/cmd/chat_view.go b/cmd/chat_view.go index af4704fc9..b6a3bceec 100644 --- a/cmd/chat_view.go +++ b/cmd/chat_view.go @@ -11,7 +11,7 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/mattn/go-runewidth" ) @@ -61,11 +61,11 @@ var ( reCreator = regexp.MustCompile(`(?i)(made|created|developed|built|trained|designed)\s+by\s+(?:a\s+company\s+called\s+|a\s+team\s+(?:at|called)\s+|the\s+team\s+at\s+)?\*{0,2}(Moonshot\s*AI|OpenAI|Anthropic|Google|Google\s*DeepMind|DeepMind|Meta|Meta\s*AI|Alibaba|Alibaba\s*Cloud|Mistral\s*AI|xAI|Microsoft|Microsoft\s*AI|Amazon|AWS|Cohere|01\.AI|Baidu|Huawei|IBM|Nvidia|EleutherAI|Hugging\s*Face|AI21\s*Labs|Yandex|Databricks|StepFun|Xiaomi|Sarvam\s*AI|MiniMax|BharatGen|Z\.ai|Zhipu\s*AI|Cerebras|Technology\s*Innovation\s*Institute|TII|Inflection\s*AI|Stability\s*AI|Anysphere|Cognition\s*AI|Scale\s*AI|Sakana\s*AI)\*{0,2}`) ) -// sanitizeIdentity replaces model self-identifications with "graycode" / "GrayCode AI". +// sanitizeIdentity replaces model self-identifications with "hawk" / "GrayCode AI". func sanitizeIdentity(s string) string { s = reModelName.ReplaceAllStringFunc(s, func(m string) string { parts := reModelName.FindStringSubmatch(m) - return parts[1] + " graycode" + return parts[1] + " hawk" }) s = reCreator.ReplaceAllString(s, "${1} by GrayCode AI") return s @@ -547,7 +547,7 @@ func renderPermissionBox(summary string, width int, timeoutAt time.Time) string } } body := lipgloss.JoinVertical(lipgloss.Left, bodyParts...) - options := lipgloss.NewStyle().Foreground(graycodeColor).Render("[y] allow once [n] deny [a] always allow tool [d] always deny tool") + options := lipgloss.NewStyle().Foreground(hawkColor).Render("[y] allow once [n] deny [a] always allow tool [d] always deny tool") hint := lipgloss.NewStyle().Foreground(textMuted).Render("Esc cancels · prompt times out after 5 minutes") rows := []string{title, "", body} @@ -642,7 +642,7 @@ func renderReflectionBox(reflection string, width int) string { boxW = 40 } - titleStyle := lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + titleStyle := lipgloss.NewStyle().Foreground(hawkColor).Bold(true) labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("39")).Bold(true) // blue contentStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("252")) // light gray @@ -672,7 +672,7 @@ func renderReflectionBox(reflection string, width int) string { border := lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). - BorderForeground(graycodeColor). + BorderForeground(hawkColor). Width(boxW). Padding(0, 1) @@ -711,12 +711,12 @@ func (m *chatModel) renderTokenCounters() string { var b strings.Builder b.WriteString(ansiMagenta + ansiBold + "↓" + ansiReset) b.WriteString(ansiMagenta) - b.WriteString(formatGraycodeTokenCount(outTok)) + b.WriteString(formatHawkTokenCount(outTok)) b.WriteString(ansiReset) b.WriteString(ansiDim + " " + ansiReset) b.WriteString(ansiCyan + ansiBold + "↑" + ansiReset) b.WriteString(ansiCyan) - b.WriteString(formatGraycodeTokenCount(inTok)) + b.WriteString(formatHawkTokenCount(inTok)) b.WriteString(ansiReset) return b.String() } @@ -753,9 +753,9 @@ func (m *chatModel) tokenOutputTarget() int { return m.turnEstimatedOutputRunes / 4 } -// formatGraycodeTokenCount renders a token count in graycode's compact form: +// formatHawkTokenCount renders a token count in hawk's compact form: // ≥1m → "1.5m", ≥10k → "150k", else raw digits. -func formatGraycodeTokenCount(tokens int) string { +func formatHawkTokenCount(tokens int) string { if tokens <= 0 { return "0" } diff --git a/cmd/chat_view_test.go b/cmd/chat_view_test.go index 2d59fce62..11927fe25 100644 --- a/cmd/chat_view_test.go +++ b/cmd/chat_view_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestFormatGraycodeTokenCount(t *testing.T) { +func TestFormatHawkTokenCount(t *testing.T) { t.Parallel() cases := []struct { in int @@ -25,9 +25,9 @@ func TestFormatGraycodeTokenCount(t *testing.T) { tc := tc t.Run("", func(t *testing.T) { t.Parallel() - got := formatGraycodeTokenCount(tc.in) + got := formatHawkTokenCount(tc.in) if got != tc.want { - t.Fatalf("formatGraycodeTokenCount(%d) = %q, want %q", tc.in, got, tc.want) + t.Fatalf("formatHawkTokenCount(%d) = %q, want %q", tc.in, got, tc.want) } }) } diff --git a/cmd/chat_viewport.go b/cmd/chat_viewport.go index 7a350dd11..5df6beef6 100644 --- a/cmd/chat_viewport.go +++ b/cmd/chat_viewport.go @@ -237,7 +237,7 @@ func (m chatModel) effectiveWheelY(msg tea.MouseMsg) int { return y } -// syncViewportMouseWheel disables bubbletea viewport auto-wheel; graycode routes wheel +// syncViewportMouseWheel disables bubbletea viewport auto-wheel; hawk routes wheel // events manually so chat scrolls only when the pointer is over the chat pane. func (m chatModel) syncViewportMouseWheel() chatModel { m.viewport.MouseWheelEnabled = false diff --git a/cmd/chat_viewport_render.go b/cmd/chat_viewport_render.go index b25038a09..b47d721c4 100644 --- a/cmd/chat_viewport_render.go +++ b/cmd/chat_viewport_render.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // Viewport render cache — avoids re-wrapping and re-rendering markdown for the @@ -30,7 +30,7 @@ const ( ) func renderDisplayMessage(msg displayMsg, i int, messages []displayMsg, viewWidth int, expanded map[int]bool) string { - graycodeC := ansiOrange + hawkC := ansiOrange rst := ansiReset bgDark := "\033[48;2;30;30;40m" @@ -50,7 +50,7 @@ func renderDisplayMessage(msg displayMsg, i int, messages []displayMsg, viewWidt wrappedLines := strings.Split(wrapped, "\n") for li, wl := range wrappedLines { if li == 0 { - b.WriteString(bgDark + graycodeC + "█" + rst + bgDark + " " + wl) + b.WriteString(bgDark + hawkC + "█" + rst + bgDark + " " + wl) } else { b.WriteString(bgDark + " " + wl) } @@ -70,7 +70,7 @@ func renderDisplayMessage(msg displayMsg, i int, messages []displayMsg, viewWidt // message entirely rather than leaving an orphan "◈" line. return "" } - b.WriteString(graycodeC + icons.Robot() + " " + rst + renderMarkdown(content, viewWidth-3)) + b.WriteString(hawkC + icons.Robot() + " " + rst + renderMarkdown(content, viewWidth-3)) case "tool_use": b.WriteString(toolStyle.Render(icons.CircleFilled() + " " + msg.content)) case "tool_result": diff --git a/cmd/chat_viewport_test.go b/cmd/chat_viewport_test.go index fb67e688f..af9988b9b 100644 --- a/cmd/chat_viewport_test.go +++ b/cmd/chat_viewport_test.go @@ -8,7 +8,7 @@ import ( "charm.land/bubbles/v2/viewport" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestRouteKeyToViewport_ArrowsInPromptFocus(t *testing.T) { @@ -84,20 +84,20 @@ func TestShouldRouteMouseToViewport_SplitPaneUX(t *testing.T) { } func TestSyncViewportMouseWheel_ManualRouting(t *testing.T) { - t.Setenv("GRAYCODE_MOUSE", "") + t.Setenv("HAWK_MOUSE", "") vp := viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)) m := chatModel{viewport: vp, uiFocus: focusPrompt} m = m.syncViewportMouseWheel() if m.viewport.MouseWheelEnabled { - t.Fatal("viewport auto-wheel must stay off; graycode routes wheel by pane") + t.Fatal("viewport auto-wheel must stay off; hawk routes wheel by pane") } } func TestSyncViewportMouseWheel_DisabledWithOptOut(t *testing.T) { - t.Setenv("GRAYCODE_MOUSE", "0") + t.Setenv("HAWK_MOUSE", "0") vp := viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)) disabled := false - m := chatModel{viewport: vp, uiFocus: focusPrompt, settings: graycodeconfig.Settings{TuiMouse: &disabled}} + m := chatModel{viewport: vp, uiFocus: focusPrompt, settings: hawkconfig.Settings{TuiMouse: &disabled}} m = m.syncViewportMouseWheel() if m.viewport.MouseWheelEnabled { t.Fatal("wheel should be disabled when mouse capture is off") diff --git a/cmd/chat_welcome.go b/cmd/chat_welcome.go index edb316cd9..0e62de660 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -9,15 +9,15 @@ import ( "github.com/mattn/go-runewidth" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) type welcomeStatusSnapshot struct { - setup graycodeconfig.SetupState + setup hawkconfig.SetupState agentsOK bool } @@ -53,8 +53,8 @@ func (m chatModel) welcomeDockerRunning() *bool { func loadWelcomeStatusSnapshot() welcomeStatusSnapshot { ctx := context.Background() return welcomeStatusSnapshot{ - setup: graycodeconfig.EvaluateSetupCached(ctx), - agentsOK: graycodeconfig.LoadAgentsMD() != "", + setup: hawkconfig.EvaluateSetupCached(ctx), + agentsOK: hawkconfig.LoadAgentsMD() != "", } } @@ -100,8 +100,8 @@ func (m *chatModel) rebuildWelcomeCache(opts ...any) { m.welcomeCache = buildWelcomeMessageWithSnapshot(m.session, m.sessionID, m.registry, nil, m.settings, skillsCount, connectedMCPCount(m.registry), frame, width, height, m.welcomeDockerRunning(), m.welcomeStatusSnapshot(), m.containerEnabled, m.lastCommand) } -// buildWelcomeMessage renders the branded inline GRAYCODE welcome block. -func buildWelcomeMessage(sess *engine.Session, sessionID string, registry *tool.Registry, saved *session.Session, settings graycodeconfig.Settings, skillsCount int, blinkClosed bool, width, height int, dockerRunning *bool) string { +// buildWelcomeMessage renders the branded inline HAWK welcome block. +func buildWelcomeMessage(sess *engine.Session, sessionID string, registry *tool.Registry, saved *session.Session, settings hawkconfig.Settings, skillsCount int, blinkClosed bool, width, height int, dockerRunning *bool) string { frame := 0 if blinkClosed { frame = 2 @@ -109,8 +109,8 @@ func buildWelcomeMessage(sess *engine.Session, sessionID string, registry *tool. return buildWelcomeMessageWithSnapshot(sess, sessionID, registry, saved, settings, skillsCount, connectedMCPCount(registry), frame, width, height, dockerRunning, loadWelcomeStatusSnapshot(), false, "") } -func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, registry *tool.Registry, saved *session.Session, settings graycodeconfig.Settings, skillsCount, mcpCount int, eyeFrame int, width, height int, dockerRunning *bool, snapshot welcomeStatusSnapshot, containerMode bool, lastCommand string) string { - // Talon Gold is used for the GRAYCODE wordmark. All escapes come from the +func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, registry *tool.Registry, saved *session.Session, settings hawkconfig.Settings, skillsCount, mcpCount int, eyeFrame int, width, height int, dockerRunning *bool, snapshot welcomeStatusSnapshot, containerMode bool, lastCommand string) string { + // Talon Gold is used for the HAWK wordmark. All escapes come from the // theme palette (theme.go) so a rebrand stays a one-file change. logoC := ansiOrange dimC := ansiDim @@ -148,7 +148,7 @@ func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, reg return strings.Repeat(" ", pad) + styled } - art := graycodeLogoArtLines + art := hawkLogoArtLines var eyeGlyph string switch eyeFrame { case 1, 3: @@ -157,13 +157,13 @@ func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, reg eyeGlyph = "|-\\/-|" } if eyeGlyph != "" { - art = append([]string(nil), graycodeLogoArtLines...) + art = append([]string(nil), hawkLogoArtLines...) for i, line := range art { art[i] = strings.Replace(line, "|0\\/0|", eyeGlyph, 1) } } - // Inject the version into the graycode's body — centered in the lower gap. + // Inject the version into the hawk's body — centered in the lower gap. verStr := DisplayVersion() if verStr != "" && !strings.HasPrefix(verStr, "v") && !strings.HasPrefix(verStr, "V") { verStr = "v" + verStr @@ -186,13 +186,13 @@ func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, reg if tight { // Compact single-line wordmark for small terminals — version sits - // inline so it's always visible even when the full graycode is hidden. + // inline so it's always visible even when the full hawk is hidden. verDisplay := DisplayVersion() if verDisplay != "" && !strings.HasPrefix(verDisplay, "v") && !strings.HasPrefix(verDisplay, "V") { verDisplay = "v" + verDisplay } - compactArt := logoC + "GRAYCODE" + rst + " " + verDisplay - b.WriteString(center(runewidth.StringWidth("GRAYCODE "+verDisplay), compactArt) + "\n") + compactArt := logoC + "HAWK" + rst + " " + verDisplay + b.WriteString(center(runewidth.StringWidth("HAWK "+verDisplay), compactArt) + "\n") } else { artW := blockLinesWidth(art) for _, line := range art { @@ -373,7 +373,7 @@ func toolListSummary(registry *tool.Registry) string { if registry == nil { return "No tools enabled." } - tools := registry.GraycodeRouterTools() + tools := registry.EyrieTools() registered := len(registry.PrimaryTools()) if len(tools) == 0 { return "No tools enabled." @@ -403,7 +403,7 @@ func envSummary(provider, model string) string { func envSummaryWithSelection(provider, model string, includeSelection bool) string { var providers []string - for _, gateway := range graycodeconfig.GatewayStatuses(context.Background(), provider, model) { + for _, gateway := range hawkconfig.GatewayStatuses(context.Background(), provider, model) { providers = append(providers, gateway.ID) } sort.Strings(providers) @@ -411,17 +411,17 @@ func envSummaryWithSelection(provider, model string, includeSelection bool) stri if includeSelection { b.WriteString(fmt.Sprintf("Provider: %s\nModel: %s\n\n", provider, model)) } - b.WriteString(fmt.Sprintf("Credentials (%s):\n", graycodeconfig.CredentialStoreName())) + b.WriteString(fmt.Sprintf("Credentials (%s):\n", hawkconfig.CredentialStoreName())) for _, providerID := range providers { - b.WriteString(fmt.Sprintf(" %s: %s\n", providerID, graycodeconfig.EnvKeyStatus(providerID))) + b.WriteString(fmt.Sprintf(" %s: %s\n", providerID, hawkconfig.EnvKeyStatus(providerID))) } return strings.TrimRight(b.String(), "\n") } -func configCommandSummary(settings graycodeconfig.Settings) string { +func configCommandSummary(settings hawkconfig.Settings) string { _ = settings - providerName := displayConfigValue(graycodeconfig.ActiveProvider(context.Background())) - modelName := displayConfigValue(graycodeconfig.ActiveModel(context.Background())) + providerName := displayConfigValue(hawkconfig.ActiveProvider(context.Background())) + modelName := displayConfigValue(hawkconfig.ActiveModel(context.Background())) keys := configuredKeyList() keysColor := infoSky if keys == "(none)" { @@ -431,15 +431,15 @@ func configCommandSummary(settings graycodeconfig.Settings) string { /config → paste API key (OS keychain) + pick model /path → verify readiness in TUI - graycode path (CLI) + hawk path (CLI) %s: %s %s %s %s %s %s -Model catalog and routing live in graycode-router — graycode is the UI only.`, - auditTint("Setup (graycode-router)", textPrimary), +Model catalog and routing live in eyrie — hawk is the UI only.`, + auditTint("Setup (eyrie)", textPrimary), auditTint("Current", textPrimary), auditTint("provider:", textMuted), auditTint(providerName, infoSky), auditTint("model:", textMuted), auditTint(modelName, infoSky), @@ -447,7 +447,7 @@ Model catalog and routing live in graycode-router — graycode is the UI only.`, } func apiKeyConfigSummary() string { - return auditTint("API keys ("+graycodeconfig.CredentialStoreName()+")", textPrimary) + "\n" + indentedAPIKeyLines() + return auditTint("API keys ("+hawkconfig.CredentialStoreName()+")", textPrimary) + "\n" + indentedAPIKeyLines() } func configuredKeyList() string { @@ -493,11 +493,11 @@ func apiKeyStatusColor(status string) color.Color { } func apiKeyStatusLines() []string { - providers := graycodeconfig.AllSetupGateways() + providers := hawkconfig.AllSetupGateways() sort.Strings(providers) var lines []string for _, provider := range providers { - lines = append(lines, fmt.Sprintf("%s: %s", provider, graycodeconfig.EnvKeyStatus(provider))) + lines = append(lines, fmt.Sprintf("%s: %s", provider, hawkconfig.EnvKeyStatus(provider))) } return lines } diff --git a/cmd/chat_yolo_confirm_test.go b/cmd/chat_yolo_confirm_test.go index ae4ec4236..b6987c80f 100644 --- a/cmd/chat_yolo_confirm_test.go +++ b/cmd/chat_yolo_confirm_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestYOLOConfirm_PendingConsumesNextInput(t *testing.T) { diff --git a/cmd/checkpoint.go b/cmd/checkpoint.go index 4d0325b6a..de7a04d4a 100644 --- a/cmd/checkpoint.go +++ b/cmd/checkpoint.go @@ -5,19 +5,19 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" "github.com/spf13/cobra" ) // checkpointCmd groups named session-checkpoint operations: snapshot the current // (or latest) session under a label, list snapshots, restore one, or delete one. // This is additive on top of `--resume `; named checkpoints let you save a -// labeled point you can come back to with `graycode resume `. +// labeled point you can come back to with `hawk resume `. var checkpointCmd = &cobra.Command{ Use: "checkpoint", Short: "Save and restore named session checkpoints", Long: `checkpoint snapshots a session under a human-friendly label so you can -return to it later with "graycode resume ". +return to it later with "hawk resume ". Subcommands: save Snapshot the latest session in this directory under @@ -49,7 +49,7 @@ var checkpointSaveCmd = &cobra.Command{ return err } cmd.Printf("%s\n", auditTint("Saved checkpoint ", doneGreen)+auditTint(cp.Name, textPrimary)+auditTint(fmt.Sprintf(" (session %s, %d messages, %s/%s)", cp.Session.ID, len(cp.Session.Messages), cp.Session.Provider, cp.Session.Model), textMuted)) - cmd.Printf("%s\n", auditTint("Resume with: graycode resume "+name, textMuted)) + cmd.Printf("%s\n", auditTint("Resume with: hawk resume "+name, textMuted)) return nil }, } @@ -121,12 +121,12 @@ var checkpointDeleteCmd = &cobra.Command{ }, } -// resumeCmd is a top-level convenience for `graycode resume `: it restores a +// resumeCmd is a top-level convenience for `hawk resume `: it restores a // named checkpoint into a session file and tells the user how to continue it. var resumeCmd = &cobra.Command{ Use: "resume ", Short: "Restore a named session checkpoint and resume it", - Long: `resume restores a session previously saved with "graycode checkpoint save" + Long: `resume restores a session previously saved with "hawk checkpoint save" into a resumable session, then prints the command to continue the conversation.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -150,7 +150,7 @@ func restoreNamedCheckpoint(cmd *cobra.Command, name string) error { return fmt.Errorf("restore session: %w", err) } cmd.Printf("%s\n", auditTint("Restored checkpoint ", doneGreen)+auditTint(cp.Name, textPrimary)+auditTint(fmt.Sprintf(" into session %s (%d messages, %s/%s)", cp.Session.ID, len(cp.Session.Messages), cp.Session.Provider, cp.Session.Model), textMuted)) - cmd.Printf("%s\n", auditTint("Continue with: graycode --resume "+cp.Session.ID, textMuted)) + cmd.Printf("%s\n", auditTint("Continue with: hawk --resume "+cp.Session.ID, textMuted)) return nil } diff --git a/cmd/cli_contracts_test.go b/cmd/cli_contracts_test.go index 175124008..6fb96ef5b 100644 --- a/cmd/cli_contracts_test.go +++ b/cmd/cli_contracts_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" ) func TestResumeRecoveredSession_StartsChatFlow(t *testing.T) { @@ -62,7 +62,7 @@ func TestResumeRecoveredSession_StartsChatFlow(t *testing.T) { } func TestPrepareSession_ResumeUsesRecoveryPath(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) oldResumeID := resumeID oldContinueFlag := continueFlag @@ -121,7 +121,7 @@ func TestPrepareSession_ResumeUsesRecoveryPath(t *testing.T) { t.Fatalf("loaded persisted messages = %d, want %d", sess.MessageCount(), len(saved.Messages)) } - walPath := filepath.Join(os.Getenv("GRAYCODE_STATE_DIR"), "sessions", saved.ID+".wal") + walPath := filepath.Join(os.Getenv("HAWK_STATE_DIR"), "sessions", saved.ID+".wal") if _, err := os.Stat(walPath); !os.IsNotExist(err) { t.Fatalf("expected stale WAL to be removed, stat err = %v", err) } @@ -154,7 +154,7 @@ func TestReplBuiltinResponse_ToolsAndSession(t *testing.T) { sess := engine.NewSession("demo-provider", "demo-model", "system", nil) sess.AddUser("hello") - toolsOut, handled, err := replBuiltinResponse("/tools", sess, graycodeconfig.Settings{}, "session-1") + toolsOut, handled, err := replBuiltinResponse("/tools", sess, hawkconfig.Settings{}, "session-1") if err != nil { t.Fatalf("/tools error = %v", err) } @@ -165,7 +165,7 @@ func TestReplBuiltinResponse_ToolsAndSession(t *testing.T) { t.Fatalf("/tools output missing tool summary: %q", toolsOut) } - sessionOut, handled, err := replBuiltinResponse("/session", sess, graycodeconfig.Settings{}, "session-1") + sessionOut, handled, err := replBuiltinResponse("/session", sess, hawkconfig.Settings{}, "session-1") if err != nil { t.Fatalf("/session error = %v", err) } @@ -181,7 +181,7 @@ func TestReplBuiltinResponse_ToolsAndSession(t *testing.T) { func TestReplBuiltinResponse_Models(t *testing.T) { sess := engine.NewSession("openrouter", "openrouter/auto", "system", nil) - out, handled, err := replBuiltinResponse("/models", sess, graycodeconfig.Settings{}, "session-1") + out, handled, err := replBuiltinResponse("/models", sess, hawkconfig.Settings{}, "session-1") if err != nil { t.Fatalf("/models error = %v", err) } @@ -218,7 +218,7 @@ func TestPluginListSubcommandUsesCobraTree(t *testing.T) { } func TestRecoverCommand_ExecutesResumeFlow(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) oldResumeID := resumeID oldContinueFlag := continueFlag diff --git a/cmd/clipboard.go b/cmd/clipboard.go index 7634fc11a..3cf3f1d38 100644 --- a/cmd/clipboard.go +++ b/cmd/clipboard.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) // copyResult describes where the copied content ended up. @@ -68,7 +68,7 @@ func copyToClipboardNative(text string) error { } // copyToFallbackFile writes the given text to a timestamped file in the -// graycode state directory when the system clipboard is unavailable. This +// hawk state directory when the system clipboard is unavailable. This // ensures the user can still recover copied content from SSH sessions, // containers, or headless environments. func copyToFallbackFile(text string) (string, error) { diff --git a/cmd/clipboard_test.go b/cmd/clipboard_test.go index 7c00dc5f0..cd762fdd2 100644 --- a/cmd/clipboard_test.go +++ b/cmd/clipboard_test.go @@ -30,7 +30,7 @@ func TestClipboardRoundTrip(t *testing.T) { t.Skip("skipping clipboard test on linux (requires xclip/xsel)") } - text := "graycode clipboard test" + text := "hawk clipboard test" if err := copyToClipboardNative(text); err != nil { t.Skipf("native clipboard unavailable: %v", err) } diff --git a/cmd/cloud.go b/cmd/cloud.go index f4f5347cc..ec44e9b7d 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -7,15 +7,15 @@ import ( "runtime" "time" - cloud "github.com/GrayCodeAI/graycode-cli/internal/platform/cloud" + cloud "github.com/GrayCodeAI/hawk/internal/platform/cloud" "github.com/spf13/cobra" ) -var cloudCmd = &cobra.Command{Use: "cloud", Short: "Manage optional Graycode Cloud synchronization"} +var cloudCmd = &cobra.Command{Use: "cloud", Short: "Manage optional Hawk Cloud synchronization"} var cloudConnectCmd = &cobra.Command{ Use: "connect", - Short: "Connect this Graycode device to Graycode Cloud", + Short: "Connect this Hawk device to Hawk Cloud", RunE: func(cmd *cobra.Command, _ []string) error { endpoint, _ := cmd.Flags().GetString("endpoint") deviceID, _ := cmd.Flags().GetString("device-id") @@ -27,22 +27,22 @@ var cloudConnectCmd = &cobra.Command{ if err := cloud.SaveDeviceConfig(cloud.DeviceConfig{Endpoint: endpoint, DeviceID: deviceID, ProjectID: projectID}, token); err != nil { return err } - cmd.Println(auditTint("Graycode Cloud connected. Usage synchronization is opt-in and fail-open.", doneGreen)) + cmd.Println(auditTint("Hawk Cloud connected. Usage synchronization is opt-in and fail-open.", doneGreen)) return nil }, } var cloudLoginCmd = &cobra.Command{ Use: "login", - Short: "Sign in to Graycode Cloud in a browser", + Short: "Sign in to Hawk Cloud in a browser", RunE: func(cmd *cobra.Command, _ []string) error { endpoint, _ := cmd.Flags().GetString("endpoint") label, _ := cmd.Flags().GetString("label") if endpoint == "" { - endpoint = os.Getenv("GRAYCODE_CLOUD_URL") + endpoint = os.Getenv("HAWK_CLOUD_URL") } if endpoint == "" { - return fmt.Errorf("graycode cloud endpoint is required (use --endpoint or GRAYCODE_CLOUD_URL)") + return fmt.Errorf("hawk cloud endpoint is required (use --endpoint or HAWK_CLOUD_URL)") } if label == "" { label, _ = os.Hostname() @@ -54,7 +54,7 @@ var cloudLoginCmd = &cobra.Command{ if err != nil { return err } - cmd.Printf("%s\n", auditTint("Open ", textPrimary)+auditTint(start.VerificationURI, infoSky)+auditTint(" and enter code ", textPrimary)+auditTint(start.UserCode, graycodeColor)) + cmd.Printf("%s\n", auditTint("Open ", textPrimary)+auditTint(start.VerificationURI, infoSky)+auditTint(" and enter code ", textPrimary)+auditTint(start.UserCode, hawkColor)) if err := openBrowser(start.VerificationURI + "?code=" + start.UserCode); err != nil { cmd.Printf("%s\n", auditTint(fmt.Sprintf("Could not open the browser automatically: %v", err), textMuted)) } @@ -82,7 +82,7 @@ var cloudLoginCmd = &cobra.Command{ case "approved": if poll.Token == "" || poll.DeviceID == "" || poll.ProjectID == "" { prog.FailStep(0, "incomplete device authorization") - return fmt.Errorf("graycode cloud returned an incomplete device authorization") + return fmt.Errorf("hawk cloud returned an incomplete device authorization") } if err := cloud.SaveDeviceConfig(cloud.DeviceConfig{Endpoint: endpoint, DeviceID: poll.DeviceID, ProjectID: poll.ProjectID}, poll.Token); err != nil { prog.FailStep(0, err.Error()) @@ -90,39 +90,39 @@ var cloudLoginCmd = &cobra.Command{ } prog.CompleteStep(0) prog.Done() - cmd.Println(auditTint("Graycode Cloud connected for project ", doneGreen) + auditTint(poll.ProjectID, textPrimary) + auditTint(".", doneGreen)) + cmd.Println(auditTint("Hawk Cloud connected for project ", doneGreen) + auditTint(poll.ProjectID, textPrimary) + auditTint(".", doneGreen)) return nil case "expired": prog.FailStep(0, "device authorization expired") - return fmt.Errorf("graycode cloud device authorization expired") + return fmt.Errorf("hawk cloud device authorization expired") default: prog.FailStep(0, fmt.Sprintf("unknown status %q", poll.Status)) - return fmt.Errorf("graycode cloud returned unknown device authorization status %q", poll.Status) + return fmt.Errorf("hawk cloud returned unknown device authorization status %q", poll.Status) } } }, } var cloudStatusCmd = &cobra.Command{ - Use: "status", Short: "Show Graycode Cloud connection status", + Use: "status", Short: "Show Hawk Cloud connection status", RunE: func(cmd *cobra.Command, _ []string) error { client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - cmd.Println(auditTint("Graycode Cloud is not connected.", textMuted)) + cmd.Println(auditTint("Hawk Cloud is not connected.", textMuted)) return nil } - cmd.Println(auditTint("Graycode Cloud connected: ", doneGreen) + auditTint(cfg.Endpoint, textPrimary) + auditTint(fmt.Sprintf(" (device %s, project %s)", cfg.DeviceID, cfg.ProjectID), textMuted)) + cmd.Println(auditTint("Hawk Cloud connected: ", doneGreen) + auditTint(cfg.Endpoint, textPrimary) + auditTint(fmt.Sprintf(" (device %s, project %s)", cfg.DeviceID, cfg.ProjectID), textMuted)) return nil }, } var cloudContextCmd = &cobra.Command{ Use: "context", - Short: "Sync repository context to Graycode Cloud", + Short: "Sync repository context to Hawk Cloud", RunE: func(cmd *cobra.Command, _ []string) error { client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - return fmt.Errorf("graycode cloud is not connected") + return fmt.Errorf("hawk cloud is not connected") } detected, detectErr := detectGitContext(cmd.Context()) repository, _ := cmd.Flags().GetString("repository") @@ -182,7 +182,7 @@ var cloudContextCmd = &cobra.Command{ event.Deployment = &cloud.DeploymentContext{Provider: contextProvider, ExternalID: deploymentID, Environment: deploymentEnvironment, Status: deploymentStatus} } client.RecordDeliveryContext(cmd.Context(), event) - cmd.Println(auditTint("Repository context queued for Graycode Cloud.", doneGreen)) + cmd.Println(auditTint("Repository context queued for Hawk Cloud.", doneGreen)) return nil }, } @@ -197,12 +197,12 @@ func firstValue(values ...string) string { } func init() { - cloudLoginCmd.Flags().String("endpoint", "", "Graycode Cloud endpoint (or GRAYCODE_CLOUD_URL)") - cloudLoginCmd.Flags().String("label", "", "Name for this Graycode device") - cloudConnectCmd.Flags().String("endpoint", "", "Graycode Cloud endpoint") - cloudConnectCmd.Flags().String("device-id", "", "Graycode Cloud device ID") - cloudConnectCmd.Flags().String("project-id", "", "Graycode Cloud project ID") - cloudConnectCmd.Flags().String("token", "", "Graycode Cloud device token") + cloudLoginCmd.Flags().String("endpoint", "", "Hawk Cloud endpoint (or HAWK_CLOUD_URL)") + cloudLoginCmd.Flags().String("label", "", "Name for this Hawk device") + cloudConnectCmd.Flags().String("endpoint", "", "Hawk Cloud endpoint") + cloudConnectCmd.Flags().String("device-id", "", "Hawk Cloud device ID") + cloudConnectCmd.Flags().String("project-id", "", "Hawk Cloud project ID") + cloudConnectCmd.Flags().String("token", "", "Hawk Cloud device token") cloudContextCmd.Flags().String("repository", "", "Repository name (auto-detected from Git when omitted)") cloudContextCmd.Flags().String("provider", "", "Repository provider (auto-detected when omitted)") cloudContextCmd.Flags().String("external-id", "", "Provider repository identifier (defaults to repository)") diff --git a/cmd/cloud_context_test.go b/cmd/cloud_context_test.go index 651d1d557..c4f05c309 100644 --- a/cmd/cloud_context_test.go +++ b/cmd/cloud_context_test.go @@ -12,7 +12,7 @@ func TestDetectGitContext(t *testing.T) { runCloudGit = func(_ context.Context, args ...string) (string, error) { switch args[0] { case "config": - return "git@github.com:GrayCodeAI/graycode-cli.git", nil + return "git@github.com:GrayCodeAI/hawk.git", nil case "branch": return "main", nil case "rev-parse": @@ -25,7 +25,7 @@ func TestDetectGitContext(t *testing.T) { if err != nil { t.Fatal(err) } - if got.Repository != "GrayCodeAI/graycode-cli" || got.Provider != "github" || got.Branch != "main" || got.Commit != "abc123" { + if got.Repository != "GrayCodeAI/hawk" || got.Provider != "github" || got.Branch != "main" || got.Commit != "abc123" { t.Fatalf("context = %+v", got) } } diff --git a/cmd/cloud_graph.go b/cmd/cloud_graph.go index 5cc2eee99..7df5e3d6f 100644 --- a/cmd/cloud_graph.go +++ b/cmd/cloud_graph.go @@ -5,8 +5,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - cloud "github.com/GrayCodeAI/graycode-cli/internal/platform/cloud" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + cloud "github.com/GrayCodeAI/hawk/internal/platform/cloud" "github.com/spf13/cobra" ) @@ -22,15 +22,15 @@ func newCloudGraphCmd() *cobra.Command { var missionDir string syncCmd := &cobra.Command{ Use: "sync [session-id]", - Short: "Upload a privacy-normalized execution graph to Graycode Cloud", - Long: `Build the same read-only execution graph as "graycode graph export", hash -cloud-sensitive metadata, enforce Graycode Cloud's upload bounds, and upload it + Short: "Upload a privacy-normalized execution graph to Hawk Cloud", + Long: `Build the same read-only execution graph as "hawk graph export", hash +cloud-sensitive metadata, enforce Hawk Cloud's upload bounds, and upload it with a deterministic idempotency key. Sync completed session snapshots: graph facts are immutable after acceptance. This is explicit and opt-in; local execution never depends on cloud synchronization.`, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - prog := NewCLIProgress("Graph sync", []string{"Building execution graph", "Uploading to Graycode Cloud"}) + prog := NewCLIProgress("Graph sync", []string{"Building execution graph", "Uploading to Hawk Cloud"}) defer prog.Abort() prog.StartStep(0) var export executiongraph.Export @@ -53,11 +53,11 @@ execution never depends on cloud synchronization.`, } client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - return fmt.Errorf("graycode cloud is not connected") + return fmt.Errorf("hawk cloud is not connected") } prepared, err := cloud.PrepareGraph(export) if err != nil { - return fmt.Errorf("prepare graph for Graycode Cloud: %w", err) + return fmt.Errorf("prepare graph for Hawk Cloud: %w", err) } prog.CompleteStep(0) prog.StartStep(1) diff --git a/cmd/cmdhistory_cmd.go b/cmd/cmdhistory_cmd.go index 8038c1f50..3ad938fb4 100644 --- a/cmd/cmdhistory_cmd.go +++ b/cmd/cmdhistory_cmd.go @@ -6,8 +6,8 @@ import ( "path/filepath" "strconv" - "github.com/GrayCodeAI/graycode-cli/internal/cmdhistory" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/cmdhistory" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) diff --git a/cmd/command_history.go b/cmd/command_history.go index f546c59bc..04a1a50ad 100644 --- a/cmd/command_history.go +++ b/cmd/command_history.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) // commandHistory persists recently-used slash commands so the command diff --git a/cmd/compact_ui.go b/cmd/compact_ui.go index 1b043f141..a8b9268db 100644 --- a/cmd/compact_ui.go +++ b/cmd/compact_ui.go @@ -11,8 +11,8 @@ import ( lipgloss "charm.land/lipgloss/v2" "github.com/mattn/go-runewidth" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) const compactProgressBarWidth = 40 diff --git a/cmd/compact_ui_test.go b/cmd/compact_ui_test.go index a965c51d1..8effc7322 100644 --- a/cmd/compact_ui_test.go +++ b/cmd/compact_ui_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestRenderContextUsageBar(t *testing.T) { @@ -32,7 +32,7 @@ func TestContextUsagePercentForBar(t *testing.T) { func TestRenderCompactProgressPanel(t *testing.T) { m := chatModel{ session: &engine.Session{}, - brailleSpinner: NewBrailleSpinner(SpinnerGraycode, "Compacting conversation"), + brailleSpinner: NewBrailleSpinner(SpinnerHawk, "Compacting conversation"), manualCompacting: true, } out := m.renderCompactProgressPanel(80) diff --git a/cmd/compat-test/drift.go b/cmd/compat-test/drift.go index 2456bdfbf..cc17d2da0 100644 --- a/cmd/compat-test/drift.go +++ b/cmd/compat-test/drift.go @@ -9,22 +9,22 @@ import ( ) // trackedPins are the shared leaf dependencies most likely to drift silently: -// a consumer (merlin, kestrel, ...) can pin an older version than what graycode's +// a consumer (merlin, kestrel, ...) can pin an older version than what hawk's // own go.mod requires, and Go's minimal version selection will silently pull -// in graycode's newer version at build time without the consumer's own CI ever +// in hawk's newer version at build time without the consumer's own CI ever // having tested it. See docs/compatibility.md. var trackedPins = []string{ "github.com/GrayCodeAI/falcon", } -// checkDrift compares graycode's own go.mod requirements for trackedPins against -// what each sibling repo (a peer checkout of a graycode dependency in the shared +// checkDrift compares hawk's own go.mod requirements for trackedPins against +// what each sibling repo (a peer checkout of a hawk dependency in the shared // workspace) declares for the same modules in its own go.mod. It never fails — // this is advisory, printed for humans/CI logs to notice, not a build gate. func checkDrift(repoRoot string) error { - graycodeRequires, err := readRequires(filepath.Join(repoRoot, "go.mod")) + hawkRequires, err := readRequires(filepath.Join(repoRoot, "go.mod")) if err != nil { - return fmt.Errorf("read graycode go.mod: %w", err) + return fmt.Errorf("read hawk go.mod: %w", err) } workspaceDir := filepath.Join(repoRoot, "..") @@ -45,18 +45,18 @@ func checkDrift(repoRoot string) error { continue // sibling not a Go module / no go.mod — skip silently } for _, pin := range trackedPins { - graycodeVer, graycodeHas := graycodeRequires[pin] + hawkVer, hawkHas := hawkRequires[pin] consumerVer, consumerHas := consumerRequires[pin] - if !graycodeHas || !consumerHas || graycodeVer == consumerVer { + if !hawkHas || !consumerHas || hawkVer == consumerVer { continue } drifted++ - fmt.Printf(" %-22s requires %s@%s, graycode requires %s@%s\n", - e.Name(), pin, consumerVer, pin, graycodeVer) + fmt.Printf(" %-22s requires %s@%s, hawk requires %s@%s\n", + e.Name(), pin, consumerVer, pin, hawkVer) } } if drifted == 0 { - fmt.Println(" OK — no drift between graycode's pins and sibling consumers") + fmt.Println(" OK — no drift between hawk's pins and sibling consumers") } return nil } diff --git a/cmd/compat-test/drift_test.go b/cmd/compat-test/drift_test.go index 27dac8dc9..1ef375736 100644 --- a/cmd/compat-test/drift_test.go +++ b/cmd/compat-test/drift_test.go @@ -68,10 +68,10 @@ func TestReadRequires_InvalidMod(t *testing.T) { } } -// TestCheckDrift reports drift when a consumer pins an older version than graycode. +// TestCheckDrift reports drift when a consumer pins an older version than hawk. func TestCheckDrift_DetectsDrift(t *testing.T) { ws := t.TempDir() - writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli + writeMod(t, filepath.Join(ws, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk go 1.26 @@ -89,7 +89,7 @@ require github.com/GrayCodeAI/falcon v1.2.0 old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w - err := checkDrift(filepath.Join(ws, "graycode")) + err := checkDrift(filepath.Join(ws, "hawk")) _ = w.Close() os.Stdout = old _, _ = buf.ReadFrom(r) @@ -106,7 +106,7 @@ require github.com/GrayCodeAI/falcon v1.2.0 // pins produce the "OK" line and no per-consumer drift lines. func TestCheckDrift_NoDriftWhenVersionsMatch(t *testing.T) { ws := t.TempDir() - writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli + writeMod(t, filepath.Join(ws, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk go 1.26 @@ -123,7 +123,7 @@ require github.com/GrayCodeAI/falcon v1.5.0 r, w, _ := os.Pipe() old := os.Stdout os.Stdout = w - err := checkDrift(filepath.Join(ws, "graycode")) + err := checkDrift(filepath.Join(ws, "hawk")) _ = w.Close() os.Stdout = old _, _ = buf.ReadFrom(r) @@ -140,7 +140,7 @@ require github.com/GrayCodeAI/falcon v1.5.0 // without a go.mod (e.g. not a Go module) is skipped without failing. func TestCheckDrift_SkipsMissingSiblings(t *testing.T) { ws := t.TempDir() - writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli + writeMod(t, filepath.Join(ws, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk go 1.26 @@ -154,7 +154,7 @@ require github.com/GrayCodeAI/falcon v1.5.0 r, w, _ := os.Pipe() old := os.Stdout os.Stdout = w - err := checkDrift(filepath.Join(ws, "graycode")) + err := checkDrift(filepath.Join(ws, "hawk")) _ = w.Close() os.Stdout = old _, _ = io.Copy(io.Discard, r) diff --git a/cmd/compat-test/main.go b/cmd/compat-test/main.go index b9d6d6380..8f7ae7d8f 100644 --- a/cmd/compat-test/main.go +++ b/cmd/compat-test/main.go @@ -17,7 +17,7 @@ // go run ./cmd/compat-test -matrix=stable -strict // # exit non-zero if any // # component lacks a version -// go run ./cmd/compat-test -check-external # advisory: compare graycode's own +// go run ./cmd/compat-test -check-external # advisory: compare hawk's own // # go.mod pins for shared leaf // # deps against what each // # sibling repo declares. @@ -176,7 +176,7 @@ func report(mf matrixFile, m matrix, strict bool) error { } // findMatrixFile locates the cross-repo compatibility matrix (testdata/compatibility-matrix.json). -// It must not pick graycode/platform-capabilities.json, which is a different document. +// It must not pick hawk/platform-capabilities.json, which is a different document. func findMatrixFile() string { dir, err := os.Getwd() if err != nil { @@ -184,7 +184,7 @@ func findMatrixFile() string { } candidates := []string{ "testdata/compatibility-matrix.json", - "graycode/testdata/compatibility-matrix.json", + "hawk/testdata/compatibility-matrix.json", } for i := 0; i < 6; i++ { for _, rel := range candidates { diff --git a/cmd/completions.go b/cmd/completions.go index 6d09852ce..4e22ea37b 100644 --- a/cmd/completions.go +++ b/cmd/completions.go @@ -9,8 +9,8 @@ import ( "sort" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/routing" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -32,7 +32,7 @@ type CommandInfo struct { Flags []FlagInfo `json:"flags,omitempty"` } -// CompletionGenerator generates shell completion scripts for graycode. +// CompletionGenerator generates shell completion scripts for hawk. type CompletionGenerator struct { Commands []CommandInfo `json:"commands"` Flags []FlagInfo `json:"flags"` @@ -41,7 +41,7 @@ type CompletionGenerator struct { Providers []string `json:"providers"` } -// NewCompletionGenerator creates a CompletionGenerator pre-populated with graycode's +// NewCompletionGenerator creates a CompletionGenerator pre-populated with hawk's // command structure, flags, providers, models, and slash commands. func NewCompletionGenerator() *CompletionGenerator { g := &CompletionGenerator{} @@ -63,7 +63,7 @@ func (g *CompletionGenerator) populateFlags() { } func (g *CompletionGenerator) populateProviders() { - g.Providers = graycodeconfig.AllSetupGateways() + g.Providers = hawkconfig.AllSetupGateways() } func (g *CompletionGenerator) populateModels() { @@ -91,12 +91,12 @@ func (g *CompletionGenerator) populateSlashCommands() { } } -// GenerateBash returns a complete bash completion script for graycode. +// GenerateBash returns a complete bash completion script for hawk. func (g *CompletionGenerator) GenerateBash() string { var b strings.Builder - b.WriteString("# bash completion for graycode\n") - b.WriteString("# Auto-generated by graycode completions generator\n\n") + b.WriteString("# bash completion for hawk\n") + b.WriteString("# Auto-generated by hawk completions generator\n\n") // Build subcommand list subcommands := make([]string, 0, len(g.Commands)) @@ -119,7 +119,7 @@ func (g *CompletionGenerator) GenerateBash() string { // Build slash commands list slashCmds := strings.Join(g.SlashCommands, " ") - b.WriteString("_graycode_completions() {\n") + b.WriteString("_hawk_completions() {\n") b.WriteString(" local cur prev words cword\n") b.WriteString(" _init_completion || return\n") b.WriteString("\n") @@ -170,7 +170,7 @@ func (g *CompletionGenerator) GenerateBash() string { b.WriteString(" return 0\n") b.WriteString(" fi\n") b.WriteString("\n") - b.WriteString(" # Complete subcommands after graycode\n") + b.WriteString(" # Complete subcommands after hawk\n") b.WriteString(" if [[ ${COMP_CWORD} -eq 1 ]]; then\n") b.WriteString(fmt.Sprintf(" COMPREPLY=($(compgen -W \"%s\" -- \"$cur\"))\n", strings.Join(subcommands, " "))) b.WriteString(" return 0\n") @@ -198,20 +198,20 @@ func (g *CompletionGenerator) GenerateBash() string { b.WriteString(" return 0\n") b.WriteString("}\n") b.WriteString("\n") - b.WriteString("complete -F _graycode_completions graycode\n") + b.WriteString("complete -F _hawk_completions hawk\n") return b.String() } -// GenerateZsh returns a complete zsh completion script for graycode. +// GenerateZsh returns a complete zsh completion script for hawk. func (g *CompletionGenerator) GenerateZsh() string { var b strings.Builder - b.WriteString("#compdef graycode\n") - b.WriteString("# zsh completion for graycode\n") - b.WriteString("# Auto-generated by graycode completions generator\n\n") + b.WriteString("#compdef hawk\n") + b.WriteString("# zsh completion for hawk\n") + b.WriteString("# Auto-generated by hawk completions generator\n\n") - b.WriteString("_graycode() {\n") + b.WriteString("_hawk() {\n") b.WriteString(" local -a commands\n") b.WriteString(" local -a global_flags\n") b.WriteString("\n") @@ -254,7 +254,7 @@ func (g *CompletionGenerator) GenerateZsh() string { b.WriteString(" case $state in\n") b.WriteString(" commands)\n") - b.WriteString(" _describe -t commands 'graycode commands' commands\n") + b.WriteString(" _describe -t commands 'hawk commands' commands\n") b.WriteString(" ;;\n") b.WriteString(" args)\n") b.WriteString(" case $words[1] in\n") @@ -277,7 +277,7 @@ func (g *CompletionGenerator) GenerateZsh() string { b.WriteString("}\n\n") // Slash commands completion function - b.WriteString("_graycode_slash_commands() {\n") + b.WriteString("_hawk_slash_commands() {\n") b.WriteString(" local -a slash_commands\n") b.WriteString(" slash_commands=(\n") for _, sc := range g.SlashCommands { @@ -288,7 +288,7 @@ func (g *CompletionGenerator) GenerateZsh() string { b.WriteString("}\n\n") // Provider completion function - b.WriteString("_graycode_providers() {\n") + b.WriteString("_hawk_providers() {\n") b.WriteString(" local -a providers\n") b.WriteString(" providers=(\n") for _, p := range g.Providers { @@ -298,25 +298,25 @@ func (g *CompletionGenerator) GenerateZsh() string { b.WriteString(" compadd -a providers\n") b.WriteString("}\n\n") - b.WriteString("_graycode \"$@\"\n") + b.WriteString("_hawk \"$@\"\n") return b.String() } -// GenerateFish returns a complete fish shell completion script for graycode. +// GenerateFish returns a complete fish shell completion script for hawk. func (g *CompletionGenerator) GenerateFish() string { var b strings.Builder - b.WriteString("# fish completion for graycode\n") - b.WriteString("# Auto-generated by graycode completions generator\n\n") + b.WriteString("# fish completion for hawk\n") + b.WriteString("# Auto-generated by hawk completions generator\n\n") // Disable file completions by default - b.WriteString("complete -c graycode -f\n\n") + b.WriteString("complete -c hawk -f\n\n") // Subcommands b.WriteString("# Subcommands\n") for _, cmd := range g.Commands { - b.WriteString(fmt.Sprintf("complete -c graycode -n '__fish_use_subcommand' -a '%s' -d '%s'\n", + b.WriteString(fmt.Sprintf("complete -c hawk -n '__fish_use_subcommand' -a '%s' -d '%s'\n", cmd.Name, escapeFish(cmd.Description))) } b.WriteString("\n") @@ -326,7 +326,7 @@ func (g *CompletionGenerator) GenerateFish() string { if len(cmd.Subcommands) > 0 { b.WriteString(fmt.Sprintf("# %s subcommands\n", cmd.Name)) for _, sc := range cmd.Subcommands { - b.WriteString(fmt.Sprintf("complete -c graycode -n '__fish_seen_subcommand_from %s' -a '%s' -d '%s'\n", + b.WriteString(fmt.Sprintf("complete -c hawk -n '__fish_seen_subcommand_from %s' -a '%s' -d '%s'\n", cmd.Name, sc.Name, escapeFish(sc.Description))) } b.WriteString("\n") @@ -336,10 +336,10 @@ func (g *CompletionGenerator) GenerateFish() string { b.WriteString(fmt.Sprintf("# %s flags\n", cmd.Name)) for _, f := range cmd.Flags { if f.Short != "" { - b.WriteString(fmt.Sprintf("complete -c graycode -n '__fish_seen_subcommand_from %s' -l '%s' -s '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c hawk -n '__fish_seen_subcommand_from %s' -l '%s' -s '%s' -d '%s'", cmd.Name, f.Name, f.Short, escapeFish(f.Description))) } else { - b.WriteString(fmt.Sprintf("complete -c graycode -n '__fish_seen_subcommand_from %s' -l '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c hawk -n '__fish_seen_subcommand_from %s' -l '%s' -d '%s'", cmd.Name, f.Name, escapeFish(f.Description))) } if f.Type == "bool" { @@ -360,10 +360,10 @@ func (g *CompletionGenerator) GenerateFish() string { b.WriteString("# Global flags\n") for _, f := range g.Flags { if f.Short != "" { - b.WriteString(fmt.Sprintf("complete -c graycode -l '%s' -s '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c hawk -l '%s' -s '%s' -d '%s'", f.Name, f.Short, escapeFish(f.Description))) } else { - b.WriteString(fmt.Sprintf("complete -c graycode -l '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c hawk -l '%s' -d '%s'", f.Name, escapeFish(f.Description))) } if f.Type == "bool" { @@ -380,14 +380,14 @@ func (g *CompletionGenerator) GenerateFish() string { // Provider completions for --provider b.WriteString("# Provider completions\n") - b.WriteString(fmt.Sprintf("complete -c graycode -l 'provider' -r -a '%s' -d 'LLM provider'\n", + b.WriteString(fmt.Sprintf("complete -c hawk -l 'provider' -r -a '%s' -d 'LLM provider'\n", strings.Join(g.Providers, " "))) b.WriteString("\n") // Slash commands b.WriteString("# Slash commands (for interactive mode reference)\n") for _, sc := range g.SlashCommands { - b.WriteString(fmt.Sprintf("complete -c graycode -a '%s' -d 'Slash command'\n", sc)) + b.WriteString(fmt.Sprintf("complete -c hawk -a '%s' -d 'Slash command'\n", sc)) } return b.String() @@ -408,7 +408,7 @@ func (g *CompletionGenerator) GenerateJSON() (string, error) { Providers []string `json:"providers"` Models []string `json:"models"` }{ - Name: "graycode", + Name: "hawk", Version: v, Commands: g.Commands, GlobalFlags: g.Flags, @@ -540,11 +540,11 @@ func bashInstallPath() string { // Prefer user-local path home, err := os.UserHomeDir() if err != nil { - return "/etc/bash_completion.d/graycode" + return "/etc/bash_completion.d/hawk" } localDir := filepath.Join(home, ".local", "share", "bash-completion", "completions") if info, err := os.Stat(localDir); err == nil && info.IsDir() { - return filepath.Join(localDir, "graycode") + return filepath.Join(localDir, "hawk") } // On macOS, use homebrew path if available. // ARM Macs (M1+) install Homebrew to /opt/homebrew; Intel Macs use /usr/local. @@ -552,17 +552,17 @@ func bashInstallPath() string { for _, prefix := range []string{"/opt/homebrew", "/usr/local"} { brewDir := filepath.Join(prefix, "etc", "bash_completion.d") if info, err := os.Stat(brewDir); err == nil && info.IsDir() { - return filepath.Join(brewDir, "graycode") + return filepath.Join(brewDir, "hawk") } } } // Fallback: try system-wide sysDir := "/etc/bash_completion.d" if info, err := os.Stat(sysDir); err == nil && info.IsDir() { - return filepath.Join(sysDir, "graycode") + return filepath.Join(sysDir, "hawk") } // Final fallback: user-local - return filepath.Join(home, ".local", "share", "bash-completion", "completions", "graycode") + return filepath.Join(home, ".local", "share", "bash-completion", "completions", "hawk") } func zshInstallPath() string { @@ -573,28 +573,28 @@ func zshInstallPath() string { for _, p := range parts { if p != "" { if info, err := os.Stat(p); err == nil && info.IsDir() { // #nosec G703 -- shell completion only probes the user-selected directory - return filepath.Join(p, "_graycode") + return filepath.Join(p, "_hawk") } } } // Use first entry even if it doesn't exist yet if parts[0] != "" { - return filepath.Join(parts[0], "_graycode") + return filepath.Join(parts[0], "_hawk") } } home, err := os.UserHomeDir() if err != nil { - return "/usr/local/share/zsh/site-functions/_graycode" + return "/usr/local/share/zsh/site-functions/_hawk" } - return filepath.Join(home, ".zsh", "completions", "_graycode") + return filepath.Join(home, ".zsh", "completions", "_hawk") } func fishInstallPath() string { home, err := os.UserHomeDir() if err != nil { - return filepath.Join(os.TempDir(), "graycode.fish") + return filepath.Join(os.TempDir(), "hawk.fish") } - return filepath.Join(home, ".config", "fish", "completions", "graycode.fish") + return filepath.Join(home, ".config", "fish", "completions", "hawk.fish") } // escapeZsh escapes single quotes for zsh completion descriptions. diff --git a/cmd/completions_test.go b/cmd/completions_test.go index ba26c6ef9..d47625bb9 100644 --- a/cmd/completions_test.go +++ b/cmd/completions_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-router/catalog/registry" + "github.com/GrayCodeAI/eyrie/catalog/registry" ) func TestNewCompletionGenerator(t *testing.T) { @@ -35,10 +35,10 @@ func TestGenerateBashContainsFunctionDefinition(t *testing.T) { g := NewCompletionGenerator() bash := g.GenerateBash() - if !strings.Contains(bash, "_graycode_completions()") { - t.Error("Bash completion should contain _graycode_completions() function definition") + if !strings.Contains(bash, "_hawk_completions()") { + t.Error("Bash completion should contain _hawk_completions() function definition") } - if !strings.Contains(bash, "complete -F _graycode_completions graycode") { + if !strings.Contains(bash, "complete -F _hawk_completions hawk") { t.Error("Bash completion should register the completion function with 'complete'") } } @@ -112,8 +112,8 @@ func TestGenerateZshContainsCompdefHeader(t *testing.T) { g := NewCompletionGenerator() zsh := g.GenerateZsh() - if !strings.HasPrefix(zsh, "#compdef graycode") { - t.Error("Zsh completion should start with #compdef graycode header") + if !strings.HasPrefix(zsh, "#compdef hawk") { + t.Error("Zsh completion should start with #compdef hawk header") } } @@ -121,8 +121,8 @@ func TestGenerateZshContainsFunction(t *testing.T) { g := NewCompletionGenerator() zsh := g.GenerateZsh() - if !strings.Contains(zsh, "_graycode()") { - t.Error("Zsh completion should contain _graycode() function") + if !strings.Contains(zsh, "_hawk()") { + t.Error("Zsh completion should contain _hawk() function") } if !strings.Contains(zsh, "_arguments") { t.Error("Zsh completion should use _arguments for flag completion") @@ -171,8 +171,8 @@ func TestGenerateFishContainsCompleteDirectives(t *testing.T) { g := NewCompletionGenerator() fish := g.GenerateFish() - if !strings.Contains(fish, "complete -c graycode") { - t.Error("Fish completion should contain 'complete -c graycode' directives") + if !strings.Contains(fish, "complete -c hawk") { + t.Error("Fish completion should contain 'complete -c hawk' directives") } } @@ -334,8 +334,8 @@ func TestInstallCompletionBash(t *testing.T) { if path == "" { t.Error("InstallCompletion(bash) returned empty path") } - if !strings.Contains(path, "graycode") { - t.Errorf("Bash install path should contain 'graycode', got %q", path) + if !strings.Contains(path, "hawk") { + t.Errorf("Bash install path should contain 'hawk', got %q", path) } // Should be a bash-related path if !strings.Contains(path, "bash") && !strings.Contains(path, "completion") { @@ -351,8 +351,8 @@ func TestInstallCompletionZsh(t *testing.T) { if path == "" { t.Error("InstallCompletion(zsh) returned empty path") } - if !strings.Contains(path, "_graycode") { - t.Errorf("Zsh install path should contain '_graycode', got %q", path) + if !strings.Contains(path, "_hawk") { + t.Errorf("Zsh install path should contain '_hawk', got %q", path) } } @@ -364,8 +364,8 @@ func TestInstallCompletionFish(t *testing.T) { if path == "" { t.Error("InstallCompletion(fish) returned empty path") } - if !strings.Contains(path, "graycode.fish") { - t.Errorf("Fish install path should contain 'graycode.fish', got %q", path) + if !strings.Contains(path, "hawk.fish") { + t.Errorf("Fish install path should contain 'hawk.fish', got %q", path) } if !strings.Contains(path, "fish") { t.Errorf("Fish install path should be fish-related, got %q", path) @@ -522,8 +522,8 @@ func TestGenerateZshProviderChoices(t *testing.T) { zsh := g.GenerateZsh() // The provider completion function should list all providers - if !strings.Contains(zsh, "_graycode_providers()") { - t.Error("Zsh completion should contain _graycode_providers() function") + if !strings.Contains(zsh, "_hawk_providers()") { + t.Error("Zsh completion should contain _hawk_providers() function") } } diff --git a/cmd/config_table.go b/cmd/config_table.go index 0a249dfcd..3bfa9157e 100644 --- a/cmd/config_table.go +++ b/cmd/config_table.go @@ -5,7 +5,7 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/mattn/go-runewidth" ) diff --git a/cmd/container_boot.go b/cmd/container_boot.go index 4b6b19ed7..ec4918957 100644 --- a/cmd/container_boot.go +++ b/cmd/container_boot.go @@ -8,8 +8,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/sandbox" ) // containerStatusMsg carries container lifecycle updates to the TUI. @@ -22,19 +22,19 @@ type containerStatusMsg struct { var dockerAvailable = sandbox.DockerAvailable -// shouldUseContainer is intentionally unconditional: Graycode agent command +// shouldUseContainer is intentionally unconditional: Hawk agent command // execution is Docker-only and never falls back to the host. func shouldUseContainer() bool { return true } -// startRequiredContainer starts Graycode's mandatory Docker sandbox. It fails +// startRequiredContainer starts Hawk's mandatory Docker sandbox. It fails // closed with an actionable error; there is deliberately no host fallback. // Egress is restricted to a domain allowlist via NetworkProxy by default; set -// GRAYCODE_DISABLE_EGRESS_PROXY=1 to opt out (unrestricted bridge egress). +// HAWK_DISABLE_EGRESS_PROXY=1 to opt out (unrestricted bridge egress). func startRequiredContainer(projectDir string) (*sandbox.ContainerSandbox, error) { var cs *sandbox.ContainerSandbox - if os.Getenv("GRAYCODE_DISABLE_EGRESS_PROXY") == "1" { + if os.Getenv("HAWK_DISABLE_EGRESS_PROXY") == "1" { cs = sandbox.NewContainerSandbox(projectDir) } else { cs = sandbox.NewContainerSandboxWithEgressProxy(projectDir) diff --git a/cmd/container_boot_test.go b/cmd/container_boot_test.go index ff095efec..2ccdc6424 100644 --- a/cmd/container_boot_test.go +++ b/cmd/container_boot_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestShouldUseContainerAlwaysTrue(t *testing.T) { - t.Setenv("GRAYCODE_NO_CONTAINER", "1") + t.Setenv("HAWK_NO_CONTAINER", "1") if !shouldUseContainer() { - t.Fatal("Graycode must require Docker even when the legacy opt-out variable is set") + t.Fatal("Hawk must require Docker even when the legacy opt-out variable is set") } } diff --git a/cmd/context_export.go b/cmd/context_export.go index c53f29a1d..81ab48fe0 100644 --- a/cmd/context_export.go +++ b/cmd/context_export.go @@ -9,9 +9,9 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/fsutil" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/fsutil" + "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" + "github.com/GrayCodeAI/hawk/internal/tool" ) // ExportContext generates a comprehensive context document about the current project. @@ -62,7 +62,7 @@ func ExportContext(dir string, focus string) (string, error) { } // AGENTS.md / project instructions - for _, instrFile := range []string{"AGENTS.md", "AGENTS.md", "CLAUDE.md", ".graycode.md"} { + for _, instrFile := range []string{"AGENTS.md", "AGENTS.md", "CLAUDE.md", ".hawk.md"} { data, err := os.ReadFile(filepath.Join(dir, instrFile)) // #nosec G304 -- instrFile is one of a fixed set of well-known project instruction filenames if err == nil && len(data) > 0 { b.WriteString(fmt.Sprintf("## Project Instructions (%s)\n\n%s\n\n", instrFile, strings.TrimSpace(string(data)))) diff --git a/cmd/contextual_help.go b/cmd/contextual_help.go index d3e091775..b4612001b 100644 --- a/cmd/contextual_help.go +++ b/cmd/contextual_help.go @@ -78,7 +78,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "/undo", Summary: "Undo last action safely", - Detail: "Reverts the last graycode action (commit, file edit, etc.) using safe snapshot-based rollback.", + Detail: "Reverts the last hawk action (commit, file edit, etc.) using safe snapshot-based rollback.", Examples: []string{"/undo — undo last action", "/undo --hard — undo and discard changes", "/undo 3 — undo last 3 actions"}, Related: []string{"/commit", "/snapshot", "/history"}, Category: "slash-commands", @@ -102,7 +102,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "/config", Summary: "Open configuration panel", - Detail: "Opens the interactive configuration panel for graycode settings, model selection, and preferences.", + Detail: "Opens the interactive configuration panel for hawk settings, model selection, and preferences.", Examples: []string{"/config — open config panel", "/config model — change model", "/config key remove — remove stored API key", "/config keys — show key status"}, Related: []string{"/session", "/profile", "/rules"}, Category: "slash-commands", @@ -165,8 +165,8 @@ func (ch *ContextualHelp) registerAllEntries() { }, { Topic: "/session", - Summary: "Manage graycode sessions", - Detail: "Start, stop, resume, or list graycode working sessions with full context preservation.", + Summary: "Manage hawk sessions", + Detail: "Start, stop, resume, or list hawk working sessions with full context preservation.", Examples: []string{"/session — show current session", "/session new — start new session", "/session resume 3 — resume session #3"}, Related: []string{"/status", "/history", "/config"}, Category: "slash-commands", @@ -230,7 +230,7 @@ func (ch *ContextualHelp) registerAllEntries() { // ─── Common Tasks ──────────────────────────────────────────── { Topic: "how to fix tests", - Summary: "Fixing failing tests with graycode", + Summary: "Fixing failing tests with hawk", Detail: "Use /test to run tests and identify failures, then /fix test to apply AI-generated fixes. For complex failures, use /chat to discuss the issue.", Examples: []string{"/test — identify failures", "/fix test — auto-fix test failures", "/chat \"why is TestX failing?\""}, Related: []string{"/test", "/fix", "/bugfind"}, @@ -238,7 +238,7 @@ func (ch *ContextualHelp) registerAllEntries() { }, { Topic: "how to commit", - Summary: "Making commits with graycode", + Summary: "Making commits with hawk", Detail: "Stage your changes with git add, then use /commit to create a commit with an AI-generated message. Use /diff first to review what you are committing.", Examples: []string{"/diff --staged — review staged changes", "/commit — commit with AI message", `/commit "my message" — commit with custom message`}, Related: []string{"/commit", "/diff", "/branch"}, @@ -246,7 +246,7 @@ func (ch *ContextualHelp) registerAllEntries() { }, { Topic: "how to debug", - Summary: "Debugging with graycode AI assistance", + Summary: "Debugging with hawk AI assistance", Detail: "Describe the bug in /chat or use /bugfind for automated detection. For test failures, /test with /fix provides targeted debugging.", Examples: []string{"/bugfind — automated bug detection", "/chat \"I see panic at line 42\"", "/test -v ./pkg/... — verbose test output"}, Related: []string{"/bugfind", "/test", "/fix", "/chat"}, @@ -263,16 +263,16 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "how to refactor", Summary: "Refactoring code with AI assistance", - Detail: "Use /chat to discuss refactoring strategies, then graycode can apply changes. Use /snapshot before large refactors for safety.", + Detail: "Use /chat to discuss refactoring strategies, then hawk can apply changes. Use /snapshot before large refactors for safety.", Examples: []string{"/snapshot — save state before refactor", "/chat \"refactor auth to use interfaces\"", "/test — verify nothing broke"}, Related: []string{"/chat", "/snapshot", "/test", "/undo"}, Category: "common-tasks", }, { Topic: "how to start a session", - Summary: "Starting a new graycode session", - Detail: "Run graycode in your project directory to start a session. Your context is preserved across the session. Use /session to manage sessions.", - Examples: []string{"graycode — start graycode in current dir", "/session new — start fresh session", "/session resume — resume last session"}, + Summary: "Starting a new hawk session", + Detail: "Run hawk in your project directory to start a session. Your context is preserved across the session. Use /session to manage sessions.", + Examples: []string{"hawk — start hawk in current dir", "/session new — start fresh session", "/session resume — resume last session"}, Related: []string{"/session", "/config", "/status"}, Category: "common-tasks", }, @@ -281,14 +281,14 @@ func (ch *ContextualHelp) registerAllEntries() { Topic: "error: api key invalid", Summary: "API key is missing or invalid", Detail: "Your API key is not configured or has expired. Save a new key via /config (paste in the panel). Keys are stored in the OS secret store (macOS Keychain / Linux keyring).", - Examples: []string{"/config — paste API key in the config panel", "graycode credentials status — verify stored keys"}, + Examples: []string{"/config — paste API key in the config panel", "hawk credentials status — verify stored keys"}, Related: []string{"/config", "error: rate limit", "error: network"}, Category: "errors", }, { Topic: "error: rate limit", Summary: "API rate limit exceeded", - Detail: "You have exceeded the API rate limit. Graycode will automatically retry with exponential backoff. Consider upgrading your plan for higher limits.", + Detail: "You have exceeded the API rate limit. Hawk will automatically retry with exponential backoff. Consider upgrading your plan for higher limits.", Examples: []string{"/config model — switch to a lower-tier model", "/status — check rate limit status"}, Related: []string{"error: api key invalid", "/config", "error: timeout"}, Category: "errors", @@ -312,7 +312,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "error: git conflict", Summary: "Git merge conflict detected", - Detail: "A merge conflict was encountered. Graycode can assist with conflict resolution using AI to understand both sides.", + Detail: "A merge conflict was encountered. Hawk can assist with conflict resolution using AI to understand both sides.", Examples: []string{"/merge --resolve — AI-assisted resolution", "/diff --conflicts — show conflict details", "/undo — abort and go back"}, Related: []string{"/merge", "/diff", "/undo"}, Category: "errors", @@ -320,7 +320,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "error: no git repo", Summary: "Not in a git repository", - Detail: "Graycode requires a git repository. Initialize one with git init or navigate to an existing repo.", + Detail: "Hawk requires a git repository. Initialize one with git init or navigate to an existing repo.", Examples: []string{"git init — initialize new repo", "cd /path/to/repo — navigate to a repo"}, Related: []string{"/status", "/branch", "/commit"}, Category: "errors", @@ -354,7 +354,7 @@ func (ch *ContextualHelp) registerAllEntries() { Topic: "config: api-key", Summary: "Set the API key", Detail: "API keys are stored in the OS secret store. Use /config to paste a key, or /config key remove to delete one.", - Examples: []string{"/config — paste API key in the config panel", "/config key remove — remove a stored key", "graycode credentials status — list configured providers"}, + Examples: []string{"/config — paste API key in the config panel", "/config key remove — remove a stored key", "hawk credentials status — list configured providers"}, Related: []string{"/config", "config: model", "error: api key invalid"}, Category: "configuration", }, @@ -377,7 +377,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "config: editor", Summary: "Set preferred editor", - Detail: "Configure which editor graycode uses when opening files for editing.", + Detail: "Configure which editor hawk uses when opening files for editing.", Examples: []string{"/config editor vim — use vim", "/config editor code — use VS Code", "/config editor nano — use nano"}, Related: []string{"/config", "config: theme", "config: shell"}, Category: "configuration", @@ -385,7 +385,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "config: theme", Summary: "Set color theme", - Detail: "Choose the color theme for graycode terminal output. Supports light and dark terminal backgrounds.", + Detail: "Choose the color theme for hawk terminal output. Supports light and dark terminal backgrounds.", Examples: []string{"/config theme dark — dark background theme", "/config theme light — light background theme", "/config theme none — disable colors"}, Related: []string{"/config", "config: editor", "config: shell"}, Category: "configuration", @@ -393,7 +393,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "config: shell", Summary: "Set shell for command execution", - Detail: "Configure which shell graycode uses when running commands.", + Detail: "Configure which shell hawk uses when running commands.", Examples: []string{"/config shell bash — use bash", "/config shell zsh — use zsh", "/config shell fish — use fish"}, Related: []string{"/config", "config: editor", "config: theme"}, Category: "configuration", @@ -458,7 +458,7 @@ func (ch *ContextualHelp) registerAllEntries() { { Topic: "tool: sandbox", Summary: "Safe execution via Docker container", - Detail: "Runs agent tools in a mandatory isolated Docker container. If Docker is unavailable, tools remain locked and Graycode never falls back to the host.", + Detail: "Runs agent tools in a mandatory isolated Docker container. If Docker is unavailable, tools remain locked and Hawk never falls back to the host.", Examples: []string{"/test — runs in container", "/fix — validates fixes in container"}, Related: []string{"/test", "/fix", "tool: snapshot"}, Category: "tools", @@ -467,7 +467,7 @@ func (ch *ContextualHelp) registerAllEntries() { Topic: "tool: daemon", Summary: "Background daemon service", Detail: "Runs in the background to provide file watching, indexing, and real-time analysis of your project.", - Examples: []string{"graycode daemon start — start the daemon", "graycode daemon status — check daemon status", "graycode daemon stop — stop the daemon"}, + Examples: []string{"hawk daemon start — start the daemon", "hawk daemon status — check daemon status", "hawk daemon stop — stop the daemon"}, Related: []string{"/status", "tool: repomap", "tool: memory"}, Category: "tools", }, diff --git a/cmd/control_plane_hints.go b/cmd/control_plane_hints.go index 68ad8c117..c379a1c9c 100644 --- a/cmd/control_plane_hints.go +++ b/cmd/control_plane_hints.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // controlPlaneOnboardingHint is a short first-session tip (not a wall of text). @@ -24,7 +24,7 @@ func workModeSwitchSummary(sess *engine.Session, wm engine.WorkMode) string { } visible := 0 if sess != nil && sess.Tools() != nil && sess.Tools().Registry() != nil { - visible = len(sess.Tools().Registry().GraycodeRouterTools()) + visible = len(sess.Tools().Registry().EyrieTools()) } return fmt.Sprintf("Work mode → %s\n%s\nModel-visible tools: %d · /status for full control plane", wm, hint, visible) } diff --git a/cmd/cost.go b/cmd/cost.go index bfb614762..3469c8985 100644 --- a/cmd/cost.go +++ b/cmd/cost.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + analytics "github.com/GrayCodeAI/hawk/internal/observability" "github.com/spf13/cobra" ) @@ -64,7 +64,7 @@ var costAnalyzeCmd = &cobra.Command{ cmd.Println(auditTint(" - Model routing recommendations", textMuted)) cmd.Println(auditTint(" - Prompt caching suggestions", textMuted)) cmd.Println() - cmd.Println(auditTint("To track progress: https://github.com/GrayCodeAI/graycode-cli/issues", textMuted)) + cmd.Println(auditTint("To track progress: https://github.com/GrayCodeAI/hawk/issues", textMuted)) return nil } diff --git a/cmd/cost_test.go b/cmd/cost_test.go index 2b73fa572..bad0476d2 100644 --- a/cmd/cost_test.go +++ b/cmd/cost_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + analytics "github.com/GrayCodeAI/hawk/internal/observability" ) func TestCostAnalyze_JSON_Empty(t *testing.T) { diff --git a/cmd/credential_gate.go b/cmd/credential_gate.go index 1f07a34ca..0554975b4 100644 --- a/cmd/credential_gate.go +++ b/cmd/credential_gate.go @@ -3,7 +3,7 @@ package cmd import ( "sync/atomic" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/tool" ) // credentialGate holds the current host-side credential gate callback. It is diff --git a/cmd/credentials.go b/cmd/credentials.go index 175621ab1..b97c6e359 100644 --- a/cmd/credentials.go +++ b/cmd/credentials.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" "github.com/spf13/cobra" ) @@ -19,7 +19,7 @@ var credentialsStatusCmd = &cobra.Command{ Short: "Show where API keys are stored", RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() - cmd.Println(graycodeconfig.FormatCredentialCLIStatus(ctx)) + cmd.Println(hawkconfig.FormatCredentialCLIStatus(ctx)) return nil }, } @@ -30,7 +30,7 @@ var credentialsRemoveCmd = &cobra.Command{ Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() - ok, err := confirmDestructive(fmt.Sprintf("Remove stored API key(s) for %q from %s?", args[0], graycodeconfig.CredentialStoreName())) + ok, err := confirmDestructive(fmt.Sprintf("Remove stored API key(s) for %q from %s?", args[0], hawkconfig.CredentialStoreName())) if err != nil { return err } @@ -38,11 +38,11 @@ var credentialsRemoveCmd = &cobra.Command{ cmd.Printf("%s\n", auditTint("Cancelled.", textMuted)) return nil } - removed, err := graycodeconfig.RemoveStoredCredential(ctx, args[0]) + removed, err := hawkconfig.RemoveStoredCredential(ctx, args[0]) if err != nil { return err } - cmd.Printf("%s\n", auditTint(fmt.Sprintf("Removed %d key(s) from %s: %s", len(removed), graycodeconfig.CredentialStoreName(), strings.Join(removed, ", ")), doneGreen)) + cmd.Printf("%s\n", auditTint(fmt.Sprintf("Removed %d key(s) from %s: %s", len(removed), hawkconfig.CredentialStoreName(), strings.Join(removed, ", ")), doneGreen)) return nil }, } @@ -52,18 +52,18 @@ var credentialsMigrateCmd = &cobra.Command{ Short: "Import plaintext credential files into the OS secret store", RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() - storage := graycodeconfig.CredentialStorageStatus(ctx) + storage := hawkconfig.CredentialStorageStatus(ctx) if !storage.Writable { return fmt.Errorf("cannot migrate: %s", storage.Detail) } - n, err := graycodeconfig.MigrateEnvFileCredentials(ctx) + n, err := hawkconfig.MigrateEnvFileCredentials(ctx) if err != nil { return err } if n == 0 { cmd.Println(auditTint("No plaintext credential files found (already using secure storage).", textMuted)) } else { - cmd.Printf("%s\n", auditTint(fmt.Sprintf("Migrated %d key(s) to %s and removed plaintext credential files.", n, graycodeconfig.CredentialStoreName()), doneGreen)) + cmd.Printf("%s\n", auditTint(fmt.Sprintf("Migrated %d key(s) to %s and removed plaintext credential files.", n, hawkconfig.CredentialStoreName()), doneGreen)) } return nil }, diff --git a/cmd/daemon.go b/cmd/daemon.go index bfb167cc9..b224c0568 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -14,18 +14,18 @@ import ( "syscall" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/daemon" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/fsutil" - "github.com/GrayCodeAI/graycode-cli/internal/multiagent/agents" - "github.com/GrayCodeAI/graycode-cli/internal/netutil" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/observability/otellog" - "github.com/GrayCodeAI/graycode-cli/internal/observability/oteltrace" - "github.com/GrayCodeAI/graycode-cli/internal/securitylog" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/daemon" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/fsutil" + "github.com/GrayCodeAI/hawk/internal/multiagent/agents" + "github.com/GrayCodeAI/hawk/internal/netutil" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/observability/otellog" + "github.com/GrayCodeAI/hawk/internal/observability/oteltrace" + "github.com/GrayCodeAI/hawk/internal/securitylog" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -43,8 +43,8 @@ var ( var daemonCmd = &cobra.Command{ Use: "daemon", - Short: "Manage the graycode background server", - Long: "Run graycode as a background HTTP server for programmatic/CI access.", + Short: "Manage the hawk background server", + Long: "Run hawk as a background HTTP server for programmatic/CI access.", } var daemonStartCmd = &cobra.Command{ @@ -68,7 +68,7 @@ var daemonStatusCmd = &cobra.Command{ func init() { daemonStartCmd.Flags().IntVarP(&daemonPort, "port", "p", 4590, "Port to listen on") daemonStartCmd.Flags().StringVar(&daemonHost, "host", netutil.LoopbackHost, "Host to bind to (default: 127.0.0.1, use 0.0.0.0 for remote access)") - daemonStartCmd.Flags().StringVar(&daemonAPIKey, "api-key", "", "API key for protected daemon endpoints (defaults to GRAYCODE_DAEMON_API_KEY or a generated key)") + daemonStartCmd.Flags().StringVar(&daemonAPIKey, "api-key", "", "API key for protected daemon endpoints (defaults to HAWK_DAEMON_API_KEY or a generated key)") daemonStartCmd.Flags().StringVar(&daemonLogLevel, "log-level", "INFO", "Log level for daemon output (DEBUG, INFO, WARN, ERROR)") daemonStartCmd.Flags().StringSliceVar(&daemonCORSOrigins, "cors", []string{}, "Comma-separated list of allowed CORS origins (empty disables CORS, '*' allows all)") daemonStartCmd.Flags().StringVar(&daemonTLSCertFile, "tls-cert", "", "Path to TLS certificate file (enables HTTPS when paired with --tls-key)") @@ -81,9 +81,9 @@ func init() { } func runDaemonStart(_ *cobra.Command, _ []string) error { - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() - // Initialize OpenTelemetry telemetry (opt-in via GRAYCODE_ENABLE_TELEMETRY=1). + // Initialize OpenTelemetry telemetry (opt-in via HAWK_ENABLE_TELEMETRY=1). telemetryProviders, telemetryErr := oteltrace.InitTelemetry(oteltrace.DefaultTelemetryConfig()) if telemetryErr != nil { fmt.Fprintf(os.Stderr, "%s\n", auditTint(fmt.Sprintf("warning: telemetry initialization failed: %v", telemetryErr), warnAmber)) @@ -111,7 +111,7 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { } // Set up file-backed logging for the daemon. Logs go to - // ~/.graycode/state/daemon.log with slog structured output. + // ~/.hawk/state/daemon.log with slog structured output. logFile, logErr := openDaemonLogFile() var daemonLogger *logger.Logger if logErr != nil { @@ -128,10 +128,10 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { } // Replace the discarded logger with the real file-backed logger. - newSession := newConfiguredGraycodeSessionFactory(settings, daemonLogger) + newSession := newConfiguredHawkSessionFactory(settings, daemonLogger) // Log startup banner. - daemonLogger.Info("graycode daemon starting", map[string]interface{}{ + daemonLogger.Info("hawk daemon starting", map[string]interface{}{ "host": daemonHost, "port": daemonPort, "telemetry_enabled": telemetryProviders != nil && telemetryProviders.IsEnabled(), @@ -139,7 +139,7 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { apiKey := daemonAPIKey if apiKey == "" { - apiKey = os.Getenv("GRAYCODE_DAEMON_API_KEY") + apiKey = os.Getenv("HAWK_DAEMON_API_KEY") } if apiKey == "" { var err error @@ -209,9 +209,9 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { ) }) - // Wire GraycodeRouter's authoritative local preflight into GET /v1/ready. A session - // factory only proves Graycode can attempt construction; readiness additionally - // requires GraycodeRouter's provider state, catalog, credentials, and model selection. + // Wire Eyrie's authoritative local preflight into GET /v1/ready. A session + // factory only proves Hawk can attempt construction; readiness additionally + // requires Eyrie's provider state, catalog, credentials, and model selection. srv.SetReadyFn(daemonReadyProbe(factory)) addr, err := srv.Start() if err != nil { @@ -227,19 +227,19 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { }) defer preheater.Stop() - fmt.Printf("graycode daemon running on http://%s\n", addr) + fmt.Printf("hawk daemon running on http://%s\n", addr) fmt.Println("Endpoints: GET /v1/health, GET /v1/ready, POST /v1/chat, GET /v1/sessions, GET /v1/metrics") fmt.Println("Protected endpoints require Authorization: Bearer or X-API-Key.") if len(apiKey) > 8 { fmt.Printf("API key: %s...%s\n", apiKey[:4], apiKey[len(apiKey)-4:]) } else { - fmt.Println("API key: (set via --api-key or GRAYCODE_DAEMON_API_KEY)") + fmt.Println("API key: (set via --api-key or HAWK_DAEMON_API_KEY)") } fmt.Printf("Logs: %s\n", filepath.Join(storage.DaemonRunDir(), "daemon.log")) if telemetryProviders != nil && telemetryProviders.IsEnabled() { fmt.Println("Telemetry: enabled (OTLP export configured)") } else { - fmt.Println("Telemetry: disabled (set GRAYCODE_ENABLE_TELEMETRY=1 to enable)") + fmt.Println("Telemetry: disabled (set HAWK_ENABLE_TELEMETRY=1 to enable)") } keyFile := filepath.Join(storage.DaemonRunDir(), "daemon.key") _ = os.MkdirAll(filepath.Dir(keyFile), 0o700) @@ -283,7 +283,7 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { } // openDaemonLogFile opens (or creates) the daemon log file at -// ~/.graycode/state/daemon.log and returns it. The directory is created if needed. +// ~/.hawk/state/daemon.log and returns it. The directory is created if needed. func openDaemonLogFile() (*os.File, error) { dir := storage.DaemonRunDir() if err := os.MkdirAll(dir, 0o750); err != nil { // #nosec G301 -- daemon run dir needs group traversal @@ -327,26 +327,26 @@ func slogLevelFromString(s string) slog.Level { } // daemonReadyProbe builds the readiness function installed via SetReadyFn. It -// performs GraycodeRouter's local preflight (provider state, catalog, credentials, and +// performs Eyrie's local preflight (provider state, catalog, credentials, and // model selection) under a short timeout: // // - No session factory wired -> not ready ("engine not configured"). // - Preflight reports ready -> ready. -// - Preflight reports incomplete -> not ready with the failed GraycodeRouter check. +// - Preflight reports incomplete -> not ready with the failed Eyrie check. // -// This never performs a paid/live model call — GraycodeRouter preflight only inspects +// This never performs a paid/live model call — Eyrie preflight only inspects // local catalog/credential/model state — so it is safe to call on every probe. func daemonReadyProbe(factory daemon.SessionFactory) func() (bool, string) { - return daemonReadyProbeWithPreflight(factory, graycodeconfig.EnginePreflightReport) + return daemonReadyProbeWithPreflight(factory, hawkconfig.EnginePreflightReport) } -func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func(context.Context) graycodeconfig.EnginePreflight) func() (bool, string) { +func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func(context.Context) hawkconfig.EnginePreflight) func() (bool, string) { return func() (bool, string) { if factory == nil { return false, "engine not configured" } if preflight == nil { - return false, "GraycodeRouter readiness probe not configured" + return false, "Eyrie readiness probe not configured" } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -360,13 +360,13 @@ func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func if detail == "" { detail = "failed" } - return false, fmt.Sprintf("GraycodeRouter %s: %s", check.Name, detail) + return false, fmt.Sprintf("Eyrie %s: %s", check.Name, detail) } } if ctx.Err() != nil { - return false, "GraycodeRouter preflight timed out" + return false, "Eyrie preflight timed out" } - return false, "GraycodeRouter preflight is not ready" + return false, "Eyrie preflight is not ready" } } @@ -397,14 +397,14 @@ func generateDaemonAPIKey() (string, error) { return base64.RawURLEncoding.EncodeToString(b[:]), nil } -// daemonAutonomyFromFlag resolves the --autonomy flag / GRAYCODE_DAEMON_AUTONOMY +// daemonAutonomyFromFlag resolves the --autonomy flag / HAWK_DAEMON_AUTONOMY // env var into the server-side autonomy cap. An empty value leaves the // default cap (AutonomySemi) in place; invalid values fail closed at // "supervised" rather than silently allowing full autonomy. func daemonAutonomyFromFlag(s string) engine.AutonomyLevel { s = strings.TrimSpace(s) if s == "" { - s = os.Getenv("GRAYCODE_DAEMON_AUTONOMY") + s = os.Getenv("HAWK_DAEMON_AUTONOMY") } if s == "" { return 0 // zero => DefaultMaxAutonomy in the daemon diff --git a/cmd/daemon_ready_test.go b/cmd/daemon_ready_test.go index 66e45f114..d67bc3ee2 100644 --- a/cmd/daemon_ready_test.go +++ b/cmd/daemon_ready_test.go @@ -12,10 +12,10 @@ import ( "testing" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/daemon" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/daemon" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/storage" ) // TestDaemonReadyProbe_NilFactory verifies the probe reports not-ready when no @@ -30,30 +30,30 @@ func TestDaemonReadyProbe_NilFactory(t *testing.T) { } } -// TestDaemonReadyProbe_FailedGraycodeRouterPreflight verifies a factory alone does not -// make the daemon ready when GraycodeRouter's authoritative preflight is incomplete. -func TestDaemonReadyProbe_FailedGraycodeRouterPreflight(t *testing.T) { +// TestDaemonReadyProbe_FailedEyriePreflight verifies a factory alone does not +// make the daemon ready when Eyrie's authoritative preflight is incomplete. +func TestDaemonReadyProbe_FailedEyriePreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } - probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { - return graycodeconfig.EnginePreflight{Ready: false} + probe := daemonReadyProbeWithPreflight(factory, func(context.Context) hawkconfig.EnginePreflight { + return hawkconfig.EnginePreflight{Ready: false} }) ok, reason := probe() if ok { - t.Fatal("failed GraycodeRouter preflight must report not ready") + t.Fatal("failed Eyrie preflight must report not ready") } if reason == "" { - t.Fatal("failed GraycodeRouter preflight must include a reason") + t.Fatal("failed Eyrie preflight must include a reason") } } -func TestDaemonReadyProbe_ReadyGraycodeRouterPreflight(t *testing.T) { +func TestDaemonReadyProbe_ReadyEyriePreflight(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } - probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { - return graycodeconfig.EnginePreflight{Ready: true} + probe := daemonReadyProbeWithPreflight(factory, func(context.Context) hawkconfig.EnginePreflight { + return hawkconfig.EnginePreflight{Ready: true} }) ok, reason := probe() if !ok || reason != "" { - t.Fatalf("ready GraycodeRouter preflight = (%v, %q), want (true, empty)", ok, reason) + t.Fatalf("ready Eyrie preflight = (%v, %q), want (true, empty)", ok, reason) } } @@ -62,8 +62,8 @@ func TestDaemonReadyProbe_ReadyGraycodeRouterPreflight(t *testing.T) { func TestDaemonReadyProbe_AffectsReadyEndpoint(t *testing.T) { factory := func(daemon.ChatRequest) (*engine.Session, error) { return nil, nil } srv := daemon.New(daemon.Config{Port: 0, Host: "127.0.0.1"}, factory) - srv.SetReadyFn(daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { - return graycodeconfig.EnginePreflight{Ready: false} + srv.SetReadyFn(daemonReadyProbeWithPreflight(factory, func(context.Context) hawkconfig.EnginePreflight { + return hawkconfig.EnginePreflight{Ready: false} })) addr, err := srv.Start() @@ -93,7 +93,7 @@ func TestDaemonReadyProbe_AffectsReadyEndpoint(t *testing.T) { defer resp.Body.Close() if resp.StatusCode != http.StatusServiceUnavailable { - t.Errorf("expected 503 from /v1/ready when GraycodeRouter is not ready, got %d", resp.StatusCode) + t.Errorf("expected 503 from /v1/ready when Eyrie is not ready, got %d", resp.StatusCode) } var body daemon.ReadyResponse if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go index 71eaa0e32..39c363e9e 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -12,17 +12,17 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/resilience/health" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/stt" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/resilience/health" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/stt" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) -func doctorReport(settings graycodeconfig.Settings) string { +func doctorReport(settings hawkconfig.Settings) string { // Diagnostics must report the requested/effective selection even when its // credential is missing; readiness and health sections explain why it is // not yet usable. Hiding it as auto/default makes misconfiguration harder @@ -38,7 +38,7 @@ func doctorReport(settings graycodeconfig.Settings) string { cwd, _ := os.Getwd() var b strings.Builder - b.WriteString("Graycode doctor\n") + b.WriteString("Hawk doctor\n") 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)) @@ -55,7 +55,7 @@ func doctorReport(settings graycodeconfig.Settings) string { // Ecosystem versions b.WriteString("\nEcosystem versions:\n") for _, component := range []struct{ directory, product string }{ - {directory: "graycode-router", product: "GraycodeRouter"}, + {directory: "eyrie", product: "Eyrie"}, {directory: "harrier", product: "Harrier"}, {directory: "shrike", product: "Shrike"}, {directory: "kestrel", product: "Kestrel"}, @@ -70,17 +70,17 @@ func doctorReport(settings graycodeconfig.Settings) string { b.WriteString(fmt.Sprintf(" %s (%s): not checked out\n", component.product, component.directory)) } } - b.WriteString("\n" + graycodeconfig.FormatEcosystemPanel(context.Background(), providerName, modelName) + "\n") - b.WriteString("\n" + graycodeconfig.FormatCatalogHealth(graycodeconfig.CatalogHealthReport(context.Background())) + "\n") - preflight := graycodeconfig.EnginePreflightReportWithSettings(context.Background(), settings, graycodeconfig.EnginePreflightOptions{}) - b.WriteString("\n" + graycodeconfig.FormatEnginePreflight(preflight) + "\n") - b.WriteString("\n" + graycodeconfig.FormatSandboxChecklist(graycodeconfig.EvaluateSandboxChecklist(context.Background())) + "\n") + b.WriteString("\n" + hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName) + "\n") + b.WriteString("\n" + hawkconfig.FormatCatalogHealth(hawkconfig.CatalogHealthReport(context.Background())) + "\n") + preflight := hawkconfig.EnginePreflightReportWithSettings(context.Background(), settings, hawkconfig.EnginePreflightOptions{}) + b.WriteString("\n" + hawkconfig.FormatEnginePreflight(preflight) + "\n") + b.WriteString("\n" + hawkconfig.FormatSandboxChecklist(hawkconfig.EvaluateSandboxChecklist(context.Background())) + "\n") b.WriteString("\nBackends (Gap-05):\n") b.WriteString(fmt.Sprintf(" media: %s\n", backendStatus(tool.MediaEngineName(), tool.MediaEngineName() != ""))) b.WriteString(fmt.Sprintf(" stt: %s\n", backendStatus("", stt.Enabled()))) b.WriteString(fmt.Sprintf(" computer: %s\n", backendStatus(tool.ComputerBackendName(), tool.ComputerBackendName() != ""))) - b.WriteString("\n" + graycodeconfig.CredentialStorageStatus(context.Background()).Formatted + "\n") - if deployReport, err := graycodeconfig.DeploymentStatusReportWithSettings(context.Background(), settings, modelName); err == nil { + b.WriteString("\n" + hawkconfig.CredentialStorageStatus(context.Background()).Formatted + "\n") + if deployReport, err := hawkconfig.DeploymentStatusReportWithSettings(context.Background(), settings, modelName); err == nil { b.WriteString("\n" + deployReport + "\n") } b.WriteString("\n" + envSummaryWithSelection(providerName, modelName, false) + "\n") @@ -92,18 +92,18 @@ func doctorReport(settings graycodeconfig.Settings) string { } // Project instructions - if md := graycodeconfig.LoadAgentsMD(); md != "" { + if md := hawkconfig.LoadAgentsMD(); md != "" { b.WriteString("\nProject instructions: found\n") } else { b.WriteString("\nProject instructions: not found (consider creating AGENTS.md)\n") } - // Installed skills (graycode ships none; skills come from user/marketplace installs) + // Installed skills (hawk ships none; skills come from user/marketplace installs) skillsDir := filepath.Join(storage.StateDir(), "skills") if entries, err := os.ReadDir(skillsDir); err == nil && len(entries) > 0 { b.WriteString(fmt.Sprintf("Installed skills: %d\n", len(entries))) } else { - b.WriteString("Installed skills: none (install with `graycode skills install`)\n") + b.WriteString("Installed skills: none (install with `hawk skills install`)\n") } b.WriteString(fmt.Sprintf("Configured MCP servers: %d\n", len(settings.MCPServers)+len(mcpServers))) @@ -112,7 +112,7 @@ func doctorReport(settings graycodeconfig.Settings) string { // Session recovery status recoveryCandidates := session.ScanForRecovery() if len(recoveryCandidates) > 0 { - b.WriteString(fmt.Sprintf("\nInterrupted sessions: %d (run graycode recover)\n", len(recoveryCandidates))) + b.WriteString(fmt.Sprintf("\nInterrupted sessions: %d (run hawk recover)\n", len(recoveryCandidates))) } else { b.WriteString("\nInterrupted sessions: none\n") } @@ -134,14 +134,14 @@ func backendStatus(name string, enabled bool) string { return "wired (" + name + ")" } -func healthCheckReport(settings graycodeconfig.Settings, provider string) string { +func healthCheckReport(settings hawkconfig.Settings, provider string) string { registry := health.NewRegistry() registry.Register("api_key", providerCredentialHealthChecker(provider)) // Settings validation registry.Register("config", func(ctx context.Context) health.Check { - result := graycodeconfig.ValidateSettings(settings) + result := hawkconfig.ValidateSettings(settings) if result.Valid { return health.Check{Name: "config", Status: health.Healthy, Message: "Configuration valid"} } @@ -219,7 +219,7 @@ func providerCredentialHealthChecker(provider string) health.Checker { status := health.Unhealthy message := label + " credential not configured" - if providerID != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, providerID) { + if providerID != "" && hawkconfig.HasStoredCredentialForProvider(ctx, providerID) { status = health.Healthy message = label + " credential configured" } @@ -237,19 +237,19 @@ func providerCredentialHealthChecker(provider string) health.Checker { func diagnosticsProvider(ctx context.Context, provider string) string { provider = strings.TrimSpace(provider) if provider == "" || strings.EqualFold(provider, "auto") { - provider = strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)) + provider = strings.TrimSpace(hawkconfig.ActiveGateway(ctx)) if provider == "" || strings.EqualFold(provider, "auto") { - provider = strings.TrimSpace(graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{}).Provider) + provider = strings.TrimSpace(hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{}).Provider) } } - return graycodeconfig.ActiveProviderID(provider) + return hawkconfig.ActiveProviderID(provider) } -func settingsSummary(settings graycodeconfig.Settings) string { +func settingsSummary(settings hawkconfig.Settings) string { return configCommandSummary(settings) } -func mcpConfigSummary(settings graycodeconfig.Settings) string { +func mcpConfigSummary(settings hawkconfig.Settings) string { if len(settings.MCPServers) == 0 && len(mcpServers) == 0 { return auditTint("No MCP servers configured.", textMuted) } diff --git a/cmd/diagnostics_test.go b/cmd/diagnostics_test.go index 7aca806f2..b4dfdb73d 100644 --- a/cmd/diagnostics_test.go +++ b/cmd/diagnostics_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/resilience/health" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/resilience/health" ) type diagnosticsContextKey struct{} @@ -25,7 +25,7 @@ func (s *contextRecordingCredentialStore) Get(ctx context.Context, account strin func TestDoctorReport(t *testing.T) { t.Parallel() - settings := graycodeconfig.Settings{} + settings := hawkconfig.Settings{} report := doctorReport(settings) if report == "" { t.Error("doctorReport should produce non-empty output") @@ -33,14 +33,14 @@ func TestDoctorReport(t *testing.T) { if !strings.Contains(report, "Version") { t.Error("report should mention version") } - if !strings.Contains(report, "Ecosystem (graycode-router · harrier · shrike)") { + if !strings.Contains(report, "Ecosystem (eyrie · harrier · shrike)") { t.Error("report should include ecosystem panel") } } func TestDoctorReportProviderModelOrder(t *testing.T) { t.Parallel() - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Model: "claude-sonnet-4-20250514", Provider: "anthropic", } @@ -56,14 +56,14 @@ func TestDoctorReportProviderModelOrder(t *testing.T) { func TestDoctorReportUsesResolvedProviderForChecks(t *testing.T) { isolateCredentialHome(t) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) - report := doctorReport(graycodeconfig.Settings{ + report := doctorReport(hawkconfig.Settings{ Model: "claude-sonnet-4-20250514", Provider: "anthropic", }) @@ -74,23 +74,23 @@ func TestDoctorReportUsesResolvedProviderForChecks(t *testing.T) { func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { isolateCredentialHome(t) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", t.TempDir()) - graycodeconfig.InvalidateConfigUICache() + t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) + hawkconfig.InvalidateConfigUICache() store := &contextRecordingCredentialStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.WithValue(context.Background(), diagnosticsContextKey{}, "checker-context") if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } store.contextValue = nil @@ -117,12 +117,12 @@ func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { func TestProviderCredentialHealthCheckerMissingIsUnhealthy(t *testing.T) { isolateCredentialHome(t) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", t.TempDir()) - graycodeconfig.InvalidateConfigUICache() + t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) + hawkconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) result := providerCredentialHealthChecker("openai")(context.Background()) @@ -142,7 +142,7 @@ func TestProviderCredentialHealthCheckerMissingIsUnhealthy(t *testing.T) { func TestSettingsSummary(t *testing.T) { t.Parallel() - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Model: "claude-sonnet-4-20250514", Provider: "anthropic", } @@ -154,7 +154,7 @@ func TestSettingsSummary(t *testing.T) { func TestMcpConfigSummary(t *testing.T) { t.Parallel() - settings := graycodeconfig.Settings{} + settings := hawkconfig.Settings{} summary := mcpConfigSummary(settings) if summary == "" { t.Error("mcpConfigSummary should produce output") diff --git a/cmd/dx.go b/cmd/dx.go index fb8900427..18dde767b 100644 --- a/cmd/dx.go +++ b/cmd/dx.go @@ -10,19 +10,19 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/storage" ) // startTime records when the process started, used by debugOutput for uptime. var startTime = time.Now() // doctorOutput returns a comprehensive system diagnostics string. -func doctorOutput(settings graycodeconfig.Settings) string { +func doctorOutput(settings hawkconfig.Settings) string { var b strings.Builder - b.WriteString("=== Graycode Doctor ===\n\n") + b.WriteString("=== Hawk Doctor ===\n\n") // Go version, OS, arch b.WriteString("System:\n") @@ -49,12 +49,12 @@ func doctorOutput(settings graycodeconfig.Settings) string { b.WriteString(fmt.Sprintf(" TERM: %s\n", termVal)) b.WriteString(fmt.Sprintf(" COLORTERM: %s\n", colorTerm)) - // Graycode version + // Hawk version v := version if v == "" { v = "(dev)" } - b.WriteString("\nGraycode:\n") + b.WriteString("\nHawk:\n") b.WriteString(fmt.Sprintf(" Version: %s\n", v)) if buildDate != "" && buildDate != "unknown" { b.WriteString(fmt.Sprintf(" Build date: %s\n", buildDate)) @@ -67,10 +67,10 @@ func doctorOutput(settings graycodeconfig.Settings) string { } b.WriteString("\nProvider:\n") b.WriteString(fmt.Sprintf(" Provider: %s\n", effectiveProvider)) - b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(graycodeconfig.ActiveProvider(context.Background())))) + b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(hawkconfig.ActiveProvider(context.Background())))) - // Model configured (graycode-router provider.json) - effectiveModel := strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) + // Model configured (eyrie provider.json) + effectiveModel := strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" } @@ -111,7 +111,7 @@ func doctorOutput(settings graycodeconfig.Settings) string { } // AGENTS.md found - agentsMD := graycodeconfig.LoadAgentsMD() + agentsMD := hawkconfig.LoadAgentsMD() if agentsMD != "" { b.WriteString("AGENTS.md: found\n") } else { @@ -146,7 +146,7 @@ func doctorOutput(settings graycodeconfig.Settings) string { // doctorJSON returns the doctor diagnostics as indented JSON, mirroring the // fields of doctorOutput but as machine-parseable structured data. -func doctorJSON(settings graycodeconfig.Settings) string { +func doctorJSON(settings hawkconfig.Settings) string { type sessionDirInfo struct { Path string `json:"path"` Status string `json:"status"` @@ -165,7 +165,7 @@ func doctorJSON(settings graycodeconfig.Settings) string { if effectiveProvider == "" { effectiveProvider = "(not configured)" } - effectiveModel := strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) + effectiveModel := strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" } @@ -219,7 +219,7 @@ func doctorJSON(settings graycodeconfig.Settings) string { plugins = len(manifests) } - agentsMD := graycodeconfig.LoadAgentsMD() + agentsMD := hawkconfig.LoadAgentsMD() agentsState := "not found" if agentsMD != "" { agentsState = "found" @@ -274,7 +274,7 @@ func doctorJSON(settings graycodeconfig.Settings) string { Version: v, BuildDate: buildDate, Provider: effectiveProvider, - APIKey: maskedKeyStatus(graycodeconfig.ActiveProvider(context.Background())), + APIKey: maskedKeyStatus(hawkconfig.ActiveProvider(context.Background())), Model: effectiveModel, SessionDir: sessDir, MCPServers: mcpCount, @@ -293,7 +293,7 @@ func maskedKeyStatus(provider string) string { if provider == "" { return "(no provider set)" } - status := graycodeconfig.EnvKeyStatus(provider) + status := hawkconfig.EnvKeyStatus(provider) if status == "set" { return "configured (masked)" } @@ -414,7 +414,7 @@ func countOpenFDs() int { // Returns the file path and any error. func exportMarkdown(messages []displayMsg, sessionID string) (string, error) { var b strings.Builder - b.WriteString(fmt.Sprintf("# Graycode Session: %s\n\n", sessionID)) + b.WriteString(fmt.Sprintf("# Hawk Session: %s\n\n", sessionID)) b.WriteString(fmt.Sprintf("Exported: %s\n\n", time.Now().Format(time.RFC3339))) b.WriteString("---\n\n") @@ -443,7 +443,7 @@ func exportMarkdown(messages []displayMsg, sessionID string) (string, error) { } } - filename := fmt.Sprintf("graycode-session-%s.md", sessionID) + filename := fmt.Sprintf("hawk-session-%s.md", sessionID) if err := os.WriteFile(filename, []byte(b.String()), 0o600); err != nil { return "", fmt.Errorf("failed to write %s: %w", filename, err) } @@ -490,7 +490,7 @@ func exportJSON(messages []displayMsg, sessionID string) (string, error) { return "", fmt.Errorf("failed to marshal session: %w", err) } - filename := fmt.Sprintf("graycode-session-%s.json", sessionID) + filename := fmt.Sprintf("hawk-session-%s.json", sessionID) if err := os.WriteFile(filename, data, 0o600); err != nil { return "", fmt.Errorf("failed to write %s: %w", filename, err) } diff --git a/cmd/dx_test.go b/cmd/dx_test.go index 5c9a07f8c..57bad5a63 100644 --- a/cmd/dx_test.go +++ b/cmd/dx_test.go @@ -7,15 +7,15 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestDoctorOutputContainsSections(t *testing.T) { preserveCLICompilerVersionState(t) version = "test-dx-version" - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Provider: "openai", Model: "gpt-4o", } @@ -23,7 +23,7 @@ func TestDoctorOutputContainsSections(t *testing.T) { out := doctorOutput(settings) sections := []string{ - "Graycode Doctor", + "Hawk Doctor", "Go version:", "OS:", "Arch:", @@ -50,10 +50,10 @@ func TestDoctorOutputContainsSections(t *testing.T) { func TestDoctorOutputWithMCPServers(t *testing.T) { preserveCLICompilerVersionState(t) version = "test-dx-version" - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Provider: "anthropic", Model: "claude-sonnet-4-20250514", - MCPServers: []graycodeconfig.MCPServerConfig{ + MCPServers: []hawkconfig.MCPServerConfig{ {Name: "test-mcp", Command: "test-cmd"}, }, } @@ -67,10 +67,10 @@ func TestDoctorOutputWithMCPServers(t *testing.T) { func TestDoctorJSONIsValidStructuredOutput(t *testing.T) { preserveCLICompilerVersionState(t) version = "test-dx-version" - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ Provider: "anthropic", Model: "claude-sonnet-4-20250514", - MCPServers: []graycodeconfig.MCPServerConfig{ + MCPServers: []hawkconfig.MCPServerConfig{ {Name: "test-mcp", Command: "test-cmd"}, }, } @@ -168,7 +168,7 @@ func TestExportMarkdownCreatesFile(t *testing.T) { defer os.Chdir(origDir) messages := []displayMsg{ - {role: "user", content: "Hello graycode"}, + {role: "user", content: "Hello hawk"}, {role: "assistant", content: "Hello! How can I help?"}, {role: "system", content: "System message here"}, {role: "welcome", content: "Should be skipped"}, @@ -189,10 +189,10 @@ func TestExportMarkdownCreatesFile(t *testing.T) { } content := string(data) - if !strings.Contains(content, "# Graycode Session: test-export-id") { + if !strings.Contains(content, "# Hawk Session: test-export-id") { t.Errorf("export missing session header") } - if !strings.Contains(content, "Hello graycode") { + if !strings.Contains(content, "Hello hawk") { t.Errorf("export missing user message") } if !strings.Contains(content, "Hello! How can I help?") { diff --git a/cmd/ecosystem.go b/cmd/ecosystem.go index 3baab0c9d..6049c5a90 100644 --- a/cmd/ecosystem.go +++ b/cmd/ecosystem.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" "github.com/spf13/cobra" ) @@ -12,8 +12,8 @@ var ecosystemJSON bool var ecosystemCmd = &cobra.Command{ Use: "ecosystem", - Short: "Show graycode-router, harrier, and shrike integration status", - Long: "Print the ecosystem panel summarizing LLM provider (graycode-router), memory graph (harrier), and token pipeline (shrike). Same block as the top of graycode doctor.", + Short: "Show eyrie, harrier, and shrike integration status", + Long: "Print the ecosystem panel summarizing LLM provider (eyrie), memory graph (harrier), and token pipeline (shrike). Same block as the top of hawk doctor.", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -24,12 +24,12 @@ var ecosystemCmd = &cobra.Command{ providerName = "auto" } if ecosystemJSON { - report := graycodeconfig.BuildEcosystemReport(context.Background(), providerName, modelName) + report := hawkconfig.BuildEcosystemReport(context.Background(), providerName, modelName) enc := json.NewEncoder(cmd.OutOrStdout()) enc.SetIndent("", " ") return enc.Encode(report) } - cmd.Println(graycodeconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)) + cmd.Println(hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)) return nil }, } diff --git a/cmd/ecosystem_test.go b/cmd/ecosystem_test.go index f17cbe339..82b9f127b 100644 --- a/cmd/ecosystem_test.go +++ b/cmd/ecosystem_test.go @@ -4,17 +4,17 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestEcosystemCmdRuns(t *testing.T) { - settings := graycodeconfig.Settings{} + settings := hawkconfig.Settings{} model, provider := effectiveModelAndProvider(settings) if provider == "" { provider = "auto" } - out := graycodeconfig.FormatEcosystemPanel(t.Context(), provider, model) - if !strings.Contains(out, "Ecosystem (graycode-router · harrier · shrike)") { + out := hawkconfig.FormatEcosystemPanel(t.Context(), provider, model) + if !strings.Contains(out, "Ecosystem (eyrie · harrier · shrike)") { t.Fatalf("unexpected panel: %q", out) } if err := ecosystemCmd.RunE(ecosystemCmd, nil); err != nil { diff --git a/cmd/error_classify.go b/cmd/error_classify.go index 5827c2d9e..e52162476 100644 --- a/cmd/error_classify.go +++ b/cmd/error_classify.go @@ -4,41 +4,41 @@ import ( "fmt" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/errhint" - "github.com/GrayCodeAI/graycode-cli/internal/graycodeerr" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/errhint" + "github.com/GrayCodeAI/hawk/internal/hawkerr" ) // friendlyErrorMessage returns the user-friendly message for an error. -// Delegates to the shared graycodeerr.ClassifyErrorMessage for the base message, +// Delegates to the shared hawkerr.ClassifyErrorMessage for the base message, // then enriches specific cases with dynamic hints that require the config -// package (graycodeerr can't import internal/config without a cycle). +// package (hawkerr can't import internal/config without a cycle). func friendlyErrorMessage(err error) string { - msg := graycodeerr.ClassifyErrorMessage(err) + msg := hawkerr.ClassifyErrorMessage(err) if err == nil { return msg } - ec := graycodeerr.ClassifyError(err) + ec := hawkerr.ClassifyError(err) low := strings.ToLower(err.Error()) switch ec.ExitCode { - case graycodeerr.ExitNotFound: + case hawkerr.ExitNotFound: // Enrich model-not-found errors with concrete examples from the catalog. if strings.Contains(low, "model") || strings.Contains(low, "unknown") || strings.Contains(low, "does not exist") { - ex1, ex2 := graycodeconfig.ExampleModelHints() + ex1, ex2 := hawkconfig.ExampleModelHints() msg = fmt.Sprintf( - "Model not found. Check your model name with /model.\n Examples from the graycode-router catalog: %s, %s\n Use /models to list all models, or /config to change provider.", + "Model not found. Check your model name with /model.\n Examples from the eyrie catalog: %s, %s\n Use /models to list all models, or /config to change provider.", ex1, ex2, ) } - case graycodeerr.ExitAuth: + case hawkerr.ExitAuth: msg += "\n Check your API key with /config. Keys can expire or be revoked." - case graycodeerr.ExitNetwork: + case hawkerr.ExitNetwork: msg += "\n Check your internet connection. If you're behind a proxy, configure it with /config." - case graycodeerr.ExitTimeout: + case hawkerr.ExitTimeout: msg += "\n The request took too long. Try again, or use /model to switch to a faster provider." } diff --git a/cmd/error_classify_test.go b/cmd/error_classify_test.go index 0a94997d2..82eca742f 100644 --- a/cmd/error_classify_test.go +++ b/cmd/error_classify_test.go @@ -15,7 +15,7 @@ func TestFriendlyErrorMessageAppendsProviderHint(t *testing.T) { } func TestFriendlyErrorMessageNoHintForLocalError(t *testing.T) { - // A local error must not draw an errhint provider hint (graycode's own + // A local error must not draw an errhint provider hint (hawk's own // ExitNotFound enrichment is separate and fine). msg := friendlyErrorMessage(errors.New("file not found: x")) for _, marker := range []string{"API key rejected", "Rate limited", "Can't reach the provider", "Context window full", "Model unavailable"} { diff --git a/cmd/errors.go b/cmd/errors.go index baabee7fd..80ad5a0fc 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -11,8 +11,8 @@ import ( "sync" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/storage" ) // friendlyError translates a raw error into a user-friendly message with an @@ -25,7 +25,7 @@ func friendlyError(err error) string { // ─── panicRecovery ──────────────────────────────────────────────────────────── // Catches panics, saves the current session state, logs the stack trace to -// Graycode's user state crash log, and exits with a user-friendly message. +// Hawk's user state crash log, and exits with a user-friendly message. // panicSaveFn is set by runChat to a closure that persists the active session // and stops the container sandbox. panicRecovery invokes it on an unexpected @@ -51,7 +51,7 @@ func panicRecovery(saveFn func()) { stack := string(debug.Stack()) // Generate a short, unique error ID for support reference. - // Format: graycode-YYMMDD-<6 hex chars from stack hash> + // Format: hawk-YYMMDD-<6 hex chars from stack hash> errorID := generateErrorID(stack) // Attempt to save session @@ -84,14 +84,14 @@ func panicRecovery(saveFn func()) { } // Print user-friendly message - _, _ = fmt.Fprintf(os.Stderr, "\ngraycode encountered an unexpected error and needs to exit.\n") + _, _ = fmt.Fprintf(os.Stderr, "\nhawk encountered an unexpected error and needs to exit.\n") if saveFn != nil { _, _ = fmt.Fprintf(os.Stderr, "Your session has been saved.\n") } else { _, _ = fmt.Fprintf(os.Stderr, "Session messages are persisted incrementally; the in-flight message may be lost.\n") } _, _ = fmt.Fprintf(os.Stderr, "Details logged to %s\n", filepath.Join(storage.StateDir(), "crash.log")) - _, _ = fmt.Fprintf(os.Stderr, "Please report this at: https://github.com/GrayCodeAI/graycode-cli/issues\n") + _, _ = fmt.Fprintf(os.Stderr, "Please report this at: https://github.com/GrayCodeAI/hawk/issues\n") _, _ = fmt.Fprintf(os.Stderr, "Include this error ID: %s\n\n", errorID) _, _ = fmt.Fprintf(os.Stderr, "panic: %v\n", r) os.Exit(1) // os.Exit intentional: panic recovery, defer already unwound @@ -99,7 +99,7 @@ func panicRecovery(saveFn func()) { } // generateErrorID creates a short, unique error ID from the stack trace. -// Format: graycode-YYMMDD-<6 hex chars> — enough to correlate with crash.log +// Format: hawk-YYMMDD-<6 hex chars> — enough to correlate with crash.log // entries without requiring a random source. func generateErrorID(stack string) string { // Simple hash of the stack trace for uniqueness. @@ -107,18 +107,18 @@ func generateErrorID(stack string) string { for i := 0; i < len(stack) && i < 2000; i++ { hash = ((hash << 5) + hash) ^ uint32(stack[i]) } - return fmt.Sprintf("graycode-%s-%06x", time.Now().Format("060102"), hash&0xFFFFFF) + return fmt.Sprintf("hawk-%s-%06x", time.Now().Format("060102"), hash&0xFFFFFF) } // ─── errorLogger ────────────────────────────────────────────────────────────── -// Writes errors to Graycode's user state error log with timestamps. Thread-safe. +// Writes errors to Hawk's user state error log with timestamps. Thread-safe. type errorLoggerT struct { mu sync.Mutex path string } -// LogError writes a timestamped error entry to the Graycode error log. +// LogError writes a timestamped error entry to the Hawk error log. func (l *errorLoggerT) LogError(context string, err error) { if l == nil || err == nil { return @@ -142,7 +142,7 @@ func (l *errorLoggerT) LogError(context string, err error) { _, _ = f.WriteString(entry) } -// LogErrorf writes a formatted, timestamped error entry to the Graycode error log. +// LogErrorf writes a formatted, timestamped error entry to the Hawk error log. func (l *errorLoggerT) LogErrorf(format string, args ...interface{}) { if l == nil { return @@ -181,24 +181,24 @@ func (w StartupWarning) String() string { return fmt.Sprintf("[%s] %s", w.Check, w.Message) } -func validateStartup(settings graycodeconfig.Settings) []StartupWarning { +func validateStartup(settings hawkconfig.Settings) []StartupWarning { var warnings []StartupWarning // 1. Check API key for configured provider. - // Graycode reads credentials from the OS secret store (macOS Keychain / + // Hawk reads credentials from the OS secret store (macOS Keychain / // Linux Keyring), not just env vars — so we must check there too. providerName := strings.TrimSpace(settings.Provider) if providerName == "" { - providerName = strings.TrimSpace(graycodeconfig.ActiveProvider(context.Background())) + providerName = strings.TrimSpace(hawkconfig.ActiveProvider(context.Background())) } if providerName != "" && providerName != "ollama" { hasEnv := false - if envKey := graycodeconfig.ProviderAPIKeyEnv(providerName); envKey != "" { + if envKey := hawkconfig.ProviderAPIKeyEnv(providerName); envKey != "" { hasEnv = os.Getenv(envKey) != "" } - hasStored := graycodeconfig.HasStoredCredentialForProvider(context.Background(), providerName) + hasStored := hawkconfig.HasStoredCredentialForProvider(context.Background(), providerName) if !hasEnv && !hasStored { - envKey := graycodeconfig.ProviderAPIKeyEnv(providerName) + envKey := hawkconfig.ProviderAPIKeyEnv(providerName) warnings = append(warnings, StartupWarning{ Check: "api_key", Message: fmt.Sprintf("No API key found for %s. Set %s in your environment or run /config.", providerName, envKey), @@ -234,10 +234,10 @@ func validateStartup(settings graycodeconfig.Settings) []StartupWarning { // post-first-paint (background), not on the TUI startup critical path where an // offline machine would stall the UI for seconds. Returns a warning message, or // "" when reachable/not applicable. -func checkNetworkReachability(settings graycodeconfig.Settings) string { +func checkNetworkReachability(settings hawkconfig.Settings) string { providerName := strings.TrimSpace(settings.Provider) if providerName == "" { - providerName = strings.TrimSpace(graycodeconfig.ActiveProvider(context.Background())) + providerName = strings.TrimSpace(hawkconfig.ActiveProvider(context.Background())) } if providerName == "" || providerName == "ollama" { return "" @@ -254,5 +254,5 @@ func checkNetworkReachability(settings graycodeconfig.Settings) string { // providerDNSHost returns a hostname to check DNS resolution for a provider. func providerDNSHost(provider string) string { - return graycodeconfig.GatewayDNSHost(provider) + return hawkconfig.GatewayDNSHost(provider) } diff --git a/cmd/errors_test.go b/cmd/errors_test.go index 44ad7a30c..26880194d 100644 --- a/cmd/errors_test.go +++ b/cmd/errors_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestFriendlyErrorNil(t *testing.T) { @@ -151,7 +151,7 @@ func TestFriendlyErrorInvalidModel(t *testing.T) { if !strings.Contains(got, "/model") { t.Errorf("friendlyError(%q) = %q, should suggest /model", tt.errMsg, got) } - example1, example2 := graycodeconfig.ExampleModelHints() + example1, example2 := hawkconfig.ExampleModelHints() if !strings.Contains(got, example1) || !strings.Contains(got, example2) { t.Errorf("friendlyError(%q) = %q, should suggest valid model names", tt.errMsg, got) } @@ -582,7 +582,7 @@ func TestFriendlyErrorBackwardCompat(t *testing.T) { err string contains string }{ - {"rate limit 429", "graycode-router: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, + {"rate limit 429", "eyrie: openai stream request failed: max retries (3) exceeded: HTTP 429", "Rate limited"}, {"unauthorized 401", "HTTP 401 Unauthorized", "Authentication failed"}, {"forbidden 403", "HTTP 403 Forbidden", "Access denied"}, {"not found 404", "model not found", "/model"}, @@ -604,8 +604,8 @@ func TestFriendlyErrorBackwardCompat(t *testing.T) { } // helper for tests -func emptySettings() graycodeconfig.Settings { - return graycodeconfig.Settings{} +func emptySettings() hawkconfig.Settings { + return hawkconfig.Settings{} } // ── signalHandler test ──────────────────────────────────────────────────────── diff --git a/cmd/eval.go b/cmd/eval.go index c0add1e7c..d41d952fe 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -9,10 +9,10 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/feature/eval" - "github.com/GrayCodeAI/graycode-cli/internal/feature/evalloop" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/feature/eval" + "github.com/GrayCodeAI/hawk/internal/feature/evalloop" + "github.com/GrayCodeAI/hawk/internal/tool" "github.com/spf13/cobra" ) @@ -98,22 +98,22 @@ func runEvalLoop(cmd *cobra.Command, _ []string) error { if strings.TrimSpace(evalLoopPrompt) == "" { return fmt.Errorf("--prompt is required") } - settings := graycodeconfig.LoadGlobalSettings() + settings := hawkconfig.LoadGlobalSettings() ctx := context.Background() - gw, err := graycodeconfig.NewGraycodeRouterEngineForSettings(settings) + gw, err := hawkconfig.NewEyrieEngineForSettings(settings) if err != nil { return fmt.Errorf("eval loop: build engine client: %w", err) } model := strings.TrimSpace(evalLoopModel) if model == "" { - model = strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) + model = strings.TrimSpace(hawkconfig.ActiveModel(ctx)) } if model == "" { model = strings.TrimSpace(settings.Model) } - workDir, err := os.MkdirTemp("", "graycode-eval-loop-*") + workDir, err := os.MkdirTemp("", "hawk-eval-loop-*") if err != nil { return fmt.Errorf("eval loop: create temp dir: %w", err) } @@ -225,7 +225,7 @@ func runEval(_ *cobra.Command, _ []string) error { fmt.Printf("%s\n", auditTint(fmt.Sprintf("Running %d tasks with model %s...", len(tasks), modelName), textPrimary)) - suite := &eval.BenchmarkSuite{Name: "graycode-eval", Tasks: tasks} + suite := &eval.BenchmarkSuite{Name: "hawk-eval", Tasks: tasks} runner := eval.NewRunner(modelName, "") runner.NoCache = evalNoCache if !evalNoCache { diff --git a/cmd/eval_tools.go b/cmd/eval_tools.go index 5e1d2c685..37769598e 100644 --- a/cmd/eval_tools.go +++ b/cmd/eval_tools.go @@ -6,9 +6,9 @@ import ( "fmt" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/feature/eval" - "github.com/GrayCodeAI/graycode-cli/internal/types" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/feature/eval" + "github.com/GrayCodeAI/hawk/internal/types" "github.com/spf13/cobra" ) @@ -29,7 +29,7 @@ var evalToolsCmd = &cobra.Command{ } // defaultToolUseCases is a small built-in set exercising clear positive and -// negative tool-trigger situations against graycode's standard tools. +// negative tool-trigger situations against hawk's standard tools. func defaultToolUseCases() []eval.ToolUseCase { return []eval.ToolUseCase{ { @@ -68,7 +68,7 @@ func defaultToolUseCases() []eval.ToolUseCase { } func runEvalTools(cmd *cobra.Command, _ []string) error { - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() registry, err := defaultRegistry(settings) if err != nil { @@ -79,18 +79,18 @@ func runEvalTools(cmd *cobra.Command, _ []string) error { return err } modelName, providerName := effectiveModelAndProvider(settings) - sess, err := newConfiguredGraycodeSession(settings, providerName, modelName, systemPrompt, registry, nil) + sess, err := newConfiguredHawkSession(settings, providerName, modelName, systemPrompt, registry, nil) if err != nil { return err } - tools := registry.GraycodeRouterTools() + tools := registry.EyrieTools() // caller performs one tool-aware turn and reports the first tool the model // chose (if any). It does not execute the tool — we are scoring selection, // not effects. caller := func(ctx context.Context, c eval.ToolUseCase) (eval.ObservedCall, error) { - resp, err := sess.Chat(ctx, []types.GraycodeRouterMessage{ + resp, err := sess.Chat(ctx, []types.EyrieMessage{ {Role: "user", Content: c.Prompt}, }, types.ChatOptions{Model: model, Tools: tools}) if err != nil { diff --git a/cmd/exec.go b/cmd/exec.go index 3533097b0..6a72c2f6b 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -14,16 +14,16 @@ import ( "time" "unicode/utf8" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/errhint" - "github.com/GrayCodeAI/graycode-cli/internal/multiagent/agents" - "github.com/GrayCodeAI/graycode-cli/internal/notify" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - cloud "github.com/GrayCodeAI/graycode-cli/internal/platform/cloud" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/errhint" + "github.com/GrayCodeAI/hawk/internal/multiagent/agents" + "github.com/GrayCodeAI/hawk/internal/notify" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + cloud "github.com/GrayCodeAI/hawk/internal/platform/cloud" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/spf13/cobra" ) @@ -93,13 +93,13 @@ Autonomy Levels: yolo Never ask for permission Examples: - graycode exec "analyze this codebase" - graycode exec --auto full "fix the tests and commit" - graycode exec --json "what files are in src/" - graycode exec --ephemeral --json "run tests and report" > result.json - echo "explain main.go" | graycode exec - - graycode exec --agent reviewer "review the latest commit" - graycode exec --model claude-sonnet-4-6 "quick fix: typo in README"`, + hawk exec "analyze this codebase" + hawk exec --auto full "fix the tests and commit" + hawk exec --json "what files are in src/" + hawk exec --ephemeral --json "run tests and report" > result.json + echo "explain main.go" | hawk exec - + hawk exec --agent reviewer "review the latest commit" + hawk exec --model claude-sonnet-4-6 "quick fix: typo in README"`, Args: cobra.MaximumNArgs(1), RunE: runExec, } @@ -110,7 +110,7 @@ func init() { execCmd.Flags().StringVarP(&execModel, "model", "m", "", "Model ID to use") execCmd.Flags().IntVar(&execMaxTurns, "max-turns", 0, "Maximum agentic turns (0 = unlimited)") execCmd.Flags().StringVar(&execCWD, "cwd", "", "Working directory") - execCmd.Flags().StringVar(&execAgent, "agent", "", "Agent persona to use (from Graycode user state)") + execCmd.Flags().StringVar(&execAgent, "agent", "", "Agent persona to use (from Hawk user state)") execCmd.Flags().StringVarP(&execSessionID, "session-id", "s", "", "Continue an existing session") execCmd.Flags().StringVar(&execTag, "tag", "", "Session tag for categorization") execCmd.Flags().BoolVarP(&execWorktree, "worktree", "w", false, "Run in an isolated git worktree") @@ -176,7 +176,7 @@ func runExec(_ *cobra.Command, args []string) error { base := getCurrentBranch(cwd) branch := execWorktreeName if branch == "" { - branch = fmt.Sprintf("graycode-exec/%d-%s", start.UnixMilli(), randomHex(4)) + branch = fmt.Sprintf("hawk-exec/%d-%s", start.UnixMilli(), randomHex(4)) } var wtErr error wtPath, wtErr = createExecWorktree(cwd, base, branch) @@ -191,7 +191,7 @@ func runExec(_ *cobra.Command, args []string) error { } // Load settings - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() // Build system prompt systemPrompt, err := buildSystemPrompt() @@ -227,7 +227,7 @@ func runExec(_ *cobra.Command, args []string) error { } // Create engine session - sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) + sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) if cfgErr != nil { return cfgErr } @@ -250,11 +250,11 @@ func runExec(_ *cobra.Command, args []string) error { // GitHub Actions event (an outside contributor's issue/PR/comment body), // clamp autonomy to read-only auto-approval so attacker-controlled text // cannot drive writes or Bash. Maintainers can opt out with - // GRAYCODE_GHA_TRUST_EVENT=1. + // HAWK_GHA_TRUST_EVENT=1. if ghaCtx.Active && !ghaCtx.Trusted { const ceiling = engine.AutonomyBasic if sess.PermSvc().Autonomy() > ceiling { - fmt.Fprintf(os.Stderr, "%s\n", auditTint(fmt.Sprintf("graycode: untrusted GitHub event (author_association=%q); capping autonomy at %s", ghaCtx.AuthorAssociation, ceiling), warnAmber)) + fmt.Fprintf(os.Stderr, "%s\n", auditTint(fmt.Sprintf("hawk: untrusted GitHub event (author_association=%q); capping autonomy at %s", ghaCtx.AuthorAssociation, ceiling), warnAmber)) sess.PermSvc().SetAutonomy(ceiling) } } @@ -381,7 +381,7 @@ func runExec(_ *cobra.Command, args []string) error { DeviceID: cfg.DeviceID, ProjectID: cfg.ProjectID, SessionID: sessionID, - Capability: "graycode", + Capability: "hawk", Model: effectiveModel, InputTokens: totalIn, OutputTokens: totalOut, @@ -402,10 +402,10 @@ func runExec(_ *cobra.Command, args []string) error { fmt.Println() } if !IsQuiet() { - summary := fmt.Sprintf("graycode: %d tokens in / %d out · %d turn(s) · %s · %s", + summary := fmt.Sprintf("hawk: %d tokens in / %d out · %d turn(s) · %s · %s", totalIn, totalOut, turns, time.Since(start).Round(time.Millisecond), effectiveModel) if cacheRead > 0 || cacheWrite > 0 { - summary = fmt.Sprintf("graycode: %d tokens in / %d out (cache %d read · %d write) · %d turn(s) · %s · %s", + summary = fmt.Sprintf("hawk: %d tokens in / %d out (cache %d read · %d write) · %d turn(s) · %s · %s", totalIn, totalOut, cacheRead, cacheWrite, turns, time.Since(start).Round(time.Millisecond), effectiveModel) } fmt.Fprintf(os.Stderr, "%s\n", auditTint(summary, textMuted)) @@ -470,16 +470,16 @@ type GHAMode string const ( // GHAModeNone means we are not running inside GitHub Actions. GHAModeNone GHAMode = "" - // GHAModeInteractive is used when a human mentioned @graycode in a comment and + // GHAModeInteractive is used when a human mentioned @hawk in a comment and // expects a conversational reply. GHAModeInteractive GHAMode = "interactive" - // GHAModeAutomation is used for label/issue triggers where graycode should act + // GHAModeAutomation is used for label/issue triggers where hawk should act // autonomously on the issue/PR body. GHAModeAutomation GHAMode = "automation" ) // ghMention is the trigger token that promotes an event to interactive mode. -const ghMention = "@graycode" +const ghMention = "@hawk" // ghTrustedAssociations are the GitHub author_association values that identify // a repository insider. Everyone else (CONTRIBUTOR, FIRST_TIME_CONTRIBUTOR, @@ -497,7 +497,7 @@ type GHAContext struct { EventName string // GITHUB_EVENT_NAME Mode GHAMode // resolved operating mode Prompt string // event-derived prompt body - Mention bool // whether an @graycode mention was found in a comment + Mention bool // whether an @hawk mention was found in a comment AuthorAssociation string // GitHub author_association of the triggering actor Trusted bool // author is a repo insider (or explicitly trusted) } @@ -530,7 +530,7 @@ func detectGitHubActions(getenv func(string) string, readFile func(string) ([]by // Trust signal: GitHub reports the actor's relationship to the repo. // Only insiders are trusted to drive high-autonomy tool use; content // from outside contributors is untrusted (prompt-injection surface). - // GRAYCODE_GHA_TRUST_EVENT=1 lets a maintainer opt into trusting all events. + // HAWK_GHA_TRUST_EVENT=1 lets a maintainer opt into trusting all events. ctx.AuthorAssociation = ghAuthorAssociation(payload) ctx.Trusted = ghTrustedAssociations[strings.ToUpper(strings.TrimSpace(ctx.AuthorAssociation))] || ghTrustEventOverride(getenv) @@ -558,7 +558,7 @@ func detectGitHubActions(getenv func(string) string, readFile func(string) ([]by // ghTrustEventOverride reports whether the maintainer has opted into // trusting GitHub Actions event content regardless of author association. func ghTrustEventOverride(getenv func(string) string) bool { - switch strings.ToLower(strings.TrimSpace(getenv("GRAYCODE_GHA_TRUST_EVENT"))) { + switch strings.ToLower(strings.TrimSpace(getenv("HAWK_GHA_TRUST_EVENT"))) { case "1", "true", "yes", "on": return true default: @@ -615,7 +615,7 @@ func ghIssueBody(payload map[string]interface{}) string { return strings.TrimSpace(ghCommentBody(payload)) } -// ghStripMention removes the leading @graycode mention from a comment so the +// ghStripMention removes the leading @hawk mention from a comment so the // remaining text becomes the prompt. func ghStripMention(body string) string { out := body @@ -637,7 +637,7 @@ type skillRunner interface { Run(name string) (string, error) } -// pluginSkillRunner is the production skillRunner backed by Graycode skill storage. +// pluginSkillRunner is the production skillRunner backed by Hawk skill storage. type pluginSkillRunner struct{} func (pluginSkillRunner) Run(name string) (string, error) { @@ -647,7 +647,7 @@ func (pluginSkillRunner) Run(name string) (string, error) { return fmt.Sprintf("[Skill: %s]\n\n%s", s.Name, s.Content), nil } } - return "", fmt.Errorf("skill %q not found (run `graycode skills` to list available skills)", name) + return "", fmt.Errorf("skill %q not found (run `hawk skills` to list available skills)", name) } // defaultSkillRunner is overridable in tests. @@ -776,7 +776,7 @@ func runExecFanout(prompt string, n int) error { fmt.Fprintf(os.Stderr, "\n%s\n", auditTint(fmt.Sprintf("=== fanout attempt %d/%d ===", i, n), infoSky)) att := fanoutAttempt{Attempt: i} - branch := fmt.Sprintf("graycode-exec/%d-fanout%d-%s", start.UnixMilli(), i, randomHex(4)) + branch := fmt.Sprintf("hawk-exec/%d-fanout%d-%s", start.UnixMilli(), i, randomHex(4)) wtPath, wtErr := createExecWorktree(cwd, base, branch) if wtErr != nil { att.Error = fmt.Sprintf("worktree: %v", wtErr) @@ -845,7 +845,7 @@ func runExecFanout(prompt string, n int) error { // best-effort and only when a channel is configured. title := fmt.Sprintf("Fan-out finished: %d/%d attempts succeeded", countOK(attempts), n) _ = notify.SendCompletion(notify.Completion{ - Title: title, Source: "graycode exec --fanout", OK: anyOK, Body: fanoutSummaryLines(attempts), + Title: title, Source: "hawk exec --fanout", OK: anyOK, Body: fanoutSummaryLines(attempts), }) if !anyOK { @@ -909,7 +909,7 @@ func printFanoutReport(attempts []fanoutAttempt) { // (expected to be the attempt's worktree) and returns the structured result. // Stream events are captured rather than printed so N attempts do not interleave. func execOnceInWorktree(prompt string, attemptIdx int) (*ExecResult, error) { - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() systemPrompt, err := buildSystemPrompt() if err != nil { @@ -932,7 +932,7 @@ func execOnceInWorktree(prompt string, attemptIdx int) (*ExecResult, error) { if err != nil { return nil, err } - sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) + sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) if cfgErr != nil { return nil, cfgErr } diff --git a/cmd/exec_test.go b/cmd/exec_test.go index 6de569f81..de312db97 100644 --- a/cmd/exec_test.go +++ b/cmd/exec_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) // --- Skill dispatch tests --------------------------------------------------- @@ -134,7 +134,7 @@ func TestDetectGitHubActions_InteractiveMention(t *testing.T) { "GITHUB_EVENT_NAME": "issue_comment", "GITHUB_EVENT_PATH": "/tmp/event.json", } - payload := `{"comment":{"body":"@graycode please fix the failing test"}}` + payload := `{"comment":{"body":"@hawk please fix the failing test"}}` gha := detectGitHubActions(envFunc(env), fileFunc(payload)) if !gha.Active { t.Fatal("expected Active=true") @@ -156,7 +156,7 @@ func TestDetectGitHubActions_ReviewCommentMention(t *testing.T) { "GITHUB_EVENT_NAME": "pull_request_review_comment", "GITHUB_EVENT_PATH": "/tmp/event.json", } - payload := `{"comment":{"body":"@Graycode explain this change"}}` + payload := `{"comment":{"body":"@Hawk explain this change"}}` gha := detectGitHubActions(envFunc(env), fileFunc(payload)) if gha.Mode != GHAModeInteractive { t.Errorf("expected interactive mode for review comment, got %q", gha.Mode) @@ -223,14 +223,14 @@ func TestDetectGitHubActions_TrustFromAssociation(t *testing.T) { func TestDetectGitHubActions_TrustEnvOverride(t *testing.T) { env := map[string]string{ - "GITHUB_ACTIONS": "true", - "GITHUB_EVENT_NAME": "issues", - "GITHUB_EVENT_PATH": "/tmp/event.json", - "GRAYCODE_GHA_TRUST_EVENT": "1", + "GITHUB_ACTIONS": "true", + "GITHUB_EVENT_NAME": "issues", + "GITHUB_EVENT_PATH": "/tmp/event.json", + "HAWK_GHA_TRUST_EVENT": "1", } outsider := `{"issue":{"title":"t","body":"b","author_association":"NONE"}}` if gha := detectGitHubActions(envFunc(env), fileFunc(outsider)); !gha.Trusted { - t.Error("GRAYCODE_GHA_TRUST_EVENT=1 should trust even NONE association") + t.Error("HAWK_GHA_TRUST_EVENT=1 should trust even NONE association") } } @@ -254,7 +254,7 @@ func TestResolveExecPrompt_Empty(t *testing.T) { func TestPersistExecSession(t *testing.T) { // Set up temp session dir dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(dir, "state")) persistExecSession("test-123", "claude-opus", "anthropic", "hello", "world") @@ -276,7 +276,7 @@ func TestExecResult_JSON(t *testing.T) { Duration: "1.5s", Model: "test-model", Worktree: "/tmp/wt", - Branch: "graycode-exec/123", + Branch: "hawk-exec/123", } data, err := json.Marshal(r) if err != nil { @@ -292,7 +292,7 @@ func TestExecResult_JSON(t *testing.T) { if decoded.Worktree != "/tmp/wt" { t.Errorf("expected worktree path, got %s", decoded.Worktree) } - if decoded.Branch != "graycode-exec/123" { + if decoded.Branch != "hawk-exec/123" { t.Errorf("expected branch, got %s", decoded.Branch) } } diff --git a/cmd/execution_graph.go b/cmd/execution_graph.go index 14a266d7a..c5d6a96d3 100644 --- a/cmd/execution_graph.go +++ b/cmd/execution_graph.go @@ -10,22 +10,22 @@ import ( "strings" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - policycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/fsutil" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/taskruntime" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + policycontracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/fsutil" + "github.com/GrayCodeAI/hawk/internal/graphjournal" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/taskruntime" + "github.com/GrayCodeAI/hawk/internal/tool" "github.com/spf13/cobra" ) func newExecutionGraphCmd() *cobra.Command { graphCmd := &cobra.Command{ Use: "graph", - Short: "Merlin Graycode's portable execution graph", - Long: `Project Graycode-owned sessions, task requests, structured tasks, runtime tasks, + Short: "Merlin Hawk's portable execution graph", + Long: `Project Hawk-owned sessions, task requests, structured tasks, runtime tasks, tool calls, policy observations, verification results, and explicit Swift checkpoint links into the shared graph contract. @@ -39,7 +39,7 @@ truth for scheduling, tools, policy, verification, persistence, and tracing.`, var missionDir string exportCmd := &cobra.Command{ Use: "export [session-id]", - Short: "Export a Graycode session or mission as graph JSON", + Short: "Export a Hawk session or mission as graph JSON", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var export executiongraph.Export @@ -320,12 +320,12 @@ func loadRuntimeGraphObservations( for _, entry := range entries { subject := graphcontracts.Ref{ Kind: graphcontracts.NodeExecution, - ID: "graycode/session/" + saved.ID, + ID: "hawk/session/" + saved.ID, } if _, ok := toolCallIDs[entry.ToolCallID]; ok && entry.ToolCallID != "" { subject = graphcontracts.Ref{ Kind: graphcontracts.NodeExecution, - ID: "graycode/tool-call/" + saved.ID + "/" + entry.ToolCallID, + ID: "hawk/tool-call/" + saved.ID + "/" + entry.ToolCallID, } } if entry.Policy != nil { diff --git a/cmd/execution_graph_test.go b/cmd/execution_graph_test.go index 7d0931cdd..304019458 100644 --- a/cmd/execution_graph_test.go +++ b/cmd/execution_graph_test.go @@ -11,11 +11,11 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - policycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" - "github.com/GrayCodeAI/graycode-cli/internal/session" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + policycontracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/graphjournal" + "github.com/GrayCodeAI/hawk/internal/session" ) type stubSwiftCorrelationResolver struct { @@ -46,18 +46,18 @@ func TestLoadMissionGraphExportValidatesTopology(t *testing.T) { t.Parallel() now := time.Date(2026, time.July, 25, 7, 0, 0, 0, time.UTC) - mission := graphcontracts.Ref{Kind: graphcontracts.NodeExecution, ID: "graycode/mission/m1"} + mission := graphcontracts.Ref{Kind: graphcontracts.NodeExecution, ID: "hawk/mission/m1"} export := executiongraph.Export{ SchemaVersion: executiongraph.SchemaVersion, GeneratedAt: now, Nodes: []graphcontracts.Node{{ ID: mission.ID, Kind: mission.Kind, CreatedAt: now, - Provenance: graphcontracts.Provenance{Producer: "graycode"}, + Provenance: graphcontracts.Provenance{Producer: "hawk"}, }}, Events: []graphcontracts.Event{{ - ID: "graycode/event/mission/m1/created", Type: graphcontracts.EventCreated, + ID: "hawk/event/mission/m1/created", Type: graphcontracts.EventCreated, Subject: mission, OccurredAt: now, - Provenance: graphcontracts.Provenance{Producer: "graycode"}, + Provenance: graphcontracts.Provenance{Producer: "hawk"}, }}, } dir := t.TempDir() @@ -76,7 +76,7 @@ func TestLoadMissionGraphExportValidatesTopology(t *testing.T) { t.Fatalf("loaded graph = %#v", loaded) } - export.Events[0].Subject.ID = "graycode/mission/missing" + export.Events[0].Subject.ID = "hawk/mission/missing" data, _ = json.Marshal(export) if err := os.WriteFile(filepath.Join(dir, "mission-graph.json"), data, 0o600); err != nil { t.Fatalf("rewrite graph: %v", err) @@ -89,11 +89,11 @@ func TestLoadMissionGraphExportValidatesTopology(t *testing.T) { func TestExecutionGraphRepositoryID(t *testing.T) { t.Parallel() - if got := executionGraphRepositoryID("custom", "/work/graycode"); got != "custom" { + if got := executionGraphRepositoryID("custom", "/work/hawk"); got != "custom" { t.Fatalf("override repository ID = %q, want custom", got) } - if got := executionGraphRepositoryID("", "/work/graycode"); got != "graycode" { - t.Fatalf("derived repository ID = %q, want graycode", got) + if got := executionGraphRepositoryID("", "/work/hawk"); got != "hawk" { + t.Fatalf("derived repository ID = %q, want hawk", got) } } @@ -111,11 +111,11 @@ func TestValidateSwiftCheckpointID(t *testing.T) { } func TestExecutionGraphExportCommand(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) saved := &session.Session{ ID: "graph-command-session", - CWD: "/workspace/graycode", + CWD: "/workspace/hawk", CreatedAt: time.Date(2026, time.July, 25, 4, 0, 0, 0, time.UTC), Messages: []session.Message{{ Role: "user", @@ -225,8 +225,8 @@ func TestExecutionGraphExportCommand(t *testing.T) { if export.SchemaVersion != executiongraph.SchemaVersion { t.Fatalf("SchemaVersion = %q, want %q", export.SchemaVersion, executiongraph.SchemaVersion) } - if export.Scope.RepositoryID != "graycode" { - t.Fatalf("Scope.RepositoryID = %q, want graycode", export.Scope.RepositoryID) + if export.Scope.RepositoryID != "hawk" { + t.Fatalf("Scope.RepositoryID = %q, want hawk", export.Scope.RepositoryID) } if output.String() == "" { t.Fatal("graph export command produced no output") @@ -240,10 +240,10 @@ func TestExecutionGraphExportCommand(t *testing.T) { t.Fatalf("graph export command leaked %q", secret) } } - if !hasExportNodePrefix(export, "graycode/policy/") { + if !hasExportNodePrefix(export, "hawk/policy/") { t.Fatal("graph export omitted automatic policy observation") } - if !hasExportNodePrefix(export, "graycode/verification/") { + if !hasExportNodePrefix(export, "hawk/verification/") { t.Fatal("graph export omitted automatic verification observation") } if !hasExportNodePrefix(export, "harrier/memory/") { @@ -258,12 +258,12 @@ func TestExecutionGraphExportCommand(t *testing.T) { } func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) now := time.Date(2026, time.July, 25, 6, 0, 0, 0, time.UTC) saved := &session.Session{ - ID: "graycode-correlated-session", - CWD: "/workspace/graycode", + ID: "hawk-correlated-session", + CWD: "/workspace/hawk", CreatedAt: now.Add(-time.Hour), UpdatedAt: now, } @@ -272,7 +272,7 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi } resolver := stubSwiftCorrelationResolver{correlation: swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - GraycodeSessionID: saved.ID, + HawkSessionID: saved.ID, CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{ { @@ -312,7 +312,7 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi assertExportEdge( t, export, - "graycode/session/"+saved.ID, + "hawk/session/"+saved.ID, "swift/session/swift-alpha", graphcontracts.EdgeReferences, ) @@ -326,12 +326,12 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi } func TestBuildExecutionGraphExportSwiftLookupFailureIsFailOpen(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) now := time.Date(2026, time.July, 25, 6, 30, 0, 0, time.UTC) saved := &session.Session{ - ID: "graycode-swift-fail-open", - CWD: "/workspace/graycode", + ID: "hawk-swift-fail-open", + CWD: "/workspace/hawk", CreatedAt: now.Add(-time.Hour), UpdatedAt: now, } diff --git a/cmd/features.go b/cmd/features.go index c3170023b..5a2b1ddc4 100644 --- a/cmd/features.go +++ b/cmd/features.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/feature" + "github.com/GrayCodeAI/hawk/internal/feature" "github.com/spf13/cobra" ) @@ -21,14 +21,14 @@ capabilities without code changes or restarts (some changes may require a daemon restart). Override a flag via environment variable: - GRAYCODE_FEATURE_=1 graycode daemon start + HAWK_FEATURE_=1 hawk daemon start Show a specific flag: - graycode features get `, + hawk features get `, RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 && args[0] == "get" { if len(args) < 2 { - return fmt.Errorf("usage: graycode features get ") + return fmt.Errorf("usage: hawk features get ") } f, ok := feature.Info(args[1]) if !ok { @@ -38,7 +38,7 @@ Show a specific flag: fmt.Printf("%s\n", auditTint("Default: ", textMuted)+auditTint(fmt.Sprintf("%v", f.DefaultValue()), textPrimary)) fmt.Printf("%s\n", auditTint("Current: ", textMuted)+auditTint(fmt.Sprintf("%v", feature.EnabledByName(args[1])), textPrimary)) fmt.Printf("%s\n", auditTint("Description: ", textMuted)+auditTint(f.Description(), textPrimary)) - envVar := "GRAYCODE_FEATURE_" + strings.ReplaceAll(strings.ToUpper(args[1]), "-", "_") + envVar := "HAWK_FEATURE_" + strings.ReplaceAll(strings.ToUpper(args[1]), "-", "_") fmt.Printf("%s\n", auditTint("Env var: ", textMuted)+auditTint(envVar, textPrimary)) return nil } @@ -50,7 +50,7 @@ Show a specific flag: } sort.Strings(names) - fmt.Println(auditTint("Feature Flags:", graycodeColor)) + fmt.Println(auditTint("Feature Flags:", hawkColor)) fmt.Println() for _, name := range names { f, _ := feature.Info(name) @@ -65,7 +65,7 @@ Show a specific flag: if f != nil { fmt.Printf("%s\n", auditTint(fmt.Sprintf(" default: %v", f.DefaultValue()), textMuted)) fmt.Printf("%s\n", auditTint(" description: "+f.Description(), textMuted)) - envVar := "GRAYCODE_FEATURE_" + strings.ReplaceAll(strings.ToUpper(name), "-", "_") + envVar := "HAWK_FEATURE_" + strings.ReplaceAll(strings.ToUpper(name), "-", "_") fmt.Printf("%s\n", auditTint(" env: "+envVar, textMuted)) } fmt.Println() diff --git a/cmd/feedback.go b/cmd/feedback.go index 3b61506ca..7d730128c 100644 --- a/cmd/feedback.go +++ b/cmd/feedback.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -37,17 +37,17 @@ type FeedbackReport struct { var feedbackCmd = &cobra.Command{ Use: "feedback [message]", - Short: "Submit feedback about graycode", - Long: `Capture feedback about your graycode experience. By default, opens a + Short: "Submit feedback about hawk", + Long: `Capture feedback about your hawk experience. By default, opens a GitHub issue template URL in your browser. Use --local to save feedback -to Graycode's user state directory for later submission. +to Hawk's user state directory for later submission. Categories: bug, feature, ux, performance, other Examples: - graycode feedback "The completion is slow" - graycode feedback --category bug "Crash when using /compact" - graycode feedback --local "Wish it could do X"`, + hawk feedback "The completion is slow" + hawk feedback --category bug "Crash when using /compact" + hawk feedback --local "Wish it could do X"`, Args: cobra.MinimumNArgs(0), RunE: runFeedback, } @@ -141,7 +141,7 @@ func openFeedbackIssue(report FeedbackReport) error { bodyBuilder.WriteString(fmt.Sprintf("- **Timestamp:** %s\n", report.Timestamp)) issueURL := fmt.Sprintf( - "https://github.com/GrayCodeAI/graycode-cli/issues/new?title=%s&body=%s&labels=%s", + "https://github.com/GrayCodeAI/hawk/issues/new?title=%s&body=%s&labels=%s", url.QueryEscape(title), url.QueryEscape(bodyBuilder.String()), url.QueryEscape(report.Category), diff --git a/cmd/fingerprint.go b/cmd/fingerprint.go index 664ecf43a..bfd4fb4c9 100644 --- a/cmd/fingerprint.go +++ b/cmd/fingerprint.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/GrayCodeAI/graycode-cli/internal/feature/fingerprint" + "github.com/GrayCodeAI/hawk/internal/feature/fingerprint" "github.com/spf13/cobra" ) @@ -19,10 +19,10 @@ including detected languages, dependency counts, CI presence, license, and git metadata. Examples: - graycode fingerprint - graycode fingerprint ./myproject - graycode fingerprint --format json . - graycode fingerprint --format markdown /path/to/repo`, + hawk fingerprint + hawk fingerprint ./myproject + hawk fingerprint --format json . + hawk fingerprint --format markdown /path/to/repo`, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { dir := "." diff --git a/cmd/footer_layout_clip_test.go b/cmd/footer_layout_clip_test.go index 781368750..eb03f451d 100644 --- a/cmd/footer_layout_clip_test.go +++ b/cmd/footer_layout_clip_test.go @@ -9,7 +9,7 @@ import ( func TestLayoutFooterRow_TokensSurviveFinishFooterLine(t *testing.T) { m := chatModel{width: 70, height: 24} - left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/OSS2026/RealWork/graycode-eco/graycode") + left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/OSS2026/RealWork/graycode-eco/hawk") left += " " + lipgloss.NewStyle().Foreground(statusBranchColor).Inline(true).Render("⎇ main") right := lipgloss.NewStyle().Foreground(statusTokenColor).Inline(true).Render("[db] 13k") right += lipgloss.NewStyle().Foreground(dimColor).Inline(true).Render(" · ") @@ -42,7 +42,7 @@ func TestLayoutFooterRow_LeftWiderThanFooterStillShowsTokens(t *testing.T) { func TestLayoutFooterRow_ClipDoesNotDropStyledTokens(t *testing.T) { m := chatModel{width: 55, height: 24} - left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/Desktop/OSS2026/RealWork/graycode-eco/graycode") + left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/Desktop/OSS2026/RealWork/graycode-eco/hawk") left += " " + lipgloss.NewStyle().Foreground(statusBranchColor).Inline(true).Render("⎇ feature/footer-fix") dim := lipgloss.NewStyle().Foreground(dimColor).Inline(true) tok := lipgloss.NewStyle().Foreground(statusTokenColor).Inline(true) diff --git a/cmd/footer_layout_width_test.go b/cmd/footer_layout_width_test.go index ac4444ef1..d85687001 100644 --- a/cmd/footer_layout_width_test.go +++ b/cmd/footer_layout_width_test.go @@ -13,7 +13,7 @@ func TestLayoutFooterRow_StyledStringsAlignRight(t *testing.T) { tokenStyle := lipgloss.NewStyle().Foreground(statusTokenColor).Inline(true) dim := lipgloss.NewStyle().Foreground(dimColor).Inline(true) - left := cwdStyle.Render("~/graycode") + " " + cwdStyle.Render("⎇ main") + left := cwdStyle.Render("~/hawk") + " " + cwdStyle.Render("⎇ main") right := tokenStyle.Render("[db] 13k") + dim.Render(" · ") + tokenStyle.Render("$0.00") width := 80 diff --git a/cmd/formatter.go b/cmd/formatter.go index e63587fba..d6d01263c 100644 --- a/cmd/formatter.go +++ b/cmd/formatter.go @@ -9,12 +9,12 @@ import ( "golang.org/x/term" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // stdoutIsTerminal reports whether stdout is connected to a terminal (TTY). // When stdout is a pipe or file — which is exactly the case when an agent or -// shell script captures graycode's output — this is false, and color/Unicode +// shell script captures hawk's output — this is false, and color/Unicode // chrome must be suppressed so the payload stays clean. It is a var so tests // can override it. var stdoutIsTerminal = func() bool { diff --git a/cmd/formatter_test.go b/cmd/formatter_test.go index 0fb7e5877..8c8aa1349 100644 --- a/cmd/formatter_test.go +++ b/cmd/formatter_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestDetectColorSupport_NonTTYStdout(t *testing.T) { diff --git a/cmd/golden_test.go b/cmd/golden_test.go index f15b72015..fec827c41 100644 --- a/cmd/golden_test.go +++ b/cmd/golden_test.go @@ -51,8 +51,8 @@ func TestGoldenHelp(t *testing.T) { t.Fatalf("golden file %s not found (run with -update-golden to create): %v", golden, err) } - if !strings.Contains(got, "graycode") { - t.Error("help output should contain 'graycode'") + if !strings.Contains(got, "hawk") { + t.Error("help output should contain 'hawk'") } if len(got) < 100 { t.Error("help output seems too short") diff --git a/cmd/governance_cmd.go b/cmd/governance_cmd.go index a51f33abc..089d3fcbb 100644 --- a/cmd/governance_cmd.go +++ b/cmd/governance_cmd.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/governance" + "github.com/GrayCodeAI/hawk/internal/governance" "github.com/spf13/cobra" ) @@ -20,10 +20,10 @@ var governanceCmd = &cobra.Command{ per-session PROFILE (tightest-wins). Tools are permitted only when both layers allow them. - graycode governance Show the managed policy status - graycode governance show Print the effective capability rows - graycode governance validate Validate a policy or profile document - graycode governance explain Evaluate a tool against the policy`, + hawk governance Show the managed policy status + hawk governance show Print the effective capability rows + hawk governance validate Validate a policy or profile document + hawk governance explain Evaluate a tool against the policy`, RunE: func(cmd *cobra.Command, args []string) error { return runGovernanceStatus(cmd) }, diff --git a/cmd/harness.go b/cmd/harness.go index 8f94e7a9a..734bfc1af 100644 --- a/cmd/harness.go +++ b/cmd/harness.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "github.com/GrayCodeAI/graycode-cli/internal/harness" + "github.com/GrayCodeAI/hawk/internal/harness" "github.com/spf13/cobra" ) @@ -84,7 +84,7 @@ Use --fix to automatically repair missing AGENTS.md, skills, or spec directories if fixErr != nil { return fmt.Errorf("harness auto-fix failed: %w", fixErr) } - fmt.Printf("%s\n", auditTint("[FIX] Graycode Harness Auto-Repair Results:", warnAmber)) + fmt.Printf("%s\n", auditTint("[FIX] Hawk Harness Auto-Repair Results:", warnAmber)) for _, repair := range fixResult.RepairsPerformed { fmt.Printf("%s\n", auditTint(" + "+repair, doneGreen)) } @@ -95,7 +95,7 @@ Use --fix to automatically repair missing AGENTS.md, skills, or spec directories outDir := harnessOutDir if outDir == "" { - outDir = filepath.Join(targetDir, ".graycode", "harness") + outDir = filepath.Join(targetDir, ".hawk", "harness") } if mkdirErr := os.MkdirAll(outDir, 0o750); mkdirErr != nil { @@ -132,11 +132,11 @@ Use --fix to automatically repair missing AGENTS.md, skills, or spec directories return fmt.Errorf("failed to write findings.json: %w", writeErr) } - // Journal quality observation to Graycode execution graph + // Journal quality observation to Hawk execution graph _ = harness.JournalHarnessReport(report, "") finish() - fmt.Printf("%s\n", auditTint("[GRAYCODE] Graycode Harness Evaluation Complete", graycodeColor)) + fmt.Printf("%s\n", auditTint("[HAWK] Hawk Harness Evaluation Complete", hawkColor)) fmt.Printf(" %s : %s (%s)\n", auditTint("Overall Score", textPrimary), auditTint(fmt.Sprintf("%d/100", report.OverallScore), textPrimary), @@ -151,7 +151,7 @@ Use --fix to automatically repair missing AGENTS.md, skills, or spec directories } func init() { - harnessCmd.Flags().StringVar(&harnessOutDir, "out-dir", "", "Directory to save harness reports (default: .graycode/harness)") + harnessCmd.Flags().StringVar(&harnessOutDir, "out-dir", "", "Directory to save harness reports (default: .hawk/harness)") harnessCmd.Flags().StringVar(&harnessFormat, "format", "all", "Report output format (html, markdown, json, all)") harnessCmd.Flags().BoolVar(&harnessFix, "fix", false, "Automatically repair missing harness assets (AGENTS.md, skills, specs)") } diff --git a/cmd/graycode/integration_test.go b/cmd/hawk/integration_test.go similarity index 98% rename from cmd/graycode/integration_test.go rename to cmd/hawk/integration_test.go index 0a5947558..cad9620ca 100644 --- a/cmd/graycode/integration_test.go +++ b/cmd/hawk/integration_test.go @@ -9,12 +9,12 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" - "github.com/GrayCodeAI/graycode-cli/internal/token" "github.com/GrayCodeAI/harrier/engine" "github.com/GrayCodeAI/harrier/graph" "github.com/GrayCodeAI/harrier/storage" + "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/token" kestrelLib "github.com/GrayCodeAI/kestrel" merlinLib "github.com/GrayCodeAI/merlin" "github.com/GrayCodeAI/shrike" diff --git a/cmd/graycode/main.go b/cmd/hawk/main.go similarity index 84% rename from cmd/graycode/main.go rename to cmd/hawk/main.go index 9e2883a69..3939bab8e 100644 --- a/cmd/graycode/main.go +++ b/cmd/hawk/main.go @@ -9,12 +9,12 @@ import ( "golang.org/x/term" - "github.com/GrayCodeAI/graycode-cli/cmd" - "github.com/GrayCodeAI/graycode-cli/internal/crash" - "github.com/GrayCodeAI/graycode-cli/internal/graycodeerr" - "github.com/GrayCodeAI/graycode-cli/internal/mcp" - "github.com/GrayCodeAI/graycode-cli/internal/observability/otellog" - "github.com/GrayCodeAI/graycode-cli/internal/observability/oteltrace" + "github.com/GrayCodeAI/hawk/cmd" + "github.com/GrayCodeAI/hawk/internal/crash" + "github.com/GrayCodeAI/hawk/internal/hawkerr" + "github.com/GrayCodeAI/hawk/internal/mcp" + "github.com/GrayCodeAI/hawk/internal/observability/otellog" + "github.com/GrayCodeAI/hawk/internal/observability/oteltrace" ) // Version, Commit, and BuildDate are set at build time via ldflags. @@ -43,12 +43,12 @@ func main() { // Handle --version flag immediately if len(os.Args) > 1 && os.Args[1] == "--version" { - fmt.Println("graycode " + Version) + fmt.Println("hawk " + Version) return } - // Initialize OpenTelemetry telemetry (opt-in via GRAYCODE_ENABLE_TELEMETRY=1). - // Telemetry failures are non-fatal: graycode continues with in-memory tracing only. + // Initialize OpenTelemetry telemetry (opt-in via HAWK_ENABLE_TELEMETRY=1). + // Telemetry failures are non-fatal: hawk continues with in-memory tracing only. telemetryProviders, telemetryErr := oteltrace.InitTelemetry(oteltrace.DefaultTelemetryConfig()) if telemetryErr != nil { fmt.Fprintln(os.Stderr, "warning: telemetry initialization failed:", telemetryErr) @@ -62,7 +62,7 @@ func main() { } // OTLP log-record export (DSH session-telemetry-otel port). Opt-in like - // tracing: GRAYCODE_ENABLE_TELEMETRY=1 plus an OTLP logs endpoint. The + // tracing: HAWK_ENABLE_TELEMETRY=1 plus an OTLP logs endpoint. The // sharing policy gates emission; failures are non-fatal. logBackend, logBackendErr := otellog.NewBackend(otellog.DefaultConfig()) if logBackendErr != nil { @@ -93,7 +93,7 @@ func main() { if errors.As(err, &exitErr) { os.Exit(exitErr.Code) } - os.Exit(graycodeerr.ClassifyExitCode(err)) + os.Exit(hawkerr.ClassifyExitCode(err)) } } diff --git a/cmd/help_template.go b/cmd/help_template.go index f51b81692..70b15191a 100644 --- a/cmd/help_template.go +++ b/cmd/help_template.go @@ -10,7 +10,7 @@ import ( // last column so coloring them (zero-width ANSI) cannot break alignment. All // color honors ShouldColor() (NO_COLOR, --quiet, non-TTY) via auditTint. func init() { - cobra.AddTemplateFunc("gcHeader", func(s string) string { return auditTint(s, graycodeColor) }) + cobra.AddTemplateFunc("gcHeader", func(s string) string { return auditTint(s, hawkColor) }) cobra.AddTemplateFunc("gcCmd", func(s string) string { return auditTint(s, textPrimary) }) cobra.AddTemplateFunc("gcDesc", func(s string) string { return auditTint(s, textMuted) }) rootCmd.SetUsageTemplate(modernUsageTemplate) diff --git a/cmd/history.go b/cmd/history.go index bfc70d5b6..506bdba3b 100644 --- a/cmd/history.go +++ b/cmd/history.go @@ -5,7 +5,7 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) const maxHistoryEntries = 1000 @@ -15,7 +15,7 @@ func historyFilePath() string { return filepath.Join(storage.StateDir(), "history") } -// loadInputHistory loads input history from Graycode's user state directory. +// loadInputHistory loads input history from Hawk's user state directory. // Returns an empty slice if the file does not exist. func loadInputHistory() []string { data, err := os.ReadFile(historyFilePath()) @@ -33,7 +33,7 @@ func loadInputHistory() []string { return entries } -// saveInputHistory writes the history list to Graycode's user state directory. +// saveInputHistory writes the history list to Hawk's user state directory. // Deduplicates entries (keeping the last occurrence) and caps at maxHistoryEntries. func saveInputHistory(history []string) { // Deduplicate: keep the last occurrence of each entry diff --git a/cmd/history_test.go b/cmd/history_test.go index 412e71c28..cda9aab7b 100644 --- a/cmd/history_test.go +++ b/cmd/history_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestLoadInputHistory_Empty(t *testing.T) { @@ -94,7 +94,7 @@ func TestAppendToHistory_EmptySkipped(t *testing.T) { func TestHistoryFilePath(t *testing.T) { stateDir := filepath.Join(t.TempDir(), "state") - t.Setenv("GRAYCODE_STATE_DIR", stateDir) + t.Setenv("HAWK_STATE_DIR", stateDir) expected := filepath.Join(stateDir, "history") if got := historyFilePath(); got != expected { t.Fatalf("got %q, want %q", got, expected) diff --git a/cmd/hud_panel.go b/cmd/hud_panel.go index 12e98f7ad..7c830ca21 100644 --- a/cmd/hud_panel.go +++ b/cmd/hud_panel.go @@ -6,7 +6,7 @@ import ( lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // HUDData is a snapshot of agent/mission/memory state rendered by the HUD panel. diff --git a/cmd/image.go b/cmd/image.go index b81e1f7e3..62876cddf 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -13,8 +13,8 @@ import ( "regexp" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/tui" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/tui" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // ImageAttachment represents an image ready to be attached to a message. @@ -188,7 +188,7 @@ func IsPDFFile(path string) bool { // ReadPDFText reads a PDF and extracts its text content using stdlib only. // -// graycode-router does not expose a native document/PDF content block, so PDFs are +// eyrie does not expose a native document/PDF content block, so PDFs are // degraded to text and injected inline. This is a best-effort extractor: it // inflates FlateDecode content streams and pulls text from PDF text-showing // operators (Tj / TJ). It does not handle every PDF (encrypted, scanned-image, diff --git a/cmd/input_indicator.go b/cmd/input_indicator.go index 0dee4c8ef..cb3c0ac8a 100644 --- a/cmd/input_indicator.go +++ b/cmd/input_indicator.go @@ -3,8 +3,8 @@ package cmd import ( lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // InputClass represents the classification of user input. diff --git a/cmd/input_indicator_test.go b/cmd/input_indicator_test.go index 7b1d92302..e39c519e0 100644 --- a/cmd/input_indicator_test.go +++ b/cmd/input_indicator_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" + "github.com/GrayCodeAI/hawk/internal/feature/shellmode" ) func TestInputIndicator_Classify(t *testing.T) { diff --git a/cmd/issue.go b/cmd/issue.go index 8e54fde20..583a65187 100644 --- a/cmd/issue.go +++ b/cmd/issue.go @@ -134,5 +134,5 @@ func generateIssueBody(ctx string) string { if ctx == "" { return "No additional context was provided." } - return "**Reported via graycode**\n\n```\n" + ctx + "\n```\n" + return "**Reported via hawk**\n\n```\n" + ctx + "\n```\n" } diff --git a/cmd/learn_cmd.go b/cmd/learn_cmd.go index c7575c8cb..417eed957 100644 --- a/cmd/learn_cmd.go +++ b/cmd/learn_cmd.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/spf13/cobra" ) @@ -24,13 +24,13 @@ var ( var learnCmd = &cobra.Command{ Use: "learn", Short: "Manage lessons learned across sessions", - Long: `Graycode persists lessons from failures (and manual entries) so future + Long: `Hawk persists lessons from failures (and manual entries) so future sessions avoid repeating them. Lessons are injected into the system prompt. - graycode learn List recent lessons - graycode learn add Add a lesson manually - graycode learn prompt Print the lesson-extraction prompt for a context - graycode learn clear Remove all lessons`, + hawk learn List recent lessons + hawk learn add Add a lesson manually + hawk learn prompt Print the lesson-extraction prompt for a context + hawk learn clear Remove all lessons`, RunE: func(cmd *cobra.Command, args []string) error { return runLearnList(cmd) }, @@ -104,7 +104,7 @@ func runLearnList(cmd *cobra.Command) error { si := engine.NewSelfImprover() lessons := si.Lessons("") if len(lessons) == 0 { - cmd.Println(auditTint("No lessons yet. Add one with: graycode learn add --what ... --lesson ...", textMuted)) + cmd.Println(auditTint("No lessons yet. Add one with: hawk learn add --what ... --lesson ...", textMuted)) return nil } diff --git a/cmd/main_test.go b/cmd/main_test.go index e27d9d968..1f6d9ee7b 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/catalogtest" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestMain(m *testing.M) { diff --git a/cmd/manpage.go b/cmd/manpage.go index 118168c58..2c65961d7 100644 --- a/cmd/manpage.go +++ b/cmd/manpage.go @@ -12,7 +12,7 @@ import ( var manpageCmd = &cobra.Command{ Use: "manpage", Short: "Generate man page in roff format", - Long: "Generate a man page for graycode in roff format and print it to stdout.\nRedirect to a file in your man path, e.g.: graycode manpage > /usr/local/share/man/man1/graycode.1", + Long: "Generate a man page for hawk in roff format and print it to stdout.\nRedirect to a file in your man path, e.g.: hawk manpage > /usr/local/share/man/man1/hawk.1", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if _, err := fmt.Fprint(cmd.OutOrStdout(), GenerateManPage()); err != nil { @@ -22,7 +22,7 @@ var manpageCmd = &cobra.Command{ }, } -// GenerateManPage produces a man page in roff format for graycode. +// GenerateManPage produces a man page in roff format for hawk. // The OPTIONS section is generated from the live Cobra flag set so it // never drifts from the actual CLI surface. func GenerateManPage() string { @@ -35,20 +35,20 @@ func GenerateManPage() string { var b strings.Builder // Header - b.WriteString(fmt.Sprintf(`.TH GRAYCODE 1 "%s" "%s" "User Commands"`, date, ver)) + b.WriteString(fmt.Sprintf(`.TH HAWK 1 "%s" "%s" "User Commands"`, date, ver)) b.WriteString("\n") // Name - b.WriteString(".SH NAME\ngraycode \\- AI coding agent powered by graycode-router\n") + b.WriteString(".SH NAME\nhawk \\- AI coding agent powered by eyrie\n") // Synopsis b.WriteString(".SH SYNOPSIS\n") - b.WriteString(".B graycode\n[\\fIOPTIONS\\fR] [\\fIPROMPT\\fR]\n") + b.WriteString(".B hawk\n[\\fIOPTIONS\\fR] [\\fIPROMPT\\fR]\n") // Description b.WriteString(".SH DESCRIPTION\n") - b.WriteString("graycode is an AI coding agent that reads, writes, and runs code in your terminal.\n") - fmt.Fprintf(&b, "It connects to %d first-class LLM providers through graycode-router, executes tools (file I/O,\n", registeredProviderCount()) + b.WriteString("hawk is an AI coding agent that reads, writes, and runs code in your terminal.\n") + fmt.Fprintf(&b, "It connects to %d first-class LLM providers through eyrie, executes tools (file I/O,\n", registeredProviderCount()) b.WriteString("shell, git, web search), and manages sessions from a keyboard-driven TUI\n") b.WriteString("or headless mode for scripts and CI.\n") @@ -81,7 +81,7 @@ func GenerateManPage() string { if sub.Hidden { continue } - b.WriteString(fmt.Sprintf(".TP\n\\fBgraycode %s\\fR\n%s\n", sub.Use, sub.Short)) + b.WriteString(fmt.Sprintf(".TP\n\\fBhawk %s\\fR\n%s\n", sub.Use, sub.Short)) } // Slash Commands @@ -99,7 +99,7 @@ func GenerateManPage() string { {"/review", "Code review"}, {"/doctor", "Run diagnostics"}, {"/tools", "List enabled tools"}, - {"/quit", "Exit graycode"}, + {"/quit", "Exit hawk"}, } for _, sc := range slashCmds { b.WriteString(fmt.Sprintf(".TP\n\\fB%s\\fR\n%s\n", sc.cmd, sc.desc)) @@ -107,17 +107,17 @@ func GenerateManPage() string { // Files b.WriteString(".SH FILES\n") - b.WriteString(".TP\n\\fBGraycode user config directory\\fR\nGlobal configuration files\n") + b.WriteString(".TP\n\\fBHawk user config directory\\fR\nGlobal configuration files\n") b.WriteString(".TP\n\\fBAGENTS.md\\fR\nProject instructions file\n") - b.WriteString(".TP\n\\fBGraycode user state directory\\fR\nSaved session data, plans, skills, and runtime state\n") + b.WriteString(".TP\n\\fBHawk user state directory\\fR\nSaved session data, plans, skills, and runtime state\n") // Credentials b.WriteString(".SH CREDENTIALS\n") b.WriteString("API keys are stored in the OS secret service (macOS Keychain or Linux GNOME Keyring / KWallet).\n") - b.WriteString("Use \\fBgraycode\\fR and \\fB/config\\fR to save keys; graycode does not read API keys from .env files.\n") - b.WriteString(".TP\n\\fBgraycode credentials status\\fR\nShow secure storage status\n") - b.WriteString(".TP\n\\fBgraycode credentials remove \\fR\nRemove a stored API key from the OS secret store\n") - b.WriteString(".TP\n\\fBgraycode credentials migrate\\fR\nImport legacy plaintext credential files into the OS store\n") + b.WriteString("Use \\fBhawk\\fR and \\fB/config\\fR to save keys; hawk does not read API keys from .env files.\n") + b.WriteString(".TP\n\\fBhawk credentials status\\fR\nShow secure storage status\n") + b.WriteString(".TP\n\\fBhawk credentials remove \\fR\nRemove a stored API key from the OS secret store\n") + b.WriteString(".TP\n\\fBhawk credentials migrate\\fR\nImport legacy plaintext credential files into the OS store\n") // Environment b.WriteString(".SH ENVIRONMENT\n") @@ -125,14 +125,14 @@ func GenerateManPage() string { envVars := []struct{ env, desc string }{ {"OPENAI_MODEL", "Override default OpenAI model"}, {"OLLAMA_BASE_URL", "Ollama server URL (also saved via /config for Ollama)"}, - {"GRAYCODE_CONFIG_DIR", "Override graycode config directory"}, + {"HAWK_CONFIG_DIR", "Override hawk config directory"}, } for _, ev := range envVars { b.WriteString(fmt.Sprintf(".TP\n\\fB%s\\fR\n%s\n", ev.env, ev.desc)) } // Authors - b.WriteString(".SH AUTHORS\nGrayCode AI \n") + b.WriteString(".SH AUTHORS\nGrayCode AI \n") return b.String() } diff --git a/cmd/manpage_test.go b/cmd/manpage_test.go index e8971a5d5..6bfa55321 100644 --- a/cmd/manpage_test.go +++ b/cmd/manpage_test.go @@ -26,7 +26,7 @@ func TestGenerateManPage(t *testing.T) { version = "1.0.0" page := GenerateManPage() - if !strings.Contains(page, ".TH GRAYCODE 1") { + if !strings.Contains(page, ".TH HAWK 1") { t.Fatal("missing .TH header") } if !strings.Contains(page, "1.0.0") { diff --git a/cmd/markdown.go b/cmd/markdown.go index fcbaab475..7707c54c2 100644 --- a/cmd/markdown.go +++ b/cmd/markdown.go @@ -25,12 +25,12 @@ import ( // Markdown rendering styles using the project's purpose-named palette. var ( - mdH1Style = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true).Underline(true) + mdH1Style = lipgloss.NewStyle().Foreground(hawkColor).Bold(true).Underline(true) mdH2Style = lipgloss.NewStyle().Foreground(successTeal).Bold(true) mdH3Style = lipgloss.NewStyle().Foreground(infoSky).Bold(true) mdH4Style = lipgloss.NewStyle().Foreground(costViolet).Bold(true) mdHeaderStyle = lipgloss.NewStyle().Foreground(textPrimary).Bold(true) - mdBoldStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + mdBoldStyle = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) mdItalicStyle = lipgloss.NewStyle().Italic(true) mdInlineCodeStyle = lipgloss.NewStyle().Foreground(infoSky) mdCodeBlockStyle = lipgloss.NewStyle().Background(bgCode) @@ -621,13 +621,13 @@ func protectInlineCode(text string, render func(string) string) (string, func(st if len(parts) < 2 { return m } - placeholder := fmt.Sprintf("\x00GRAYCODE_INLINE_CODE_%d\x00", len(replacements)) + placeholder := fmt.Sprintf("\x00HAWK_INLINE_CODE_%d\x00", len(replacements)) replacements = append(replacements, render(parts[1])) return placeholder }) restore := func(s string) string { for i, repl := range replacements { - s = strings.ReplaceAll(s, fmt.Sprintf("\x00GRAYCODE_INLINE_CODE_%d\x00", i), repl) + s = strings.ReplaceAll(s, fmt.Sprintf("\x00HAWK_INLINE_CODE_%d\x00", i), repl) } return s } diff --git a/cmd/markdown_renderer.go b/cmd/markdown_renderer.go index 8fce937f8..c206c5dbe 100644 --- a/cmd/markdown_renderer.go +++ b/cmd/markdown_renderer.go @@ -310,13 +310,13 @@ func protectRendererInlineCode(text string, render func(string) string) (string, if len(parts) < 2 { return m } - placeholder := fmt.Sprintf("\x00GRAYCODE_R_INLINE_CODE_%d\x00", len(replacements)) + placeholder := fmt.Sprintf("\x00HAWK_R_INLINE_CODE_%d\x00", len(replacements)) replacements = append(replacements, render(parts[1])) return placeholder }) restore := func(s string) string { for i, repl := range replacements { - s = strings.ReplaceAll(s, fmt.Sprintf("\x00GRAYCODE_R_INLINE_CODE_%d\x00", i), repl) + s = strings.ReplaceAll(s, fmt.Sprintf("\x00HAWK_R_INLINE_CODE_%d\x00", i), repl) } return s } diff --git a/cmd/markdown_test.go b/cmd/markdown_test.go index 53ebee745..5e25fe56b 100644 --- a/cmd/markdown_test.go +++ b/cmd/markdown_test.go @@ -210,10 +210,10 @@ func TestRenderMarkdownOrderedList(t *testing.T) { } func TestRenderMarkdownLinks(t *testing.T) { - input := "Visit [Graycode](https://example.com) for info" + input := "Visit [Hawk](https://example.com) for info" out := renderMarkdown(input, 80) plain := stripAnsi(out) - if !strings.Contains(plain, "Graycode") { + if !strings.Contains(plain, "Hawk") { t.Errorf("expected link text in output, got %q", plain) } if !strings.Contains(plain, "https://example.com") { @@ -831,7 +831,7 @@ func TestMarkdownRendererPlainText(t *testing.T) { func TestRenderTableFunction(t *testing.T) { rows := [][]string{ {"Name", "Language", "Stars"}, - {"graycode", "Go", "1200"}, + {"hawk", "Go", "1200"}, {"glow", "Go", "15000"}, {"bat", "Rust", "47000"}, } diff --git a/cmd/mcp_serve.go b/cmd/mcp_serve.go index 13379b85a..8997af389 100644 --- a/cmd/mcp_serve.go +++ b/cmd/mcp_serve.go @@ -6,8 +6,8 @@ import ( "os/signal" "syscall" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/mcp" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/mcp" "github.com/spf13/cobra" ) @@ -20,28 +20,28 @@ func init() { mcpCmd.AddCommand(mcpConfigCmd) } -// mcpServeCmd runs graycode itself as an MCP server over stdio, exposing graycode's +// mcpServeCmd runs hawk itself as an MCP server over stdio, exposing hawk's // capabilities (chat, search, memory, review, scan, compress) to MCP clients // such as Claude Desktop, Cursor, and Windsurf. var mcpServeCmd = &cobra.Command{ Use: "serve", - Short: "Run graycode as an MCP server over stdio", - Long: "Run graycode as a Model Context Protocol server over stdio (JSON-RPC 2.0), " + - "exposing graycode's tools to MCP clients like Claude Desktop, Cursor, and Windsurf.\n\n" + - "Use `graycode mcp config` to print the JSON block that registers this command in a client.", + Short: "Run hawk as an MCP server over stdio", + Long: "Run hawk as a Model Context Protocol server over stdio (JSON-RPC 2.0), " + + "exposing hawk's tools to MCP clients like Claude Desktop, Cursor, and Windsurf.\n\n" + + "Use `hawk mcp config` to print the JSON block that registers this command in a client.", RunE: runMCPServe, } func runMCPServe(cmd *cobra.Command, _ []string) error { - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() serverVersion := version if serverVersion == "" { serverVersion = "dev" } - server := mcp.NewMCPServer(mcp.ServerInfo{Name: "graycode", Version: serverVersion}) + server := mcp.NewMCPServer(mcp.ServerInfo{Name: "hawk", Version: serverVersion}) - // Wire graycode's tool registry in as the executor so delegating tools run for + // Wire hawk's tool registry in as the executor so delegating tools run for // real; a registry build failure degrades to not-configured rather than // aborting (the server still answers initialize/tools/list). registry, err := defaultRegistry(settings) @@ -56,24 +56,24 @@ func runMCPServe(cmd *cobra.Command, _ []string) error { return server.ServeStdio(ctx) } -// mcpConfigCmd emits the JSON block that registers graycode as an MCP server in a +// mcpConfigCmd emits the JSON block that registers hawk as an MCP server in a // client's config file, so users don't hand-edit JSON. var mcpConfigCmd = &cobra.Command{ Use: "config", - Short: "Print the MCP-server config block to register graycode in a client", - Long: "Print the JSON block that registers graycode as an MCP server (pointing at " + - "`graycode mcp serve`) for clients like Claude Desktop, Cursor, and Windsurf.\n\n" + + Short: "Print the MCP-server config block to register hawk in a client", + Long: "Print the JSON block that registers hawk as an MCP server (pointing at " + + "`hawk mcp serve`) for clients like Claude Desktop, Cursor, and Windsurf.\n\n" + "Pipe it to the client's config file, e.g.:\n" + - " graycode mcp config >> ~/Library/Application Support/Claude/claude_desktop_config.json", + " hawk mcp config >> ~/Library/Application Support/Claude/claude_desktop_config.json", RunE: runMCPConfig, } func runMCPConfig(cmd *cobra.Command, _ []string) error { - exe := graycodeExecutablePath() + exe := hawkExecutablePath() block := map[string]any{ "mcpServers": map[string]any{ - "graycode": map[string]any{ + "hawk": map[string]any{ "command": exe, "args": []string{"mcp", "serve"}, }, @@ -85,7 +85,7 @@ func runMCPConfig(cmd *cobra.Command, _ []string) error { } if mcpConfigWrite { - cmd.Println("# Add the \"graycode\" entry below into the \"mcpServers\" object of your client config:") + cmd.Println("# Add the \"hawk\" entry below into the \"mcpServers\" object of your client config:") cmd.Println("# Claude Desktop (macOS): ~/Library/Application Support/Claude/claude_desktop_config.json") cmd.Println("# Cursor: ~/.cursor/mcp.json") cmd.Println("# Windsurf: ~/.codeium/windsurf/mcp_config.json") @@ -95,12 +95,12 @@ func runMCPConfig(cmd *cobra.Command, _ []string) error { return nil } -// graycodeExecutablePath returns the absolute path to the running graycode binary, or -// the bare name "graycode" if it cannot be resolved (e.g. during `go run`), so the +// hawkExecutablePath returns the absolute path to the running hawk binary, or +// the bare name "hawk" if it cannot be resolved (e.g. during `go run`), so the // emitted config is still copy-pasteable. -func graycodeExecutablePath() string { +func hawkExecutablePath() string { if exe, err := os.Executable(); err == nil && exe != "" { return exe } - return "graycode" + return "hawk" } diff --git a/cmd/mcp_serve_test.go b/cmd/mcp_serve_test.go index ab256eaa4..8b37d46b2 100644 --- a/cmd/mcp_serve_test.go +++ b/cmd/mcp_serve_test.go @@ -30,15 +30,15 @@ func TestMCPConfigEmitsValidServerBlock(t *testing.T) { t.Fatalf("emitted config is not valid JSON: %v\n%s", err, buf.String()) } - graycode, ok := cfg.MCPServers["graycode"] + hawk, ok := cfg.MCPServers["hawk"] if !ok { - t.Fatal(`config missing "graycode" server entry`) + t.Fatal(`config missing "hawk" server entry`) } - if graycode.Command == "" { - t.Error("graycode server entry missing command") + if hawk.Command == "" { + t.Error("hawk server entry missing command") } - if len(graycode.Args) != 2 || graycode.Args[0] != "mcp" || graycode.Args[1] != "serve" { - t.Errorf(`args = %v, want ["mcp" "serve"]`, graycode.Args) + if len(hawk.Args) != 2 || hawk.Args[0] != "mcp" || hawk.Args[1] != "serve" { + t.Errorf(`args = %v, want ["mcp" "serve"]`, hawk.Args) } } diff --git a/cmd/mentions.go b/cmd/mentions.go index 329315ac9..a78aa9d34 100644 --- a/cmd/mentions.go +++ b/cmd/mentions.go @@ -6,8 +6,8 @@ import ( "strings" "unicode/utf8" - "github.com/GrayCodeAI/graycode-cli/internal/mention" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/mention" + "github.com/GrayCodeAI/hawk/internal/tool" ) // handleMentions processes @-prefixed file mentions in user input. diff --git a/cmd/mentions_test.go b/cmd/mentions_test.go index 8c320435a..63802f007 100644 --- a/cmd/mentions_test.go +++ b/cmd/mentions_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestHandleMentions_BlocksSensitivePaths(t *testing.T) { diff --git a/cmd/merlin_pipeline.go b/cmd/merlin_pipeline.go index 691cd0120..aa911ad90 100644 --- a/cmd/merlin_pipeline.go +++ b/cmd/merlin_pipeline.go @@ -5,10 +5,10 @@ import ( "fmt" "strings" - graycodeMerlin "github.com/GrayCodeAI/graycode-cli/internal/bridge/merlin" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - verifycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/verify" + hawkMerlin "github.com/GrayCodeAI/hawk/internal/bridge/merlin" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + verifycontracts "github.com/GrayCodeAI/hawk/internal/contracts/verify" merlinLib "github.com/GrayCodeAI/merlin" ) @@ -91,7 +91,7 @@ func RunMerlinPipeline(ctx context.Context, cfg MerlinPipelineConfig) ([]ReviewF opts = append(opts, merlinLib.WithConcurrency(cfg.Concurrency)) } - bridge := graycodeMerlin.NewBridge(opts...) + bridge := hawkMerlin.NewBridge(opts...) if !bridge.Ready() { return nil, "", fmt.Errorf("merlin bridge failed to initialize") } @@ -103,7 +103,7 @@ func RunMerlinPipeline(ctx context.Context, cfg MerlinPipelineConfig) ([]ReviewF if strings.TrimSpace(cfg.GraphSessionID) == "" { report, err = bridge.RunContracts(ctx, cfg.Target) } else { - report, err = bridge.RunContractsObserved(ctx, cfg.Target, graycodeMerlin.GraphObservation{ + report, err = bridge.RunContractsObserved(ctx, cfg.Target, hawkMerlin.GraphObservation{ SessionID: cfg.GraphSessionID, ToolCallID: cfg.GraphToolCallID, Stage: "merlin-pipeline", diff --git a/cmd/merlin_pipeline_test.go b/cmd/merlin_pipeline_test.go index 52e14e81e..efc34d23c 100644 --- a/cmd/merlin_pipeline_test.go +++ b/cmd/merlin_pipeline_test.go @@ -3,8 +3,8 @@ package cmd import ( "testing" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - verifycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/verify" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + verifycontracts "github.com/GrayCodeAI/hawk/internal/contracts/verify" ) func TestMerlinToReviewFindings_NilReport(t *testing.T) { diff --git a/cmd/migrate_secrets_test.go b/cmd/migrate_secrets_test.go index 4f4c099fd..4e1a50ccc 100644 --- a/cmd/migrate_secrets_test.go +++ b/cmd/migrate_secrets_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/observability/logger" ) func TestLogMigrateProviderSecretsError_Nil_NoOutput(t *testing.T) { @@ -45,8 +45,8 @@ func TestLogMigrateProviderSecretsError_IncludesRemediationHint(t *testing.T) { logMigrateProviderSecretsError(l, errors.New("boom")) out := buf.String() - if !strings.Contains(out, "graycode /config") { - t.Errorf("expected remediation hint mentioning `graycode /config`, got: %q", out) + if !strings.Contains(out, "hawk /config") { + t.Errorf("expected remediation hint mentioning `hawk /config`, got: %q", out) } if !strings.Contains(out, "keychain") { t.Errorf("expected remediation hint mentioning keychain, got: %q", out) diff --git a/cmd/mission.go b/cmd/mission.go index f66a24c09..06df34d78 100644 --- a/cmd/mission.go +++ b/cmd/mission.go @@ -9,12 +9,12 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - mission "github.com/GrayCodeAI/graycode-cli/internal/multiagent" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + mission "github.com/GrayCodeAI/hawk/internal/multiagent" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/spf13/cobra" ) @@ -35,10 +35,10 @@ Each feature runs in its own worktree with a full engine session. Results are committed on separate branches for review/merge. Examples: - graycode mission "Add auth, rate limiting, and logging to the API" - graycode mission --workers 6 "Refactor the database layer into 3 services" - graycode mission --model claude-sonnet-4-6 "Add tests for all untested packages" - graycode mission --from-tasks`, + hawk mission "Add auth, rate limiting, and logging to the API" + hawk mission --workers 6 "Refactor the database layer into 3 services" + hawk mission --model claude-sonnet-4-6 "Add tests for all untested packages" + hawk mission --from-tasks`, Args: cobra.ArbitraryArgs, RunE: runMission, } @@ -60,7 +60,7 @@ func runMission(_ *cobra.Command, args []string) error { cwd, _ := os.Getwd() baseBranch := getCurrentBranch(cwd) - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() effectiveModel, effectiveProvider := effectiveModelAndProvider(settings) if missionModel != "" { effectiveModel = missionModel @@ -78,7 +78,7 @@ func runMission(_ *cobra.Command, args []string) error { m := mission.New(prompt, cfg) // Clean up the mission's temp directory when the command finishes, - // whether it succeeded or failed. Without this, /tmp/graycode-missions/ + // whether it succeeded or failed. Without this, /tmp/hawk-missions/ // accumulates one directory per run indefinitely (C6 fix). defer func() { _ = m.Cleanup() }() @@ -174,7 +174,7 @@ func runMission(_ *cobra.Command, args []string) error { // Propagate feature failures as a non-zero exit so CI sees a real // failure instead of green: Mission.Run historically returned nil even - // when every feature failed (H9), so `graycode mission` exited 0. + // when every feature failed (H9), so `hawk mission` exited 0. failed := 0 for _, f := range m.Features { if f.Status == mission.FeatureFailed { @@ -188,7 +188,7 @@ func runMission(_ *cobra.Command, args []string) error { return nil } -func planWithLLM(ctx context.Context, prompt, provider, model string, settings graycodeconfig.Settings) ([]mission.Feature, error) { +func planWithLLM(ctx context.Context, prompt, provider, model string, settings hawkconfig.Settings) ([]mission.Feature, error) { planPrompt := fmt.Sprintf( "Decompose this task into independent features that can be implemented in parallel.\n\n"+ "Task: %s\n\n"+ @@ -201,7 +201,7 @@ func planWithLLM(ctx context.Context, prompt, provider, model string, settings g ) registry, _ := defaultRegistry(settings) - sess, err := newConfiguredGraycodeSession(settings, provider, model, planPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, err := newConfiguredHawkSession(settings, provider, model, planPrompt, registry, logger.New(io.Discard, logger.Error)) if err != nil { return nil, err } diff --git a/cmd/mission_graph.go b/cmd/mission_graph.go index 0946f8b83..3f221b8cd 100644 --- a/cmd/mission_graph.go +++ b/cmd/mission_graph.go @@ -6,8 +6,8 @@ import ( "strings" "time" - mission "github.com/GrayCodeAI/graycode-cli/internal/multiagent" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + mission "github.com/GrayCodeAI/hawk/internal/multiagent" + "github.com/GrayCodeAI/hawk/internal/tool" ) var missionFromTasks bool @@ -57,7 +57,7 @@ func missionFeaturesFromTasks(store *tool.TaskStore, missionID string) ([]missio ID: task.ID, Description: task.Subject, ExpectedBehavior: expected, - Branch: fmt.Sprintf("graycode-mission/%s/%s", missionID, task.ID), + Branch: fmt.Sprintf("hawk-mission/%s/%s", missionID, task.ID), Status: status, }) } diff --git a/cmd/mission_graph_test.go b/cmd/mission_graph_test.go index dec1d03c7..d9e204c9f 100644 --- a/cmd/mission_graph_test.go +++ b/cmd/mission_graph_test.go @@ -3,8 +3,8 @@ package cmd import ( "testing" - mission "github.com/GrayCodeAI/graycode-cli/internal/multiagent" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + mission "github.com/GrayCodeAI/hawk/internal/multiagent" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestMissionFeaturesFromTasks(t *testing.T) { @@ -26,7 +26,7 @@ func TestMissionFeaturesFromTasks(t *testing.T) { if len(waves) != 2 || len(waves[0]) != 1 || waves[0][0] != root.ID || waves[1][0] != child.ID { t.Fatalf("waves = %#v", waves) } - if features[0].Branch != "graycode-mission/mission123/"+root.ID { + if features[0].Branch != "hawk-mission/mission123/"+root.ID { t.Fatalf("branch = %q", features[0].Branch) } if features[0].Status != mission.FeaturePending { diff --git a/cmd/model_table.go b/cmd/model_table.go index e07ecf2f5..14f6884f2 100644 --- a/cmd/model_table.go +++ b/cmd/model_table.go @@ -6,8 +6,8 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/ui/icons" "github.com/mattn/go-runewidth" ) @@ -119,17 +119,17 @@ func modelTableRowFromOption(o configModelOption) modelTableRow { } func formatModelThinkingCell(o configModelOption) string { - supports := graycodeconfig.ModelCapabilitySupportsThinking(o.Capabilities) - settings := graycodeconfig.LoadSettings() - pref := graycodeconfig.ThinkingPrefForModel(settings, o.ID) + supports := hawkconfig.ModelCapabilitySupportsThinking(o.Capabilities) + settings := hawkconfig.LoadSettings() + pref := hawkconfig.ThinkingPrefForModel(settings, o.ID) if pref == nil && o.CanonicalID != "" && o.CanonicalID != o.ID { - pref = graycodeconfig.ThinkingPrefForModel(settings, o.CanonicalID) + pref = hawkconfig.ThinkingPrefForModel(settings, o.CanonicalID) } provider := strings.TrimSpace(o.GatewayID) if provider == "" { provider = strings.TrimSpace(o.ProviderID) } - return graycodeconfig.FormatModelThinkingLabel(supports, pref, provider) + return hawkconfig.FormatModelThinkingLabel(supports, pref, provider) } func formatModelCapabilities(capabilities []string) string { @@ -370,7 +370,7 @@ func modelTableFooter(total, scroll, end, allTotal int, muted lipgloss.Style) st return muted.Render(fmt.Sprintf("%s%s · t toggle thinking · enter to select", prefix, label)) } -func modelTableRowFromCatalogEntry(m graycodeconfig.EngineModel) modelTableRow { +func modelTableRowFromCatalogEntry(m hawkconfig.EngineModel) modelTableRow { name := strings.TrimSpace(m.DisplayName) if name == "" { name = m.ID @@ -401,7 +401,7 @@ func modelTableRowFromCatalogEntry(m graycodeconfig.EngineModel) modelTableRow { Model: name, Provider: owner, Caps: formatModelCapabilities(m.Capabilities), - Think: graycodeconfig.FormatModelThinkingLabel(graycodeconfig.ModelCapabilitySupportsThinking(m.Capabilities), graycodeconfig.ThinkingPrefForModel(graycodeconfig.LoadSettings(), m.ID), provider), + Think: hawkconfig.FormatModelThinkingLabel(hawkconfig.ModelCapabilitySupportsThinking(m.Capabilities), hawkconfig.ThinkingPrefForModel(hawkconfig.LoadSettings(), m.ID), provider), Price: price, Context: formatModelTableContext(m.ContextWindow), Free: free, diff --git a/cmd/model_table_test.go b/cmd/model_table_test.go index a7571588c..6e482be4e 100644 --- a/cmd/model_table_test.go +++ b/cmd/model_table_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestFormatModelTablePrice(t *testing.T) { @@ -67,7 +67,7 @@ func TestFormatModelCapabilities(t *testing.T) { } func TestFormatModelThinkingCell(t *testing.T) { - // Isolate from the developer's real graycode settings (per-model thinking + // Isolate from the developer's real hawk settings (per-model thinking // preferences would otherwise change the asserted defaults). t.Setenv("HOME", t.TempDir()) row := modelTableRowFromOption(configModelOption{ @@ -109,7 +109,7 @@ func TestModelTableOwnerFallback(t *testing.T) { if option.Provider != "deployment" { t.Fatalf("option provider = %q, want deployment", option.Provider) } - entry := modelTableRowFromCatalogEntry(graycodeconfig.EngineModel{GatewayID: "gateway"}) + entry := modelTableRowFromCatalogEntry(hawkconfig.EngineModel{GatewayID: "gateway"}) if entry.Provider != "gateway" { t.Fatalf("entry provider = %q, want gateway", entry.Provider) } diff --git a/cmd/models.go b/cmd/models.go index 57b4eeca0..3b4c1f1e0 100644 --- a/cmd/models.go +++ b/cmd/models.go @@ -7,7 +7,7 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" "github.com/spf13/cobra" ) @@ -19,18 +19,18 @@ var ( var modelsCmd = &cobra.Command{ Use: "models", - Short: "Deployment-aware model catalog (via graycode-router)", - Long: `Manage the graycode-router model catalog used by graycode for models, pricing, and deployment routing. + Short: "Deployment-aware model catalog (via eyrie)", + Long: `Manage the eyrie model catalog used by hawk for models, pricing, and deployment routing. -The catalog is stored at ~/.graycode-router/model_catalog.json (override with GRAYCODE_ROUTER_MODEL_CATALOG_PATH). -Graycode refreshes the catalog automatically on startup when the cache is missing, empty, or stale (disable with --no-auto-catalog-refresh or GRAYCODE_AUTO_REFRESH_CATALOG=0). -Use 'graycode models refresh' for a manual refresh or full discover report.`, +The catalog is stored at ~/.eyrie/model_catalog.json (override with EYRIE_MODEL_CATALOG_PATH). +Hawk refreshes the catalog automatically on startup when the cache is missing, empty, or stale (disable with --no-auto-catalog-refresh or HAWK_AUTO_REFRESH_CATALOG=0). +Use 'hawk models refresh' for a manual refresh or full discover report.`, } var modelsRefreshCmd = &cobra.Command{ Use: "refresh", Aliases: []string{"update"}, - Short: "Discover model catalog (graycode-router remote + live provider APIs) into ~/.graycode-router/model_catalog.json", + Short: "Discover model catalog (eyrie remote + live provider APIs) into ~/.eyrie/model_catalog.json", RunE: func(cmd *cobra.Command, _ []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -41,7 +41,7 @@ var modelsRefreshCmd = &cobra.Command{ prog := NewCLIProgress("Models", []string{"Discovering catalog"}) defer prog.Abort() prog.StartStep(0) - summary, err := graycodeconfig.RefreshModelCatalogV1WithSettings(ctx, settings) + summary, err := hawkconfig.RefreshModelCatalogV1WithSettings(ctx, settings) if err != nil { prog.FailStep(0, err.Error()) return err @@ -62,13 +62,13 @@ var modelsStatusCmd = &cobra.Command{ if err != nil { return err } - cmd.Println(graycodeconfig.FormatCatalogHealth(graycodeconfig.CatalogHealthReport(ctx))) + cmd.Println(hawkconfig.FormatCatalogHealth(hawkconfig.CatalogHealthReport(ctx))) cmd.Println() modelName, _ := effectiveModelAndProvider(settings) if len(args) > 0 { modelName = args[0] } - report, err := graycodeconfig.DeploymentStatusReportWithSettings(ctx, settings, modelName) + report, err := hawkconfig.DeploymentStatusReportWithSettings(ctx, settings, modelName) if err != nil { return err } @@ -87,7 +87,7 @@ var modelsRoutingPreviewCmd = &cobra.Command{ return err } modelName := args[0] - out, err := graycodeconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, modelName) + out, err := hawkconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, modelName) if err != nil { return err } @@ -98,7 +98,7 @@ var modelsRoutingPreviewCmd = &cobra.Command{ var modelsListCmd = &cobra.Command{ Use: "list [provider]", - Short: "List models from the graycode-router catalog cache (or live provider API)", + Short: "List models from the eyrie catalog cache (or live provider API)", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -109,7 +109,7 @@ var modelsListCmd = &cobra.Command{ providerName = args[0] } ctx := cmd.Context() - var models []graycodeconfig.EngineModel + var models []hawkconfig.EngineModel // Only the live provider fetch is slow enough to animate, and only when // the output is a human table (JSON/raw must stay pure). animate := modelsListLive && !modelsListJSON && !modelsListRaw @@ -121,11 +121,11 @@ var modelsListCmd = &cobra.Command{ } if modelsListLive { if providerName == "" { - return fmt.Errorf("provider required with --live (e.g. graycode models list canopywave --live --json)") + return fmt.Errorf("provider required with --live (e.g. hawk models list canopywave --live --json)") } - models, err = graycodeconfig.ListLiveEngineModelsWithSettings(ctx, settings, graycodeconfig.ActiveProviderID(providerName)) + models, err = hawkconfig.ListLiveEngineModelsWithSettings(ctx, settings, hawkconfig.ActiveProviderID(providerName)) } else { - models, err = graycodeconfig.FetchModelsForProviderWithSettings(ctx, settings, providerName) + models, err = hawkconfig.FetchModelsForProviderWithSettings(ctx, settings, providerName) } if err != nil { if prog != nil { @@ -159,9 +159,9 @@ var modelsListCmd = &cobra.Command{ }, } -// modelListJSONEntry is Graycode's versioned command-output contract. Keep this -// separate from GraycodeRouter's host-facing Model DTO so engine-only fields can evolve -// without breaking users that consume `graycode models list --json`. +// modelListJSONEntry is Hawk's versioned command-output contract. Keep this +// separate from Eyrie's host-facing Model DTO so engine-only fields can evolve +// without breaking users that consume `hawk models list --json`. type modelListJSONEntry struct { ID string `json:"id"` InputPricePer1M float64 `json:"input_price_per_1m"` @@ -175,7 +175,7 @@ type modelListJSONEntry struct { LiveMetadata json.RawMessage `json:"live_metadata,omitempty"` } -func modelListJSONEntryFromEngine(m graycodeconfig.EngineModel) modelListJSONEntry { +func modelListJSONEntryFromEngine(m hawkconfig.EngineModel) modelListJSONEntry { return modelListJSONEntry{ ID: m.ID, InputPricePer1M: m.InputPricePer1M, @@ -198,7 +198,7 @@ func validModelLiveMetadata(raw json.RawMessage) json.RawMessage { return append(json.RawMessage(nil), metadata...) } -func marshalModelListJSON(models []graycodeconfig.EngineModel, rawOnly, live bool) ([]byte, error) { +func marshalModelListJSON(models []hawkconfig.EngineModel, rawOnly, live bool) ([]byte, error) { entries := make([]modelListJSONEntry, len(models)) for i, model := range models { entries[i] = modelListJSONEntryFromEngine(model) diff --git a/cmd/models_test.go b/cmd/models_test.go index 6aeea06c8..3d8967567 100644 --- a/cmd/models_test.go +++ b/cmd/models_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestMarshalModelListJSONCompatibilityGolden(t *testing.T) { @@ -72,7 +72,7 @@ func TestMarshalModelListJSONCompatibilityGolden(t *testing.T) { } func TestModelListJSONEntryFromEnginePreservesLegacyFieldMapping(t *testing.T) { - model := graycodeconfig.EngineModel{ + model := hawkconfig.EngineModel{ ID: "vendor/model", DisplayName: "Model", Description: "Description", @@ -97,7 +97,7 @@ func TestModelListJSONEntryFromEnginePreservesLegacyFieldMapping(t *testing.T) { t.Fatalf("ServerTools = %v, want %v", entry.ServerTools, model.Capabilities) } - out, err := marshalModelListJSON([]graycodeconfig.EngineModel{model}, false, false) + out, err := marshalModelListJSON([]hawkconfig.EngineModel{model}, false, false) if err != nil { t.Fatalf("marshalModelListJSON() error = %v", err) } diff --git a/cmd/notifications.go b/cmd/notifications.go index e6a677ae8..95ebd81af 100644 --- a/cmd/notifications.go +++ b/cmd/notifications.go @@ -15,7 +15,7 @@ func notifyCompletion(duration time.Duration) { return } - msg := "Graycode query completed" + msg := "Hawk query completed" switch runtime.GOOS { case "darwin": @@ -23,10 +23,10 @@ func notifyCompletion(duration time.Duration) { _ = exec.CommandContext( context.Background(), "osascript", "-e", - `display notification "`+msg+`" with title "Graycode"`, + `display notification "`+msg+`" with title "Hawk"`, ).Start() case "linux": // Linux: use notify-send if available - _ = exec.CommandContext(context.Background(), "notify-send", "Graycode", msg).Start() + _ = exec.CommandContext(context.Background(), "notify-send", "Hawk", msg).Start() } } diff --git a/cmd/notify.go b/cmd/notify.go index 05fbec0cb..781140b83 100644 --- a/cmd/notify.go +++ b/cmd/notify.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // Notification represents a single notification event. @@ -23,7 +23,7 @@ type Notification struct { Read bool } -// Notifier manages terminal notifications for graycode events. +// Notifier manages terminal notifications for hawk events. type Notifier struct { Enabled bool Level string // "all", "important", "critical" @@ -151,7 +151,7 @@ $textNodes = $template.GetElementsByTagName('text') $textNodes.Item(0).AppendChild($template.CreateTextNode('%s')) | Out-Null $textNodes.Item(1).AppendChild($template.CreateTextNode('%s')) | Out-Null $toast = [Windows.UI.Notifications.ToastNotification]::new($template) -[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('graycode').Show($toast)`, +[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('hawk').Show($toast)`, escapePowerShell(title), escapePowerShell(message)) cmd := exec.CommandContext(context.Background(), "powershell", "-Command", script) // #nosec G204 -- fixed command 'powershell'; script built from escaped internal strings return cmd.Run() diff --git a/cmd/notify_test.go b/cmd/notify_test.go index cb1098a50..9a5f0a078 100644 --- a/cmd/notify_test.go +++ b/cmd/notify_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewNotifier(t *testing.T) { @@ -253,7 +253,7 @@ func TestSetTerminalTitle(t *testing.T) { n.Desktop = false // Just verify it doesn't panic - n.SetTerminalTitle("graycode: running") + n.SetTerminalTitle("hawk: running") n.ClearTitle() } diff --git a/cmd/ocg_live_test.go b/cmd/ocg_live_test.go index c0703febe..2ec04ee9e 100644 --- a/cmd/ocg_live_test.go +++ b/cmd/ocg_live_test.go @@ -4,7 +4,7 @@ // Live integration test for the OpenCodeGo provider adapter end-to-end. // Opt-in only — not run by default `go test ./...` or CI. Run with: // -// OPENCODEGO_API_KEY=... go test -tags=live_test -run TestLiveOpenCodeGoMiniMaxM3FullGraycodePath ./cmd +// OPENCODEGO_API_KEY=... go test -tags=live_test -run TestLiveOpenCodeGoMiniMaxM3FullHawkPath ./cmd // // Or via `make test-live` in this repo. package cmd @@ -15,15 +15,15 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" - "github.com/GrayCodeAI/graycode-router/credentials" - "github.com/GrayCodeAI/graycode-router/setup" + eyriecfg "github.com/GrayCodeAI/eyrie/config" + "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/eyrie/setup" + "github.com/GrayCodeAI/hawk/internal/observability/logger" ) -func TestLiveOpenCodeGoMiniMaxM3FullGraycodePath(t *testing.T) { +func TestLiveOpenCodeGoMiniMaxM3FullHawkPath(t *testing.T) { if credentials.LookupSecret(context.Background(), "OPENCODEGO_API_KEY") == "" { - t.Skip("OPENCODEGO_API_KEY not configured") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/29 + t.Skip("OPENCODEGO_API_KEY not configured") // TODO: https://github.com/GrayCodeAI/hawk/issues/29 } settings, err := loadEffectiveSettings() if err != nil { @@ -38,12 +38,12 @@ func TestLiveOpenCodeGoMiniMaxM3FullGraycodePath(t *testing.T) { if err != nil { t.Fatal(err) } - t.Logf("provider=%s model=%s tools=%d system_len=%d", effectiveProvider, effectiveModel, len(registry.GraycodeRouterTools()), len(systemPrompt)) + t.Logf("provider=%s model=%s tools=%d system_len=%d", effectiveProvider, effectiveModel, len(registry.EyrieTools()), len(systemPrompt)) - adapter := setup.ConfiguredDeploymentAdapters(graycoderoutercfg.LoadProviderConfig(""))["opencodego"] + adapter := setup.ConfiguredDeploymentAdapters(eyriecfg.LoadProviderConfig(""))["opencodego"] t.Logf("adapter_type=%T", adapter.Provider) - sess := newGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) + sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) sess.SetLogger(logger.New(ioDiscard{}, logger.Info)) if cfgErr := configureSession(sess, settings); cfgErr != nil { t.Fatal(cfgErr) diff --git a/cmd/options.go b/cmd/options.go index aafc2bf8b..7d0163b0d 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -9,20 +9,20 @@ import ( "os" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - ctxrepomap "github.com/GrayCodeAI/graycode-cli/internal/context/repomap" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/engine/branching" - "github.com/GrayCodeAI/graycode-cli/internal/engine/lifecycle" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/prompt" - "github.com/GrayCodeAI/graycode-cli/internal/prompts" - graycodemodel "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/snapshot" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + ctxrepomap "github.com/GrayCodeAI/hawk/internal/context/repomap" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine/branching" + "github.com/GrayCodeAI/hawk/internal/engine/lifecycle" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/prompt" + "github.com/GrayCodeAI/hawk/internal/prompts" + hawkmodel "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/snapshot" + "github.com/GrayCodeAI/hawk/internal/tool" ) func buildSystemPrompt() (string, error) { @@ -70,7 +70,7 @@ func buildSystemPromptWithOptions(includeWorkspaceContext, includeRepoMap bool) modularPrompt = "" } - base := prompt.System() + "\n\n" + graycodeconfig.BuildStartupContextWithDirs(addDirs) + base := prompt.System() + "\n\n" + hawkconfig.BuildStartupContextWithDirs(addDirs) if modularPrompt != "" { base += "\n\n" + modularPrompt } @@ -120,7 +120,7 @@ func buildDeferredWorkspacePromptContext() string { return "" } var sections []string - if deferred := strings.TrimSpace(graycodeconfig.BuildDeferredContextWithDirs(addDirs)); deferred != "" { + if deferred := strings.TrimSpace(hawkconfig.BuildDeferredContextWithDirs(addDirs)); deferred != "" { sections = append(sections, deferred) } if ws := prompts.GatherWorkspaceContext(cwd); ws != nil { @@ -157,7 +157,7 @@ func injectRepoMap(base string) string { return base } - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() if settings.RepoMap == nil || !*settings.RepoMap { return base } @@ -183,43 +183,43 @@ func injectRepoMap(base string) string { return base + "\n\n# Repository Map\n" + formatted } -func loadEffectiveSettings() (graycodeconfig.Settings, error) { - return graycodeconfig.LoadSettingsWithOverride(settingsFlag) +func loadEffectiveSettings() (hawkconfig.Settings, error) { + return hawkconfig.LoadSettingsWithOverride(settingsFlag) } -func resolveSelection(settings graycodeconfig.Settings) graycodeconfig.Selection { - return graycodeconfig.EffectiveSelectionWithSettings(context.Background(), settings, graycodeconfig.SelectionOptions{ +func resolveSelection(settings hawkconfig.Settings) hawkconfig.Selection { + return hawkconfig.EffectiveSelectionWithSettings(context.Background(), settings, hawkconfig.SelectionOptions{ ProviderOverride: firstNonEmptyTrimmed(provider, settings.Provider), ModelOverride: firstNonEmptyTrimmed(model, settings.Model), }) } -func startupSelection(settings graycodeconfig.Settings) graycodeconfig.Selection { +func startupSelection(settings hawkconfig.Settings) hawkconfig.Selection { providerOverride := firstNonEmptyTrimmed(provider, settings.Provider) modelOverride := firstNonEmptyTrimmed(model, settings.Model) explicitProvider, explicitModel := explicitSelection(context.Background()) - providerID := graycodeconfig.ActiveProviderID(firstNonEmptyTrimmed(providerOverride, explicitProvider)) + providerID := hawkconfig.ActiveProviderID(firstNonEmptyTrimmed(providerOverride, explicitProvider)) modelID := strings.TrimSpace(firstNonEmptyTrimmed(modelOverride, explicitModel)) if providerID == "" && modelID != "" { - providerID = graycodeconfig.ActiveProviderID(graycodeconfig.ProviderOfModelWithSettings(settings, modelID)) + providerID = hawkconfig.ActiveProviderID(hawkconfig.ProviderOfModelWithSettings(settings, modelID)) } if modelID == "" && providerID != "" { - modelID = strings.TrimSpace(graycodeconfig.DefaultModelForProviderWithSettings(settings, providerID)) + modelID = strings.TrimSpace(hawkconfig.DefaultModelForProviderWithSettings(settings, providerID)) } if providerID == "" { providerID = startupPlaceholderProvider } - return graycodeconfig.Selection{ + return hawkconfig.Selection{ Provider: providerID, Model: modelID, } } -func effectiveModelAndProvider(settings graycodeconfig.Settings) (string, string) { +func effectiveModelAndProvider(settings hawkconfig.Settings) (string, string) { selection := resolveSelection(settings) if !selection.HasConfiguredDeployment { return "", "" @@ -227,7 +227,7 @@ func effectiveModelAndProvider(settings graycodeconfig.Settings) (string, string return selection.Model, selection.Provider } -func newStartupGraycodeSession(selection graycodeconfig.Selection, systemPrompt string, registry *tool.Registry) *engine.Session { +func newStartupHawkSession(selection hawkconfig.Selection, systemPrompt string, registry *tool.Registry) *engine.Session { providerID := strings.TrimSpace(selection.Provider) if providerID == "" { providerID = startupPlaceholderProvider @@ -235,7 +235,7 @@ func newStartupGraycodeSession(selection graycodeconfig.Selection, systemPrompt return engine.NewSession(providerID, strings.TrimSpace(selection.Model), systemPrompt, registry) } -func newGraycodeSession(settings graycodeconfig.Settings, effectiveProvider, effectiveModel, systemPrompt string, registry *tool.Registry) *engine.Session { +func newHawkSession(settings hawkconfig.Settings, effectiveProvider, effectiveModel, systemPrompt string, registry *tool.Registry) *engine.Session { selection := resolveSelection(settings) if strings.TrimSpace(selection.Provider) == "" { selection.Provider = effectiveProvider @@ -243,18 +243,18 @@ func newGraycodeSession(settings graycodeconfig.Settings, effectiveProvider, eff if strings.TrimSpace(selection.Model) == "" { selection.Model = effectiveModel } - sess := engine.NewGraycodeSessionForSettings(context.Background(), settings, selection, selection.Provider, selection.Model, systemPrompt, registry) - // Graycode requires Docker. Any entry point that has not attached a running + sess := engine.NewHawkSessionForSettings(context.Background(), settings, selection, selection.Provider, selection.Model, systemPrompt, registry) + // Hawk requires Docker. Any entry point that has not attached a running // container remains fail-closed at the engine tool boundary. sess.SetContainerRequired(true) return sess } -// newConfiguredGraycodeSession is the non-interactive command composition root. +// newConfiguredHawkSession is the non-interactive command composition root. // Interactive chat intentionally keeps its lightweight startup and deferred // heavy configuration split; batch/daemon/ACP callers use this atomic path. -func newConfiguredGraycodeSession(settings graycodeconfig.Settings, effectiveProvider, effectiveModel, systemPrompt string, registry *tool.Registry, sessionLogger *logger.Logger, maxTurnsOverride ...int) (*engine.Session, error) { - sess := newGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) +func newConfiguredHawkSession(settings hawkconfig.Settings, effectiveProvider, effectiveModel, systemPrompt string, registry *tool.Registry, sessionLogger *logger.Logger, maxTurnsOverride ...int) (*engine.Session, error) { + sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) if sessionLogger != nil { sess.SetLogger(sessionLogger) } @@ -264,11 +264,11 @@ func newConfiguredGraycodeSession(settings graycodeconfig.Settings, effectivePro return sess, nil } -// newConfiguredGraycodeSessionFactory is the shared composition seam for +// newConfiguredHawkSessionFactory is the shared composition seam for // non-interactive protocol/server entry points. It owns registry creation and // settings-based model selection while allowing each protocol to provide its // own prompt and optional model override. -func newConfiguredGraycodeSessionFactory(settings graycodeconfig.Settings, sessionLogger *logger.Logger) func(string, string, ...int) (*engine.Session, error) { +func newConfiguredHawkSessionFactory(settings hawkconfig.Settings, sessionLogger *logger.Logger) func(string, string, ...int) (*engine.Session, error) { return func(systemPrompt, modelOverride string, maxTurnsOverride ...int) (*engine.Session, error) { registry, err := defaultRegistry(settings) if err != nil { @@ -278,14 +278,14 @@ func newConfiguredGraycodeSessionFactory(settings graycodeconfig.Settings, sessi if strings.TrimSpace(modelOverride) != "" { effectiveModel = modelOverride } - return newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, sessionLogger, maxTurnsOverride...) + return newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, sessionLogger, maxTurnsOverride...) } } // prepareInteractiveSessionStartup applies only the cheap TUI startup slice. // Transport rebuild and heavy memory setup remain deferred until the first // real chat request in bootstrapSessionForChat. -func prepareInteractiveSessionStartup(sess *engine.Session, settings graycodeconfig.Settings) error { +func prepareInteractiveSessionStartup(sess *engine.Session, settings hawkconfig.Settings) error { syncSessionFromPersistedSelection(sess) sess.SetLogger(logger.New(io.Discard, logger.Error)) return configureSessionStartup(sess, settings) @@ -300,7 +300,7 @@ func firstNonEmptyTrimmed(values ...string) string { return "" } -func configureSession(sess *engine.Session, settings graycodeconfig.Settings, maxTurnsOverride ...int) error { +func configureSession(sess *engine.Session, settings hawkconfig.Settings, maxTurnsOverride ...int) error { if err := configureSessionStartup(sess, settings, maxTurnsOverride...); err != nil { return err } @@ -308,7 +308,7 @@ func configureSession(sess *engine.Session, settings graycodeconfig.Settings, ma return nil } -func configureSessionStartup(sess *engine.Session, settings graycodeconfig.Settings, maxTurnsOverride ...int) error { +func configureSessionStartup(sess *engine.Session, settings hawkconfig.Settings, maxTurnsOverride ...int) error { sess.WireAgentTool() sess.SetAllowedDirs(addDirs) // Unified isolation profile (OS sandbox + optional container-required). @@ -372,7 +372,7 @@ func configureSessionStartup(sess *engine.Session, settings graycodeconfig.Setti } // Model cascade router: automatically routes tasks to optimal model tier - roles := graycodemodel.DefaultRoles(sess.Model()) + roles := hawkmodel.DefaultRoles(sess.Model()) if settings.ModelRoles != nil { roles = *settings.ModelRoles } @@ -420,7 +420,7 @@ func configureSessionStartup(sess *engine.Session, settings graycodeconfig.Setti // provider-specific defaults (e.g. LongCat off). modelID := strings.TrimSpace(sess.Model()) providerID := strings.TrimSpace(sess.Provider()) - sess.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(settings, modelID, providerID)) + sess.SetThinkingEnabled(hawkconfig.ResolveThinkingForModel(settings, modelID, providerID)) return nil } diff --git a/cmd/options_test.go b/cmd/options_test.go index 33149f038..1ff3f35cf 100644 --- a/cmd/options_test.go +++ b/cmd/options_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestValidateRootFlagsRejectsInvalidSandbox(t *testing.T) { diff --git a/cmd/options_welcome_test.go b/cmd/options_welcome_test.go index 4db87cc36..b003a1d84 100644 --- a/cmd/options_welcome_test.go +++ b/cmd/options_welcome_test.go @@ -7,65 +7,65 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func isolateCredentialHome(t *testing.T) { t.Helper() home := t.TempDir() - graycodeDir := filepath.Join(home, ".graycode") - _ = os.MkdirAll(graycodeDir, 0o700) + hawkDir := filepath.Join(home, ".hawk") + _ = os.MkdirAll(hawkDir, 0o700) t.Setenv("HOME", home) - t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) + t.Setenv("HAWK_CONFIG_DIR", hawkDir) + t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) } func TestEffectiveModelAndProvider_ClearsWithoutCredentials(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() - if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - model, provider := effectiveModelAndProvider(graycodeconfig.Settings{}) + model, provider := effectiveModelAndProvider(hawkconfig.Settings{}) if model != "" || provider != "" { t.Fatalf("expected empty selection without credentials, got model=%q provider=%q", model, provider) } } func TestEffectiveModelAndProvider_KeepsWithCredentials(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - graycodeconfig.InvalidateConfigUICache() - if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + hawkconfig.InvalidateConfigUICache() + if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - model, provider := effectiveModelAndProvider(graycodeconfig.Settings{}) + model, provider := effectiveModelAndProvider(hawkconfig.Settings{}) if provider == "" { t.Fatalf("expected provider with credentials, got model=%q provider=%q", model, provider) } diff --git a/cmd/pager.go b/cmd/pager.go index 0df35572b..a923979a6 100644 --- a/cmd/pager.go +++ b/cmd/pager.go @@ -95,7 +95,7 @@ func StopPager() { // Returns "" if paging should be disabled. func resolvePager() string { // Disable paging via environment. - if v := os.Getenv("GRAYCODE_PAGER"); v != "" { + if v := os.Getenv("HAWK_PAGER"); v != "" { if v == "cat" || v == "none" { return "" } diff --git a/cmd/path.go b/cmd/path.go index 608f569d5..cbc78831d 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" "github.com/spf13/cobra" ) @@ -17,16 +17,16 @@ var ( var pathCmd = &cobra.Command{ Use: "path", Short: "Developer path readiness (setup, security, sandbox, ecosystem)", - Long: `Check whether graycode is configured on the developer path: + Long: `Check whether hawk is configured on the developer path: API keys in OS secret store, model selected, no secrets on disk, -mandatory Docker isolation, and graycode-router/harrier/shrike integration. +mandatory Docker isolation, and eyrie/harrier/shrike integration. Built for individual developers first — teams and enterprise later. See docs/DEVELOPER-PATH.md and docs/SECURITY-DEVELOPER.md.`, RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() - report := graycodeconfig.EvaluateDeveloperPath(ctx) + report := hawkconfig.EvaluateDeveloperPath(ctx) if pathJSON { enc := json.NewEncoder(cmd.OutOrStdout()) @@ -34,11 +34,11 @@ See docs/DEVELOPER-PATH.md and docs/SECURITY-DEVELOPER.md.`, return enc.Encode(report) } - cmd.Println(graycodeconfig.FormatDeveloperPathReport(ctx)) + cmd.Println(hawkconfig.FormatDeveloperPathReport(ctx)) if pathStrict { for _, c := range report.Checks { - if c.Section == "Sandbox" && c.Name == "docker" && c.Status == graycodeconfig.PathWarn { + if c.Section == "Sandbox" && c.Name == "docker" && c.Status == hawkconfig.PathWarn { return fmt.Errorf("strict mode: start Docker for isolated Bash") } } diff --git a/cmd/path_test.go b/cmd/path_test.go index f56299c70..ae0d3dc8a 100644 --- a/cmd/path_test.go +++ b/cmd/path_test.go @@ -5,20 +5,20 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestPathCmdRuns(t *testing.T) { useInMemoryCredentials(t) if err := pathCmd.RunE(pathCmd, nil); err == nil { - t.Skip("machine has full developer path setup") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/30 + t.Skip("machine has full developer path setup") // TODO: https://github.com/GrayCodeAI/hawk/issues/30 } } func TestPathCmdPrintsReport(t *testing.T) { useInMemoryCredentials(t) - out := graycodeconfig.FormatDeveloperPathReport(context.Background()) + out := hawkconfig.FormatDeveloperPathReport(context.Background()) if !strings.Contains(out, "Developer path") { t.Fatalf("unexpected output: %s", out) } diff --git a/cmd/permissions.go b/cmd/permissions.go index 26a733e5e..505ebfce3 100644 --- a/cmd/permissions.go +++ b/cmd/permissions.go @@ -7,8 +7,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/permissions" - "github.com/GrayCodeAI/graycode-cli/internal/permissions/stableid" + "github.com/GrayCodeAI/hawk/internal/permissions" + "github.com/GrayCodeAI/hawk/internal/permissions/stableid" "github.com/spf13/cobra" ) diff --git a/cmd/permissions_center.go b/cmd/permissions_center.go index 46c9fa011..fab0e5d38 100644 --- a/cmd/permissions_center.go +++ b/cmd/permissions_center.go @@ -7,9 +7,9 @@ import ( "time" tea "charm.land/bubbletea/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/sandbox" ) const defaultPermissionSandbox = "workspace" @@ -87,7 +87,7 @@ func normalizePermissionSandbox(raw string) (string, string, bool) { } } -func effectivePermissionSandbox(settings graycodeconfig.Settings) string { +func effectivePermissionSandbox(settings hawkconfig.Settings) string { if normalized, _, ok := normalizePermissionSandbox(sandboxFlag); ok && strings.TrimSpace(sandboxFlag) != "" { return normalized } @@ -297,7 +297,7 @@ func permissionRulesSummary(m *chatModel) string { return strings.TrimRight(b.String(), "\n") } -func effectiveAllowRules(settings graycodeconfig.Settings) []string { +func effectiveAllowRules(settings hawkconfig.Settings) []string { var rules []string rules = append(rules, settings.AutoAllow...) rules = append(rules, settings.AllowedTools...) @@ -305,7 +305,7 @@ func effectiveAllowRules(settings graycodeconfig.Settings) []string { return dedupeStrings(rules) } -func effectiveDenyRules(settings graycodeconfig.Settings) []string { +func effectiveDenyRules(settings hawkconfig.Settings) []string { rules := append([]string{}, settings.DisallowedTools...) rules = append(rules, parseToolListFromCLI(disallowedToolsFlag)...) return dedupeStrings(rules) @@ -325,7 +325,7 @@ func dedupeStrings(values []string) []string { return out } -func rebuildSessionPermissionRules(sess *engine.Session, settings graycodeconfig.Settings) { +func rebuildSessionPermissionRules(sess *engine.Session, settings hawkconfig.Settings) { if sess == nil { return } @@ -356,7 +356,7 @@ func rebuildSessionPermissionRules(sess *engine.Session, settings graycodeconfig } } -func savePermissionSettings(scope string, settings graycodeconfig.Settings, level engine.AutonomyLevel) (string, error) { +func savePermissionSettings(scope string, settings hawkconfig.Settings, level engine.AutonomyLevel) (string, error) { scope = strings.ToLower(strings.TrimSpace(scope)) if scope == "" { scope = "global" @@ -371,14 +371,14 @@ func savePermissionSettings(scope string, settings graycodeconfig.Settings, leve case "project": return "", fmt.Errorf("project-local settings writes are disabled; use scope \"global\" or an explicit --settings file") case "global": - target := graycodeconfig.LoadGlobalSettings() + target := hawkconfig.LoadGlobalSettings() target.AutoAllow = append([]string{}, settings.AutoAllow...) target.AllowedTools = append([]string{}, settings.AllowedTools...) target.DisallowedTools = append([]string{}, settings.DisallowedTools...) target.Autonomy = settings.Autonomy target.AutonomyExplicit = true target.Sandbox = settings.Sandbox - if err := graycodeconfig.SaveGlobal(target); err != nil { + if err := hawkconfig.SaveGlobal(target); err != nil { return "", err } return "user settings", nil diff --git a/cmd/permissions_center_test.go b/cmd/permissions_center_test.go index df68abaf6..07d1f40c5 100644 --- a/cmd/permissions_center_test.go +++ b/cmd/permissions_center_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestNormalizePermissionTier(t *testing.T) { @@ -34,7 +34,7 @@ func TestNormalizePermissionSandbox(t *testing.T) { } func TestEffectivePermissionRules(t *testing.T) { - settings := graycodeconfig.Settings{ + settings := hawkconfig.Settings{ AutoAllow: []string{"Read"}, AllowedTools: []string{"Bash(git:*)", "Read"}, DisallowedTools: []string{"Bash(rm -rf *)"}, @@ -55,7 +55,7 @@ func TestAutonomyCenterSummary(t *testing.T) { sess.PermSvc().SetSpecStage(engine.SpecStageSpecify) model := &chatModel{ session: sess, - settings: graycodeconfig.Settings{ + settings: hawkconfig.Settings{ Sandbox: "workspace", AllowedTools: []string{"Bash(git:*)"}, DisallowedTools: []string{"Bash(rm -rf *)"}, @@ -138,7 +138,7 @@ func TestResetPermissionCenter(t *testing.T) { sess.PermSvc().SetDryRun(true) model := &chatModel{ session: sess, - settings: graycodeconfig.Settings{ + settings: hawkconfig.Settings{ Autonomy: permissionTierSettingValue(engine.AutonomyYOLO), Sandbox: "strict", AutoAllow: []string{"Read"}, diff --git a/cmd/plan.go b/cmd/plan.go index 579851cb1..8390e08da 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/planner" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/intelligence/planner" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) @@ -19,7 +19,7 @@ var planCmd = &cobra.Command{ Use: "plan", Short: "Create and manage structured development plans", Long: `plan helps you create, list, and track structured development plans. -Plans are stored in Graycode's user state directory, partitioned by project. +Plans are stored in Hawk's user state directory, partitioned by project. Subcommands: create Create a new plan (generates a plan prompt) @@ -38,13 +38,13 @@ var planCreateCmd = &cobra.Command{ // Generate the plan prompt (would normally be sent to an LLM). prompt := planner.Generate(description, "") cmd.Println(auditTint("Plan prompt generated. Send this to an LLM to produce a plan:", textMuted)) - cmd.Println(auditTint("--- System ---", graycodeColor)) + cmd.Println(auditTint("--- System ---", hawkColor)) cmd.Println(prompt.System) cmd.Println() - cmd.Println(auditTint("--- User ---", graycodeColor)) + cmd.Println(auditTint("--- User ---", hawkColor)) cmd.Println(prompt.User) cmd.Println() - cmd.Println(auditTint("Once you have the LLM response, save it with an explicit output path or import it into Graycode plans.", textMuted)) + cmd.Println(auditTint("Once you have the LLM response, save it with an explicit output path or import it into Hawk plans.", textMuted)) return nil }, } @@ -60,7 +60,7 @@ var planListCmd = &cobra.Command{ if planJSON { fmt.Println("[]") } else { - cmd.Println(auditTint("No plans found. Create one with: graycode plan create ", textMuted)) + cmd.Println(auditTint("No plans found. Create one with: hawk plan create ", textMuted)) } return nil } @@ -92,7 +92,7 @@ var planListCmd = &cobra.Command{ } if len(plans) == 0 { - cmd.Println(auditTint("No plans found. Create one with: graycode plan create ", textMuted)) + cmd.Println(auditTint("No plans found. Create one with: hawk plan create ", textMuted)) return nil } @@ -147,10 +147,10 @@ var planDoneCmd = &cobra.Command{ Use: "done ", Short: "Mark a task as completed in the most recent plan", Long: `Mark a task as done by its numeric ID. This operates on the most -recently modified plan in Graycode's user state directory for the current project. +recently modified plan in Hawk's user state directory for the current project. To target a specific plan, set the plan name as the first argument -followed by the task ID: graycode plan done `, +followed by the task ID: hawk plan done `, Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { var planPath string @@ -204,7 +204,7 @@ func init() { planShowCmd.Flags().BoolVar(&planJSON, "json", false, "output plan as JSON") } -// resolvePlanPath converts a plan name to a file path in Graycode's user state dir. +// resolvePlanPath converts a plan name to a file path in Hawk's user state dir. func resolvePlanPath(name string) string { plansDir := currentProjectPlansDir() // If the name already has a .json extension, use it directly. diff --git a/cmd/plugin_dynamic.go b/cmd/plugin_dynamic.go index cc1fea742..325fde2f5 100644 --- a/cmd/plugin_dynamic.go +++ b/cmd/plugin_dynamic.go @@ -8,8 +8,8 @@ import ( "path/filepath" "time" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/theme" "github.com/spf13/cobra" ) @@ -90,7 +90,7 @@ var pluginStatusCmd = &cobra.Command{ statuses := dm.Status() if len(statuses) == 0 { - cmd.Println(auditTint("No plugins discovered. Run 'graycode plugin install' to add plugins.", textMuted)) + cmd.Println(auditTint("No plugins discovered. Run 'hawk plugin install' to add plugins.", textMuted)) return nil } @@ -209,7 +209,7 @@ var pluginCreateCmd = &cobra.Command{ manifest := &plugin.ManifestV2{ Name: name, Version: "0.1.0", - Description: fmt.Sprintf("A graycode plugin: %s", name), + Description: fmt.Sprintf("A hawk plugin: %s", name), Author: "", Mode: "subprocess", Tools: []plugin.ManifestTool{ @@ -281,12 +281,12 @@ func main() { // Write README.md readme := fmt.Sprintf(`# %s -A graycode plugin. +A hawk plugin. ## Installation `+"```bash"+` -graycode plugin install ./%s +hawk plugin install ./%s `+"```"+` ## Usage @@ -339,8 +339,8 @@ See `+"`plugin.json`"+` for the full manifest configuration. cmd.Println() cmd.Printf("%s\n", auditTint("Next steps:", textPrimary)) cmd.Printf("%s\n", auditTint(" cd "+name+" && go mod init "+name, textMuted)) - cmd.Printf("%s\n", auditTint(" graycode plugin install ./"+name, textMuted)) - cmd.Printf("%s\n", auditTint(" graycode plugin activate "+name, textMuted)) + cmd.Printf("%s\n", auditTint(" hawk plugin install ./"+name, textMuted)) + cmd.Printf("%s\n", auditTint(" hawk plugin activate "+name, textMuted)) return nil }, } @@ -422,13 +422,13 @@ var pluginMarketplaceListCmd = &cobra.Command{ entries, err := mc.FetchAll() if err != nil { prog.Abort() - return fmt.Errorf("fetch marketplace: %w (indexes may be unpublished; add a source with graycode plugin marketplace add)", err) + return fmt.Errorf("fetch marketplace: %w (indexes may be unpublished; add a source with hawk plugin marketplace add)", err) } prog.CompleteStep(0) prog.Done() if len(entries) == 0 { cmd.Println(auditTint("No marketplace plugins found.", textMuted)) - cmd.Println(auditTint("Add a source: graycode plugin marketplace add ", textMuted)) + cmd.Println(auditTint("Add a source: hawk plugin marketplace add ", textMuted)) return nil } rows := make([][]string, 0, len(entries)) diff --git a/cmd/power.go b/cmd/power.go index 1b29e2fb8..178d5b04f 100644 --- a/cmd/power.go +++ b/cmd/power.go @@ -3,8 +3,8 @@ package cmd import ( "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // PowerConfig maps a power level (1-10) to all relevant settings. diff --git a/cmd/pr.go b/cmd/pr.go index 6c46b732f..060d406b3 100644 --- a/cmd/pr.go +++ b/cmd/pr.go @@ -30,10 +30,10 @@ Subcommands: describe Generate or update a PR description Examples: - graycode pr review - graycode pr review 42 --post-comments - graycode pr create --base develop --draft - graycode pr describe 42 --update`, + hawk pr review + hawk pr review 42 --post-comments + hawk pr create --base develop --draft + hawk pr describe 42 --update`, } var prReviewCmd = &cobra.Command{ diff --git a/cmd/preflight_test.go b/cmd/preflight_test.go index f6df23fd3..c1479d617 100644 --- a/cmd/preflight_test.go +++ b/cmd/preflight_test.go @@ -9,7 +9,7 @@ import ( // preflightReportShape mirrors the JSON-tagged PreflightReport returned by // EnginePreflightReportWithSettings. We assert against a local shape to -// avoid coupling the test to the exact gateway/graycode-router import path. +// avoid coupling the test to the exact gateway/eyrie import path. type preflightReportShape struct { Ready bool `json:"ready"` Checks []preflightCheckShape `json:"checks"` @@ -23,7 +23,7 @@ type preflightCheckShape struct { func TestPreflightJSON_Structure(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(dir, "state")) old := preflightJSON oldLive := preflightLiveFlag @@ -59,7 +59,7 @@ func TestPreflightJSON_Structure(t *testing.T) { func TestPreflightText_NotJSON(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(dir, "state")) old := preflightJSON oldLive := preflightLiveFlag diff --git a/cmd/progress_cli.go b/cmd/progress_cli.go index 6911b9d92..1895c4fa6 100644 --- a/cmd/progress_cli.go +++ b/cmd/progress_cli.go @@ -9,7 +9,7 @@ import ( "time" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // CLIProgress renders a ProgressTracker as live single-line progress for @@ -36,7 +36,7 @@ func newCLIProgress(title string, steps []string, w io.Writer, tty bool) *CLIPro for _, s := range steps { pt.AddStep(s) } - return &CLIProgress{w: w, pt: pt, spinner: NewBrailleSpinner(SpinnerGraycode, ""), tty: tty} + return &CLIProgress{w: w, pt: pt, spinner: NewBrailleSpinner(SpinnerHawk, ""), tty: tty} } // StartStep marks step i active and, on a TTY, starts animating it in place. @@ -47,7 +47,7 @@ func (c *CLIProgress) StartStep(i int) { if IsQuiet() || !c.tty || i < 0 || i >= len(c.pt.Steps) { return } - c.spinner = NewBrailleSpinner(SpinnerGraycode, c.pt.Steps[i].Name) + c.spinner = NewBrailleSpinner(SpinnerHawk, c.pt.Steps[i].Name) c.spinner.Start(80*time.Millisecond, func(frame string) { eta := "" if remaining := c.pt.EstimateRemaining(); remaining > 0 { diff --git a/cmd/progress_debug_test.go b/cmd/progress_debug_test.go index c26d869d5..0ac60e579 100644 --- a/cmd/progress_debug_test.go +++ b/cmd/progress_debug_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestZDebugProgress(t *testing.T) { diff --git a/cmd/progress_tracker.go b/cmd/progress_tracker.go index b68e43c20..3cbb70e3d 100644 --- a/cmd/progress_tracker.go +++ b/cmd/progress_tracker.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // ProgressStep represents a single step in a multi-step task. diff --git a/cmd/progress_tracker_test.go b/cmd/progress_tracker_test.go index f5c500a44..a3825cd15 100644 --- a/cmd/progress_tracker_test.go +++ b/cmd/progress_tracker_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewProgressTracker(t *testing.T) { diff --git a/cmd/progressive_disclosure.go b/cmd/progressive_disclosure.go index fd82b67f6..ee735b8b2 100644 --- a/cmd/progressive_disclosure.go +++ b/cmd/progressive_disclosure.go @@ -52,22 +52,22 @@ func DefaultDisclosureConfig() DisclosureConfig { // BeginnerHelp returns a simplified help message for new users. func BeginnerHelp() string { - return `Getting Started with graycode + return `Getting Started with hawk - Type your question or task and graycode will help. - graycode reads your project and understands your code. + Type your question or task and hawk will help. + hawk reads your project and understands your code. Essential Commands: /help Show all commands /test Run your project's tests /diff See what you've changed /commit Save your work with a smart message - /review Have graycode review your code + /review Have hawk review your code /clear Start a fresh conversation Tips: - Just describe what you want in plain English - - graycode will read files, run commands, and make changes + - hawk will read files, run commands, and make changes - Use /help to discover more features as you get comfortable Type anything to get started!` @@ -75,7 +75,7 @@ Type anything to get started!` // IntermediateHelp returns the standard help message. func IntermediateHelp() string { - return `graycode Commands + return `hawk Commands Workflow: /test Run tests and fix failures @@ -110,7 +110,7 @@ Use /help all for the complete list.` // AdvancedHelp returns the full command reference. func AdvancedHelp() string { - return `graycode Full Command Reference + return `hawk Full Command Reference Workflow: /test [cmd] Run tests (default: go test ./...) diff --git a/cmd/prompt_input.go b/cmd/prompt_input.go index 674b531bb..12b7a5694 100644 --- a/cmd/prompt_input.go +++ b/cmd/prompt_input.go @@ -7,7 +7,7 @@ import ( "os" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) var errNoInteractivePromptInput = errors.New("no interactive terminal available for permission prompt") diff --git a/cmd/record.go b/cmd/record.go index ebc494290..88e80d6ba 100644 --- a/cmd/record.go +++ b/cmd/record.go @@ -4,7 +4,7 @@ import ( "io" "os" - "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" + "github.com/GrayCodeAI/hawk/internal/terminal/tape" ) // recordPath is set by --record; when non-empty, interactive REPL output is diff --git a/cmd/record_test.go b/cmd/record_test.go index 2014620c5..7ee56f426 100644 --- a/cmd/record_test.go +++ b/cmd/record_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" + "github.com/GrayCodeAI/hawk/internal/terminal/tape" ) func TestStartRecordingCapturesOutput(t *testing.T) { diff --git a/cmd/replay.go b/cmd/replay.go index 5aeb92d9d..3fde35d82 100644 --- a/cmd/replay.go +++ b/cmd/replay.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" + "github.com/GrayCodeAI/hawk/internal/terminal/tape" ) var ( @@ -22,7 +22,7 @@ var replayCmd = &cobra.Command{ Use: "replay ", Short: "Replay a recorded terminal capture (fxtape)", Long: `Replay a terminal capture recorded by the FX_RECORD tape writer (the -binary fxtape format from vercel-labs/fx, which graycode's tape package reads +binary fxtape format from vercel-labs/fx, which hawk's tape package reads byte-for-byte compatibly). Feeds the recorded stdout bytes into a virtual terminal grid and prints the final visible snapshot. diff --git a/cmd/resize_unix.go b/cmd/resize_unix.go index cc776dce7..33482420d 100644 --- a/cmd/resize_unix.go +++ b/cmd/resize_unix.go @@ -7,7 +7,7 @@ import ( "os/signal" "syscall" - "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" + "github.com/GrayCodeAI/hawk/internal/terminal/tape" ) // watchTerminalResize records terminal resize (SIGWINCH) events into the tape diff --git a/cmd/resize_windows.go b/cmd/resize_windows.go index b50bb0f70..11038a9cb 100644 --- a/cmd/resize_windows.go +++ b/cmd/resize_windows.go @@ -2,7 +2,7 @@ package cmd -import "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" +import "github.com/GrayCodeAI/hawk/internal/terminal/tape" // watchTerminalResize is a no-op on Windows, where SIGWINCH is not defined. func watchTerminalResize(rec *tape.Recorder) func() { diff --git a/cmd/review.go b/cmd/review.go index 2182895aa..baa529a7e 100644 --- a/cmd/review.go +++ b/cmd/review.go @@ -10,17 +10,17 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var reviewCmd = &cobra.Command{ Use: "review", Short: "Continuous AI code review on commits", - Long: `graycode review provides continuous background code review. + Long: `hawk review provides continuous background code review. -Run 'graycode review init' to install a post-commit hook, then every commit -is automatically reviewed using kestrel. View findings with 'graycode review tui' -or fix them with 'graycode review fix'.`, +Run 'hawk review init' to install a post-commit hook, then every commit +is automatically reviewed using kestrel. View findings with 'hawk review tui' +or fix them with 'hawk review fix'.`, } var reviewInitCmd = &cobra.Command{ @@ -38,10 +38,10 @@ func init() { } const hookScript = `#!/bin/sh -# graycode review — continuous code review hook -# Installed by 'graycode review init' +# hawk review — continuous code review hook +# Installed by 'hawk review init' SHA=$(git rev-parse HEAD) -graycode review run "$SHA" --background & +hawk review run "$SHA" --background & ` func runReviewInit(_ *cobra.Command, _ []string) error { @@ -60,8 +60,8 @@ func runReviewInit(_ *cobra.Command, _ []string) error { // Check for existing hook. if _, err := os.Stat(hookPath); err == nil && !reviewInitForce { existing, _ := os.ReadFile(hookPath) // #nosec G304 -- hookPath built from internal hooksDir constant, not external input - if strings.Contains(string(existing), "graycode review") { - fmt.Println(auditTint(icons.CheckBold()+" ", doneGreen) + auditTint("graycode review hook already installed", textPrimary)) + if strings.Contains(string(existing), "hawk review") { + fmt.Println(auditTint(icons.CheckBold()+" ", doneGreen) + auditTint("hawk review hook already installed", textPrimary)) return nil } return fmt.Errorf("post-commit hook already exists at %s\nUse --force to overwrite, or manually add:\n %s", hookPath, strings.TrimSpace(hookScript)) @@ -74,8 +74,8 @@ func runReviewInit(_ *cobra.Command, _ []string) error { fmt.Println(auditTint(icons.CheckBold()+" ", doneGreen) + auditTint("Installed post-commit hook at ", textPrimary) + auditTint(hookPath, textMuted)) fmt.Println(auditTint(" Every commit will now be reviewed automatically.", textMuted)) - fmt.Println(auditTint(" View reviews: graycode review status", textMuted)) - fmt.Println(auditTint(" Interactive: graycode review tui", textMuted)) + fmt.Println(auditTint(" View reviews: hawk review status", textMuted)) + fmt.Println(auditTint(" Interactive: hawk review tui", textMuted)) return nil } diff --git a/cmd/review_analyze.go b/cmd/review_analyze.go index 6f2843716..b62fe0495 100644 --- a/cmd/review_analyze.go +++ b/cmd/review_analyze.go @@ -8,11 +8,11 @@ import ( "strings" "time" - graycodeKestrel "github.com/GrayCodeAI/graycode-cli/internal/bridge/kestrel" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkKestrel "github.com/GrayCodeAI/hawk/internal/bridge/kestrel" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" kestrelLib "github.com/GrayCodeAI/kestrel" "github.com/spf13/cobra" ) @@ -38,9 +38,9 @@ Types: test-fixtures Find test helper opportunities Examples: - graycode review analyze security ./... - graycode review analyze complexity --fix main.go - graycode review analyze duplication ./internal/...`, + hawk review analyze security ./... + hawk review analyze complexity --fix main.go + hawk review analyze duplication ./internal/...`, Args: cobra.MinimumNArgs(1), RunE: runReviewAnalyze, } @@ -124,9 +124,9 @@ func runReviewAnalyze(_ *cobra.Command, args []string) error { return nil } - // Build the Kestrel bridge through Graycode's GraycodeRouter engine boundary. + // Build the Kestrel bridge through Hawk's Eyrie engine boundary. ctx := context.Background() - selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{ + selection := hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), ModelOverride: strings.TrimSpace(analyzeModel), }) @@ -141,7 +141,7 @@ func runReviewAnalyze(_ *cobra.Command, args []string) error { } opts = append(opts, kestrelLib.WithConcerns(analysisType)) - bridge := graycodeKestrel.NewBridge(chatProvider, providerID, opts...) + bridge := hawkKestrel.NewBridge(chatProvider, providerID, opts...) if !bridge.Ready() { return fmt.Errorf("kestrel bridge not ready (check API key)") } @@ -257,12 +257,12 @@ func autoFixAnalysis(result *reviewcontracts.Result) error { } b.WriteString("\nApply minimal, focused fixes. Commit with 'fix: address analysis findings'.") - graycodeBin, err := os.Executable() + hawkBin, err := os.Executable() if err != nil { - graycodeBin = "graycode" + hawkBin = "hawk" } - cmd := exec.CommandContext(context.Background(), graycodeBin, "exec", "--auto", "full", b.String()) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; args are internal flags + cmd := exec.CommandContext(context.Background(), hawkBin, "exec", "--auto", "full", b.String()) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() diff --git a/cmd/review_fix.go b/cmd/review_fix.go index 1417847b0..10a5eeb3d 100644 --- a/cmd/review_fix.go +++ b/cmd/review_fix.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var ( @@ -21,8 +21,8 @@ var ( var reviewFixCmd = &cobra.Command{ Use: "fix [id...]", - Short: "Auto-fix review findings using graycode exec", - Long: `Feeds review findings to graycode's engine which applies fixes and commits. + Short: "Auto-fix review findings using hawk exec", + Long: `Feeds review findings to hawk's engine which applies fixes and commits. Without arguments, fixes all open reviews. Specify IDs to fix specific ones.`, RunE: runReviewFix, } @@ -82,7 +82,7 @@ func fixReview(store *ReviewStore, r *ReviewRecord) error { prompt := buildFixPrompt(r) - // Invoke graycode exec with the fix prompt. + // Invoke hawk exec with the fix prompt. execArgs := []string{"exec", "--auto", "full"} if reviewFixWorktree { execArgs = append(execArgs, "--worktree") @@ -92,18 +92,18 @@ func fixReview(store *ReviewStore, r *ReviewRecord) error { } execArgs = append(execArgs, prompt) - graycodeBin, err := os.Executable() + hawkBin, err := os.Executable() if err != nil { - graycodeBin = "graycode" + hawkBin = "hawk" } - cmd := exec.CommandContext(context.Background(), graycodeBin, execArgs...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; args are internal flags + cmd := exec.CommandContext(context.Background(), hawkBin, execArgs...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin if err := cmd.Run(); err != nil { - return fmt.Errorf("graycode exec: %w", err) + return fmt.Errorf("hawk exec: %w", err) } // Mark as fixed. diff --git a/cmd/review_read.go b/cmd/review_read.go index 9b8c7931e..b2feb0b2e 100644 --- a/cmd/review_read.go +++ b/cmd/review_read.go @@ -10,8 +10,8 @@ import ( "github.com/spf13/cobra" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var reviewStatusCmd = &cobra.Command{ @@ -72,7 +72,7 @@ func runReviewStatus(_ *cobra.Command, _ []string) error { total += v } if total == 0 { - fmt.Println(auditTint("No reviews yet. Run 'graycode review init' to start.", textMuted)) + fmt.Println(auditTint("No reviews yet. Run 'hawk review init' to start.", textMuted)) return nil } diff --git a/cmd/review_refine.go b/cmd/review_refine.go index 11607d74e..90f2643ba 100644 --- a/cmd/review_refine.go +++ b/cmd/review_refine.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var ( @@ -69,7 +69,7 @@ func runReviewRefine(_ *cobra.Command, args []string) error { fmt.Printf("%s\n\n", auditTint(fmt.Sprintf("Refining %d review(s), max %d iterations...", len(reviews), refineMaxIter), textPrimary)) for iter := 1; iter <= refineMaxIter; iter++ { - fmt.Printf("%s\n", auditTint(fmt.Sprintf("── Iteration %d/%d ──", iter, refineMaxIter), graycodeColor)) + fmt.Printf("%s\n", auditTint(fmt.Sprintf("── Iteration %d/%d ──", iter, refineMaxIter), hawkColor)) // Fix all open reviews. for _, r := range reviews { @@ -129,7 +129,7 @@ func runReviewRefine(_ *cobra.Command, args []string) error { } if len(remaining) > 0 { fmt.Printf("\n%s %s\n", auditTint(icons.Alert(), warnAmber), auditTint(fmt.Sprintf("%d review(s) still open after %d iterations.", len(remaining), refineMaxIter), textPrimary)) - fmt.Println(auditTint(" Run 'graycode review show' to inspect, or increase --max-iterations.", textMuted)) + fmt.Println(auditTint(" Run 'hawk review show' to inspect, or increase --max-iterations.", textMuted)) } return nil } @@ -137,9 +137,9 @@ func runReviewRefine(_ *cobra.Command, args []string) error { func fixReviewRefine(store *ReviewStore, r *ReviewRecord) error { prompt := buildFixPrompt(r) - graycodeBin, err := os.Executable() + hawkBin, err := os.Executable() if err != nil { - graycodeBin = "graycode" + hawkBin = "hawk" } execArgs := []string{"exec", "--auto", "full"} @@ -148,7 +148,7 @@ func fixReviewRefine(store *ReviewStore, r *ReviewRecord) error { } execArgs = append(execArgs, prompt) - cmd := exec.CommandContext(context.Background(), graycodeBin, execArgs...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; args are internal flags + cmd := exec.CommandContext(context.Background(), hawkBin, execArgs...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -159,9 +159,9 @@ func fixReviewRefine(store *ReviewStore, r *ReviewRecord) error { } func runReviewOnSHA(store *ReviewStore, sha string) error { - graycodeBin, err := os.Executable() + hawkBin, err := os.Executable() if err != nil { - graycodeBin = "graycode" + hawkBin = "hawk" } args := []string{"review", "run", sha} @@ -172,7 +172,7 @@ func runReviewOnSHA(store *ReviewStore, sha string) error { args = append(args, "--model", refineModel) } - cmd := exec.CommandContext(context.Background(), graycodeBin, args...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; args are internal flags + cmd := exec.CommandContext(context.Background(), hawkBin, args...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() diff --git a/cmd/review_run.go b/cmd/review_run.go index ecb23fe07..6648d04ad 100644 --- a/cmd/review_run.go +++ b/cmd/review_run.go @@ -9,12 +9,12 @@ import ( "strings" "time" - graycodeKestrel "github.com/GrayCodeAI/graycode-cli/internal/bridge/kestrel" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkKestrel "github.com/GrayCodeAI/hawk/internal/bridge/kestrel" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/ui/icons" kestrelLib "github.com/GrayCodeAI/kestrel" "github.com/spf13/cobra" ) @@ -123,9 +123,9 @@ func runReviewRun(_ *cobra.Command, args []string) error { } } - // Build the Kestrel bridge through Graycode's GraycodeRouter engine boundary. + // Build the Kestrel bridge through Hawk's Eyrie engine boundary. ctx := context.Background() - selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{ + selection := hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), ModelOverride: strings.TrimSpace(reviewRunModel), }) @@ -150,7 +150,7 @@ func runReviewRun(_ *cobra.Command, args []string) error { opts = append(opts, kestrelLib.WithConcerns(concerns...)) } - bridge := graycodeKestrel.NewBridge(chatProvider, providerID, opts...) + bridge := hawkKestrel.NewBridge(chatProvider, providerID, opts...) if !bridge.Ready() { if statusErr := store.SetStatus(id, ReviewStatusFailed); statusErr != nil { return silentErr(statusErr, "mark review failed") diff --git a/cmd/review_run_test.go b/cmd/review_run_test.go index 7e1f4a355..a48157f46 100644 --- a/cmd/review_run_test.go +++ b/cmd/review_run_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" ) // captureStdout runs fn with stdout redirected to a pipe and returns what was diff --git a/cmd/review_store.go b/cmd/review_store.go index 71ac9402c..5a95abab5 100644 --- a/cmd/review_store.go +++ b/cmd/review_store.go @@ -10,8 +10,8 @@ import ( "sync" "time" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + "github.com/GrayCodeAI/hawk/internal/storage" ) // ReviewStatus represents the state of a review. diff --git a/cmd/review_test.go b/cmd/review_test.go index cfd4c4cea..2063d8084 100644 --- a/cmd/review_test.go +++ b/cmd/review_test.go @@ -6,11 +6,11 @@ import ( "strings" "testing" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func setReviewTestDirs(t *testing.T) string { @@ -22,7 +22,7 @@ func setReviewTestDirs(t *testing.T) string { func TestReviewStore_CreateAndGet(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -52,7 +52,7 @@ func TestReviewStore_CreateAndGet(t *testing.T) { func TestReviewStore_Update(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -92,7 +92,7 @@ func TestReviewStore_Update(t *testing.T) { func TestReviewStore_GetBySHA(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -114,7 +114,7 @@ func TestReviewStore_GetBySHA(t *testing.T) { func TestReviewStore_ListOpen(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -141,7 +141,7 @@ func TestReviewStore_ListOpen(t *testing.T) { func TestReviewStore_Summary(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -171,7 +171,7 @@ func TestReviewStore_Summary(t *testing.T) { func TestReviewStore_SetStatus(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -191,7 +191,7 @@ func TestReviewStore_SetStatus(t *testing.T) { func TestReviewStore_ListAll(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -222,7 +222,7 @@ func TestReviewStore_ListAll(t *testing.T) { func TestReviewStore_GetMissing(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -239,7 +239,7 @@ func TestReviewStore_GetMissing(t *testing.T) { func TestReviewStore_EmptyDiffToPassedLifecycle(t *testing.T) { // Mirrors runReviewRun's empty-diff path: create → set running → set passed. dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -278,7 +278,7 @@ func TestReviewStore_EmptyDiffToPassedLifecycle(t *testing.T) { func TestReviewStore_CloseCheckpointsWAL(t *testing.T) { dir := setReviewTestDirs(t) - os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(dir, ".hawk"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -365,9 +365,9 @@ func TestAnalysisPrompts_AllTypesExist(t *testing.T) { } } -func TestHookScript_ContainsGraycodeReview(t *testing.T) { - if !strings.Contains(hookScript, "graycode review") { - t.Error("hook script should contain 'graycode review'") +func TestHookScript_ContainsHawkReview(t *testing.T) { + if !strings.Contains(hookScript, "hawk review") { + t.Error("hook script should contain 'hawk review'") } if !strings.Contains(hookScript, "git rev-parse HEAD") { t.Error("hook script should get HEAD sha") diff --git a/cmd/review_tui.go b/cmd/review_tui.go index e395dee00..e25e84920 100644 --- a/cmd/review_tui.go +++ b/cmd/review_tui.go @@ -9,7 +9,7 @@ import ( lipgloss "charm.land/lipgloss/v2" "github.com/spf13/cobra" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var reviewTUICmd = &cobra.Command{ @@ -127,11 +127,11 @@ func (m reviewTUIModel) View() tea.View { selected := lipgloss.NewStyle().Bold(true).Background(lipgloss.Color("236")) var b strings.Builder - b.WriteString(header.Render(" graycode review") + dim.Render(" j/k:nav enter:expand c:close f:fix r:refresh q:quit") + "\n") + b.WriteString(header.Render(" hawk review") + dim.Render(" j/k:nav enter:expand c:close f:fix r:refresh q:quit") + "\n") b.WriteString(strings.Repeat("─", reviewMin(m.width, 80)) + "\n") if len(m.reviews) == 0 { - b.WriteString("\n No reviews yet. Run 'graycode review init' to get started.\n") + b.WriteString("\n No reviews yet. Run 'hawk review init' to get started.\n") v := tea.View{Content: b.String()} v.AltScreen = true return v diff --git a/cmd/root.go b/cmd/root.go index 932f3ac5f..83fae56e5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,14 +10,14 @@ import ( "strings" "time" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" - "github.com/GrayCodeAI/graycode-cli/internal/onboarding" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/update" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/hawk/internal/onboarding" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/update" "github.com/spf13/cobra" ) @@ -88,33 +88,33 @@ func SetBuildDate(d string) { } func registeredProviderCount() int { - return graycodeconfig.RegisteredProviderCount() + return hawkconfig.RegisteredProviderCount() } var rootCmd = &cobra.Command{ - Use: "graycode [prompt]", - Short: "AI coding agent powered by graycode-router", - Long: fmt.Sprintf(`graycode is an AI coding agent that reads, writes, and runs code in your terminal. + Use: "hawk [prompt]", + Short: "AI coding agent powered by eyrie", + Long: fmt.Sprintf(`hawk is an AI coding agent that reads, writes, and runs code in your terminal. -It connects to %d first-class LLM providers through graycode-router, executes tools (file I/O, shell, +It connects to %d first-class LLM providers through eyrie, executes tools (file I/O, shell, git, web search), and manages sessions — all from a keyboard-driven TUI or headless mode for scripts and CI. Quick orientation: - graycode Start interactive TUI - graycode -p "prompt" One-shot: send prompt, print response, exit - graycode exec "task" Autonomous multi-turn execution - graycode path Check environment readiness - graycode doctor Run diagnostics - graycode config Manage settings and credentials + hawk Start interactive TUI + hawk -p "prompt" One-shot: send prompt, print response, exit + hawk exec "task" Autonomous multi-turn execution + hawk path Check environment readiness + hawk doctor Run diagnostics + hawk config Manage settings and credentials API keys are stored in the OS keychain (macOS Keychain / Linux keyring). -Run graycode and use /config to set up your first provider.`, registeredProviderCount()), - Example: ` graycode - graycode -p "explain this repo" - graycode exec "fix failing tests" - graycode preflight - graycode path`, +Run hawk and use /config to set up your first provider.`, registeredProviderCount()), + Example: ` hawk + hawk -p "explain this repo" + hawk exec "fix failing tests" + hawk preflight + hawk path`, Args: cobra.ArbitraryArgs, SilenceUsage: true, SilenceErrors: true, @@ -148,10 +148,10 @@ Run graycode and use /config to set up your first provider.`, registeredProvider if printMode || promptFlag != "" || inputFormat == "stream-json" || replFlag || watchFlag { // Credential migration is deferred until a path that actually - // uses credentials: `graycode path`, `graycode version`, auto-skill and - // other cold commands no longer construct the graycode-router engine + // uses credentials: `hawk path`, `hawk version`, auto-skill and + // other cold commands no longer construct the eyrie engine // (M17 — was ~1.8s on every root command). - logMigrateProviderSecretsError(logger.Default(), graycodeconfig.MigrateProviderSecrets()) + logMigrateProviderSecretsError(logger.Default(), hawkconfig.MigrateProviderSecrets()) if promptFlag == "" && !replFlag && !watchFlag { stdinPrompt, err := readPromptFromStdin(inputFormat) if err != nil { @@ -170,7 +170,7 @@ Run graycode and use /config to set up your first provider.`, registeredProvider // the TUI, so gate them identically: untrusted folders block // project automation. if tr := engine.ProjectTrust(""); tr.Blocked { - return fmt.Errorf("cannot start: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'graycode trust add' to trust this folder before running graycode", tr.Path) + return fmt.Errorf("cannot start: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'hawk trust add' to trust this folder before running hawk", tr.Path) } if replFlag { return runRepl() @@ -206,20 +206,20 @@ Run graycode and use /config to set up your first provider.`, registeredProvider } // TUI path uses credentials — run the one-time hygiene pass here. - logMigrateProviderSecretsError(logger.Default(), graycodeconfig.MigrateProviderSecrets()) + logMigrateProviderSecretsError(logger.Default(), hawkconfig.MigrateProviderSecrets()) // Folder trust check — block starting CLI in an untrusted directory if tr := engine.ProjectTrust(""); tr.Blocked { - return fmt.Errorf("cannot start CLI: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'graycode trust add' to trust this folder before starting graycode", tr.Path) + return fmt.Errorf("cannot start CLI: folder not trusted (%s)\nProject-scoped hooks, MCP servers, and custom specialists are blocked.\nRun 'hawk trust add' to trust this folder before starting hawk", tr.Path) } - // Launch TUI — use /config to set API keys; graycode-router supplies providers and models + // Launch TUI — use /config to set API keys; eyrie supplies providers and models return runChat() }, } func init() { - rootCmd.Flags().StringVarP(&model, "model", "m", "", "model to use (from graycode-router catalog; see /models)") + rootCmd.Flags().StringVarP(&model, "model", "m", "", "model to use (from eyrie catalog; see /models)") rootCmd.Flags().BoolVarP(&printMode, "print", "p", false, "print response and exit") rootCmd.Flags().StringVar(&promptFlag, "prompt", "", "send a single prompt and exit (legacy alias for --print)") rootCmd.Flags().StringVar(&outputFormat, "output-format", "text", `output format for --print: "text", "json", or "stream-json"`) @@ -261,7 +261,7 @@ func init() { rootCmd.Flags().IntVar(&teachDepth, "teach-depth", 2, "explanation depth: 1=what, 2=why, 3=how") rootCmd.Flags().BoolVar(&autoSkillFlag, "auto-skill", false, "auto-detect project and install matching skills") rootCmd.Flags().BoolVarP(&versionFlag, "version", "v", false, "output the version number") - rootCmd.Flags().BoolVar(&refreshCatalogFlag, "refresh-catalog", false, "refresh the graycode-router model catalog before starting") + rootCmd.Flags().BoolVar(&refreshCatalogFlag, "refresh-catalog", false, "refresh the eyrie model catalog before starting") rootCmd.Flags().BoolVar(&skipCatalogRefreshFlag, "no-auto-catalog-refresh", false, "disable automatic catalog refresh when cache is missing, empty, or stale") rootCmd.Flags().BoolVar(&recoverFlag, "recover", false, "scan for interrupted sessions and offer to resume") rootCmd.Flags().BoolVar(&startupProfileFlag, "startup-profile", false, "print startup performance profile") @@ -311,7 +311,7 @@ func init() { // In a terminal, it requires typing the full confirmation token (not a single // key) so a stray keystroke or terminal-escape trickery cannot confirm it. In // non-interactive mode (CI, scripts), it requires the -// GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS=1 environment variable. +// HAWK_DANGEROUSLY_SKIP_PERMISSIONS=1 environment variable. func confirmDangerousSkipPermissions() error { if isStdinTerminal() { fmt.Fprintf(os.Stderr, "Type %s to confirm skipping permission prompts: ", dangerSkipConfirmToken) @@ -326,8 +326,8 @@ func confirmDangerousSkipPermissions() error { return nil } // Non-interactive: require explicit env var override. - if os.Getenv("GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS") != "1" { - return fmt.Errorf("--dangerously-skip-permissions requires GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS=1 in non-interactive mode") + if os.Getenv("HAWK_DANGEROUSLY_SKIP_PERMISSIONS") != "1" { + return fmt.Errorf("--dangerously-skip-permissions requires HAWK_DANGEROUSLY_SKIP_PERMISSIONS=1 in non-interactive mode") } return nil } @@ -351,31 +351,31 @@ var completionCmd = &cobra.Command{ Long: `To load completions: Bash: - source <(graycode completion bash) + source <(hawk completion bash) # To load completions for each session, execute once: # Linux: - graycode completion bash > /etc/bash_completion.d/graycode + hawk completion bash > /etc/bash_completion.d/hawk # macOS: - graycode completion bash > /usr/local/etc/bash_completion.d/graycode + hawk completion bash > /usr/local/etc/bash_completion.d/hawk Zsh: - source <(graycode completion zsh) + source <(hawk completion zsh) # To load completions for each session, execute once: - graycode completion zsh > "${fpath[1]}/_graycode" + hawk completion zsh > "${fpath[1]}/_hawk" Fish: - graycode completion fish | source + hawk completion fish | source # To load completions for each session, execute once: - graycode completion fish > ~/.config/fish/completions/graycode.fish + hawk completion fish > ~/.config/fish/completions/hawk.fish PowerShell: - graycode completion powershell | Out-String | Invoke-Expression + hawk completion powershell | Out-String | Invoke-Expression # To load completions for every new session, run: - graycode completion powershell > graycode.ps1 + hawk completion powershell > hawk.ps1 # and source this file from your PowerShell profile. JSON: - graycode completion json + hawk completion json # Print a machine-readable command/flag spec for IDE integration. `, DisableFlagsInUseLine: true, @@ -409,17 +409,17 @@ var completionInstallCmd = &cobra.Command{ Long: `Install the shell completion script to the standard location for your OS. Bash: - graycode completion install bash - # Installs to ~/.local/share/bash-completion/completions/graycode (Linux) - # or /opt/homebrew/etc/bash_completion.d/graycode (macOS Homebrew) + hawk completion install bash + # Installs to ~/.local/share/bash-completion/completions/hawk (Linux) + # or /opt/homebrew/etc/bash_completion.d/hawk (macOS Homebrew) Zsh: - graycode completion install zsh - # Installs to the first directory in $fpath (e.g. /usr/local/share/zsh/site-functions/_graycode) + hawk completion install zsh + # Installs to the first directory in $fpath (e.g. /usr/local/share/zsh/site-functions/_hawk) Fish: - graycode completion install fish - # Installs to ~/.config/fish/completions/graycode.fish`, + hawk completion install fish + # Installs to ~/.config/fish/completions/hawk.fish`, DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish"}, Args: cobra.ExactArgs(1), @@ -461,8 +461,8 @@ Fish: var updateCmd = &cobra.Command{ Use: "update", - Short: "Check for graycode updates", - Long: "Check GitHub for a newer graycode release and print upgrade instructions.", + Short: "Check for hawk updates", + Long: "Check GitHub for a newer hawk release and print upgrade instructions.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ver := version @@ -480,7 +480,7 @@ var updateCmd = &cobra.Command{ return nil } if release == nil { - cmd.Println(auditTint("graycode is up to date ("+ver+")", doneGreen)) + cmd.Println(auditTint("hawk is up to date ("+ver+")", doneGreen)) return nil } cmd.Println(auditTint("Update available: ", warnAmber) + auditTint(ver+" -> "+release.TagName, textPrimary)) @@ -502,13 +502,13 @@ API keys and secrets are never included.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { var b strings.Builder - b.WriteString("## graycode bug report\n\n") + b.WriteString("## hawk bug report\n\n") b.WriteString(fmt.Sprintf("- **Version:** %s\n", versionLine())) b.WriteString(fmt.Sprintf("- **Platform:** %s\n", update.Platform())) b.WriteString(fmt.Sprintf("- **Go:** %s\n", runtime.Version())) b.WriteString(fmt.Sprintf("- **OS/Arch:** %s/%s\n", runtime.GOOS, runtime.GOARCH)) b.WriteString("\n## Doctor output\n\n```\n") - settings := graycodeconfig.LoadSettings() + settings := hawkconfig.LoadSettings() b.WriteString(doctorReport(settings)) b.WriteString("\n```\n") cmd.Print(b.String()) @@ -526,7 +526,7 @@ var versionJSON bool var versionCmd = &cobra.Command{ Use: "version", - Short: "Print graycode version", + Short: "Print hawk version", Run: func(cmd *cobra.Command, args []string) { if versionJSON { info := versionInfo{Version: DisplayVersion()} @@ -563,7 +563,7 @@ var setupCmd = &cobra.Command{ var initCmd = &cobra.Command{ Use: "init", Short: "Interactive onboarding wizard for first-time setup", - Long: "Launch the interactive setup wizard to configure credentials, select providers/models, and initialize graycode.", + Long: "Launch the interactive setup wizard to configure credentials, select providers/models, and initialize hawk.", RunE: func(cmd *cobra.Command, args []string) error { onboarding.Welcome(version) return onboarding.RunSetup() @@ -627,7 +627,7 @@ var preflightCmd = &cobra.Command{ defer prog.Abort() prog.StartStep(0) } - r := graycodeconfig.EnginePreflightReportWithSettings(ctx, settings, graycodeconfig.EnginePreflightOptions{VerifyLive: preflightLiveFlag}) + r := hawkconfig.EnginePreflightReportWithSettings(ctx, settings, hawkconfig.EnginePreflightOptions{VerifyLive: preflightLiveFlag}) if prog != nil { prog.CompleteStep(0) prog.Done() @@ -639,15 +639,15 @@ var preflightCmd = &cobra.Command{ } cmd.Println(string(out)) } else { - out := graycodeconfig.FormatEnginePreflight(r) - out += "\n\n" + graycodeconfig.FormatSandboxChecklist(graycodeconfig.EvaluateSandboxChecklist(ctx)) + out := hawkconfig.FormatEnginePreflight(r) + out += "\n\n" + hawkconfig.FormatSandboxChecklist(hawkconfig.EvaluateSandboxChecklist(ctx)) cmd.Println(out) } if !r.Ready { if preflightLiveFlag { return fmt.Errorf("live preflight failed — check the selected provider credential and network access") } - return fmt.Errorf("preflight failed — run graycode and complete /config") + return fmt.Errorf("preflight failed — run hawk and complete /config") } return nil }, @@ -657,10 +657,10 @@ var preflightCmd = &cobra.Command{ // showing a modern old → new transition when the value actually changed. // Settable keys are non-secret (API keys error out before reaching here), // so displaying the prior value cannot leak a secret. -func printConfigSetResult(cmd *cobra.Command, key, newVal string, settings graycodeconfig.Settings) { - oldVal, hadOld := graycodeconfig.SettingValue(settings, key) +func printConfigSetResult(cmd *cobra.Command, key, newVal string, settings hawkconfig.Settings) { + oldVal, hadOld := hawkconfig.SettingValue(settings, key) if hadOld && oldVal != "" && oldVal != newVal { - cmd.Println(auditTint(key, textPrimary) + auditTint(": ", textMuted) + auditTint(oldVal, textMuted) + auditTint(" → ", graycodeColor) + auditTint(newVal, textPrimary) + auditTint(" (updated)", doneGreen)) + cmd.Println(auditTint(key, textPrimary) + auditTint(": ", textMuted) + auditTint(oldVal, textMuted) + auditTint(" → ", hawkColor) + auditTint(newVal, textPrimary) + auditTint(" (updated)", doneGreen)) return } cmd.Println(auditTint("updated ", doneGreen) + auditTint(key, textPrimary)) @@ -674,13 +674,13 @@ var configCmd = &cobra.Command{ switch args[0] { case "get": if len(args) != 2 { - return fmt.Errorf("usage: graycode config get ") + return fmt.Errorf("usage: hawk config get ") } settings, err := loadEffectiveSettings() if err != nil { return err } - value, ok := graycodeconfig.SettingValue(settings, args[1]) + value, ok := hawkconfig.SettingValue(settings, args[1]) if !ok { return fmt.Errorf("unsupported setting key %q", args[1]) } @@ -692,7 +692,7 @@ var configCmd = &cobra.Command{ return nil case "set": if len(args) < 3 { - return fmt.Errorf("usage: graycode config set ") + return fmt.Errorf("usage: hawk config set ") } key := args[1] newVal := strings.Join(args[2:], " ") @@ -700,35 +700,35 @@ var configCmd = &cobra.Command{ if err != nil { return err } - if err := graycodeconfig.SetGlobalSetting(key, newVal); err != nil { + if err := hawkconfig.SetGlobalSetting(key, newVal); err != nil { return err } printConfigSetResult(cmd, key, newVal, settings) return nil case "provider": if len(args) < 2 { - return fmt.Errorf("usage: graycode config provider ") + return fmt.Errorf("usage: hawk config provider ") } newVal := strings.Join(args[1:], " ") settings, err := loadEffectiveSettings() if err != nil { return err } - if err := graycodeconfig.SetGlobalSetting("provider", newVal); err != nil { + if err := hawkconfig.SetGlobalSetting("provider", newVal); err != nil { return err } printConfigSetResult(cmd, "provider", newVal, settings) return nil case "model": if len(args) < 2 { - return fmt.Errorf("usage: graycode config model ") + return fmt.Errorf("usage: hawk config model ") } newVal := strings.Join(args[1:], " ") settings, err := loadEffectiveSettings() if err != nil { return err } - if err := graycodeconfig.SetGlobalSetting("model", newVal); err != nil { + if err := hawkconfig.SetGlobalSetting("model", newVal); err != nil { return err } printConfigSetResult(cmd, "model", newVal, settings) @@ -738,13 +738,13 @@ var configCmd = &cobra.Command{ return nil case "routing-preview": if len(args) < 2 { - return fmt.Errorf("usage: graycode config routing-preview ") + return fmt.Errorf("usage: hawk config routing-preview ") } settings, err := loadEffectiveSettings() if err != nil { return err } - out, err := graycodeconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, strings.Join(args[1:], " ")) + out, err := hawkconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, strings.Join(args[1:], " ")) if err != nil { return err } @@ -765,10 +765,10 @@ var configCmd = &cobra.Command{ var mcpCmd = &cobra.Command{ Use: "mcp", - Short: "Show MCP configuration; run or register graycode as an MCP server", - Long: "With no subcommand, summarizes the MCP servers graycode connects to (consumes).\n" + - " graycode mcp serve — run graycode itself as an MCP server over stdio\n" + - " graycode mcp config — print the JSON block to register graycode in Claude Desktop/Cursor/Windsurf", + Short: "Show MCP configuration; run or register hawk as an MCP server", + Long: "With no subcommand, summarizes the MCP servers hawk connects to (consumes).\n" + + " hawk mcp serve — run hawk itself as an MCP server over stdio\n" + + " hawk mcp config — print the JSON block to register hawk in Claude Desktop/Cursor/Windsurf", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -850,7 +850,7 @@ var ( var researchCmd = &cobra.Command{ Use: "research [flags] ", Short: "Autonomous research loop (Karpathy autoresearch pattern)", - Long: "graycode research --grep '^val_bpb:' --direction lower 'uv run train.py'", + Long: "hawk research --grep '^val_bpb:' --direction lower 'uv run train.py'", RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return fmt.Errorf("metric command is required") @@ -943,9 +943,9 @@ var recoverCmd = &cobra.Command{ and offer to resume them. If a session-id is provided, resume that specific session. Examples: - graycode recover # List interrupted sessions - graycode recover abc123 # Resume specific session - graycode --recover # Auto-resume most recent interrupted session`, + hawk recover # List interrupted sessions + hawk recover abc123 # Resume specific session + hawk --recover # Auto-resume most recent interrupted session`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { s, note, err := session.ResumeSession(args[0]) @@ -962,8 +962,8 @@ Examples: cmd.Println(session.FormatRecoveryCandidates(candidates)) if len(candidates) > 0 { - cmd.Println(auditTint("Resume with: graycode recover ", textMuted)) - cmd.Println(auditTint("Or launch TUI with: graycode --recover", textMuted)) + cmd.Println(auditTint("Resume with: hawk recover ", textMuted)) + cmd.Println(auditTint("Or launch TUI with: hawk --recover", textMuted)) } return nil }, @@ -979,12 +979,12 @@ func resumeRecoveredSession(ctx context.Context, sessionID string) error { } // logMigrateProviderSecretsError surfaces a non-nil error from -// graycodeconfig.MigrateProviderSecrets via the structured logger. +// hawkconfig.MigrateProviderSecrets via the structured logger. // // MigrateProviderSecrets is a one-time hygiene pass that strips API keys // from the on-disk provider.json (a known-bad location — see AGENTS.md). // If it fails, the keys remain in the file and the user must be told so -// they can run graycode /config to move them to the OS keychain. Previously +// they can run hawk /config to move them to the OS keychain. Previously // the error was silently discarded (cmd/root.go:114), so a failure left // the user with secrets in plaintext and no indication that anything was // wrong. @@ -997,7 +997,7 @@ func logMigrateProviderSecretsError(l *logger.Logger, err error) { return } l.Warn( - "provider secret migration failed; API keys may remain in provider.json. Run `graycode /config` to move them to the OS keychain.", + "provider secret migration failed; API keys may remain in provider.json. Run `hawk /config` to move them to the OS keychain.", map[string]interface{}{"err": err.Error()}, ) } diff --git a/cmd/rules.go b/cmd/rules.go index 927f4c842..0c5aeedd7 100644 --- a/cmd/rules.go +++ b/cmd/rules.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/rules" + "github.com/GrayCodeAI/hawk/internal/rules" "github.com/spf13/cobra" ) @@ -18,7 +18,7 @@ var rulesCmd = &cobra.Command{ Use: "rules", Short: "Detect, import, and export AI coding rules between tool formats", Long: `rules manages AI coding rule files across different tools. -Supported formats: graycode, cursor, claudecode, copilot, gemini. +Supported formats: hawk, cursor, claudecode, copilot, gemini. Subcommands: detect Show which AI tool rule files exist in the current directory @@ -56,7 +56,7 @@ var rulesDetectCmd = &cobra.Command{ var rulesImportCmd = &cobra.Command{ Use: "import", - Short: "Import rules from another tool's format into graycode", + Short: "Import rules from another tool's format into hawk", RunE: func(cmd *cobra.Command, args []string) error { if rulesImportFrom == "" { return fmt.Errorf("--from flag is required (e.g. --from cursor)") @@ -73,9 +73,9 @@ var rulesImportCmd = &cobra.Command{ return nil } - // Export to graycode format. - if err := rules.Export(".", rules.FormatGraycode, imported); err != nil { - return fmt.Errorf("export to graycode format failed: %w", err) + // Export to hawk format. + if err := rules.Export(".", rules.FormatHawk, imported); err != nil { + return fmt.Errorf("export to hawk format failed: %w", err) } cmd.Println(auditTint(fmt.Sprintf("Imported %d rule(s) from %s to .agents/rules/.", len(imported), rulesImportFrom), doneGreen)) @@ -88,30 +88,30 @@ var rulesImportCmd = &cobra.Command{ var rulesExportCmd = &cobra.Command{ Use: "export", - Short: "Export graycode rules to another tool's format", + Short: "Export hawk rules to another tool's format", RunE: func(cmd *cobra.Command, args []string) error { if rulesExportTo == "" { return fmt.Errorf("--to flag is required (e.g. --to claudecode)") } - // Read graycode rules. - graycodeRules, err := rules.Import(".", rules.FormatGraycode) + // Read hawk rules. + hawkRules, err := rules.Import(".", rules.FormatHawk) if err != nil { - return fmt.Errorf("read graycode rules failed: %w", err) + return fmt.Errorf("read hawk rules failed: %w", err) } - if len(graycodeRules) == 0 { - cmd.Println(auditTint("No graycode rules found in .agents/rules/. Nothing to export.", textMuted)) + if len(hawkRules) == 0 { + cmd.Println(auditTint("No hawk rules found in .agents/rules/. Nothing to export.", textMuted)) return nil } to := rules.Format(rulesExportTo) - if err := rules.Export(".", to, graycodeRules); err != nil { + if err := rules.Export(".", to, hawkRules); err != nil { return fmt.Errorf("export to %s format failed: %w", rulesExportTo, err) } - cmd.Println(auditTint(fmt.Sprintf("Exported %d rule(s) to %s format.", len(graycodeRules), rulesExportTo), doneGreen)) - for _, r := range graycodeRules { + cmd.Println(auditTint(fmt.Sprintf("Exported %d rule(s) to %s format.", len(hawkRules), rulesExportTo), doneGreen)) + for _, r := range hawkRules { cmd.Println(auditTint(" - "+r.Name, textPrimary)) } return nil diff --git a/cmd/sandbox.go b/cmd/sandbox.go index 6a3777c2d..788dfcc19 100644 --- a/cmd/sandbox.go +++ b/cmd/sandbox.go @@ -4,12 +4,12 @@ import ( "fmt" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/diffsandbox" + "github.com/GrayCodeAI/hawk/internal/diffsandbox" "github.com/spf13/cobra" ) // sandboxInstance is a package-level sandbox for the CLI session. -// In a real integration this would be loaded/shared from the graycode engine; +// In a real integration this would be loaded/shared from the hawk engine; // for now we create a fresh sandbox rooted at the current directory. var ( sandboxInstance *diffsandbox.Sandbox diff --git a/cmd/schema.go b/cmd/schema.go index 8f2812510..1080cb541 100644 --- a/cmd/schema.go +++ b/cmd/schema.go @@ -9,13 +9,13 @@ import ( var schemaCmd = &cobra.Command{ Use: "schema", - Short: "Output JSON schema for graycode settings.json", - Long: "Prints the JSON schema for graycode's settings.json configuration file. Use with $schema for IDE autocompletion.", + Short: "Output JSON schema for hawk settings.json", + Long: "Prints the JSON schema for hawk's settings.json configuration file. Use with $schema for IDE autocompletion.", RunE: func(cmd *cobra.Command, args []string) error { schema := map[string]interface{}{ "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Graycode Settings", - "description": "Configuration for the Graycode AI coding agent", + "title": "Hawk Settings", + "description": "Configuration for the Hawk AI coding agent", "type": "object", "properties": map[string]interface{}{ "model": map[string]interface{}{"type": "string", "description": "Default model (e.g. claude-sonnet-4-20250514)"}, @@ -56,7 +56,7 @@ var schemaCmd = &cobra.Command{ "type": "object", "properties": map[string]interface{}{ "trailer_style": map[string]interface{}{"type": "string", "enum": []string{"none", "assisted-by"}, "default": "none"}, - "generated_with": map[string]interface{}{"type": "boolean", "description": "Append 'Generated with Graycode' to commits"}, + "generated_with": map[string]interface{}{"type": "boolean", "description": "Append 'Generated with Hawk' to commits"}, }, }, "repo_map": map[string]interface{}{"type": "boolean"}, diff --git a/cmd/search.go b/cmd/search.go index 35c6f7416..21bae6d42 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -5,8 +5,8 @@ import ( "os" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/theme" "github.com/spf13/cobra" ) @@ -18,14 +18,14 @@ var ( var searchCmd = &cobra.Command{ Use: "search ", Short: "Search across saved sessions", - Long: `Full-text search across all saved graycode sessions. + Long: `Full-text search across all saved hawk sessions. Searches message content, tool results, and assistant responses. Examples: - graycode search "authentication" - graycode search --limit 5 "database migration" - graycode search "func main"`, + hawk search "authentication" + hawk search --limit 5 "database migration" + hawk search "func main"`, Args: cobra.ExactArgs(1), RunE: runSearch, } diff --git a/cmd/security_verify_governance_cli_test.go b/cmd/security_verify_governance_cli_test.go index 116e1fb43..0b539846d 100644 --- a/cmd/security_verify_governance_cli_test.go +++ b/cmd/security_verify_governance_cli_test.go @@ -8,15 +8,15 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/securitylog" + "github.com/GrayCodeAI/hawk/internal/securitylog" ) -// withTempState runs a cli test body with GRAYCODE_STATE_DIR pointed at a temp dir +// withTempState runs a cli test body with HAWK_STATE_DIR pointed at a temp dir // so the real user state is never touched. func withTempState(t *testing.T, body func(stateDir string)) { t.Helper() dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", dir) + t.Setenv("HAWK_STATE_DIR", dir) body(dir) } diff --git a/cmd/securitylog_cmd.go b/cmd/securitylog_cmd.go index aa2feda31..e9ba6257a 100644 --- a/cmd/securitylog_cmd.go +++ b/cmd/securitylog_cmd.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/graycode-cli/internal/securitylog" + "github.com/GrayCodeAI/hawk/internal/securitylog" "github.com/spf13/cobra" ) @@ -18,13 +18,13 @@ var ( var securitylogCmd = &cobra.Command{ Use: "securitylog", Short: "Inspect the tamper-evident security event log", - Long: `Graycode records security-relevant events (permission denials, approval + Long: `Hawk records security-relevant events (permission denials, approval denials) to an append-only, HMAC-chained log. Entries are linked so that reordering, deletion, or alteration is detectable. - graycode securitylog Show a summary and recent events - graycode securitylog show List logged events - graycode securitylog verify Verify the hash chain has not been tampered with`, + hawk securitylog Show a summary and recent events + hawk securitylog show List logged events + hawk securitylog verify Verify the hash chain has not been tampered with`, RunE: func(cmd *cobra.Command, args []string) error { return runSecuritylogShow(cmd, 20, false) }, diff --git a/cmd/session_export.go b/cmd/session_export.go index ab43a18f5..bbf3acc23 100644 --- a/cmd/session_export.go +++ b/cmd/session_export.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" "github.com/spf13/cobra" ) diff --git a/cmd/session_migrate.go b/cmd/session_migrate.go index 452949eee..94a99cb79 100644 --- a/cmd/session_migrate.go +++ b/cmd/session_migrate.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" "github.com/spf13/cobra" ) diff --git a/cmd/session_migrate_test.go b/cmd/session_migrate_test.go index 3b1e9b205..1596166ca 100644 --- a/cmd/session_migrate_test.go +++ b/cmd/session_migrate_test.go @@ -9,7 +9,7 @@ import ( func TestRunSessionMigrate(t *testing.T) { state := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", state) + t.Setenv("HAWK_STATE_DIR", state) id := "migrate-cmd-test" sessDir := filepath.Join(state, "sessions") if err := os.MkdirAll(sessDir, 0o700); err != nil { @@ -40,7 +40,7 @@ func TestRunSessionMigrate(t *testing.T) { func TestRunSessionMigrateJSON(t *testing.T) { state := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", state) + t.Setenv("HAWK_STATE_DIR", state) id := "migrate-cmd-json" sessDir := filepath.Join(state, "sessions") _ = os.MkdirAll(sessDir, 0o700) diff --git a/cmd/session_share.go b/cmd/session_share.go index bfd766a65..4bdac9401 100644 --- a/cmd/session_share.go +++ b/cmd/session_share.go @@ -9,12 +9,12 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" "github.com/spf13/cobra" ) // sessionShareCmd shares a session. It uploads the export to a configured -// hosted endpoint (GRAYCODE_SHARE_URL) when one is set, else falls back to the +// hosted endpoint (HAWK_SHARE_URL) when one is set, else falls back to the // local content-derived deeplink + export path. var sessionShareCmd = &cobra.Command{ Use: "share [session-id]", @@ -40,7 +40,7 @@ func runSessionShare(_ *cobra.Command, args []string) error { return fmt.Errorf("export session: %w", err) } - host := strings.TrimSpace(os.Getenv("GRAYCODE_SHARE_URL")) + host := strings.TrimSpace(os.Getenv("HAWK_SHARE_URL")) if host != "" { url, err := uploadShare(context.Background(), host, s.ID, data) if err != nil { @@ -56,7 +56,7 @@ func runSessionShare(_ *cobra.Command, args []string) error { return fmt.Errorf("could not generate a share link for session %q", s.ID) } fmt.Printf("Share deeplink: %s\n", link) - fmt.Printf("(Set GRAYCODE_SHARE_URL to a Graycode Cloud share endpoint for a hosted URL.)\n") + fmt.Printf("(Set HAWK_SHARE_URL to a Hawk Cloud share endpoint for a hosted URL.)\n") return nil } diff --git a/cmd/session_sync.go b/cmd/session_sync.go index d1268b575..2dff22d12 100644 --- a/cmd/session_sync.go +++ b/cmd/session_sync.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" ) func (m *chatModel) ensureDeferredSystemContext() { @@ -32,12 +32,12 @@ func explicitSelection(ctx context.Context) (provider, model string) { if ctx == nil { ctx = context.Background() } - return strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)), strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) + return strings.TrimSpace(hawkconfig.ActiveGateway(ctx)), strings.TrimSpace(hawkconfig.ActiveModel(ctx)) } -// syncSessionFromPersistedSelection copies explicit graycode-router provider.json +// syncSessionFromPersistedSelection copies explicit eyrie provider.json // selection into the live session when the session fields are empty. -// It intentionally avoids runtime defaults so Graycode can preserve the +// It intentionally avoids runtime defaults so Hawk can preserve the // "gateway selected, model still missing" setup state. func syncSessionFromPersistedSelection(sess *engine.Session) { if sess == nil { diff --git a/cmd/session_sync_test.go b/cmd/session_sync_test.go index 2c5191994..c78046d46 100644 --- a/cmd/session_sync_test.go +++ b/cmd/session_sync_test.go @@ -5,26 +5,26 @@ import ( "strings" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestSyncSessionFromPersistedSelection_FillsEmptySessionModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") - _ = graycodeconfig.SetActiveModel(ctx, "gpt-4o") + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.SetActiveProvider(ctx, "openrouter") + _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") sess := engine.NewSession("", "", "test", nil) syncSessionFromPersistedSelection(sess) @@ -38,20 +38,20 @@ func TestSyncSessionFromPersistedSelection_FillsEmptySessionModel(t *testing.T) } func TestEnsureSessionReadyForChat_UsesPersistedModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") - _ = graycodeconfig.SetActiveModel(ctx, "gpt-4o") + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.SetActiveProvider(ctx, "openrouter") + _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") m := &chatModel{session: engine.NewSession("", "", "test", nil)} if err := m.ensureSessionReadyForChat(); err != nil { @@ -63,18 +63,18 @@ func TestEnsureSessionReadyForChat_UsesPersistedModel(t *testing.T) { } func TestEnsureSessionReadyForChat_NoModel(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.ClearActiveSelection(ctx) + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.ClearActiveSelection(ctx) m := &chatModel{session: engine.NewSession("", "", "test", nil)} if err := m.ensureSessionReadyForChat(); err == nil { @@ -83,20 +83,20 @@ func TestEnsureSessionReadyForChat_NoModel(t *testing.T) { } func TestEnsureSessionReadyForChat_AppliesDeferredSystemContextOnce(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - graycodeconfig.InvalidateConfigUICache() - _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") - _ = graycodeconfig.SetActiveModel(ctx, "gpt-4o") + hawkconfig.InvalidateConfigUICache() + _ = hawkconfig.SetActiveProvider(ctx, "openrouter") + _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") m := &chatModel{ session: engine.NewSession("", "", "base", nil), diff --git a/cmd/skills_cmd.go b/cmd/skills_cmd.go index 70d3f78cc..6c78bb2e7 100644 --- a/cmd/skills_cmd.go +++ b/cmd/skills_cmd.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/tool" "github.com/spf13/cobra" ) diff --git a/cmd/skills_create.go b/cmd/skills_create.go index 09d3dbe49..cbf7ad991 100644 --- a/cmd/skills_create.go +++ b/cmd/skills_create.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/types" "github.com/spf13/cobra" ) @@ -30,10 +30,10 @@ func runSkillsCreate(_ *cobra.Command, args []string) error { return err } model, provider := effectiveModelAndProvider(settings) - sess := newGraycodeSession(settings, provider, model, "You are a skill author.", tool.NewRegistry()) + sess := newHawkSession(settings, provider, model, "You are a skill author.", tool.NewRegistry()) prompt := plugin.BuildNewSkillPrompt(desc) - resp, err := sess.Chat(context.Background(), []types.GraycodeRouterMessage{ + resp, err := sess.Chat(context.Background(), []types.EyrieMessage{ {Role: "user", Content: prompt}, }, types.ChatOptions{Model: model, MaxTokens: 4096}) if err != nil { diff --git a/cmd/skills_curator_cmd.go b/cmd/skills_curator_cmd.go index af0e24e69..4ae40a54a 100644 --- a/cmd/skills_curator_cmd.go +++ b/cmd/skills_curator_cmd.go @@ -5,14 +5,14 @@ import ( "image/color" "path/filepath" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/skillcurator" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/intelligence/skillcurator" + "github.com/GrayCodeAI/hawk/internal/storage" "github.com/spf13/cobra" ) // skillsCuratorCmd exposes the background skill curator (adopted from Hermes // Agent) as a CLI surface: review/archive/pin/unpin over agent-created skills -// in ~/.graycode/skills. +// in ~/.hawk/skills. var skillsCuratorCmd = &cobra.Command{ Use: "curator [command]", Short: "Skill lifecycle curation (status, run, pin, unpin, archive)", diff --git a/cmd/sleep_prevent.go b/cmd/sleep_prevent.go index b95761784..41957077c 100644 --- a/cmd/sleep_prevent.go +++ b/cmd/sleep_prevent.go @@ -43,7 +43,7 @@ func preventSleepLinux() func() { "systemd-inhibit", "--mode=block", "--what=idle:sleep", - "--who=graycode", + "--who=hawk", "--why=Agent turn in progress", "sleep", "86400") if err := cmd.Start(); err != nil { diff --git a/cmd/snapshot_cmd.go b/cmd/snapshot_cmd.go index 86c633146..b035d2f7e 100644 --- a/cmd/snapshot_cmd.go +++ b/cmd/snapshot_cmd.go @@ -7,7 +7,7 @@ import ( "strings" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/snapshot" + "github.com/GrayCodeAI/hawk/internal/snapshot" "github.com/spf13/cobra" ) @@ -51,7 +51,7 @@ func (m chatModel) showSnapshotList() (tea.Model, tea.Cmd) { history, err := t.History(15) if err != nil || len(history) == 0 { - m.messages = append(m.messages, displayMsg{role: "system", content: "No snapshots yet. Snapshots are created automatically when graycode modifies files."}) + m.messages = append(m.messages, displayMsg{role: "system", content: "No snapshots yet. Snapshots are created automatically when hawk modifies files."}) return m, nil } @@ -123,7 +123,7 @@ func (m chatModel) diffSnapshot(hash string) (tea.Model, tea.Cmd) { var snapshotCmd = &cobra.Command{ Use: "snapshot", Short: "Manage file snapshots (undo any change)", - Long: "View, restore, and diff file snapshots. Graycode automatically snapshots every file modification.", + Long: "View, restore, and diff file snapshots. Hawk automatically snapshots every file modification.", } var snapshotListCmd = &cobra.Command{ diff --git a/cmd/spec_picker.go b/cmd/spec_picker.go index d320784aa..35e233c68 100644 --- a/cmd/spec_picker.go +++ b/cmd/spec_picker.go @@ -7,7 +7,7 @@ import ( "charm.land/bubbles/v2/textinput" tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/spec_picker_test.go b/cmd/spec_picker_test.go index e8aa31abb..3fe5a8ff1 100644 --- a/cmd/spec_picker_test.go +++ b/cmd/spec_picker_test.go @@ -4,7 +4,7 @@ import ( "testing" tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestSpecPicker_HasSevenActions(t *testing.T) { diff --git a/cmd/spinner_wave.go b/cmd/spinner_wave.go index aa7378092..98d82a3e8 100644 --- a/cmd/spinner_wave.go +++ b/cmd/spinner_wave.go @@ -5,12 +5,12 @@ import ( "strings" "unicode/utf8" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // spinnerWaveColors — 20 distinct hues; the wave flows glyph → verb → ▪▫▫. var spinnerWaveColors = [20][3]int{ - {255, 215, 0}, // Talon Gold — anchor the wave in Graycode's brand color. + {255, 215, 0}, // Talon Gold — anchor the wave in Hawk's brand color. {255, 60, 60}, {255, 100, 80}, {255, 150, 60}, @@ -46,7 +46,7 @@ func ansiSpinnerWaveColor(index int) string { // renderSpinnerWaveLine paints glyph + verb + ▪▫▫ as one flowing strip (20 colors). func renderSpinnerWaveLine(glyph, verb string, wavePhase, dotPhase int) string { verbRunes := utf8.RuneCountInString(verb) - total := 1 + verbRunes + 1 + graycodeTypingDots + total := 1 + verbRunes + 1 + hawkTypingDots if verb == "" { total = 1 } @@ -66,10 +66,10 @@ func renderSpinnerWaveLine(glyph, verb string, wavePhase, dotPhase int) string { } b.WriteString(renderSpinnerWaveSlot(' ', wavePhase+pos, head == pos, false)) pos++ - for i := 0; i < graycodeTypingDots; i++ { + for i := 0; i < hawkTypingDots; i++ { g := icons.CircleOutline() bold := false - if i == dotPhase%graycodeTypingDots { + if i == dotPhase%hawkTypingDots { g = icons.CircleFilled() bold = true } diff --git a/cmd/spinner_wave_test.go b/cmd/spinner_wave_test.go index 48ac5aedd..dde12f268 100644 --- a/cmd/spinner_wave_test.go +++ b/cmd/spinner_wave_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // frameContainsSpinnerWave reports whether a rendered frame contains the @@ -17,7 +17,7 @@ func frameContainsSpinnerWave(s string) bool { } func TestSpinnerWave_AdvancesOnTick(t *testing.T) { - s := NewBrailleSpinner(SpinnerGraycode, "Hi") + s := NewBrailleSpinner(SpinnerHawk, "Hi") f1 := s.Frame() s.Tick() f2 := s.Frame() @@ -57,7 +57,7 @@ func TestSpinnerWave_FlowsThroughGlyphVerbAndDots(t *testing.T) { } func TestSpinnerWave_GlyphUsesWaveColor(t *testing.T) { - s := NewBrailleSpinner(SpinnerGraycode, "Go") + s := NewBrailleSpinner(SpinnerHawk, "Go") f := s.Frame() if !strings.Contains(f, "◐") && !strings.Contains(f, "◓") && !strings.Contains(f, "◑") && !strings.Contains(f, "◒") { t.Fatalf("expected compass spinner glyph, got %q", f) diff --git a/cmd/stats.go b/cmd/stats.go index 329890e7a..471bffd98 100644 --- a/cmd/stats.go +++ b/cmd/stats.go @@ -8,7 +8,7 @@ import ( "strings" "time" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + analytics "github.com/GrayCodeAI/hawk/internal/observability" "github.com/spf13/cobra" ) @@ -85,7 +85,7 @@ func runStats(cmd *cobra.Command, args []string) error { if len(filtered) == 0 { cmd.Println(auditTint("No session data found for the specified time period.", textMuted)) - cmd.Println(auditTint("Sessions are recorded automatically when you use graycode.", textMuted)) + cmd.Println(auditTint("Sessions are recorded automatically when you use hawk.", textMuted)) return nil } @@ -180,7 +180,7 @@ func printStatsText(cmd *cobra.Command, out *statsOutput) { _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, "══════════════════════════════════════════════════\n") - _, _ = fmt.Fprintf(w, " %s\n", auditTint(fmt.Sprintf("Graycode Usage Statistics (%s)", out.Period), graycodeColor)) + _, _ = fmt.Fprintf(w, " %s\n", auditTint(fmt.Sprintf("Hawk Usage Statistics (%s)", out.Period), hawkColor)) _, _ = fmt.Fprintf(w, "══════════════════════════════════════════════════\n") // Overview section diff --git a/cmd/status_snapshot.go b/cmd/status_snapshot.go index b3a3bcaec..e149a907b 100644 --- a/cmd/status_snapshot.go +++ b/cmd/status_snapshot.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/plugin" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/status" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/status" "github.com/spf13/cobra" ) @@ -35,11 +35,11 @@ var statusCmd = &cobra.Command{ func buildStatusSnapshot() status.Snapshot { snapshot := status.New() - snapshot.GraycodeVersion = version + snapshot.HawkVersion = version snapshot.Workspace = status.Workspace() snapshot.GitBranch = engine.InspectGitBranch("").Branch - settings := graycodeconfig.LoadGlobalSettings() - selection := graycodeconfig.EffectiveSelection(context.Background(), graycodeconfig.SelectionOptions{}) + settings := hawkconfig.LoadGlobalSettings() + selection := hawkconfig.EffectiveSelection(context.Background(), hawkconfig.SelectionOptions{}) snapshot.Model = strings.TrimSpace(selection.Model) snapshot.Provider = strings.TrimSpace(selection.Provider) if snapshot.Model == "" { @@ -89,7 +89,7 @@ func formatStatusSnapshot(s status.Snapshot) string { return fmt.Sprintf("%s: %s\n", auditTint(label, textMuted), auditTint(val, textPrimary)) } var b strings.Builder - b.WriteString(auditTint("Graycode status", graycodeColor) + "\n") + b.WriteString(auditTint("Hawk status", hawkColor) + "\n") b.WriteString(line("Schema", s.SchemaVersion)) b.WriteString(line("Workspace", s.Workspace)) b.WriteString(line("Git branch", s.GitBranch)) diff --git a/cmd/status_snapshot_test.go b/cmd/status_snapshot_test.go index f0fbdd354..ed8ad8d0b 100644 --- a/cmd/status_snapshot_test.go +++ b/cmd/status_snapshot_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/sandbox" ) func TestFormatStatusSnapshot(t *testing.T) { snapshot := buildStatusSnapshot() formatted := formatStatusSnapshot(snapshot) - for _, expected := range []string{"Graycode status", "Schema: 1", "Secrets redacted: true"} { + for _, expected := range []string{"Hawk status", "Schema: 1", "Secrets redacted: true"} { if !strings.Contains(formatted, expected) { t.Errorf("status output missing %q: %s", expected, formatted) } diff --git a/cmd/statusbar.go b/cmd/statusbar.go index 3c47d00fe..03d3c4082 100644 --- a/cmd/statusbar.go +++ b/cmd/statusbar.go @@ -12,9 +12,9 @@ import ( "golang.org/x/text/language" "golang.org/x/text/message" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/engine/git" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine/git" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) var ( diff --git a/cmd/statusbar_test.go b/cmd/statusbar_test.go index bc92e3a67..7c87259dc 100644 --- a/cmd/statusbar_test.go +++ b/cmd/statusbar_test.go @@ -6,7 +6,7 @@ import ( "time" lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) func TestRenderStatusBar_SignatureExists(t *testing.T) { @@ -100,8 +100,8 @@ func TestRenderStatusBarLeft_UsesCachedState(t *testing.T) { func TestShortenHomePath(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) - got := shortenHomePath(home + "/project/graycode") - if got != "~/project/graycode" { + got := shortenHomePath(home + "/project/hawk") + if got != "~/project/hawk" { t.Fatalf("got %q", got) } } diff --git a/cmd/storage_policy_test.go b/cmd/storage_policy_test.go index 22f14e6b3..20025a6d8 100644 --- a/cmd/storage_policy_test.go +++ b/cmd/storage_policy_test.go @@ -7,10 +7,10 @@ import ( "testing" ) -func TestStoragePolicyHelpersDoNotCreateProjectGraycode(t *testing.T) { +func TestStoragePolicyHelpersDoNotCreateProjectHawk(t *testing.T) { project := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(t.TempDir(), "state")) - t.Setenv("GRAYCODE_CACHE_DIR", filepath.Join(t.TempDir(), "cache")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("HAWK_CACHE_DIR", filepath.Join(t.TempDir(), "cache")) oldwd, err := os.Getwd() if err != nil { t.Fatal(err) @@ -21,13 +21,13 @@ func TestStoragePolicyHelpersDoNotCreateProjectGraycode(t *testing.T) { } planPath := resolvePlanPath("demo") - if strings.Contains(planPath, filepath.Join(project, ".graycode")) { - t.Fatalf("resolvePlanPath leaked project .graycode: %q", planPath) + if strings.Contains(planPath, filepath.Join(project, ".hawk")) { + t.Fatalf("resolvePlanPath leaked project .hawk: %q", planPath) } saveInputHistory([]string{"hello"}) recordTipShown("slash-help") - if _, err := os.Stat(filepath.Join(project, ".graycode")); !os.IsNotExist(err) { - t.Fatalf("normal storage helpers created project .graycode, stat err=%v", err) + if _, err := os.Stat(filepath.Join(project, ".hawk")); !os.IsNotExist(err) { + t.Fatalf("normal storage helpers created project .hawk, stat err=%v", err) } } diff --git a/cmd/swift.go b/cmd/swift.go index 194d8a28f..ad4394e86 100644 --- a/cmd/swift.go +++ b/cmd/swift.go @@ -4,9 +4,9 @@ import ( swiftcli "github.com/GrayCodeAI/swift/cli" ) -// swift is a sibling library consumed by graycode, not a standalone product. Its full +// swift is a sibling library consumed by hawk, not a standalone product. Its full // command tree is built by swiftcli.NewRootCmd() (Use: "swift"), so mounting it here -// surfaces every swift feature under `graycode swift ...` without porting any code. +// surfaces every swift feature under `hawk swift ...` without porting any code. func init() { rootCmd.AddCommand(swiftcli.NewRootCmd()) } diff --git a/cmd/swift_correlation.go b/cmd/swift_correlation.go index 6ce05c1b5..1742fd074 100644 --- a/cmd/swift_correlation.go +++ b/cmd/swift_correlation.go @@ -31,7 +31,7 @@ type swiftCLICorrelationResolver struct{} type swiftCorrelation struct { SchemaVersion string `json:"schema_version"` - GraycodeSessionID string `json:"graycode_session_id"` + HawkSessionID string `json:"hawk_session_id"` CheckpointLookupComplete bool `json:"checkpoint_lookup_complete"` Matches []swiftCorrelationMatch `json:"matches"` } @@ -46,11 +46,11 @@ type swiftCorrelationMatch struct { func (swiftCLICorrelationResolver) Resolve( ctx context.Context, - graycodeSessionID string, + hawkSessionID string, ) (swiftCorrelation, error) { - graycodeSessionID = strings.TrimSpace(graycodeSessionID) - if graycodeSessionID == "" { - return swiftCorrelation{}, fmt.Errorf("resolve Swift correlation: Graycode session ID is required") + hawkSessionID = strings.TrimSpace(hawkSessionID) + if hawkSessionID == "" { + return swiftCorrelation{}, fmt.Errorf("resolve Swift correlation: Hawk session ID is required") } var stdout, stderr cappedBuffer @@ -59,7 +59,7 @@ func (swiftCLICorrelationResolver) Resolve( root := swiftcli.NewRootCmd() root.SetOut(&stdout) root.SetErr(&stderr) - root.SetArgs([]string{"graph", "correlation", "--graycode-session", graycodeSessionID}) + root.SetArgs([]string{"graph", "correlation", "--hawk-session", hawkSessionID}) // Internal composition must not emit telemetry or launch an asynchronous // version check after the read-only lookup completes. root.PersistentPostRun = nil @@ -68,10 +68,10 @@ func (swiftCLICorrelationResolver) Resolve( return swiftCorrelation{}, fmt.Errorf("resolve Swift correlation through CLI: %w", err) } - return decodeSwiftCorrelation(stdout.Bytes(), graycodeSessionID) + return decodeSwiftCorrelation(stdout.Bytes(), hawkSessionID) } -func decodeSwiftCorrelation(payload []byte, graycodeSessionID string) (swiftCorrelation, error) { +func decodeSwiftCorrelation(payload []byte, hawkSessionID string) (swiftCorrelation, error) { decoder := json.NewDecoder(bytes.NewReader(payload)) decoder.DisallowUnknownFields() var correlation swiftCorrelation @@ -81,13 +81,13 @@ func decodeSwiftCorrelation(payload []byte, graycodeSessionID string) (swiftCorr if err := requireJSONEOF(decoder); err != nil { return swiftCorrelation{}, err } - if err := normalizeSwiftCorrelation(&correlation, graycodeSessionID); err != nil { + if err := normalizeSwiftCorrelation(&correlation, hawkSessionID); err != nil { return swiftCorrelation{}, err } return correlation, nil } -func normalizeSwiftCorrelation(correlation *swiftCorrelation, graycodeSessionID string) error { +func normalizeSwiftCorrelation(correlation *swiftCorrelation, hawkSessionID string) error { if correlation == nil { return fmt.Errorf("validate Swift correlation: response is nil") } @@ -97,8 +97,8 @@ func normalizeSwiftCorrelation(correlation *swiftCorrelation, graycodeSessionID correlation.SchemaVersion, ) } - if correlation.GraycodeSessionID != graycodeSessionID { - return fmt.Errorf("validate Swift correlation: Graycode session identity mismatch") + if correlation.HawkSessionID != hawkSessionID { + return fmt.Errorf("validate Swift correlation: Hawk session identity mismatch") } seenSessions := make(map[string]struct{}, len(correlation.Matches)) diff --git a/cmd/swift_correlation_test.go b/cmd/swift_correlation_test.go index d4652f06d..b16cc513c 100644 --- a/cmd/swift_correlation_test.go +++ b/cmd/swift_correlation_test.go @@ -13,7 +13,7 @@ func TestNormalizeSwiftCorrelationDeterministicAndDeduplicated(t *testing.T) { correlation := swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - GraycodeSessionID: "graycode-session", + HawkSessionID: "hawk-session", CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{ { @@ -26,7 +26,7 @@ func TestNormalizeSwiftCorrelationDeterministicAndDeduplicated(t *testing.T) { }, }, } - if err := normalizeSwiftCorrelation(&correlation, "graycode-session"); err != nil { + if err := normalizeSwiftCorrelation(&correlation, "hawk-session"); err != nil { t.Fatalf("normalizeSwiftCorrelation() error = %v", err) } if correlation.Matches[0].SwiftSessionID != "swift-alpha" { @@ -44,7 +44,7 @@ func TestNormalizeSwiftCorrelationRejectsUntrustedIdentityData(t *testing.T) { valid := func() swiftCorrelation { return swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - GraycodeSessionID: "graycode-session", + HawkSessionID: "hawk-session", CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{{ SwiftSessionID: "swift-session", @@ -63,9 +63,9 @@ func TestNormalizeSwiftCorrelationRejectsUntrustedIdentityData(t *testing.T) { }, }, { - name: "graycode identity", + name: "hawk identity", mutate: func(value *swiftCorrelation) { - value.GraycodeSessionID = "other-session" + value.HawkSessionID = "other-session" }, }, { @@ -93,7 +93,7 @@ func TestNormalizeSwiftCorrelationRejectsUntrustedIdentityData(t *testing.T) { t.Parallel() value := valid() test.mutate(&value) - if err := normalizeSwiftCorrelation(&value, "graycode-session"); err == nil { + if err := normalizeSwiftCorrelation(&value, "hawk-session"); err == nil { t.Fatal("normalizeSwiftCorrelation() error = nil") } }) @@ -105,14 +105,14 @@ func TestNormalizeSwiftCorrelationDropsUnverifiedCheckpointList(t *testing.T) { correlation := swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - GraycodeSessionID: "graycode-session", + HawkSessionID: "hawk-session", CheckpointLookupComplete: false, Matches: []swiftCorrelationMatch{{ SwiftSessionID: "swift-session", CheckpointIDs: []string{"abc123def456"}, }}, } - if err := normalizeSwiftCorrelation(&correlation, "graycode-session"); err != nil { + if err := normalizeSwiftCorrelation(&correlation, "hawk-session"); err != nil { t.Fatalf("normalizeSwiftCorrelation() error = %v", err) } if len(correlation.Matches) != 1 || correlation.Matches[0].CheckpointIDs == nil || @@ -126,7 +126,7 @@ func TestDecodeSwiftCorrelationAcceptsCompleteSwiftV1Envelope(t *testing.T) { payload := []byte(`{ "schema_version": "swift.correlation/v1", - "graycode_session_id": "graycode-session", + "hawk_session_id": "hawk-session", "checkpoint_lookup_complete": true, "matches": [{ "swift_session_id": "swift-session", @@ -136,7 +136,7 @@ func TestDecodeSwiftCorrelationAcceptsCompleteSwiftV1Envelope(t *testing.T) { "phase": "ENDED" }] }`) - correlation, err := decodeSwiftCorrelation(payload, "graycode-session") + correlation, err := decodeSwiftCorrelation(payload, "hawk-session") if err != nil { t.Fatalf("decodeSwiftCorrelation() error = %v", err) } diff --git a/cmd/swift_report.go b/cmd/swift_report.go index ea1840731..5a012f2b0 100644 --- a/cmd/swift_report.go +++ b/cmd/swift_report.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/permissions" - "github.com/GrayCodeAI/graycode-cli/internal/swift" + "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/permissions" + "github.com/GrayCodeAI/hawk/internal/swift" "github.com/spf13/cobra" ) @@ -59,7 +59,7 @@ func runSwiftReport(cmd *cobra.Command, _ []string) error { Platform: runtime.GOOS + "/" + runtime.GOARCH, Model: config.ActiveModel(context.Background()), Workspace: cwd, - SessionID: os.Getenv("GRAYCODE_SESSION_ID"), + SessionID: os.Getenv("HAWK_SESSION_ID"), PID: os.Getpid(), Terminal: terminalSize(), Env: selectedEnv(), diff --git a/cmd/tabcomplete.go b/cmd/tabcomplete.go index cdd6f9b2e..dcd95fc07 100644 --- a/cmd/tabcomplete.go +++ b/cmd/tabcomplete.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/tool" ) // lruCache is a simple fixed-size LRU cache for directory listing results. diff --git a/cmd/tabcomplete_test.go b/cmd/tabcomplete_test.go index 1f88eb697..3f1ba84b0 100644 --- a/cmd/tabcomplete_test.go +++ b/cmd/tabcomplete_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestFilePathCompletions_CurrentDir(t *testing.T) { diff --git a/cmd/tape.go b/cmd/tape.go index d22343353..1973dc9fb 100644 --- a/cmd/tape.go +++ b/cmd/tape.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" + "github.com/GrayCodeAI/hawk/internal/terminal/tape" "github.com/spf13/cobra" ) @@ -47,7 +47,7 @@ overwrites, and write a sidecar meta.json with the content hash and commit ID.`, func init() { tapeStatusCmd.Flags().BoolVar(&tapeStatusJSON, "json", false, "output status as JSON") tapeCommitCmd.Flags().StringVar(&tapeCommitName, "name", "", "commit name (default: source basename)") - tapeCommitCmd.Flags().StringVar(&tapeCommitDir, "dir", "", "commit directory (default: GRAYCODE_TAPES_DIR or user config dir)") + tapeCommitCmd.Flags().StringVar(&tapeCommitDir, "dir", "", "commit directory (default: HAWK_TAPES_DIR or user config dir)") tapeCmd.AddCommand(tapeStatusCmd, tapeCommitCmd) rootCmd.AddCommand(tapeCmd) } diff --git a/cmd/taste.go b/cmd/taste.go index 145499ff9..f300f9463 100644 --- a/cmd/taste.go +++ b/cmd/taste.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/feature/taste" + "github.com/GrayCodeAI/hawk/internal/feature/taste" "github.com/spf13/cobra" ) @@ -36,8 +36,8 @@ var tastePushCmd = &cobra.Command{ with teammates or used across machines. Examples: - graycode taste push # Export to stdout - graycode taste push --file team.json # Export to file`, + hawk taste push # Export to stdout + hawk taste push --file team.json # Export to file`, RunE: runTastePush, } @@ -48,8 +48,8 @@ var tastePullCmd = &cobra.Command{ will be merged with your existing profile (not replaced). Examples: - graycode taste pull team.json - cat team.json | graycode taste pull -`, + hawk taste pull team.json + cat team.json | hawk taste pull -`, Args: cobra.ExactArgs(1), RunE: runTastePull, } diff --git a/cmd/terminal_notify.go b/cmd/terminal_notify.go index a29b9c639..074f97a3e 100644 --- a/cmd/terminal_notify.go +++ b/cmd/terminal_notify.go @@ -46,7 +46,7 @@ func sendTerminalNotification(title, body string) { _, _ = fmt.Fprintf(os.Stderr, "\033]9;%s\007", body) case "kitty": // Kitty OSC 99 notification - _, _ = fmt.Fprintf(os.Stderr, "\033]99;i=graycode:d=0;%s\033\\", body) + _, _ = fmt.Fprintf(os.Stderr, "\033]99;i=hawk:d=0;%s\033\\", body) case "ghostty": // Ghostty OSC 777 notification _, _ = fmt.Fprintf(os.Stderr, "\033]777;notify;%s;%s\033\\", title, body) diff --git a/cmd/textutil_shim.go b/cmd/textutil_shim.go index f3d5eb79c..51fcb7356 100644 --- a/cmd/textutil_shim.go +++ b/cmd/textutil_shim.go @@ -1,6 +1,6 @@ package cmd -import "github.com/GrayCodeAI/graycode-cli/internal/textutil" +import "github.com/GrayCodeAI/hawk/internal/textutil" // truncateWithEllipsis truncates s to at most max runes, appending "..." when // content is dropped. Rune-safe: never splits a multi-byte character. diff --git a/cmd/theme.go b/cmd/theme.go index abfb4ad86..1f5976d05 100644 --- a/cmd/theme.go +++ b/cmd/theme.go @@ -1,11 +1,11 @@ package cmd -// theme.go — the single source of truth for graycode's visual identity. +// theme.go — the single source of truth for hawk's visual identity. // // All color constants (24-bit RGB via lipgloss), raw ANSI escape codes // (used by the spinner line), and glyph/icon constants live here. Every // other file in the package references these names instead of repeating -// hex codes or magic strings. To rebrand graycode, edit this file; to audit +// hex codes or magic strings. To rebrand hawk, edit this file; to audit // what's used where, grep this file. // // Organization: @@ -24,17 +24,17 @@ import ( lipgloss "charm.land/lipgloss/v2" "charm.land/lipgloss/v2/compat" - internaltheme "github.com/GrayCodeAI/graycode-cli/internal/theme" + internaltheme "github.com/GrayCodeAI/hawk/internal/theme" ) // --------------------------------------------------------------------------- // 1. Brand & identity // --------------------------------------------------------------------------- -// graycodeColor is Talon Gold (#FFD700). Used for the GRAYCODE wordmark, graycode, +// hawkColor is Talon Gold (#FFD700). Used for the HAWK wordmark, hawk, // ⛬ assistant prefix, prompt arrow, cursor, exit prompt, and // any place that should "speak" as the brand. -var graycodeColor = lipgloss.Color(internaltheme.BrandPrimary) +var hawkColor = lipgloss.Color(internaltheme.BrandPrimary) // --------------------------------------------------------------------------- // 2. UI state @@ -197,7 +197,7 @@ const ( // --------------------------------------------------------------------------- // 10. Icons & glyphs // -// Graycode's icon registry lives in internal/ui/icons. Every call site +// Hawk's icon registry lives in internal/ui/icons. Every call site // references icons.ChevronRight() / icons.Robot() / etc. directly; this // file no longer holds any glyph constants. The audit test in // internal/testaudit fails CI if any non-ASCII literal appears in @@ -240,7 +240,7 @@ func ApplyTheme(name string) { p := entry.Palette // 1. Brand — fixed across themes; palette accents remain theme-specific. - graycodeColor = lipgloss.Color(internaltheme.BrandPrimary) + hawkColor = lipgloss.Color(internaltheme.BrandPrimary) // 2. Semantic feedback. successTeal = lipgloss.Color(p.Green) @@ -298,12 +298,12 @@ func ApplyTheme(name string) { // unsynchronized from View. func refreshThemeStyles() { // markdown.go - mdH1Style = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true).Underline(true) + mdH1Style = lipgloss.NewStyle().Foreground(hawkColor).Bold(true).Underline(true) mdH2Style = lipgloss.NewStyle().Foreground(successTeal).Bold(true) mdH3Style = lipgloss.NewStyle().Foreground(infoSky).Bold(true) mdH4Style = lipgloss.NewStyle().Foreground(costViolet).Bold(true) mdHeaderStyle = lipgloss.NewStyle().Foreground(textPrimary).Bold(true) - mdBoldStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + mdBoldStyle = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) mdInlineCodeStyle = lipgloss.NewStyle().Foreground(infoSky) mdCodeBlockStyle = lipgloss.NewStyle().Background(bgCode) mdCodeLabelStyle = lipgloss.NewStyle().Foreground(textDisabled).Background(bgCode) @@ -322,15 +322,15 @@ func refreshThemeStyles() { toolDimStyle = lipgloss.NewStyle().Foreground(textDisabled) slashCmdStyle = lipgloss.NewStyle().Foreground(textDisabled) slashDescStyle = lipgloss.NewStyle().Foreground(textDisabled) - slashSelCmdStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) - slashSelDescStyle = lipgloss.NewStyle().Foreground(graycodeColor) + slashSelCmdStyle = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) + slashSelDescStyle = lipgloss.NewStyle().Foreground(hawkColor) inputBorderStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false, true, false).BorderForeground(borderDim) containerErrStyle = lipgloss.NewStyle().Foreground(errorCoral) containerLabelStyle = lipgloss.NewStyle().Foreground(containerBlue) dimColor = textDisabled // agent_grid.go - agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(graycodeColor).Padding(0, 1) + agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(hawkColor).Padding(0, 1) agentDoneStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(doneGreen).Padding(0, 1) agentFailStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(errorCoral).Padding(0, 1) agentIdleStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(textDisabled).Padding(0, 1) @@ -338,5 +338,5 @@ func refreshThemeStyles() { agentStatusStyle = lipgloss.NewStyle().Foreground(textMuted) // chat_scrollbar.go - scrollbarThumbStyle = lipgloss.NewStyle().Foreground(graycodeColor) + scrollbarThumbStyle = lipgloss.NewStyle().Foreground(hawkColor) } diff --git a/cmd/theme_picker.go b/cmd/theme_picker.go index e101a9874..3e6026a32 100644 --- a/cmd/theme_picker.go +++ b/cmd/theme_picker.go @@ -7,13 +7,13 @@ import ( tea "charm.land/bubbletea/v2" lipgloss "charm.land/lipgloss/v2" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - internaltheme "github.com/GrayCodeAI/graycode-cli/internal/theme" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + internaltheme "github.com/GrayCodeAI/hawk/internal/theme" ) // detectAutoIsDark returns whether "auto" theme currently resolves to dark. func detectAutoIsDark() bool { - settings := graycodeconfig.LoadGlobalSettings() + settings := hawkconfig.LoadGlobalSettings() return settings.Theme == "auto" || settings.Theme == "system" || internaltheme.DetectOSTheme() != "light" } @@ -152,7 +152,7 @@ func (tp *ThemePicker) View() tea.View { } titleStyle := lipgloss.NewStyle(). - Background(graycodeColor). + Background(hawkColor). Foreground(lipgloss.Color("#FFFFFF")). Bold(true). Padding(0, 1) @@ -166,9 +166,9 @@ func (tp *ThemePicker) View() tea.View { for i, e := range tp.entries { if i == tp.sel { - rowStyle := lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + rowStyle := lipgloss.NewStyle().Foreground(hawkColor).Bold(true) b.WriteString(fmt.Sprintf(" ▶ %s\n", rowStyle.Render(e.Name))) - b.WriteString(fmt.Sprintf(" %s\n", lipgloss.NewStyle().Foreground(graycodeColor).Render(e.Desc))) + b.WriteString(fmt.Sprintf(" %s\n", lipgloss.NewStyle().Foreground(hawkColor).Render(e.Desc))) } else { nameStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#D0D0D0")) b.WriteString(fmt.Sprintf(" %s\n", nameStyle.Render(e.Name))) diff --git a/cmd/tips.go b/cmd/tips.go index 4f0765b29..3557b8f43 100644 --- a/cmd/tips.go +++ b/cmd/tips.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) -// Tip represents a single graycode usage tip. +// Tip represents a single hawk usage tip. type Tip struct { ID string `json:"id"` Text string `json:"text"` @@ -30,7 +30,7 @@ func allTips() []Tip { {ID: "tab-complete", Text: "Press Tab to autocomplete slash commands.", Category: "shortcuts"}, {ID: "history-nav", Text: "Press Up/Down to navigate command history.", Category: "shortcuts"}, {ID: "esc-cancel", Text: "Press Esc to cancel a running query.", Category: "shortcuts"}, - {ID: "ctrl-c-quit", Text: "Press Ctrl+C twice to quit graycode.", Category: "shortcuts"}, + {ID: "ctrl-c-quit", Text: "Press Ctrl+C twice to quit hawk.", Category: "shortcuts"}, {ID: "copy-chat", Text: "Ctrl+Shift+C or /copy copies chat; /copy input copies your draft; /mouse off enables click-drag select.", Category: "shortcuts"}, {ID: "vim-mode", Text: "Use /vim to toggle vim-style keybindings.", Category: "editing"}, {ID: "model-switch", Text: "Use /model to switch LLM models on the fly.", Category: "config"}, @@ -44,7 +44,7 @@ func allTips() []Tip { {ID: "slash-review", Text: "Use /review to get a code review of current changes.", Category: "workflow"}, {ID: "slash-init", Text: "Use /init to analyze a new project automatically.", Category: "project"}, {ID: "add-dir", Text: "Use /add-dir to add extra directories to context.", Category: "context"}, - {ID: "slash-memory", Text: "Use /memory for AGENTS.md; /harrier for graph memory; /ecosystem for graycode-router/harrier/shrike status.", Category: "context"}, + {ID: "slash-memory", Text: "Use /memory for AGENTS.md; /harrier for graph memory; /ecosystem for eyrie/harrier/shrike status.", Category: "context"}, {ID: "slash-rewind", Text: "Use /rewind to undo the last exchange.", Category: "session"}, {ID: "slash-fork", Text: "Use /fork to branch off the current conversation.", Category: "session"}, {ID: "slash-context", Text: "Use /context to see what the agent knows about your project.", Category: "context"}, @@ -52,7 +52,7 @@ func allTips() []Tip { {ID: "slash-mode-plan", Text: "Use /mode plan to research read-only, then /mode act to implement.", Category: "workflow"}, {ID: "slash-isolation", Text: "Use /isolation workspace so shell runs under OS sandbox wrap.", Category: "safety"}, {ID: "slash-trust", Text: "Use /trust add so project hooks and MCP can load (folder trust).", Category: "safety"}, - {ID: "slash-branch-agent", Text: "Use /branch-agent before big edits on main — creates graycode/agent-* branch.", Category: "git"}, + {ID: "slash-branch-agent", Text: "Use /branch-agent before big edits on main — creates hawk/agent-* branch.", Category: "git"}, {ID: "tool-search-select", Text: "Use ToolSearch select:Impact (etc.) to unlock optional tools on the lazy surface.", Category: "tools"}, {ID: "slash-auto-commit", Text: "Use /auto-commit on so Write/Edit create git commits automatically.", Category: "git"}, } diff --git a/cmd/tips_test.go b/cmd/tips_test.go index 6ba98bd52..efc3212c2 100644 --- a/cmd/tips_test.go +++ b/cmd/tips_test.go @@ -34,7 +34,7 @@ func TestNextTip_ReturnsNonEmpty(t *testing.T) { orig := os.Getenv("HOME") tmp := t.TempDir() os.Setenv("HOME", tmp) - os.MkdirAll(filepath.Join(tmp, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(tmp, ".hawk"), 0o755) defer os.Setenv("HOME", orig) tip := nextTip(false, "") @@ -47,7 +47,7 @@ func TestRecordTipShown(t *testing.T) { orig := os.Getenv("HOME") tmp := t.TempDir() os.Setenv("HOME", tmp) - os.MkdirAll(filepath.Join(tmp, ".graycode"), 0o755) + os.MkdirAll(filepath.Join(tmp, ".hawk"), 0o755) defer os.Setenv("HOME", orig) recordTipShown("slash-help") diff --git a/cmd/toolset_cmd.go b/cmd/toolset_cmd.go index dd98a3d6a..b6b353c44 100644 --- a/cmd/toolset_cmd.go +++ b/cmd/toolset_cmd.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/toolset" + "github.com/GrayCodeAI/hawk/internal/toolset" "github.com/spf13/cobra" ) @@ -17,8 +17,8 @@ var toolsetCmd = &cobra.Command{ Short: "List or resolve composable tool groups", Long: `Named, composable tool groups for scoping an agent's tool surface. - graycode toolset List available toolsets - graycode toolset research Resolve 'research' to its concrete tool list + hawk toolset List available toolsets + hawk toolset research Resolve 'research' to its concrete tool list Toolsets compose from other toolsets; resolving expands Requires transitively (cycle-safe) and de-duplicates.`, diff --git a/cmd/trust.go b/cmd/trust.go index 3abf64cee..6c9f92e4e 100644 --- a/cmd/trust.go +++ b/cmd/trust.go @@ -5,10 +5,10 @@ import ( "fmt" "os" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/theme" - "github.com/GrayCodeAI/graycode-cli/internal/flags" - "github.com/GrayCodeAI/graycode-cli/internal/trust" + "github.com/GrayCodeAI/hawk/internal/flags" + "github.com/GrayCodeAI/hawk/internal/trust" "github.com/spf13/cobra" ) @@ -18,10 +18,10 @@ var trustCmd = &cobra.Command{ Long: `Folder trust controls whether project-scoped hooks, MCP servers, LSP configs, and plugins may load from a repository. -When GRAYCODE_Y0_FOLDER_TRUST is enabled (default after Year 0 PACK-03), +When HAWK_Y0_FOLDER_TRUST is enabled (default after Year 0 PACK-03), untrusted projects cannot run project automation (RCE mitigation). -User-global plugins under the Graycode state directory always load.`, +User-global plugins under the Hawk state directory always load.`, } var trustAddCmd = &cobra.Command{ @@ -103,7 +103,7 @@ var trustListCmd = &cobra.Command{ fmt.Println("[]") } else { cmd.Println(auditTint("No trusted directories.", textMuted)) - cmd.Printf("%s\n", auditTint(fmt.Sprintf("Folder trust enforcement: %v (GRAYCODE_Y0_FOLDER_TRUST)", flags.FolderTrust()), textMuted)) + cmd.Printf("%s\n", auditTint(fmt.Sprintf("Folder trust enforcement: %v (HAWK_Y0_FOLDER_TRUST)", flags.FolderTrust()), textMuted)) } return nil } diff --git a/cmd/usage.go b/cmd/usage.go index 9c218ea92..117556632 100644 --- a/cmd/usage.go +++ b/cmd/usage.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/usage" + "github.com/GrayCodeAI/hawk/internal/usage" "github.com/spf13/cobra" ) @@ -65,7 +65,7 @@ func runUsage(cmd *cobra.Command, _ []string) error { return nil } - cmd.Println(auditTint(fmt.Sprintf("Usage (last %s)", usagePeriod), graycodeColor)) + cmd.Println(auditTint(fmt.Sprintf("Usage (last %s)", usagePeriod), hawkColor)) cmd.Println(auditTint(fmt.Sprintf("%-28s %10s %10s %8s %12s", "model", "in", "out", "gen", "cost"), textMuted)) for _, m := range sum.ByModel { // Pad the cost to its column width first, then colorize, so the diff --git a/cmd/verify_cmd.go b/cmd/verify_cmd.go index 694bd468d..754e4274b 100644 --- a/cmd/verify_cmd.go +++ b/cmd/verify_cmd.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/governance" - "github.com/GrayCodeAI/graycode-cli/internal/securitylog" - "github.com/GrayCodeAI/graycode-cli/internal/testrunner" + "github.com/GrayCodeAI/hawk/internal/governance" + "github.com/GrayCodeAI/hawk/internal/securitylog" + "github.com/GrayCodeAI/hawk/internal/testrunner" "github.com/spf13/cobra" ) @@ -19,7 +19,7 @@ import ( var verifyCmd = &cobra.Command{ Use: "verify", Short: "Run local self-verification (security log, governance policy)", - Long: `Run graycode's self-verification checks without a model: + Long: `Run hawk's self-verification checks without a model: 1. The tamper-evident security event log hash chain is intact. 2. The managed governance policy (if installed) parses and validates. diff --git a/cmd/version_display.go b/cmd/version_display.go index 75405ed9f..2c008d81f 100644 --- a/cmd/version_display.go +++ b/cmd/version_display.go @@ -7,13 +7,13 @@ import ( ) // versionLine is the single user-facing version format shared by -// `graycode --version` and `graycode version`. +// `hawk --version` and `hawk version`. func versionLine() string { ver := DisplayVersion() if ver != "" && !strings.HasPrefix(ver, "v") && !strings.HasPrefix(ver, "V") { ver = "v" + ver } - line := auditTint("graycode", textPrimary) + " " + auditTint(ver, graycodeColor) + line := auditTint("hawk", textPrimary) + " " + auditTint(ver, hawkColor) if d := strings.TrimSpace(buildDate); d != "" && d != "unknown" { line += auditTint(" (built "+d+")", textMuted) } diff --git a/cmd/version_display_test.go b/cmd/version_display_test.go index 59fdc2ad0..7047f4b3c 100644 --- a/cmd/version_display_test.go +++ b/cmd/version_display_test.go @@ -5,8 +5,8 @@ import ( "path/filepath" "testing" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestDisplayVersion_FromVERSIONFile(t *testing.T) { @@ -29,12 +29,12 @@ func TestDisplayVersion_ReleaseBuild(t *testing.T) { } func TestChatConnectionStatus_NoCredentials(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{session: nil} @@ -45,12 +45,12 @@ func TestChatConnectionStatus_NoCredentials(t *testing.T) { } func TestChatBottomRightStatus_NoCredentials(t *testing.T) { - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - graycodeconfig.InvalidateConfigUICache() + hawkconfig.InvalidateConfigUICache() }) m := chatModel{inputIndicator: &InputIndicator{}} diff --git a/cmd/vibe.go b/cmd/vibe.go index 600f2c031..4a159c859 100644 --- a/cmd/vibe.go +++ b/cmd/vibe.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // VibeConfig controls vibe coding behavior. diff --git a/cmd/visual_diff.go b/cmd/visual_diff.go index 1cb053ae6..0f77fee31 100644 --- a/cmd/visual_diff.go +++ b/cmd/visual_diff.go @@ -35,7 +35,7 @@ type DiffTheme struct { Reset string } -// DefaultDiffTheme returns a DiffTheme using graycode's semantic palette so diff +// DefaultDiffTheme returns a DiffTheme using hawk's semantic palette so diff // output reads consistently with the rest of the UI: additions in doneGreen, // deletions in errorCoral, hunk markers in infoSky, file headers in // containerBlue — each color mapped to one meaning, never reused for another. diff --git a/cmd/welcome_banner.go b/cmd/welcome_banner.go index 2e76ee35c..94cf06425 100644 --- a/cmd/welcome_banner.go +++ b/cmd/welcome_banner.go @@ -6,8 +6,8 @@ import ( "github.com/mattn/go-runewidth" ) -// graycodeBlockGlyphs — fixed 8-column ██ font used by the welcome gate banners. -var graycodeBlockGlyphs = map[rune][5]string{ +// hawkBlockGlyphs — fixed 8-column ██ font used by the welcome gate banners. +var hawkBlockGlyphs = map[rune][5]string{ 'H': {"██ ██ ", "██ ██ ", "███████ ", "██ ██ ", "██ ██ "}, 'A': {" ███ ", " █████ ", "███████ ", "██ ██ ", "██ ██ "}, 'W': {"██ ██ ", "██ ██ ", "██ █ ██ ", "███ ███ ", "██ ██ "}, @@ -20,8 +20,8 @@ var graycodeBlockGlyphs = map[rune][5]string{ 'T': {"████████", " ██ ", " ██ ", " ██ ", " ██ "}, } -// graycodeLogoArtLines is the canonical GRAYCODE wordmark, with the graycode forming the W. -var graycodeLogoArtLines = []string{ +// hawkLogoArtLines is the canonical HAWK wordmark, with the hawk forming the W. +var hawkLogoArtLines = []string{ " . .", " . . . .", " . | | .", @@ -40,19 +40,19 @@ var graycodeLogoArtLines = []string{ } const ( - graycodeBlockCellW = 8 - graycodeBlockLetterGap = 1 - graycodeBlockWordGap = 4 + hawkBlockCellW = 8 + hawkBlockLetterGap = 1 + hawkBlockWordGap = 4 ) // welcomeWordLines — "WELCOME" block (row-aligned, fixed grid). -var welcomeWordLines = composeGraycodeBlockLines("WELCOME") +var welcomeWordLines = composeHawkBlockLines("WELCOME") // welcomeToWordLines — "TO" block, centered under WELCOME on the gate. -var welcomeToWordLines = composeGraycodeBlockLines("TO") +var welcomeToWordLines = composeHawkBlockLines("TO") // welcomeToPhraseLines — "WELCOME TO" block for wide welcome gates. -var welcomeToPhraseLines = composeGraycodeBlockLines("WELCOME TO") +var welcomeToPhraseLines = composeHawkBlockLines("WELCOME TO") // welcomeToBannerMinWidth is the visible width for the WELCOME block. const welcomeToBannerMinWidth = 61 @@ -60,24 +60,24 @@ const welcomeToBannerMinWidth = 61 // welcomeToPhraseMinWidth is the visible width for the combined "WELCOME TO" block. var welcomeToPhraseMinWidth = blockLinesWidth(welcomeToPhraseLines) -func composeGraycodeBlockLines(text string) []string { +func composeHawkBlockLines(text string) []string { rows := make([]string, 5) words := strings.Fields(text) for wi, word := range words { for ci, ch := range word { - glyph, ok := graycodeBlockGlyphs[ch] + glyph, ok := hawkBlockGlyphs[ch] if !ok { continue } for i := range rows { if rows[i] != "" { if ci == 0 && wi > 0 { - rows[i] += strings.Repeat(" ", graycodeBlockWordGap) + rows[i] += strings.Repeat(" ", hawkBlockWordGap) } else { - rows[i] += strings.Repeat(" ", graycodeBlockLetterGap) + rows[i] += strings.Repeat(" ", hawkBlockLetterGap) } } - cell := padBlockCell(glyph[i], graycodeBlockCellW) + cell := padBlockCell(glyph[i], hawkBlockCellW) rows[i] += cell } } diff --git a/cmd/welcome_banner_test.go b/cmd/welcome_banner_test.go index bfe5a5296..d79ef1e83 100644 --- a/cmd/welcome_banner_test.go +++ b/cmd/welcome_banner_test.go @@ -8,7 +8,7 @@ import ( ) func TestWelcomeToBanner_TOAligned(t *testing.T) { - to := composeGraycodeBlockLines("TO") + to := composeHawkBlockLines("TO") if len(to) != 5 { t.Fatalf("expected 5 rows, got %d", len(to)) } @@ -48,10 +48,10 @@ func TestWelcomeToPhraseLinesContainSingleRowPhrase(t *testing.T) { } } -func TestWelcomeWordLines_SameWGlyphAsGRAYCODE(t *testing.T) { - w := strings.TrimRight(graycodeBlockGlyphs['W'][0], " ") +func TestWelcomeWordLines_SameWGlyphAsHAWK(t *testing.T) { + w := strings.TrimRight(hawkBlockGlyphs['W'][0], " ") if !strings.HasPrefix(welcomeWordLines[0], w) { - t.Fatalf("W row should start with graycode logo glyph:\nwelcome: %q\ngraycode W: %q", + t.Fatalf("W row should start with hawk logo glyph:\nwelcome: %q\nhawk W: %q", welcomeWordLines[0], w) } } diff --git a/cmd/welcome_inline_test.go b/cmd/welcome_inline_test.go index d44038e4e..3820818bb 100644 --- a/cmd/welcome_inline_test.go +++ b/cmd/welcome_inline_test.go @@ -8,9 +8,9 @@ import ( "charm.land/bubbles/v2/textarea" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) type welcomeMCPStub struct { @@ -30,7 +30,7 @@ func TestWelcomeScreenNerdIconsUnique(t *testing.T) { stopped := false states := []*bool{nil, &running, &stopped} for i, docker := range states { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 100, 24, docker) + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 100, 24, docker) seen := make(map[rune]struct{}) for _, r := range out { if r < 0xE000 || r > 0xF8FF { @@ -53,7 +53,7 @@ func (s welcomeMCPStub) Execute(context.Context, json.RawMessage) (string, error func (s welcomeMCPStub) MCPServerName() string { return s.server } func TestBuildWelcomeMessage_InlineShowsSetupGuidance(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 100, 24, nil) + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 100, 24, nil) if !strings.Contains(out, "v") { t.Fatalf("inline welcome should show version, got:\n%s", out) } @@ -63,7 +63,7 @@ func TestBuildWelcomeMessage_InlineShowsSetupGuidance(t *testing.T) { } func TestBuildWelcomeMessage_InlineShowsGuidance(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 100, 24, nil) + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 100, 24, nil) for _, want := range []string{"Container Starting", "Skills (0)", "AGENTS.md", "MCPs (0)"} { if !strings.Contains(out, want) { t.Fatalf("minimal welcome missing %q in:\n%s", want, out) @@ -106,7 +106,7 @@ func TestBuildWelcomeMessage_InlineShowsGuidance(t *testing.T) { } func TestBuildWelcomeMessage_ShortTerminalUsesCompactCopy(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 72, 20, nil) + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 72, 20, nil) if strings.Contains(out, "PgUp/Dn scroll chat") || strings.Contains(out, "for new session") { t.Fatalf("compact welcome should drop verbose descriptions, got:\n%s", out) } @@ -115,23 +115,23 @@ func TestBuildWelcomeMessage_ShortTerminalUsesCompactCopy(t *testing.T) { } } -func TestBuildWelcomeMessage_WideTerminalUsesGraycodeWordmark(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 120, 40, nil) +func TestBuildWelcomeMessage_WideTerminalUsesHawkWordmark(t *testing.T) { + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 120, 40, nil) for _, want := range []string{ "___ ___ _________", "(\\.|\\/|./)", "|0\\/0|", } { if !strings.Contains(out, want) { - t.Fatalf("wide welcome missing graycode wordmark line %q in:\n%s", want, out) + t.Fatalf("wide welcome missing hawk wordmark line %q in:\n%s", want, out) } } } -func TestBuildWelcomeMessage_GraycodeWordmarkBlinks(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, true, 120, 40, nil) +func TestBuildWelcomeMessage_HawkWordmarkBlinks(t *testing.T) { + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, true, 120, 40, nil) if !strings.Contains(out, "|-\\/-|") { - t.Fatalf("blinking welcome should close the graycode's eyes, got:\n%s", out) + t.Fatalf("blinking welcome should close the hawk's eyes, got:\n%s", out) } } @@ -167,7 +167,7 @@ func TestEyeBlinkTick_CyclesEyeFrameStates(t *testing.T) { } func TestWelcomeMessage_OneLineGapBeforeStatusLine(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 120, 40, nil) + out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 120, 40, nil) lines := strings.Split(out, "\n") artBottomIdx := -1 for i, line := range lines { diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml index 645e72a16..8c9ffb741 100644 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -1,7 +1,7 @@ -name: graycode +name: hawk services: - graycode: + hawk: build: context: ../../ dockerfile: Dockerfile @@ -11,7 +11,7 @@ services: VERSION: ${VERSION:-dev} COMMIT: ${COMMIT:-none} BUILD_DATE: ${BUILD_DATE:-unknown} - image: ghcr.io/graycodeai/graycode:dev + image: ghcr.io/graycodeai/hawk:dev # Override the Dockerfile's default `CMD ["--help"]` so the container # actually runs the HTTP/SSE daemon. `--host 0.0.0.0` is required for # the published port to be reachable from outside the container. @@ -19,9 +19,9 @@ services: ports: - "4590:4590" environment: - - GRAYCODE_DAEMON_API_KEY=${GRAYCODE_DAEMON_API_KEY:-} - - GRAYCODE_DAEMON_PORT=4590 - - GRAYCODE_DAEMON_HOST=0.0.0.0 + - HAWK_DAEMON_API_KEY=${HAWK_DAEMON_API_KEY:-} + - HAWK_DAEMON_PORT=4590 + - HAWK_DAEMON_HOST=0.0.0.0 env_file: - path: ../../.env.example required: false diff --git a/docs/BENCHMARKS.md b/docs/BENCHMARKS.md index a9a36b40a..4ac27f84f 100644 --- a/docs/BENCHMARKS.md +++ b/docs/BENCHMARKS.md @@ -1,6 +1,6 @@ # Benchmarks -Published, reproducible CPU benchmarks for graycode-cli. These are TUI-independent +Published, reproducible CPU benchmarks for hawk. These are TUI-independent measurements (no latency theater) run with existing `go test -bench` targets; they exist so performance is documented and regressions are visible, not to make marketing claims. diff --git a/docs/COMPETITIVE.md b/docs/COMPETITIVE.md index 337cf5f99..baca16501 100644 --- a/docs/COMPETITIVE.md +++ b/docs/COMPETITIVE.md @@ -1,7 +1,7 @@ -# graycode-cli vs Top 20 OSS — Competitive Analysis +# hawk vs Top 20 OSS — Competitive Analysis Status: Implemented (branch `feat/competitive-analysis-top20`, 2026-09-09) -Scope: graycode-cli (this repo) vs 10 AI coding CLIs + 6 dev CLIs + 4 terminals (incl. `herdr` multiplexer) +Scope: hawk (this repo) vs 10 AI coding CLIs + 6 dev CLIs + 4 terminals (incl. `herdr` multiplexer) Related: `docs/plans/toolbench-comparison-vs-top20.md` (tool-count parity), `docs/SECURITY-DEVELOPER.md` (sandbox model), `docs/plans/pi-adoption-plan.md` (Kitty graphics already proposed), `docs/RESEARCH.md` (top-20 research-paper comparison + implementation record) ## Methodology (no assumptions) @@ -12,13 +12,13 @@ Verified from source in this repo: - Browser/Screenshot: `tool.BrowserTool{}`, `tool.ScreenshotTool{}` in essential set (`cmd/chat_tools.go:75-76`); headless Chrome via chromedp per prior plan. - Sandbox: mandatory Docker, fail-closed, never host fallback — `docs/SECURITY-DEVELOPER.md:71-73`, `internal/sandbox/container.go:72-74`. - Creds: OS secret store only, no `.env`/env read — `docs/SECURITY-DEVELOPER.md:7-12`. -- Share: local deeplink only — `internal/session/export.go:801-819` returns `graycode://share/`, no hosted URL. -- Custom providers: supported — `internal/config/settings.go:50` (`custom_providers`), `internal/config/graycode_router_engine.go:32-50`. +- Share: local deeplink only — `internal/session/export.go:801-819` returns `hawk://share/`, no hosted URL. +- Custom providers: supported — `internal/config/settings.go:50` (`custom_providers`), `internal/config/eyrie_engine.go:32-50`. - Unwired backends: `internal/tool/computer_use.go:67-94` (`SetComputerBackend`, nil default), `internal/tool/media_generation.go:69-71` (`SetMediaEngine`, nil default). - Terminal detect covers kitty/ghostty/wezterm/alacritty names (`internal/ui/icons/detect_test.go:56`); Kitty graphics protocol not implemented (see `docs/plans/pi-adoption-plan.md:25`). - Bench infra exists (`internal/bench/suite.go`, `internal/feature/eval/`, `make bench`) but README publishes no numbers. -External star counts below are approximate web-search snapshots (2026-09-08), not repo-verified. Treat as order-of-magnitude traction, not exact rankings. graycode-cli is pre-release (`VERSION`: `0.0.1`, `README.md:40-44` source-build primary) — it competes on architecture, not stars. +External star counts below are approximate web-search snapshots (2026-09-08), not repo-verified. Treat as order-of-magnitude traction, not exact rankings. hawk is pre-release (`VERSION`: `0.0.1`, `README.md:40-44` source-build primary) — it competes on architecture, not stars. ## The 20 @@ -41,7 +41,7 @@ External star counts below are approximate web-search snapshots (2026-09-08), no ### B. Dev CLIs (substrate + UX bar) -| # | Repo | Stars~ | Lang / Lic | Lesson for graycode | +| # | Repo | Stars~ | Lang / Lic | Lesson for hawk | |---|---|---|---|---| | B1 | `junegunn/fzf` | ~82k | Go, MIT | Pipe-first Unix design; zero-config speed | | B2 | `jesseduffield/lazygit` | ~82k | Go, MIT | Keyboard TUI that makes hard git trivial; closest Go-TUI comp | @@ -61,10 +61,10 @@ External star counts below are approximate web-search snapshots (2026-09-08), no ## Deep dimensions -1. **Traction.** graycode has no star-moat (pre-release). Leaders won via day-1 provider-agnostic + one-liner install + Web/Desktop alongside TUI. graycode already ships script/brew/npm paths (`README.md:48-59`) — keep, don't add Desktop. -2. **Language/distro.** Go+MIT+zero-CGO (`Makefile:54`, `go.mod:3`) matches `gh/fzf/lazygit` enterprise-safe profile. Avoid GPL/EUPL patterns (kitty/eza). Rust wave wins on published benchmarks — graycode has `make bench` but publishes none (Gap-04). -3. **Providers.** graycode routes only via `graycode-router/engine` facade (`docs/SECURITY-DEVELOPER.md:51-56`, `ecosystem.yaml:29-30`); custom OpenAI-compat supported (`internal/config/settings.go:50`). Count messaging ("28 first-class" per README) trails OpenCode 75+ / Hermes 300+ — fix by exposing catalog count dynamically, not by forking providers into CLI (ownership lives in router per AGENTS.md). -4. **TUI/UX.** Bubble Tea v2 + vim keys + `/autonomy` + `/spec` + watch `AI!`/`AI?` + visual diff is competitive. Missing vs field: hosted share-link (ours is local `graycode://` deeplink), multi-session grid (we have `mission` worktrees + daemon — unsurfaced like herdr/cmux). Gap-02. +1. **Traction.** hawk has no star-moat (pre-release). Leaders won via day-1 provider-agnostic + one-liner install + Web/Desktop alongside TUI. hawk already ships script/brew/npm paths (`README.md:48-59`) — keep, don't add Desktop. +2. **Language/distro.** Go+MIT+zero-CGO (`Makefile:54`, `go.mod:3`) matches `gh/fzf/lazygit` enterprise-safe profile. Avoid GPL/EUPL patterns (kitty/eza). Rust wave wins on published benchmarks — hawk has `make bench` but publishes none (Gap-04). +3. **Providers.** hawk routes only via `eyrie/engine` facade (`docs/SECURITY-DEVELOPER.md:51-56`, `ecosystem.yaml:29-30`); custom OpenAI-compat supported (`internal/config/settings.go:50`). Count messaging ("28 first-class" per README) trails OpenCode 75+ / Hermes 300+ — fix by exposing catalog count dynamically, not by forking providers into CLI (ownership lives in router per AGENTS.md). +4. **TUI/UX.** Bubble Tea v2 + vim keys + `/autonomy` + `/spec` + watch `AI!`/`AI?` + visual diff is competitive. Missing vs field: hosted share-link (ours is local `hawk://` deeplink), multi-session grid (we have `mission` worktrees + daemon — unsurfaced like herdr/cmux). Gap-02. 5. **Sandbox.** Docker-only fail-closed is strictest default alongside Codex net-off and Gemini gVisor. Tradeoff is onboarding friction without Docker. Must not add host-exec fallback (violates `docs/SECURITY-DEVELOPER.md:71-73`); fix with preflight/path/doctor messaging + image pull/build guidance. Gap-01. 6. **Memory/context.** AST repomap + Harrier graph + compaction segments + relevance-prune + conversation-arc + 80% tool-result clearing exceeds most. Missing: Hermes-style auto-skill learning loop (we have curator archive + harness — surface it). 7. **Multi-agent.** `mission` worktrees + family messenger + path reservations + budgets + portable `mission-graph.json` + `graph export` (hashes only) is unique verifiable-execution story. Surface it; no new runtime needed. diff --git a/docs/DAEMON-PORT-THREAT-MODEL.md b/docs/DAEMON-PORT-THREAT-MODEL.md index 3de2e7ae7..01b84e668 100644 --- a/docs/DAEMON-PORT-THREAT-MODEL.md +++ b/docs/DAEMON-PORT-THREAT-MODEL.md @@ -1,6 +1,6 @@ -# Graycode Daemon — Port 4590 Threat Model +# Hawk Daemon — Port 4590 Threat Model -The Graycode daemon (`graycode daemon`) binds an HTTP server on port **4590** (default) +The Hawk daemon (`hawk daemon`) binds an HTTP server on port **4590** (default) of the **loopback interface only** (`127.0.0.1:4590`). This document describes the threat model, attack surface, and mitigation controls. @@ -33,19 +33,19 @@ A malicious local process (malware, compromised script) can send HTTP requests to `127.0.0.1:4590` without any network privilege. **Mitigation:** -- Set `GRAYCODE_DAEMON_API_KEY` and configure the daemon with `--api-key` to +- Set `HAWK_DAEMON_API_KEY` and configure the daemon with `--api-key` to require `Authorization: Bearer ` on all non-readiness endpoints. - The key is validated per-request; the daemon does not issue tokens. #### T3 — Port scanning / fingerprinting -Any local process can detect that port 4590 is open and identify graycode. +Any local process can detect that port 4590 is open and identify hawk. **Mitigation:** Low severity — this is unavoidable for a local HTTP service. The daemon does not expose version information on unauthenticated endpoints. #### T4 — Session data exfiltration Session data (conversation history, tool outputs) is stored in SQLite at -`~/.graycode/sessions/graycode.db`. A local attacker with filesystem read access can +`~/.hawk/sessions/hawk.db`. A local attacker with filesystem read access can read this file directly regardless of the daemon's auth. **Mitigation:** @@ -59,7 +59,7 @@ credits. **Mitigation:** - A global concurrency cap bounds in-flight generations (default **4**, tuned - via `GRAYCODE_DAEMON_MAX_CONCURRENT`). When the cap is hit, new `/v1/chat` + via `HAWK_DAEMON_MAX_CONCURRENT`). When the cap is hit, new `/v1/chat` requests are refused with `503` instead of queuing unboundedly. - Per-IP token-bucket rate limiting: `/v1/chat` is limited to ~30 req/min (burst 6) and other authenticated endpoints to ~10 req/min (burst 4). @@ -73,7 +73,7 @@ credits. - **Remote network attackers**: the daemon does not bind to `0.0.0.0`; remote access is architecturally blocked. -- **Process privilege escalation**: graycode does not run as root and does not use +- **Process privilege escalation**: hawk does not run as root and does not use `setuid`/`setgid`. --- @@ -84,11 +84,11 @@ Set the daemon API key before starting the daemon: ```bash # Option 1: environment variable (recommended for CI/automation) -export GRAYCODE_DAEMON_API_KEY="your-random-secret-here" -graycode daemon +export HAWK_DAEMON_API_KEY="your-random-secret-here" +hawk daemon # Option 2: CLI flag -graycode daemon --api-key "your-random-secret-here" +hawk daemon --api-key "your-random-secret-here" ``` All clients must then pass: @@ -96,7 +96,7 @@ All clients must then pass: Authorization: Bearer your-random-secret-here ``` -The SDK reads this from `GRAYCODE_DAEMON_API_KEY` automatically if the env var is +The SDK reads this from `HAWK_DAEMON_API_KEY` automatically if the env var is set. --- @@ -104,10 +104,10 @@ set. ## Changing the port ```bash -graycode daemon --port 9000 +hawk daemon --port 9000 ``` -Or in `~/.graycode/settings.json`: +Or in `~/.hawk/settings.json`: ```json { "daemon": { @@ -120,13 +120,13 @@ Or in `~/.graycode/settings.json`: ## Shared machine considerations -If graycode is running on a machine shared by multiple OS users (e.g., a dev +If hawk is running on a machine shared by multiple OS users (e.g., a dev server), set the API key **and** use OS firewall rules to restrict which local users can connect to port 4590: ```bash # macOS — pf: allow only current user's processes (requires pf.conf editing) -# Linux — iptables: allow only the graycode-owner UID +# Linux — iptables: allow only the hawk-owner UID sudo iptables -A OUTPUT -p tcp --dport 4590 -m owner ! --uid-owner $UID -j REJECT ``` diff --git a/docs/DEVELOPER-PATH.md b/docs/DEVELOPER-PATH.md index 8ce518ac8..e77fb5674 100644 --- a/docs/DEVELOPER-PATH.md +++ b/docs/DEVELOPER-PATH.md @@ -1,130 +1,130 @@ -# Graycode Developer Path +# Hawk Developer Path -This guide explains what `graycode path` checks and how to get a fresh developer machine ready to use Graycode safely. +This guide explains what `hawk path` checks and how to get a fresh developer machine ready to use Hawk safely. ## What "developer path" means -For Graycode, the developer path is the minimum local setup required to chat, edit code, and keep credentials off disk: +For Hawk, the developer path is the minimum local setup required to chat, edit code, and keep credentials off disk: - A provider credential stored in the OS secret store -- A model selected in Graycode settings -- A local model catalog available through graycode-router -- No plaintext API keys left in GraycodeRouter's configured `provider.json` or legacy env files +- A model selected in Hawk settings +- A local model catalog available through eyrie +- No plaintext API keys left in Eyrie's configured `provider.json` or legacy env files - Safe defaults for Bash execution and filesystem access - Optional but healthy ecosystem integrations like harrier memory Run the report at any time: ```bash -graycode path -graycode path --strict -graycode doctor -graycode preflight +hawk path +hawk path --strict +hawk doctor +hawk preflight ``` ## Setup checklist ### 1. Build and workspace setup -If you are contributing from source, clone Graycode as the main CLI. The support +If you are contributing from source, clone Hawk as the main CLI. The support repositories are independent sibling checkouts when you need the full local -workspace; they are not nested under Graycode: +workspace; they are not nested under Hawk: ```bash mkdir graycode-eco && cd graycode-eco -git clone https://github.com/GrayCodeAI/graycode-cli -git clone https://github.com/GrayCodeAI/graycode-router -cd graycode-cli +git clone https://github.com/GrayCodeAI/hawk +git clone https://github.com/GrayCodeAI/eyrie +cd hawk make setup -go build -o graycode ./cmd/graycode +go build -o hawk ./cmd/hawk ``` `make setup` validates the canonical 15-repository manifest and regenerates the -parent `../go.work` from the nine local Go repositories. Graycode can also be built -as a standalone checkout with `GOWORK=off go build ./cmd/graycode`; the sibling +parent `../go.work` from the nine local Go repositories. Hawk can also be built +as a standalone checkout with `GOWORK=off go build ./cmd/hawk`; the sibling workspace is only required for cross-repository development and boundary checks. ### 2. Configure credentials -Start Graycode and use `/config` to paste an API key or configure a local provider like Ollama. +Start Hawk and use `/config` to paste an API key or configure a local provider like Ollama. -Graycode stores credentials in the macOS Keychain or Linux secret store. It should not rely on shell env vars, `.env`, or plaintext config files for provider secrets. +Hawk stores credentials in the macOS Keychain or Linux secret store. It should not rely on shell env vars, `.env`, or plaintext config files for provider secrets. Useful checks: ```bash -graycode credentials status -graycode preflight +hawk credentials status +hawk preflight ``` -`graycode preflight` is a local-ready check; it does not contact a provider. To +`hawk preflight` is a local-ready check; it does not contact a provider. To live-verify the selected provider credential and connectivity, use `/config` -validation or `graycode models list --live`. +validation or `hawk models list --live`. ### 3. Select a model -Pick a model in `/config`. Graycode stores the selected model in settings and uses graycode-router for provider routing and catalog resolution. +Pick a model in `/config`. Hawk stores the selected model in settings and uses eyrie for provider routing and catalog resolution. If the catalog is missing or empty: ```bash -graycode models refresh +hawk models refresh ``` ## Security checks -`graycode path` treats these as important security conditions: +`hawk path` treats these as important security conditions: -- GraycodeRouter's resolved `provider.json` must not contain secret fields -- legacy `~/.graycode/env` or `~/.graycode/.env` files should be migrated away +- Eyrie's resolved `provider.json` must not contain secret fields +- legacy `~/.hawk/env` or `~/.hawk/.env` files should be migrated away - sensitive files like provider config and SSH paths should be blocked from agent reads -GraycodeRouter resolves provider state from `GRAYCODE_ROUTER_CONFIG_DIR` first, then -`GRAYCODE_CONFIG_DIR` for compatibility, then the platform user-config directory. -Graycode protects that resolved path even when it is customized or symlinked. +Eyrie resolves provider state from `EYRIE_CONFIG_DIR` first, then +`HAWK_CONFIG_DIR` for compatibility, then the platform user-config directory. +Hawk protects that resolved path even when it is customized or symlinked. -If Graycode detects old plaintext secrets, run Graycode once and complete `/config`, or remove the secret fields manually after backing up the file. +If Hawk detects old plaintext secrets, run Hawk once and complete `/config`, or remove the secret fields manually after backing up the file. Read the full credential and isolation model in [SECURITY-DEVELOPER.md](./SECURITY-DEVELOPER.md). ## Sandbox checks Docker is mandatory for agent command execution. If Docker is unavailable, -`graycode path` reports a blocking failure and agent tools remain locked. Graycode does +`hawk path` reports a blocking failure and agent tools remain locked. Hawk does not offer a host-execution fallback. -The versioned `graycodeai/graycode-sandbox` image is pulled automatically when it -is not already local. If the public registry is unavailable, Graycode builds its +The versioned `graycodeai/hawk-sandbox` image is pulled automatically when it +is not already local. If the public registry is unavailable, Hawk builds its bundled sandbox Dockerfile locally through Docker. ```bash -graycode path --strict +hawk path --strict ``` ## Ecosystem checks -`graycode path` also verifies the core support layer behind Graycode: +`hawk path` also verifies the core support layer behind Hawk: -- `graycode-router` for provider routing and local preflight readiness +- `eyrie` for provider routing and local preflight readiness - `shrike` for token estimation and compression - `harrier` for optional persistent memory If you want the broader status summary: ```bash -graycode ecosystem -graycode doctor +hawk ecosystem +hawk doctor ``` ## Typical recovery path -If `graycode path` says you are not ready, this is the intended order: +If `hawk path` says you are not ready, this is the intended order: -1. Run `graycode` +1. Run `hawk` 2. Open `/config` 3. Paste an API key or configure Ollama 4. Pick a model -5. Re-run `graycode preflight` -6. Re-run `graycode path` +5. Re-run `hawk preflight` +6. Re-run `hawk path` If security items still fail, fix those before treating the machine as ready. diff --git a/docs/DYNAMIC-MODELS.md b/docs/DYNAMIC-MODELS.md index 25109894c..574c8c51f 100644 --- a/docs/DYNAMIC-MODELS.md +++ b/docs/DYNAMIC-MODELS.md @@ -1,10 +1,10 @@ -# Dynamic Model Discovery — Graycode Developer Guide +# Dynamic Model Discovery — Hawk Developer Guide ## Ownership and flow -Graycode is the product face; GraycodeRouter is the provider engine. Graycode owns the CLI/TUI, +Hawk is the product face; Eyrie is the provider engine. Hawk owns the CLI/TUI, model-picker presentation, user intent, and output compatibility. The -`graycode-router/engine` facade alone owns provider registry details, credentials, +`eyrie/engine` facade alone owns provider registry details, credentials, discovery, catalog/cache policy, model aliases, deployment routing, and chat transport. @@ -12,23 +12,23 @@ transport. provider APIs / remote catalog / local cache | v - graycode-router/engine.Engine + eyrie/engine.Engine catalog + credentials + routing | stable host DTOs and methods v - Graycode composition and presentation + Hawk composition and presentation /config | models | conversation ``` -Production Graycode packages must not import GraycodeRouter packages below -`github.com/GrayCodeAI/graycode-router/engine`. Shell and AST guards enforce a +Production Hawk packages must not import Eyrie packages below +`github.com/GrayCodeAI/eyrie/engine`. Shell and AST guards enforce a zero-exception boundary. -## Graycode composition boundary +## Hawk composition boundary -`internal/config` is Graycode's control-plane composition root. It creates an -GraycodeRouter engine and projects engine models into Graycode UI and command contracts: +`internal/config` is Hawk's control-plane composition root. It creates an +Eyrie engine and projects engine models into Hawk UI and command contracts: ```go models, err := config.ListEngineModels(ctx, "anthropic", false) @@ -36,36 +36,36 @@ live, err := config.ListEngineModels(ctx, "anthropic", true) public, err := config.ListPublicEngineModels(ctx, "xiaomi_mimo_payg") ``` -Conversation construction goes through Graycode's `internal/engine` adapter. The -adapter translates Graycode-owned message, tool, usage, and stream DTOs to the -GraycodeRouter engine facade; conversation history, WAL, resume, approvals, and tool -execution remain Graycode-owned. +Conversation construction goes through Hawk's `internal/engine` adapter. The +adapter translates Hawk-owned message, tool, usage, and stream DTOs to the +Eyrie engine facade; conversation history, WAL, resume, approvals, and tool +execution remain Hawk-owned. ## Catalog and live discovery -The GraycodeRouter engine combines its provider registry, provider-scoped live -discovery, and the cache at `~/.graycode-router/model_catalog.json` by default. Override -the cache with `GRAYCODE_ROUTER_MODEL_CATALOG_PATH`. Credential and state dependencies +The Eyrie engine combines its provider registry, provider-scoped live +discovery, and the cache at `~/.eyrie/model_catalog.json` by default. Override +the cache with `EYRIE_MODEL_CATALOG_PATH`. Credential and state dependencies are injected into each Engine instance, so discovery does not need hidden process-global credentials. Use the normal cache-backed path for repeatable UI and automation: ```bash -graycode models list anthropic -graycode models list anthropic --json +hawk models list anthropic +hawk models list anthropic --json ``` Use a provider-scoped live request when current connectivity and credentials must be checked: ```bash -graycode models list anthropic --live -graycode models list anthropic --live --json -graycode models list anthropic --live --raw +hawk models list anthropic --live +hawk models list anthropic --live --json +hawk models list anthropic --live --raw ``` -`graycode preflight` reports **local readiness**: usable local state, a selected +`hawk preflight` reports **local readiness**: usable local state, a selected model, and presence of the required stored credential. It is intentionally cheap and does not prove that a remote provider accepts that credential. Treat a successful provider-scoped `--live` request (or `/config` live @@ -73,8 +73,8 @@ validation) as **live verified**. ## Stable command output -`graycode models list --json` is a Graycode-owned compatibility contract, not a direct -serialization of GraycodeRouter's evolving `engine.Model` DTO. Its stable fields are: +`hawk models list --json` is a Hawk-owned compatibility contract, not a direct +serialization of Eyrie's evolving `engine.Model` DTO. Its stable fields are: ```text id, input_price_per_1m, output_price_per_1m, context_window, max_output, @@ -83,37 +83,37 @@ server_tools, display_name, description, owner, live_metadata New fields must be additive. `--raw` returns provider-native `live_metadata` objects when available; for cache/public rows without native -metadata, it returns the stable Graycode compatibility row instead of `null`. +metadata, it returns the stable Hawk compatibility row instead of `null`. ## Custom gateways -Graycode converts effective `custom_providers` settings into +Hawk converts effective `custom_providers` settings into `engine.Options.CustomGateways` at its composition root. Custom gateway metadata is snapshotted per Engine instance. Do not register custom gateways -in GraycodeRouter process-global state: tests, parallel sessions, and future multi-tenant +in Eyrie process-global state: tests, parallel sessions, and future multi-tenant hosts must be isolated from one another. ## Adding or changing a provider 1. Implement registry, discovery, credentials, aliases, and transport behavior - behind GraycodeRouter's engine facade. -2. Add GraycodeRouter tests for cache and live discovery, credential status, selection, + behind Eyrie's engine facade. +2. Add Eyrie tests for cache and live discovery, credential status, selection, and generation/streaming. -3. Commit and verify standalone GraycodeRouter. -4. Advance Graycode's `../graycode-router` sibling checkout to that exact commit, then update - Graycode's module version when the GraycodeRouter revision is published. +3. Commit and verify standalone Eyrie. +4. Advance Hawk's `../eyrie` sibling checkout to that exact commit, then update + Hawk's module version when the Eyrie revision is published. 5. Verify both the workspace (`go.work`) and published-module (`GOWORK=off`) build modes. -Graycode changes are needed only for a new product behavior or an additive -Graycode-owned presentation field—not for provider-specific mechanics. +Hawk changes are needed only for a new product behavior or an additive +Hawk-owned presentation field—not for provider-specific mechanics. ## Checks ```bash -graycode models refresh -graycode models status -graycode preflight -make graycode-router-engine-guard +hawk models refresh +hawk models status +hawk preflight +make eyrie-engine-guard go test ./cmd ./internal/config ./internal/engine -count=1 ``` diff --git a/docs/ECOSYSTEM-CONFIG.md b/docs/ECOSYSTEM-CONFIG.md index 4ac2910b9..d045e1bc0 100644 --- a/docs/ECOSYSTEM-CONFIG.md +++ b/docs/ECOSYSTEM-CONFIG.md @@ -1,7 +1,7 @@ # graycode-eco Unified Config-as-Code Status: Draft / shared spec -Applies to: graycode, graycode-router, harrier, shrike, swift +Applies to: hawk, eyrie, harrier, shrike, swift This document specifies a **single, unified configuration schema** for the graycode-eco ecosystem: one declarative file (`graycode-eco.yaml`, with an equivalent @@ -11,7 +11,7 @@ file is the source of truth, version-controlled alongside a project, and each repo reads the slice of the schema it owns. Today each repo configures itself independently through its own env vars, -flags, and config files (graycode: `config.json` + `GRAYCODE_*`/`GRAYCODE_*` env; graycode-router: +flags, and config files (hawk: `config.json` + `HAWK_*`/`HAWK_*` env; eyrie: provider env vars; harrier: `~/.harrier/config.toml`; shrike: `TOK_*` env; swift: `SWIFT_*` env). This spec does **not** replace those mechanisms — it defines a superset schema and maps every setting back to the repo + existing env @@ -27,7 +27,7 @@ any runtime behavior. `graycode-eco.yaml` value > repo default. This preserves current behavior where env/flags are authoritative. 3. **Repo-owned sections.** Each top-level section is owned by one repo (with - `model`/`providers` shared by graycode + graycode-router). A repo only reads its sections. + `model`/`providers` shared by hawk + eyrie). A repo only reads its sections. 4. **Two encodings, one schema.** YAML is canonical for humans; the identical structure is valid JSON for machine generation. (harrier's on-disk format is TOML; its section maps 1:1 to `~/.harrier/config.toml`.) @@ -40,7 +40,7 @@ Search order (first found wins for the file itself; values still follow the runtime precedence above): 1. `--config ` flag (where a repo's CLI supports it) -2. `$GRAYCODE_ECO_CONFIG` +2. `$HAWK_ECO_CONFIG` 3. `./graycode-eco.yaml` (project root) 4. `~/.config/graycode-eco/config.yaml` @@ -49,7 +49,7 @@ runtime precedence above): ```yaml version: 1 -# ─── Shared: model + providers (graycode + graycode-router) ─────────────────────────────── +# ─── Shared: model + providers (hawk + eyrie) ─────────────────────────────── model: default: anthropic/claude-sonnet-4-5 # provider/model the agent uses small_fast: anthropic/claude-haiku # cheap model for trivial steps @@ -66,16 +66,16 @@ providers: api_key_env: GEMINI_API_KEY model: gemini-2.0-flash -# ─── graycode-router: gateway / runtime ─────────────────────────────────────────────── +# ─── eyrie: gateway / runtime ─────────────────────────────────────────────── gateway: - base_url: http://localhost:8080 # graycode-router endpoint graycode talks to - api_key_env: GRAYCODE_ROUTER_API_KEY + base_url: http://localhost:8080 # eyrie endpoint hawk talks to + api_key_env: EYRIE_API_KEY allow_insecure_public_api: false - deployment_routing: "" # GRAYCODE_ROUTER_DEPLOYMENT_ROUTING + deployment_routing: "" # EYRIE_DEPLOYMENT_ROUTING model_catalog: - path_env: GRAYCODE_ROUTER_MODEL_CATALOG_PATH - url_env: GRAYCODE_ROUTER_MODEL_CATALOG_URL - refresh: GRAYCODE_ROUTER_MODEL_CATALOG_REFRESH + path_env: EYRIE_MODEL_CATALOG_PATH + url_env: EYRIE_MODEL_CATALOG_URL + refresh: EYRIE_MODEL_CATALOG_REFRESH # ─── harrier: memory ─────────────────────────────────────────────────────────── memory: @@ -119,9 +119,9 @@ swift: telemetry: # OTel exporter settings shared by all repos. Span attribute keys follow # docs/OTEL-CONVENTIONS.md. - enabled: false # graycode: GRAYCODE_ENABLE_TELEMETRY + enabled: false # hawk: HAWK_ENABLE_TELEMETRY otlp_endpoint: "" # OTEL_EXPORTER_OTLP_ENDPOINT - shutdown_timeout_ms: 0 # GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS + shutdown_timeout_ms: 0 # HAWK_OTEL_SHUTDOWN_TIMEOUT_MS ``` ## Setting → repo → existing mechanism @@ -129,30 +129,30 @@ telemetry: The authoritative mapping. "Mechanism today" is what already implements the setting; the unified key is rendered down to it. -### Shared: model / providers (graycode + graycode-router) +### Shared: model / providers (hawk + eyrie) | Unified key | Repo | Mechanism today | |---------------------------------|-------------|--------------------------------------------------| -| `model.default` | graycode | `GRAYCODE_MODEL` env / `config.json` | -| `model.small_fast` | graycode | `GRAYCODE_SMALL_FAST_MODEL` env | -| `providers[].api_key_env` | graycode-router/graycode | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `ZAI_API_KEY`, `CANOPYWAVE_API_KEY`, `FIREWORKS_API_KEY` | -| `providers[].base_url_env` | graycode-router | `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` / `OPENAI_API_BASE`, `OLLAMA_BASE_URL`, `FIREWORKS_BASE_URL` | -| `providers[].model` (openai) | graycode-router | `OPENAI_MODEL` env | -| `providers[].model` (gemini) | graycode-router | `GEMINI_MODEL` env | -| `providers[].model` (anthropic) | graycode-router | `ANTHROPIC_MODEL` env | +| `model.default` | hawk | `HAWK_MODEL` env / `config.json` | +| `model.small_fast` | hawk | `HAWK_SMALL_FAST_MODEL` env | +| `providers[].api_key_env` | eyrie/hawk | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, `XAI_API_KEY`, `ZAI_API_KEY`, `CANOPYWAVE_API_KEY`, `FIREWORKS_API_KEY` | +| `providers[].base_url_env` | eyrie | `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` / `OPENAI_API_BASE`, `OLLAMA_BASE_URL`, `FIREWORKS_BASE_URL` | +| `providers[].model` (openai) | eyrie | `OPENAI_MODEL` env | +| `providers[].model` (gemini) | eyrie | `GEMINI_MODEL` env | +| `providers[].model` (anthropic) | eyrie | `ANTHROPIC_MODEL` env | -### graycode-router: gateway / runtime +### eyrie: gateway / runtime -| Unified key | Mechanism today (graycode-router) | +| Unified key | Mechanism today (eyrie) | |--------------------------------------|--------------------------------------| -| `gateway.base_url` | `GRAYCODE_ROUTER_BASE_URL` (graycode→graycode-router link) | -| `gateway.api_key_env` | `GRAYCODE_ROUTER_API_KEY` | -| `gateway.allow_insecure_public_api` | `GRAYCODE_ROUTER_ALLOW_INSECURE_PUBLIC_API` | -| `gateway.deployment_routing` | `GRAYCODE_ROUTER_DEPLOYMENT_ROUTING` (also `GRAYCODE_DEPLOYMENT_ROUTING`) | -| `gateway.model_catalog.path_env` | `GRAYCODE_ROUTER_MODEL_CATALOG_PATH` | -| `gateway.model_catalog.url_env` | `GRAYCODE_ROUTER_MODEL_CATALOG_URL` | -| `gateway.model_catalog.refresh` | `GRAYCODE_ROUTER_MODEL_CATALOG_REFRESH` / `GRAYCODE_AUTO_REFRESH_CATALOG` / `GRAYCODE_CATALOG_REFRESH_ALWAYS` | -| `gateway` config dir | `GRAYCODE_CONFIG_DIR` (default `~/.graycode-router`) | +| `gateway.base_url` | `EYRIE_BASE_URL` (hawk→eyrie link) | +| `gateway.api_key_env` | `EYRIE_API_KEY` | +| `gateway.allow_insecure_public_api` | `EYRIE_ALLOW_INSECURE_PUBLIC_API` | +| `gateway.deployment_routing` | `EYRIE_DEPLOYMENT_ROUTING` (also `HAWK_DEPLOYMENT_ROUTING`) | +| `gateway.model_catalog.path_env` | `EYRIE_MODEL_CATALOG_PATH` | +| `gateway.model_catalog.url_env` | `EYRIE_MODEL_CATALOG_URL` | +| `gateway.model_catalog.refresh` | `EYRIE_MODEL_CATALOG_REFRESH` / `HAWK_AUTO_REFRESH_CATALOG` / `HAWK_CATALOG_REFRESH_ALWAYS` | +| `gateway` config dir | `HAWK_CONFIG_DIR` (default `~/.eyrie`) | ### harrier: memory @@ -193,22 +193,22 @@ few env vars. ### swift + telemetry -| Unified key | Mechanism today (swift / graycode) | +| Unified key | Mechanism today (swift / hawk) | |-----------------------------------|-------------------------------------------------| | `swift.search_url` | `SWIFT_SEARCH_URL` env | | `swift.log_level` | `SWIFT_LOG_LEVEL` env | | `swift.telemetry_optout` | `SWIFT_TELEMETRY_OPTOUT` / `SWIFT_NO_TELEMETRY` env | | `swift.posthog.api_key_env` | `POSTHOG_API_KEY` env | | `swift.posthog.endpoint` | `POSTHOG_ENDPOINT` env | -| `telemetry.enabled` | `GRAYCODE_ENABLE_TELEMETRY` env (graycode) | -| `telemetry.shutdown_timeout_ms` | `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS` env (graycode) | +| `telemetry.enabled` | `HAWK_ENABLE_TELEMETRY` env (hawk) | +| `telemetry.shutdown_timeout_ms` | `HAWK_OTEL_SHUTDOWN_TIMEOUT_MS` env (hawk) | | `telemetry.otlp_endpoint` | `OTEL_EXPORTER_OTLP_ENDPOINT` (standard OTel) | ## Rendering down to per-repo config The unified file is designed to be **resolved** into the existing mechanisms: -- **env-based repos** (graycode, graycode-router, shrike, swift): export the mapped env var for +- **env-based repos** (hawk, eyrie, shrike, swift): export the mapped env var for any key set in `graycode-eco.yaml` that is not already present in the process environment (preserving "env wins" precedence). - **file-based repos** (harrier): write/merge the `memory.*` section into @@ -230,7 +230,7 @@ The schema is encoding-agnostic. The YAML above is identical in structure to: "providers": [ { "name": "anthropic", "api_key_env": "ANTHROPIC_API_KEY" } ], - "gateway": { "base_url": "http://localhost:8080", "api_key_env": "GRAYCODE_ROUTER_API_KEY" }, + "gateway": { "base_url": "http://localhost:8080", "api_key_env": "EYRIE_API_KEY" }, "memory": { "addr": "127.0.0.1:3456", "data_dir": "~/.harrier" }, "compression": { "enabled": true, "db_path": "~/.shrike/usage.db" }, "swift": { "telemetry_optout": false }, @@ -242,5 +242,5 @@ The schema is encoding-agnostic. The YAML above is identical in structure to: The `telemetry` and `swift` sections only configure *transport/opt-out*. The *attribute vocabulary* emitted on spans is defined separately in -[`OTEL-CONVENTIONS.md`](./OTEL-CONVENTIONS.md), with graycode-router's +[`OTEL-CONVENTIONS.md`](./OTEL-CONVENTIONS.md), with eyrie's `internal/observability` package as the reference implementation. diff --git a/docs/ECOSYSTEM-ROADMAP.md b/docs/ECOSYSTEM-ROADMAP.md index 1199f40d7..d15f5d59d 100644 --- a/docs/ECOSYSTEM-ROADMAP.md +++ b/docs/ECOSYSTEM-ROADMAP.md @@ -1,7 +1,7 @@ # GrayCode Ecosystem Roadmap (2026) **Status:** Active · **Last updated:** 2026-09-09 -**Scope:** the 4 in-workspace repos — `graycode-cli`, `graycode-router`, +**Scope:** the 4 in-workspace repos — `hawk`, `eyrie`, `graycode-skills`, `graycode-platform` — plus the 5 external engine repos they depend on. **Evidence base:** `docs/RESEARCH.md`, `docs/COMPETITIVE.md`, `docs/plans/competitive-gap-*.md` @@ -29,7 +29,7 @@ load-bearing bets: 3. **Portable execution graph** — every agent run is exported as a verifiable, hash-addressed graph (provenance for replay/audit). 4. **Router-facade-only provider access** — the CLI never talks to an LLM API - directly; `graycode-router/engine` is the sole boundary. + directly; `eyrie/engine` is the sole boundary. The roadmap is organized around making those bets *true and honest* end-to-end, then extending the surface where the field (Codex, OpenCode, Gemini CLI, Goose, @@ -37,7 +37,7 @@ Claude Code) has proven demand. ### The single most important fact (historical) -`graycode-cli` imports **5 engine modules** — `harrier` (memory graph), +`hawk` imports **5 engine modules** — `harrier` (memory graph), `shrike` (token/compress), `kestrel` (code review), `merlin` (site audit), `swift` (session correlation) — that were **absent from this workspace and 404 on GitHub**. They were replaced in `go.mod` by no-op stubs, so the CLI compiled @@ -55,12 +55,12 @@ dropped. | Repo | Role | Build | Tests | Health | |---|---|---|---|---| -| `graycode-cli` | Product face (Go, Bubble Tea v2) | ✅ `go build ./...` | ✅ 179 pkgs green | Engines restored; memory/token/review/audit/correlation live | -| `graycode-router` | Provider runtime (Go) | ✅ | ✅ 37 pkgs green | Healthy; 28 providers; gRPC ChatService wired | +| `hawk` | Product face (Go, Bubble Tea v2) | ✅ `go build ./...` | ✅ 179 pkgs green | Engines restored; memory/token/review/audit/correlation live | +| `eyrie` | Provider runtime (Go) | ✅ | ✅ 37 pkgs green | Healthy; 28 providers; gRPC ChatService wired | | `graycode-skills` | Skill marketplace (Python) | ✅ | ✅ 382/382 | Healthy; single parsed schema | | `graycode-platform` | Web + Cloud control plane (TS) | ✅ | ✅ 320 worker / 33 web | Healthy; worker+bff routes added; migration deduped | -### 2.1 graycode-cli +### 2.1 hawk - Builds and vets clean **only because** the 5 engines are stubbed. - **5 failing tests:** `internal/token/shrike_test.go` asserts real token counts @@ -69,10 +69,10 @@ dropped. - `internal/token/shrike.go` — token counting/compression → 0 / identity. - `internal/intelligence/memory/harrier_bridge.go` — `Ready()` is true but nothing persists (stub store returns nil DB). - - `internal/bridge/kestrel/bridge.go` — `graycode review run|analyze` always + - `internal/bridge/kestrel/bridge.go` — `hawk review run|analyze` always report "no issues found" / status `Passed`. - `internal/bridge/merlin/bridge.go` — site audits return 0 pages/findings. - - `cmd/swift.go` / `cmd/swift_correlation.go` — `graycode swift` has no + - `cmd/swift.go` / `cmd/swift_correlation.go` — `hawk swift` has no working subcommands; correlation silently errors. - `internal/engine/compact.go` etc. — context compaction depends on shrike. - **Orphaned code:** `cmd/merlin_pipeline.go` defines + unit-tests @@ -83,7 +83,7 @@ dropped. `shrike: embedded · token/compress pipeline OK (sample=0 tokens)` — sample 0 should be a red flag, not OK. -### 2.2 graycode-router +### 2.2 eyrie - Healthy and self-contained. 28 `ProviderSpec`s, ~25 adapters, weighted/strategy LB, retries, semantic cache, circuit breakers, deployment router, OpenAI-compat @@ -187,7 +187,7 @@ Make the current state truthful and green. **No new user-facing features.** | # | Repo | Task | Acceptance | Effort | |---|---|---|---|---| | 0.1 | cli | Add `token.ShrikeAvailable()` functional probe; guard the 5 shrike boundary tests to skip when the engine is the stub | `go test ./internal/token/...` green; stub detected as unavailable | 0.5d ✅ | -| 0.2 | cli | Report shrike honestly in `ecosystem` / `doctor` (unavailable when stub, not "pipeline OK (sample=0)") | `graycode ecosystem` flags stub; JSON `shrike.embedded=false` | 0.5d ✅ | +| 0.2 | cli | Report shrike honestly in `ecosystem` / `doctor` (unavailable when stub, not "pipeline OK (sample=0)") | `hawk ecosystem` flags stub; JSON `shrike.embedded=false` | 0.5d ✅ | | 0.3 | skills | Make `manifest-schema.toml` the single source of truth; wire a validator into CI that enforces the *enforced* schema (not the aspirational one) | schema parsed by tooling; CI gate matches reality; corpus still 0-warning | 1-2d ✅ | | 0.4 | skills | Add a declared dev/test dependency group so `pytest`/`ruff`/`pytest-cov` install reproducibly | `pip install -e '.[dev]'` then `pytest` green | 0.5d ✅ | | 0.5 | platform | Renumber duplicate migration `0022_identity_ui.sql` → `0023`; dedupe identity schema note | migrations apply in deterministic order; tests green | 0.5d ✅ | @@ -209,14 +209,14 @@ memory, token, review, audit, and correlation features are dead. | 1.5 | `swift` | session correlation / checkpoint linking | `graph correlation` subcommand the CLI shells into | **Acceptance:** `go.mod` `replace` directives removed; `make check-replace` -passes; `graycode path` shows all engines genuinely ready; `go test ./...` +passes; `hawk path` shows all engines genuinely ready; `go test ./...` green with real engines. **Effort:** 4-8 weeks total across the 5 repos. ### Phase 2 — Product honesty + verification surface (P1) | # | Repo | Task | Acceptance | |---|---|---|---| -| 2.1 | cli | Wire `RunMerlinPipeline` into a `graycode audit`/`merlin` command (orphaned code) | command runs the pipeline end-to-end with a real merlin | +| 2.1 | cli | Wire `RunMerlinPipeline` into a `hawk audit`/`merlin` command (orphaned code) | command runs the pipeline end-to-end with a real merlin | | 2.2 | cli | Wire `BeamSearch` into the live agent loop as scorer/expander | tree-search used for planning with real model outputs | | 2.3 | cli | Publish benchmark numbers in README (Gap-04 follow-through) | `make bench` numbers in README, cited | | 2.4 | router | Wire the gRPC `ChatService` behind the `grpc` build tag | `grpc`-tagged build serves Chat; unit-tested | @@ -241,7 +241,7 @@ green with real engines. **Effort:** 4-8 weeks total across the 5 repos. - IDE integration (VS Code extension) — only after engines restored. - Hosted share links / multi-session grid (herdr-style) — Gap-02 follow-through. -- Provider-count messaging: expose `graycode-router` catalog count dynamically. +- Provider-count messaging: expose `eyrie` catalog count dynamically. - OpenTelemetry/metrics consolidation across engines (per `OTEL-CONVENTIONS.md`). --- @@ -270,7 +270,7 @@ green with real engines. **Effort:** 4-8 weeks total across the 5 repos. ## 7. Metrics - `go test ./...` green in all Go repos; `make ci` green. -- `graycode path` / `graycode ecosystem` report engine availability honestly. +- `hawk path` / `hawk ecosystem` report engine availability honestly. - Skills: 0-warning corpus maintained; single parsed schema. - Platform: worker reachable at a stable domain; migrations deterministic. - Benchmarks published (per Gap-04). diff --git a/docs/ECOSYSTEM-WIRING.md b/docs/ECOSYSTEM-WIRING.md index 22097bc7f..a9fdabac5 100644 --- a/docs/ECOSYSTEM-WIRING.md +++ b/docs/ECOSYSTEM-WIRING.md @@ -1,7 +1,7 @@ # GrayCode ecosystem wiring > NOTE (2026-09-04): the `eagle` repository has been removed; its contracts -> were vendored into graycode-cli's `internal/contracts` and `ecosystem.yaml` +> were vendored into hawk's `internal/contracts` and `ecosystem.yaml` > no longer lists it. Diagrams below predate the removal. This document is the implementation contract for the 14 repositories in the @@ -17,23 +17,23 @@ flowchart LR Lists --> Scripts[Workspace and release scripts] Lists --> Owl[Owl catalog] - Graycode[Graycode product] --> Engines[Six engine repositories] + Hawk[Hawk product] --> Engines[Six engine repositories] Engines --> Eagle[Eagle contracts] - Graycode --> Eagle + Hawk --> Eagle - GraycodeSpec[Graycode OpenAPI] -. manual snapshots .-> Sparrow - GraycodeSpec -. manual snapshots .-> Robin - GraycodeSpec -. no snapshot .-> Wren + HawkSpec[Hawk OpenAPI] -. manual snapshots .-> Sparrow + HawkSpec -. manual snapshots .-> Robin + HawkSpec -. no snapshot .-> Wren Browser[GrayCode browser] --> Mixed[One mixed Worker] Mixed --> Identity[(Identity tables)] - Mixed --> Cloud[(Graycode Cloud tables)] + Mixed --> Cloud[(Hawk Cloud tables)] Mixed -. fire-and-forget .-> Queue[Usage Queue] ``` The runtime engine integrations were mostly sound, but the surrounding wiring could drift: repository names appeared independently in shell scripts and Owl, -SDK contracts were inconsistent, browser identity and the Graycode control plane +SDK contracts were inconsistent, browser identity and the Hawk control plane shared a Worker and D1 binding, and a successful usage write did not guarantee a durable Queue-delivery intent. @@ -41,21 +41,21 @@ durable Queue-delivery intent. ```mermaid flowchart TB - Manifest[graycode/ecosystem.yaml\ncanonical repo and contract inventory] + Manifest[hawk/ecosystem.yaml\ncanonical repo and contract inventory] Manifest --> Workspace[generated root go.work] Manifest --> Guards[boundary and release parity guards] Manifest --> OwlSnapshot[owl/ecosystem.json] subgraph Local[Local-first runtime] - User[CLI / daemon user] --> Graycode[Graycode composition root] - Graycode --> GraycodeRouter[GraycodeRouter facade] - Graycode --> Harrier[Harrier / Harrier facade] - Graycode --> Shrike[Shrike / Shrike facade] - Graycode --> Swift[Swift / Swift facade] - Graycode --> Kestrel[Kestrel / Kestrel facade] - Graycode --> Merlin[Merlin / Merlin facade] - Graycode --> Eagle[Eagle neutral contracts] - GraycodeRouter --> Eagle + User[CLI / daemon user] --> Hawk[Hawk composition root] + Hawk --> Eyrie[Eyrie facade] + Hawk --> Harrier[Harrier / Harrier facade] + Hawk --> Shrike[Shrike / Shrike facade] + Hawk --> Swift[Swift / Swift facade] + Hawk --> Kestrel[Kestrel / Kestrel facade] + Hawk --> Merlin[Merlin / Merlin facade] + Hawk --> Eagle[Eagle neutral contracts] + Eyrie --> Eagle Harrier --> Eagle Shrike --> Eagle Swift --> Eagle @@ -63,7 +63,7 @@ flowchart TB Merlin --> Eagle end - Graycode --> Daemon[Graycode daemon API] + Hawk --> Daemon[Hawk daemon API] Daemon --> OpenAPI[api/openapi.yaml] OpenAPI --> Sparrow[Sparrow Go SDK snapshot] OpenAPI --> Robin[Robin Python SDK snapshot] @@ -72,7 +72,7 @@ flowchart TB subgraph Hosted[Optional hosted plane] Browser[GrayCode browser] --> BFF[GrayCode identity/BFF Worker] BFF --> Identity[(Identity D1)] - BFF -->|private typed Service Binding| Cloud[Graycode Cloud Worker] + BFF -->|private typed Service Binding| Cloud[Hawk Cloud Worker] Cloud --> Control[(Control-plane D1)] Cloud -->|same D1 transaction| Outbox[(Usage outbox)] Outbox -->|immediate + scheduled retry| Queue[Cloudflare Queue] @@ -80,21 +80,21 @@ flowchart TB Queue --> Archive[(R2 archive)] end - Graycode -. explicit opt-in; fail-open .-> Cloud + Hawk -. explicit opt-in; fail-open .-> Cloud ``` ## Ownership rules | Boundary | Owner | Rule | |---|---|---| -| Product orchestration | Graycode | Engines never orchestrate Graycode or one another. | -| Provider runtime | GraycodeRouter | Graycode imports its supported `engine` facade. | +| Product orchestration | Hawk | Engines never orchestrate Hawk or one another. | +| Provider runtime | Eyrie | Hawk imports its supported `engine` facade. | | Shared data contracts | Eagle | Neutral types only; no product behavior. | -| Portable local graph | Source engine/Graycode | Emit bounded `graycode.graph/v1` facts without raw secrets or prompts. | -| Daemon HTTP API | Graycode | `graycode/api/openapi.yaml` is authoritative. | +| Portable local graph | Source engine/Hawk | Emit bounded `hawk.graph/v1` facts without raw secrets or prompts. | +| Daemon HTTP API | Hawk | `hawk/api/openapi.yaml` is authoritative. | | SDK endpoint support | Each SDK | Exact contract snapshot plus an explicit decision for every path. | | Browser identity | GrayCode BFF | Users, sessions, email, API keys, and UI activity remain in identity D1. | -| Hosted control plane | Graycode Cloud | Organizations, projects, devices, usage, billing, graph ledger, and audit. | +| Hosted control plane | Hawk Cloud | Organizations, projects, devices, usage, billing, graph ledger, and audit. | | Cloud delivery | D1 outbox + Queue | Business state and delivery intent commit together; consumers are idempotent. | | Architecture discovery | Owl | Generated projection of the canonical manifest, never a second inventory. | @@ -112,7 +112,7 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. 2. Eagle commit `9d358dde4ad8` is the cross-repository contract revision, pinned through its reachable Go pseudo-version until the next semver tag is published; the parity gate checks every declared consumer. -3. Local graph projections use `graycode.graph/v1`. Graycode Cloud independently +3. Local graph projections use `hawk.graph/v1`. Hawk Cloud independently validates and privacy-normalizes them into `graycode-cloud.graph/v1`. 4. Usage ingestion commits its idempotency claim, budget counter, event, billing ledger row, daily activity projection, and `usage.recorded.v1` @@ -123,12 +123,12 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. ## Release and development workflow -- Run `make workspace` in Graycode to regenerate the parent `go.work`. +- Run `make workspace` in Hawk to regenerate the parent `go.work`. - Run `make boundaries` to validate the inventory, module isolation, Eagle parity, facade locations, and support-repository coupling. - Root `go.mod` files never contain local `replace` directives. Local sibling substitutions exist only in the generated, uncommitted parent workspace. -- SDK CI checks its snapshot byte-for-byte against Graycode and separately checks +- SDK CI checks its snapshot byte-for-byte against Hawk and separately checks that every OpenAPI path has a support decision. - Run `owl/scripts/sync-ecosystem.sh` after changing the canonical inventory; Owl CI rejects drift. @@ -137,33 +137,33 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. ### Coordinated Go publication gate -The compatible Eagle-migrated GraycodeRouter source is currently ahead of GraycodeRouter's -published `origin/main`. Do not merge or release Graycode against the older GraycodeRouter -pseudo-version: it still exposes the retired `graycode-core-contracts` types and is -not type-compatible with Graycode's Eagle boundary. +The compatible Eagle-migrated Eyrie source is currently ahead of Eyrie's +published `origin/main`. Do not merge or release Hawk against the older Eyrie +pseudo-version: it still exposes the retired `hawk-core-contracts` types and is +not type-compatible with Hawk's Eagle boundary. -1. Merge and publish the GraycodeRouter ecosystem-wiring branch first. +1. Merge and publish the Eyrie ecosystem-wiring branch first. 2. Resolve that final remote commit to its canonical Go pseudo-version with - `go list -m github.com/GrayCodeAI/graycode-router@`. -3. Update Graycode's GraycodeRouter requirement, run `GOWORK=off go mod tidy`, and remove any + `go list -m github.com/GrayCodeAI/eyrie@`. +3. Update Hawk's Eyrie requirement, run `GOWORK=off go mod tidy`, and remove any transition excludes no longer required by the published engine graphs. -4. Require Graycode's `public-modules` and `release-parity` CI jobs to pass before - merging or tagging Graycode. +4. Require Hawk's `public-modules` and `release-parity` CI jobs to pass before + merging or tagging Hawk. This publication is intentionally not performed by the source implementation: remote branch merges and tags are externally visible release operations. ## Deployment sequence for the Worker split -1. Create or automatically provision the `graycode-identity` D1 database and +1. Create or automatically provision the `hawk-identity` D1 database and confirm its binding in `apps/bff/wrangler.jsonc`. 2. Export the existing identity tables from the legacy mixed database and import them into the identity database; verify row counts and login flows. -3. Deploy `graycode-cloud` with the named `GraycodeCloudService` entrypoint and +3. Deploy `graycode-cloud` with the named `HawkCloudService` entrypoint and apply migration `0023_usage_outbox.sql`. -4. Deploy `graycode-bff` with its D1 binding and private service binding. -5. Point the web frontend API hostname at the BFF. Graycode/CLI device start and - poll traffic continues to target Graycode Cloud. +4. Deploy `hawk-bff` with its D1 binding and private service binding. +5. Point the web frontend API hostname at the BFF. Hawk/CLI device start and + poll traffic continues to target Hawk Cloud. 6. After a rollback window, remove the legacy identity tables from the old cloud D1 using a separately reviewed data-retirement migration. diff --git a/docs/IMPLEMENTATION-ROADMAP.md b/docs/IMPLEMENTATION-ROADMAP.md index 6f81cdeef..83a64f175 100644 --- a/docs/IMPLEMENTATION-ROADMAP.md +++ b/docs/IMPLEMENTATION-ROADMAP.md @@ -5,7 +5,7 @@ **Date:** 2026-07-05 **Source:** Historical comparison document; feature and market claims require independent revalidation. Architecture status is tracked in -`docs/architecture/graycode-architecture-baseline.md`. +`docs/architecture/hawk-architecture-baseline.md`. --- @@ -27,14 +27,14 @@ Numeric scores are intentionally not used as current architecture evidence. ## Phase 1: High Priority (Immediate - Score Impact: +0.5) -### 1. Add VS Code Extension Integration (graycode repo) +### 1. Add VS Code Extension Integration (hawk repo) **Effort:** Large (2-3 weeks) **Priority:** HIGH **What:** -- Create `graycode-vscode` repo for VS Code extension -- Use Graycode SDK for communication +- Create `hawk-vscode` repo for VS Code extension +- Use Hawk SDK for communication - Add WebSocket transport for real-time updates **Why:** @@ -45,12 +45,12 @@ Numeric scores are intentionally not used as current architecture evidence. **Implementation Plan:** ```bash # Create new repo -mkdir graycode-vscode -cd graycode-vscode +mkdir hawk-vscode +cd hawk-vscode # Initialize git init -go mod init github.com/GrayCodeAI/graycode-cli-vscode +go mod init github.com/GrayCodeAI/hawk-vscode # Add extension scaffold mkdir -p cmd extension/src @@ -64,8 +64,8 @@ go get golang.org/x/net/websocket # Create extension files cat > extension/package.json << 'EOF' { - "name": "graycode", - "displayName": "Graycode", + "name": "hawk", + "displayName": "Hawk", "description": "AI coding assistant in terminal", "version": "0.1.0", "engines": { "vscode": "^1.90.0" }, @@ -75,14 +75,14 @@ cat > extension/package.json << 'EOF' "contributes": { "commands": [ { - "command": "graycode.activate", - "title": "Activate Graycode" + "command": "hawk.activate", + "title": "Activate Hawk" } ], "menus": { "commandPalette": [ { - "command": "graycode.activate", + "command": "hawk.activate", "when": "editorLangId" } ] @@ -94,22 +94,22 @@ EOF # Add extension scaffold cat > extension/src/extension.ts << 'EOF' import * as vscode from 'vscode'; -import * as graycodesdk from '@graycodeai/graycodesdk'; +import * as hawksdk from '@graycodeai/hawksdk'; export function activate(context: vscode.ExtensionContext) { - const client = new graycodesdk.Client(); + const client = new hawksdk.Client(); // Register completion provider vscode.languages.registerCompletionItemProvider( 'go', - new GraycodeCompletionProvider(client), + new HawkCompletionProvider(client), 'g', 'h' ); // Register hover provider vscode.languages.registerHoverProvider( 'go', - new GraycodeHoverProvider(client) + new HawkHoverProvider(client) ); } EOF @@ -123,7 +123,7 @@ vsce publish **Repository Structure:** ``` -graycode-vscode/ +hawk-vscode/ ├── extension/ # VS Code extension code │ ├── src/ │ │ ├── extension.ts @@ -140,25 +140,25 @@ graycode-vscode/ --- -### 2. Add Extension Marketplace (graycode repo) +### 2. Add Extension Marketplace (hawk repo) **Effort:** Medium (1-2 weeks) **Priority:** HIGH **What:** - Create marketplace for community extensions -- Add extension discovery endpoint to graycode +- Add extension discovery endpoint to hawk - Create `starling` integration - Add version compatibility checking **Why:** - Top agents have 100+ extensions -- Build ecosystem around Graycode +- Build ecosystem around Hawk - Increase adoption **Implementation Plan:** ```go -// Add to graycode +// Add to hawk package marketplace // Extension represents a community extension @@ -305,12 +305,12 @@ cat >> SKILL.md << 'EOF' To discover available extensions, use: ``` -graycode extensions list +hawk extensions list ``` To install an extension: ``` -graycode extensions install +hawk extensions install ``` EOF ``` @@ -431,7 +431,7 @@ go build forum.go --- -### 5. Add More Extension Points (graycode) +### 5. Add More Extension Points (hawk) **Effort:** Small (3-5 days) **Priority:** MEDIUM @@ -499,7 +499,7 @@ func main() { ## Phase 3: Low Priority (Backlog - Score Impact: +0.2) -### 6. Add AI Code Completion (graycode-router) +### 6. Add AI Code Completion (eyrie) **Effort:** Medium (1-2 weeks) **Priority:** LOW @@ -514,7 +514,7 @@ func main() { --- -### 7. Add Debugging Support (graycode) +### 7. Add Debugging Support (hawk) **Effort:** Large (2-3 weeks) **Priority:** LOW @@ -530,7 +530,7 @@ func main() { --- -### 8. Add Web UI for Monitoring (graycode) +### 8. Add Web UI for Monitoring (hawk) **Effort:** Small (3-5 days) **Priority:** LOW @@ -562,7 +562,7 @@ func main() { --- -### 10. Add Completion Endpoint to graycode-router +### 10. Add Completion Endpoint to eyrie **Effort:** Medium (1 week) **Priority:** LOW @@ -579,7 +579,7 @@ func main() { ## Detailed Repo-Specific Roadmap -### **graycode** (Main Repo) - Primary Product +### **hawk** (Main Repo) - Primary Product | Phase | Improvement | Effort | Impact | Status | |-------|--------------|--------|--------|--------| @@ -590,7 +590,7 @@ func main() { | 3 | Add Web UI for monitoring | Small | +0.2 | Planned | | 3 | Add SDK analytics | Small | +0.1 | Planned | -Current architecture status: see `docs/architecture/graycode-architecture-baseline.md`. +Current architecture status: see `docs/architecture/hawk-architecture-baseline.md`. --- @@ -628,7 +628,7 @@ Current architecture status: see `docs/architecture/graycode-architecture-baseli --- -### **graycode-router** (LLM Runtime) +### **eyrie** (LLM Runtime) | Phase | Improvement | Effort | Impact | Status | |-------|--------------|--------|--------|--------| @@ -657,7 +657,7 @@ Current architecture status: see `docs/architecture/graycode-architecture-baseli The roadmap is sequenced by product value and implementation effort. It does not assign target architecture scores. Current architecture status is tracked -in `docs/architecture/graycode-architecture-baseline.md`. +in `docs/architecture/hawk-architecture-baseline.md`. --- @@ -667,7 +667,7 @@ in `docs/architecture/graycode-architecture-baseline.md`. |------|--------|------------| | Marketplace competition | High | Focus on quality extensions | | Community building | Medium | Active engagement, good docs | -| Development velocity | Medium | Prioritize graycode SDK first | +| Development velocity | Medium | Prioritize hawk SDK first | | Adoption curve | Medium | VS Code extension is key | --- diff --git a/docs/OTEL-CONVENTIONS.md b/docs/OTEL-CONVENTIONS.md index 418f27cea..ad82b42fe 100644 --- a/docs/OTEL-CONVENTIONS.md +++ b/docs/OTEL-CONVENTIONS.md @@ -1,7 +1,7 @@ # graycode-eco OpenTelemetry Semantic Conventions for AI Agent Spans Status: Draft / shared spec -Applies to: graycode, graycode-router, harrier, shrike, swift +Applies to: hawk, eyrie, harrier, shrike, swift This document defines the **ecosystem-wide** OpenTelemetry (OTel) semantic conventions that every graycode-eco repo should follow when emitting spans for AI @@ -19,24 +19,24 @@ concerns that the GenAI spec does not yet standardize. ## Reference ownership -GraycodeRouter owns provider-call instrumentation behind its `graycode-router/engine` facade. +Eyrie owns provider-call instrumentation behind its `eyrie/engine` facade. Its lower provider layer contains the reference OTel decorator for chat and stream calls: it starts a client span, records provider/model/usage attributes, sets status from the result, and ends a streamed span on completion. That -decorator is an GraycodeRouter implementation detail; Graycode must not import or compose it +decorator is an Eyrie implementation detail; Hawk must not import or compose it directly. -- `graycode-router/internal/observability/observability.go` provides a stdlib-only, +- `eyrie/internal/observability/observability.go` provides a stdlib-only, zero-dependency telemetry/metrics layer (spans, latency histograms, Prometheus + JSON export) for environments that cannot pull in the OTel SDK. -- `graycode-router/internal/observability/genai_semconv.go` exports the canonical +- `eyrie/internal/observability/genai_semconv.go` exports the canonical attribute-key constants defined below, so Go code can reference them instead of hard-coding strings. A pinning test (`genai_semconv_test.go`) guards the exact key values. -When adding tracing to Graycode, propagate swift context through the Engine call and -use the attribute keys in this document. GraycodeRouter wraps provider operations; -Graycode wraps product turns and tools. Harrier, Shrike, and Swift instrument only their +When adding tracing to Hawk, propagate swift context through the Engine call and +use the attribute keys in this document. Eyrie wraps provider operations; +Hawk wraps product turns and tools. Harrier, Shrike, and Swift instrument only their own operations. ## Span kinds and names @@ -52,7 +52,7 @@ own operations. LLM/provider request spans SHOULD use span kind `CLIENT`. Tool invocations and agent steps that represent internal work SHOULD use span kind `INTERNAL`. -graycode-router's existing stdlib layer also defines short span names +eyrie's existing stdlib layer also defines short span names (`llm.chat`, `llm.stream`, `llm.retry`, `llm.cache_hit`) — these remain valid for the internal metrics collector; the names above are the cross-repo convention for OTLP-exported spans. @@ -60,10 +60,10 @@ convention for OTLP-exported spans. ## Required / recommended attributes The required attribute set for any AI agent span. Keys are exported as Go -constants in `graycode-router/internal/observability/genai_semconv.go` (constant name in +constants in `eyrie/internal/observability/genai_semconv.go` (constant name in parentheses). -| Attribute key | Go const (`graycode-router` observability) | Type | Required | Meaning | +| Attribute key | Go const (`eyrie` observability) | Type | Required | Meaning | |---------------------------------|------------------------------------|--------|----------|----------------------------------------------------------------| | `gen_ai.system` | `AttrGenAISystem` | string | yes | Provider/system: `openai`, `anthropic`, `gemini`, etc. | | `gen_ai.request.model` | `AttrGenAIRequestModel` | string | yes | Model requested by the caller, e.g. `gpt-4o`. | @@ -79,13 +79,13 @@ parentheses). ### Notes - **Tokens as integers.** Emit `gen_ai.usage.input_tokens` / - `gen_ai.usage.output_tokens` as OTel integer attributes. graycode-router's stdlib + `gen_ai.usage.output_tokens` as OTel integer attributes. eyrie's stdlib `Span.Attributes` is `map[string]string`; when bridging that layer to OTLP, convert to integer attributes. - **Cost is a double in USD.** `cost.usd` is the ecosystem standard. - graycode-router's metrics collector stores cost internally in micro-USD for precision + eyrie's metrics collector stores cost internally in micro-USD for precision (`costMicroUSD`) but exports USD; emit the USD value on spans. -- **No prompt/response bodies by default.** Following graycode-router's audit design +- **No prompt/response bodies by default.** Following eyrie's audit design (`observability/audit.go`), spans MUST NOT carry raw prompt/response text by default. If content capture is enabled, use the OTel GenAI event/log channel, not span attributes, and gate it behind explicit opt-in. @@ -94,7 +94,7 @@ parentheses). ## Legacy attribute mapping -graycode-router's older stdlib constants (`AttrLLMProvider`, `AttrLLMModel`, +eyrie's older stdlib constants (`AttrLLMProvider`, `AttrLLMModel`, `AttrLLMInputTokens`, …, keyed `llm.*`) predate this spec and remain for backwards compatibility. New instrumentation should use the `gen_ai.*` keys. Mapping: @@ -111,12 +111,12 @@ Mapping: ## Per-repo guidance -- **graycode-router** — owns provider/model/usage spans behind `graycode-router/engine`; align +- **eyrie** — owns provider/model/usage spans behind `eyrie/engine`; align attribute keys to `gen_ai.*` over time. -- **graycode** — daemon/orchestrator. Already has OTel hooks - (`GRAYCODE_ENABLE_TELEMETRY`, `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS`). Emit +- **hawk** — daemon/orchestrator. Already has OTel hooks + (`HAWK_ENABLE_TELEMETRY`, `HAWK_OTEL_SHUTDOWN_TIMEOUT_MS`). Emit `agent.id` and `session.id` on agent-turn spans; propagate them downstream to - graycode-router via context so provider spans inherit the same IDs. + eyrie via context so provider spans inherit the same IDs. - **harrier** — memory service. Add `embeddings` spans with `gen_ai.system` + `gen_ai.request.model` from its embeddings config; tag retrieval spans with `session.id`. @@ -129,5 +129,5 @@ Mapping: ## Versioning This spec tracks OTel GenAI conventions, which are still evolving. Pin changes -to the constants via `genai_semconv_test.go` in graycode-router and update this table in +to the constants via `genai_semconv_test.go` in eyrie and update this table in the same change. diff --git a/docs/PERMISSION-MODEL-IMPROVEMENTS.md b/docs/PERMISSION-MODEL-IMPROVEMENTS.md index cc4f67a3f..9f21f8f43 100644 --- a/docs/PERMISSION-MODEL-IMPROVEMENTS.md +++ b/docs/PERMISSION-MODEL-IMPROVEMENTS.md @@ -1,6 +1,6 @@ -# Graycode Permission Model — Improvements (2026-08-07) +# Hawk Permission Model — Improvements (2026-08-07) -This document describes the improvements made to graycode's permission, isolation, +This document describes the improvements made to hawk's permission, isolation, and autonomy systems. All changes preserve the existing fail-closed architecture and are backward-compatible. @@ -186,7 +186,7 @@ second within 1.5s lands on Supervised. Prompt auto-expires. **Problem:** Crash could leave `sandbox-*.sb` temp files behind. -**Solution:** `seatbelt.go` `init()` removes orphaned `graycode-seatbelt-*.sb` files from +**Solution:** `seatbelt.go` `init()` removes orphaned `hawk-seatbelt-*.sb` files from `os.TempDir()` at process startup. --- diff --git a/docs/RESEARCH.md b/docs/RESEARCH.md index f7bee49bb..c15642c48 100644 --- a/docs/RESEARCH.md +++ b/docs/RESEARCH.md @@ -1,7 +1,7 @@ # Research & Competitive Comparison — graycode-eco Status: Implemented (branch `feat/competitive-analysis-top20`, 2026-09-09) -Scope: 4 repos (`graycode-cli`, `graycode-router`, `graycode-platform`, `graycode-skills`) vs +Scope: 4 repos (`hawk`, `eyrie`, `graycode-platform`, `graycode-skills`) vs top-20 OSS competitors and top-20 AI-coding-agent research papers. This document maps the field (competitors + research) to our repos, records what @@ -12,15 +12,15 @@ plans in `docs/plans/competitive-gap-*.md` and the extra fixes they surfaced. - Competitors: `docs/COMPETITIVE.md` (20 OSS tools, source-cited). - Research: 20 papers read via arXiv (abstracts) 2026-09-09 — listed below. -- Repo capability inventory: `graycode-cli/README.md`, `graycode-router/README.md`, +- Repo capability inventory: `hawk/README.md`, `eyrie/README.md`, `graycode-platform/README.md`, `graycode-skills/README.md`, plus source audits. ## Our repos at a glance | Repo | Role | Key strengths | |---|---|---| -| graycode-cli | Product face (Go, Bubble Tea v2) | 120+ tools, Docker fail-closed sandbox, `/autonomy`+`/spec` gates, `mission` multi-agent, execution-graph export, AST repomap + Harrier memory, MCP/LSP | -| graycode-router | Provider engine facade | 22 gateways, routing/retry/caching/compaction, OpenAI-compat proxy, model catalog | +| hawk | Product face (Go, Bubble Tea v2) | 120+ tools, Docker fail-closed sandbox, `/autonomy`+`/spec` gates, `mission` multi-agent, execution-graph export, AST repomap + Harrier memory, MCP/LSP | +| eyrie | Provider engine facade | 22 gateways, routing/retry/caching/compaction, OpenAI-compat proxy, model catalog | | graycode-platform | Optional cloud/BFF plane | web + identity BFF + control-plane worker (not a runtime dep) | | graycode-skills | Skill marketplace | 14,015 skills, 27 categories, SKILL.md frontmatter + validation | diff --git a/docs/SECURITY-DEVELOPER.md b/docs/SECURITY-DEVELOPER.md index 05a4e336b..4cf2d5d55 100644 --- a/docs/SECURITY-DEVELOPER.md +++ b/docs/SECURITY-DEVELOPER.md @@ -1,13 +1,13 @@ -# Graycode developer security model +# Hawk developer security model -This document describes how graycode and graycode-router handle API keys and agent isolation for an individual developer on macOS or Linux (no Vault, no proxy). Teams and enterprise deployment models come later. +This document describes how hawk and eyrie handle API keys and agent isolation for an individual developer on macOS or Linux (no Vault, no proxy). Teams and enterprise deployment models come later. ## Goals - API keys live only in the OS secret store (macOS Keychain / Linux GNOME Keyring or KWallet). -- Graycode does not read API keys from `.env`, shell env, or plaintext files. -- GraycodeRouter's `provider.json` holds routing and deployment metadata only — never secrets on disk. -- Graycode talks to graycode-router without putting keys in JSON or chat messages. +- Hawk does not read API keys from `.env`, shell env, or plaintext files. +- Eyrie's `provider.json` holds routing and deployment metadata only — never secrets on disk. +- Hawk talks to eyrie without putting keys in JSON or chat messages. - Agent commands run inside mandatory Docker isolation; file tools cannot read credential paths. @@ -15,16 +15,16 @@ This document describes how graycode and graycode-router handle API keys and age | Write | Read | Remove | |-------|------|--------| -| `/config` paste flow → `graycode-router/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `graycode credentials remove` | +| `/config` paste flow → `eyrie/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `hawk credentials remove` | -On startup, Graycode asks the GraycodeRouter engine facade to migrate legacy -`~/.graycode/env` / `~/.graycode/.env` values into the secret store and delete those +On startup, Hawk asks the Eyrie engine facade to migrate legacy +`~/.hawk/env` / `~/.hawk/.env` values into the secret store and delete those files. It also imports recognized historical secret fields from `provider.json` before atomically rewriting that file with metadata only. A secret-store or state-write failure aborts the rewrite and rolls back newly imported values. -Check status: `graycode credentials status`, `graycode path`, or `graycode preflight`. +Check status: `hawk credentials status`, `hawk path`, or `hawk preflight`. ## First-run flow (`/config`) @@ -32,10 +32,10 @@ Check status: `graycode credentials status`, `graycode path`, or `graycode prefl User pastes API key in /config | v -Graycode /config -> GraycodeRouter engine credential service (OS secret store) +Hawk /config -> Eyrie engine credential service (OS secret store) | v -GraycodeRouter engine discover/apply (credentials from store, not JSON body) +Eyrie engine discover/apply (credentials from store, not JSON body) | v SetupUI JSON (display_name + canonical_id per model) @@ -46,20 +46,20 @@ User picks model -> settings.json (canonical id only) Remove a stored key: `/config key remove` (interactive picker). -## Graycode to GraycodeRouter +## Hawk to Eyrie -- **Control plane**: Graycode calls only `graycode-router/engine`; no lower GraycodeRouter package is a +- **Control plane**: Hawk calls only `eyrie/engine`; no lower Eyrie package is a production import. - **Discovery/apply**: credentials are resolved from the Engine's injected secret store; provider state and request bodies remain sanitized. -- **Chat**: Graycode sends model intent, messages, and tool definitions; GraycodeRouter +- **Chat**: Hawk sends model intent, messages, and tool definitions; Eyrie resolves the gateway and reads secrets internally. ## Agent isolation ``` +------------------+ +------------------+ -| Graycode TUI/host | | Docker sandbox | +| Hawk TUI/host | | Docker sandbox | | Keychain access | | Commands only | | /config paste | | project mount | +------------------+ +------------------+ @@ -69,57 +69,57 @@ Remove a stored key: `/config key remove` (interactive picker). ``` When the container is ready, `session.ContainerExecutor` runs agent commands in -Docker. Graycode fails closed when Docker is unavailable; it never falls back to +Docker. Hawk fails closed when Docker is unavailable; it never falls back to host command execution. -The sandbox image has an independent compatibility version embedded in Graycode. +The sandbox image has an independent compatibility version embedded in Hawk. Startup first checks the local Docker image cache, then anonymously pulls the -public `graycodeai/graycode-sandbox` image. If the registry cannot be reached, Graycode +public `graycodeai/hawk-sandbox` image. If the registry cannot be reached, Hawk builds the same bundled sandbox Dockerfile locally. Registry login is not required for users, and neither provisioning path enables host execution. ### Blocked for agents -- **Read** tool: legacy Graycode env files, GraycodeRouter's configured `provider.json`, +- **Read** tool: legacy Hawk env files, Eyrie's configured `provider.json`, `~/.ssh/*`, etc. -- **Bash**: `printenv`, `env`, reading graycode env paths, echoing `*_API_KEY` variables. +- **Bash**: `printenv`, `env`, reading hawk env paths, echoing `*_API_KEY` variables. ## Migration -- **Legacy env files**: startup migration imports `~/.graycode/env` and - `~/.graycode/.env` into the OS secret store, then deletes the plaintext files. -- **provider.json secrets**: GraycodeRouter transactionally imports recognized top-level +- **Legacy env files**: startup migration imports `~/.hawk/env` and + `~/.hawk/.env` into the OS secret store, then deletes the plaintext files. +- **provider.json secrets**: Eyrie transactionally imports recognized top-level and deployment credentials, atomically writes sanitized metadata, and uses a temporary `provider.json.pre-secret-migrate.bak` only during the transaction. -- **All subsequent writes**: the GraycodeRouter engine applies the same sanitization and +- **All subsequent writes**: the Eyrie engine applies the same sanitization and atomic-write path, so migrated secret fields cannot be reintroduced. ## Provider state path -GraycodeRouter owns the provider-state path. Resolution order is: +Eyrie owns the provider-state path. Resolution order is: -1. `GRAYCODE_ROUTER_CONFIG_DIR/provider.json` -2. `GRAYCODE_CONFIG_DIR/provider.json` (compatibility fallback) -3. the platform user-config directory under `graycode/provider.json` +1. `EYRIE_CONFIG_DIR/provider.json` +2. `HAWK_CONFIG_DIR/provider.json` (compatibility fallback) +3. the platform user-config directory under `hawk/provider.json` -Graycode's Read/Edit/Write and Bash safety checks protect the resolved path, -including a custom or symlinked `GRAYCODE_ROUTER_CONFIG_DIR`; protection is not limited +Hawk's Read/Edit/Write and Bash safety checks protect the resolved path, +including a custom or symlinked `EYRIE_CONFIG_DIR`; protection is not limited to the historical default provider-state location. ## Environment variables -Non-secret overrides only (graycode does not load provider API keys from env): +Non-secret overrides only (hawk does not load provider API keys from env): | Variable | Meaning | |----------|---------| -| `GRAYCODE_CONFIG_DIR` | Override graycode config directory | -| `GRAYCODE_ROUTER_CONFIG_DIR` | Override GraycodeRouter provider-state directory; takes precedence for `provider.json` | +| `HAWK_CONFIG_DIR` | Override hawk config directory | +| `EYRIE_CONFIG_DIR` | Override Eyrie provider-state directory; takes precedence for `provider.json` | | `OPENAI_MODEL` | Override default OpenAI model | | `OLLAMA_BASE_URL` | Ollama server URL (also saved via `/config` for Ollama) | ## Related code -- Graycode: `internal/config/graycode-router_engine.go`, `internal/tool/safety.go`, +- Hawk: `internal/config/eyrie_engine.go`, `internal/tool/safety.go`, `internal/storage/paths.go`, `cmd/credentials.go` -- GraycodeRouter public host boundary: `engine/` +- Eyrie public host boundary: `engine/` - Daemon HTTP surface: [`docs/DAEMON-PORT-THREAT-MODEL.md`](DAEMON-PORT-THREAT-MODEL.md) diff --git a/docs/architecture.md b/docs/architecture.md index 52cdfa013..c3d1fe39a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,6 +1,6 @@

-# bird graycode Architecture +# bird hawk Architecture **AI Coding Agent for Your Terminal** @@ -14,19 +14,19 @@ ## target Overview -graycode is an AI-powered coding agent for the terminal. It reads codebases, writes and edits files, runs tests, and manages git — all through natural language. Zero CGO, single static binary for linux/darwin/windows on amd64/arm64. +hawk is an AI-powered coding agent for the terminal. It reads codebases, writes and edits files, runs tests, and manages git — all through natural language. Zero CGO, single static binary for linux/darwin/windows on amd64/arm64. -Detailed planning docs for the Graycode product architecture live in [`docs/architecture/`](architecture/README.md). +Detailed planning docs for the Hawk product architecture live in [`docs/architecture/`](architecture/README.md). --- ## blocks Layered Architecture ``` -graycode/ +hawk/ ├── api/openapi.yaml file-text Daemon REST API contract (OpenAPI 3.1) ├── cmd/ terminal Cobra CLI commands (200+ files) -│ ├── graycode/main.go zap Entry point — calls cmd.Execute() +│ ├── hawk/main.go zap Entry point — calls cmd.Execute() │ ├── root.go settings Root command, flag definitions │ ├── daemon.go server Daemon start/stop/status │ ├── chat.go message-square Interactive TUI chat @@ -55,7 +55,7 @@ graycode/ └── (ecosystem siblings live at ../ in the graycode-eco workspace; see docs/architecture/ecosystem-design.md) ``` -Legacy note: `graycode/shared/types` has been removed. Shared cross-repo severity +Legacy note: `hawk/shared/types` has been removed. Shared cross-repo severity and finding contracts now live in `eagle/types`. --- @@ -65,8 +65,8 @@ and finding contracts now live in `eagle/types`. | | | |---|---| | **Contract** | [`api/openapi.yaml`](../api/openapi.yaml) | -| **Port** | `:4590` (default). Override: `GRAYCODE_DAEMON_PORT` | -| **Auth** | Bearer token or `X-API-Key`. Set via `GRAYCODE_DAEMON_API_KEY` | +| **Port** | `:4590` (default). Override: `HAWK_DAEMON_PORT` | +| **Auth** | Bearer token or `X-API-Key`. Set via `HAWK_DAEMON_API_KEY` |
radio Endpoint Summary @@ -90,7 +90,7 @@ and finding contracts now live in `eagle/types`. | Service | Role | Connection | |---------|------|------------| -| bird **graycode-router** | LLM provider runtime | `:8080` — all LLM calls routed here | +| bird **eyrie** | LLM provider runtime | `:8080` — all LLM calls routed here | | brain **harrier** | Persistent memory | `:3456` — session context, recall | | eye **kestrel** | Code review | Library — diff-based review | | search **merlin** | Security audit | Library — website scanning | @@ -107,7 +107,7 @@ and finding contracts now live in `eagle/types`. All three SDKs share types from **eagle** and consume the daemon REST API (:4590). -> lightbulb **graycode never talks to LLM APIs directly** — all calls go through graycode-router. +> lightbulb **hawk never talks to LLM APIs directly** — all calls go through eyrie. --- @@ -125,9 +125,9 @@ Tool Call → ` — persisted Graycode session; -- `graycode/task-request//` — user task request, content represented by +- `hawk/session/` — persisted Hawk session; +- `hawk/task-request//` — user task request, content represented by SHA-256 only; -- `graycode/task/` — structured task; -- `graycode/runtime-task/` — background agent, shell, or monitor task; -- `graycode/tool-call//` — tool invocation metadata; -- `graycode/policy/` — permission verdict; -- `graycode/verification/` — neutral verification result; +- `hawk/task/` — structured task; +- `hawk/runtime-task/` — background agent, shell, or monitor task; +- `hawk/tool-call//` — tool invocation metadata; +- `hawk/policy/` — permission verdict; +- `hawk/verification/` — neutral verification result; - `harrier/memory/` — retrieved memory knowledge; -- `graycode/code-chunk/` — retrieved code-index knowledge; +- `hawk/code-chunk/` — retrieved code-index knowledge; - `merlin/report/` and `merlin/finding/` — website audit quality facts; - `kestrel/review/` and `kestrel/finding/` — code-review quality facts; - `shrike/compression/`, `shrike/usage/`, `shrike/budget/`, and `shrike/redaction/` — operations, policy, and quality facts; -- `graycode-router/route/` and `graycode-router/generation/` — model route and +- `eyrie/route/` and `eyrie/generation/` — model route and normalized generation operations; - `swift/checkpoint/` — externally owned Swift checkpoint reference. @@ -116,7 +116,7 @@ Edges: - `contains` — session/task hierarchy and task-to-tool containment; - `depends_on` — blocking task dependency; -- `references` — related tasks and Graycode-to-Swift checkpoint linkage; +- `references` — related tasks and Hawk-to-Swift checkpoint linkage; - `governed_by` — execution subject to policy verdict; - `validated_by` — execution subject to verification result. - imported Merlin `contains` edges — report-to-finding hierarchy. @@ -138,15 +138,15 @@ The export deliberately excludes: Where correlation is useful, sensitive values are represented by SHA-256 digests. Every node declares `data_classification=metadata_only`. -Graycode writes runtime observations to a per-session append-only JSONL journal. -The journal is stored with mode `0600` under Graycode's state directory and contains +Hawk writes runtime observations to a per-session append-only JSONL journal. +The journal is stored with mode `0600` under Hawk's state directory and contains only verdict metadata, aggregate verification counts, and SHA-256 digests. A journal failure is logged and never changes a permission, approval, or tool result. Mission-mode graph execution persists a separate `mission-graph.json` artifact beside `mission.json` in the mission directory. The artifact uses the same -portable `graycode.graph/v1` envelope, but it is mission-scoped rather than +portable `hawk.graph/v1` envelope, but it is mission-scoped rather than session-scoped: mission node, feature nodes, and wave-join operations nodes are rewritten after each mission run or wave join using only metadata and SHA-256 digests. @@ -154,18 +154,18 @@ digests. ## CLI ```bash -graycode graph export [session-id] +hawk graph export [session-id] --repository --swift-checkpoint <12-hex-id> # repeatable ``` The default repository scope is derived from the saved session's working directory basename. If Swift captured -`SWIFT_TAG_GRAYCODE_SESSION_ID=` at its actual session-start +`SWIFT_TAG_HAWK_SESSION_ID=` at its actual session-start boundary, export automatically resolves the exact Swift session and committed checkpoints through `swift graph correlation`. Explicit `--swift-checkpoint` values remain additive. Swift lookup failures never block -Graycode export, and incomplete checkpoint enumeration produces a session link +Hawk export, and incomplete checkpoint enumeration produces a session link without unverified checkpoint links. ## Daemon API and SDKs @@ -181,18 +181,18 @@ GET /v1/sessions/{id}/graph The endpoint uses the daemon's normal Bearer or `X-API-Key` authentication, validates session, repository, and checkpoint inputs before projection, and -returns the typed `graycode.graph/v1` envelope. The handler owns transport concerns -only; the Graycode composition root injects the existing graph builder, so CLI, +returns the typed `hawk.graph/v1` envelope. The handler owns transport concerns +only; the Hawk composition root injects the existing graph builder, so CLI, Cloud sync, and HTTP projections share one construction path. The Go, Python sync/async, and TypeScript SDK clients expose this endpoint and validate the returned graph topology before returning it to callers. -After connecting a project with `graycode cloud login` or `graycode cloud connect`, a +After connecting a project with `hawk cloud login` or `hawk cloud connect`, a completed session snapshot can be uploaded explicitly: ```bash -graycode cloud graph sync [session-id] +hawk cloud graph sync [session-id] --repository --swift-checkpoint <12-hex-id> # repeatable ``` @@ -202,18 +202,18 @@ keys to `*_sha256`, enforces the 250-node/500-edge/500-event/900-total-fact and 1 MiB limits, and derives the sync ID from the prepared graph. Repeating the same completed snapshot is therefore idempotent. Upload errors are reported to the explicit command, but cloud availability never affects local execution. -Graycode does not upload prompt bodies, response bodies, tool arguments, tool +Hawk does not upload prompt bodies, response bodies, tool arguments, tool results, or other large artifacts. Mission-scoped graph artifacts can use the same explicit path without being -pretended to be a Graycode session: +pretended to be a Hawk session: ```bash -graycode graph export --mission-dir -graycode cloud graph sync --mission-dir +hawk graph export --mission-dir +hawk cloud graph sync --mission-dir ``` -The CLI reads only `mission-graph.json`, validates its `graycode.graph/v1` schema +The CLI reads only `mission-graph.json`, validates its `hawk.graph/v1` schema and all edge/event references, then applies the same privacy normalization and Cloud limits. Mission graphs intentionally omit `sessionId`; they are durable mission facts, not Cloud session telemetry. @@ -227,50 +227,50 @@ The central tool-execution seam automatically records: - aggregate results from `VerifyPlanExecution`. - Harrier subgraphs selected by direct, graph-budget, global, proactive, and shared-memory retrieval; -- Graycode code-index chunks selected through Harrier-backed code search. +- Hawk code-index chunks selected through Harrier-backed code search. - Merlin scans invoked through the observed bridge/pipeline path. - Kestrel reviews invoked through the observed bridge path. - Shrike compression performed by persisted-session context compaction. - Shrike-only secret matches removed from persisted-session responses. - Shrike usage summaries and budget decisions emitted from the central, deduplicated provider-accounting seam. -- GraycodeRouter model route and usage reported for persisted-session turns. +- Eyrie model route and usage reported for persisted-session turns. Mission execution now also persists a portable graph artifact for local mission -runs. Plain `graycode mission` runs emit mission and feature execution nodes; the -graph-driven `graycode mission --from-tasks` path additionally emits operations +runs. Plain `hawk mission` runs emit mission and feature execution nodes; the +graph-driven `hawk mission --from-tasks` path additionally emits operations nodes for each deterministic wave join, including bounded completion, failure, and blocked-downstream counts. Merlin and Kestrel observed bridge calls now emit both their producer-owned -quality topology and Graycode's neutral aggregate verification observation. The +quality topology and Hawk's neutral aggregate verification observation. The latter contains only failure state, finding count, maximum severity, and a SHA-256 target digest, allowing `validated_by` composition without retaining URLs, diffs, findings, evidence, or fixes in the observation journal. Shrike's tracker now contributes hourly, daily, session, and cost limits to the -pre-turn guard. Existing Graycode cost accounting and limits remain authoritative +pre-turn guard. Existing Hawk cost accounting and limits remain authoritative and are updated first; Shrike observes the same deduplicated request usage and provides the additional token-window decision. ### Swift identity handshake contract Swift now exposes the Swift-owned, read-only -`graph correlation --graycode-session ` surface through the permitted +`graph correlation --hawk-session ` surface through the permitted `cli.NewRootCmd()` boundary. It: -- matches only the `SWIFT_TAG_GRAYCODE_SESSION_ID` value stored at Swift's actual +- matches only the `SWIFT_TAG_HAWK_SESSION_ID` value stored at Swift's actual session-start boundary; -- returns the authoritative Swift session and checkpoint IDs for that Graycode ID; +- returns the authoritative Swift session and checkpoint IDs for that Hawk ID; - never guesses identity from the current branch, HEAD, timestamps, or prompt similarity; - returns an empty match set rather than a speculative match. -Graycode now consumes this surface through `cli.NewRootCmd()`, validates the schema -and echoed Graycode identity, bounds the response, and emits: +Hawk now consumes this surface through `cli.NewRootCmd()`, validates the schema +and echoed Hawk identity, bounds the response, and emits: -- Graycode session `references` Swift session; -- Graycode session `references` each authoritative Swift checkpoint; +- Hawk session `references` Swift session; +- Hawk session `references` each authoritative Swift checkpoint; - Swift session `produced` Swift checkpoint. Malformed, mismatched, oversized, unavailable, or incomplete responses fail diff --git a/docs/architecture/graycode-ecosystem-summary.md b/docs/architecture/graycode-ecosystem-summary.md index ffe058e5f..9b85ef440 100644 --- a/docs/architecture/graycode-ecosystem-summary.md +++ b/docs/architecture/graycode-ecosystem-summary.md @@ -1,7 +1,7 @@ # GrayCode Ecosystem Summary -`graycode-eco` is only a local parent folder. `graycode` is the main CLI and the -only primary Graycode product; the other repositories provide capabilities, +`graycode-eco` is only a local parent folder. `hawk` is the main CLI and the +only primary Hawk product; the other repositories provide capabilities, contracts, integrations, tooling, or optional platform services. ## Repository layers @@ -10,18 +10,18 @@ contracts, integrations, tooling, or optional platform services. API consumers/extensions sparrow robin wren starling \ | | / - graycode + hawk (main CLI and daemon) | Support engines - graycode-router harrier shrike swift kestrel merlin + eyrie harrier shrike swift kestrel merlin | Foundations eagle falcon Outside the Go runtime graph owl architecture tooling - graycode-platform web, BFF, and Graycode Cloud Worker + graycode-platform web, BFF, and Hawk Cloud Worker ``` Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike, @@ -30,42 +30,42 @@ Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike ## Dependency direction ```text -graycode -> graycode-router / harrier / shrike / swift / kestrel / merlin / eagle +hawk -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # when shared contracts are needed harrier / kestrel / merlin -> falcon -sparrow / robin / wren -> Graycode daemon API -starling -> Graycode skill/plugin API +sparrow / robin / wren -> Hawk daemon API +starling -> Hawk skill/plugin API ``` -Forbidden edges are engine-to-engine, engine-to-Graycode-internal, SDK-to-engine, +Forbidden edges are engine-to-engine, engine-to-Hawk-internal, SDK-to-engine, skills-to-engine, and any Go-module dependency on GrayCode Platform. ## Runtime and hosted plane ```text -Graycode main CLI - ├── GraycodeRouter provider execution +Hawk main CLI + ├── Eyrie provider execution ├── Harrier memory ├── Shrike token/context management ├── Swift swift/provenance ├── Kestrel review └── Merlin verification -graycode ── optional authenticated HTTP ──> graycode-platform/apps/worker +hawk ── optional authenticated HTTP ──> graycode-platform/apps/worker web ──> graycode-platform/apps/bff ── private Service Binding ──> worker worker ──> control-plane D1 + usage Queue + R2 ``` The Worker is deployed as `graycode-cloud`, but that is an application name, -not a repository. GrayCode Platform remains outside the Graycode Go module graph. -Graycode's cloud usage path is fail-open; graph synchronization is explicit. +not a repository. GrayCode Platform remains outside the Hawk Go module graph. +Hawk's cloud usage path is fail-open; graph synchronization is explicit. ## Repository roles | Repository | Role | Direct dependency rule | |---|---|---| -| `graycode` | Main CLI, daemon, orchestration, policy | Integrates engines and contracts | -| `graycode-router` | Provider runtime | Uses Eagle contracts; exposes `engine` | +| `hawk` | Main CLI, daemon, orchestration, policy | Integrates engines and contracts | +| `eyrie` | Provider runtime | Uses Eagle contracts; exposes `engine` | | `harrier` | Harrier memory | Uses Eagle/Falcon where required | | `shrike` | Shrike context engine | Uses Eagle where required | | `swift` | Swift/provenance | Uses Eagle where required | @@ -73,10 +73,10 @@ Graycode's cloud usage path is fail-open; graph synchronization is explicit. | `merlin` | Merlin verification | Uses Eagle and Falcon | | `eagle` | Neutral shared contracts | Leaf module | | `falcon` | MCP kit | Upstream MCP library only | -| `sparrow` | Go SDK | Graycode API contract | -| `robin` | Python SDK | Graycode API contract | -| `wren` | TypeScript SDK | Graycode API contract | -| `starling` | Skills/extensions | Graycode skill surface | +| `sparrow` | Go SDK | Hawk API contract | +| `robin` | Python SDK | Hawk API contract | +| `wren` | TypeScript SDK | Hawk API contract | +| `starling` | Skills/extensions | Hawk skill surface | | `owl` | Architecture explorer | Generated read-only projection | | `graycode-platform` | Web/BFF/cloud | HTTP and Service Binding only | @@ -85,6 +85,6 @@ Graycode's cloud usage path is fail-open; graph synchronization is explicit. The repository-level target is implemented: independent Git repositories, canonical manifest, generated Owl inventory, sibling Go workspace, Eagle parity, and dependency boundary checks are all present. Remaining work is to publish -the Eagle-compatible GraycodeRouter revision, remove the transitional -`graycode-core-contracts` dependency from the standalone graph, and decide whether -Graycode's graph/projection packages need additional engine-owned facades. +the Eagle-compatible Eyrie revision, remove the transitional +`hawk-core-contracts` dependency from the standalone graph, and decide whether +Hawk's graph/projection packages need additional engine-owned facades. diff --git a/docs/architecture/graycode-architecture-baseline.md b/docs/architecture/hawk-architecture-baseline.md similarity index 78% rename from docs/architecture/graycode-architecture-baseline.md rename to docs/architecture/hawk-architecture-baseline.md index 42ee11719..24310753c 100644 --- a/docs/architecture/graycode-architecture-baseline.md +++ b/docs/architecture/hawk-architecture-baseline.md @@ -1,10 +1,10 @@ -# Graycode Architecture Baseline +# Hawk Architecture Baseline **Status:** Phase 0 baseline **Date:** 2026-08-04 **Baseline commit:** `69ce83f55f9098623e5a891e8c52be636db89c7c` -This document records the architecture that exists in the Graycode repository at +This document records the architecture that exists in the Hawk repository at the beginning of the architecture improvement program. It separates current implementation facts from the intended ecosystem design. It is not a product quality score and does not claim that the migration is complete. @@ -14,16 +14,16 @@ quality score and does not claim that the migration is complete. Use the documents in this order when statements conflict: 1. This document for the dated implementation baseline and migration status. -2. `graycode-current-vs-proposed.md` for the ecosystem repository map. -3. `graycode-product-architecture.md` for ownership and runtime responsibilities. -4. `graycode-dependency-rules.md` for allowed and forbidden dependency edges. +2. `hawk-current-vs-proposed.md` for the ecosystem repository map. +3. `hawk-product-architecture.md` for ownership and runtime responsibilities. +4. `hawk-dependency-rules.md` for allowed and forbidden dependency edges. 5. `spec.md` for behavioral requirements and agent-loop semantics. -Graycode is the main Go CLI/product and workspace entry point in a +Hawk is the main Go CLI/product and workspace entry point in a multi-repository ecosystem. `graycode-eco` is only the plain parent folder; the sibling repositories retain independent Git histories and release cadences. Local integration uses the parent `go.work`; standalone builds use -published module pins. There is no current `graycode/external/` vendor tree. +published module pins. There is no current `hawk/external/` vendor tree. ## Target product graph @@ -31,9 +31,9 @@ published module pins. There is no current `graycode/external/` vendor tree. users / SDKs / skills / daemon clients | v - graycode + hawk / | \ - graycode-router harrier shrike + eyrie harrier shrike swift kestrel merlin | v @@ -42,28 +42,28 @@ users / SDKs / skills / daemon clients The graph is intentionally directional: -- Graycode owns user-facing orchestration, sessions, tools, permissions, +- Hawk owns user-facing orchestration, sessions, tools, permissions, composition, and public product surfaces. -- GraycodeRouter owns provider protocols, routing, credentials, catalogs, and provider - execution behind `graycode-router/engine`. +- Eyrie owns provider protocols, routing, credentials, catalogs, and provider + execution behind `eyrie/engine`. - Harrier, Shrike, Swift, Kestrel, and Merlin are support engines and must not import - Graycode internals or one another. + Hawk internals or one another. - Core contracts contain stable cross-repository vocabulary and DTOs, not runtime orchestration. -- SDKs and skills consume Graycode surfaces rather than support-engine internals. +- SDKs and skills consume Hawk surfaces rather than support-engine internals. ## Current implementation state ### Complete or enforced -- Graycode production code uses GraycodeRouter through the `graycode-router/engine` facade. -- Kestrel and Merlin are integrated through Graycode bridge packages. -- Support-engine sibling imports and imports of Graycode internals are guarded. +- Hawk production code uses Eyrie through the `eyrie/engine` facade. +- Kestrel and Merlin are integrated through Hawk bridge packages. +- Support-engine sibling imports and imports of Hawk internals are guarded. - The AST/package-graph guard reports production boundary violations with - file/line diagnostics across Graycode and available support repositories. + file/line diagnostics across Hawk and available support repositories. - Persisted tool, review, verification, event, and policy contracts use the implemented portions of `eagle`. -- Native-compaction capability contracts use `eagle/llm`; GraycodeRouter +- Native-compaction capability contracts use `eagle/llm`; Eyrie request translation remains inside `internal/provider/gateway`, keeping the engine layer independent of the provider adapter package for this path. - Container-required state and its executor are owned by `ToolService` and @@ -87,10 +87,10 @@ The graph is intentionally directional: At this baseline its top-level production files contain approximately 19,253 lines, its top-level tests approximately 11,855 lines, and the subtree contains compatibility alias/re-export files. -- Graycode's Harrier and Shrike implementation imports are now concentrated in +- Hawk's Harrier and Shrike implementation imports are now concentrated in `HarrierBridge` and `internal/token` for the migrated production paths. The graph/projection packages used for capture remain explicit integration - surfaces; replaceability is improved, but still not equivalent to the GraycodeRouter + surfaces; replaceability is improved, but still not equivalent to the Eyrie boundary. - `PersistenceService` is the in-memory runtime owner for transcript/context state and checkpoint metadata. The active durable session path remains @@ -104,7 +104,7 @@ The graph is intentionally directional: - CLI, daemon, and other entry points share substantial construction and orchestration responsibilities instead of depending on one explicit application composition root. -- Non-interactive entry points now share `cmd.newConfiguredGraycodeSession`; the +- Non-interactive entry points now share `cmd.newConfiguredHawkSession`; the interactive TUI intentionally retains a lightweight startup path followed by deferred heavy configuration to protect first-frame latency. @@ -112,16 +112,16 @@ The graph is intentionally directional: ### ADR-B01 — Keep the multi-repository ecosystem -Do not collapse the support engines into Graycode or force a shared release cycle. -The `graycode-eco` parent workspace and Graycode's published module pins provide +Do not collapse the support engines into Hawk or force a shared release cycle. +The `graycode-eco` parent workspace and Hawk's published module pins provide local integration without removing independent ownership and release boundaries. -### ADR-B02 — Preserve the GraycodeRouter boundary +### ADR-B02 — Preserve the Eyrie boundary Provider implementation, catalog metadata, credential mapping, and protocol -adapters remain owned by GraycodeRouter. Graycode may own product policy and user-facing -selection, but production provider access remains through `graycode-router/engine`. +adapters remain owned by Eyrie. Hawk may own product policy and user-facing +selection, but production provider access remains through `eyrie/engine`. ### ADR-B03 — Complete internal consolidation before adding new seams @@ -132,7 +132,7 @@ packages should not be added until the current seams are explicit. ### ADR-B04 — Add facades selectively Harrier, Shrike, and Swift require a facade decision based on actual replacement and -release needs. A facade is justified when it isolates Graycode from implementation +release needs. A facade is justified when it isolates Hawk from implementation types or enables independent upgrades; it is not justified merely to increase the number of packages. @@ -178,7 +178,7 @@ initialization, cost snapshots, and WAL recovery error reporting are now synchronized and tested. Phase 3 has explicit non-interactive and interactive startup composition boundaries, with heavy TUI configuration remaining deferred for first-frame latency. Phase 4 has consolidated the migrated Harrier -and Shrike implementation imports behind narrow Graycode-owned facades. The next +and Shrike implementation imports behind narrow Hawk-owned facades. The next decision is the persistence ADR: document and enforce one durable authority, then define the migration and recovery contract before introducing additional storage backends. diff --git a/docs/architecture/graycode-architecture-v1-definition-of-done.md b/docs/architecture/hawk-architecture-v1-definition-of-done.md similarity index 69% rename from docs/architecture/graycode-architecture-v1-definition-of-done.md rename to docs/architecture/hawk-architecture-v1-definition-of-done.md index f237498bf..6df391277 100644 --- a/docs/architecture/graycode-architecture-v1-definition-of-done.md +++ b/docs/architecture/hawk-architecture-v1-definition-of-done.md @@ -1,6 +1,6 @@ -# Graycode Architecture v1 — Definition of Done +# Hawk Architecture v1 — Definition of Done -This document defines **realistic v1 complete** for the Graycode main CLI and its +This document defines **realistic v1 complete** for the Hawk main CLI and its connected ecosystem architecture. `graycode-eco` is only the local parent folder; it is not a repository or product. @@ -10,7 +10,7 @@ It is the shipping bar for the contracts-and-boundaries refactor. It is **not** v1 is done when the ecosystem has: -- one product surface (`graycode`) +- one product surface (`hawk`) - six peer support engines with no sibling imports - shared vocabulary only where cross-repo pain is real - automated guards so the old coupling cannot return @@ -27,18 +27,18 @@ Status note: ### Product and dependency graph -- [x] `graycode` is the only primary end-user product in the ecosystem -- [x] Graycode coordinates engines; engines do not import each other -- [x] SDKs and community skills consume Graycode public surfaces, not engines directly -- [x] `graycode-platform` and other company/platform repos stay outside Graycode runtime dependencies +- [x] `hawk` is the only primary end-user product in the ecosystem +- [x] Hawk coordinates engines; engines do not import each other +- [x] SDKs and community skills consume Hawk public surfaces, not engines directly +- [x] `graycode-platform` and other company/platform repos stay outside Hawk runtime dependencies ### Forbidden edges stay forbidden -- [x] no support repo imports `graycode/internal/*` -- [x] no support repo imports removed `graycode/shared/types` +- [x] no support repo imports `hawk/internal/*` +- [x] no support repo imports removed `hawk/shared/types` - [x] no SDK/skills repo references support engines as primary dependencies -- [x] Graycode production code imports GraycodeRouter only through `graycode-router/engine` -- [x] Graycode's graph/projection imports are documented as explicit integration +- [x] Hawk production code imports Eyrie only through `eyrie/engine` +- [x] Hawk's graph/projection imports are documented as explicit integration surfaces and do not create engine-to-engine dependencies ### `eagle` (implemented packages only) @@ -49,25 +49,25 @@ These packages are in scope for v1: - [x] `review/` — neutral review results - [x] `verify/` — neutral verification reports - [x] `tools/` — persisted tool call/result contracts -- [x] `events/` — normalized audit/swift event subset used by Graycode +- [x] `events/` — normalized audit/swift event subset used by Hawk - [x] `policy/` — permission and guardian decision contracts Adoption bar: - [x] `kestrel` and `merlin` import contracts for shared severity/findings and expose boundary adapters - [x] `shrike/types` compatibility shim removed from the local ecosystem -- [x] `graycode-router`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type +- [x] `eyrie`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type -### Graycode integration seams +### Hawk integration seams - [x] session persistence uses `eagle/tools`, not lower-level provider tool types - [x] review persistence and merlin/review bridge paths use neutral `review` / `verify` contracts -- [x] Graycode owns runtime DTOs in `internal/types` and translates them to `graycode-router/engine` in `internal/engine` -- [x] `graycode swift ...` remains a Graycode-mounted subcommand, not a competing product surface +- [x] Hawk owns runtime DTOs in `internal/types` and translates them to `eyrie/engine` in `internal/engine` +- [x] `hawk swift ...` remains a Hawk-mounted subcommand, not a competing product surface ### Enforcement -- [x] Graycode CI runs ecosystem, shared-types, graycode-router-client, and peer-coupling guards +- [x] Hawk CI runs ecosystem, shared-types, eyrie-client, and peer-coupling guards - [x] each support repo runs `check-ecosystem-boundaries.sh` in CI - [x] Go SDK runs consumer boundary guard in CI - [x] Python SDK and community skills run consumer boundary guards in CI @@ -78,15 +78,15 @@ Adoption bar: ### Ship state - [ ] local architecture commits are pushed and merged to upstream default branches -- [ ] open architecture PRs for engines, consumers, and Graycode integration are merged to `main` -- [ ] published module versions used by Graycode match the merged contract changes +- [ ] open architecture PRs for engines, consumers, and Hawk integration are merged to `main` +- [ ] published module versions used by Hawk match the merged contract changes ## Explicit non-goals for v1 Do not block v1 on any of the following: - `eagle/sessions` or `eagle/engines` -- moving every Graycode internal event struct into `eagle/events` +- moving every Hawk internal event struct into `eagle/events` - moving swift timeline/event models out of `swift` - unifying runtime `internal/types` DTOs and persisted contracts into one type - forcing every engine into the same integration depth (library vs subcommand vs service) @@ -108,10 +108,10 @@ Remove compatibility shims only when: ## Verify locally -From `graycode`: +From `hawk`: ```bash -make ecosystem-guard contracts-guard graycode-router-client-guard graycode-router-engine-guard peer-guard +make ecosystem-guard contracts-guard eyrie-client-guard eyrie-engine-guard peer-guard go test ./internal/testaudit/... -count=1 go test ./... -count=1 ``` @@ -125,7 +125,7 @@ go test ./... -count=1 ## Related docs -- `graycode-product-architecture.md` — target shape and phases -- `graycode-dependency-rules.md` — allowed and forbidden edges +- `hawk-product-architecture.md` — target shape and phases +- `hawk-dependency-rules.md` — allowed and forbidden edges - `eagle-spec.md` — contract inventory and planned packages - `../plans/eagles-migration-backlog.md` — migration history and follow-ups diff --git a/docs/architecture/graycode-capability-seams.md b/docs/architecture/hawk-capability-seams.md similarity index 96% rename from docs/architecture/graycode-capability-seams.md rename to docs/architecture/hawk-capability-seams.md index dce2eb91e..e52062913 100644 --- a/docs/architecture/graycode-capability-seams.md +++ b/docs/architecture/hawk-capability-seams.md @@ -1,7 +1,7 @@ # Capability seams Deepseek-harness keeps every side effect behind a named, replaceable seam with a -strict owner. Graycode adopts the same discipline without the Cordis dependency +strict owner. Hawk adopts the same discipline without the Cordis dependency container: seams are plain Go interfaces or registries wired at the composition root, and every registration that can outlive a request returns a disposer. diff --git a/docs/architecture/graycode-contract-spec.md b/docs/architecture/hawk-contract-spec.md similarity index 76% rename from docs/architecture/graycode-contract-spec.md rename to docs/architecture/hawk-contract-spec.md index a0015a4e9..776c9d95e 100644 --- a/docs/architecture/graycode-contract-spec.md +++ b/docs/architecture/hawk-contract-spec.md @@ -1,10 +1,10 @@ -# Graycode Core Contracts Spec +# Hawk Core Contracts Spec ## Purpose -`eagle` is the shared language spoken by Graycode and its engines. +`eagle` is the shared language spoken by Hawk and its engines. -It exists to remove cross-repo dependency on Graycode internals and to prevent engines from importing each other. +It exists to remove cross-repo dependency on Hawk internals and to prevent engines from importing each other. ## Scope @@ -25,7 +25,7 @@ Not allowed: - runtime logic - storage implementations - engine implementations -- Graycode application internals +- Hawk application internals ## Package layout @@ -87,12 +87,12 @@ list in sync with the code (it is the inventory the dependency rules assume). ## Migration order -1. inventory cross-repo shared types in `graycode/shared` and `graycode/internal/types` +1. inventory cross-repo shared types in `hawk/shared` and `hawk/internal/types` 2. move stable shared types into contracts 3. update `kestrel` 4. update `merlin` -5. update other repos that rely on Graycode-exported shared types -6. leave product-only types inside Graycode +5. update other repos that rely on Hawk-exported shared types +6. leave product-only types inside Hawk ## Current status @@ -103,10 +103,10 @@ list in sync with the code (it is the inventory the dependency rules assume). - event contracts now exist in `eagle/events` - policy contracts now exist in `eagle/policy` - portable graph contracts now exist in `eagle/graph` -- Graycode session persistence has started migrating to provider-neutral tool contracts -- Graycode review storage and merlin/review bridge paths now consume neutral review/verify contracts -- Graycode runtime conversation DTOs and the `ChatClient` port are Graycode-owned and - translated to the stable `graycode-router/engine` contract at the integration edge +- Hawk session persistence has started migrating to provider-neutral tool contracts +- Hawk review storage and merlin/review bridge paths now consume neutral review/verify contracts +- Hawk runtime conversation DTOs and the `ChatClient` port are Hawk-owned and + translated to the stable `eyrie/engine` contract at the integration edge ## Versioning rule diff --git a/docs/architecture/graycode-current-vs-proposed.md b/docs/architecture/hawk-current-vs-proposed.md similarity index 70% rename from docs/architecture/graycode-current-vs-proposed.md rename to docs/architecture/hawk-current-vs-proposed.md index b8539d82e..7effb34b3 100644 --- a/docs/architecture/graycode-current-vs-proposed.md +++ b/docs/architecture/hawk-current-vs-proposed.md @@ -1,17 +1,17 @@ -# Graycode Current vs Proposed Architecture +# Hawk Current vs Proposed Architecture ## Purpose This document explains the connected repository design. The canonical -machine-readable inventory is [`graycode/ecosystem.yaml`](../../ecosystem.yaml); +machine-readable inventory is [`hawk/ecosystem.yaml`](../../ecosystem.yaml); this document must not maintain a second repository list. The repository directory and product-name distinction is intentional: | Directory/repository | Product or role | |---|---| -| `graycode` | Graycode product and orchestration root | -| `graycode-router` | GraycodeRouter provider runtime | +| `hawk` | Hawk product and orchestration root | +| `eyrie` | Eyrie provider runtime | | `harrier` | Harrier memory engine | | `shrike` | Shrike token/context engine | | `swift` | Swift provenance engine | @@ -24,7 +24,7 @@ The repository directory and product-name distinction is intentional: | `wren` | TypeScript SDK | | `starling` | Community skills and extensions | | `owl` | Architecture visualization tooling | -| `graycode-platform` | GrayCode web, BFF, and Graycode Cloud deployment | +| `graycode-platform` | GrayCode web, BFF, and Hawk Cloud deployment | ## Current workspace @@ -34,10 +34,10 @@ only the nine Go modules marked `workspace: true` in the manifest: ```text graycode-eco/ -├── graycode # product / composition root +├── hawk # product / composition root ├── eagle # neutral contracts foundation ├── falcon # MCP foundation -├── graycode-router # GraycodeRouter provider engine +├── eyrie # Eyrie provider engine ├── harrier # Harrier memory engine ├── shrike # Shrike context engine ├── swift # Swift engine @@ -48,19 +48,19 @@ graycode-eco/ ├── wren # TypeScript SDK ├── starling # skills/extensions ├── owl # architecture tooling -└── graycode-platform # web/BFF/Graycode Cloud platform +└── graycode-platform # web/BFF/Hawk Cloud platform ``` -There is no `graycode/external` vendor tree in the current workspace. Local +There is no `hawk/external` vendor tree in the current workspace. Local development uses sibling checkouts and `go.work`; standalone builds use the published versions pinned in each `go.mod`. ## Current connected design ```text -sparrow / robin / wren ── HTTP/OpenAPI ──> graycode <── skill surface ── starling +sparrow / robin / wren ── HTTP/OpenAPI ──> hawk <── skill surface ── starling │ - ├── graycode-router/engine + ├── eyrie/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -71,7 +71,7 @@ sparrow / robin / wren ── HTTP/OpenAPI ──> graycode <── skill surfac engines ──> eagle harrier / kestrel / merlin ──> falcon ──> mark3labs/mcp-go -graycode ── optional, fail-open HTTP ──> graycode-platform/apps/worker +hawk ── optional, fail-open HTTP ──> graycode-platform/apps/worker web ──> graycode-platform/apps/bff ── private Service Binding ──> worker worker ──> D1 + Queue + R2 @@ -80,27 +80,27 @@ owl <── canonical manifest and read-only generated architecture artifacts The compile-time graph is deliberately one-way: -- Graycode is the only orchestrator and product integration root. -- Engines are peers and do not import Graycode internals or one another. +- Hawk is the only orchestrator and product integration root. +- Engines are peers and do not import Hawk internals or one another. - Eagle contains only neutral, implementation-light contracts. - Falcon contains only reusable MCP transport/handler scaffolding. -- SDKs consume Graycode's daemon API contract; they do not import engines. -- Starling provides content and extension metadata through Graycode's skill surface. -- GrayCode Platform is outside the Graycode runtime module graph. +- SDKs consume Hawk's daemon API contract; they do not import engines. +- Starling provides content and extension metadata through Hawk's skill surface. +- GrayCode Platform is outside the Hawk runtime module graph. ## Current versus proposed At the repository level, the proposed architecture is already implemented: independent repositories, a canonical manifest, sibling Go workspace wiring, -contract parity checks, and Graycode-centered dependency direction all exist. +contract parity checks, and Hawk-centered dependency direction all exist. The remaining proposed work is boundary refinement rather than repository reorganization: -1. publish the Eagle-migrated GraycodeRouter revision and update Graycode's standalone pin; -2. remove the transitional `graycode-core-contracts` dependency from the published +1. publish the Eagle-migrated Eyrie revision and update Hawk's standalone pin; +2. remove the transitional `hawk-core-contracts` dependency from the published module graph; -3. decide whether graph/projection packages should remain explicit Graycode +3. decide whether graph/projection packages should remain explicit Hawk integration exceptions or be hidden behind engine-owned facades; 4. keep generated Owl inventory and architecture documents synchronized with `ecosystem.yaml`. @@ -108,12 +108,12 @@ reorganization: ## Allowed dependency edges ```text -graycode -> graycode-router / harrier / shrike / swift / kestrel / merlin / eagle +hawk -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # only for shared contracts harrier / kestrel / merlin -> falcon -sparrow / robin / wren -> Graycode public HTTP/OpenAPI surface -starling -> Graycode skill/plugin surface -graycode-platform <-> Graycode via authenticated HTTP/Service Binding only +sparrow / robin / wren -> Hawk public HTTP/OpenAPI surface +starling -> Hawk skill/plugin surface +graycode-platform <-> Hawk via authenticated HTTP/Service Binding only ``` The product names `Harrier`, `Shrike`, `Swift`, `Kestrel`, and `Merlin` are user-facing @@ -124,15 +124,15 @@ directories and Go module paths. ```text engine -> engine -engine -> graycode/internal/* +engine -> hawk/internal/* SDK -> engine skills -> engine -any Graycode engine -> graycode-platform code +any Hawk engine -> graycode-platform code any Go module -> graycode-platform code ``` -The sanctioned platform connection is runtime-only and HTTP-based: Graycode may -send explicitly enabled usage or graph requests to the deployed Graycode Cloud +The sanctioned platform connection is runtime-only and HTTP-based: Hawk may +send explicitly enabled usage or graph requests to the deployed Hawk Cloud Worker, and local execution must remain usable when that request fails. ## Recommendation diff --git a/docs/architecture/graycode-dependency-rules.md b/docs/architecture/hawk-dependency-rules.md similarity index 61% rename from docs/architecture/graycode-dependency-rules.md rename to docs/architecture/hawk-dependency-rules.md index e225cf03a..f6560a266 100644 --- a/docs/architecture/graycode-dependency-rules.md +++ b/docs/architecture/hawk-dependency-rules.md @@ -1,15 +1,15 @@ -# Graycode Dependency Rules +# Hawk Dependency Rules -`graycode-eco` is only a local parent folder. `graycode` is the main CLI and the +`graycode-eco` is only a local parent folder. `hawk` is the main CLI and the single orchestration root. Repository names below use directory/module names; product labels are shown in parentheses where they differ. ## Required graph ```text -sparrow / robin / wren ── Graycode daemon API ──> graycode <── skill API ── starling +sparrow / robin / wren ── Hawk daemon API ──> hawk <── skill API ── starling │ - ├── graycode-router/engine + ├── eyrie/engine ├── harrier (Harrier) ├── shrike (Shrike) ├── swift/cli (Swift) @@ -22,7 +22,7 @@ engines ──> eagle when a shared contract is required ``` The canonical list of all 15 repositories is in -[`graycode/ecosystem.yaml`](../../ecosystem.yaml). `owl/ecosystem.json` is a +[`hawk/ecosystem.yaml`](../../ecosystem.yaml). `owl/ecosystem.json` is a generated projection of that list. ## Contract edges @@ -32,14 +32,14 @@ contract such as a finding, severity, event, or graph fact. Current Go module consumers are tracked by `ecosystem.yaml` and checked for Eagle version parity. Falcon is a separate foundation for shared MCP transport and handler patterns. -It remains upstream-only and must not import Graycode, Eagle, or an engine. +It remains upstream-only and must not import Hawk, Eagle, or an engine. ## Forbidden graph ```text engine -> engine -engine -> graycode/internal/* -engine -> graycode/shared/* +engine -> hawk/internal/* +engine -> hawk/shared/* SDK -> engine skills -> engine any Go module -> graycode-platform code @@ -47,9 +47,9 @@ any Go module -> graycode-platform code ## Rules -### 1. Graycode is the orchestrator +### 1. Hawk is the orchestrator -Only Graycode coordinates the support engines and owns the user-facing CLI, +Only Hawk coordinates the support engines and owns the user-facing CLI, daemon, sessions, tools, policy, and product workflows. ### 2. Engines are peers @@ -58,20 +58,20 @@ Engines may share concepts through Eagle contracts, but may not import each other. Product names do not change this rule: `harrier` is Harrier, `shrike` is Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is Merlin. -### 3. Provider logic stays behind GraycodeRouter +### 3. Provider logic stays behind Eyrie -Graycode production code reaches provider credentials, catalogs, routing, and -transport only through `github.com/GrayCodeAI/graycode-router/engine`. This is enforced +Hawk production code reaches provider credentials, catalogs, routing, and +transport only through `github.com/GrayCodeAI/eyrie/engine`. This is enforced by shell and AST/package-graph guards. -### 4. Graycode schemas stay Graycode-owned +### 4. Hawk schemas stay Hawk-owned -Graycode conversation persistence and CLI/JSON output are explicit projections, -not aliases or direct serialization of GraycodeRouter DTOs. +Hawk conversation persistence and CLI/JSON output are explicit projections, +not aliases or direct serialization of Eyrie DTOs. ### 5. Graph integrations are explicit surfaces -Graycode currently imports selected engine-owned graph/projection packages for +Hawk currently imports selected engine-owned graph/projection packages for memory, token, review, and verification capture. These are integration exceptions, not peer-engine dependencies. If replaceability requires stronger isolation, move those calls behind engine-owned facades; do not expose more @@ -79,20 +79,20 @@ storage or implementation types. ### 6. Cloud is runtime-only -`graycode-platform` is outside the Graycode Go module graph. Graycode may call the +`graycode-platform` is outside the Hawk Go module graph. Hawk may call the deployed `graycode-cloud` Worker over authenticated HTTP for optional usage or explicit graph synchronization. Platform failures must not block local CLI execution. ### 7. Engine configuration is instance-scoped -Graycode supplies effective gateway settings while constructing an GraycodeRouter Engine. -Product code must not mutate GraycodeRouter process-global gateway state. +Hawk supplies effective gateway settings while constructing an Eyrie Engine. +Product code must not mutate Eyrie process-global gateway state. ## Enforcement -Graycode CI runs manifest validation, no-local-replace checks, Eagle parity, -support-repository coupling, GraycodeRouter facade checks, internal-layer checks, and +Hawk CI runs manifest validation, no-local-replace checks, Eagle parity, +support-repository coupling, Eyrie facade checks, internal-layer checks, and the AST package-boundary audit. Support repositories run their own boundary -guards. SDK contract tests compare their OpenAPI snapshots with Graycode's public +guards. SDK contract tests compare their OpenAPI snapshots with Hawk's public daemon contract. diff --git a/docs/architecture/graycode-harness.md b/docs/architecture/hawk-harness.md similarity index 76% rename from docs/architecture/graycode-harness.md rename to docs/architecture/hawk-harness.md index ca2503bd1..9f0938459 100644 --- a/docs/architecture/graycode-harness.md +++ b/docs/architecture/hawk-harness.md @@ -1,11 +1,11 @@ -# Graycode Harness +# Hawk Harness -Graycode treats the agent runtime as a product boundary around provider output. +Hawk treats the agent runtime as a product boundary around provider output. ## Tool-Call Path -1. GraycodeRouter normalizes provider protocol responses. -2. Graycode validates and resolves tool metadata. +1. Eyrie normalizes provider protocol responses. +2. Hawk validates and resolves tool metadata. 3. Permission and sandbox policy runs before execution. 4. Tool execution observes timeouts, cancellation, and path boundaries. 5. Results are redacted, persisted, and returned to the model. @@ -23,7 +23,7 @@ interchangeable. The harness is testable without live providers through scripted providers and recorded interactions. Provider protocol and behavioral conformance belongs in -GraycodeRouter's verification package; Graycode owns host-level UX, permissions, persistence, +Eyrie's verification package; Hawk owns host-level UX, permissions, persistence, and review contracts. ## Security Rule diff --git a/docs/architecture/graycode-product-architecture.md b/docs/architecture/hawk-product-architecture.md similarity index 61% rename from docs/architecture/graycode-product-architecture.md rename to docs/architecture/hawk-product-architecture.md index 2cfb32d7f..63b7c65b8 100644 --- a/docs/architecture/graycode-product-architecture.md +++ b/docs/architecture/hawk-product-architecture.md @@ -1,23 +1,23 @@ -# Graycode Product Architecture +# Hawk Product Architecture ## Product statement -Graycode is the model-agnostic main AI coding-agent CLI from GrayCodeAI. +Hawk is the model-agnostic main AI coding-agent CLI from GrayCodeAI. `graycode-eco` is only the local parent folder used to develop the independent repositories. It is not a monorepo or a runtime product. -Graycode is the only primary product surface in the graycode-eco ecosystem. The support repos exist to power Graycode, not to compete with it as standalone products. +Hawk is the only primary product surface in the graycode-eco ecosystem. The support repos exist to power Hawk, not to compete with it as standalone products. -For model execution specifically: **Graycode is the face and composition layer; -GraycodeRouter is the engine.** Graycode owns the conversation and product experience while -the `graycode-router/engine` facade owns the complete provider path from credential and +For model execution specifically: **Hawk is the face and composition layer; +Eyrie is the engine.** Hawk owns the conversation and product experience while +the `eyrie/engine` facade owns the complete provider path from credential and catalog state through model selection and normalized generation/streaming. ## Goals -- Keep Graycode model agnostic. -- Keep Graycode CLI-first and local-first. +- Keep Hawk model agnostic. +- Keep Hawk CLI-first and local-first. - Keep provider integration pluggable. - Keep support engines isolated from each other. - Make swift, review, and verification first-class. @@ -25,8 +25,8 @@ catalog state through model selection and normalized generation/streaming. ## Target repo set -- `graycode` -- `graycode-router` +- `hawk` +- `eyrie` - `harrier` - `shrike` - `swift` @@ -39,7 +39,7 @@ catalog state through model selection and normalized generation/streaming. - `falcon` - `wren` - `owl` -- `graycode-platform` (outside the Graycode runtime module graph) +- `graycode-platform` (outside the Hawk runtime module graph) Directory names are authoritative for dependencies: `harrier` is Harrier, `shrike` is Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is @@ -51,13 +51,13 @@ Merlin. The product labels remain useful in CLI and user-facing documentation. Users / SDKs / Skills | v - GRAYCODE + HAWK | +-----------------------------+ | | v v core execution trust / quality - graycode-router harrier shrike swift kestrel merlin + eyrie harrier shrike swift kestrel merlin \ | / \ | / \ | / \ | / +--+--+---------------------+--+--+ @@ -71,8 +71,8 @@ Users / SDKs / Skills Current implementation in the workspace: ```text -graycode - -> graycode-router +hawk + -> eyrie -> harrier -> shrike -> swift @@ -83,7 +83,7 @@ graycode support engines -> eagle when they need shared contracts -> falcon where MCP serving is shared - x-> Graycode internals or each other + x-> Hawk internals or each other ``` Proposed steady-state architecture: @@ -92,12 +92,12 @@ Proposed steady-state architecture: SDKs / Skills / future integrations | v - Graycode + Hawk | +------------+------------+------------+------------+------------+------------+ | | | | | | | v v v v v v v - GraycodeRouter Harrier Shrike Swift Kestrel Merlin public APIs + Eyrie Harrier Shrike Swift Kestrel Merlin public APIs \ | | | | / +-----------+------------+------------+------------+-----------+ | @@ -107,17 +107,17 @@ SDKs / Skills / future integrations This means: -- Graycode is the product and orchestration boundary +- Hawk is the product and orchestration boundary - all six engines stay at the same architectural level - engines remain independent from each other - shared cross-repo vocabulary lives below them in `eagle` -- SDKs and community skills consume Graycode, not the engines directly +- SDKs and community skills consume Hawk, not the engines directly - `graycode-platform` provides the optional hosted plane through HTTP and a - private Service Binding; it is not imported by Graycode or any engine + private Service Binding; it is not imported by Hawk or any engine -## Graycode responsibilities +## Hawk responsibilities -Graycode owns: +Hawk owns: - CLI entrypoints - session lifecycle @@ -129,7 +129,7 @@ Graycode owns: - engine coordination - public integration surfaces -Graycode does not own: +Hawk does not own: - provider-specific implementation details - engine-specific business logic @@ -137,8 +137,8 @@ Graycode does not own: ## Engine responsibilities -### `graycode-router` -- stable host control and generation facade (`graycode-router/engine`) +### `eyrie` +- stable host control and generation facade (`eyrie/engine`) - credential storage and safe credential status - catalog discovery and model metadata - concrete provider/deployment selection @@ -179,22 +179,22 @@ Graycode does not own: ## Primary runtime flow -1. User invokes `graycode`. -2. Graycode loads product settings, policy, and workspace state, then creates an - GraycodeRouter Engine with effective per-instance custom gateway settings. -3. Graycode creates or resumes a session. -4. Graycode asks `shrike` for context assembly. -5. Graycode asks `harrier` for relevant memory. -6. Graycode routes provider execution through `graycode-router`. -7. Graycode invokes tools and records actions through `swift`. -8. Graycode invokes `kestrel` when review should run. -9. Graycode invokes `merlin` when verification should run. -10. Graycode persists results and returns output to the user. - -At step 6, Graycode passes intent and Graycode-owned conversation DTOs through its -adapter. GraycodeRouter loads provider/catalog/credential state, resolves the gateway, -and returns normalized events. No production Graycode package imports a lower -GraycodeRouter package, and no GraycodeRouter engine DTO is used as Graycode's persistent or CLI +1. User invokes `hawk`. +2. Hawk loads product settings, policy, and workspace state, then creates an + Eyrie Engine with effective per-instance custom gateway settings. +3. Hawk creates or resumes a session. +4. Hawk asks `shrike` for context assembly. +5. Hawk asks `harrier` for relevant memory. +6. Hawk routes provider execution through `eyrie`. +7. Hawk invokes tools and records actions through `swift`. +8. Hawk invokes `kestrel` when review should run. +9. Hawk invokes `merlin` when verification should run. +10. Hawk persists results and returns output to the user. + +At step 6, Hawk passes intent and Hawk-owned conversation DTOs through its +adapter. Eyrie loads provider/catalog/credential state, resolves the gateway, +and returns normalized events. No production Hawk package imports a lower +Eyrie package, and no Eyrie engine DTO is used as Hawk's persistent or CLI schema. ## Implementation phases @@ -206,41 +206,41 @@ schema. ### Phase 2 - add `eagle` -- move shared types out of Graycode internals +- move shared types out of Hawk internals Status: - completed - shared contracts now exist for `types`, `review`, `verify`, `tools`, `events`, and `policy` ### Phase 3 -- remove engine imports of Graycode internals +- remove engine imports of Hawk internals - remove engine-to-engine coupling Status: - completed for current workspace boundaries -- local/CI guards now block support-repo imports of `graycode/internal/*` and removed legacy `graycode/shared/types` +- local/CI guards now block support-repo imports of `hawk/internal/*` and removed legacy `hawk/shared/types` ### Phase 4 -- harden orchestration boundaries in Graycode +- harden orchestration boundaries in Hawk - formalize provider, swift, review, and verify integration points Status: - completed for the local runtime boundary -- Graycode owns runtime DTOs and review/verify product-boundary contracts -- Graycode's `ChatClient` anti-corruption port translates only to `graycode-router/engine` -- all lower-level GraycodeRouter production imports are forbidden by shell guards and meta-audit tests +- Hawk owns runtime DTOs and review/verify product-boundary contracts +- Hawk's `ChatClient` anti-corruption port translates only to `eyrie/engine` +- all lower-level Eyrie production imports are forbidden by shell guards and meta-audit tests ### Phase 5 -- align SDKs and skills to Graycode public interfaces only +- align SDKs and skills to Hawk public interfaces only Status: -- policy is now explicit and guarded in Graycode docs +- policy is now explicit and guarded in Hawk docs - `sparrow` is covered by the support-repo coupling guard so it cannot grow direct engine imports - broader non-Go consumer enforcement remains future work ### Phase 6 -- remove legacy `graycode/shared/types` +- remove legacy `hawk/shared/types` - keep import guards in place so the old path cannot return Status: @@ -250,9 +250,9 @@ Status: The architecture is in good shape when: -- `graycode` is the only product surface +- `hawk` is the only product surface - engines depend only on `eagle` -- shared types no longer live in Graycode internals as a cross-repo API +- shared types no longer live in Hawk internals as a cross-repo API - provider abstraction is stable - swift, review, and verification are part of the standard runtime flow - deprecated compatibility surfaces have a documented removal path and active guardrails diff --git a/docs/architecture/graycode-provider-abstraction.md b/docs/architecture/hawk-provider-abstraction.md similarity index 58% rename from docs/architecture/graycode-provider-abstraction.md rename to docs/architecture/hawk-provider-abstraction.md index 7bddf41c4..6ac4a47e0 100644 --- a/docs/architecture/graycode-provider-abstraction.md +++ b/docs/architecture/hawk-provider-abstraction.md @@ -1,23 +1,23 @@ -# Graycode Provider Abstraction +# Hawk Provider Abstraction ## Goal -Graycode must remain model agnostic. +Hawk must remain model agnostic. -That means Graycode should support multiple providers without leaking provider-specific assumptions across the product. +That means Hawk should support multiple providers without leaking provider-specific assumptions across the product. ## Design principle -Provider-specific code lives behind GraycodeRouter's stable `graycode-router/engine` host facade. -Graycode is the face and composition root; GraycodeRouter is the engine. +Provider-specific code lives behind Eyrie's stable `eyrie/engine` host facade. +Hawk is the face and composition root; Eyrie is the engine. -Graycode decides: +Hawk decides: - what capability the task needs - the semantic intent (`fast`, `balanced`, `reasoning`, `economical`) - whether an exact user-selected model may fall back -`graycode-router` handles: +`eyrie` handles: - capability-to-model resolution - provider and deployment selection @@ -40,10 +40,10 @@ Graycode decides: - error classification - timeout/cancellation support -## Graycode-facing abstraction +## Hawk-facing abstraction -Graycode depends on its small `ChatClient` product port and adapts it only to -`graycode-router/engine`, never to a vendor-specific or lower-level GraycodeRouter client. +Hawk depends on its small `ChatClient` product port and adapts it only to +`eyrie/engine`, never to a vendor-specific or lower-level Eyrie client. Example concerns: @@ -56,16 +56,16 @@ Example concerns: ## Rules -- no direct vendor SDK imports in unrelated Graycode packages +- no direct vendor SDK imports in unrelated Hawk packages - no provider-specific branches inside review/verify logic - no model-specific assumptions inside session persistence -- keep task-semantic policy inside Graycode orchestration -- keep provider/deployment routing, health, retry, and fallback inside GraycodeRouter -- Graycode production integrations use `github.com/GrayCodeAI/graycode-router/engine` -- direct imports of lower GraycodeRouter packages are forbidden and CI-enforced +- keep task-semantic policy inside Hawk orchestration +- keep provider/deployment routing, health, retry, and fallback inside Eyrie +- Hawk production integrations use `github.com/GrayCodeAI/eyrie/engine` +- direct imports of lower Eyrie packages are forbidden and CI-enforced - custom gateway settings enter as `engine.Options.CustomGateways` and are isolated per Engine instance -- Graycode-owned JSON, session, and conversation schemas are explicit projections; +- Hawk-owned JSON, session, and conversation schemas are explicit projections; they never become aliases of engine DTOs - local preflight readiness and remote live verification are distinct states diff --git a/docs/architecture/graycode-repo-roles.md b/docs/architecture/hawk-repo-roles.md similarity index 58% rename from docs/architecture/graycode-repo-roles.md rename to docs/architecture/hawk-repo-roles.md index 106043570..384818361 100644 --- a/docs/architecture/graycode-repo-roles.md +++ b/docs/architecture/hawk-repo-roles.md @@ -1,8 +1,8 @@ -# Graycode Repo Roles +# Hawk Repo Roles ## Product repo -### `graycode` +### `hawk` Main CLI and product repository. It is the orchestration root; `graycode-eco` is only the local parent folder for the independent repositories. @@ -17,57 +17,57 @@ Owns: - engine coordination - user-facing model/configuration presentation and stable CLI schemas -Graycode is the only primary product surface. Users interact with Graycode, not with six +Hawk is the only primary product surface. Users interact with Hawk, not with six separate end-user products. ## Support engines -### `graycode-router` -Graycode provider engine. Its public host boundary is `graycode-router/engine`, which owns +### `eyrie` +Hawk provider engine. Its public host boundary is `eyrie/engine`, which owns credentials, provider state, catalog discovery, model/deployment selection, -transport, resilience, and normalized generation/streaming. Graycode production -code has zero imports of GraycodeRouter's lower-level packages. +transport, resilience, and normalized generation/streaming. Hawk production +code has zero imports of Eyrie's lower-level packages. ### `harrier` (Harrier) -Graycode memory engine. +Hawk memory engine. ### `shrike` (Shrike) -Graycode context and token engine. +Hawk context and token engine. ### `swift` (Swift) -Graycode audit and replay engine. +Hawk audit and replay engine. ### `kestrel` (Kestrel) -Graycode review engine. +Hawk review engine. ### `merlin` (Merlin) -Graycode verification engine. +Hawk verification engine. All six support engines are peers: -- `graycode-router` +- `eyrie` - `harrier` (Harrier) - `shrike` (Shrike) - `swift` (Swift) - `kestrel` (Kestrel) - `merlin` (Merlin) -They should stay isolated from each other and are coordinated by Graycode; +They should stay isolated from each other and are coordinated by Hawk; they may depend on `eagle` where a shared vocabulary is required. ## Ecosystem repos ### `sparrow` -Go integration surface for Graycode public APIs/contracts. +Go integration surface for Hawk public APIs/contracts. ### `robin` -Python integration surface for Graycode public APIs/contracts. +Python integration surface for Hawk public APIs/contracts. ### `wren` -TypeScript integration surface for Graycode public APIs/contracts. +TypeScript integration surface for Hawk public APIs/contracts. ### `starling` -Reusable Graycode skills, recipes, and extension packs. +Reusable Hawk skills, recipes, and extension packs. ## Shared foundation @@ -82,23 +82,23 @@ transports, and handler helpers that MCP-serving engines (`kestrel`, `merlin`) would otherwise duplicate. Like `eagle`, it sits below the engines: it must not import -engines, graycode, or graycode-platform. +engines, hawk, or graycode-platform. ## Tooling and platform ### `owl` Read-only architecture visualization tooling. It consumes the generated -projection of `graycode/ecosystem.yaml`; it is not a runtime dependency. +projection of `hawk/ecosystem.yaml`; it is not a runtime dependency. ### `graycode-platform` -Separate web, browser-BFF, and Graycode Cloud repository. Its Worker deployment is -named `graycode-cloud`, but it connects to Graycode only through authenticated +Separate web, browser-BFF, and Hawk Cloud repository. Its Worker deployment is +named `graycode-cloud`, but it connects to Hawk only through authenticated runtime HTTP/Service Binding and is never a Go dependency. ## Role rules -- Users should feel they are using `graycode`, not six unrelated tools. +- Users should feel they are using `hawk`, not six unrelated tools. - Engines are internal capabilities from a product perspective. - Engines can stay in separate repos for isolation, testing, and replacement. - Engines must not import each other. -- SDKs and skills extend Graycode, but should not bypass Graycode to reach engines directly. +- SDKs and skills extend Hawk, but should not bypass Hawk to reach engines directly. diff --git a/docs/architecture/graycode-review-verify-lifecycle.md b/docs/architecture/hawk-review-verify-lifecycle.md similarity index 84% rename from docs/architecture/graycode-review-verify-lifecycle.md rename to docs/architecture/hawk-review-verify-lifecycle.md index b50711c95..9802da615 100644 --- a/docs/architecture/graycode-review-verify-lifecycle.md +++ b/docs/architecture/hawk-review-verify-lifecycle.md @@ -1,8 +1,8 @@ -# Graycode Review and Verify Lifecycle +# Hawk Review and Verify Lifecycle ## Goal -Review and verification should be standard parts of Graycode's workflow, not optional bolt-ons. +Review and verification should be standard parts of Hawk's workflow, not optional bolt-ons. ## Roles @@ -54,7 +54,7 @@ Run `merlin` for: ## Decision policy -Graycode should define when review and verification are: +Hawk should define when review and verification are: - required - recommended diff --git a/docs/architecture/graycode-swift-event-model.md b/docs/architecture/hawk-swift-event-model.md similarity index 95% rename from docs/architecture/graycode-swift-event-model.md rename to docs/architecture/hawk-swift-event-model.md index de092ab71..ccd33400e 100644 --- a/docs/architecture/graycode-swift-event-model.md +++ b/docs/architecture/hawk-swift-event-model.md @@ -1,4 +1,4 @@ -# Graycode Swift Event Model +# Hawk Swift Event Model ## Goal @@ -71,7 +71,7 @@ In the local CLI world, swift primarily supports: - debugging - reproduction - replay -- audit of what Graycode changed and why +- audit of what Hawk changed and why ## Long-term use diff --git a/docs/architecture/plan.md b/docs/architecture/plan.md index 6c9b239de..09275039a 100644 --- a/docs/architecture/plan.md +++ b/docs/architecture/plan.md @@ -1,8 +1,8 @@ -# Graycode Architecture - Technical Plan +# Hawk Architecture - Technical Plan ## Overview -This plan defines the technical approach for implementing the graycode architecture specification. The architecture is already largely implemented; this plan documents the existing design decisions and identifies gaps. +This plan defines the technical approach for implementing the hawk architecture specification. The architecture is already largely implemented; this plan documents the existing design decisions and identifies gaps. ## Architecture Decisions @@ -151,7 +151,7 @@ services; it is not a claim that those fields have already moved. ## Dependencies -- **graycode-router:** LLM provider engine behind `graycode-router/engine` (workspace sibling) +- **eyrie:** LLM provider engine behind `eyrie/engine` (workspace sibling) - **harrier:** Graph-based persistent memory (workspace sibling) - **shrike:** Tokenizer, compression (workspace sibling) - **eagle:** Shared types (workspace sibling) diff --git a/docs/architecture/spec.md b/docs/architecture/spec.md index 7966b25b6..959158ac0 100644 --- a/docs/architecture/spec.md +++ b/docs/architecture/spec.md @@ -1,8 +1,8 @@ -# Graycode Architecture Specification +# Hawk Architecture Specification ## Problem Statement -Graycode is an AI-powered coding agent for the terminal. This specification defines the complete architecture: repository structure, agent loop, agile workflow, feedback loops, and edge case handling. It serves as the authoritative reference for how graycode works. +Hawk is an AI-powered coding agent for the terminal. This specification defines the complete architecture: repository structure, agent loop, agile workflow, feedback loops, and edge case handling. It serves as the authoritative reference for how hawk works. ## Scope @@ -20,15 +20,15 @@ Graycode is an AI-powered coding agent for the terminal. This specification defi ### REQ-1: Repository Structure -Graycode SHALL be organized as a Go repository and workspace entry point within a -multi-repository ecosystem. The Graycode repository has the following top-level +Hawk SHALL be organized as a Go repository and workspace entry point within a +multi-repository ecosystem. The Hawk repository has the following top-level layout: | Directory | Purpose | |-----------|---------| | `cmd/` | CLI entry point (Cobra) and TUI (Bubble Tea) | | `internal/` | Private Go packages (not importable by external repos) | -| Parent `go.work` | Resolves the nine local Go siblings: graycode, eagle, falcon, graycode-router, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | +| Parent `go.work` | Resolves the nine local Go siblings: hawk, eagle, falcon, eyrie, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | | `spec/` | OpenSpec schema consumed by `internal/spec` | | `docs/` | Architecture docs, design docs, plans | | `rules/` | User-defined rules | @@ -45,7 +45,7 @@ The `internal/` directory SHALL contain the following packages: | `tool/` | 40+ built-in tools (file edit, git, codegen, spec tools, etc.) | | `permissions/` | Guardian, rules DSL, boundary checker | | `plugin/` | Skills loader, registry, auto-skill, marketplace install | -| `config/` | Product settings, GraycodeRouter composition, state migration | +| `config/` | Product settings, Eyrie composition, state migration | | `session/` | SQLite persistence, search, export, replay | | `hooks/` | Event-driven plugin system | | `mcp/` | Model Context Protocol client/server | @@ -74,7 +74,7 @@ The `internal/engine/` package SHALL contain the following sub-systems: |------------|---------| | `stream.go` | The agent loop (agentLoop) - main orchestration | | `session.go` | Session struct and sub-services | -| `chat_service.go` | Graycode ChatClient port, engine adapter coordination, compact | +| `chat_service.go` | Hawk ChatClient port, engine adapter coordination, compact | | `safety/` | Permission engine, trust tiers, spec gate | | `compact/` | Context compaction (collapse, micro, smart, truncate) | | `ctxmgr/` | Context providers, packing, visualization | @@ -94,17 +94,17 @@ The `internal/engine/` package SHALL contain the following sub-systems: Provider boundary invariant: ```text -Graycode CLI/TUI + conversation + tools +Hawk CLI/TUI + conversation + tools | - Graycode-owned ports/DTOs + Hawk-owned ports/DTOs | v - graycode-router/engine + eyrie/engine credentials -> catalog -> routing -> generate/stream ``` -No production Graycode package may import a lower GraycodeRouter package. Custom gateways -are supplied per Engine instance, and GraycodeRouter DTOs are not Graycode persistence or +No production Hawk package may import a lower Eyrie package. Custom gateways +are supplied per Engine instance, and Eyrie DTOs are not Hawk persistence or CLI output schemas. ### REQ-4: Agent Loop Lifecycle @@ -129,7 +129,7 @@ The agent loop in `stream.go` SHALL execute the following phases: 6. Refresh Harrier memories 7. Build LLM ChatOptions (system prompt, tools, model) 8. Inject ephemeral context (beliefs, matched skills, spec stage) -9. Execute the LLM call via `ChatService.Stream()` and the `graycode-router/engine` adapter +9. Execute the LLM call via `ChatService.Stream()` and the `eyrie/engine` adapter 10. Process response (tool calls, messages, cost tracking) 11. Check termination conditions diff --git a/docs/architecture/tasks.md b/docs/architecture/tasks.md index f0712a7b5..40d608b1e 100644 --- a/docs/architecture/tasks.md +++ b/docs/architecture/tasks.md @@ -1,7 +1,7 @@ -# Graycode Architecture - Implementation Tasks +# Hawk Architecture - Implementation Tasks > **Historical.** This was the initial architecture implementation checklist. -> It is superseded by `graycode-architecture-v1-definition-of-done.md`, which +> It is superseded by `hawk-architecture-v1-definition-of-done.md`, which > reflects the current shipping bar. Kept for record; do not use as a > current TODO list. diff --git a/docs/compatibility.md b/docs/compatibility.md index 56cee9299..1a2da6939 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -12,11 +12,11 @@ Platform/provider capability metadata is separate: [`platform-capabilities.json` ```jsonc { - "components": ["graycode", "graycode-router", ...], // canonical eco roster - "dependencies": { "graycode": ["graycode-router", ...] }, // who depends on who + "components": ["hawk", "eyrie", ...], // canonical eco roster + "dependencies": { "hawk": ["eyrie", ...] }, // who depends on who "matrices": [ - { "name": "stable", "components": { "graycode": "0.1.0", "graycode-router": "0.1.0", ... } }, - { "name": "next", "components": { "graycode": "main", "graycode-router": "main", ... } } + { "name": "stable", "components": { "hawk": "0.1.0", "eyrie": "0.1.0", ... } }, + { "name": "next", "components": { "hawk": "main", "eyrie": "main", ... } } ] } ``` @@ -48,7 +48,7 @@ existing consumers. `.shared-templates/workflows/compatibility-test.yml.tmpl` is a reusable workflow that: -1. Reads `testdata/compatibility-matrix.json` from the graycode repo. +1. Reads `testdata/compatibility-matrix.json` from the hawk repo. 2. Checks out each component at the version listed in the named matrix. 3. Builds + tests the cross-repo integration scenarios. @@ -59,7 +59,7 @@ It runs on: ## Why bother -- **Bug reports become triageable.** "I ran graycode 0.4 with graycode-router 0.2" — you +- **Bug reports become triageable.** "I ran hawk 0.4 with eyrie 0.2" — you immediately know whether that combination was ever tested. - **Consumers can pin reliably.** Downstream projects that vendor multiple eco packages can pin to a known-good stable matrix instead of guessing. @@ -70,18 +70,18 @@ It runs on: ## Pin freshness (advisory) -Separate from the matrix above: `graycode`'s own `go.mod` directly pins +Separate from the matrix above: `hawk`'s own `go.mod` directly pins shared leaf dependencies (currently `falcon` is tracked), and several sibling-repository consumers (`merlin`/Merlin, `kestrel`/Kestrel, ...) pin the *same* dependencies independently in their own `go.mod`. Go's minimal version -selection means whatever `graycode` pins wins in `graycode`'s own build — but if a +selection means whatever `hawk` pins wins in `hawk`'s own build — but if a consumer's own pin is older, that consumer's CI has never actually tested the version that ships. This is exactly the kind of drift that let a real bug through in July 2026 (a stale goreleaser pin sat unnoticed until the first tag push exercised it). `make compat-drift` (wired into this workflow as an advisory, non-blocking -step) reports any such mismatch by comparing `graycode/go.mod` against each +step) reports any such mismatch by comparing `hawk/go.mod` against each `..//go.mod`: ```bash @@ -90,7 +90,7 @@ make compat-drift This **never fails the build** — it's a signal for humans to notice and re-pin the consumer, not a gate. It checks sibling repositories when they are -present; a Graycode-only checkout simply reports no local consumers to compare. +present; a Hawk-only checkout simply reports no local consumers to compare. ## Validating the file diff --git a/docs/design/ECOSYSTEM-MARKETPLACE.md b/docs/design/ECOSYSTEM-MARKETPLACE.md index 6dfdbcc5b..bf4cdfbed 100644 --- a/docs/design/ECOSYSTEM-MARKETPLACE.md +++ b/docs/design/ECOSYSTEM-MARKETPLACE.md @@ -3,7 +3,7 @@ **Status:** Draft **Author:** Ecosystem / DX working group **Last updated:** 2026-06-06 -**Scope:** Multi-month effort spanning all 5 repos (graycode, graycode-router, harrier, shrike, swift) + a new gallery web property + a unified docs site. +**Scope:** Multi-month effort spanning all 5 repos (hawk, eyrie, harrier, shrike, swift) + a new gallery web property + a unified docs site. > This is a design a team executes against, not a code-session deliverable. It grounds every claim in the actual graycode-eco codebase (cited as `path:line`) and reuses what already exists rather than greenfielding. @@ -13,7 +13,7 @@ Two adjacent gaps from `TOP20_COMPARISON.md` are addressed together because they share infrastructure (a registry index, a content format, a web property): -1. **Centralized extension gallery / plugin marketplace** — `TOP20_COMPARISON.md:65` (graycode P1) and `TOP20_COMPARISON.md:227` (cross-cutting P1). +1. **Centralized extension gallery / plugin marketplace** — `TOP20_COMPARISON.md:65` (hawk P1) and `TOP20_COMPARISON.md:227` (cross-cutting P1). 2. **Documentation site (Docusaurus/Mintlify) for the graycode-eco ecosystem** — `TOP20_COMPARISON.md:232` (cross-cutting P1). ### Who ships this in the Top 20 @@ -29,14 +29,14 @@ Two adjacent gaps from `TOP20_COMPARISON.md` are addressed together because they graycode-eco is **not** starting from zero. A large fraction of the marketplace already exists as working Go and a populated registry: -- **A populated registry already ships.** `starling/registry.json` is a 4.3 MB JSON array of skill entries with `name`, `description`, `category`, `tags`, `path`, `file_count`, `has_scripts` (see file head). It is served raw from GitHub and consumed by graycode at `graycode/internal/plugin/registry.go:18` (`defaultIndexURL = https://raw.githubusercontent.com/GrayCodeAI/starling/main/registry.json`). -- **A registry client already works.** `graycode/internal/plugin/registry.go` defines `SkillEntry`/`SkillIndex` (`:21`, `:36`), `FetchIndex` with a 1-hour cache (`:60`), and `Install/Remove/InstalledSkillInfo` (`:196`, `:289`, `:309`). -- **CLI surface already exists.** `graycode skills {list,search,install,remove,info,trending,audit}` is wired in `graycode/cmd/skills_cmd.go:16-211`. +- **A populated registry already ships.** `starling/registry.json` is a 4.3 MB JSON array of skill entries with `name`, `description`, `category`, `tags`, `path`, `file_count`, `has_scripts` (see file head). It is served raw from GitHub and consumed by hawk at `hawk/internal/plugin/registry.go:18` (`defaultIndexURL = https://raw.githubusercontent.com/GrayCodeAI/starling/main/registry.json`). +- **A registry client already works.** `hawk/internal/plugin/registry.go` defines `SkillEntry`/`SkillIndex` (`:21`, `:36`), `FetchIndex` with a 1-hour cache (`:60`), and `Install/Remove/InstalledSkillInfo` (`:196`, `:289`, `:309`). +- **CLI surface already exists.** `hawk skills {list,search,install,remove,info,trending,audit}` is wired in `hawk/cmd/skills_cmd.go:16-211`. - **The extension format is partially standardized.** `SKILL.md` files carry YAML frontmatter (`name`, `description`, `license`, `tags`, `version`) — see `starling/api/openapi.yaml` `x-skill-format` and `categories/testing/ab-test-setup/SKILL.md` frontmatter. -- **A V2 manifest already supports MCP-adjacent bundles.** `graycode/internal/plugin/manifest_v2.go:11` (`ManifestV2`) carries `Tools`, `Permissions`, `Hooks` (`:32` `ManifestHook` with `Event`/`Command`/`Priority`), `Config`, `Dependencies`, `Mode` (subprocess/daemon). -- **Trust scaffolding already exists.** `graycode/internal/plugin/malware_check.go:19` blocks `eval()`, pipe-to-shell, reverse shells; `graycode/internal/plugin/audit.go` flags hidden-Unicode / homoglyph attacks (`graycode skills audit`). +- **A V2 manifest already supports MCP-adjacent bundles.** `hawk/internal/plugin/manifest_v2.go:11` (`ManifestV2`) carries `Tools`, `Permissions`, `Hooks` (`:32` `ManifestHook` with `Event`/`Command`/`Priority`), `Config`, `Dependencies`, `Mode` (subprocess/daemon). +- **Trust scaffolding already exists.** `hawk/internal/plugin/malware_check.go:19` blocks `eval()`, pipe-to-shell, reverse shells; `hawk/internal/plugin/audit.go` flags hidden-Unicode / homoglyph attacks (`hawk skills audit`). -The gap is therefore **consolidation and elevation**, not invention: (a) a *standardized, multi-asset* extension format (today's registry is skills-only); (b) a *browsable web gallery*; (c) a *trust/signing* story beyond static malware regexes; and (d) a *unified docs site* that today is five disconnected `README.md`/`ARCHITECTURE.md` trees (`graycode/docs/`, `graycode-router/docs/`, `harrier/`, `shrike/docs/`, `swift/docs/`). +The gap is therefore **consolidation and elevation**, not invention: (a) a *standardized, multi-asset* extension format (today's registry is skills-only); (b) a *browsable web gallery*; (c) a *trust/signing* story beyond static malware regexes; and (d) a *unified docs site* that today is five disconnected `README.md`/`ARCHITECTURE.md` trees (`hawk/docs/`, `eyrie/docs/`, `harrier/`, `shrike/docs/`, `swift/docs/`). --- @@ -44,9 +44,9 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan ### Goals -- **G1.** A single standardized **Graycode Extension** format: a directory with `extension.yaml` (or extended `SKILL.md` frontmatter) that can bundle any subset of: MCP servers, slash commands, prompts, hooks, themes, sub-agents (personas), and skills. -- **G2.** A **browsable gallery** (graycodeskills.dev-style) generated from the registry index, with search, categories, per-extension detail pages, and copy-paste install commands. -- **G3.** A versioned, machine-readable **registry index format** (v2) that is a superset of today's `registry.json` and remains backward compatible with `graycode/internal/plugin/registry.go`. +- **G1.** A single standardized **Hawk Extension** format: a directory with `extension.yaml` (or extended `SKILL.md` frontmatter) that can bundle any subset of: MCP servers, slash commands, prompts, hooks, themes, sub-agents (personas), and skills. +- **G2.** A **browsable gallery** (hawkskills.dev-style) generated from the registry index, with search, categories, per-extension detail pages, and copy-paste install commands. +- **G3.** A versioned, machine-readable **registry index format** (v2) that is a superset of today's `registry.json` and remains backward compatible with `hawk/internal/plugin/registry.go`. - **G4.** An **install / update / remove flow** with **signing & trust verification** (provenance + signature check, not just regex malware scan). - **G5.** A **unified documentation site** spanning all 5 repos: getting-started, per-repo API reference, architecture diagrams, cookbook recipes, and the competitor comparison tables — with the gallery either embedded in or cross-linked from it. - **G6.** Privacy-first throughout: no telemetry-by-default, no account required to browse/install, install is auditable and offline-capable. @@ -56,7 +56,7 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan - **NG1.** A hosted, multi-tenant SaaS backend with accounts, billing, or org RBAC (those are separate P0/P2 gaps at `TOP20_COMPARISON.md:33`, `:72`). The gallery is a **static, build-time-generated site backed by a git repo**, not a dynamic app server. - **NG2.** Paid/commercial extensions, license-key enforcement, or DRM. - **NG3.** Replacing per-repo deep-dive docs; the unified site **aggregates and cross-links**, it does not fork content. -- **NG4.** A new package manager. We reuse `git clone`-based install (`registry.go:215`) and the `~/.graycode/skills` / `.graycode/skills` layout (`registry.go:201-203`). +- **NG4.** A new package manager. We reuse `git clone`-based install (`registry.go:215`) and the `~/.hawk/skills` / `.hawk/skills` layout (`registry.go:201-203`). - **NG5.** Runtime arbitrary-code execution in the browser gallery (it renders metadata only; nothing executes client-side). --- @@ -78,10 +78,10 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan │ raw.githubusercontent (index + sigs) │ build inputs ▼ ▼ ┌───────────────────────────────┐ ┌──────────────────────────────┐ -│ graycode CLI (Go) │ │ Gallery + Docs site (static) │ +│ hawk CLI (Go) │ │ Gallery + Docs site (static) │ │ internal/plugin/registry.go │ │ Docusaurus or Mintlify │ │ + new: trust.go, extman.go │ │ - /extensions (gallery) │ -│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{graycode,graycode-router,...} │ +│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{hawk,eyrie,...} │ │ internal/plugin/manifest_v2 │ │ - generated from registry.v2 │ └───────────────────────────────┘ └──────────────────────────────┘ ``` @@ -91,22 +91,22 @@ The gap is therefore **consolidation and elevation**, not invention: (a) a *stan Two interoperable representations, chosen so we **do not break** today's skills-only registry: **(a) Minimal — extended `SKILL.md` frontmatter** (for single-asset skills, unchanged path): -Already parsed at `graycode/internal/plugin/skill_loader.go:51` (`parseSkillFrontMatter`) and discovered at `graycode/internal/tool/skill.go:62` (`discoverSkills`). We keep this working verbatim. +Already parsed at `hawk/internal/plugin/skill_loader.go:51` (`parseSkillFrontMatter`) and discovered at `hawk/internal/tool/skill.go:62` (`discoverSkills`). We keep this working verbatim. **(b) Full — `extension.yaml`** (new, for multi-asset bundles): ```yaml -apiVersion: graycode.extension/v1 +apiVersion: hawk.extension/v1 kind: Extension name: terraform-pro version: 1.4.0 description: Terraform authoring — skills, an MCP server, slash commands, and a theme. license: MIT author: { name: GrayCode AI, url: https://github.com/GrayCodeAI } -homepage: https://graycodeskills.dev/extensions/terraform-pro +homepage: https://hawkskills.dev/extensions/terraform-pro keywords: [terraform, iac, devops] compat: - minGraycodeVersion: "1.2.0" # maps to ManifestV2.MinGraycodeVersion (manifest_v2.go:20) + minHawkVersion: "1.2.0" # maps to ManifestV2.MinHawkVersion (manifest_v2.go:20) provides: skills: [ ./SKILL.md ] commands: [ ./commands/plan.md, ./commands/apply.md ] # slash commands @@ -121,11 +121,11 @@ provides: subagents: [ ./agents/iac-reviewer.md ] # personas (internal/multiagent) permissions: [ run_shell, network ] # ManifestV2.Permissions signing: - publisher: "graycode" + publisher: "hawk" signature: "signatures/terraform-pro.sig" ``` -The `provides` block is intentionally a **strict superset of the existing assets graycode already loads**: skills (`tool/skill.go`), hooks (`manifest_v2.go:32`), MCP servers (`internal/mcp/mcp.go`), and personas/sub-agents (`internal/multiagent/agents/`, cited at `TOP20_COMPARISON.md:62`). No new runtime concept is invented — `extension.yaml` is a *bundling manifest* over existing loaders. +The `provides` block is intentionally a **strict superset of the existing assets hawk already loads**: skills (`tool/skill.go`), hooks (`manifest_v2.go:32`), MCP servers (`internal/mcp/mcp.go`), and personas/sub-agents (`internal/multiagent/agents/`, cited at `TOP20_COMPARISON.md:62`). No new runtime concept is invented — `extension.yaml` is a *bundling manifest* over existing loaders. ### 3.3 Registry index format v2 (G3) @@ -149,7 +149,7 @@ Today's `SkillEntry` (`registry.go:21`) already has most fields. v2 adds a `kind "repo": "GrayCodeAI/starling", "provides": { "skills":1, "commands":2, "hooks":1, "mcpServers":1, "themes":1, "subagents":1 }, "signed": true, - "publisher": "graycode", + "publisher": "hawk", "installs": 0, "updated_at": "..." } @@ -157,16 +157,16 @@ Today's `SkillEntry` (`registry.go:21`) already has most fields. v2 adds a `kind } ``` -Backward compatibility: `registry.go`'s `SkillIndex.Skills` (`:36`) keeps reading `registry.json`. A small shim maps v2 `extensions[]` → `SkillEntry` so older graycode binaries keep working; new binaries prefer `registry.v2.json` and fall back. The existing `tools/update_registry.py` / `tools/registry_schema.py` generators (in `starling/tools/`) are extended to emit both. +Backward compatibility: `registry.go`'s `SkillIndex.Skills` (`:36`) keeps reading `registry.json`. A small shim maps v2 `extensions[]` → `SkillEntry` so older hawk binaries keep working; new binaries prefer `registry.v2.json` and fall back. The existing `tools/update_registry.py` / `tools/registry_schema.py` generators (in `starling/tools/`) are extended to emit both. ### 3.4 API surface The marketplace has **no HTTP API server** (per NG1). Its "API" is: 1. **The static index files** over `raw.githubusercontent.com` (already the contract — `registry.go:18`). -2. **The graycode CLI**, extended: - - Existing: `graycode skills {list,search,install,remove,info,trending,audit}` (`cmd/skills_cmd.go`). - - New: `graycode ext {search,install,update,verify,info,publish}` for multi-asset extensions, plus `graycode ext install --verify` (signature-checked by default). +2. **The hawk CLI**, extended: + - Existing: `hawk skills {list,search,install,remove,info,trending,audit}` (`cmd/skills_cmd.go`). + - New: `hawk ext {search,install,update,verify,info,publish}` for multi-asset extensions, plus `hawk ext install --verify` (signature-checked by default). 3. **The gallery's generated JSON** (`/extensions/index.json`) for the site's client-side search (build-time, no server). ### 3.5 Key flows (sequences) @@ -174,7 +174,7 @@ The marketplace has **no HTTP API server** (per NG1). Its "API" is: **Flow A — Install with trust verification** ``` -user: graycode ext install GrayCodeAI/starling terraform-pro +user: hawk ext install GrayCodeAI/starling terraform-pro → RegistryClient.FetchIndex() registry.go:60 (cache <1h) → resolve entry, get repo+path+publisher+signature → git clone --depth 1 (existing) registry.go:215 @@ -184,7 +184,7 @@ user: graycode ext install GrayCodeAI/starling terraform-pro → audit.go (hidden-unicode) + malware_check.go (regex) on every file blocked pattern → abort malware_check.go:19 → parse extension.yaml → ManifestV2 manifest_v2.go:42 - → copy assets to ~/.graycode/skills/ (and commands/hooks/mcp dirs) + → copy assets to ~/.hawk/skills/ (and commands/hooks/mcp dirs) scope user|project registry.go:201-203 → inject source-tracking metadata registry.go (injectSourceMetadata) → print: installed N assets (skills, 1 mcp, 1 theme, ...) @@ -192,18 +192,18 @@ user: graycode ext install GrayCodeAI/starling terraform-pro **Flow B — Gallery browse & install (no account, no server)** ``` -browser → graycodeskills.dev/extensions (static Docusaurus/Mintlify page) +browser → hawkskills.dev/extensions (static Docusaurus/Mintlify page) → client loads /extensions/index.json (generated from registry.v2.json) → user filters by category/tag/capability badge (skills, MCP, hooks, theme) → detail page renders extension.yaml metadata + README + capability list - → "Install" shows: graycode ext install (copy button) + → "Install" shows: hawk ext install (copy button) → nothing executes in browser; install happens locally via CLI ``` **Flow C — Docs build (unified site, all 5 repos)** ``` CI (docs repo) → - pull README.md / ARCHITECTURE.md / docs/** from each of graycode, graycode-router, + pull README.md / ARCHITECTURE.md / docs/** from each of hawk, eyrie, harrier (Harrier), shrike (Shrike), swift (Swift) (independent checkouts or sparse checkout) → transform: inject sidebars, rewrite intra-repo links → generate /extensions/* from registry.v2.json @@ -219,36 +219,36 @@ Concrete reuse map — what is *already there* and what each piece becomes: | New capability | Reuse today | File:line | |---|---|---| -| Registry fetch + 1h cache | `RegistryClient.FetchIndex` | `graycode/internal/plugin/registry.go:60` | +| Registry fetch + 1h cache | `RegistryClient.FetchIndex` | `hawk/internal/plugin/registry.go:60` | | Index URL contract | `defaultIndexURL` | `registry.go:18` | | Entry/index schema | `SkillEntry`, `SkillIndex` | `registry.go:21`, `:36` | | git-clone install + scopes | `RegistryClient.Install` | `registry.go:196-215`, `:201-203` | | Remove / info | `Remove`, `InstalledSkillInfo` | `registry.go:289`, `:309` | -| CLI command tree | `skillsCmd` + subcommands | `graycode/cmd/skills_cmd.go:16-211` | -| Skill discovery roots | `discoverSkills`, `skillRoots` | `graycode/internal/tool/skill.go:62`, `:91` | -| Frontmatter parsing | `parseSkillFrontMatter` | `graycode/internal/plugin/skill_loader.go:51` | -| Multi-asset manifest | `ManifestV2` (+`Hooks`,`Config`,`Dependencies`,`Mode`) | `graycode/internal/plugin/manifest_v2.go:11`, `:32` | +| CLI command tree | `skillsCmd` + subcommands | `hawk/cmd/skills_cmd.go:16-211` | +| Skill discovery roots | `discoverSkills`, `skillRoots` | `hawk/internal/tool/skill.go:62`, `:91` | +| Frontmatter parsing | `parseSkillFrontMatter` | `hawk/internal/plugin/skill_loader.go:51` | +| Multi-asset manifest | `ManifestV2` (+`Hooks`,`Config`,`Dependencies`,`Mode`) | `hawk/internal/plugin/manifest_v2.go:11`, `:32` | | Hook bundling | `ManifestHook{Event,Command,Priority}` | `manifest_v2.go:32` | -| MCP server bundling | MCP client/loader | `graycode/internal/mcp/mcp.go`, `graycode/internal/tool/mcp_tool.go` | -| Sub-agent bundling | Persona system (YAML frontmatter MD) | `graycode/internal/multiagent/agents/` (cf. `TOP20_COMPARISON.md:62`) | -| Rule/skill discovery precedence | `DefaultRuleSources`, `RuleDiscoverer` | `graycode/internal/context/rules.go:20`, `:46` | -| Trust — static scan | `malware_check.go` (blocked/suspicious regex) | `graycode/internal/plugin/malware_check.go:19` | -| Trust — unicode/homoglyph | `audit.go` (`AuditFinding`, severities) | `graycode/internal/plugin/audit.go:12-22` | +| MCP server bundling | MCP client/loader | `hawk/internal/mcp/mcp.go`, `hawk/internal/tool/mcp_tool.go` | +| Sub-agent bundling | Persona system (YAML frontmatter MD) | `hawk/internal/multiagent/agents/` (cf. `TOP20_COMPARISON.md:62`) | +| Rule/skill discovery precedence | `DefaultRuleSources`, `RuleDiscoverer` | `hawk/internal/context/rules.go:20`, `:46` | +| Trust — static scan | `malware_check.go` (blocked/suspicious regex) | `hawk/internal/plugin/malware_check.go:19` | +| Trust — unicode/homoglyph | `audit.go` (`AuditFinding`, severities) | `hawk/internal/plugin/audit.go:12-22` | | Registry generators/validators | Python tooling | `starling/tools/{update_registry,registry_schema,validate_skill,package_skill,sync_marketplace}.py` | | Format reference | OpenAPI skill-format spec | `starling/api/openapi.yaml` (`x-skill-format`) | | IDE/agent plugin manifests | `.claude-plugin/{plugin,marketplace}.json` | `starling/.claude-plugin/` | **Net-new Go (small, additive):** -- `graycode/internal/plugin/trust.go` — signature verification (Flow A) and a `trustdb` of publisher public keys. -- `graycode/internal/plugin/extension.go` — parse `extension.yaml`, expand `provides` into the existing loaders. -- `graycode/cmd/ext_cmd.go` — `graycode ext` command tree mirroring `skills_cmd.go`. +- `hawk/internal/plugin/trust.go` — signature verification (Flow A) and a `trustdb` of publisher public keys. +- `hawk/internal/plugin/extension.go` — parse `extension.yaml`, expand `provides` into the existing loaders. +- `hawk/cmd/ext_cmd.go` — `hawk ext` command tree mirroring `skills_cmd.go`. - Extend `manifest_v2.go` with a `Signing` struct and a `Provides` map (or a thin adapter from `extension.yaml`). **Net-new outside Go:** - `registry.v2.json` generator (extend `tools/update_registry.py`). -- The gallery + docs site (new repo, e.g. `graycode-docs`). +- The gallery + docs site (new repo, e.g. `hawk-docs`). -The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + local/global/distance/priority sort at `rules.go:97-108`) is reused unchanged for *where* installed extension assets are found — installed extensions land in `.graycode/skills` / `~/.graycode/skills` (project then user), exactly the precedence `RuleDiscoverer` already honors. +The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + local/global/distance/priority sort at `rules.go:97-108`) is reused unchanged for *where* installed extension assets are found — installed extensions land in `.hawk/skills` / `~/.hawk/skills` (project then user), exactly the precedence `RuleDiscoverer` already honors. --- @@ -257,22 +257,22 @@ The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + l ### P0 — Foundation (format + index + trust core) - **M0.1** Specify and freeze the `extension.yaml` schema; publish as `x-extension-format` alongside the existing `x-skill-format` in `starling/api/openapi.yaml`. - **M0.2** `registry.v2.json` generator in `tools/update_registry.py`; emit both v1 and v2; v1 stays the default `defaultIndexURL` consumer for older binaries. -- **M0.3** `extension.go` parser + adapter into `ManifestV2`; `graycode ext install` reuses `registry.go:196` clone path; multi-asset copy (skills/commands/hooks/mcp/themes/subagents). -- **M0.4** Trust v1: detached **minisign/cosign** signatures (`signatures/.sig`), publisher pubkey bundled in graycode; `--allow-unsigned` escape hatch; wire `malware_check.go` + `audit.go` into the install path as hard gates. -- **Exit:** `graycode ext install` works for at least 5 multi-asset extensions, signature-verified, on a v2 index. +- **M0.3** `extension.go` parser + adapter into `ManifestV2`; `hawk ext install` reuses `registry.go:196` clone path; multi-asset copy (skills/commands/hooks/mcp/themes/subagents). +- **M0.4** Trust v1: detached **minisign/cosign** signatures (`signatures/.sig`), publisher pubkey bundled in hawk; `--allow-unsigned` escape hatch; wire `malware_check.go` + `audit.go` into the install path as hard gates. +- **Exit:** `hawk ext install` works for at least 5 multi-asset extensions, signature-verified, on a v2 index. ### P1 — Gallery + Docs site (the visible surface) -- **M1.1** Stand up the static site (decision in §6). Pages: `/extensions` (gallery), per-extension detail, `/docs/{graycode,graycode-router,harrier,shrike,swift}` (getting-started + API ref + architecture), `/comparison` (rendered from `TOP20_COMPARISON.md`). +- **M1.1** Stand up the static site (decision in §6). Pages: `/extensions` (gallery), per-extension detail, `/docs/{hawk,eyrie,harrier,shrike,swift}` (getting-started + API ref + architecture), `/comparison` (rendered from `TOP20_COMPARISON.md`). - **M1.2** Build-time generation of `/extensions/index.json` from `registry.v2.json`; client-side fuzzy search, category + capability-badge filters. - **M1.3** Aggregate the 5 independent repositories' docs via checkout or sparse checkout in CI (Flow C); link-rewriting + unified sidebar. -- **M1.4** `graycode ext update` (diff installed version vs index, re-verify, atomic replace) and `graycode ext verify ` (re-check signature/audit of an installed extension). -- **Exit:** graycodeskills.dev (or chosen domain) live; gallery browseable; one-line install copy works; docs cover all 5 repos with getting-started + API ref. +- **M1.4** `hawk ext update` (diff installed version vs index, re-verify, atomic replace) and `hawk ext verify ` (re-check signature/audit of an installed extension). +- **Exit:** hawkskills.dev (or chosen domain) live; gallery browseable; one-line install copy works; docs cover all 5 repos with getting-started + API ref. ### P2 — Ecosystem maturity & publisher self-service -- **M2.1** `graycode ext publish` — scaffolds `extension.yaml`, runs validators (`tools/validate_skill.py` extended), signs locally, opens a PR to `starling`. -- **M2.2** Publisher trust tiers: verified-publisher badge (key in graycode's bundled trust DB) vs community (signed but unverified) vs unsigned. +- **M2.1** `hawk ext publish` — scaffolds `extension.yaml`, runs validators (`tools/validate_skill.py` extended), signs locally, opens a PR to `starling`. +- **M2.2** Publisher trust tiers: verified-publisher badge (key in hawk's bundled trust DB) vs community (signed but unverified) vs unsigned. - **M2.3** Trending/installs analytics — privacy-preserving (aggregate counts from PR-based opt-in pings or GitHub stars only; **no per-user tracking**, see §7). -- **M2.4** Cross-repo extension kinds: graycode-router provider plug-ins, shrike compression profiles (`TOP20_COMPARISON.md:181` team profiles), swift exporters — registered through the same `extension.yaml` `provides` mechanism with new `kind`s. +- **M2.4** Cross-repo extension kinds: eyrie provider plug-ins, shrike compression profiles (`TOP20_COMPARISON.md:181` team profiles), swift exporters — registered through the same `extension.yaml` `provides` mechanism with new `kind`s. - **Exit:** External contributors can publish a verified, multi-asset extension end-to-end; gallery shows mixed-kind extensions across the ecosystem. --- @@ -297,13 +297,13 @@ The discovery-precedence engine (`rules.go:20` `DefaultRuleSources`, walk-up + l minisign is the lighter P0 choice (single keypair, no external infra). cosign/sigstore is the P2 upgrade if we want keyless, transparency-log-backed provenance (which also dovetails with the SBOM/Cosign work at `TOP20_COMPARISON.md:240`). -### New runtime dependencies for graycode +### New runtime dependencies for hawk - A minisign/Ed25519 verify path: Go stdlib `crypto/ed25519` is sufficient — **no new dependency** for verification; only key management is new. - No new dependency for install: `git` is already required (`registry.go:215`). ### Licensing implications - Every extension's `extension.yaml` **must** carry a `license` field (already required for skills — `openapi.yaml` `required_frontmatter`). The gallery surfaces it; the validator rejects missing/unknown licenses. -- The community repo is **MIT** (`starling/LICENSE`). Contributed extensions retain their own license but must be OSI-approved; a CI check (extend `tools/validate_skill.py`) flags GPL/AGPL bundled binaries that would conflict with graycode's distribution model (mirrors `TOP20_COMPARISON.md:241`). +- The community repo is **MIT** (`starling/LICENSE`). Contributed extensions retain their own license but must be OSI-approved; a CI check (extend `tools/validate_skill.py`) flags GPL/AGPL bundled binaries that would conflict with hawk's distribution model (mirrors `TOP20_COMPARISON.md:241`). - Docusaurus (MIT) and minisign (ISC) impose no copyleft obligations. --- @@ -316,24 +316,24 @@ These repos are privacy-first; the marketplace must not regress that. - **Supply-chain trust (the core new risk).** Installing an extension means importing executable assets (hooks `manifest_v2.go:32`, MCP server binaries, scripts). Mitigations, in order of enforcement at install time (Flow A): 1. **Signature verification** (`trust.go`): detached signature over the extension tree hash, verified against a bundled/known publisher key. Unsigned → blocked unless `--allow-unsigned`. 2. **Static malware scan** (`malware_check.go:19`): blocks `eval(`, pipe-to-shell, base64-to-shell, reverse shells, netcat `-e`. - 3. **Hidden-Unicode / homoglyph audit** (`audit.go`): catches prompt-injection-via-invisible-characters in `SKILL.md`/prompts (already `graycode skills audit`). + 3. **Hidden-Unicode / homoglyph audit** (`audit.go`): catches prompt-injection-via-invisible-characters in `SKILL.md`/prompts (already `hawk skills audit`). 4. **Permission disclosure:** `extension.yaml` `permissions` (mapped to `ManifestV2.Permissions`, `manifest_v2.go:18`) are shown to the user before install; `run_shell`/`network` require explicit confirmation. - **MCP server execution is the highest-risk asset.** Bundled MCP servers run as subprocesses. P0 ships them as **opt-in** (the extension installs the *definition*; the user must explicitly enable the server), and prefers `stdio` transport with no inbound network surface. - **Prompt-injection through extension content.** Skills/prompts are injected into the system prompt via the same path as rules (`rules.go`). The audit (3) plus the existing HTML-comment-stripping concern (`TOP20_COMPARISON.md:74`) apply; the validator strips/flags suspicious frontmatter. - **Offline/air-gapped install.** Because install is `git clone` + local copy with local signature verification, an org can mirror `starling` internally and point `defaultIndexURL`/`--index` at it — no call to GitHub required. - **No third-party doc analytics.** Self-hosted Docusaurus (§6) avoids shipping user reading data to a SaaS. Any "installs" metric (P2) is aggregate-only and opt-in. -- **Signing key custody.** Verified-publisher keys ship in graycode's binary; rotation requires a graycode release. P2 cosign/sigstore would move trust to a transparency log, reducing reliance on bundled keys. +- **Signing key custody.** Verified-publisher keys ship in hawk's binary; rotation requires a hawk release. P2 cosign/sigstore would move trust to a transparency log, reducing reliance on bundled keys. --- ## 8. Open Questions 1. **Signing backend:** minisign (simple, key-in-binary) for P0, or go straight to sigstore/cosign keyless (transparency log, no key custody) and align with the SBOM effort (`TOP20_COMPARISON.md:240`)? Tradeoff: operational simplicity now vs. provenance rigor later. -2. **One repo or two?** Keep gallery + docs in `starling`, or split docs into a new `graycode-docs` repo with the 5 repos as submodules? (Submodules complicate contributor flow but isolate doc build from skill content.) -3. **Cross-repo extension kinds (P2):** do graycode-router/shrike/swift assets (provider plugins, compression profiles, exporters) belong in the *same* `starling` registry, or a per-repo registry federated into one gallery index? -4. **Versioning & compat matrix:** `compat.minGraycodeVersion` exists (`manifest_v2.go:20`), but do we also need per-asset compat (e.g., an MCP server needing a specific transport graycode supports)? Tie-in to the tri-modal MCP transport gap (`TOP20_COMPARISON.md:83`). -5. **Install integrity for non-skill assets:** today install only copies `SKILL.md` trees (`registry.go` discovers `SKILL.md`); multi-asset copy (commands/hooks/mcp/themes/subagents) needs a defined on-disk layout under `~/.graycode/`. What are the canonical install dirs for each asset kind? -6. **Domain & hosting:** confirm `graycodeskills.dev` (referenced `TOP20_COMPARISON.md:65`) ownership and Pages target (GitHub vs Cloudflare). +2. **One repo or two?** Keep gallery + docs in `starling`, or split docs into a new `hawk-docs` repo with the 5 repos as submodules? (Submodules complicate contributor flow but isolate doc build from skill content.) +3. **Cross-repo extension kinds (P2):** do eyrie/shrike/swift assets (provider plugins, compression profiles, exporters) belong in the *same* `starling` registry, or a per-repo registry federated into one gallery index? +4. **Versioning & compat matrix:** `compat.minHawkVersion` exists (`manifest_v2.go:20`), but do we also need per-asset compat (e.g., an MCP server needing a specific transport hawk supports)? Tie-in to the tri-modal MCP transport gap (`TOP20_COMPARISON.md:83`). +5. **Install integrity for non-skill assets:** today install only copies `SKILL.md` trees (`registry.go` discovers `SKILL.md`); multi-asset copy (commands/hooks/mcp/themes/subagents) needs a defined on-disk layout under `~/.hawk/`. What are the canonical install dirs for each asset kind? +6. **Domain & hosting:** confirm `hawkskills.dev` (referenced `TOP20_COMPARISON.md:65`) ownership and Pages target (GitHub vs Cloudflare). 7. **Docs source of truth:** auto-aggregate from each repo's `README.md`/`ARCHITECTURE.md` (drift-free but messy formatting) vs. hand-curated landing pages that link into per-repo docs (cleaner but duplicative)? --- @@ -342,14 +342,14 @@ These repos are privacy-first; the marketplace must not regress that. | Workstream | Scope | Est. | |---|---|---| -| **P0 — format + index v2** | `extension.yaml` spec, v2 generator in `tools/`, `extension.go` parser + `ManifestV2` adapter, multi-asset install layout, `graycode ext` command tree | **4–5 ew** | +| **P0 — format + index v2** | `extension.yaml` spec, v2 generator in `tools/`, `extension.go` parser + `ManifestV2` adapter, multi-asset install layout, `hawk ext` command tree | **4–5 ew** | | **P0 — trust core** | `trust.go` (Ed25519/minisign verify), publisher key bundling, wire `malware_check`/`audit` as install gates, permission-disclosure UX | **3–4 ew** | | **P1 — gallery** | Docusaurus setup, `/extensions` React route, build-time `index.json`, search + filters + detail pages | **4–5 ew** | | **P1 — unified docs** | Aggregate 5 independent repositories (checkout/sparse-checkout CI), sidebars + link rewrite, getting-started + API-ref scaffolding per repo, comparison tables from `TOP20_COMPARISON.md` | **5–7 ew** | -| **P1 — CLI update/verify** | `graycode ext update`, `graycode ext verify`, atomic replace | **2 ew** | -| **P2 — publisher self-service** | `graycode ext publish` scaffolder + validators, PR automation | **3 ew** | +| **P1 — CLI update/verify** | `hawk ext update`, `hawk ext verify`, atomic replace | **2 ew** | +| **P2 — publisher self-service** | `hawk ext publish` scaffolder + validators, PR automation | **3 ew** | | **P2 — trust tiers + sigstore upgrade** | verified-publisher badges, optional cosign/sigstore keyless + transparency log | **3–4 ew** | -| **P2 — cross-repo kinds** | graycode-router/shrike/swift extension kinds + federated index | **3–4 ew** | +| **P2 — cross-repo kinds** | eyrie/shrike/swift extension kinds + federated index | **3–4 ew** | | **Cross-cutting** | docs writing (content, not framework), security review, CI, design iteration | **4–6 ew** | | **Total** | | **≈ 31–44 eng-weeks** (~7.5–11 eng-months; 2 engineers ~4–5 calendar months) | @@ -360,13 +360,13 @@ The estimate is bounded on the low side because the hardest plumbing — the reg ### Appendix: grounding index (real files cited) - `starling/registry.json`, `registry.json` head (schema), `tools/*.py`, `api/openapi.yaml`, `.claude-plugin/{plugin,marketplace}.json`, `categories/testing/ab-test-setup/SKILL.md`, `LICENSE` -- `graycode/internal/plugin/registry.go:18,21,36,60,196,201,215,289,309` -- `graycode/internal/plugin/manifest_v2.go:11,18,20,32,42` -- `graycode/internal/plugin/skill_loader.go:51` -- `graycode/internal/plugin/malware_check.go:19` -- `graycode/internal/plugin/audit.go:12` -- `graycode/internal/tool/skill.go:62,91` -- `graycode/internal/context/rules.go:20,46,97` -- `graycode/cmd/skills_cmd.go:16-211` -- `graycode/internal/mcp/mcp.go`, `graycode/internal/tool/mcp_tool.go` +- `hawk/internal/plugin/registry.go:18,21,36,60,196,201,215,289,309` +- `hawk/internal/plugin/manifest_v2.go:11,18,20,32,42` +- `hawk/internal/plugin/skill_loader.go:51` +- `hawk/internal/plugin/malware_check.go:19` +- `hawk/internal/plugin/audit.go:12` +- `hawk/internal/tool/skill.go:62,91` +- `hawk/internal/context/rules.go:20,46,97` +- `hawk/cmd/skills_cmd.go:16-211` +- `hawk/internal/mcp/mcp.go`, `hawk/internal/tool/mcp_tool.go` - `TOP20_COMPARISON.md:65,74,83,181,227,232,240,241` diff --git a/docs/design/GRAYCODE-CLOUD-SAAS.md b/docs/design/HAWK-CLOUD-SAAS.md similarity index 89% rename from docs/design/GRAYCODE-CLOUD-SAAS.md rename to docs/design/HAWK-CLOUD-SAAS.md index c7c435ae8..3ad9d42b0 100644 --- a/docs/design/GRAYCODE-CLOUD-SAAS.md +++ b/docs/design/HAWK-CLOUD-SAAS.md @@ -1,4 +1,4 @@ -# Graycode Cloud — Hosted Execution Plane (Design Doc) +# Hawk Cloud — Hosted Execution Plane (Design Doc) **Status:** Draft / Proposal **Author:** Platform team @@ -9,17 +9,17 @@ ## 1. Overview & Competitive Context -Graycode today is a terminal-native, single-user, privacy-first Go binary. The daemon -(`graycode/internal/daemon/daemon.go`) exposes a small HTTP API — `POST /v1/chat` +Hawk today is a terminal-native, single-user, privacy-first Go binary. The daemon +(`hawk/internal/daemon/daemon.go`) exposes a small HTTP API — `POST /v1/chat` with optional SSE streaming, plus session CRUD — bound to loopback (`netutil.LoopbackHost`, port `4590` by default) and protected by a single optional shared API key (`Server.apiKey`, compared in `daemon.go:185-205`). There is no notion of a user, an org, a tenant, a credit balance, or remote isolated execution. Sandboxed execution is **local Docker** only -(`graycode/internal/sandbox/container.go`). +(`hawk/internal/sandbox/container.go`). -This doc designs **Graycode Cloud**: a hosted, multi-tenant execution plane that runs -the graycode agent as a managed service, authenticated by OAuth and API keys, metered +This doc designs **Hawk Cloud**: a hosted, multi-tenant execution plane that runs +the hawk agent as a managed service, authenticated by OAuth and API keys, metered and billed by credits, organized into team workspaces with org RBAC, and backed by cloud sandboxed execution (E2B-/Daytona-style microVMs) instead of the user's local Docker. It also covers the two surfaces that depend on this plane: a @@ -28,7 +28,7 @@ browser/web chat-editing UI served from the daemon, and IDE extensions ### Which Top-20 repos ship this -From `TOP20_COMPARISON.md`, the graycode P0 table (lines 27-37) and P2 table +From `TOP20_COMPARISON.md`, the hawk P0 table (lines 27-37) and P2 table (lines 70-86) name these directly: | Capability | Top-20 repos that ship it | Comparison ref | @@ -43,11 +43,11 @@ From `TOP20_COMPARISON.md`, the graycode P0 table (lines 27-37) and P2 table The comparison doc is blunt that this is a **fundamental product-tier gap** (`TOP20_COMPARISON.md:33`) — the single largest item in the report — and that org RBAC "requires a multi-tenant SaaS backend ... beyond the current single-user -token store at `/graycode/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). +token store at `/hawk/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). -The graycode-router side of the house has the parallel gaps: a LiteLLM-compatible proxy +The eyrie side of the house has the parallel gaps: a LiteLLM-compatible proxy endpoint and "multi-tenant team/project management with SSO/RBAC" with per-key -budgets (`TOP20_COMPARISON.md:95-96`). Graycode Cloud and graycode-router multi-tenancy share +budgets (`TOP20_COMPARISON.md:95-96`). Hawk Cloud and eyrie multi-tenancy share the same identity, billing, and RBAC substrate; this doc designs that substrate once and shows how both consume it. @@ -68,28 +68,28 @@ once and shows how both consume it. `sync.Map` (`daemon.go:35`). Net-new: a routing layer that maps tenant → session → execution worker. 4. **Billing / credit system.** Meter token spend and sandbox-minutes per tenant, - price normalized usage with Graycode Cloud's versioned server catalog, enforce + price normalized usage with Hawk Cloud's versioned server catalog, enforce hard limits, and bill from the authoritative cloud ledger. 5. **Team workspaces & conversation sharing.** Shared, permissioned session state; read-only and continue-able shares. 6. **Org RBAC** with Member / Admin / Owner tiers, invitations, and org-scoped credit pools and provider config. 7. **Cloud sandboxed execution.** Replace local Docker with on-demand isolated - microVMs (E2B/Daytona/Firecracker) behind graycode's existing sandbox interface. + microVMs (E2B/Daytona/Firecracker) behind hawk's existing sandbox interface. 8. **Browser/web UI** for chat-driven editing, served from the plane, consuming the existing SSE stream. 9. **IDE extensions** (VS Code, JetBrains) over ACP with IDE-native diff review. ### Non-Goals -- **Not** replacing the local/offline single-binary mode. Local graycode remains +- **Not** replacing the local/offline single-binary mode. Local hawk remains fully functional with zero cloud dependency; cloud is strictly additive. The privacy-first posture (`TOP20_COMPARISON.md:20`) is preserved — cloud is opt-in. - **Not** building our own microVM hypervisor. We integrate E2B/Daytona (build-vs-buy in §6), not write Firecracker orchestration from scratch. -- **Not** an LLM gateway rewrite. GraycodeRouter remains the provider engine behind - Graycode's `graycode-router/engine` integration; the plane calls a Graycode agent worker, which - composes GraycodeRouter. The plane does not re-implement routing, caching, or provider +- **Not** an LLM gateway rewrite. Eyrie remains the provider engine behind + Hawk's `eyrie/engine` integration; the plane calls a Hawk agent worker, which + composes Eyrie. The plane does not re-implement routing, caching, or provider adapters. - **Not** SSO/SAML in P0 (deferred to P2; OIDC social login + API keys first). - **Not** on-prem/self-hosted enterprise distribution in P0 (P2). @@ -103,7 +103,7 @@ once and shows how both consume it. ``` ┌────────────────────────────────────────┐ - Browser UI ──┐ │ Graycode Cloud │ + Browser UI ──┐ │ Hawk Cloud │ IDE (ACP) ──┼──TLS──▶│ │ CLI (token)──┘ │ ┌──────────────┐ ┌────────────────┐ │ │ │ Edge / API │ │ Identity svc │ │ @@ -121,7 +121,7 @@ once and shows how both consume it. │ │ │ │ ▼ │ │ ┌──────────────┐ ┌─────────────┐ │ - │ │ Agent Worker │─────▶│ graycode-router │ │ + │ │ Agent Worker │─────▶│ eyrie │ │ │ │ (engine. │ │ (model │ │ │ │ Session) │ │ runtime) │ │ │ └──────┬───────┘ └─────────────┘ │ @@ -144,8 +144,8 @@ middleware (`daemon.go:185`) and `routes()` table (`daemon.go:174-183`). Net-new. Wraps/extends `internal/auth` (see §4). **Billing service** — credit ledger, metering ingestion, server-side pricing, -hard-limit enforcement, and invoicing. Graycode Cloud owns this authority; GraycodeRouter -supplies normalized model and token usage through Graycode's Engine boundary. +hard-limit enforcement, and invoicing. Hawk Cloud owns this authority; Eyrie +supplies normalized model and token usage through Hawk's Engine boundary. **Workspace service** — workspace membership, shared session state, share links, permission checks for view/continue. @@ -156,7 +156,7 @@ of the daemon's `sessions sync.Map` (`daemon.go:35`). **Agent Worker** — a process (or pod) running `engine.NewSessionWithClient(...)` (`internal/engine/session.go:137`) and driving `Session.Stream(ctx)` -(`internal/engine/stream.go:20`). This is the existing graycode agent loop, unchanged, +(`internal/engine/stream.go:20`). This is the existing hawk agent loop, unchanged, running server-side. One worker handles one active session at a time (worktree-per- session isolation), matching the existing single-user model — we scale by running many workers, not by making one worker multi-tenant. @@ -196,11 +196,11 @@ credit_ledger(id, org_id, ts, delta_credits, reason, ref_session_id, ref_meter_i meter_events(id, org_id, workspace_id, session_id, ts, kind, tokens_in, tokens_out, model, sandbox_ms, cost_usd, credits, pricing_catalog_version) - -- kind ∈ {llm, sandbox}; cost_usd is priced by Graycode Cloud + -- kind ∈ {llm, sandbox}; cost_usd is priced by Hawk Cloud ``` `meter_events.cost_usd` is computed from normalized model/token dimensions by -Graycode Cloud's versioned pricing catalog. Client-side estimates may be displayed +Hawk Cloud's versioned pricing catalog. Client-side estimates may be displayed or audited, but never affect credits or invoices. ### 3.3 API Surface @@ -256,7 +256,7 @@ Edge: check credit balance(org) > floor [Billing svc] ← rejec Edge → Router: route(org, ws, user, session_id?) Router → Worker: acquire/lease Agent Worker (+ Sandbox) Worker: engine.Session.Stream(ctx) [stream.go:20] -Worker → graycode-router: Chat/StreamChat with ctx carrying virtual key +Worker → eyrie: Chat/StreamChat with ctx carrying virtual key (WithVirtualKey, budget_provider.go:23) Worker → Edge: SSE content events (daemon.go:321-337, reused verbatim) Edge → Client: SSE relay @@ -267,9 +267,9 @@ Billing: append credit_ledger debit; if balance ≤ 0 → signal Worker **B. OAuth login (browser UI / IDE)** PKCE authorization-code grant for the web UI; **device grant for the CLI/IDE** — -graycode already implements RFC 8628 device flow end-to-end +hawk already implements RFC 8628 device flow end-to-end (`internal/auth/device_flow.go`: `RequestCode`, `PollForToken`, -`exchangeCode`). Graycode Cloud stands up the *server* side of these grants; the +`exchangeCode`). Hawk Cloud stands up the *server* side of these grants; the client side is largely present. **C. Share a conversation** @@ -303,7 +303,7 @@ This is the crux: **what is reusable today vs. net-new.** ### Reusable today (high leverage) -| Existing asset | File | How Graycode Cloud reuses it | +| Existing asset | File | How Hawk Cloud reuses it | |---|---|---| | Engine session + agent loop | `internal/engine/session.go:42,132,137`, `internal/engine/stream.go:20` | The Agent Worker *is* `engine.Session.Stream`. No change to the agent loop — it already runs headless with a `SessionFactory` (`daemon.go:27`). | | HTTP daemon + routes + SSE | `internal/daemon/daemon.go:174-183, 315-337` | The Edge/API gateway is the daemon's `routes()`/`handleChat` generalized. SSE framing (per-line `data:` escaping, `daemon.go:326-329`) is correct and reused verbatim. | @@ -311,10 +311,10 @@ This is the crux: **what is reusable today vs. net-new.** | Autonomy / permission gating | `daemon.go:286-298` (`PresetConfig`, `NeedsPermission`) | Server-side auto-approval policy already exists for non-interactive runs; cloud reuses it per-workspace policy. | | Auth primitives | `internal/auth/auth.go` (`TokenStore`, `SecureStorage`), `device_flow.go` (full RFC 8628) | Device-grant **client** is done. `SecureStorage` (macOS keychain + file fallback, `auth.go:54-121`) stays for local credential caching of cloud tokens. `GenerateNonce` (`auth.go:124`) for OAuth state/PKCE. | | Constant-time key compare | `daemon.go:207-224` | API-key verification logic carries over (but keys move to hashed storage; see net-new). | -| GraycodeRouter normalized usage | `graycode-router/engine` response and stream usage DTOs through Graycode's adapter | Reuse model/token dimensions as metering input. Do not import lower GraycodeRouter packages or trust client-computed prices for billing. | +| Eyrie normalized usage | `eyrie/engine` response and stream usage DTOs through Hawk's adapter | Reuse model/token dimensions as metering input. Do not import lower Eyrie packages or trust client-computed prices for billing. | | Sandbox executor interface | `internal/sandbox/container.go:18-21` (`containerExecutor`: `Exec`, `Running`) | `CloudSandbox` implements the same interface → drop-in. Callers don't know if they're on local Docker or a cloud microVM. | | Sandbox lifecycle manager | `internal/sandbox/snapshot_sandbox.go:52-228` (`Create/Pause/Resume/Snapshot/Restore/List/Cleanup`) | Existing pause/resume/snapshot semantics map cleanly onto E2B/Daytona pause+snapshot APIs; the manager abstraction guides the `CloudSandbox` API shape. | -| Messaging gateways | `internal/daemon/gateway.go`, `telegram.go`, `discord.go`, `slack.go` | Already forward to `/v1/chat` via `forwardToGraycode` (`gateway.go:17`) with bearer auth. In cloud they forward to the tenant-scoped chat endpoint with the org's key — minimal change. | +| Messaging gateways | `internal/daemon/gateway.go`, `telegram.go`, `discord.go`, `slack.go` | Already forward to `/v1/chat` via `forwardToHawk` (`gateway.go:17`) with bearer auth. In cloud they forward to the tenant-scoped chat endpoint with the org's key — minimal change. | | Cron engine | `internal/system/cron/cron.go` | Foundation for cloud scheduled runs (Routines), per `TOP20_COMPARISON.md:48`. Out of scope here but shares the worker plane. | ### Net-new (must build) @@ -339,7 +339,7 @@ This is the crux: **what is reusable today vs. net-new.** in-process engine. Net-new control loop. 6. **`CloudSandbox`** — E2B/Daytona-backed implementation of `containerExecutor`. The interface and call sites exist; the cloud backend does not. -7. **Billing/credit ledger + invoicing + Stripe integration** — graycode-router gives +7. **Billing/credit ledger + invoicing + Stripe integration** — eyrie gives enforcement and cost math; the ledger, top-ups, plans, and payment processor are net-new. 8. **Workspace sharing** — `session_shares` and the share/continue flows. @@ -362,11 +362,11 @@ This is the crux: **what is reusable today vs. net-new.** - **M0.5** Single-region Agent Worker pool (1 session/worker, worktree isolation). - **M0.6** `CloudSandbox` (E2B *or* Daytona — pick one) implementing `containerExecutor` (`container.go:18`). - **M0.7** Metering: ingest normalized model/token dimensions, price them with - the versioned Graycode Cloud catalog, persist the Postgres credit ledger, and + the versioned Hawk Cloud catalog, persist the Postgres credit ledger, and return HTTP 402 at the hard credit floor. - **M0.8** Minimal web UI (single `go:embed` page) for chat + diff view. -**Exit criteria:** a paying single team can log in via OAuth, run graycode against a +**Exit criteria:** a paying single team can log in via OAuth, run hawk against a cloud sandbox, see streamed output and diffs in the browser, and get cut off when credits hit zero. @@ -385,7 +385,7 @@ credits hit zero. - **M2.1** SSO/SAML + SCIM provisioning. - **M2.2** Multi-region worker pools + autoscaling; second sandbox provider for redundancy. - **M2.3** JetBrains extension (ACP). -- **M2.4** Org-scoped provider config (BYO keys / BYO model endpoints), aligning with graycode-router multi-tenant proxy (`TOP20_COMPARISON.md:96`). +- **M2.4** Org-scoped provider config (BYO keys / BYO model endpoints), aligning with eyrie multi-tenant proxy (`TOP20_COMPARISON.md:96`). - **M2.5** Audit logging, IT-managed policy tiers, self-hosted/on-prem distribution. - **M2.6** Cloud Routines (scheduled/triggered runs) on the worker plane via `internal/system/cron/cron.go` (`TOP20_COMPARISON.md:48`). @@ -397,9 +397,9 @@ credits hit zero. |---|---|---| | MicroVM sandboxing | **Buy/integrate** E2B (P0) + Daytona (P2 redundancy) | Building Firecracker orchestration is a multi-quarter effort orthogonal to product value. E2B SDK is open source (Apache-2.0/MIT family); Daytona is Apache-2.0. Both have hosted offerings; we can also self-host E2B later. The `containerExecutor` seam (`container.go:18`) keeps us provider-agnostic. | | Identity / OAuth server | **Buy** (Ory Hydra/Kratos, Auth0, or WorkOS) for P0; revisit P2 | Writing a compliant OAuth2/OIDC AS is risky. Ory is Apache-2.0 (self-hostable, privacy-aligned). WorkOS/Auth0 are SaaS — faster but introduce a third party in the auth path (weigh against privacy posture). Prefer **Ory self-hosted** to keep the privacy-first promise. | -| Billing / payments | **Buy** Stripe for payment + invoicing; **build** the credit ledger | Never build a card processor. The ledger and metering are ours (and partly exist in graycode-router). Stripe SDK is permissive. | +| Billing / payments | **Buy** Stripe for payment + invoicing; **build** the credit ledger | Never build a card processor. The ledger and metering are ours (and partly exist in eyrie). Stripe SDK is permissive. | | Database | **Buy** managed Postgres | Standard. | -| Metering enforcement | **Build in Graycode Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower GraycodeRouter packages remain engine implementation details. | +| Metering enforcement | **Build in Hawk Cloud** on normalized Engine usage | Billing authority must be server-owned and versioned; lower Eyrie packages remain engine implementation details. | | Web UI framework | **Build** small React SPA, `go:embed` served (Aider/OpenHands pattern) | Keeps deployment a single binary; matches `TOP20_COMPARISON.md:31`. | | IDE protocol | **Adopt** ACP (Agent Client Protocol) | Industry trajectory (Gemini CLI, Zed, JetBrains), `TOP20_COMPARISON.md:32,36`. Avoid bespoke per-IDE protocols. | @@ -415,12 +415,12 @@ service, so AGPL deps would create source-disclosure obligations — avoid them. These repos are privacy-first (`TOP20_COMPARISON.md:20`); the cloud plane must not erode that. -1. **Cloud is opt-in and isolated from local mode.** Local graycode never phones home; +1. **Cloud is opt-in and isolated from local mode.** Local hawk never phones home; nothing in `internal/engine` or `internal/sandbox` gains a cloud dependency. The plane is a separate deployable. 2. **Tenant isolation by construction.** One session ↔ one Agent Worker ↔ one microVM; no shared filesystem or process between tenants. Worktree-per-session - (already graycode's isolation model) carries over. Sandboxes default to + (already hawk's isolation model) carries over. Sandboxes default to `--network none` semantics like `ContainerSandbox` (`container.go:81`) unless a workspace explicitly grants egress, gated by the existing net-proxy allowlisting (`internal/sandbox/netproxy.go`). @@ -430,7 +430,7 @@ erode that. 4. **Per-line SSE escaping is a real injection defense** already present (`daemon.go:324-329`) and must be preserved in the relay path — LLM output can contain newlines that would otherwise forge SSE events. -5. **Credit enforcement is a DoS control too.** Hard floors (HTTP 402) via graycode-router +5. **Credit enforcement is a DoS control too.** Hard floors (HTTP 402) via eyrie `CheckBudget` (`budget_provider.go:181`) bound runaway agent spend per tenant. 6. **Code privacy.** Customer code lives only in the ephemeral microVM and the session store; offer per-org data-retention controls and a "no-retention" mode @@ -454,13 +454,13 @@ erode that. single-user — `daemon.go` keys one `apiKey`, sessions share a process). Leaning process/pod-per-session for isolation; needs a cost model. 2. **Sandbox provider:** E2B vs. Daytona for P0. E2B = fastest microVM cold start; - Daytona = richer dev-environment model. Which maps better onto graycode's + Daytona = richer dev-environment model. Which maps better onto hawk's pause/resume/snapshot (`snapshot_sandbox.go`)? 3. **Credit unit:** bill in $-equivalent credits (1 credit = $0.01?) covering both server-priced LLM usage and sandbox-minutes — what's the blended margin? 4. **BYO keys vs. managed keys:** if an org brings its own provider keys, do their LLM tokens still consume credits (we only charge sandbox/orchestration), or run - free? Affects graycode-router provider-config plumbing (`TOP20_COMPARISON.md:96`). + free? Affects eyrie provider-config plumbing (`TOP20_COMPARISON.md:96`). 5. **Identity build-vs-buy:** Ory self-hosted (privacy, ops burden) vs. WorkOS/Auth0 (speed, third party in auth path). Privacy posture argues for Ory. 6. **Session store backend:** Postgres for everything vs. Postgres (metadata) + @@ -497,7 +497,7 @@ Assumes a small senior team (3-4 engineers). Estimates are for engineering only with P0 a hard ~8-10 calendar weeks for a usable single-team beta.** Leverage note: the agent loop, SSE, sandbox interface, device-flow client, and -graycode-router budget machinery are **already built** — they collapse what would otherwise +eyrie budget machinery are **already built** — they collapse what would otherwise be the most expensive parts (the agent runtime and the metering enforcement engine) into integration work. The genuinely greenfield cost is identity/multi-tenancy, worker orchestration, and the cloud sandbox backend. diff --git a/docs/design/three-repo-architecture.md b/docs/design/three-repo-architecture.md index 48272776b..484ecbd48 100644 --- a/docs/design/three-repo-architecture.md +++ b/docs/design/three-repo-architecture.md @@ -8,12 +8,12 @@ proposal. See also `gateway-architecture.md` (human-readable version), ```mermaid flowchart LR - subgraph CLI["graycode-cli"] + subgraph CLI["hawk"] TUI["chat · exec · review\nagent loop · sessions"] - ENG["graycode-router engine\n(compiled in)"] + ENG["eyrie engine\n(compiled in)"] end - subgraph ROUTER["graycode-router"] + subgraph ROUTER["eyrie"] LIB["engine library\n(router, adapters)"] SRV["HTTP server (skeleton)\ninternal/api + openapi.yaml"] end @@ -32,18 +32,18 @@ Wires, with evidence: | From → To | Mechanism | Evidence | |---|---|---| -| CLI → Router | Go module dep, in-process construction | `go.mod` requires `github.com/GrayCodeAI/graycode-router`; `internal/provider/gateway/gateway.go` is the sole importer | +| CLI → Router | Go module dep, in-process construction | `go.mod` requires `github.com/GrayCodeAI/eyrie`; `internal/provider/gateway/gateway.go` is the sole importer | | CLI → Platform | Opt-in HTTPS (login, usage, graph sync) | `internal/platform/cloud/client.go`; `Enabled()` requires endpoint + token; no default URL | -| Router → CLI | none | router `go.mod`/`go.sum` contain zero graycode-cli refs | +| Router → CLI | none | router `go.mod`/`go.sum` contain zero hawk refs | | Router ↔ Platform | none | no imports, package deps, or API calls either direction | ## Target state (proposal) ```mermaid flowchart LR - CLI2["graycode-cli\n(default: in-process)"] -. "opt-in --model-gateway" .-> GW + CLI2["hawk\n(default: in-process)"] -. "opt-in --model-gateway" .-> GW WEB2["platform web/BFF"] -->|per-user keys| GW - subgraph GW["graycode-router: common service"] + subgraph GW["eyrie: common service"] API["OpenAI-compatible API\n/v1/chat/completions + SSE"] POL["routing · retry · guardrails\nspend tracking"] end diff --git a/docs/ecosystem-message-flow.md b/docs/ecosystem-message-flow.md index 0d672ec5d..72ca6dffe 100644 --- a/docs/ecosystem-message-flow.md +++ b/docs/ecosystem-message-flow.md @@ -1,11 +1,11 @@ -# Ecosystem message flow (graycode-router · harrier · shrike) +# Ecosystem message flow (eyrie · harrier · shrike) -How one user message travels through graycode and the GrayCodeAI ecosystem libraries. +How one user message travels through hawk and the GrayCodeAI ecosystem libraries. ## Overview ``` -User prompt (TUI or graycode exec) +User prompt (TUI or hawk exec) │ ▼ ┌───────────────────┐ @@ -26,7 +26,7 @@ User prompt (TUI or graycode exec) │ ▼ ┌───────────────────┐ ┌─────────────┐ -│ Graycode ChatClient │────►│ graycode-router/engine│ catalog, credentials, routing +│ Hawk ChatClient │────►│ eyrie/engine│ catalog, credentials, routing │ port + adapter │ │ generate/ │────► provider API └─────────┬─────────┘ │ stream │ └─────────────┘ @@ -42,25 +42,25 @@ User prompt (TUI or graycode exec) ▼ (when context grows) ┌───────────────────┐ │ shrike Compress │ fast path before LLM summarization -│ + graycode-router compact │ +│ + eyrie compact │ └───────────────────┘ ``` ## Step by step -### 1. Session start (`graycode` or `graycode exec`) +### 1. Session start (`hawk` or `hawk exec`) -- **graycode-router**: The Graycode composition root creates an `graycode-router/engine.Engine` with - GraycodeRouter-owned state paths, an injected secret store, and per-engine custom +- **eyrie**: The Hawk composition root creates an `eyrie/engine.Engine` with + Eyrie-owned state paths, an injected secret store, and per-engine custom gateway metadata. The engine loads provider state and the model catalog, then - builds transport behind Graycode's `ChatClient` port. -- **harrier**: `configureSession` creates `HarrierBridge` → opens `~/.harrier/data/harrier.db`. If missing, graycode runs without persistent memory. + builds transport behind Hawk's `ChatClient` port. +- **harrier**: `configureSession` creates `HarrierBridge` → opens `~/.harrier/data/harrier.db`. If missing, hawk runs without persistent memory. - **shrike**: No startup step — linked at compile time. ### 2. System prompt assembly -- Graycode templates (`internal/prompts/templates/*.md`) define behavior, tools, and practices. -- Project `AGENTS.md` is appended via `graycodeconfig.BuildContextWithDirs`. +- Hawk templates (`internal/prompts/templates/*.md`) define behavior, tools, and practices. +- Project `AGENTS.md` is appended via `hawkconfig.BuildContextWithDirs`. - **harrier**: `Memory.Recall` injects relevant graph nodes into the system prompt. ### 3. User message → agent loop (`internal/engine/stream.go`) @@ -68,8 +68,8 @@ User prompt (TUI or graycode exec) Each turn: 1. **harrier** — recall memories matching the latest user message (token budget ~2000). -2. **graycode-router** — Graycode's adapter calls engine generate/stream with Graycode-owned tool - definitions; GraycodeRouter normalizes provider events and tool requests. +2. **eyrie** — Hawk's adapter calls engine generate/stream with Hawk-owned tool + definitions; Eyrie normalizes provider events and tool requests. 3. Tools run with `HarrierBridge` in context for `CoreMemory*` tools. 4. **harrier** — sleeptime consolidation, skill distillation, auto-remember after turns. @@ -78,7 +78,7 @@ Each turn: When messages exceed limits (`internal/engine/compact.go`): 1. **shrike** — `shrike.Compress()` tries a fast compression path for summaries. -2. **graycode-router** — if shrike reduction is insufficient, graycode calls the LLM to summarize, then keeps recent messages. +2. **eyrie** — if shrike reduction is insufficient, hawk calls the LLM to summarize, then keeps recent messages. ### 5. Token accounting @@ -87,24 +87,24 @@ When messages exceed limits (`internal/engine/compact.go`): ## Verify locally ```bash -graycode doctor # ecosystem panel + graycode-router preflight + harrier status -graycode harrier # merlin memory graph -./scripts/smoke-graycode.sh # build + quick tests +hawk doctor # ecosystem panel + eyrie preflight + harrier status +hawk harrier # merlin memory graph +./scripts/smoke-hawk.sh # build + quick tests ``` ## Module layout -| Module | Role in graycode | Required? | +| Module | Role in hawk | Required? | |--------|----------------|-----------| -| **graycode-router** | LLM APIs, catalog, credentials, routing | Yes | +| **eyrie** | LLM APIs, catalog, credentials, routing | Yes | | **harrier** | SQLite memory graph at `~/.harrier/data/` | No (degrades gracefully) | | **shrike** | Token estimate + context compression | Yes (embedded, no config) | -The support repositories are independent sibling checkouts: `graycode-router`, +The support repositories are independent sibling checkouts: `eyrie`, `harrier` (Harrier), and `shrike` (Shrike), with the parent `go.work` wiring the local Go workspace. -Production Graycode code imports GraycodeRouter only through `graycode-router/engine`. Conversation -history, WAL/resume, permissions, and tool execution remain in Graycode; provider +Production Hawk code imports Eyrie only through `eyrie/engine`. Conversation +history, WAL/resume, permissions, and tool execution remain in Hawk; provider credentials, discovery, selection, transport, resilience, and normalized -streaming remain in GraycodeRouter. +streaming remain in Eyrie. diff --git a/docs/ecosystem-remediation-plan.md b/docs/ecosystem-remediation-plan.md index abc9089e9..eb01e3f7b 100644 --- a/docs/ecosystem-remediation-plan.md +++ b/docs/ecosystem-remediation-plan.md @@ -4,21 +4,21 @@ This document captures the improvement recommendations from the 2026-07-11 full-ecosystem audit that require significant refactoring and are documented here for scheduled execution rather than immediate implementation. -## 1. charmbracelet v1/v2 Dependency Duplication (graycode + swift) +## 1. charmbracelet v1/v2 Dependency Duplication (hawk + swift) **Status:** ✅ Done (2026-07-11) -**Impact:** High — contributed ~20-30MB to graycode binary size +**Impact:** High — contributed ~20-30MB to hawk binary size ### Outcome -- Graycode and Swift imports migrated to `charm.land/*/v2` (`bubbles`, `bubbletea`, +- Hawk and Swift imports migrated to `charm.land/*/v2` (`bubbles`, `bubbletea`, `lipgloss`, `huh`, `glamour`). - Direct `github.com/charmbracelet/{bubbles,bubbletea,lipgloss}` requires removed - from graycode/swift `go.mod`. + from hawk/swift `go.mod`. - Binary size gate tightened: **110MB → 80MB** (`make size-check`). -- Verified build: graycode binary **~75 MB** (under gate) after migration. +- Verified build: hawk binary **~75 MB** (under gate) after migration. -Commits (graycode): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size gate). +Commits (hawk): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size gate). ### Residual notes @@ -38,13 +38,13 @@ Commits (graycode): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size (`replace github.com/GrayCodeAI/harrier => ../..`). - Core `github.com/GrayCodeAI/harrier` no longer requires `charmbracelet/{bubbles,bubbletea,lipgloss}`. -- Graycode embeds only the library packages (`engine`, `storage`, `graph`, …), so - the default graycode binary does not pull the demo TUI module. +- Hawk embeds only the library packages (`engine`, `storage`, `graph`, …), so + the default hawk binary does not pull the demo TUI module. - Verify: `cd harrier && go test ./...`; `cd harrier/cmd/harrier-tui && go test ./...`. ### Residual notes -- The local `harrier` (Harrier) checkout and Graycode's `go.mod` must resolve to a +- The local `harrier` (Harrier) checkout and Hawk's `go.mod` must resolve to a published matching commit. **Publish Harrier before** relying on `GOWORK=off` / module-release parity (the Go proxy must see the commit for sumdb download). @@ -98,13 +98,13 @@ large diffs and bloats the repo. `.gitignore`. 2. **Generate at build/publish time:** Run `python tools/update_registry.py` in CI before publishing artifacts. -3. **Runtime fetch:** Have `graycode` fetch the registry from a CDN or GitHub +3. **Runtime fetch:** Have `hawk` fetch the registry from a CDN or GitHub raw URL rather than embedding it. ### Risk -If `registry.json` is read offline by graycode, removing it requires adding a -fetch-or-cache mechanism. Verify how graycode consumes the registry before +If `registry.json` is read offline by hawk, removing it requires adding a +fetch-or-cache mechanism. Verify how hawk consumes the registry before removing from git. --- diff --git a/docs/intelligent-cli.md b/docs/intelligent-cli.md index f578c6ca0..721dda263 100644 --- a/docs/intelligent-cli.md +++ b/docs/intelligent-cli.md @@ -1,6 +1,6 @@ # Intelligent CLI capabilities -Graycode keeps the startup tool schema small while making the full capability +Hawk keeps the startup tool schema small while making the full capability surface discoverable on demand. The registry currently contains core tools, lazy tools, and MCP tools; intent routing promotes only the tools relevant to the current request. @@ -37,8 +37,8 @@ Dependency and GitHub operations are network-gated and read-only by default. ## Inspecting the registry ```bash -graycode tools -graycode tools --json +hawk tools +hawk tools --json ``` The JSON form includes risk level, read-only status, aliases, and intent diff --git a/docs/mcp-servers.md b/docs/mcp-servers.md index 828757d40..8043a3004 100644 --- a/docs/mcp-servers.md +++ b/docs/mcp-servers.md @@ -1,10 +1,10 @@ # MCP Server Configuration -graycode supports connecting to external MCP (Model Context Protocol) servers to extend its capabilities with additional tools, resources, and prompts. +hawk supports connecting to external MCP (Model Context Protocol) servers to extend its capabilities with additional tools, resources, and prompts. ## Configuration -MCP servers are configured in `settings.json` (global: `~/.graycode/settings.json`, project: `.graycode/settings.json`). +MCP servers are configured in `settings.json` (global: `~/.hawk/settings.json`, project: `.hawk/settings.json`). ```json { @@ -42,7 +42,7 @@ My-Jogyo provides 12 MCP tools for scientific research workflows, including Pyth # Install My-Jogyo npm install -g my-jogyo -# Add to graycode settings +# Add to hawk settings ``` **settings.json:** @@ -76,7 +76,7 @@ npm install -g my-jogyo ### harrier (Memory Engine) -graycode's built-in memory engine. Configured automatically when harrier is installed. +hawk's built-in memory engine. Configured automatically when harrier is installed. ```json { @@ -93,7 +93,7 @@ graycode's built-in memory engine. Configured automatically when harrier is inst ### kestrel (Code Review) -graycode's built-in code review engine. +hawk's built-in code review engine. ```json { @@ -110,7 +110,7 @@ graycode's built-in code review engine. ### merlin (Security Audit) -graycode's built-in security scanner. +hawk's built-in security scanner. ```json { @@ -129,16 +129,16 @@ graycode's built-in security scanner. ```bash # Add an MCP server -graycode mcp add [args...] +hawk mcp add [args...] # List configured servers -graycode mcp list +hawk mcp list # Remove a server -graycode mcp remove +hawk mcp remove # Test a server connection -graycode mcp test +hawk mcp test ``` ## Troubleshooting @@ -149,8 +149,8 @@ graycode mcp test - Run the command manually to check for errors **Tools not appearing:** -- Restart graycode after adding a new server -- Check `graycode mcp test ` for connection errors +- Restart hawk after adding a new server +- Check `hawk mcp test ` for connection errors - Verify the server's tools/list response is valid JSON-RPC **Timeout errors:** diff --git a/docs/monitoring-guide.md b/docs/monitoring-guide.md index 5ece59f17..354166062 100644 --- a/docs/monitoring-guide.md +++ b/docs/monitoring-guide.md @@ -1,6 +1,6 @@ # Monitoring Guide -This guide covers how to monitor graycode's daemon and CLI for production health, +This guide covers how to monitor hawk's daemon and CLI for production health, performance, and security. ## 1. Daemon Health & Readiness @@ -18,7 +18,7 @@ curl -sf http://localhost:4590/v1/health ### `GET /v1/ready` — Readiness probe Returns 200 when the daemon is fully ready to serve traffic (session factory -configured, GraycodeRouter preflight checks pass). Returns 503 with the failed +configured, Eyrie preflight checks pass). Returns 503 with the failed dependency during startup or when dependencies are unavailable. ```bash @@ -31,7 +31,7 @@ Returns aggregated usage statistics (sessions, messages, tool calls, cost) for the last N days (default 30, `?days=30`). ```bash -curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ +curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" \ http://localhost:4590/v1/stats ``` @@ -43,7 +43,7 @@ The daemon exposes metrics in Prometheus text exposition format at `GET /v1/metrics`. This endpoint requires authentication. ```bash -curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ +curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" \ http://localhost:4590/v1/metrics ``` @@ -51,9 +51,9 @@ Available metrics: | Metric | Type | Description | |--------|------|-------------| -| `graycode_daemon_active_sessions` | gauge | Number of active daemon sessions | -| `graycode_daemon_chat_concurrency_used` | gauge | Number of in-use chat concurrency slots | -| `graycode_daemon_uptime_seconds` | gauge | Daemon uptime in seconds | +| `hawk_daemon_active_sessions` | gauge | Number of active daemon sessions | +| `hawk_daemon_chat_concurrency_used` | gauge | Number of in-use chat concurrency slots | +| `hawk_daemon_uptime_seconds` | gauge | Daemon uptime in seconds | | `http_requests_total` | counter | Total HTTP requests received | | `http_request_duration_ms` | histogram | HTTP request duration in milliseconds | | `http_rate_limited_total` | counter | Number of requests rejected by rate limiter | @@ -70,9 +70,9 @@ text format. Telemetry is **opt-in**. Enable it by setting: ```bash -export GRAYCODE_ENABLE_TELEMETRY=1 +export HAWK_ENABLE_TELEMETRY=1 export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -graycode daemon start +hawk daemon start ``` The daemon will automatically: @@ -80,31 +80,31 @@ The daemon will automatically: - Initialize the OTel SDK with a batch span processor (5s batch interval). - Export traces to the OTLP endpoint (`OTEL_EXPORTER_OTLP_ENDPOINT`). - Send swift headers from `OTEL_EXPORTER_OTLP_HEADERS`. -- Set the service name (default: `graycode`) and version. +- Set the service name (default: `hawk`) and version. ### Configuration | Environment Variable | Default | Description | |---------------------|---------|-------------| -| `GRAYCODE_ENABLE_TELEMETRY` | `0` | Set to `1` to enable OTP telemetry | +| `HAWK_ENABLE_TELEMETRY` | `0` | Set to `1` to enable OTP telemetry | | `OTEL_EXPORTER_OTLP_ENDPOINT` | _(empty)_ | OTLP collector endpoint | | `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` | OTLP transport protocol | | `OTEL_EXPORTER_OTLP_HEADERS` | _(empty)_ | Comma-separated `key=value` headers | -| `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS` | `2000` | Shutdown timeout in milliseconds | +| `HAWK_OTEL_SHUTDOWN_TIMEOUT_MS` | `2000` | Shutdown timeout in milliseconds | ### OTel Conventions -graycode follows the OpenTelemetry semantic conventions for traces. See +hawk follows the OpenTelemetry semantic conventions for traces. See [docs/OTEL-CONVENTIONS.md](OTEL-CONVENTIONS.md) for span naming and attribute details. ## 4. Structured Logging -The daemon writes structured SLOG logs to `~/.graycode/state/daemon.log` by +The daemon writes structured SLOG logs to `~/.hawk/state/daemon.log` by default. Control the log level via: ```bash -graycode daemon start --log-level DEBUG +hawk daemon start --log-level DEBUG ``` Or via environment variable: @@ -127,13 +127,13 @@ All daemon log entries include: ### Audit log Security-relevant events (auth failures, tool executions) are written to a -tamper-evident log at `~/.graycode/state/securitylog/security_events.jsonl`. +tamper-evident log at `~/.hawk/state/securitylog/security_events.jsonl`. Verify the audit log integrity: ```bash # The securitylog package provides a verify command -go run ./cmd/graycode securitylog verify +go run ./cmd/hawk securitylog verify ``` ## 5. Prometheus Scraping @@ -142,12 +142,12 @@ go run ./cmd/graycode securitylog verify ```yaml services: - graycode: - image: ghcr.io/graycodeai/graycode-daemon:latest + hawk: + image: ghcr.io/graycodeai/hawk-daemon:latest ports: - "4590:4590" environment: - - GRAYCODE_DAEMON_API_KEY=secret + - HAWK_DAEMON_API_KEY=secret labels: - "prometheus.io/scrape=true" - "prometheus.io/port=4590" @@ -160,14 +160,14 @@ services: apiVersion: v1 kind: Service metadata: - name: graycode-daemon + name: hawk-daemon annotations: prometheus.io/scrape: "true" prometheus.io/port: "4590" prometheus.io/path: "/v1/metrics" spec: selector: - app: graycode-daemon + app: hawk-daemon ports: - port: 4590 targetPort: 4590 @@ -175,18 +175,18 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: graycode-daemon + name: hawk-daemon spec: template: spec: containers: - - name: graycode - image: ghcr.io/graycodeai/graycode-daemon:latest + - name: hawk + image: ghcr.io/graycodeai/hawk-daemon:latest env: - - name: GRAYCODE_DAEMON_API_KEY + - name: HAWK_DAEMON_API_KEY valueFrom: secretKeyRef: - name: graycode-secret + name: hawk-secret key: api-key ports: - containerPort: 4590 @@ -208,26 +208,26 @@ spec: | Alert | Condition | Severity | |-------|-----------|----------| -| Daemon down | `graycode_daemon_uptime_seconds` does not increase for 2+ minutes | critical | +| Daemon down | `hawk_daemon_uptime_seconds` does not increase for 2+ minutes | critical | | High request latency | `histogram_quantile(0.95, http_request_duration_ms)` > 10000ms for 5 minutes | warning | | Rate limit saturation | `rate(http_rate_limited_total[5m])` > 10/s | warning | | Auth failures | `rate(auth_denied_total[5m])` > 5/s | critical (possible brute force) | -| High concurrency | `graycode_daemon_chat_concurrency_used` sustained at max for 5+ minutes | warning | -| No active sessions | `graycode_daemon_active_sessions` = 0 during business hours | warning | +| High concurrency | `hawk_daemon_chat_concurrency_used` sustained at max for 5+ minutes | warning | +| No active sessions | `hawk_daemon_active_sessions` = 0 during business hours | warning | ## 7. Systemd Logging When running under systemd, logs from stderr/stdout are captured by journald: ```bash -journalctl -u graycode-daemon -f +journalctl -u hawk-daemon -f ``` The daemon also writes its own structured log to -`~/.graycode/state/daemon.log`: +`~/.hawk/state/daemon.log`: ```bash -tail -f ~/.graycode/state/daemon.log +tail -f ~/.hawk/state/daemon.log ``` ## 8. Feature Flags @@ -236,7 +236,7 @@ Feature flags allow runtime configuration without restarts. They are controlled via environment variables: ```bash -export GRAYCODE_FEATURE_=1 +export HAWK_FEATURE_=1 ``` | Flag | Default | Description | @@ -251,5 +251,5 @@ export GRAYCODE_FEATURE_=1 List all registered flags: ```bash -graycode features +hawk features ``` diff --git a/docs/operations-checklist.md b/docs/operations-checklist.md index 58d3f04e6..361349284 100644 --- a/docs/operations-checklist.md +++ b/docs/operations-checklist.md @@ -1,22 +1,22 @@ # Production Operations Checklist -Use this checklist when deploying or upgrading graycode in a production +Use this checklist when deploying or upgrading hawk in a production environment. Each item links to the relevant configuration option or documentation section. ## Pre-Deployment -- [ ] **API key is set** — `GRAYCODE_DAEMON_API_KEY` environment variable is +- [ ] **API key is set** — `HAWK_DAEMON_API_KEY` environment variable is configured to a cryptographically random value (≥ 32 bytes). Do **not** rely on the auto-generated key for production. ```bash - export GRAYCODE_DAEMON_API_KEY=$(openssl rand -base64 32) + export HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) ``` - [ ] **Bind address** — Daemon binds to `0.0.0.0` (not just loopback) only when remote access is needed. A non-loopback bind requires an API key and native TLS; otherwise startup fails closed. - [ ] **TLS configured** — Enable native TLS with `--tls-cert` / `--tls-key` - flags. If a reverse proxy terminates TLS, keep Graycode bound to loopback or + flags. If a reverse proxy terminates TLS, keep Hawk bound to loopback or an internal-only interface and restrict that network path at the firewall; the daemon does not treat `X-Forwarded-Proto` as transport encryption. - [ ] **CORS configured** — If serving browser-based clients, set @@ -24,14 +24,14 @@ documentation section. trusted origins only. Use `--cors '*'` only for development. - [ ] **Rate limits reviewed** — Default: 10 req/min for general API, 30 req/min for chat, 4 concurrent chat sessions. Tune via - `GRAYCODE_DAEMON_MAX_CONCURRENT`. + `HAWK_DAEMON_MAX_CONCURRENT`. - [ ] **Resource limits set** — Configure CPU/memory limits in systemd (`MemoryMax`, `CPUQuota`) or Kubernetes. Defaults in the systemd unit file: `MemoryMax=4G`, `CPUQuota=200%`. -- [ ] **Log retention** — Daemon logs at `~/.graycode/state/daemon.log`. +- [ ] **Log retention** — Daemon logs at `~/.hawk/state/daemon.log`. Configure log rotation (logrotate, journald retention) to prevent disk exhaustion. -- [ ] **State directory backed up** — The `~/.graycode/state/` directory contains +- [ ] **State directory backed up** — The `~/.hawk/state/` directory contains the PID file, API key pin file, audit log, and session state. Back up the audit log key (`securitylog/sel.key`) — **losing it makes all historical audit entries unverifiable**. @@ -40,17 +40,17 @@ documentation section. ## Observability -- [ ] **Telemetry enabled (optional)** — Set `GRAYCODE_ENABLE_TELEMETRY=1` +- [ ] **Telemetry enabled (optional)** — Set `HAWK_ENABLE_TELEMETRY=1` and configure `OTEL_EXPORTER_OTLP_ENDPOINT` to send traces to your OTLP collector. Telemetry is opt-in by default. - [ ] **Prometheus scraping** — If using Prometheus, configure a scrape target for `http://:4590/v1/metrics` with authentication: ```yaml scrape_configs: - - job_name: 'graycode-daemon' - bearer_token: '' + - job_name: 'hawk-daemon' + bearer_token: '' static_configs: - - targets: ['graycode-daemon:4590'] + - targets: ['hawk-daemon:4590'] metrics_path: '/v1/metrics' ``` - [ ] **Health/readiness probes** — Configure in your orchestrator: @@ -62,13 +62,13 @@ documentation section. ## Security Hardening - [ ] **API key rotated** — The API key is written to a pinned file at - `~/.graycode/state/daemon.key` for convenience. **Remove this file in + `~/.hawk/state/daemon.key` for convenience. **Remove this file in production** or ensure it has `0600` permissions and is not world-readable. - [ ] **Audit log verification** — Periodically verify the audit log integrity: ```bash # The Verify function checks the HMAC chain - go run ./cmd/graycode securitylog verify + go run ./cmd/hawk securitylog verify ``` - [ ] **Security headers** — Verify `X-Content-Type-Options`, `X-Frame-Options`, and `Content-Security-Policy` headers are present @@ -79,7 +79,7 @@ documentation section. - [ ] **No CGO** — The binary is built with `CGO_ENABLED=0` for a static binary. Verify the deployed binary has no dynamic library dependencies: ```bash - ldd /usr/local/bin/graycode # should say "not a dynamic executable" + ldd /usr/local/bin/hawk # should say "not a dynamic executable" ``` ## Post-Deployment @@ -97,7 +97,7 @@ documentation section. ``` - [ ] **Metrics test** — Verify the metrics endpoint: ```bash - curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" http://localhost:4590/v1/metrics + curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/metrics ``` - [ ] **Version check** — Verify the deployed version matches expectations: ```bash @@ -106,10 +106,10 @@ documentation section. ## Upgrade Procedure -1. **Back up state** — Copy `~/.graycode/state/` to a safe location. +1. **Back up state** — Copy `~/.hawk/state/` to a safe location. 2. **Drain traffic** — Remove the daemon from load balancer rotation or stop sending new requests. -3. **Install new binary** — Replace the binary and run `graycode daemon start` +3. **Install new binary** — Replace the binary and run `hawk daemon start` with the same configuration. 4. **Verify health** — Check `GET /v1/health` and `GET /v1/ready`. 5. **Verify metrics** — Check `GET /v1/metrics` for expected counters. diff --git a/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md b/docs/plans/FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md similarity index 73% rename from docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md rename to docs/plans/FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md index be3de0828..fd5292865 100644 --- a/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md +++ b/docs/plans/FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md @@ -5,7 +5,7 @@ **Active execution:** [YEAR-0-ACTIVE.md](./YEAR-0-ACTIVE.md) (Year 0 control-plane track) **ADR:** [ADR-0003](../architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md) **Meaning of “port”:** Reimplement **every Grok Build capability** in idiomatic **Go** across graycode-eco repos. -**Not meaning:** Copy Rust crates, depend on Grok binaries, or collapse graycode into a Rust monorepo. +**Not meaning:** Copy Rust crates, depend on Grok binaries, or collapse hawk into a Rust monorepo. **Source tree:** `grok-eco/grok-build` (~1.35M LOC Rust, 1 product monorepo) **Target tree:** `graycode-eco/*` (multi-repo Go platform + cloud/TS/Python) @@ -18,10 +18,10 @@ ## 0. Port principles 1. **Behavior parity over code structure** — same user-visible contracts, not same file tree. -2. **Go multi-repo stays** — map Grok crates into graycode / engines / contracts / cloud / skills. -3. **Prefer wire-first** — Graycode already has partial types; complete wiring before greenfield. +2. **Go multi-repo stays** — map Grok crates into hawk / engines / contracts / cloud / skills. +3. **Prefer wire-first** — Hawk already has partial types; complete wiring before greenfield. 4. **Privacy-first defaults** — Grok Mixpanel/Sentry defaults become **opt-in** OTEL/privacy-safe telemetry. -5. **Multi-provider stays** — Grok sampler/auth maps to **graycode-router**, not a single-vendor clone. +5. **Multi-provider stays** — Grok sampler/auth maps to **eyrie**, not a single-vendor clone. 6. **Memory stays harrier** — Grok markdown memory maps to **harrier APIs + UX**, not a second store. 7. **Tokens stay shrike** — Grok `xai-token-estimation` is obsolete relative to shrike. 8. **Track everything** — even 70-line crates appear in the matrix (Done / Partial / Port / Skip / N/A). @@ -67,9 +67,9 @@ Critical path remains **months**; **complete parity** including TUI polish and e ```text grok-eco/grok-build (one Rust workspace) │ - ├─► graycode product CLI/TUI/engine/tools/hooks/plugins/ACP + ├─► hawk product CLI/TUI/engine/tools/hooks/plugins/ACP ├─► eagle pure DTOs (tool/spawn/hooks/policy) - ├─► graycode-router LLM routing/stream/retry/catalog/auth credentials + ├─► eyrie LLM routing/stream/retry/catalog/auth credentials ├─► harrier memory graph + dream UX ├─► shrike compression / secrets / token cost ├─► swift session capture/import/replay @@ -92,87 +92,87 @@ grok-eco/grok-build (one Rust workspace) | Grok crate | ~LOC | Capability | Target repo | Status | Effort | Notes | |------------|-----:|------------|-------------|--------|--------|-------| -| `xai-grok-pager` | 415k | Full TUI | graycode `cmd/` | Partial | XXL | Bubble Tea already; parity of panes/modals/slash is multi-year | -| `xai-grok-pager-render` | 35k | Render pipeline | graycode | Partial | L | Streaming render, blocks, media | -| `xai-grok-pager-minimal` | 5.6k | `--minimal` native scrollback | graycode | Partial | M | `graycode --repl` / print modes exist | -| `xai-grok-pager-bin` | 2.9k | Binary composition root | graycode `cmd/graycode` | Done | — | | -| `xai-grok-pager-pty-harness` | 10k | PTY test harness | graycode tests | Partial | M | Test infra | -| `xai-grok-shell` | 336k | Agent runtime host | graycode `internal/engine` | Partial | XXL | Largest runtime port | -| `xai-grok-shell-base` | 2.7k | Shared shell foundation | graycode | Partial | S | Absorb | -| `xai-grok-shell-session-support` | 1.5k | Session support extract | graycode `session` | Partial | S | Absorb | -| `xai-grok-tools` | 112k | Tool implementations | graycode `internal/tool` | Partial | XL | See tool matrix §3 | -| `xai-grok-tools-api` | 0.7k | Tool API / slash wording | graycode + contracts | Partial | S | | -| `xai-grok-workspace` | 77k | FS/VCS/permissions/hub | graycode + sandbox + session | Partial | XL | | +| `xai-grok-pager` | 415k | Full TUI | hawk `cmd/` | Partial | XXL | Bubble Tea already; parity of panes/modals/slash is multi-year | +| `xai-grok-pager-render` | 35k | Render pipeline | hawk | Partial | L | Streaming render, blocks, media | +| `xai-grok-pager-minimal` | 5.6k | `--minimal` native scrollback | hawk | Partial | M | `hawk --repl` / print modes exist | +| `xai-grok-pager-bin` | 2.9k | Binary composition root | hawk `cmd/hawk` | Done | — | | +| `xai-grok-pager-pty-harness` | 10k | PTY test harness | hawk tests | Partial | M | Test infra | +| `xai-grok-shell` | 336k | Agent runtime host | hawk `internal/engine` | Partial | XXL | Largest runtime port | +| `xai-grok-shell-base` | 2.7k | Shared shell foundation | hawk | Partial | S | Absorb | +| `xai-grok-shell-session-support` | 1.5k | Session support extract | hawk `session` | Partial | S | Absorb | +| `xai-grok-tools` | 112k | Tool implementations | hawk `internal/tool` | Partial | XL | See tool matrix §3 | +| `xai-grok-tools-api` | 0.7k | Tool API / slash wording | hawk + contracts | Partial | S | | +| `xai-grok-workspace` | 77k | FS/VCS/permissions/hub | hawk + sandbox + session | Partial | XL | | | `xai-grok-workspace-types` | 9.2k | Workspace wire types | contracts | Partial | M | | -| `xai-grok-workspace-client` | 0.8k | Workspace RPC client | graycode (if hub) | Port | M | Only if computer-hub ported | -| `xai-grok-agent` | 21k | Agent defs + system prompt | graycode agent/personas | Partial | L | | -| `xai-grok-subagent-resolution` | 2.6k | Capability/isolation resolve | graycode + contracts | Port | M | Critical | -| `xai-chat-state` | 13k | Chat state actor | graycode session/engine | Partial | L | | -| `xai-grok-markdown` | 22k | Streaming MD TUI | graycode markdown | Partial | L | | -| `xai-grok-markdown-core` | 1.1k | Headless MD analysis | graycode | Partial | S | | -| `xai-ratatui-textarea` | 12k | Input widget | graycode (Bubble Tea) | N/A | — | Different UI stack | -| `xai-ratatui-inline` | 3.7k | Inline render | graycode | N/A | — | UI stack | -| `xai-fast-worktree` | 19k | CoW worktree speed | graycode worktree | Port | L | Perf enhancement | -| `xai-file-utils` | 15k | Event tracking / upload | graycode observability | Partial | M | Privacy opt-in | -| `xai-grok-telemetry` | 14k | Events + OTEL + Mixpanel + Sentry | graycode observability | Partial | L | **Skip Mixpanel default**; keep OTEL | -| `xai-hunk-tracker` | 13k | Agent vs external hunks | graycode | Port | L | | -| `xai-grok-sampling-types` | 13k | Chat API types | **graycode-router** | Done* | — | Different shape; graycode-router owns | -| `xai-grok-sampler` | 11k | HTTP stream + retry | **graycode-router** | Done* | — | Do not replace graycode-router | -| `xai-grok-update` | 11k | Auto-update | graycode | Partial | M | | -| `xai-grok-mcp` | 10k | MCP client (oauth, wire) | graycode `mcp` | Partial | L | | -| `xai-codebase-graph` | 9.7k | Tree-sitter graph | graycode codegraph/repomap | Partial | L | | +| `xai-grok-workspace-client` | 0.8k | Workspace RPC client | hawk (if hub) | Port | M | Only if computer-hub ported | +| `xai-grok-agent` | 21k | Agent defs + system prompt | hawk agent/personas | Partial | L | | +| `xai-grok-subagent-resolution` | 2.6k | Capability/isolation resolve | hawk + contracts | Port | M | Critical | +| `xai-chat-state` | 13k | Chat state actor | hawk session/engine | Partial | L | | +| `xai-grok-markdown` | 22k | Streaming MD TUI | hawk markdown | Partial | L | | +| `xai-grok-markdown-core` | 1.1k | Headless MD analysis | hawk | Partial | S | | +| `xai-ratatui-textarea` | 12k | Input widget | hawk (Bubble Tea) | N/A | — | Different UI stack | +| `xai-ratatui-inline` | 3.7k | Inline render | hawk | N/A | — | UI stack | +| `xai-fast-worktree` | 19k | CoW worktree speed | hawk worktree | Port | L | Perf enhancement | +| `xai-file-utils` | 15k | Event tracking / upload | hawk observability | Partial | M | Privacy opt-in | +| `xai-grok-telemetry` | 14k | Events + OTEL + Mixpanel + Sentry | hawk observability | Partial | L | **Skip Mixpanel default**; keep OTEL | +| `xai-hunk-tracker` | 13k | Agent vs external hunks | hawk | Port | L | | +| `xai-grok-sampling-types` | 13k | Chat API types | **eyrie** | Done* | — | Different shape; eyrie owns | +| `xai-grok-sampler` | 11k | HTTP stream + retry | **eyrie** | Done* | — | Do not replace eyrie | +| `xai-grok-update` | 11k | Auto-update | hawk | Partial | M | | +| `xai-grok-mcp` | 10k | MCP client (oauth, wire) | hawk `mcp` | Partial | L | | +| `xai-codebase-graph` | 9.7k | Tree-sitter graph | hawk codegraph/repomap | Partial | L | | | `xai-grok-memory` | 9.7k | Cross-session memory | **harrier** | Done* | M | Port UX only | -| `xai-grok-hooks` | 8.3k | File/HTTP hooks | graycode hooks | Port | L | | -| `xai-fsnotify` | 6.7k | FS events | graycode (fsnotify) | Partial | S | | -| `xai-grok-config` | 6k | Config layers + managed | graycode config + cloud | Port | L | | +| `xai-grok-hooks` | 8.3k | File/HTTP hooks | hawk hooks | Port | L | | +| `xai-fsnotify` | 6.7k | FS events | hawk (fsnotify) | Partial | S | | +| `xai-grok-config` | 6k | Config layers + managed | hawk config + cloud | Port | L | | | `xai-grok-config-types` | 2.7k | Config DTOs | contracts/config | Partial | S | | -| `xai-grok-plugin-marketplace` | 5.3k | Marketplace | graycode + community-skills | Port | L | | -| `xai-grok-shared` | 5.2k | Shared utils | graycode | N/A | — | Absorb | -| `xai-grok-test-support` | 4.6k | Test harness | graycode testutil | Partial | M | | -| `xai-grok-sandbox` | 3.9k | OS sandbox profiles | graycode sandbox | Port | L | | -| `xai-grok-voice` | 2.7k | Streaming STT | graycode | Partial | M | whisper path exists | -| `xai-acp-lib` | 2.3k | ACP protocol | graycode acp | Port | L | | -| `xai-grok-mermaid` | 2.2k | Mermaid→PNG | graycode | Port | M | | -| `xai-crash-handler` | 1.9k | Crash + startup detect | graycode | Port | S | | -| `ptyctl` | 2.3k | Headless PTY control | graycode | Port | M | | -| `ptyctl-cli` | 0.8k | PTY CLI | graycode tests/tools | Optional | S | | -| `xai-tty-utils` | 1.2k | TTY-safe spawn | graycode | Port | S | | +| `xai-grok-plugin-marketplace` | 5.3k | Marketplace | hawk + community-skills | Port | L | | +| `xai-grok-shared` | 5.2k | Shared utils | hawk | N/A | — | Absorb | +| `xai-grok-test-support` | 4.6k | Test harness | hawk testutil | Partial | M | | +| `xai-grok-sandbox` | 3.9k | OS sandbox profiles | hawk sandbox | Port | L | | +| `xai-grok-voice` | 2.7k | Streaming STT | hawk | Partial | M | whisper path exists | +| `xai-acp-lib` | 2.3k | ACP protocol | hawk acp | Port | L | | +| `xai-grok-mermaid` | 2.2k | Mermaid→PNG | hawk | Port | M | | +| `xai-crash-handler` | 1.9k | Crash + startup detect | hawk | Port | S | | +| `ptyctl` | 2.3k | Headless PTY control | hawk | Port | M | | +| `ptyctl-cli` | 0.8k | PTY CLI | hawk tests/tools | Optional | S | | +| `xai-tty-utils` | 1.2k | TTY-safe spawn | hawk | Port | S | | | `xai-hooks-plugins-types` | 1.2k | Hooks/plugins ACP DTOs | contracts | Port | S | | -| `xai-sqlite-journal` | 0.8k | SQLite journal mode | graycode/harrier/shrike | Partial | XS | | -| `xai-system-power` | 0.7k | Sleep/wake notify | graycode sleep_prevent | Partial | S | | -| `xai-grok-http` | 0.6k | Shared HTTP client | graycode/netutil | Partial | XS | | -| `xai-agent-lifecycle` | 0.6k | Lifecycle hooks data | graycode hooks/engine | Partial | S | | -| `xai-gix-status` | 0.6k | Fast git status | graycode git tools | Partial | S | | -| `xai-grok-paths` | 0.6k | AbsPath types | graycode | N/A | XS | Go path.Clean enough | -| `xai-grok-secrets` | 0.6k | Secrets helpers | shrike + graycode-router | Partial | S | | -| `xai-grok-announcements` | 0.4k | Release announcements | graycode tips/notify | Port | S | | -| `xai-grok-auth` | 0.4k | Auth seam | graycode-router + graycode auth | Partial | M | Browser OAuth optional | +| `xai-sqlite-journal` | 0.8k | SQLite journal mode | hawk/harrier/shrike | Partial | XS | | +| `xai-system-power` | 0.7k | Sleep/wake notify | hawk sleep_prevent | Partial | S | | +| `xai-grok-http` | 0.6k | Shared HTTP client | hawk/netutil | Partial | XS | | +| `xai-agent-lifecycle` | 0.6k | Lifecycle hooks data | hawk hooks/engine | Partial | S | | +| `xai-gix-status` | 0.6k | Fast git status | hawk git tools | Partial | S | | +| `xai-grok-paths` | 0.6k | AbsPath types | hawk | N/A | XS | Go path.Clean enough | +| `xai-grok-secrets` | 0.6k | Secrets helpers | shrike + eyrie | Partial | S | | +| `xai-grok-announcements` | 0.4k | Release announcements | hawk tips/notify | Port | S | | +| `xai-grok-auth` | 0.4k | Auth seam | eyrie + hawk auth | Partial | M | Browser OAuth optional | | `xai-token-estimation` | 0.3k | Bytes/4 heuristic | **shrike** | Skip | — | shrike superior | -| `xai-tracing-macros` | 0.2k | Log macros | graycode observability | N/A | XS | | -| `xai-grok-env` | 0.2k | Backend env presets | graycode-router/graycode | Partial | XS | | -| `xai-prompt-queue` | 0.2k | Prompt queue types | graycode | Port | S | | +| `xai-tracing-macros` | 0.2k | Log macros | hawk observability | N/A | XS | | +| `xai-grok-env` | 0.2k | Backend env presets | eyrie/hawk | Partial | XS | | +| `xai-prompt-queue` | 0.2k | Prompt queue types | hawk | Port | S | | | `xai-mixpanel` | 0.1k | Mixpanel client | — | **Skip** | — | Privacy; OTEL opt-in | -| `xai-grok-version` | 0.1k | Version | graycode VERSION | Done | — | | -| `xai-grok-models` | 0.1k | Default model IDs | graycode-router catalog | Done* | — | | +| `xai-grok-version` | 0.1k | Version | hawk VERSION | Done | — | | +| `xai-grok-models` | 0.1k | Default model IDs | eyrie catalog | Done* | — | | ### 2.2 Common crates (tool protocol / hub / compaction) | Grok crate | ~LOC | Capability | Target | Status | Effort | Notes | |------------|-----:|------------|--------|--------|--------|-------| -| `xai-computer-hub-sdk` | 14k | Remote tool server SDK | new or graycode | Port (Y2+) | XL | Only if multi-host tools wanted | +| `xai-computer-hub-sdk` | 14k | Remote tool server SDK | new or hawk | Port (Y2+) | XL | Only if multi-host tools wanted | | `xai-computer-hub-core` | 4.2k | Transport/registry | same | Port (Y2+) | L | | | `xai-computer-hub-mcp-adapter` | 1k | MCP into hub | falcon | Port (Y2+) | M | | -| `xai-tool-protocol` | 6.6k | Wire protocol | contracts + graycode | Port (Y2) | L | | -| `xai-tool-runtime` | 5.4k | Tool trait runtime | graycode tool | Partial | L | | +| `xai-tool-protocol` | 6.6k | Wire protocol | contracts + hawk | Port (Y2) | L | | +| `xai-tool-runtime` | 5.4k | Tool trait runtime | hawk tool | Partial | L | | | `xai-tool-types` | 3.6k | Spawn/task types | **contracts** | Port | M | **P0** | -| `xai-grok-compaction` | 6.8k | Compaction engine | graycode engine + shrike | Partial | L | | -| `xai-circuit-breaker` | 2.2k | HTTP breaker | graycode-router/resilience | Partial | S | | -| `xai-tracing` | 0.8k | Tracing | graycode OTEL | Partial | S | | -| `xai-test-utils` | 0.4k | Hermetic git tests | graycode testutil | Partial | S | | -| `xai-interjection-core` | 0.3k | Interjection messaging | graycode | Port | S | Mid-turn user inject | +| `xai-grok-compaction` | 6.8k | Compaction engine | hawk engine + shrike | Partial | L | | +| `xai-circuit-breaker` | 2.2k | HTTP breaker | eyrie/resilience | Partial | S | | +| `xai-tracing` | 0.8k | Tracing | hawk OTEL | Partial | S | | +| `xai-test-utils` | 0.4k | Hermetic git tests | hawk testutil | Partial | S | | +| `xai-interjection-core` | 0.3k | Interjection messaging | hawk | Port | S | Mid-turn user inject | | `xai-proto-build` | — | Build tooling | — | Skip | — | Rust build | -\*Done* = domain covered by graycode engine with different API. +\*Done* = domain covered by hawk engine with different API. --- @@ -180,7 +180,7 @@ grok-eco/grok-build (one Rust workspace) ### 3.1 Grok Build native tools -| Grok tool | Graycode equivalent | Status | Port work | +| Grok tool | Hawk equivalent | Status | Port work | |-----------|-----------------|--------|-----------| | `bash` / `run_terminal_command` | `Bash` | Partial | background flag, timeout, safe-bash, kill | | `read_file` | `Read` | Partial | media/PDF page ranges parity | @@ -210,20 +210,20 @@ grok-eco/grok-build (one Rust workspace) Grok vendors codex/opencode tool implementations for compatibility profiles. -| Compat surface | Graycode | Action | +| Compat surface | Hawk | Action | |----------------|------|--------| -| Codex apply_patch / read / list / grep | graycode tools | Optional compat profile | -| OpenCode read/write/edit/bash/glob/grep/todo/skill | graycode | Optional `compat.opencode` | -| Claude import | missing | **Port** into swift/graycode | +| Codex apply_patch / read / list / grep | hawk tools | Optional compat profile | +| OpenCode read/write/edit/bash/glob/grep/todo/skill | hawk | Optional `compat.opencode` | +| Claude import | missing | **Port** into swift/hawk | | Cursor skills scan | missing | **Port** | --- ## 4. Slash commands & UX (pager) -Grok slash modules (port checklist → graycode `/` commands or CLI): +Grok slash modules (port checklist → hawk `/` commands or CLI): -| Grok slash | Status in graycode | Action | +| Grok slash | Status in hawk | Action | |------------|----------------|--------| | help | Partial | Port completeness | | model / effort | Partial | Port effort levels | @@ -259,17 +259,17 @@ Grok slash modules (port checklist → graycode `/` commands or CLI): --- -## 5. User-guide docs (port as graycode user-guide) +## 5. User-guide docs (port as hawk user-guide) | Grok doc | Port to | |----------|---------| -| 01–05 essentials | `graycode/docs/user-guide/` | +| 01–05 essentials | `hawk/docs/user-guide/` | | 06 theming | same | | 07 MCP | same + mcp-servers.md merge | | 08 skills | same | | 09 plugins | same | | 10 hooks | same | -| 11 custom models | graycode-router + graycode | +| 11 custom models | eyrie + hawk | | 12 project rules AGENTS.md | exists Partial | | 13 memory | harrier UX | | 14 headless | CLI flags doc | @@ -292,7 +292,7 @@ Grok slash modules (port checklist → graycode `/` commands or CLI): These are “small” crates/modules but required for **full** port claims: -| Item | Grok home | Graycode action | Effort | +| Item | Grok home | Hawk action | Effort | |------|-----------|-------------|--------| | Folder trust | `workspace/folder_trust.rs` | **Port** `internal/trust` | M | | envrc / direnv load | `workspace/envrc.rs` | **Port** | S | @@ -332,7 +332,7 @@ These are “small” crates/modules but required for **full** port claims: | Hyperlink routing | pager | Port | S | | Config TOML live edit | pager | Port | S | | Goal classifier | shell session | Port | S | -| Swift classifier | shell | map to graycode swift | S | +| Swift classifier | shell | map to hawk swift | S | | Repo changes tracking | shell session | Partial | M | | Active sessions multi | shell | Partial | M | | MCP doctor | shell mcp_doctor | Port | S | @@ -349,10 +349,10 @@ These are “small” crates/modules but required for **full** port claims: | Quarter | Deliverables | Repos | |---------|--------------|-------| -| **Q1** | Contracts spawn DTOs; wire explore/plan/general; stop hardcoded explore; explore bash hard gate; unify taskruntime; structured SpawnResult | contracts, graycode | -| **Q2** | sandbox.toml; folder trust; safe-bash; permission pipeline hooks-first; PreToolUse deny | graycode | -| **Q3** | File+HTTP hooks; vendor aliases; multi-harness skills; multi-component plugins; marketplace MVP | graycode, community-skills | -| **Q4** | Monitor/Wait/Kill; /loop; structured AskUser; plan/spec alignment; user-guide 01–12; crash handler; announcements; prompt queue; interjection | graycode | +| **Q1** | Contracts spawn DTOs; wire explore/plan/general; stop hardcoded explore; explore bash hard gate; unify taskruntime; structured SpawnResult | contracts, hawk | +| **Q2** | sandbox.toml; folder trust; safe-bash; permission pipeline hooks-first; PreToolUse deny | hawk | +| **Q3** | File+HTTP hooks; vendor aliases; multi-harness skills; multi-component plugins; marketplace MVP | hawk, community-skills | +| **Q4** | Monitor/Wait/Kill; /loop; structured AskUser; plan/spec alignment; user-guide 01–12; crash handler; announcements; prompt queue; interjection | hawk | **Exit Year 0:** Contributor-ready “Grok-class agent controls” without claiming full TUI parity. @@ -361,7 +361,7 @@ These are “small” crates/modules but required for **full** port claims: | Quarter | Deliverables | |---------|--------------| | **Q5** | ACP session/load/resume; richer updates; OpenAPI; sdk-go/python fields | -| **Q6** | Managed policy (signed) graycode-cloud → graycode; IT tier; config layer order | +| **Q6** | Managed policy (signed) graycode-cloud → hawk; IT tier; config layer order | | **Q7** | Foreign session import (Claude/Codex); envrc; hunk tracker; fast worktree; mermaid render | | **Q8** | Voice streaming upgrade; update checker parity; MCP doctor; slash parity batch 1; user-guide 13–24 | @@ -378,7 +378,7 @@ These are “small” crates/modules but required for **full** port claims: ### Year 3–5 — Continuous parity -- Diff audit bot: scan Grok public releases → open graycode issues +- Diff audit bot: scan Grok public releases → open hawk issues - Enterprise: MDM, SCIM (cloud), fleet policies - Full editor suite (VS Code/Zed via ACP) - Hardening, fuzz, SLSA releases @@ -406,11 +406,11 @@ Each pack is a shippable program of work with Definition of Done. - [ ] Tests for aliases and validation - [ ] Version release -**DoD:** graycode can import types; no engine deps. +**DoD:** hawk can import types; no engine deps. ### PACK-02: Spawn control plane (6–8 weeks) -**Repo:** `graycode` +**Repo:** `hawk` - [ ] Change `AgentSpawnFn` to SpawnRequest/Result - [ ] Agent tool full schema (type, capability, isolation, resume, cwd, model, thoroughness, description, background) @@ -440,7 +440,7 @@ Each pack is a shippable program of work with Definition of Done. - [ ] File discovery paths - [ ] HTTP runner - [ ] Inject into PermissionEngine before autonomy -- [ ] Plugin env GRAYCODE_PLUGIN_ROOT/DATA +- [ ] Plugin env HAWK_PLUGIN_ROOT/DATA ### PACK-05: Extensions (8–10 weeks) @@ -484,7 +484,7 @@ Each pack is a shippable program of work with Definition of Done. ### PACK-10: Enterprise policy (6 weeks) - [ ] Signed managed policy schema (cloud) -- [ ] graycode apply layers +- [ ] hawk apply layers - [ ] fail-open vs fail-closed - [ ] IT non-excludable tier end-to-end @@ -510,7 +510,7 @@ Each pack is a shippable program of work with Definition of Done. ### PACK-14: TUI parity program (ongoing XL) -Track Grok pager features against graycode Bubble Tea: +Track Grok pager features against hawk Bubble Tea: - [ ] Dashboard view parity - [ ] Agents/personas modal @@ -545,7 +545,7 @@ Only if product requires remote tool hosts: ## 9. Per graycode-eco repo full ownership checklist -### `graycode` (majority) +### `hawk` (majority) Port/finish: spawn, tools, sandbox, trust, hooks, plugins, marketplace client, TUI, ACP, headless, slash, taskruntime, hunks, mermaid, voice, crash, announcements, queue, interjection, plan, ask user, update, PTY harness, envrc, permissions pipeline, user-guide. @@ -553,7 +553,7 @@ Port/finish: spawn, tools, sandbox, trust, hooks, plugins, marketplace client, T Port: spawn/capability/isolation types, hook events, optional sandbox policy DTO, tool protocol types (later). -### `graycode-router` +### `eyrie` Absorb: sampling/stream/retry/circuit-breaker lessons; catalog already ahead; managed deployment hooks; **do not** become Grok-only auth. @@ -571,7 +571,7 @@ Foreign session import; share/export; session indexing parity with Grok session ### `kestrel` / `merlin` -No direct Grok crates; keep peer engines; ensure graycode composition matches Grok “review/merlin” product moments if any. +No direct Grok crates; keep peer engines; ensure hawk composition matches Grok “review/merlin” product moments if any. ### `falcon` @@ -605,10 +605,10 @@ Dashboard/usage/marketplace web UI only. | SpaceXAI-only browser auth as sole path | Multi-provider + keychain | | `xai-token-estimation` | shrike wins | | Replace harrier with markdown memory files | Graph better | -| Replace graycode-router with xai-grok-sampler | Multi-provider better | +| Replace eyrie with xai-grok-sampler | Multi-provider better | | ratatui widgets | Bubble Tea stack | | `xai-proto-build` | Rust build | -| Closed contribution policy | Graycode is open | +| Closed contribution policy | Hawk is open | Skipping is **documented completion**, not unfinished work. @@ -650,7 +650,7 @@ Declare complete when: 6. Folder trust + sandbox deny globs pass security tests on macOS+Linux. 7. Marketplace can install a multi-component plugin. 8. Subagent explore/plan/general + resume + worktree isolation work. -9. No second memory/token/provider stack duplicated against harrier/shrike/graycode-router. +9. No second memory/token/provider stack duplicated against harrier/shrike/eyrie. 10. Continuous parity process (PACK-17) running for 2 releases. --- @@ -692,7 +692,7 @@ Execute shorter plan as **Year 0** of this master plan. ## 16. One-line summary -**Port all of grok-eco into graycode-eco = reimplement Grok Build’s full capability surface in Go across graycode-eco repos, map each crate to an owner engine, skip vendor/privacy conflicts, wire Graycode’s existing partial systems first, and run a multi-year program ending in behavioral parity—not a Rust code transplant.** +**Port all of grok-eco into graycode-eco = reimplement Grok Build’s full capability surface in Go across graycode-eco repos, map each crate to an owner engine, skip vendor/privacy conflicts, wire Hawk’s existing partial systems first, and run a multi-year program ending in behavioral parity—not a Rust code transplant.** --- diff --git a/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md b/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md index 45ed69a88..ba36435b0 100644 --- a/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md +++ b/docs/plans/GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md @@ -4,8 +4,8 @@ **Date:** 2026-07-16 **Horizon:** ~12–18 months (phased; can compress with parallel teams) **Language:** Go only (no Rust ports; reimplement contracts and behavior) -**Primary product:** `graycode` -**Supporting repositories:** `eagle`, `graycode-router`, `harrier` (Harrier), `shrike` (Shrike), `swift` (Swift), `kestrel` (Kestrel), `merlin` (Merlin), `falcon`, `starling`, `sparrow`, `robin`, `wren`, `owl`, and `graycode-platform` (web/BFF/Graycode Cloud; outside the Graycode Go runtime graph) +**Primary product:** `hawk` +**Supporting repositories:** `eagle`, `eyrie`, `harrier` (Harrier), `shrike` (Shrike), `swift` (Swift), `kestrel` (Kestrel), `merlin` (Merlin), `falcon`, `starling`, `sparrow`, `robin`, `wren`, `owl`, and `graycode-platform` (web/BFF/Hawk Cloud; outside the Hawk Go runtime graph) --- @@ -13,21 +13,21 @@ ### Goal -Bring Graycode to **Grok-class agent control-plane quality** (typed subagents, sandbox profiles, folder trust, hooks, plugins/marketplace, ACP depth, monitor/scheduler, enterprise managed policy) **without** abandoning Graycode’s multi-repo Go platform advantages (graycode-router multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). +Bring Hawk to **Grok-class agent control-plane quality** (typed subagents, sandbox profiles, folder trust, hooks, plugins/marketplace, ACP depth, monitor/scheduler, enterprise managed policy) **without** abandoning Hawk’s multi-repo Go platform advantages (eyrie multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). ### Non-goals -- Rewrite Graycode in Rust or monorepo-collapse engines into graycode. +- Rewrite Hawk in Rust or monorepo-collapse engines into hawk. - Vendor lock-in to a single LLM host auth model. -- Replace graycode-router/harrier/shrike/swift with Grok-shaped internal crates. +- Replace eyrie/harrier/shrike/swift with Grok-shaped internal crates. - Default opt-in product telemetry that violates privacy-first posture. ### Strategy -1. **Close wiring gaps first** — Graycode already has many *types* and *partial systems* that are not exposed on the Agent tool or unified. +1. **Close wiring gaps first** — Hawk already has many *types* and *partial systems* that are not exposed on the Agent tool or unified. 2. **Stabilize contracts** in `eagle` before multi-repo consumers. 3. **Ship vertical slices** (end-to-end user value) every quarter. -4. **Keep engines peer-independent**; graycode remains the only product orchestrator. +4. **Keep engines peer-independent**; hawk remains the only product orchestrator. --- @@ -110,7 +110,7 @@ This is the single highest-ROI fix: **wire what already exists**, then extend. ```text ┌──────────────────────────────┐ - │ graycode TUI / CLI / daemon/ACP │ + │ hawk TUI / CLI / daemon/ACP │ └──────────────┬───────────────┘ │ ┌─────────────────────────┼─────────────────────────┐ @@ -130,10 +130,10 @@ This is the single highest-ROI fix: **wire what already exists**, then extend. │ ┌────┴────┬──────────┬──────────┬──────────┐ ▼ ▼ ▼ ▼ ▼ - graycode-router harrier shrike swift kestrel/merlin + eyrie harrier shrike swift kestrel/merlin ``` -**New core package (proposed):** `graycode/internal/spawn` (or expand `engine/agent`) owning: +**New core package (proposed):** `hawk/internal/spawn` (or expand `engine/agent`) owning: - `SpawnRequest` / `SpawnResult` (Go structs aligned with contracts) - capability filter, isolation worktree lifecycle @@ -186,7 +186,7 @@ Legend: **Done** | **Partial** | **Missing** | **N/A (keep engine)** | C2 | Vendor event aliases | Missing | Full | | C3 | File-discovered hooks | Partial (in-process) | Full | | C4 | HTTP hooks | Missing | Full | -| C5 | Plugin env GRAYCODE_PLUGIN_ROOT/DATA | Missing | Full | +| C5 | Plugin env HAWK_PLUGIN_ROOT/DATA | Missing | Full | | C6 | PreToolUse can deny in CheckTool path | Missing | Full | ### Wave D — Plugins & marketplace (P3) @@ -261,7 +261,7 @@ Legend: **Done** | **Partial** | **Missing** | **N/A (keep engine)** ## 4. Multi-quarter roadmap (long horizon) -Assumes ~1–2 senior Go engineers on graycode core + fractional engine/cloud; scale parallelizes quarters. +Assumes ~1–2 senior Go engineers on hawk core + fractional engine/cloud; scale parallelizes quarters. ```text Q1 Foundation: contracts + spawn wiring + background unify @@ -370,13 +370,13 @@ type SpawnResult struct { **Exit criteria** -- `go test ./...` green; graycode can depend on new pseudo-version without engine imports. +- `go test ./...` green; hawk can depend on new pseudo-version without engine imports. --- ### Phase 2 — Spawn controller & Agent tool (4–6 weeks) ★ highest ROI -**Repo:** `graycode` +**Repo:** `hawk` **Packages:** `internal/engine/agent`, `internal/engine`, `internal/tool` #### 2.1 Change `AgentSpawnFn` signature @@ -403,8 +403,8 @@ Migration: temporary adapter for one release if needed; prefer single breaking c | 2 | Map `subagent_type` → `SubAgentMode` (+ thoroughness) | | 3 | Default capability from mode; allow override | | 4 | Apply `FilterToolsForMode` **and** capability filter | -| 5 | Isolation worktree: create under `.graycode/worktrees/` using shared helper from mission worker | -| 6 | Persist child session transcript under `~/.graycode/subagents//` | +| 5 | Isolation worktree: create under `.hawk/worktrees/` using shared helper from mission worker | +| 6 | Persist child session transcript under `~/.hawk/subagents//` | | 7 | `resume_from`: load transcript, append prompt, re-spawn with same type | | 8 | Return structured `SpawnResult` JSON to model | @@ -449,7 +449,7 @@ Deprecate: #### 2.6 PR stack (Graphite-friendly) -1. Contracts bump in graycode +1. Contracts bump in hawk 2. `SpawnRequest` internal + adapter 3. Agent tool schema + parse 4. Wire modes plan/general/explore @@ -474,13 +474,13 @@ Deprecate: ### Phase 3 — Sandbox profiles + folder trust (4–5 weeks) -**Repo:** `graycode` +**Repo:** `hawk` **Packages:** `internal/sandbox`, `internal/trust` (new), `internal/mcp`, `internal/hooks`, `internal/plugin` #### 3.1 sandbox.toml ```toml -# ~/.graycode/sandbox.toml +# ~/.hawk/sandbox.toml [profiles.ci] extends = "strict" restrict_network = true @@ -490,7 +490,7 @@ deny = ["**/.env", "**/*.pem", "**/*credentials*"] **Loader rules (security-critical):** 1. Load user global. -2. Load project `.graycode/sandbox.toml` **additive only** (new names only). +2. Load project `.hawk/sandbox.toml` **additive only** (new names only). 3. Warn on conflicting redefinition; ignore project redefinition. 4. Deny globs applied on top of backend (seatbelt/landlock/bwrap-equivalent). 5. Fail closed when deny requested but OS cannot enforce. @@ -508,7 +508,7 @@ deny = ["**/.env", "**/*.pem", "**/*credentials*"] #### 3.3 Folder trust -**Store:** `~/.graycode/trusted_folders.toml` +**Store:** `~/.hawk/trusted_folders.toml` ```toml [[folders]] @@ -538,7 +538,7 @@ trusted_at = "2026-07-16T00:00:00Z" ### Phase 4 — Hooks pipeline completion (3–4 weeks) -**Repo:** `graycode` +**Repo:** `hawk` **Package:** `internal/hooks` #### 4.1 Event model expansion @@ -550,7 +550,7 @@ Keep existing; alias map for Claude/Cursor names. | Type | Behavior | |------|----------| -| command | shell with timeout, env GRAYCODE_* | +| command | shell with timeout, env HAWK_* | | http | POST JSON, timeout, optional deny on non-2xx for pre_tool | #### 4.3 Integration into PermissionEngine @@ -566,8 +566,8 @@ CheckTool: #### 4.4 File discovery -- `~/.graycode/hooks/*.json` -- `.graycode/hooks/*.json` (trust required) +- `~/.hawk/hooks/*.json` +- `.hawk/hooks/*.json` (trust required) - Compat: `.claude/settings.json` hooks, `.cursor/hooks.json` behind flags **Exit criteria** @@ -582,7 +582,7 @@ CheckTool: ### Phase 5 — Plugins multi-component + marketplace MVP (6–8 weeks) -**Repos:** `graycode`, `starling` +**Repos:** `hawk`, `starling` #### 5.1 Plugin layout (convention) @@ -603,19 +603,19 @@ Loader merges components; tools optional. 1. Session meta / SDK 2. `--plugin-dir` -3. Project `.graycode/plugins` (trust) -4. User `~/.graycode/plugins` +3. Project `.hawk/plugins` (trust) +4. User `~/.hawk/plugins` 5. Config extra paths #### 5.3 Marketplace | Component | Owner | |-----------|--------| -| Source list config | graycode | -| Index schema | community-skills + graycode | -| Install resolve (git) | graycode | -| Audit | graycode plugin malware_check (extend) | -| CLI `graycode plugins` / TUI tab | graycode | +| Source list config | hawk | +| Index schema | community-skills + hawk | +| Install resolve (git) | hawk | +| Audit | hawk plugin malware_check (extend) | +| CLI `hawk plugins` / TUI tab | hawk | | Optional web gallery | graycode-platform later | #### 5.4 Multi-harness skills @@ -661,7 +661,7 @@ Depends on Phase 2 unified `taskruntime`. | Work | Detail | |------|--------| -| Plan subagent | Default tool for “design only”; writes plan file under `.graycode/plans/` or specs | +| Plan subagent | Default tool for “design only”; writes plan file under `.hawk/plans/` or specs | | Spec workflow | Document mapping: plan agent → `/spec` stages; avoid double systems | | AskUserQuestion | questions[], options, multi_select, other, cancel message | | TUI | Reuse autonomy/spec pickers | @@ -677,7 +677,7 @@ Depends on Phase 2 unified `taskruntime`. ### Phase 8 — ACP phase-2 + SDKs + OpenAPI (5–7 weeks) -**Repos:** `graycode`, `sparrow`, `robin` +**Repos:** `hawk`, `sparrow`, `robin` | Milestone | Scope | |-----------|--------| @@ -686,11 +686,11 @@ Depends on Phase 2 unified `taskruntime`. | ACP-3 | permission round-trip hardened | | API | OpenAPI fields: spawn options, sandbox, autonomy, plugins meta | | SDK | AgentConfig + ChatRequest fields; plugin dirs | -| Optional | `graycode agent serve --bind` WS | +| Optional | `hawk agent serve --bind` WS | **Exit criteria** -- Zed/VS Code can drive graycode ACP for multi-turn with permissions. +- Zed/VS Code can drive hawk ACP for multi-turn with permissions. - SDK e2e against daemon contract snapshot tests. **Effort:** ~7 eng-weeks (+ extension work separate). @@ -699,13 +699,13 @@ Depends on Phase 2 unified `taskruntime`. ### Phase 9 — Enterprise managed policy (4–6 weeks) -**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `graycode`, optional `graycode-router` +**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `hawk`, optional `eyrie` | Piece | Detail | |-------|--------| | Policy document | models allow/deny, capabilities, sandbox deny, max budget, tool denylist | -| Signing | Ed25519 envelope; graycode verifies before apply | -| Apply path | `~/.graycode/managed_policy.json` layers under user config | +| Signing | Ed25519 envelope; hawk verifies before apply | +| Apply path | `~/.hawk/managed_policy.json` layers under user config | | Default | fail-open for individual; org can require fail-closed | | IT tier | non-excludable rules (already sketched in product — finish) | | Cloud UI | graycode-platform admin later | @@ -726,10 +726,10 @@ Build on existing enterprise policyInput (model/capability lists). | Item | Repo | Detail | |------|------|--------| -| Foreign import | swift + graycode CLI | Claude/Codex session metadata → index | -| Hunk attribution | graycode | agent vs external edits via fsnotify | -| Memory UX | harrier + graycode | toggle priority, `/dream` consolidate | -| Mermaid optional | graycode | sandbox render path | +| Foreign import | swift + hawk CLI | Claude/Codex session metadata → index | +| Hunk attribution | hawk | agent vs external edits via fsnotify | +| Memory UX | harrier + hawk | toggle priority, `/dream` consolidate | +| Mermaid optional | hawk | sandbox render path | **Exit criteria** @@ -756,20 +756,20 @@ Build on existing enterprise policyInput (model/capability lists). | Concept family | Primary | Secondary | Contracts? | |----------------|---------|-----------|------------| -| Spawn / capability / isolation | graycode | — | yes | -| Sandbox profiles | graycode | — | optional DTO | -| Folder trust | graycode | — | no | -| Hooks | graycode | — | event names yes | -| Plugins / marketplace | graycode | community-skills | manifest schema | -| Multi-harness skills | graycode | community-skills | no | -| Monitor/tasks | graycode | — | optional | -| ACP | graycode | sdk-go/python | OpenAPI | -| Managed policy | graycode-platform/apps/worker (deployed as `graycode-cloud`) | graycode, graycode-platform/apps/bff | yes DTO | -| Foreign import | swift | graycode | optional | -| Memory dream UX | harrier | graycode | no | +| Spawn / capability / isolation | hawk | — | yes | +| Sandbox profiles | hawk | — | optional DTO | +| Folder trust | hawk | — | no | +| Hooks | hawk | — | event names yes | +| Plugins / marketplace | hawk | community-skills | manifest schema | +| Multi-harness skills | hawk | community-skills | no | +| Monitor/tasks | hawk | — | optional | +| ACP | hawk | sdk-go/python | OpenAPI | +| Managed policy | graycode-platform/apps/worker (deployed as `graycode-cloud`) | hawk, graycode-platform/apps/bff | yes DTO | +| Foreign import | swift | hawk | optional | +| Memory dream UX | harrier | hawk | no | | Token | shrike | — | no change | -| Providers | graycode-router | graycode | no change | -| Review engines | kestrel/merlin | graycode | findings already | +| Providers | eyrie | hawk | no change | +| Review engines | kestrel/merlin | hawk | findings already | --- @@ -835,7 +835,7 @@ Prefer **secure defaults** (folder trust on) even if noisy. | Risk | Impact | Mitigation | |------|--------|------------| -| Signature change of AgentSpawnFn breaks plugins | High | Adapter release; version min_graycode | +| Signature change of AgentSpawnFn breaks plugins | High | Adapter release; version min_hawk | | Worktree disk bloat | Med | GC old worktrees; symlink shared dirs (already pattern) | | Explore still escapes via Bash | High | AST allowlist + sandbox | | Marketplace supply chain | High | audit, pin commit SHAs, signatures later | @@ -886,7 +886,7 @@ docs(user-guide): 01-getting-started … | Role | Q1–Q2 | Q3–Q4 | Q5–Q8 | |------|-------|-------|-------| -| Graycode core (Go) | 1.5 FTE | 1.5 FTE | 1 FTE | +| Hawk core (Go) | 1.5 FTE | 1.5 FTE | 1 FTE | | Security-minded sandbox | 0.5 FTE | 0.5 FTE | 0.25 FTE | | Community-skills / marketplace content | 0.25 | 0.75 | 0.5 | | Cloud (TS) | 0 | 0.25 | 0.75 | @@ -920,21 +920,21 @@ Background: --- -## 14. Appendix B — Mapping Grok names → Graycode names +## 14. Appendix B — Mapping Grok names → Hawk names -| Grok | Graycode | +| Grok | Hawk | |------|------| | `task` tool | `Agent` / `Task` alias | | `general-purpose` | `general` / `general-purpose` | | `capability_mode` | same | | `isolation: worktree` | same | | `resume_from` | same (replace weak `agent_id` semantics) | -| `sandbox.toml` | `~/.graycode/sandbox.toml` | -| folder trust | `~/.graycode/trusted_folders.toml` | +| `sandbox.toml` | `~/.hawk/sandbox.toml` | +| folder trust | `~/.hawk/trusted_folders.toml` | | `/loop` | `/loop` over CronScheduler | -| plugin marketplace | `graycode plugins` + community registry | +| plugin marketplace | `hawk plugins` + community registry | | managed_config | managed policy via graycode-cloud | -| ACP | `graycode acp` | +| ACP | `hawk acp` | --- @@ -976,7 +976,7 @@ Week 4 | Trust default | On | Security | | Marketplace before trust? | No | Supply chain | | Replace harrier? | No | Graph memory superior | -| Replace graycode-router? | No | Multi-provider is differentiator | +| Replace eyrie? | No | Multi-provider is differentiator | | Three bg systems | Unify early | Blocks monitor/wait | --- diff --git a/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md b/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md index 0b3eecd81..10f6aafa8 100644 --- a/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md +++ b/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md @@ -69,7 +69,7 @@ **Changes:** 1. Create `internal/tool/spec_version.go` — SpecVersionTool - - Stage .graycode/specs/ changes + - Stage .hawk/specs/ changes - Generate commit message referencing REQ IDs - Link code commits to spec requirements diff --git a/docs/plans/YEAR-0-ACTIVE.md b/docs/plans/YEAR-0-ACTIVE.md index b70e9479e..3a5764419 100644 --- a/docs/plans/YEAR-0-ACTIVE.md +++ b/docs/plans/YEAR-0-ACTIVE.md @@ -1,9 +1,9 @@ -# Year 0 Active Track (Grok → Graycode) +# Year 0 Active Track (Grok → Hawk) **Status:** Active **Date:** 2026-07-16 **ADR:** [ADR-0003](../architecture/adr/ADR-0003-grok-behavioral-port-go-multirepo.md) -**Full matrices:** [FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md](./FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md), +**Full matrices:** [FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md](./FULL-GROK-ECO-TO-HAWK-ECO-PORT-PLAN.md), [GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md](./GROK-CLASS-CAPABILITY-LONG-HORIZON-PLAN.md) This is the **executable Year 0 program**. It does not replace the full @@ -14,8 +14,8 @@ port matrices; it freezes what “Year 0 done” means and tracks pack status. | Do | Do not | |----|--------| | Reimplement Grok **behavior** in Go | Copy Rust crates or depend on Grok | -| Map capabilities to graycode-eco repos | Collapse engines into graycode monorepo | -| Wire existing modes/budgets first | Rebuild graycode-router/harrier/shrike as Grok clones | +| Map capabilities to graycode-eco repos | Collapse engines into hawk monorepo | +| Wire existing modes/budgets first | Rebuild eyrie/harrier/shrike as Grok clones | | Privacy-first telemetry (OTEL opt-in) | Port Mixpanel defaults | ## Pack status @@ -38,13 +38,13 @@ port matrices; it freezes what “Year 0 done” means and tracks pack status. - [x] Model can spawn `explore` \| `plan` \| `general-purpose` via Agent tool schema (isolation=worktree works; resume still stub) - [x] Explore bash cannot mutate (`ExploreBashAllowed` segment allowlist + `ReadOnlyBash` on subagents) - [x] Unified agent taskruntime (`internal/taskruntime`; shell TaskOutput merge is PACK-06) -- [x] Folder trust gates project hooks / plugins (`.graycode/plugins`, `.graycode/hooks`; MCP/LSP follow same AllowLoadPath) +- [x] Folder trust gates project hooks / plugins (`.hawk/plugins`, `.hawk/hooks`; MCP/LSP follow same AllowLoadPath) - [x] `sandbox.toml` profiles + project additive merge; deny globs fail-closed - [x] PreToolUse hooks can deny inside `PermissionEngine` before autonomy - [x] Multi-component plugins + marketplace MVP install path - [x] Monitor + Wait/Kill + `/loop` tools implemented and working - [~] Crash handler, announcements, prompt queue, interjection — crash handler exists in cmd/errors.go; `/btw` (interjection) implemented; announcements and prompt queue still missing -- [x] User-guide docs `01`–`24` under `graycode/docs/user-guide/` (completed July 2026) +- [x] User-guide docs `01`–`24` under `hawk/docs/user-guide/` (completed July 2026) - [x] ADR-0003 published - [x] PACK-00 inventory + flags + spawn matrix template complete @@ -58,9 +58,9 @@ computer hub, full slash/TUI pixel parity, Mixpanel. | Env | Default | Purpose | |-----|---------|---------| -| `GRAYCODE_Y0_SPAWN_V2` | `1` once PACK-02 ships; `0` during dual path | Typed SpawnRequest path | -| `GRAYCODE_Y0_FOLDER_TRUST` | `1` recommended after PACK-03 | Gate project automation | -| `GRAYCODE_Y0_MARKETPLACE` | `0` until PACK-05 + trust | Marketplace install path | +| `HAWK_Y0_SPAWN_V2` | `1` once PACK-02 ships; `0` during dual path | Typed SpawnRequest path | +| `HAWK_Y0_FOLDER_TRUST` | `1` recommended after PACK-03 | Gate project automation | +| `HAWK_Y0_MARKETPLACE` | `0` until PACK-05 + trust | Marketplace install path | Implementation: `internal/flags/y0.go`. diff --git a/docs/plans/codex-adoption-plan.md b/docs/plans/codex-adoption-plan.md index fd2507a38..9e7bb9224 100644 --- a/docs/plans/codex-adoption-plan.md +++ b/docs/plans/codex-adoption-plan.md @@ -1,6 +1,6 @@ # OpenAI Codex CLI Adoption Plan -Status: Audited. Core ideas already implemented natively in graycode; remaining +Status: Audited. Core ideas already implemented natively in hawk; remaining deltas recorded as future RFCs. Source: `https://github.com/openai/codex` (Apache-2.0, Rust workspace @@ -8,9 +8,9 @@ Source: `https://github.com/openai/codex` (Apache-2.0, Rust workspace ## Executive Decision -The audit found that every codex-rs capability relevant to graycode's security and +The audit found that every codex-rs capability relevant to hawk's security and runtime model already has a native Go implementation, several of them deeper -than codex's equivalents because they build on Graycode's independent ecosystem +than codex's equivalents because they build on Hawk's independent ecosystem repositories. No second runtime, sandbox layer, or policy engine was created. @@ -23,18 +23,18 @@ Three codex ideas are deliberately deferred as future RFCs; see ## Capability Audit -| codex-rs crate/concept | graycode implementation | Decision | +| codex-rs crate/concept | hawk implementation | Decision | |---|---|---| -| `core` agent loop | `internal/engine` | Keep graycode | -| `tui`, `ansi-escape`, `terminal-detection` | Bubble Tea/Lipgloss TUI | Keep graycode | -| `rollout`, `thread-store`, `history` JSONL sessions with resume/fork | `internal/session` JSONL + WAL + named checkpoints + fork + recovery + handover | Keep graycode (richer) | -| `app-server-daemon`, `app-server-protocol` (JSON-RPC for IDE/desktop) | `internal/daemon` HTTP/SSE on 4590 + `internal/acp` | Keep graycode | -| `mcp-server`, `codex-mcp`, `rmcp-client`, `connectors` | `internal/mcp` client+server, sibling `falcon` scaffolding | Keep graycode | -| `skills`, `plugin`, `hooks` | community skill registry + structural validator, plugins, expanded lifecycle hook events | Keep graycode | -| `login`, `keyring-store`, `aws-auth` | graycode-router credential store in OS keychain across 28 providers | Keep graycode (broader) | -| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `graycode-router` adapters, catalog, cascade routing | Keep graycode (much broader) | -| `memories`, `agent-graph-store`, `context-fragments` | sibling `harrier` (Harrier) graph memory; eventlog/graphjournal projections | Keep graycode | -| `apply-patch`, `file-search`, `file-watcher`, `git-utils` | edit tools, codegraph, git tooling, watcher hooks | Keep graycode | +| `core` agent loop | `internal/engine` | Keep hawk | +| `tui`, `ansi-escape`, `terminal-detection` | Bubble Tea/Lipgloss TUI | Keep hawk | +| `rollout`, `thread-store`, `history` JSONL sessions with resume/fork | `internal/session` JSONL + WAL + named checkpoints + fork + recovery + handover | Keep hawk (richer) | +| `app-server-daemon`, `app-server-protocol` (JSON-RPC for IDE/desktop) | `internal/daemon` HTTP/SSE on 4590 + `internal/acp` | Keep hawk | +| `mcp-server`, `codex-mcp`, `rmcp-client`, `connectors` | `internal/mcp` client+server, sibling `falcon` scaffolding | Keep hawk | +| `skills`, `plugin`, `hooks` | community skill registry + structural validator, plugins, expanded lifecycle hook events | Keep hawk | +| `login`, `keyring-store`, `aws-auth` | eyrie credential store in OS keychain across 28 providers | Keep hawk (broader) | +| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `eyrie` adapters, catalog, cascade routing | Keep hawk (much broader) | +| `memories`, `agent-graph-store`, `context-fragments` | sibling `harrier` (Harrier) graph memory; eventlog/graphjournal projections | Keep hawk | +| `apply-patch`, `file-search`, `file-watcher`, `git-utils` | edit tools, codegraph, git tooling, watcher hooks | Keep hawk | | `external-agent-migration` | swift reads Claude Code / Codex / Gemini CLI / OpenCode / Cursor sessions | Parity | | **`linux-sandbox`** (Landlock + seccomp-bpf) | `internal/sandbox/landlock.go`, `seccomp.go` — raw syscalls and BPF filter, no external tools | Already implemented | | **macOS Seatbelt** | `internal/sandbox/seatbelt.go` — SBPL profile generator with per-policy read/write/process/network rules | Already implemented | @@ -47,7 +47,7 @@ Three codex ideas are deliberately deferred as future RFCs; see ### Adopted in this change -- Status transparency: `graycode status` (text and `--json`) now resolves the +- Status transparency: `hawk status` (text and `--json`) now resolves the effective sandbox backend via `sandbox.SelectSandbox` and reports it as `permission.sandbox_backend`, so operators can confirm real kernel-level isolation (seatbelt on macOS, landlock/seccomp on Linux, ACL on Windows, @@ -67,19 +67,19 @@ Three codex ideas are deliberately deferred as future RFCs; see the safe read-only fan-out case. Arbitrary-script execution still requires an embedded runtime, capabilities model, and output-trust threat model; track as a standalone RFC. -- **Agent identity signing** (`agent-identity`): graycode already provides a +- **Agent identity signing** (`agent-identity`): hawk already provides a per-harness anonymous user identity (`internal/identity`) and a tamper-evident HMAC-chained security log with session-scoped events (`internal/securitylog`). Signed subagent delegation chains are worth a focused design once multi-org delegation exists. - **Cloud tasks client** (`cloud-tasks*`): remote task queue integration. - Graycode Cloud already provides sync/review surfaces; a queue protocol would + Hawk Cloud already provides sync/review surfaces; a queue protocol would duplicate that until a concrete consumer exists. ## Verification - `go test ./...` full suite green. -- `make vet`, `make lint`, `graycode verify` green. +- `make vet`, `make lint`, `hawk verify` green. - Repo-owned markdown passes `markdownlint-cli2 '**/*.md'` (CI scope); findings under sibling repositories belong to those repositories and follow their own contribution flow. diff --git a/docs/plans/commandcodeai-adoption-plan.md b/docs/plans/commandcodeai-adoption-plan.md index 11ad0195d..07e58ebd7 100644 --- a/docs/plans/commandcodeai-adoption-plan.md +++ b/docs/plans/commandcodeai-adoption-plan.md @@ -1,6 +1,6 @@ # CommandCodeAI Adoption Plan -Status: Implemented in the Graycode working tree where the existing architecture +Status: Implemented in the Hawk working tree where the existing architecture supports a safe, native implementation. ## Source Review @@ -13,7 +13,7 @@ The reviewed CommandCodeAI organization contains four relevant categories: | `cmd-old-public` | Archived placeholder/documentation repository | No implementation to adopt | | `BaseAI` | Archived TypeScript pipe SDK/local provider server; licensing metadata is inconsistent | Reimplement narrow ideas only; do not add as a dependency | | `agent-skills` | MIT skill collection with progressive-disclosure guidance | Adopt authoring/process ideas; preserve individual asset licenses | -| `awesome-agents` | Apache-2.0 example applications | Reference only; do not merge into Graycode skills | +| `awesome-agents` | Apache-2.0 example applications | Reference only; do not merge into Hawk skills | `starling` remains the canonical public skill registry. Its validator and registry tooling are more complete than the CommandCodeAI @@ -23,7 +23,7 @@ repositories and should remain authoritative. ### 5. Kimi Code workflow parity -The Kimi Code comparison confirmed that Graycode already provides native equivalents +The Kimi Code comparison confirmed that Hawk already provides native equivalents for most of its useful workflow ideas. The remaining gaps were addressed without adding a second agent runtime: @@ -34,20 +34,20 @@ adding a second agent runtime: `UserPromptQueued`, `TurnStarted`, `PostToolFailure`, `PermissionResult`, `SessionHeartbeat`, `TaskStarted`, `StopFailure`, `Interrupt`, and `Notification`. -- Graycode's existing permission engine already supports ordered allow/deny rules +- Hawk's existing permission engine already supports ordered allow/deny rules such as `Bash(git status*)` and `Write(*.env)`, pre-tool denial hooks, scoped policy snapshots, and destructive-command hard blocks. -- Graycode's existing goal tracker already provides durable objective state, +- Hawk's existing goal tracker already provides durable objective state, dependencies, progress, token budgets, continuation prompts, and lifecycle events. A second `GOAL.md` state machine would duplicate this implementation. The comparison also found no reason to adopt Kimi Code's two-engine split or -replace Graycode's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel -review, Merlin auditing, or GraycodeRouter provider runtime. +replace Hawk's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel +review, Merlin auditing, or Eyrie provider runtime. ### 1. Skill metadata interoperability -Graycode's smart-skill parser accepts both hyphenated and community-schema +Hawk's smart-skill parser accepts both hyphenated and community-schema snake_case keys: - `auto-invoke` / `auto_invoke` @@ -66,7 +66,7 @@ metadata. ### 2. Local skill validation -Graycode's existing Unicode audit remains the runtime security scanner. A new +Hawk's existing Unicode audit remains the runtime security scanner. A new structural validator complements it by checking: - required `name` and `description` @@ -77,13 +77,13 @@ structural validator complements it by checking: - `SKILL.md` size - `@ref(...)` path containment -`graycode skills audit` now reports both Unicode and structural findings. This is a +`hawk skills audit` now reports both Unicode and structural findings. This is a small Go-native subset of the community repository's broader validation model; it does not duplicate the registry's Python implementation. ### 3. Transparent preference model -Graycode already has `internal/feature/taste` with confidence, sample count, +Hawk already has `internal/feature/taste` with confidence, sample count, decay, project identity, merge, reset, prompt projection, and accept/edit signals. No second preference database was created. The user-facing model and policy are documented in `docs/user-guide/26-learned-preferences.md`: @@ -96,12 +96,12 @@ policy are documented in `docs/user-guide/26-learned-preferences.md`: ### 4. Workflow and harness documentation -CommandCodeAI's strongest product contribution is discoverability. Graycode now +CommandCodeAI's strongest product contribution is discoverability. Hawk now documents its existing capabilities in: - `docs/user-guide/26-learned-preferences.md` - `docs/user-guide/27-workflows.md` -- `docs/architecture/graycode-harness.md` +- `docs/architecture/hawk-harness.md` - `docs/user-guide/28-workflow-budgets.md` These cover slash/shell/file-context input, headless review, MCP-backed @@ -111,22 +111,22 @@ tool, depth, time, token, and cost budgets. ## Deliberately Not Adopted -- CommandCodeAI provider adapters: GraycodeRouter owns provider protocols and routing. -- BaseAI remote pipes: incompatible with Graycode's local authority and durable +- CommandCodeAI provider adapters: Eyrie owns provider protocols and routing. +- BaseAI remote pipes: incompatible with Hawk's local authority and durable event model. -- BaseAI `lowdb` JSON memory: weaker than Harrier and Graycode persistence. +- BaseAI `lowdb` JSON memory: weaker than Harrier and Hawk persistence. - Unconditional parallel tool execution: unsafe for mutations and approvals. - Historical `gpt3-agent`: no permissions, sandbox, path guard, audit, or tests. -- Media/UI/status repositories: outside Graycode's code-intelligence boundary. +- Media/UI/status repositories: outside Hawk's code-intelligence boundary. - CommandCodeAI branding, proprietary model claims, and undocumented services. ## Verification Plan -1. Run formatting and static checks on all changed Graycode Go files. +1. Run formatting and static checks on all changed Hawk Go files. 2. Run focused parser, validator, taste, engine, and command tests. -3. Run the full Graycode test suite and vet. +3. Run the full Hawk test suite and vet. 4. Repeat the focused and full checks independently. 5. Merlin the final diff, worktree, and sibling-repository status. -The GraycodeRouter repository remains a separate repository change and must be published -through its own feature branch and PR before updating Graycode's module pin. +The Eyrie repository remains a separate repository change and must be published +through its own feature branch and PR before updating Hawk's module pin. diff --git a/docs/plans/competitive-gap-01-docker-onboarding.md b/docs/plans/competitive-gap-01-docker-onboarding.md index 1a4cc5607..2c3f464eb 100644 --- a/docs/plans/competitive-gap-01-docker-onboarding.md +++ b/docs/plans/competitive-gap-01-docker-onboarding.md @@ -7,11 +7,11 @@ Constraint (non-negotiable): mandatory Docker isolation, fail-closed, never host See `docs/SECURITY-DEVELOPER.md:71-73` and `internal/sandbox/container.go:72-74`. This plan adds zero execution paths. It only improves messaging/docs. -## Existing graycode capabilities (verified) +## Existing hawk capabilities (verified) -- `graycode path` / `preflight` / `doctor` / `ecosystem` commands (`README.md:346-356`). -- `scripts/verify-developer-path.sh` (`make path`) and `scripts/smoke-graycode.sh` (`make smoke`). -- Sandbox image auto-pull (`graycodeai/graycode-sandbox`) with local Dockerfile build fallback (`docs/SECURITY-DEVELOPER.md:75-79`). +- `hawk path` / `preflight` / `doctor` / `ecosystem` commands (`README.md:346-356`). +- `scripts/verify-developer-path.sh` (`make path`) and `scripts/smoke-hawk.sh` (`make smoke`). +- Sandbox image auto-pull (`graycodeai/hawk-sandbox`) with local Dockerfile build fallback (`docs/SECURITY-DEVELOPER.md:75-79`). ## Decision diff --git a/docs/plans/competitive-gap-02-share-multisession.md b/docs/plans/competitive-gap-02-share-multisession.md index 5779ed73f..1e452e5e0 100644 --- a/docs/plans/competitive-gap-02-share-multisession.md +++ b/docs/plans/competitive-gap-02-share-multisession.md @@ -6,9 +6,9 @@ Source: field comparison vs OpenCode share-links/multi-session, herdr multiplexe Constraint: developer-first, local by default, no cloud account required (per `.github/ISSUE_TEMPLATE/feature_request.yml:60-63`). -## Existing graycode capabilities (verified) +## Existing hawk capabilities (verified) -- `GenerateShareLink` returns local deeplink `graycode://share/` (`internal/session/export.go:801-819`); deterministic, no hosted URL. +- `GenerateShareLink` returns local deeplink `hawk://share/` (`internal/session/export.go:801-819`); deterministic, no hosted URL. - Session export (`session_export.go`), mission graph export (`cmd/execution_graph.go`, `mission-graph.json`), daemon sessions, `mission` worktrees. - Completion list includes `exec`, `daemon`, `mission`, `sessions`, `tools`, `skills` (`cmd/completions_test.go:50`). @@ -21,7 +21,7 @@ Do not adopt: hosted share URLs, cloud account,/Desktop app. ## Priority model - P0: `sessions` list shows id/model/updated + export path; document `graph export` bundle as the share unit. -- P1: TUI session picker surfaces the `graycode://share/` deeplink + export file path for copy-paste. +- P1: TUI session picker surfaces the `hawk://share/` deeplink + export file path for copy-paste. - P2: Mission watchdog read-only overview (already in HUD panel) exposed via `mission --dry-run`/status; no new runtime. ## Steps diff --git a/docs/plans/competitive-gap-03-kitty-graphics.md b/docs/plans/competitive-gap-03-kitty-graphics.md index ea9b3f853..ac8f92dc3 100644 --- a/docs/plans/competitive-gap-03-kitty-graphics.md +++ b/docs/plans/competitive-gap-03-kitty-graphics.md @@ -3,7 +3,7 @@ Status: Implemented (2026-09-09) Source: `https://github.com/kovidgoyal/kitty` (GPL-3.0; protocol only, no code copy), Ghostty compat; extends `docs/plans/pi-adoption-plan.md:25` (already proposed there — this file scopes the TUI work, it does not re-propose). -## Existing graycode capabilities (verified) +## Existing hawk capabilities (verified) - Vision input path: `internal/engine/vision.go`; image command: `cmd/image.go`. - Terminal detection covers kitty/ghostty/wezterm/alacritty names (`internal/ui/icons/detect_test.go:56`). diff --git a/docs/plans/competitive-gap-04-published-benchmarks.md b/docs/plans/competitive-gap-04-published-benchmarks.md index 1c68dc856..aafb6bbf7 100644 --- a/docs/plans/competitive-gap-04-published-benchmarks.md +++ b/docs/plans/competitive-gap-04-published-benchmarks.md @@ -5,7 +5,7 @@ Source: field comparison vs ripgrep/fzf/alacritty (numbers in README), Aider Rep Constraint: docs-only. No new benchmark framework; infra already exists. -## Existing graycode capabilities (verified) +## Existing hawk capabilities (verified) - `make bench` (`go test -bench=. -benchmem -count=3`) in `Makefile:100-101`. - `internal/bench/suite.go` (eco suite runner + report formatter). @@ -21,7 +21,7 @@ Do not adopt: new eval harness, SWE-bench claims, provider-funded comparisons. ## Priority model - P0: fixed command + env (`make bench` subset: session save/load 100/1000, repomap size/tokens) recorded with machine + commit. -- P1: `docs/BENCHMARKS.md` table (TUI-independent, no latency theater): session save/load, repomap tokens, tool-catalog size before/after `GRAYCODE_TOOL_SHRINK=1`. +- P1: `docs/BENCHMARKS.md` table (TUI-independent, no latency theater): session save/load, repomap tokens, tool-catalog size before/after `HAWK_TOOL_SHRINK=1`. - P2: CI artifact (optional): nightly `bench` JSON upload; never gate releases on it. ## Steps diff --git a/docs/plans/competitive-gap-05-backend-wiring.md b/docs/plans/competitive-gap-05-backend-wiring.md index 3d620e058..d5fd5025a 100644 --- a/docs/plans/competitive-gap-05-backend-wiring.md +++ b/docs/plans/competitive-gap-05-backend-wiring.md @@ -5,11 +5,11 @@ Source: field comparison vs Qwen `computer_use`, Codex browser/screenshot, Goose Constraints (non-negotiable): -- Provider ownership lives in `../graycode-router`; graycode consumes only the stable engine facade (`AGENTS.md`, `docs/SECURITY-DEVELOPER.md:51-56`). -- Boundary guards must stay green: `make boundaries` (incl. `graycode-router-client-guard`, `graycode-router-engine-guard`). +- Provider ownership lives in `../eyrie`; hawk consumes only the stable engine facade (`AGENTS.md`, `docs/SECURITY-DEVELOPER.md:51-56`). +- Boundary guards must stay green: `make boundaries` (incl. `eyrie-client-guard`, `eyrie-engine-guard`). - Tools fail safe today with explicit errors when unwired — preserve that behavior when disabled. -## Existing graycode capabilities (verified) +## Existing hawk capabilities (verified) - `ComputerUseTool` reports "no computer backend installed" without `SetComputerBackend` (`internal/tool/computer_use.go:67-94`). - `GenerateMediaTool` backend nil by default via `SetMediaEngine` (`internal/tool/media_generation.go:69-71`). @@ -20,18 +20,18 @@ Constraints (non-negotiable): Adopt: opt-in host wiring through the router facade, env-gated, off by default. -Do not adopt: direct `graycode-router/client` production imports, new secrets paths, always-on media/computer-use. +Do not adopt: direct `eyrie/client` production imports, new secrets paths, always-on media/computer-use. ## Priority model - P0: design note mapping each tool seam → facade method (media/image, STT/audio, computer backend) with guard-safe import path. -- P1: env-gated wiring (e.g. `GRAYCODE_MEDIA=1`) + docs; unwired default error text unchanged. -- P2: `graycode doctor` reports backend status (wired/unwired) without leaking secrets. +- P1: env-gated wiring (e.g. `HAWK_MEDIA=1`) + docs; unwired default error text unchanged. +- P2: `hawk doctor` reports backend status (wired/unwired) without leaking secrets. ## Steps -1. Confirm facade methods exist in sibling `../graycode-router/engine`; if missing, file the change there first (router repo owns providers). -2. Implement wiring in graycode behind env gates; keep `Set*` seams for tests. +1. Confirm facade methods exist in sibling `../eyrie/engine`; if missing, file the change there first (router repo owns providers). +2. Implement wiring in hawk behind env gates; keep `Set*` seams for tests. 3. Run `make boundaries` + `make vet` + targeted `go test ./internal/tool/ -run 'TestComputer|TestMedia'`. ## Verification diff --git a/docs/plans/dsh-harness-gap-port-plan.md b/docs/plans/dsh-harness-gap-port-plan.md index 9d2ec67ed..60bf5b5a0 100644 --- a/docs/plans/dsh-harness-gap-port-plan.md +++ b/docs/plans/dsh-harness-gap-port-plan.md @@ -24,7 +24,7 @@ Same as the parent plan: ## Gap inventory (from the comparison audit) -| # | DSH package(s) | Graycode gap | Port status | +| # | DSH package(s) | Hawk gap | Port status | | --- | --- | --- | --- | | 13 | `identity/anonymous-user-id` | no per-home anonymous telemetry identity | **Delivered** | | 14 | `web/web-search-deepseek`, `-exa`, `-perplexity` | web search only has Brave/SearXNG/DDG | **Delivered** | @@ -37,7 +37,7 @@ Same as the parent plan: | 21 | `session/session-persistence-sqlite`, `session-query/*sqlite`, `storage/storage-sqlite` | JSONL-only session persistence | deferred | | 22 | `e2b/*` | no cloud Linux sandbox | deferred | | 23 | `code-runtime/*` | no sandboxed model-written program execution | deferred | -| 24 | `sdk/client`, `sdk/protocol`, `sdk/server`, `python/sdk` | graycode exposes ACP server instead of DSH JSON-RPC SDK | deferred (keep ACP) | +| 24 | `sdk/client`, `sdk/protocol`, `sdk/server`, `python/sdk` | hawk exposes ACP server instead of DSH JSON-RPC SDK | deferred (keep ACP) | | 25 | `terminal/*`, `tool-terminal` | no PTY terminal tool | deferred | | 26 | `client/*`, `web/*`, `website`, `bundle/*-web-app` | web UI layer | out of scope (CLI/TUI) | @@ -46,7 +46,7 @@ Same as the parent plan: Port of DSH `identity/anonymous-user-id/src/index.ts`. - `identity.Identity` — per-harness-home anonymous user id. -- Resolved once per process (memoized): `$GRAYCODE_HOME`-style home (`~/.graycode`), +- Resolved once per process (memoized): `$HAWK_HOME`-style home (`~/.hawk`), `.anonymous-user-id` file containing a bare random UUID. - Never derived from hostname, network address, git remote, or env. - Sync read/write; deleting the file mints a fresh identity. @@ -98,7 +98,7 @@ Port of DSH `attachment/attachment` version-one image path. `attachment.Limits` (maxImageBytes, maxImagesPerMessage, maxMessageImageBytes, maxImagePixels, mediaTypes), `attachment.SaveImage`, `attachment.Stored` (ref + data), `attachment.Store` interface + - filesystem `Store` under the graycode home data dir. + filesystem `Store` under the hawk home data dir. - Validation: declared media type checked against decoded bytes; byte and pixel limits enforced; duplicate writes rejected; ID opaque. @@ -118,7 +118,7 @@ Port of DSH `session/session-projection-cache` semantics. ## Phase 18 — OTLP log-record export (`internal/observability/otellog`) Port of DSH `session/session-telemetry-otel` backend semantics (the capture -coordinator stays out of scope — graycode has no Cordis bus; records reach the +coordinator stays out of scope — hawk has no Cordis bus; records reach the backend via `Emit`/`EmitFeedback`). - `Record{Channel, Time, Severity, Attributes, Body}` / `Sink` seam — @@ -139,8 +139,8 @@ backend via `Emit`/`EmitFeedback`). - Shutdown races the DSH deadline (default 3s); exporter-shutdown goroutine stays observed after the deadline; `Emit` is a non-blocking enqueue. - `DefaultConfig()` mirrors oteltrace env conventions - (`GRAYCODE_ENABLE_TELEMETRY=1` + `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`); - wired in `cmd/graycode/main.go` and `cmd/daemon.go`. + (`HAWK_ENABLE_TELEMETRY=1` + `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`); + wired in `cmd/hawk/main.go` and `cmd/daemon.go`. - Tests: mode resolution, load-time validation, severity mapping, full/ops/ ledger emission (in-memory exporter), feedback-only direct-drop, disabled-drop, idempotent shutdown, value conversion, attribute filtering. @@ -148,7 +148,7 @@ backend via `Emit`/`EmitFeedback`). ## Gates Each phase: `gofmt`/`go vet`/`go test` on the touched packages, then `make lint` -and `graycode verify` — run **twice** (the second pass re-runs the full touched +and `hawk verify` — run **twice** (the second pass re-runs the full touched suite after any fixes). No direct commits to `main`. ### Verification record (2026-08-17, branch `feat/dsh-harness-port-p0-eventlog`) @@ -162,14 +162,14 @@ Pass 1: - `make lint`: **0 issues** (fixed pre-existing errcheck `check-type-assertions` debt + one unused helper in `internal/session/preparations.go`, and an S1000 single-case select in `internal/jobs/jobs.go`) -- `graycode verify`: exit 0 +- `hawk verify`: exit 0 - `scripts/check-internal-layer-imports.sh`: passed Pass 2 (after lint fixes): - `gofmt -l`: clean; `go vet`: clean; `go build ./...`: clean - `go test -race` (identity, jobs, attachment): ok; `go test` (session, tool): ok -- `make lint`: 0 issues; `graycode verify`: exit 0 +- `make lint`: 0 issues; `hawk verify`: exit 0 New dependency: `golang.org/x/image v0.45.0` (direct) — webp decode validation for `internal/attachment` (stdlib has no webp decoder). @@ -178,18 +178,18 @@ for `internal/attachment` (stdlib has no webp decoder). Pass 1: -- `gofmt -l` (cmd/graycode/main.go, cmd/daemon.go, internal/observability/otellog): clean +- `gofmt -l` (cmd/hawk/main.go, cmd/daemon.go, internal/observability/otellog): clean - `go vet` (otellog, cmd): clean - `go test -race` (otellog): ok; `go build ./...`: ok - `make lint`: **0 issues** -- `graycode verify`: exit 0 +- `hawk verify`: exit 0 - `scripts/check-internal-layer-imports.sh`: passed Pass 2 (uncached re-run, no fixes were needed): - `gofmt -l`: clean; `go vet`: clean; `go build ./...`: clean - `go test -count=1 -race` (otellog): ok -- `make lint`: 0 issues; `graycode verify`: exit 0 +- `make lint`: 0 issues; `hawk verify`: exit 0 New dependencies (all direct): `go.opentelemetry.io/otel/log v0.20.0`, `go.opentelemetry.io/otel/sdk/log v0.20.0`, diff --git a/docs/plans/dsh-harness-port-plan.md b/docs/plans/dsh-harness-port-plan.md index 9450971a0..5a1c0d47f 100644 --- a/docs/plans/dsh-harness-port-plan.md +++ b/docs/plans/dsh-harness-port-plan.md @@ -1,4 +1,4 @@ -# Adopt durable-design ideas from deepseek-harness in graycode +# Adopt durable-design ideas from deepseek-harness in hawk Status: Phase 0 scaffolded on `feat/dsh-harness-port-p0-eventlog`. @@ -11,14 +11,14 @@ Status: Phase 0 scaffolded on `feat/dsh-harness-port-p0-eventlog`. ## Reference sources -| dsh file | Extract | Maps to graycode | +| dsh file | Extract | Maps to hawk | | --- | --- | --- | | `packages/core/session/src/index.ts` | append-only `SessionEvent` log + `session/event` emit | `internal/eventlog` | | `docs/architecture.md` "Turn flow" | `deriveMessages`; "model-visible ⟺ logged" | `Session.Persistence()` projection | | `packages/core/tools/src/index.ts` | `tools/pre-execute` / `tools/execute` / `tools/post-execute` waterfall | `internal/tool/interceptor.go` (Phase 1) | | `packages/interaction/user-approval/src/index.ts:30` | `approval/request` waterfall, fail-closed | `internal/engine/approval_gate.go` (Phase 1) | | `docs/cordis-primer.md` "Waterfall Semantics" | `next()` delegate / short-circuit | interceptor contract | -| `docs/capability-seams.md` | owner / impl / consumer roles | `docs/architecture/graycode-capability-seams.md` (Phase 2) | +| `docs/capability-seams.md` | owner / impl / consumer roles | `docs/architecture/hawk-capability-seams.md` (Phase 2) | ## Phase 0 — Event-sourced session log + "model-visible ⟺ logged" @@ -93,7 +93,7 @@ concrete tool, carry it through `toolExecResult` in a later PR. ## Phase 2 — Seam discipline (docs + disposers) -- `docs/architecture/graycode-capability-seams.md`: owner / impl / consumer table. +- `docs/architecture/hawk-capability-seams.md`: owner / impl / consumer table. - Registrations return disposers (tool registry, hooks, MCP client). - "Where new behavior goes" table. @@ -114,16 +114,16 @@ concrete tool, carry it through `toolExecResult` in a later PR. ## Upstream parity matrix (honest status) -These numbers compare Graycode against the real deepseek-harness source, not the +These numbers compare Hawk against the real deepseek-harness source, not the plan's own scoped promises. The clone inspected was `deepseek-ai/deepseek-harness` at `dsh-0.1.0-rc.7`. -Graycode is a **deliberate subset**. It ports the session-log spine, the fail-closed +Hawk is a **deliberate subset**. It ports the session-log spine, the fail-closed approval waterfall, and the interceptor/disposer seams; it does not port the -product-wide plugin catalogue. Measured head-to-head, Graycode has roughly **20–30%** +product-wide plugin catalogue. Measured head-to-head, Hawk has roughly **20–30%** of upstream by feature surface, and roughly **80–90%** of the plan's stated scope. -| Surface | DSH | Graycode today | Gap | +| Surface | DSH | Hawk today | Gap | | --- | --- | --- | --- | | Known event types | 44 (dsh known-event-types.ts) | **44** (internal/eventlog/event.go) | **Closed** — all 26 new DSH event types added with typed payloads, wire decode, and Append helpers | | Session spine code | ~3,156 non-test TS lines | ~868 non-test Go lines | Focused core, not full parity | @@ -234,7 +234,7 @@ surface operations, ignorable markers, format version enforcement, and session header parity. The port is functionally complete. Remaining optional depth: - ~~DSH `surface.ts` `SurfaceManager`/`SurfaceReplacePlan` (live correction protocol — replacement operations).~~ Delivered: `internal/eventlog/surface.go` — `FoldSurface` (complete replay → surface nodes + replacement history) and an incremental `SurfaceManager` (bound to a `*Log`, with `Nodes`/`ReplaceGeneration`/`ValidateNext` atomic pre-flight), mirroring DSH's surface provenance, replacement-range, contiguity, and tool-result-rewrite invariants. - ~~Zstd compression in persistence (DSH `session-persistence-jsonl/src/zstd.ts`).~~ Delivered: `internal/eventlog/zstdz/zstd.go` + `internal/session/session.go` (`.jsonl.zstd` saves) — see Phase 9. -- DSH `packages/llm/llm/src/assembler.ts` (response normalization — covered by GraycodeRouter facade). +- DSH `packages/llm/llm/src/assembler.ts` (response normalization — covered by Eyrie facade). This matrix is the honest anchor: the skeleton is ported; the deep fidelity is the actual remaining work. @@ -429,4 +429,4 @@ UI additions (Safari support, outside pointer handling) or ACP content admission ## Gates -Each phase: `make ci` + `graycode verify`; no direct commits to `main`. +Each phase: `make ci` + `hawk verify`; no direct commits to `main`. diff --git a/docs/plans/dsh-harness-rfc-port-plan.md b/docs/plans/dsh-harness-rfc-port-plan.md index 73bc8b8e5..eb8e7858b 100644 --- a/docs/plans/dsh-harness-rfc-port-plan.md +++ b/docs/plans/dsh-harness-rfc-port-plan.md @@ -1,4 +1,4 @@ -# Wave 2 — high-value deepseek-harness RFC ports for graycode +# Wave 2 — high-value deepseek-harness RFC ports for hawk Status: **Proposed** (RFC specs only; no code on this branch yet). Each phase below is a self-contained feature-branch spec in the style of the Wave 1 docs, ready to @@ -31,7 +31,7 @@ Reference source: `deepseek-ai/deepseek-harness` at `dsh-v0.1.0-rc.7` ## Wave 2 gap inventory -| # | DSH source | Graycode gap | Status | +| # | DSH source | Hawk gap | Status | | --- | --- | --- | --- | | 2.1 | `guard/timeout-policy` | no per-tool declared timeout enforced at dispatch | **implemented** on `feat/dsh-harness-rfc-2.1-timeout-policy` | | 2.2 | `compaction/compaction` tool-pairing helpers | compact strategies can cut across open tool call/result pairs | proposed | @@ -59,7 +59,7 @@ Port of DSH `guard/timeout-policy` (`dsh-tool-call-timeout-policy`) + the the tool's own declaration, not from a policy table. - `tool.Tool` gains an optional `TimeoutProvider` interface (`Timeout() - time.Duration`, 0 = no declaration) — the graycode-native form of DSH's + time.Duration`, 0 = no declaration) — the hawk-native form of DSH's `ToolDefinition.timeoutMs`, consistent with the existing optional-provider pattern (`RiskLevelProvider`, `RetryPolicyProvider`, `SchemaProvider`). `tool.TimeoutOf(t Tool) time.Duration` reads it. @@ -89,7 +89,7 @@ TOOL_TIMEOUT). ## Phase 2.2 — Compaction tool-pairing boundaries (`internal/engine/compact` + `internal/eventlog`) Port of DSH `compaction/compaction` surface contract + the -`toolPairingBalancedBefore` / `toolPairingBalancedAfter` helpers. Graycode's +`toolPairingBalancedBefore` / `toolPairingBalancedAfter` helpers. Hawk's `internal/engine/compact/` already does LLM summarization (`strategy.go`, `micro.go`, `session_memory.go`) and `internal/eventlog` already journals `tool.call` / `tool.result` / `session.compacted` (Wave 1 Phase 0). The @@ -122,7 +122,7 @@ detection and adoption on load; deterministic replay after replace. Port of DSH `sandbox/sandbox-policy` semantics (`SandboxMode` read-only / workspace-write / danger-full-access, `ctx.sandboxPolicy.resolve()`, `setSandboxMode`, the `sandbox:policy` context -contribution). Graycode already has the vocabulary — `internal/sandbox/mode.go` +contribution). Hawk already has the vocabulary — `internal/sandbox/mode.go` `Mode` strict / workspace / off — so this phase ports the **policy resolution and durability** around it. @@ -143,7 +143,7 @@ and durability** around it. durable context message on the first request and on each effective policy change; unchanged requests add nothing. `workspace` carries only the canonical workspace path (no host-dependent temp paths — summarize them). Rendered by - the prompt assembler exactly like DSH's three templates, but with graycode's + the prompt assembler exactly like DSH's three templates, but with hawk's vocabulary: - `strict` — read-only; do not refuse a required modification from this policy alone; try the tool and follow denial/escalation guidance. @@ -224,7 +224,7 @@ discovery; runtime provider registration/disposal. Revives gap `#21` in **query-only** scope (no migration of the JSONL store; the JSONL file stays the source of truth). Port of DSH -`session-query/session-query-sqlite` semantics over graycode's session logs. +`session-query/session-query-sqlite` semantics over hawk's session logs. - New `internal/sessionquery` package: - FTS5 index (external content or contentless) over the session store, @@ -272,9 +272,9 @@ resume of overdue work, no external channel, create/list/delete round-trip. ## Phase 2.8 — ACP client + external-agent subagent providers (`internal/acp` + `internal/multiagent`) Port of DSH `acp/` (client side) + `subagent/subagent-acp`, `subagent-claude-code`, -`subagent-codex`. Graycode has the ACP **server** (Wave 1 `internal/acp/server.go` — +`subagent-codex`. Hawk has the ACP **server** (Wave 1 `internal/acp/server.go` — initialize, session/new, session/prompt, streamed updates, -session/request_permission); this phase adds the client so graycode's mission mode +session/request_permission); this phase adds the client so hawk's mission mode can **delegate to** other ACP agents, Claude Code, and Codex. - `internal/acp/client.go`: @@ -317,7 +317,7 @@ tool calls and supports interactive stdin. - Local backend over the sandbox executor: PTY allocation with zero-CGO on Linux (`syscall`-based pty or a maintained pure-Go pty; verify `github.com/creack/pty`'s CGO status before adoption — zero-CGO is a hard - graycode constraint); Windows via `ConPTY` later, outside this phase. + hawk constraint); Windows via `ConPTY` later, outside this phase. - Sandbox enforcement: the backend applies the resolved Phase 2.3 policy (mode + workspace root) before exec, fail-closed. - Bounded reads: per-read byte cap; no unbounded buffering in model-facing @@ -368,7 +368,7 @@ pre-publication failure. Port of DSH `sandbox/sandbox-windows-acl`. `internal/sandbox/landlock_other.go` is currently a stub on non-Linux (`Apply` always errors; `Available` false), so -on Windows graycode is Docker-only and fails closed without Docker. This phase gives +on Windows hawk is Docker-only and fails closed without Docker. This phase gives Windows a native, unprivileged confinement backend matching the Landlock philosophy already stated in `landlock.go` ("works without root, without Docker, without external tools"). @@ -398,7 +398,7 @@ Port of DSH `lsp/lsp-stdio` operational semantics. `internal/lsp/` currently has `client.go`/`manager.go`/`config.go`; this phase audits and aligns the client lifecycle with DSH's: - **Transient-open sequence**: per query — resolve and byte-bound the source - through graycode's fs, `textDocument/didOpen` (version 1, full text), the + through hawk's fs, `textDocument/didOpen` (version 1, full text), the requested request, then `textDocument/didClose` in `finally`. No document cache, no LRU, no `didChange`; a failed/canceled `didOpen` write terminates the server instance before the pool can reuse it. @@ -428,7 +428,7 @@ scrubbing (KEY/PASSWORD/SECRET/TOKEN absent from child env); processId null. ## Gates (each phase) `gofmt`/`go vet`/`go test -race` on the touched packages, then `make lint` and -`graycode verify`, run **twice** (second pass re-runs the full touched suite after +`hawk verify`, run **twice** (second pass re-runs the full touched suite after any fixes). `scripts/check-internal-layer-imports.sh` must stay green. No direct commits to `main` — each phase is its own feature branch (`feat/dsh-harness-rfc-`), opened from `main`, with a PR and green CI. diff --git a/docs/plans/engine-refactor-plan.md b/docs/plans/engine-refactor-plan.md index 47d94ac37..f74dec386 100644 --- a/docs/plans/engine-refactor-plan.md +++ b/docs/plans/engine-refactor-plan.md @@ -1,20 +1,20 @@ -# graycode/engine sub-package split — analysis and migration plan +# hawk/engine sub-package split — analysis and migration plan > Status: **analysis only**. No code is moved by this document. The actual -> split is multi-PR work that should land incrementally to keep graycode's +> split is multi-PR work that should land incrementally to keep hawk's > build green at every step. ## The problem ``` -graycode/engine/ +hawk/engine/ ├── *.go 161 source files, 66,682 lines └── *_test.go 141 test files, 65,907 lines ───── total: ~133K lines, 302 files, ONE package ``` For comparison: the entire `kubernetes/kubectl` (a non-trivial CLI) is -~110K lines split across **dozens** of internal packages. graycode's engine +~110K lines split across **dozens** of internal packages. hawk's engine is bigger than that and lives in a single `package engine`. Concrete pain that creates today: @@ -40,7 +40,7 @@ splitting into ~15 sub-packages. The line counts are file-name-based estimates; the real numbers will shift during the split. ``` -graycode/engine/ +hawk/engine/ ├── engine.go # top-level Engine type, public API only ├── lifecycle.go # session start/stop, hooks, graceful shutdown ├── stream.go # the main response stream loop (large; consider further split) @@ -193,18 +193,18 @@ code from earlier experiments). ## Migration strategy -The split is high-risk because it touches every other package in graycode -that imports `engine.Foo`. To keep graycode green at every commit: +The split is high-risk because it touches every other package in hawk +that imports `engine.Foo`. To keep hawk green at every commit: 1. **Stage 1 — alias-only.** For each proposed sub-package, create `engine//.go` containing only re-exports: ```go package compact - import "github.com/GrayCodeAI/graycode-cli/engine" + import "github.com/GrayCodeAI/hawk/engine" type Strategy = engine.CompactStrategy var Default = engine.DefaultCompactStrategy ``` - Graycode's external callers can start migrating to the new import paths. + Hawk's external callers can start migrating to the new import paths. Old code keeps working unchanged. **Land this first.** 2. **Stage 2 — move bodies.** For one sub-package at a time: @@ -223,7 +223,7 @@ that imports `engine.Foo`. To keep graycode green at every commit: coordinator package only. This approach scales: each PR is small, reviewable, and individually -revertible. No "big bang" merge that paralyses graycode for a week. +revertible. No "big bang" merge that paralyses hawk for a week. ## Estimated effort @@ -266,7 +266,7 @@ The `compact/` sub-package is the cleanest extraction candidate: | `git/` | `engine/git/aliases.go` | 9 types, 4 funcs | | `prompt/` | `engine/prompt/aliases.go` | 6 types, 3 funcs | -New code should import `github.com/GrayCodeAI/graycode-cli/engine/` +New code should import `github.com/GrayCodeAI/hawk/engine/` instead of reaching into `engine` for these names. The remaining 17 clusters follow the same pattern: @@ -274,7 +274,7 @@ clusters follow the same pattern: // engine//aliases.go package -import "github.com/GrayCodeAI/graycode-cli/engine" +import "github.com/GrayCodeAI/hawk/engine" // Re-export the cluster's public symbols from engine as type and var // aliases. Implementation stays in engine until Stage 2 of this plan. diff --git a/docs/plans/fix-critical-and-high-review.md b/docs/plans/fix-critical-and-high-review.md index 4e145e4cd..dbf828021 100644 --- a/docs/plans/fix-critical-and-high-review.md +++ b/docs/plans/fix-critical-and-high-review.md @@ -1,7 +1,7 @@ -# Plan: Fix Critical + High-Impact Review Findings — graycode +# Plan: Fix Critical + High-Impact Review Findings — hawk > Branch: `fix/critical-and-high-review-2026-06` -> PR: +> PR: > Status: **✅ COMPLETE — all 9 items + extensive follow-up committed.** > Constraint: **no new go.mod / go.sum dependencies** for any item in this plan. @@ -77,12 +77,12 @@ These were documented in the original plan as out-of-scope-for-this-PR. They rem ## Context -A deep code review of `graycode-router` and `graycode` (companion plan at -`../graycode-router/docs/plans/fix-critical-and-high-review.md`) surfaced 7 critical -and 9 high items. This plan covers **all graycode items** (C3, C4, C5, H5, +A deep code review of `eyrie` and `hawk` (companion plan at +`../eyrie/docs/plans/fix-critical-and-high-review.md`) surfaced 7 critical +and 9 high items. This plan covers **all hawk items** (C3, C4, C5, H5, H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. -## Scope (graycode) +## Scope (hawk) | ID | Severity | Title | File(s) | Effort | |----|----------|-------|---------|--------| @@ -97,9 +97,9 @@ H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. ## Out of scope (deferred to next plan) -- H10 from graycode-router: `//nolint:errcheck` on type-assertion that can panic. +- H10 from eyrie: `//nolint:errcheck` on type-assertion that can panic. - M1–M20 medium items. -- L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.graycode/` leaked state). +- L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.hawk/` leaked state). - `internal/intelligence/repomap/` documentation (large, separate effort). - Anything that requires a new dependency. @@ -127,7 +127,7 @@ PRs can be merged individually; the branch is a namespace. ## PR 1 — Surface silent migration error (C4) **What**: `cmd/root.go:114` calls `MigrateProviderSecrets()` and discards -the error. If migration fails, secrets may remain in `~/.graycode/.env` while +the error. If migration fails, secrets may remain in `~/.hawk/.env` while the agent is told to ignore that file. **Fix**: @@ -236,7 +236,7 @@ core. Mitigation: keep both code paths behind a feature flag for one release; metric for "wait latency" before/after. **Rollback**: feature flag. If regressions appear, set -`GRAYCODE_MULTIAGENT_POLLING=1` to revert. +`HAWK_MULTIAGENT_POLLING=1` to revert. --- @@ -360,7 +360,7 @@ legitimate-input fix, not a security regression. ## PR 7 — Sandbox default-deny (H9) -**Bug**: `internal/sandbox/seatbelt.go:70-108` `DefaultGraycodePolicy` defaults +**Bug**: `internal/sandbox/seatbelt.go:70-108` `DefaultHawkPolicy` defaults to `AllowWrite: true` and `AllowProcess: true`. A sandboxed bash can write and spawn processes out of the box. @@ -464,7 +464,7 @@ behavioral changes, only structural. ```bash go mod verify -go build ./cmd/graycode +go build ./cmd/hawk go test -race -count=1 -shuffle=on ./... go vet ./... golangci-lint run @@ -492,10 +492,10 @@ Coverage target: maintained at 60%+ (CI gate). ## Cross-repo coordination -- **graycode-router PR 4 (C2 — Vertex fix)** and **graycode PR 4 (H6 — Session +- **eyrie PR 4 (C2 — Vertex fix)** and **hawk PR 4 (H6 — Session decomposition)** are independent. -- **graycode-router PR 8 (H4 — GraycodeRouterError)** is a prerequisite for any future - graycode-side `errors.As(err, &graycode-routerErr)` use (currently none). No +- **eyrie PR 8 (H4 — EyrieError)** is a prerequisite for any future + hawk-side `errors.As(err, &eyrieErr)` use (currently none). No ordering dependency. - The two repos' branches are independent and can be merged in any order. diff --git a/docs/plans/fx-adoption-plan.md b/docs/plans/fx-adoption-plan.md index 422a40b6c..7bd70b9bd 100644 --- a/docs/plans/fx-adoption-plan.md +++ b/docs/plans/fx-adoption-plan.md @@ -1,18 +1,18 @@ # Vercel fx Adoption Plan -Status: Implemented in Graycode's native Go architecture +Status: Implemented in Hawk's native Go architecture Source: `https://github.com/vercel-labs/fx` This plan records the useful ideas identified while comparing Vercel Labs' -`fx` Unix-like coding agent with Graycode and its independent ecosystem repositories. It is an -adoption plan, not a code-porting plan. Graycode should reimplement compatible +`fx` Unix-like coding agent with Hawk and its independent ecosystem repositories. It is an +adoption plan, not a code-porting plan. Hawk should reimplement compatible behavior in Go and preserve its existing provider, memory, review, audit, session, and security boundaries. ## Executive Decision -Graycode should adopt the following `fx` ideas: +Hawk should adopt the following `fx` ideas: 1. Stable identities and operational commands for persisted permission rules. 2. A unified machine-readable runtime status snapshot. @@ -21,21 +21,21 @@ Graycode should adopt the following `fx` ideas: user authority. 5. Compatibility aliases for common permission modes in CLI and ACP surfaces. 6. Stronger subagent lifecycle and permission observability. -7. A narrow embeddable host API over Graycode's existing daemon and ACP surfaces. +7. A narrow embeddable host API over Hawk's existing daemon and ACP surfaces. -Graycode should not copy `fx` source code, replace its Go runtime with Zig, create a +Hawk should not copy `fx` source code, replace its Go runtime with Zig, create a second permission system, or reduce its code-intelligence tool surface merely to match `fx`'s smaller binary. -## Existing Graycode Capabilities +## Existing Hawk Capabilities -The comparison found that Graycode already provides the foundation for most `fx` +The comparison found that Hawk already provides the foundation for most `fx` features: -| `fx` capability | Graycode implementation | Current decision | +| `fx` capability | Hawk implementation | Current decision | |---|---|---| -| Native single binary | Go static binary and cross-platform release builds | Keep Graycode implementation | -| Agent loop | `internal/engine` | Keep Graycode implementation | +| Native single binary | Go static binary and cross-platform release builds | Keep Hawk implementation | +| Agent loop | `internal/engine` | Keep Hawk implementation | | Permission and sandbox split | `internal/engine/safety`, `internal/sandbox` | Harden and document | | `ask`/automatic approval behavior | Autonomy profiles, governance, grants, hooks | Add compatibility aliases only | | Child agents | `internal/multiagent`, continuable children, cold resume | Add observability and configuration polish | @@ -45,7 +45,7 @@ features: | ACP | `internal/acp` | Extend status/config parity where useful | | Swift and replay | sibling `swift` (Swift), `internal/session/replay` | Add terminal-level tape capability | | Persistent memory | sibling `harrier` (Harrier) | Do not replace with flat JSON state | -| Provider runtime | sibling `graycode-router` | Do not add provider logic to Graycode | +| Provider runtime | sibling `eyrie` | Do not add provider logic to Hawk | ## Priority Model @@ -63,11 +63,11 @@ when their original command, path, or workspace has changed. ### Scope and ownership -- Primary implementation: Graycode `internal/permissions` and +- Primary implementation: Hawk `internal/permissions` and `internal/engine/safety`. - Shared contract changes, if needed: sibling `eagle/policy`. - User-facing commands: `cmd` and the existing slash-command surface. -- No changes to sibling `graycode-router` or `falcon`. +- No changes to sibling `eyrie` or `falcon`. ### Required behavior @@ -88,12 +88,12 @@ when their original command, path, or workspace has changed. ### Proposed interfaces ```text -graycode permissions list [--json] -graycode permissions revoke -graycode permissions reset [--scope user|project] +hawk permissions list [--json] +hawk permissions revoke +hawk permissions reset [--scope user|project] ``` -The exact command names may follow existing Graycode conventions, but the typed +The exact command names may follow existing Hawk conventions, but the typed operation should be shared by CLI, TUI, daemon, and ACP. ### Acceptance criteria @@ -155,7 +155,7 @@ the daemon, ACP clients, and the interactive UI. ### Ownership -- Snapshot contract: Graycode root or `eagle` if consumed cross-repo. +- Snapshot contract: Hawk root or `eagle` if consumed cross-repo. - Assembly: `internal/engine`, `internal/session`, `internal/mcp`, `internal/permissions`, `internal/multiagent`. - Rendering: CLI/TUI and daemon adapters. @@ -164,7 +164,7 @@ the daemon, ACP clients, and the interactive UI. ```text schema_version -graycode_version +hawk_version session_id workspace git_branch @@ -193,8 +193,8 @@ identifiers rather than raw secrets. ### Required interfaces ```text -graycode status -graycode status --json +hawk status +hawk status --json ``` The daemon and ACP should expose the same snapshot schema, with transport @@ -212,14 +212,14 @@ metadata kept outside the product snapshot. ### Goal -Add `fx`-style terminal byte and resize recording to complement Graycode's existing +Add `fx`-style terminal byte and resize recording to complement Hawk's existing agent-session swift and replay features. ### Ownership - Preferred home: sibling `swift` (Swift) if the capability is intended for reuse by other agents. -- Graycode integration: `internal/swift` or the TUI composition layer. +- Hawk integration: `internal/swift` or the TUI composition layer. - Do not put terminal tape parsing in the agent engine. ### Required behavior @@ -237,10 +237,10 @@ agent-session swift and replay features. ### Proposed interfaces ```text -graycode swift record --output -graycode swift replay -graycode swift replay --frames -graycode swift replay --json +hawk swift record --output +hawk swift replay +hawk swift replay --frames +hawk swift replay --json ``` Existing Swift session capture remains the source of prompts, tool calls, git @@ -260,35 +260,35 @@ ID. ### Goal -Expose familiar `fx` permission names without weakening Graycode's richer policy +Expose familiar `fx` permission names without weakening Hawk's richer policy engine. ### Mapping -| Compatibility name | Graycode behavior | +| Compatibility name | Hawk behavior | |---|---| | `ask` | Supervised or equivalent prompt-required policy | | `auto` | Automatic review/approval behavior where configured | | `yolo` | Explicit high-autonomy mode, still subject to governance, sandbox, and destructive-command hard blocks | -These are aliases or presentation-layer modes, not a replacement for Graycode's +These are aliases or presentation-layer modes, not a replacement for Hawk's autonomy tiers, governance ceiling, spec gates, and sandbox policy. ### Required behavior 1. Accept names in configuration, CLI, and ACP only where the surface supports them. -2. Display the effective Graycode tier and hard safety constraints. +2. Display the effective Hawk tier and hard safety constraints. 3. Never let `yolo` bypass governance, hard-deny rules, destructive-command blocks, or mandatory spec approval. -4. Persist the canonical Graycode representation, not an ambiguous alias. +4. Persist the canonical Hawk representation, not an ambiguous alias. 5. Add migration and invalid-value diagnostics. ## P1: Subagent Lifecycle and Authority Observability ### Goal -Adopt `fx`'s useful child-agent visibility while retaining Graycode's continuable +Adopt `fx`'s useful child-agent visibility while retaining Hawk's continuable child sessions, worktree isolation, model routing, and delegated policy inheritance. @@ -319,7 +319,7 @@ inheritance. ### Goal Improve discoverability and trust reporting using `fx`'s clear status model, -without changing Graycode's existing MCP and skills architecture. +without changing Hawk's existing MCP and skills architecture. ### Required behavior @@ -344,7 +344,7 @@ without changing Graycode's existing MCP and skills architecture. ### Goal Offer a supported embedding boundary inspired by `fx`'s ACP and WASM surfaces, -without committing Graycode to a WASM rewrite. +without committing Hawk to a WASM rewrite. ### First implementation @@ -417,12 +417,12 @@ make test-race make vet make lint make security -graycode verify +hawk verify ``` For sibling-repository changes, run that repository's own tests and boundary checks before -updating the Graycode pointer. Do not modify provider protocols or adapters in Graycode; -those belong in the GraycodeRouter repository. +updating the Hawk pointer. Do not modify provider protocols or adapters in Hawk; +those belong in the Eyrie repository. ## Delivery Sequence @@ -456,7 +456,7 @@ those belong in the GraycodeRouter repository. - Design and review the versioned swift artifact format in sibling `swift` (Swift). - Implement recording, replay, redaction, and deterministic golden tests. -- Integrate recording controls into Graycode without coupling tape parsing to the +- Integrate recording controls into Hawk without coupling tape parsing to the agent loop. ### Milestone 5: Host integration and trust UX @@ -469,18 +469,18 @@ those belong in the GraycodeRouter repository. ## Deliberately Rejected - Copying Zig implementation code from `vercel-labs/fx`. -- Replacing Graycode's Go runtime or independent ecosystem repositories. +- Replacing Hawk's Go runtime or independent ecosystem repositories. - Adding a second permission evaluator. - Replacing Harrier with flat JSON memory. - Replacing Swift session capture with terminal tapes. -- Removing Graycode's code intelligence, review, audit, token, or provider features +- Removing Hawk's code intelligence, review, audit, token, or provider features to target `fx`'s binary size. - Automatically executing repository-local MCP servers or hooks. - Adding WASM before daemon and ACP contracts are stable and proven. ## Success Criteria -The adoption is successful when Graycode can provide the operational clarity of +The adoption is successful when Hawk can provide the operational clarity of `fx` while retaining its stronger ecosystem architecture: - Every persistent permission rule can be listed and revoked by stable ID. @@ -488,7 +488,7 @@ The adoption is successful when Graycode can provide the operational clarity of - TUI failures can be reproduced from a credential-free terminal tape. - Project configuration cannot silently grant private authority. - Child-agent authority, lifecycle, model, and budget state are inspectable. -- Existing Graycode memory, provider, review, audit, MCP, ACP, and session behavior +- Existing Hawk memory, provider, review, audit, MCP, ACP, and session behavior remains intact. ## Implemented Scope @@ -504,7 +504,7 @@ The adoption is successful when Graycode can provide the operational clarity of continuable children, and cold resume retained as the authoritative runtime. The remaining items in this document are future refinements to the already -implemented surfaces, not missing baseline features. Graycode's subprocess LSP +implemented surfaces, not missing baseline features. Hawk's subprocess LSP manager remains a separate integration task: the current main `LSP` tool uses the codegraph path, while `internal/lsp` provides the richer language-server client and is not yet attached to the primary tool registry. diff --git a/docs/plans/goose-adoption-plan.md b/docs/plans/goose-adoption-plan.md index e0824262e..6744fb2dc 100644 --- a/docs/plans/goose-adoption-plan.md +++ b/docs/plans/goose-adoption-plan.md @@ -7,23 +7,23 @@ Linux Foundation / Agentic AI Foundation) ## Executive Decision -Goose's audit against graycode found that most of its runtime concepts already have -a native graycode implementation (providers via graycode-router, sessions, MCP, ACP, skills, -native sandboxing, permissions). The genuinely novel, graycode-relevant ideas are -adopted here in Go, without copying Rust code or weakening graycode's native +Goose's audit against hawk found that most of its runtime concepts already have +a native hawk implementation (providers via eyrie, sessions, MCP, ACP, skills, +native sandboxing, permissions). The genuinely novel, hawk-relevant ideas are +adopted here in Go, without copying Rust code or weakening hawk's native sandboxing model. -## Existing Graycode Capabilities +## Existing Hawk Capabilities -| Goose package/concept | Graycode implementation | Decision | +| Goose package/concept | Hawk implementation | Decision | |---|---|---| -| Provider abstraction (~36) | sibling `graycode-router` (28 built-in + 75+ live) | Keep graycode | -| Sessions (SQLite WAL) | `internal/session` JSONL+WAL+zstd + sibling `swift` (Swift) | Keep graycode | -| MCP (client+server) | `internal/mcp` + sibling `falcon` | Keep graycode | -| ACP | `internal/acp` | Keep graycode | -| Extensions/skills | `internal/plugin`, skills registry | Keep graycode | -| OS sandboxing | `internal/sandbox` seatbelt/landlock/seccomp/ACL | **graycode ahead** (goose has none) | -| OSV malware gate | `internal/permissions/osv_checker.go` (`CheckCommand`/`CheckPackage`) | Keep graycode | +| Provider abstraction (~36) | sibling `eyrie` (28 built-in + 75+ live) | Keep hawk | +| Sessions (SQLite WAL) | `internal/session` JSONL+WAL+zstd + sibling `swift` (Swift) | Keep hawk | +| MCP (client+server) | `internal/mcp` + sibling `falcon` | Keep hawk | +| ACP | `internal/acp` | Keep hawk | +| Extensions/skills | `internal/plugin`, skills registry | Keep hawk | +| OS sandboxing | `internal/sandbox` seatbelt/landlock/seccomp/ACL | **hawk ahead** (goose has none) | +| OSV malware gate | `internal/permissions/osv_checker.go` (`CheckCommand`/`CheckPackage`) | Keep hawk | | Hints / AGENTS.md | `internal/config` AGENTS.md loader | **Adopt** @file references + subdir hints | | Context compaction | sibling `shrike` (Shrike) + `internal/engine/compaction` | **Adopt** structured-summary retry ladder | | Extension env safety | none (only OSV gate) | **Adopt** disallowed-env-var filter | @@ -31,7 +31,7 @@ sandboxing model. ## Priority Model -- **P0:** Security-relevant, bounded, graycode-native adoptions. +- **P0:** Security-relevant, bounded, hawk-native adoptions. - **P1:** High-value product improvements. - **Defer:** Larger or cross-cutting changes needing an RFC. @@ -50,7 +50,7 @@ or MCP stdio processes by filtering dangerous environment overrides checker) — a `sanitizeEnv` / `SafeEnv` helper. - Consumers: `internal/mcp` stdio launch path and `internal/plugin` package execution. -- No changes to sibling `graycode-router`. +- No changes to sibling `eyrie`. ### Required behavior @@ -114,7 +114,7 @@ strict size/depth budgets — matching goose `hints/import_files.rs`. ### Goal -Upgrade graycode's context compaction to a structured summary with a progressive +Upgrade hawk's context compaction to a structured summary with a progressive tool-response-dropping retry ladder on overflow, and token-estimator-backed accounting, matching `goose-context-management`. @@ -142,18 +142,18 @@ accounting, matching `goose-context-management`. ## Deliberately Deferred -- Goose's SQLite session store (`usage_ledger`, token/cost schema): graycode's +- Goose's SQLite session store (`usage_ledger`, token/cost schema): hawk's JSONL/WAL + swift + cost tracker cover it; a schema migration is a larger change and is tracked separately. - MCP Apps / agent-provided HTML UIs: novel but requires UI-layer design. - ACP-as-provider wrapping other CLIs: larger provider abstraction change. -- Local-inference tool emulation / toolshim: depends on graycode-router's local-model +- Local-inference tool emulation / toolshim: depends on eyrie's local-model path. -- Recipe security scanner / cron recipes: graycode already has schedule/cron. +- Recipe security scanner / cron recipes: hawk already has schedule/cron. ## Verification - `go test ./...` full suite. -- `make vet`, `make lint`, `graycode verify`. +- `make vet`, `make lint`, `hawk verify`. - Focused tests for the env filter, AGENTS.md references, and compaction retry. - markdownlint on this document. diff --git a/docs/plans/graycode-contracts-migration-backlog.md b/docs/plans/hawk-contracts-migration-backlog.md similarity index 61% rename from docs/plans/graycode-contracts-migration-backlog.md rename to docs/plans/hawk-contracts-migration-backlog.md index a1297c2fa..4edbc9e1b 100644 --- a/docs/plans/graycode-contracts-migration-backlog.md +++ b/docs/plans/hawk-contracts-migration-backlog.md @@ -1,8 +1,8 @@ -# Plan: Graycode Contracts Migration Backlog +# Plan: Hawk Contracts Migration Backlog > Status: locally complete > Scope: graycode-eco ecosystem architecture cleanup after introducing `eagle` -> Goal: keep `graycode` as the product while moving stable cross-repo contracts out of Graycode internals +> Goal: keep `hawk` as the product while moving stable cross-repo contracts out of Hawk internals External follow-up still outside the scope of this local workspace audit: @@ -22,8 +22,8 @@ These items are already completed in the current workspace. - added `eagle/types` - moved severity and finding definitions into contracts - migrated `kestrel` and `merlin` to import contracts -- switched Graycode `internal/types/severity.go` to re-export from contracts -- removed `graycode/shared/types` after local ecosystem migration +- switched Hawk `internal/types/severity.go` to re-export from contracts +- removed `hawk/shared/types` after local ecosystem migration - removed the duplicate severity/finding definitions in `shrike/types` (shrike was the original shared-types host) - removed the `shrike/types` compatibility shim after verifying no in-workspace @@ -31,15 +31,15 @@ These items are already completed in the current workspace. ### Tool contract migration - added `eagle/tools` -- switched Graycode session persistence to provider-neutral tool contracts +- switched Hawk session persistence to provider-neutral tool contracts - added runtime/session conversion helpers - added `eagle/tools/tool_test.go` -- centralized message slice conversion in `graycode/internal/session` -- removed direct lower-level provider message reconstruction from Graycode +- centralized message slice conversion in `hawk/internal/session` +- removed direct lower-level provider message reconstruction from Hawk cmd/session restore paths -- replaced the provider-owned message alias with a Graycode-owned runtime DTO -- added explicit Graycode-owned runtime DTOs; the final boundary translates them - through `graycode-router/engine` +- replaced the provider-owned message alias with a Hawk-owned runtime DTO +- added explicit Hawk-owned runtime DTOs; the final boundary translates them + through `eyrie/engine` ### Event contract migration - added `eagle/events` @@ -56,14 +56,14 @@ These items are already completed in the current workspace. ### Governance - added `check-shared-types-imports.sh` - added `check-ecosystem-boundaries.sh` -- added `check-graycode-router-client-imports.sh` +- added `check-eyrie-client-imports.sh` - wired both guards into `Makefile` and CI -- wired the GraycodeRouter engine-boundary guards into `Makefile` and CI +- wired the Eyrie engine-boundary guards into `Makefile` and CI - added a legacy import guard so the removed `shared/types` path cannot return - extended the ecosystem boundary guard to scan sibling engine repos when present locally -- updated docs across Graycode, kestrel, merlin, and external workspace copies +- updated docs across Hawk, kestrel, merlin, and external workspace copies - added standalone boundary guards in `kestrel` and `merlin` -- added standalone boundary guards in `shrike`, `graycode-router`, `harrier`, and `swift` +- added standalone boundary guards in `shrike`, `eyrie`, `harrier`, and `swift` - updated support repo READMEs with ecosystem boundary rules ### Review and verification contract migration @@ -72,8 +72,8 @@ These items are already completed in the current workspace. - added shared review/verification contract tests - added kestrel -> review contract adapters - added merlin -> verify contract adapters -- switched Graycode review persistence to neutral review contracts -- switched Graycode review/merlin bridge paths to return neutral review/verify contracts +- switched Hawk review persistence to neutral review contracts +- switched Hawk review/merlin bridge paths to return neutral review/verify contracts ## Remaining external follow-up @@ -91,26 +91,26 @@ Still external: ### 2. Confirm release/publication convergence Local state: -- Graycode local integration snapshot points at architecture-aligned support-repo revisions +- Hawk local integration snapshot points at architecture-aligned support-repo revisions Still external: -- confirm released module versions used by Graycode match the merged contract changes +- confirm released module versions used by Hawk match the merged contract changes -### 3. Remove Graycode production dependency on lower GraycodeRouter packages +### 3. Remove Hawk production dependency on lower Eyrie packages Current state: - session persistence uses neutral tool contracts -- Graycode now owns the runtime message DTO in `internal/types.GraycodeRouterMessage` -- Graycode now owns runtime tool call/result DTOs in `internal/types` -- Graycode now owns runtime response/usage/stream DTOs in `internal/types` -- Graycode now owns runtime chat options, response format, tool choice, continuation config, and tool definition DTOs in `internal/types` -- Graycode now owns the transport-provider seam in `internal/types.ChatProvider` -- Graycode session, review, setup, catalog, diagnostics, and custom-provider paths - now enter through `graycode-router/engine` -- production imports of every lower GraycodeRouter package are zero +- Hawk now owns the runtime message DTO in `internal/types.EyrieMessage` +- Hawk now owns runtime tool call/result DTOs in `internal/types` +- Hawk now owns runtime response/usage/stream DTOs in `internal/types` +- Hawk now owns runtime chat options, response format, tool choice, continuation config, and tool definition DTOs in `internal/types` +- Hawk now owns the transport-provider seam in `internal/types.ChatProvider` +- Hawk session, review, setup, catalog, diagnostics, and custom-provider paths + now enter through `eyrie/engine` +- production imports of every lower Eyrie package are zero - cmd/session restore paths now go through centralized `session.ToRuntimeMessages` and `session.FromRuntimeMessages` Decision: -- completed: Graycode owns the product DTO/port seam and GraycodeRouter owns the engine, +- completed: Hawk owns the product DTO/port seam and Eyrie owns the engine, provider, credential, catalog, routing, resilience, and normalization layers - keep the zero-exception boundary enforced; lower packages are test-fixture-only @@ -133,7 +133,7 @@ Do this only if another repo truly needs them. Current state: - normalized review result contracts now live in `eagle/review` - normalized verification report contracts now live in `eagle/verify` -- Graycode consumes neutral review/verification contracts at persistence and bridge boundaries +- Hawk consumes neutral review/verification contracts at persistence and bridge boundaries Possible later additions: - review lifecycle status enums if another repo needs them @@ -151,12 +151,12 @@ Possible later additions: Do not move every internal event shape by default. -### 6. Remove `graycode/shared/types` +### 6. Remove `hawk/shared/types` Completed for the local ecosystem. Current status: -- Graycode no longer ships `graycode/shared/types` +- Hawk no longer ships `hawk/shared/types` - local import guards prevent the old path from returning ## Non-goals @@ -164,36 +164,36 @@ Current status: Do not do these without a separate decision: - move provider runtime types into contracts -- move Graycode orchestration logic into contracts +- move Hawk orchestration logic into contracts - move sandbox manager internals into contracts -- move every event struct in Graycode into contracts -- force Lark/Gitant architecture into Graycode contracts +- move every event struct in Hawk into contracts +- force Lark/Gitant architecture into Hawk contracts ## Recommended PR order ### PR 1 -- completed: moved chat options/request DTOs behind Graycode-owned runtime adapters +- completed: moved chat options/request DTOs behind Hawk-owned runtime adapters ### PR 2 -- completed: moved provider/config interfaces behind Graycode-owned transport adapters +- completed: moved provider/config interfaces behind Hawk-owned transport adapters ### PR 3 -- completed: removed all lower-level GraycodeRouter imports from Graycode production code and +- completed: removed all lower-level Eyrie imports from Hawk production code and replaced the compatibility allowlist with a zero-exception guard ### PR 4 -- completed: added neutral review/verification result contracts and wired Graycode bridge/persistence edges +- completed: added neutral review/verification result contracts and wired Hawk bridge/persistence edges ### PR 5 -- completed: removed `graycode/shared/types` from the local ecosystem and kept a legacy import guard +- completed: removed `hawk/shared/types` from the local ecosystem and kept a legacy import guard ## Success criteria The migration is in a good long-term state when: - `eagle` is the only source of truth for shared contracts -- support repos do not import `graycode/internal/*` -- support repos do not import `graycode/shared/types` +- support repos do not import `hawk/internal/*` +- support repos do not import `hawk/shared/types` - removed compatibility shims do not return - CI prevents regressions - new shared contracts are added deliberately, not by habit diff --git a/docs/plans/minimax-adoption-plan.md b/docs/plans/minimax-adoption-plan.md index 184184410..f433c1185 100644 --- a/docs/plans/minimax-adoption-plan.md +++ b/docs/plans/minimax-adoption-plan.md @@ -1,40 +1,40 @@ -# MiniMax-AI → graycode Adoption Plan +# MiniMax-AI → hawk Adoption Plan Status: Implemented in working tree; sibling-repository PRs and skills curation remain follow-ups Date: 2026-08-21 Scope: Adopt the high-value, verified concepts from MiniMax-AI's open-source -repos into the Graycode ecosystem (Graycode plus independent repositories: GraycodeRouter, Eagle, +repos into the Hawk ecosystem (Hawk plus independent repositories: Eyrie, Eagle, Falcon, Kestrel, and Merlin). ## Findings summary -Deep code review of 6 MiniMax-AI repos against Graycode's existing sibling +Deep code review of 6 MiniMax-AI repos against Hawk's existing sibling repositories and internals produced these adoptions, ordered by value: | # | MiniMax repo | Adopt into | Action | |---|---|---|---| -| 1 | MiniMax-Provider-Verifier | sibling `graycode-router` | Add provider-conformance metrics + wire the orphaned `verify` harness into CI | +| 1 | MiniMax-Provider-Verifier | sibling `eyrie` | Add provider-conformance metrics + wire the orphaned `verify` harness into CI | | 2 | MiniMax/skills | `starling` | Curate high-quality skill content (content, not mechanism) | -| 3 | MiniMax-Coding-Plan-MCP | graycode / `falcon` | MCP v2 no-network test pattern + image-source normalization (patterns only) | -| 4 | minimax_search | graycode tooling | Jina page→Markdown browse extractor + evidence-extraction prompt (pattern) | -| 5 | Mini-Agent | graycode engine | Cooperative-cancellation-with-cleanup + token-aware lossy summarization (patterns) | +| 3 | MiniMax-Coding-Plan-MCP | hawk / `falcon` | MCP v2 no-network test pattern + image-source normalization (patterns only) | +| 4 | minimax_search | hawk tooling | Jina page→Markdown browse extractor + evidence-extraction prompt (pattern) | +| 5 | Mini-Agent | hawk engine | Cooperative-cancellation-with-cleanup + token-aware lossy summarization (patterns) | | 6 | MiniMax-MCP / JS | — | Not adoptable (media generation, out of scope) | -## 1. GraycodeRouter provider-conformance metrics + CI wiring (highest value) +## 1. Eyrie provider-conformance metrics + CI wiring (highest value) -### Verified current state (graycode-router repository) +### Verified current state (eyrie repository) -- `graycode-router/verify/verify.go` — a complete behavioral conformance harness +- `eyrie/verify/verify.go` — a complete behavioral conformance harness exists: `Case`/`CaseResult`/`Expectation`/`Report`, `Run()`, `scoreResponse()`, `ToolCallF1()`, `DiffBaseline()`, `Markdown()`. -- `graycode-router/verify/cases.go` — `CanonicalCases()` with 3 cases: +- `eyrie/verify/cases.go` — `CanonicalCases()` with 3 cases: `basic-chat`, `deterministic-answer`, `tool-call`. -- `graycode-router/verify/metrics.go` — `ToolCallF1()` already implemented. +- `eyrie/verify/metrics.go` — `ToolCallF1()` already implemented. - **Confirmed gap:** the `verify` package is referenced ONLY by its own tests. It is not wired into any CLI, Makefile target, CI job, or provider-registration gate. Only structural registry/parity tests are enforced. -- graycode-router already ships MiniMax providers (`minimax_token_plan`, `minimax_payg`) +- eyrie already ships MiniMax providers (`minimax_token_plan`, `minimax_payg`) as OpenAI-compatible adapters with `ThinkingFormat: "minimax"`. - `client/structured.go` already has `ValidateStructuredOutput()` — a recursive JSON-schema validator (`validateValue`, `validateObject`, `validateArray`) @@ -43,7 +43,7 @@ internals produced these adoptions, ordered by value: ### What to implement **A. Add a `SchemaValidate` helper (argument-level) — new file -`graycode-router/verify/schema.go`** +`eyrie/verify/schema.go`** Add `SchemaValidate(args map[string]any, schema map[string]any) error` that checks tool-call arguments against the tool's `Parameters` JSON schema: @@ -52,7 +52,7 @@ checks tool-call arguments against the tool's `Parameters` JSON schema: This is the `ToolCalls-Schema-Accuracy` dimension from MiniMax-Provider-Verifier. -**B. Add `MatchRate` to `Report` — edit `graycode-router/verify/verify.go`** +**B. Add `MatchRate` to `Report` — edit `eyrie/verify/verify.go`** Compute the `ToolCalls-Match-Rate` (trigger-vs-stop correctness) alongside F1. Extend `CaseResult` already has `ExpectedTool`/`CalledAnyTool`/`CorrectTool`, so @@ -63,9 +63,9 @@ tool_calls_match_rate = (TP + TN) / expected_tool_call_total_count mirroring MiniMax's confusion-matrix metric. **C. Add a `verify` CLI entrypoint — new file -`graycode-router/cmd/verify/main.go`** +`eyrie/cmd/verify/main.go`** -A small `go run`-able binary (or a `verify` subcommand if graycode-router has a cmd/) that +A small `go run`-able binary (or a `verify` subcommand if eyrie has a cmd/) that runs `verify.Run` against a provider endpoint (live or cassette) and exits non-zero on unmet thresholds: - `--model`, `--base-url`, `--api-key`, `--provider` @@ -74,8 +74,8 @@ non-zero on unmet thresholds: This makes the orphaned harness operational and enables CI gating. -**D. Add a Makefile target + CI job — edit `graycode-router/Makefile` and -`graycode-router/.github/workflows/ci.yml`** +**D. Add a Makefile target + CI job — edit `eyrie/Makefile` and +`eyrie/.github/workflows/ci.yml`** - Makefile: `verify` target that runs the harness in cassette mode (no tokens) and `verify-live` for manual live runs. @@ -83,27 +83,27 @@ This makes the orphaned harness operational and enables CI gating. on PRs, ensuring the harness is exercised and providers don't regress. ### Files -- `graycode-router/verify/schema.go` (new) -- `graycode-router/verify/schema_test.go` (new) -- `graycode-router/verify/verify.go` (add MatchRate) -- `graycode-router/verify/verify_test.go` (add tests) -- `graycode-router/cmd/verify/main.go` (new) -- `graycode-router/Makefile` (verify target) -- `graycode-router/.github/workflows/ci.yml` (verify job) +- `eyrie/verify/schema.go` (new) +- `eyrie/verify/schema_test.go` (new) +- `eyrie/verify/verify.go` (add MatchRate) +- `eyrie/verify/verify_test.go` (add tests) +- `eyrie/cmd/verify/main.go` (new) +- `eyrie/Makefile` (verify target) +- `eyrie/.github/workflows/ci.yml` (verify job) ## 2. Curate MiniMax/skills content into starling -### Verified current state (graycode) -- graycode's skills system (`internal/plugin`) reads markdown+YAML-frontmatter skills - from dirs (`~/.graycode/skills`, `.claude/skills`, `.zero/skills`, `skills`) with a - registry (`starling` repo, `graycode skills search/install/list/remove`). +### Verified current state (hawk) +- hawk's skills system (`internal/plugin`) reads markdown+YAML-frontmatter skills + from dirs (`~/.hawk/skills`, `.claude/skills`, `.zero/skills`, `skills`) with a + registry (`starling` repo, `hawk skills search/install/list/remove`). - MiniMax/skills (13.4k★) has 18 high-quality skills in the same format (frontmatter `name`/`description`/`license`/`metadata`), especially `frontend-dev`, `fullstack-dev`, `shader-dev`, mobile guides, `vision-analysis`. ### What to implement - Port the best MiniMax skills into `GrayCodeAI/starling` (separate - repo), adapting frontmatter to graycode's convention (`globs`, `alwaysApply`). + repo), adapting frontmatter to hawk's convention (`globs`, `alwaysApply`). - This is a content curation task in a separate repo; tracked here for completeness but implemented as a follow-up PR in `starling`. @@ -113,10 +113,10 @@ This makes the orphaned harness operational and enables CI gating. ## 3. MCP v2 no-network test pattern + image-source normalization -### Verified current state (graycode) +### Verified current state (hawk) - `internal/mcp` implements its own JSON-RPC client + server and does NOT use the shared `falcon` scaffolding (architectural divergence). -- graycode has `internal/attachment/image.go` for image decode, and `ScreenshotTool` +- hawk has `internal/attachment/image.go` for image decode, and `ScreenshotTool` / `BrowserTool`. No image-source (URL/file/data-URL) normalization helper. - MiniMax-Coding-Plan-MCP shows: MCP v2 tool registration + no-network test harness (in-process `Client` + stdio `ClientSession` asserting exact payloads) @@ -135,8 +135,8 @@ This makes the orphaned harness operational and enables CI gating. ## 4. Jina page→Markdown browse extractor -### Verified current state (graycode) -- graycode has `WebSearchTool` (6-provider cascade: Brave/SearXNG/DeepSeek/Exa/ +### Verified current state (hawk) +- hawk has `WebSearchTool` (6-provider cascade: Brave/SearXNG/DeepSeek/Exa/ Perplexity/DDG), `AgenticFetchTool`, `WebFetchTool`, `DownloadTool`, `engine/search/url_scraper.go`. - No lightweight "URL → Markdown" extractor (Jina Reader pattern). @@ -155,14 +155,14 @@ This makes the orphaned harness operational and enables CI gating. ## 5. Agent-loop robustness patterns (reference) -### Verified current state (graycode) -- graycode's `Session.agentLoop` (`internal/engine/stream.go:133`) is a complete +### Verified current state (hawk) +- hawk's `Session.agentLoop` (`internal/engine/stream.go:133`) is a complete think→act→observe loop with memory, 70+ tools, sub-agents, multi-agent. - No cooperative-cancellation-with-history-cleanup, and long-session compaction uses truncation rather than "summarize between user turns". ### What to implement (small, low-risk) -- Add `_cleanup_incomplete_messages` equivalent to graycode's loop: on cancellation +- Add `_cleanup_incomplete_messages` equivalent to hawk's loop: on cancellation at a safe checkpoint, trim the partial assistant message + orphaned tool results so message history stays valid. - Add a token-limit-driven lossy summarization option (keep user intents, @@ -175,27 +175,27 @@ This makes the orphaned harness operational and enables CI gating. ## Out of scope (not adopted) - MiniMax-MCP / MiniMax-MCP-JS: media generation (TTS/image/video) — no - relevance to code intelligence; graycode has no TTS and that is a product decision. -- minimax_search's search layer: redundant (graycode has more providers). -- Mini-Agent's core loop: outclassed by graycode. + relevance to code intelligence; hawk has no TTS and that is a product decision. +- minimax_search's search layer: redundant (hawk has more providers). +- Mini-Agent's core loop: outclassed by hawk. - The `verify` harness's live-token path: optional, off by default. ## Execution order -1. GraycodeRouter verify metrics + schema validation + tests (highest value) -2. GraycodeRouter verify CLI + Makefile + CI wiring -3. graycode image-source normalization helper + tests -4. graycode Jina browse extractor + tests +1. Eyrie verify metrics + schema validation + tests (highest value) +2. Eyrie verify CLI + Makefile + CI wiring +3. hawk image-source normalization helper + tests +4. hawk Jina browse extractor + tests 5. Agent-loop cancellation/summarization patterns 6. starling content curation (follow-up PR in separate repo) ## Implementation status -- [x] GraycodeRouter schema-accuracy validation and tool-call match-rate metrics. -- [x] GraycodeRouter verification CLI, deterministic Makefile target, and CI job. -- [x] Graycode image-source normalization for data URIs, URLs, local files, and raw base64. -- [x] Graycode Jina Reader page-to-Markdown client with opt-in configuration and tests. -- [x] Graycode cancellation cleanup for incomplete assistant tool-use/tool-result turns. -- [x] Confirmed graycode's existing `internal/engine/compact` already provides token-triggered +- [x] Eyrie schema-accuracy validation and tool-call match-rate metrics. +- [x] Eyrie verification CLI, deterministic Makefile target, and CI job. +- [x] Hawk image-source normalization for data URIs, URLs, local files, and raw base64. +- [x] Hawk Jina Reader page-to-Markdown client with opt-in configuration and tests. +- [x] Hawk cancellation cleanup for incomplete assistant tool-use/tool-result turns. +- [x] Confirmed hawk's existing `internal/engine/compact` already provides token-triggered compaction; no duplicate summarizer was added. -- [ ] Publish the GraycodeRouter repository changes through its own feature branch and PR. +- [ ] Publish the Eyrie repository changes through its own feature branch and PR. - [ ] Curate MiniMax skills into `starling` through its own feature branch and PR. diff --git a/docs/plans/pi-adoption-plan.md b/docs/plans/pi-adoption-plan.md index 18c023b3a..c1a755bb8 100644 --- a/docs/plans/pi-adoption-plan.md +++ b/docs/plans/pi-adoption-plan.md @@ -4,16 +4,16 @@ Status: Proposed Source: `https://github.com/earendil-works/pi` (MIT, TypeScript/Bun monorepo) -This plan records the features identified as genuinely missing from graycode while -auditing the Pi agent harness against Graycode and its independent ecosystem +This plan records the features identified as genuinely missing from hawk while +auditing the Pi agent harness against Hawk and its independent ecosystem repositories. It is -an adoption plan, not a code-porting plan: graycode reimplements compatible behavior +an adoption plan, not a code-porting plan: hawk reimplements compatible behavior in Go and preserves its existing provider, session, sandbox, permission, observability, and protocol boundaries. ## Executive Decision -Graycode should adopt the following Pi features: +Hawk should adopt the following Pi features: 1. A Go telemetry conformance suite that verifies emitted OpenTelemetry spans and attributes match the documented schema. @@ -25,23 +25,23 @@ Graycode should adopt the following Pi features: 5. Kitty graphics protocol support for terminal images. 6. Session lease/ownership semantics in the daemon protocol. -Graycode should not copy Pi's TypeScript code, replace its Go runtime, adopt Pi's +Hawk should not copy Pi's TypeScript code, replace its Go runtime, adopt Pi's custom CBOR RPC protocol, or remove its native permission/sandboxing model. -## Existing Graycode Capabilities +## Existing Hawk Capabilities -The audit found that graycode already provides the foundation for most Pi features: +The audit found that hawk already provides the foundation for most Pi features: -| Pi package | Graycode implementation | Current decision | +| Pi package | Hawk implementation | Current decision | |---|---|---| -| `pi-ai` (multi-provider) | sibling `graycode-router` client + engine facade + catalog + router + credentials | Keep graycode (broader) | -| `pi-agent-core` (agent loop) | `internal/engine` | Keep graycode | -| `pi-coding-agent` (CLI) | `cmd` TUI + CLI + daemon | Keep graycode | -| `pi-session-backends` (storage) | `internal/session` JSONL + zstd + WAL + SQLite index, sibling `swift` (Swift) | Keep graycode | -| `pi-server` (RPC) | `internal/daemon` + `internal/acp` + `internal/mcp` | Keep graycode (broader) | -| Permissions/sandbox | `internal/engine/safety` + `internal/sandbox` (seatbelt/landlock/seccomp/ACL/netproxy) | Keep graycode (native, ahead) | +| `pi-ai` (multi-provider) | sibling `eyrie` client + engine facade + catalog + router + credentials | Keep hawk (broader) | +| `pi-agent-core` (agent loop) | `internal/engine` | Keep hawk | +| `pi-coding-agent` (CLI) | `cmd` TUI + CLI + daemon | Keep hawk | +| `pi-session-backends` (storage) | `internal/session` JSONL + zstd + WAL + SQLite index, sibling `swift` (Swift) | Keep hawk | +| `pi-server` (RPC) | `internal/daemon` + `internal/acp` + `internal/mcp` | Keep hawk (broader) | +| Permissions/sandbox | `internal/engine/safety` + `internal/sandbox` (seatbelt/landlock/seccomp/ACL/netproxy) | Keep hawk (native, ahead) | | `pi-tui` differential rendering | Bubble Tea v2 full-frame redraw | Adopt line-diff engine | -| `pi-telemetry` conformance | `docs/OTEL-CONVENTIONS.md`, graycode-router `genai_semconv` pinning | Adopt conformance suite | +| `pi-telemetry` conformance | `docs/OTEL-CONVENTIONS.md`, eyrie `genai_semconv` pinning | Adopt conformance suite | | `pi-evals` agent-level eval | `internal/feature/eval` (model benchmark only) | Adopt agent-runtime eval | ## Priority Model @@ -57,16 +57,16 @@ The audit found that graycode already provides the foundation for most Pi featur Guarantee the emitted OpenTelemetry spans and attributes always match the documented `gen_ai.*` / `cost.usd` / `tool.name` / `session.id` / `agent.id` -contract, so the schema cannot silently drift across Graycode and its independent +contract, so the schema cannot silently drift across Hawk and its independent ecosystem repositories. ### Scope and ownership - Primary implementation: `internal/observability` and - `graycode-router/internal/observability` (the canonical `genai_semconv` + `eyrie/internal/observability` (the canonical `genai_semconv` constants and their pinning test). - Shared schema constants: sibling `eagle` if a cross-repo - contract is needed, otherwise keep them in graycode-router as today. + contract is needed, otherwise keep them in eyrie as today. - No changes to `internal/mcp`, `internal/sandbox`, or `internal/daemon`. ### Required behavior @@ -82,24 +82,24 @@ ecosystem repositories. - asserts sensitive attributes never carry raw prompt/response text, - asserts parent/child span relationships and settlement semantics, - is runner-independent (usable from any Go test harness). -4. Wire the conformance harness into the graycode and graycode-router test suites so CI +4. Wire the conformance harness into the hawk and eyrie test suites so CI enforces the contract. 5. Keep the conformance layer passive and non-throwing: malformed or unreadable telemetry payloads must not break agent execution. ### Acceptance criteria -- Every span graycode emits is covered by the schema. +- Every span hawk emits is covered by the schema. - A deliberate schema drift (adding a span or attribute) fails the conformance test until the schema is updated. - No raw prompt/response text appears in recorded attributes. -- The conformance harness runs green in graycode and graycode-router CI. +- The conformance harness runs green in hawk and eyrie CI. ## P0: Agent-Runtime Eval Harness ### Goal -Evaluate the full graycode agent end-to-end (real session, tool loop, planning, +Evaluate the full hawk agent end-to-end (real session, tool loop, planning, sandbox) against tasks, and snapshot session data as artifacts — not just a model-level benchmark. @@ -273,7 +273,7 @@ Every implementation phase must preserve: - Eval runs the real tool loop end-to-end in an isolated dir. - Daemon and ACP parity for leased sessions. - fxtape recording/replay across the new renderer. -- Cross-repo telemetry conformance in graycode and graycode-router CI. +- Cross-repo telemetry conformance in hawk and eyrie CI. ### Security tests @@ -291,11 +291,11 @@ make test-race make vet make lint make security -graycode verify +hawk verify ``` For sibling-repository changes, run that repository's own tests and boundary checks before -updating the Graycode pointer. +updating the Hawk pointer. ## Delivery Sequence @@ -310,7 +310,7 @@ updating the Graycode pointer. - [x] Define the typed span/attribute schema. - [x] Implement the conformance harness. -- [x] Wire graycode and graycode-router CI. +- [x] Wire hawk and eyrie CI. - [x] Add schema-drift regression tests. ### Milestone 2: Agent-runtime eval (P0) @@ -318,7 +318,7 @@ updating the Graycode pointer. - [x] Implement the loop runner over the real Session/tool loop. - [x] Add isolated execution and session-JSONL artifacts. - [ ] Add comparative and reproducibility reporting. -- [x] Extend `graycode eval` with a loop mode. +- [x] Extend `hawk eval` with a loop mode. ### Milestone 3: Differential renderer (P1) @@ -344,19 +344,19 @@ updating the Graycode pointer. ## Deliberately Deferred - Copying Pi's TypeScript code or adopting its custom CBOR RPC protocol in place - of graycode's ACP/MCP/daemon stack. -- Replacing graycode's native permission/sandbox model with Pi's container-only + of hawk's ACP/MCP/daemon stack. +- Replacing hawk's native permission/sandbox model with Pi's container-only approach. -- Porting Pi's extension system verbatim; graycode's plugin/hook/skills model already +- Porting Pi's extension system verbatim; hawk's plugin/hook/skills model already covers it. - Adding a full agent-swarm/graph model beyond current subagent support. ## Success Criteria -The adoption is successful when graycode closes each confirmed gap while retaining +The adoption is successful when hawk closes each confirmed gap while retaining its stronger architecture: -- Telemetry spans always conform to the documented schema across graycode and its +- Telemetry spans always conform to the documented schema across hawk and its sibling repositories. - The agent can be evaluated end-to-end through its real tool loop. - The TUI re-renders only changed lines with synchronized output. diff --git a/docs/plans/pi-renderer-and-eval-reporting.md b/docs/plans/pi-renderer-and-eval-reporting.md index 60c03b978..ab392916d 100644 --- a/docs/plans/pi-renderer-and-eval-reporting.md +++ b/docs/plans/pi-renderer-and-eval-reporting.md @@ -8,7 +8,7 @@ the three merged Pi PRs (#226, #227, #228). ## Executive Decision Two of the three remaining sub-items are **blocked by the rendering stack** and -cannot be adopted cleanly in graycode's current UI framework: +cannot be adopted cleanly in hawk's current UI framework: - Bubble Tea v2 exposes **no public `Renderer` interface** (its renderer is an unexported type with internal methods; the only option is `WithoutRenderer`). @@ -28,7 +28,7 @@ reporting** — is fully feasible and safe (non-TUI) and is adopted here. config) so identical runs can be cached and compared. - Add a comparative report across runs/models: pass rate, token/latency/cost deltas, and per-run reproducibility hash. -- Wire it into the existing `evalloop` package and `graycode eval loop` output. +- Wire it into the existing `evalloop` package and `hawk eval loop` output. ### Scope and ownership @@ -43,7 +43,7 @@ reporting** — is fully feasible and safe (non-TUI) and is adopted here. result transcript. 3. A `Compare` helper aggregates multiple results and reports pass-rate and per-metric deltas (tokens, cost, duration). -4. `graycode eval loop --report` prints the comparative summary. +4. `hawk eval loop --report` prints the comparative summary. ### Acceptance criteria @@ -64,6 +64,6 @@ reporting** — is fully feasible and safe (non-TUI) and is adopted here. ## Verification - `go test ./...` full suite. -- `make vet`, `make lint`, `graycode verify`. +- `make vet`, `make lint`, `hawk verify`. - Focused `internal/feature/evalloop` tests. - markdownlint on this document. diff --git a/docs/plans/qwen-code-adoption-plan.md b/docs/plans/qwen-code-adoption-plan.md index c2fff1b9c..04a02e52c 100644 --- a/docs/plans/qwen-code-adoption-plan.md +++ b/docs/plans/qwen-code-adoption-plan.md @@ -1,18 +1,18 @@ # Qwen Code Adoption Plan -Status: Implemented selectively in the current Graycode feature branch. +Status: Implemented selectively in the current Hawk feature branch. ## Guardrails -Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Graycode -will independently reimplement behavioral contracts in Go, preserve the GraycodeRouter -provider boundary, and retain Graycode's event-sourced sessions and OS sandbox. +Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Hawk +will independently reimplement behavioral contracts in Go, preserve the Eyrie +provider boundary, and retain Hawk's event-sourced sessions and OS sandbox. No Qwen source or dependency is vendored. -## Existing Graycode Capabilities +## Existing Hawk Capabilities -Graycode already has durable sessions, event logging, WAL/recovery, branching, -review contracts, provider routing in GraycodeRouter, MCP integration, skills, memory, +Hawk already has durable sessions, event logging, WAL/recovery, branching, +review contracts, provider routing in Eyrie, MCP integration, skills, memory, context compaction, policy snapshots, subagents, daemon security, and filesystem/process sandboxing. These systems will not be duplicated. @@ -24,7 +24,7 @@ filesystem/process sandboxing. These systems will not be duplicated. 2. Added terminal reasons for permission denial, approval denial, unknown tool, pipeline failure, timeout, cancellation, execution failure, and success. 3. Added regression coverage for the lifecycle contract. -4. Preserved Graycode's existing cancellation transcript cleanup and compaction. +4. Preserved Hawk's existing cancellation transcript cleanup and compaction. 5. Preserved policy-snapshot inheritance and subagent cleanup already present. 6. Added explicit `StreamEvent` tool lifecycle state and terminal-reason fields, with execution-path transitions and regression coverage. @@ -47,7 +47,7 @@ filesystem/process sandboxing. These systems will not be duplicated. ### P2: Declarative extensibility -- Map Markdown subagent frontmatter to Graycode's typed SpawnRequest. +- Map Markdown subagent frontmatter to Hawk's typed SpawnRequest. - Add path-conditional skill activation and parse-error diagnostics. - Add hot reload with bounded activation listeners. - Scope child hooks and MCP resources by session/agent ID. @@ -61,6 +61,6 @@ filesystem/process sandboxing. These systems will not be duplicated. ## Verification - Focused lifecycle, skills, engine, and command tests. -- Full Graycode build, vet, and test suite. +- Full Hawk build, vet, and test suite. - Independent second verification pass. - Final diff and sibling-repository status inspection. diff --git a/docs/plans/toolbench-comparison-vs-top20.md b/docs/plans/toolbench-comparison-vs-top20.md index 275bf31fc..5b58ae8ff 100644 --- a/docs/plans/toolbench-comparison-vs-top20.md +++ b/docs/plans/toolbench-comparison-vs-top20.md @@ -1,17 +1,17 @@ -# Toolbench Comparison: graycode vs the Top-20 OSS Coding Agents (2026) +# Toolbench Comparison: hawk vs the Top-20 OSS Coding Agents (2026) > Status: analysis complete -> Scope: built-in tool inventory of graycode and the leading open-source coding agents +> Scope: built-in tool inventory of hawk and the leading open-source coding agents > Related issue: browser/screenshot automation gap ## Executive summary -graycode ships **69 built-in agent tools** — more than any top-20 OSS rival — and covers every capability category the leader board demands (file I/O, shell, web, search, memory, planning, MCP, sub-agents). The only genuine gaps versus the field are: +hawk ships **69 built-in agent tools** — more than any top-20 OSS rival — and covers every capability category the leader board demands (file I/O, shell, web, search, memory, planning, MCP, sub-agents). The only genuine gaps versus the field are: 1. **Browser / computer-use automation** — now closed (see this plan's outcome). -2. **IDE surfaces** — Cline/Continue/Zed are editor-native; graycode remains terminal-first by design. +2. **IDE surfaces** — Cline/Continue/Zed are editor-native; hawk remains terminal-first by design. -graycode's tool count exceeds the field (OpenCode ~10, Claude Code 18, Qwen Code ~26, Codex ~9+), but count alone is not the point: graycode matches **Qwen Code** tool-for-tool *and* adds a spec/planning suite, persistent core memory, impact/codegraph/git tools, and an in-tree linter toolchain (`nilaway`, `revive`) that the others leave to MCP. +hawk's tool count exceeds the field (OpenCode ~10, Claude Code 18, Qwen Code ~26, Codex ~9+), but count alone is not the point: hawk matches **Qwen Code** tool-for-tool *and* adds a spec/planning suite, persistent core memory, impact/codegraph/git tools, and an in-tree linter toolchain (`nilaway`, `revive`) that the others leave to MCP. ## Top-20 OSS coding agents by tool surface (2026 star snapshots) @@ -19,7 +19,7 @@ Star counts are approximate (GitHub, ~July 2026): OpenCode ~172k, Gemini CLI ~99 | Agent | Built-in tools | Categories covered | |---|---|---| -| graycode | **69** (+Power, +MCP) | all | +| hawk | **69** (+Power, +MCP) | all | | Qwen Code | 26 + computer_use | all; computer use, cron, worktree | | Claude Code | 18 | all; notebook_edit, skills, hooks | | Kilo Code | ~15 + Playwright MCP | all + browser via MCP | @@ -36,7 +36,7 @@ Star counts are approximate (GitHub, ~July 2026): OpenCode ~172k, Gemini CLI ~99 | Continue | ~10 | editor | | Plandex | ~5 | planning + git | -## graycode's current tool set +## hawk's current tool set See `cmd/chat_tools.go` for the registry construction. @@ -48,23 +48,23 @@ Spec/planning suite (`plan`, `spec_*`, `approve_implementation`, `clarify`, `con ## The browser gap and how it was closed -Every browser-native competitor drives a real browser: Cline bundles Puppeteer, Kilo bundles the Playwright MCP server, Codex CLI ships a Chrome extension + in-app browser, Qwen Code exposes `computer_use_*`, OpenManus embeds browser/crawl4ai. graycode previously had only file/shell/web tools. +Every browser-native competitor drives a real browser: Cline bundles Puppeteer, Kilo bundles the Playwright MCP server, Codex CLI ships a Chrome extension + in-app browser, Qwen Code exposes `computer_use_*`, OpenManus embeds browser/crawl4ai. hawk previously had only file/shell/web tools. -**Outcome:** added `internal/tool/browser.go` and `internal/tool/screenshot.go`, exposed through graycode's existing `tool.Tool` interface: +**Outcome:** added `internal/tool/browser.go` and `internal/tool/screenshot.go`, exposed through hawk's existing `tool.Tool` interface: - `BrowserTool` — headless-Chrome CDP driver with actions `navigate`, `content` (text/HTML, selector-scoped), `screenshot`, `click`, `type` (with clear), `title`, `location`, `close`. Risk level `high`; URL scheme validation restricts to `http(s)` (localhost/LAN included). - `ScreenshotTool` — single-shot full-page PNG capture to a path (defaults to a temp file). -Implementation uses `github.com/chromedp/chromedp` v0.16.0, the canonical pure-Go Chrome DevTools client, and reuses graycode's existing `validatePathAllowed` guard for the output path. A lazily-allocated, mutex-guarded browser process is shared across calls (closed via `Browser … action:"close"` or `releaseBrowser()` in tests). +Implementation uses `github.com/chromedp/chromedp` v0.16.0, the canonical pure-Go Chrome DevTools client, and reuses hawk's existing `validatePathAllowed` guard for the output path. A lazily-allocated, mutex-guarded browser process is shared across calls (closed via `Browser … action:"close"` or `releaseBrowser()` in tests). -A live end-to-end test (`TestBrowserLive`, opt-in via `GRAYCODE_LIVE_BROWSER=1`) navigates `example.com`, captures a screenshot, and reads the page title against a real Chrome install — it passes. +A live end-to-end test (`TestBrowserLive`, opt-in via `HAWK_LIVE_BROWSER=1`) navigates `example.com`, captures a screenshot, and reads the page title against a real Chrome install — it passes. ## Category parity vs the field -| Category | graycode | Field leaders | +| Category | hawk | Field leaders | |---|---|---| -| File ops | read/write/edit/structured/multi/notebook (Go) | Claude Code/Qwen have notebook_edit; graycode has it | -| Search | grep/glob/ls + LSP + code_search/code_graph | OpenCode LSP; graycode code_graph exceeds | +| File ops | read/write/edit/structured/multi/notebook (Go) | Claude Code/Qwen have notebook_edit; hawk has it | +| Search | grep/glob/ls + LSP + code_search/code_graph | OpenCode LSP; hawk code_graph exceeds | | Shell | bash (+PowerShell on Windows) + monitor/kill/wait | all | | Web | web_fetch/web_search/agentic_fetch/download | all | | Memory | **persistent core_memory_\* (Harrier)** | only Qwen/Claude have persistence | @@ -77,10 +77,10 @@ A live end-to-end test (`TestBrowserLive`, opt-in via `GRAYCODE_LIVE_BROWSER=1`) ## Remaining deltas (out of scope here) - VS Code / JetBrains extension (planned in `docs/IMPLEMENTATION-ROADMAP.md`). -- Auto-commit-per-edit discipline à la Aider (graycode uses AGENTS.md branch rules instead). +- Auto-commit-per-edit discipline à la Aider (hawk uses AGENTS.md branch rules instead). ## Verification - `go build ./internal/tool/... ./cmd/...` — clean after adding the tools. -- `go test ./internal/tool/` — unit tests pass; live browser test passes with `GRAYCODE_LIVE_BROWSER=1`. +- `go test ./internal/tool/` — unit tests pass; live browser test passes with `HAWK_LIVE_BROWSER=1`. - `/tools` REPL command now reports essential/optional breakdown (enhanced in this change; see `cmd/diagnostics.go`). diff --git a/docs/platform-capabilities.json b/docs/platform-capabilities.json index d87aa7c06..179316987 100644 --- a/docs/platform-capabilities.json +++ b/docs/platform-capabilities.json @@ -51,13 +51,13 @@ "features": ["tools", "resources", "prompts", "sampling"] }, "ecosystem_compatibility": { - "graycode+graycode-router": "full", - "graycode+shrike": "full", - "graycode+kestrel": "full", - "graycode+merlin": "full", - "graycode+harrier": "full", - "graycode+swift": "full", - "graycode-router+shrike": "token_compression", + "hawk+eyrie": "full", + "hawk+shrike": "full", + "hawk+kestrel": "full", + "hawk+merlin": "full", + "hawk+harrier": "full", + "hawk+swift": "full", + "eyrie+shrike": "token_compression", "kestrel+merlin": "security_review" } } diff --git a/docs/plugin-development.md b/docs/plugin-development.md index bfa75246b..4f209f699 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -2,10 +2,10 @@ ## Overview -Graycode supports three plugin types: +Hawk supports three plugin types: 1. **Simple plugins** — shell scripts with a `plugin.json` manifest. Tools are executed as one-shot subprocesses via stdin/stdout. -2. **Managed subprocess plugins** — binary executables managed by `PluginManager`. Supports security scanning, timeout enforcement, and auto-discovery from `~/.graycode/plugins/`. +2. **Managed subprocess plugins** — binary executables managed by `PluginManager`. Supports security scanning, timeout enforcement, and auto-discovery from `~/.hawk/plugins/`. 3. **Daemon plugins** — long-lived processes communicating via JSON-RPC 2.0 over stdin/stdout. Managed by `DynamicPluginManager` with full lifecycle (Discover → Load → Activate → Failed/Disabled). ## Plugin Manifest @@ -51,7 +51,7 @@ For hooks, daemon mode, dependencies, and configuration: #### Hook priority -Hooks can specify `priority` (higher runs first). Event data is passed as environment variables (`GRAYCODE_EVENT=file.write`, `GRAYCODE_FILE_PATH=/path/to/file`). +Hooks can specify `priority` (higher runs first). Event data is passed as environment variables (`HAWK_EVENT=file.write`, `HAWK_FILE_PATH=/path/to/file`). #### Daemon mode @@ -59,7 +59,7 @@ Set `"mode": "daemon"` with `"entrypoint": "./my-daemon"`. The daemon receives J ## Skills -Skills are Markdown files with YAML frontmatter placed in `.graycode/skills//SKILL.md`: +Skills are Markdown files with YAML frontmatter placed in `.hawk/skills//SKILL.md`: ```yaml --- @@ -81,7 +81,7 @@ Skill instructions here using markdown... - **Auto-invoke**: Skills with `auto-invoke: true` and matching path/context globs are automatically activated. - **Chaining**: Skills can declare `chain.before` and `chain.after` for ordered execution. - **References**: Use `@ref(path/to/doc.md)` to reference supporting documents within a skill. -- **Cross-agent compatibility**: Skills follow the Agent Skills spec (agentskills.io) and work with graycode, Claude Code, and Codex. +- **Cross-agent compatibility**: Skills follow the Agent Skills spec (agentskills.io) and work with hawk, Claude Code, and Codex. ## Event Hooks @@ -89,27 +89,27 @@ Available events for plugin and skill hooks: | Event | Data | Description | |-------|------|-------------| -| `session.start` | `GRAYCODE_SESSION_ID` | Session began | -| `session.end` | `GRAYCODE_SESSION_ID`, `GRAYCODE_DURATION_MS` | Session ended | -| `turn.start` | `GRAYCODE_TURN_NUM` | Agent turn started | -| `turn.end` | `GRAYCODE_TURN_NUM`, `GRAYCODE_TOOL_COUNT` | Agent turn ended | -| `tool_call.start` | `GRAYCODE_TOOL_NAME`, `GRAYCODE_TOOL_INPUT` | Tool execution started | -| `tool_call.end` | `GRAYCODE_TOOL_NAME`, `GRAYCODE_TOOL_RESULT` | Tool execution finished | -| `tool_call.error` | `GRAYCODE_TOOL_NAME`, `GRAYCODE_ERROR` | Tool execution failed | -| `file.read` | `GRAYCODE_FILE_PATH` | File was read | -| `file.write` | `GRAYCODE_FILE_PATH` | File was written | -| `file.edit` | `GRAYCODE_FILE_PATH` | File was edited | -| `file.delete` | `GRAYCODE_FILE_PATH` | File was deleted | -| `compaction.start` | `GRAYCODE_COMPACTION_REASON` | Context compaction began | -| `compaction.end` | `GRAYCODE_COMPACTION_NEW_LENGTH` | Context compaction finished | -| `budget.warning` | `GRAYCODE_BUDGET_USAGE_PCT` | Token/cost budget warning | -| `budget.exceeded` | `GRAYCODE_BUDGET_TYPE` | Budget exceeded (hard stop) | -| `error.occurred` | `GRAYCODE_ERROR_TYPE` | Error occurred | -| `error.recovered` | `GRAYCODE_RECOVERY_STRATEGY` | Error was recovered | -| `model.switch` | `GRAYCODE_NEW_MODEL` | Active model changed | -| `provider.switch` | `GRAYCODE_NEW_PROVIDER` | Active provider changed | -| `user.input` | `GRAYCODE_USER_MESSAGE` | User sent a message | -| `agent.response` | `GRAYCODE_RESPONSE_LENGTH` | Agent generated a response | +| `session.start` | `HAWK_SESSION_ID` | Session began | +| `session.end` | `HAWK_SESSION_ID`, `HAWK_DURATION_MS` | Session ended | +| `turn.start` | `HAWK_TURN_NUM` | Agent turn started | +| `turn.end` | `HAWK_TURN_NUM`, `HAWK_TOOL_COUNT` | Agent turn ended | +| `tool_call.start` | `HAWK_TOOL_NAME`, `HAWK_TOOL_INPUT` | Tool execution started | +| `tool_call.end` | `HAWK_TOOL_NAME`, `HAWK_TOOL_RESULT` | Tool execution finished | +| `tool_call.error` | `HAWK_TOOL_NAME`, `HAWK_ERROR` | Tool execution failed | +| `file.read` | `HAWK_FILE_PATH` | File was read | +| `file.write` | `HAWK_FILE_PATH` | File was written | +| `file.edit` | `HAWK_FILE_PATH` | File was edited | +| `file.delete` | `HAWK_FILE_PATH` | File was deleted | +| `compaction.start` | `HAWK_COMPACTION_REASON` | Context compaction began | +| `compaction.end` | `HAWK_COMPACTION_NEW_LENGTH` | Context compaction finished | +| `budget.warning` | `HAWK_BUDGET_USAGE_PCT` | Token/cost budget warning | +| `budget.exceeded` | `HAWK_BUDGET_TYPE` | Budget exceeded (hard stop) | +| `error.occurred` | `HAWK_ERROR_TYPE` | Error occurred | +| `error.recovered` | `HAWK_RECOVERY_STRATEGY` | Error was recovered | +| `model.switch` | `HAWK_NEW_MODEL` | Active model changed | +| `provider.switch` | `HAWK_NEW_PROVIDER` | Active provider changed | +| `user.input` | `HAWK_USER_MESSAGE` | User sent a message | +| `agent.response` | `HAWK_RESPONSE_LENGTH` | Agent generated a response | ## Security @@ -123,16 +123,16 @@ All plugins are scanned on install for: 1. Push your plugin to a public GitHub repository. 2. The registry index (maintained at `starling/registry.json`) is periodically refreshed. -3. Users discover plugins via `graycode plugin search `. -4. Users install via `graycode plugin install `. +3. Users discover plugins via `hawk plugin search `. +4. Users install via `hawk plugin install `. 5. Registry installation includes automatic audit and security scanning. ## Local Development ```bash # Create a plugin directory -mkdir -p ~/.graycode/plugins/my-plugin -cd ~/.graycode/plugins/my-plugin +mkdir -p ~/.hawk/plugins/my-plugin +cd ~/.hawk/plugins/my-plugin # Create a manifest cat > plugin.json << 'EOF' @@ -147,14 +147,14 @@ cat > plugin.json << 'EOF' EOF # List installed plugins -graycode plugin list +hawk plugin list # Run a plugin command -graycode plugin exec my-plugin hello +hawk plugin exec my-plugin hello # Create a skill (project-local) -mkdir -p .graycode/skills/my-skill -cat > .graycode/skills/my-skill/SKILL.md << 'EOF' +mkdir -p .hawk/skills/my-skill +cat > .hawk/skills/my-skill/SKILL.md << 'EOF' --- name: my-skill description: My first skill @@ -165,4 +165,4 @@ EOF ## Feedback and Learning -Users can rate skills (1–5 stars) using `/skill rate `. Ratings are stored in `~/.graycode/feedback.json` and used by the `/learn` command to recommend skills based on project signals. +Users can rate skills (1–5 stars) using `/skill rate `. Ratings are stored in `~/.hawk/feedback.json` and used by the `/learn` command to recommend skills based on project signals. diff --git a/docs/session-decomposition.md b/docs/session-decomposition.md index ab49559a6..824417243 100644 --- a/docs/session-decomposition.md +++ b/docs/session-decomposition.md @@ -6,11 +6,11 @@ > into their owning services. > Author: opencode session > Date: 2026-06-12 -> Scope: `graycode/internal/engine/session.go` (the 35-collaborator `Session` struct) +> Scope: `hawk/internal/engine/session.go` (the 35-collaborator `Session` struct) ## Problem -`Session` at `graycode/internal/engine/session.go:42-141` is a god object: +`Session` at `hawk/internal/engine/session.go:42-141` is a god object: - 35 fields, ~30 of them `*T` pointer collaborators with optional behavior - Every new feature adds a field (Beliefs, Critic, Trajectory, Steer­ing, etc.) @@ -84,13 +84,13 @@ backends without an explicit migration and recovery decision. **Owns:** `client ChatClient`, `provider string`, `model string`, `apiKeys map[string]string`, `Router *modelPkg.Router`, `DeploymentRouting bool`, `RateLimiter *ratelimit.Limiter`, `OutputSchema string`, `GLMThinkingEnabled *bool`, `ContCfg types.ContinuationConfig`, `ChatOptions{}` builder. **Methods:** -- `BuildOptions(systemPrompt string, tools []client.GraycodeRouterTool, activeModel string, maxTokens int) types.ChatOptions` -- `Chat(ctx, msgs, opts) (*types.GraycodeRouterResponse, error)` — non-streaming, used by background goroutines +- `BuildOptions(systemPrompt string, tools []client.EyrieTool, activeModel string, maxTokens int) types.ChatOptions` +- `Chat(ctx, msgs, opts) (*types.EyrieResponse, error)` — non-streaming, used by background goroutines - `Stream(ctx, msgs, opts) (*types.StreamResult, error)` — with retry, circuit-breaker record, rate-limit wait - `RecordSuccess(latency time.Duration)` / `RecordFailure(err error)` - `BuildContinuationConfig() types.ContinuationConfig` -**File:** `graycode/internal/engine/chat_service.go` (~150 LOC) +**File:** `hawk/internal/engine/chat_service.go` (~150 LOC) ### 2. `MemoryService` — owns harrier bridge + recall/remember @@ -99,12 +99,12 @@ backends without an explicit migration and recovery decision. **Methods:** - `RecallContext(ctx, lastUserMsg string, budget int) string` — unifies backend recall behind one nil-safe call - `Remember(ctx, content, category string)` — wraps memory.Remember -- `RunSleeptimeConsolidation(ctx, provider ChatService, messages []types.GraycodeRouterMessage)` — background +- `RunSleeptimeConsolidation(ctx, provider ChatService, messages []types.EyrieMessage)` — background - `RunSkillDistillation(ctx, provider ChatService, ...)` — background - `RecordFeedback(ctx, content string, action string)` — wraps EnhancedMemory feedback - `ShouldRemember(text string) bool` — heuristic for auto-remember -**File:** `graycode/internal/engine/memory_service.go` (~200 LOC) +**File:** `hawk/internal/engine/memory_service.go` (~200 LOC) ### 3. `ToolService` — owns the registry + tool execution @@ -117,7 +117,7 @@ backends without an explicit migration and recovery decision. - `SpawnBackgroundAgent(ctx, prompt string) (id string, err error)` - `PollBackgroundAgent(id string) (output string, done bool, err error)` -**File:** `graycode/internal/engine/tool_service.go` (~400 LOC — biggest, owns the 15-stage pipeline) +**File:** `hawk/internal/engine/tool_service.go` (~400 LOC — biggest, owns the 15-stage pipeline) ### 4. `PermissionService` — owns the safety layer @@ -131,7 +131,7 @@ backends without an explicit migration and recovery decision. - `SetMode(mode string) error` — validates + applies - `SetMaxTurns(n int)`, `SetMaxBudgetUSD(usd float64)` -**File:** `graycode/internal/engine/permission_service.go` (~150 LOC) +**File:** `hawk/internal/engine/permission_service.go` (~150 LOC) ### 5. `LifecycleService` — owns the self-improvement loop @@ -139,16 +139,16 @@ backends without an explicit migration and recovery decision. **Methods:** - `OnSessionStart(ctx, lastUserMsg string) (learnedCtx string)` -- `OnSessionEnd(ctx, success bool, duration time.Duration, messages []types.GraycodeRouterMessage)` +- `OnSessionEnd(ctx, success bool, duration time.Duration, messages []types.EyrieMessage)` - `SelectModel(taskType, currentModel, hint string) string` — wraps Cascade -- `OnToolFailure(ctx, intent string, history []types.GraycodeRouterMessage, errOutput string) (*Reflection, error)` -- `RecordDecision(turn int, toolNames string, msgs []types.GraycodeRouterMessage)` +- `OnToolFailure(ctx, intent string, history []types.EyrieMessage, errOutput string) (*Reflection, error)` +- `RecordDecision(turn int, toolNames string, msgs []types.EyrieMessage)` - `CheckLimits(turnCount int) (cont bool, reason string)` — wraps Limits - `RecordTurn(turn int)`, `RecordToolCall(name string)` - `DetectDoomLoop(steps []ToolStep) bool` -- `InjectSteering(messages []types.GraycodeRouterMessage) []types.GraycodeRouterMessage` +- `InjectSteering(messages []types.EyrieMessage) []types.EyrieMessage` -**File:** `graycode/internal/engine/lifecycle_service.go` (~250 LOC) +**File:** `hawk/internal/engine/lifecycle_service.go` (~250 LOC) ### 6. `PersistenceService` — owns checkpoint, session, harrier snapshot @@ -161,13 +161,13 @@ backends without an explicit migration and recovery decision. - `AddUser(content string)`, `AddAssistant(content string)`, `AddUserWithImage(...)`, `AddUserWithAttachment(...)` - `AddUserWithDocumentText(content, label, extracted string)` - `AppendSystemContext(content string)`, `ReplaceSystemContextSection(header, content string)` -- `Messages() []types.GraycodeRouterMessage`, `RawMessages()`, `MessageCount()`, `LoadMessages([]types.GraycodeRouterMessage)` +- `Messages() []types.EyrieMessage`, `RawMessages()`, `MessageCount()`, `LoadMessages([]types.EyrieMessage)` - `RemoveLastExchange()` - `ConvoHead() string`, `ForkConversation(nodeID string) (string, error)`, `SwitchBranch(nodeID string) error`, `ListBranches(nodeID string) ([]*storage.DAGNode, error)` - `CheckpointOnCompaction(strategy string, before, after int, manual bool)` - `TokenTracking() (prompt, completion int)`, `RecordAPIUsage(prompt, completion int)` -**File:** `graycode/internal/engine/persistence_service.go` (~300 LOC) +**File:** `hawk/internal/engine/persistence_service.go` (~300 LOC) ## What stays on `Session` @@ -223,7 +223,7 @@ func (s *Session) agentLoop(ctx context.Context, ch chan<- StreamEvent) { for turn := 0; ; turn++ { if !s.Lifecycle.CheckLimits(turn) { return } - opts := s.Chat.BuildOptions(s.system, s.Tools.Registry().GraycodeRouterTools(), activeModel, maxTok) + opts := s.Chat.BuildOptions(s.system, s.Tools.Registry().EyrieTools(), activeModel, maxTok) result, err := s.Chat.Stream(ctx, s.Persistence.Messages(), opts) if err != nil { /* error path */ } defer result.Close() @@ -257,7 +257,7 @@ The loop body shrinks from 800 lines to ~250 because: 3. **Phase 3**: extract `MemoryService` (most fields, but only consumed in the preamble/postamble of `agentLoop`). 4. **Phase 4**: extract `PermissionService` (smallest surface). 5. **Phase 5**: extract `LifecycleService` (biggest behavioral surface, but mostly reads `messages` and `tools`). -6. **Phase 6**: extract `PersistenceService` (last because it owns the `messages []types.GraycodeRouterMessage` field which is touched everywhere). +6. **Phase 6**: extract `PersistenceService` (last because it owns the `messages []types.EyrieMessage` field which is touched everywhere). 7. **Phase 7**: extract `ToolService` (depends on 4, 5; do last). 8. **Phase 8**: delete fields from `Session`, add backwards-compatible shims that delegate to the new services, mark for removal in v0.2.0. diff --git a/docs/terminal-icons.md b/docs/terminal-icons.md index ad2a70b02..24be39fad 100644 --- a/docs/terminal-icons.md +++ b/docs/terminal-icons.md @@ -1,6 +1,6 @@ # Terminal icons -Graycode uses current Nerd Font Codicon glyphs for interactive terminal output. +Hawk uses current Nerd Font Codicon glyphs for interactive terminal output. The application does not try to infer the installed font from `TERM`: terminal names do not report the active font, and guessing can produce tiny fallback boxes or missing glyphs. @@ -10,14 +10,14 @@ Interactive TTYs use Nerd Font icons by default. Captured output, CI, and ```bash # Real icons (requires a Nerd Font configured in the terminal profile) -GRAYCODE_ICONS=nerd ./bin/graycode +HAWK_ICONS=nerd ./bin/hawk # Portable text-only output -GRAYCODE_ICONS=ascii ./bin/graycode +HAWK_ICONS=ascii ./bin/hawk ``` -For the real icons, configure the terminal profile—not Graycode's Go code—with a +For the real icons, configure the terminal profile—not Hawk's Go code—with a patched font such as `JetBrainsMono Nerd Font` or `Symbols Nerd Font Mono`. -Font size and glyph scale are controlled by that profile. Graycode applies bold +Font size and glyph scale are controlled by that profile. Hawk applies bold weight to status icons for contrast, but there is no portable ANSI escape that can resize one glyph independently of the surrounding text. diff --git a/docs/troubleshooting-guide.md b/docs/troubleshooting-guide.md index 8c3d1cc2c..0ef4f3eae 100644 --- a/docs/troubleshooting-guide.md +++ b/docs/troubleshooting-guide.md @@ -1,6 +1,6 @@ # Troubleshooting Guide -A practical guide for diagnosing common graycode daemon and CLI issues. +A practical guide for diagnosing common hawk daemon and CLI issues. ## Table of Contents @@ -30,27 +30,27 @@ because the auth middleware would be open to the network. **Fix:** Set an API key: ```bash -export GRAYCODE_DAEMON_API_KEY=$(openssl rand -base64 32) -graycode daemon start --host 0.0.0.0 --port 4590 +export HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) +hawk daemon start --host 0.0.0.0 --port 4590 ``` Or bind to loopback only (no API key required, but not remotely accessible): ```bash -graycode daemon start --host 127.0.0.1 --port 4590 +hawk daemon start --host 127.0.0.1 --port 4590 ``` ### "permission denied" on state directory -The daemon writes logs, PID files, and the audit log to `~/.graycode/state/`. +The daemon writes logs, PID files, and the audit log to `~/.hawk/state/`. **Fix:** ```bash -mkdir -p ~/.graycode/state -chmod 750 ~/.graycode/state -# If running under systemd as user 'graycode', ensure ownership: -chown -R graycode:graycode ~/.graycode +mkdir -p ~/.hawk/state +chmod 750 ~/.hawk/state +# If running under systemd as user 'hawk', ensure ownership: +chown -R hawk:hawk ~/.hawk ``` ### "port already in use" @@ -64,7 +64,7 @@ Another process is using port 4590. lsof -i :4590 # Or use a different port -graycode daemon start --port 4591 +hawk daemon start --port 4591 ``` --- @@ -73,7 +73,7 @@ graycode daemon start --port 4591 ### `GET /v1/ready` returns 503 -The readiness probe fails when GraycodeRouter's local preflight doesn't pass. This +The readiness probe fails when Eyrie's local preflight doesn't pass. This checks provider state, catalog, credentials, and model selection. **Diagnosis:** @@ -84,9 +84,9 @@ curl -v http://localhost:4590/v1/ready Check the response body for the specific failed check. Common causes: -- No model configured — set `GRAYCODE_MODEL` or provider credentials. -- GraycodeRouter catalog not initialized — the nine Go modules are independent sibling - repositories; from a full parent workspace, run `make setup` in graycode to +- No model configured — set `HAWK_MODEL` or provider credentials. +- Eyrie catalog not initialized — the nine Go modules are independent sibling + repositories; from a full parent workspace, run `make setup` in hawk to regenerate the parent `go.work`: ```bash make setup @@ -103,11 +103,11 @@ failed to start. ```bash # Check if the process is running -ps aux | grep graycode +ps aux | grep hawk # Check logs -journalctl -u graycode-daemon -n 50 -tail -50 ~/.graycode/state/daemon.log +journalctl -u hawk-daemon -n 50 +tail -50 ~/.hawk/state/daemon.log ``` --- @@ -121,7 +121,7 @@ The daemon requires `Authorization: Bearer ` or `X-API-Key: `. **Fix:** ```bash -curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" http://localhost:4590/v1/stats +curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/stats ``` ### "constant time comparison" errors in logs @@ -132,16 +132,16 @@ No action needed. ### Forgotten API key -The API key is written to `~/.graycode/state/daemon.key` (permissions 0600). +The API key is written to `~/.hawk/state/daemon.key` (permissions 0600). ```bash -cat ~/.graycode/state/daemon.key +cat ~/.hawk/state/daemon.key ``` **Security note:** Remove this file in production after initial testing: ```bash -rm ~/.graycode/state/daemon.key +rm ~/.hawk/state/daemon.key ``` --- @@ -153,7 +153,7 @@ your request. The default limits are: - **General API**: 10 req/min, burst 4 - **Chat**: 30 req/min, burst 6 -- **Concurrent chat sessions**: 4 (configurable via `GRAYCODE_DAEMON_MAX_CONCURRENT`) +- **Concurrent chat sessions**: 4 (configurable via `HAWK_DAEMON_MAX_CONCURRENT`) **Fix:** @@ -171,7 +171,7 @@ your request. The default limits are: CORS is **disabled by default**. Enable it when serving browser-based clients: ```bash -graycode daemon start --cors https://app.example.com --cors https://admin.example.com +hawk daemon start --cors https://app.example.com --cors https://admin.example.com ``` Use `--cors '*'` only for development — it allows any origin. @@ -182,7 +182,7 @@ The CORS middleware handles OPTIONS preflight automatically when the `cors` feature flag is enabled. If you're getting 405, ensure CORS is enabled: ```bash -export GRAYCODE_FEATURE_CORS=1 +export HAWK_FEATURE_CORS=1 ``` --- @@ -198,7 +198,7 @@ mode) or when the engine is not ready (daemon mode). curl http://localhost:4590/v1/ready ``` -If readiness fails, the session factory exists but GraycodeRouter preflight is not +If readiness fails, the session factory exists but Eyrie preflight is not satisfied (missing model, credentials, etc.). --- @@ -211,7 +211,7 @@ The metrics endpoint is protected by the same API key authentication as other endpoints. ```bash -curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" http://localhost:4590/v1/metrics +curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/metrics ``` ### Metrics output is empty @@ -227,7 +227,7 @@ Prometheus version supports this format (Prometheus 2.20+). For troubleshooting, try the JSON format: ```bash -curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ +curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" \ "http://localhost:4590/v1/metrics?format=json" ``` @@ -240,13 +240,13 @@ curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ Telemetry is **opt-in**. You must explicitly enable it: ```bash -export GRAYCODE_ENABLE_TELEMETRY=1 +export HAWK_ENABLE_TELEMETRY=1 export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -graycode daemon start +hawk daemon start ``` **Important**: Setting `OTEL_EXPORTER_OTLP_ENDPOINT` alone does **not** -enable telemetry. The `GRAYCODE_ENABLE_TELEMETRY=1` flag is required. +enable telemetry. The `HAWK_ENABLE_TELEMETRY=1` flag is required. ### "telemetry initialization failed" warning in logs @@ -265,7 +265,7 @@ curl -v http://localhost:4318/v1/traces The OTel SDK uses a batch span processor with a 5-second flush interval. On shutdown, the daemon waits up to 2 seconds (configurable via -`GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS`) to flush pending spans. +`HAWK_OTEL_SHUTDOWN_TIMEOUT_MS`) to flush pending spans. To force a flush, send SIGTERM to the daemon — it will flush telemetry before shutting down. @@ -276,25 +276,25 @@ before shutting down. ### Security log won't open -The audit log is stored in `~/.graycode/state/securitylog/`. If the directory +The audit log is stored in `~/.hawk/state/securitylog/`. If the directory doesn't exist or isn't writable: ```bash -mkdir -p ~/.graycode/state/securitylog -chmod 700 ~/.graycode/state/securitylog +mkdir -p ~/.hawk/state/securitylog +chmod 700 ~/.hawk/state/securitylog ``` ### "log tail does not match head pointer (truncated or tampered)" This error means the security log has been modified or truncated. The tamper-evident design detected an inconsistency. Restore from a backup -of the `~/.graycode/state/securitylog/` directory. +of the `~/.hawk/state/securitylog/` directory. ### Lost the HMAC key The HMAC key (`sel.key`) is required to verify the audit log. If it's lost, all entries become unverifiable. **Always back up the entire -`~/.graycode/state/securitylog/` directory.** +`~/.hawk/state/securitylog/` directory.** --- @@ -306,13 +306,13 @@ Tools that require sandboxing are disabled until the sandbox container is running. Check the sandbox status: ```bash -graycode sandbox status +hawk sandbox status ``` Start the sandbox: ```bash -graycode sandbox start +hawk sandbox start ``` ### Permission denied for a tool @@ -328,13 +328,13 @@ audit log for the `denied` event type. 1. Check the metrics endpoint for request duration: ```bash - curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ + curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" \ "http://localhost:4590/v1/metrics?format=json" ``` -2. Check concurrent sessions: `graycode_daemon_chat_concurrency_used` +2. Check concurrent sessions: `hawk_daemon_chat_concurrency_used` 3. If at capacity, increase the concurrency limit: ```bash - export GRAYCODE_DAEMON_MAX_CONCURRENT=8 + export HAWK_DAEMON_MAX_CONCURRENT=8 ``` ### High memory usage @@ -352,7 +352,7 @@ large contexts can consume significant memory. Consider: ### Daemon exits immediately -The default entrypoint runs `graycode daemon start --host 0.0.0.0 --port 4590`. +The default entrypoint runs `hawk daemon start --host 0.0.0.0 --port 4590`. Non-loopback binds require both an API key and native TLS; an API key alone does not protect credentials or conversation data from plaintext interception. @@ -360,9 +360,9 @@ does not protect credentials or conversation data from plaintext interception. ```bash docker run -p 4590:4590 \ - -e GRAYCODE_DAEMON_API_KEY=$(openssl rand -base64 32) \ + -e HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) \ -v "$PWD/certs:/certs:ro" \ - ghcr.io/graycodeai/graycode-daemon:latest \ + ghcr.io/graycodeai/hawk-daemon:latest \ --tls-cert /certs/server.crt --tls-key /certs/server.key ``` @@ -377,35 +377,35 @@ or start-period if needed. ## Systemd Issues -### "Failed to start graycode-daemon.service: Unit not found" +### "Failed to start hawk-daemon.service: Unit not found" Install the unit file: ```bash -sudo cp packaging/systemd/graycode-daemon.service /etc/systemd/system/ +sudo cp packaging/systemd/hawk-daemon.service /etc/systemd/system/ sudo systemctl daemon-reload -sudo systemctl enable --now graycode-daemon +sudo systemctl enable --now hawk-daemon ``` ### "Permission denied" accessing state directory -The systemd unit runs as user `graycode` with `ProtectSystem=strict` and -`ReadWritePaths=%h/.graycode/state`. Ensure the user's home directory has +The systemd unit runs as user `hawk` with `ProtectSystem=strict` and +`ReadWritePaths=%h/.hawk/state`. Ensure the user's home directory has the correct state: ```bash -sudo -u graycode mkdir -p /home/graycode/.graycode/state -sudo -u graycode chmod 750 /home/graycode/.graycode/state +sudo -u hawk mkdir -p /home/hawk/.hawk/state +sudo -u hawk chmod 750 /home/hawk/.hawk/state ``` ### Daemon not logging to journald If using the systemd unit, logs go to both journald (stdout/stderr) and -`~/.graycode/state/daemon.log`. Check: +`~/.hawk/state/daemon.log`. Check: ```bash -journalctl -u graycode-daemon -f -tail -f ~/.graycode/state/daemon.log +journalctl -u hawk-daemon -f +tail -f ~/.hawk/state/daemon.log ``` If journald logs are missing, verify that stdout/stderr are not being diff --git a/docs/user-guide/01-getting-started.md b/docs/user-guide/01-getting-started.md index 6d7973615..a3c01d0ec 100644 --- a/docs/user-guide/01-getting-started.md +++ b/docs/user-guide/01-getting-started.md @@ -1,25 +1,25 @@ # Getting Started -Graycode is an AI-powered coding agent for your terminal, built for developers by GrayCode AI. It understands your codebase, executes shell commands, edits files, searches the web, and manages tasks — all through natural language. +Hawk is an AI-powered coding agent for your terminal, built for developers by GrayCode AI. It understands your codebase, executes shell commands, edits files, searches the web, and manages tasks — all through natural language. -You can use Graycode interactively as a full-screen TUI, run it headlessly for scripting and CI/CD, or integrate it into editors via the Agent Client Protocol (ACP). +You can use Hawk interactively as a full-screen TUI, run it headlessly for scripting and CI/CD, or integrate it into editors via the Agent Client Protocol (ACP). --- ## Installation -Graycode is currently in active development. Contributor source builds are the primary path while we harden the product in the open. +Hawk is currently in active development. Contributor source builds are the primary path while we harden the product in the open. ### From Source (Recommended for Contributors) ```bash -git clone https://github.com/GrayCodeAI/graycode-cli && cd graycode-cli -GOWORK=off go build -o graycode ./cmd/graycode -./graycode +git clone https://github.com/GrayCodeAI/hawk && cd hawk +GOWORK=off go build -o hawk ./cmd/hawk +./hawk ``` For full cross-repository development, place the nine Go repositories beside -Graycode in a parent workspace and run `make setup` from `graycode-cli`; `graycode-eco` is +Hawk in a parent workspace and run `make setup` from `hawk`; `graycode-eco` is only a local folder name, not a repository containing those modules. ### Verification @@ -27,7 +27,7 @@ only a local folder name, not a repository containing those modules. Run the developer path check to verify your setup: ```bash -./graycode path +./hawk path ``` This checks setup, security, and sandbox readiness. @@ -36,15 +36,15 @@ This checks setup, security, and sandbox readiness. ## First Launch -Start Graycode by running: +Start Hawk by running: ```bash -graycode +hawk ``` -On first launch, Graycode opens a TUI where you can configure credentials. Press `/config` (or `/autonomy`) to open the configuration picker. Your API keys are stored in your OS keychain (macOS Keychain or Linux keyring), never in plain text. +On first launch, Hawk opens a TUI where you can configure credentials. Press `/config` (or `/autonomy`) to open the configuration picker. Your API keys are stored in your OS keychain (macOS Keychain or Linux keyring), never in plain text. -Graycode supports multiple providers: +Hawk supports multiple providers: - **xAI Grok** — `XAI_API_KEY` - **Anthropic Claude** — `ANTHROPIC_API_KEY` @@ -58,12 +58,12 @@ See [Authentication](02-authentication.md) for the full set of auth options incl ## Basic Interaction -Once authenticated, Graycode presents a full-screen TUI powered by Bubble Tea with two main areas: +Once authenticated, Hawk presents a full-screen TUI powered by Bubble Tea with two main areas: -- **Scrollback** — the conversation history showing your prompts, Graycode's responses, tool calls, file edits, and more +- **Scrollback** — the conversation history showing your prompts, Hawk's responses, tool calls, file edits, and more - **Prompt** — the input area at the bottom where you type messages -Type a message and press `Enter` to send it. Graycode reads files, runs commands, and edits code as needed. Each tool run streams into the scrollback in real time. +Type a message and press `Enter` to send it. Hawk reads files, runs commands, and edits code as needed. Each tool run streams into the scrollback in real time. Press `Tab` to move focus between the prompt and the scrollback. While a turn is running, `Ctrl+C` cancels it. In Vim mode, use `j`/`k` to navigate and `h`/`l` to collapse/expand entries. @@ -81,7 +81,7 @@ The `@` operator opens a fuzzy file picker. By default it respects `.gitignore` ### Permissions -Graycode exposes two independent control surfaces: +Hawk exposes two independent control surfaces: - **`/autonomy`** — Controls trust tier (Always Ask, Scout, Builder, Operator, Autonomous) and sandbox profile (strict, workspace, off) - **`/spec`** — A workflow gate that blocks Write/Edit/Bash until you approve implementation @@ -106,21 +106,21 @@ Every conversation is a **session**. Sessions are automatically saved and can be - Start a new session: `Ctrl+N` or `/new` - Resume a previous session: `/resume` in the TUI, or `--resume ` from the CLI -- Continue the most recent session: `graycode -c` +- Continue the most recent session: `hawk -c` ### Scrollback The scrollback shows: - **User prompts** — your messages, rendered as sticky headers -- **Agent messages** — Graycode's responses with markdown rendering -- **Thinking blocks** — Graycode's reasoning process (collapsible) +- **Agent messages** — Hawk's responses with markdown rendering +- **Thinking blocks** — Hawk's reasoning process (collapsible) - **Tool calls** — file edits, command executions, search results - **Task lists** — TODO items tracking progress ### Tools -Graycode has built-in tools for: +Hawk has built-in tools for: | Tool | Description | |------|-------------| @@ -153,44 +153,44 @@ See [Slash Commands](04-slash-commands.md) for the complete reference. ```bash # Start the interactive TUI -graycode +hawk # Submit an initial prompt as the first turn -graycode "fix the failing auth test and run it" +hawk "fix the failing auth test and run it" # Start in a specific project directory -graycode --cwd ~/projects/my-app +hawk --cwd ~/projects/my-app # Resume a previous session -graycode -r abc123 +hawk -r abc123 # Continue the most recent session -graycode -c +hawk -c # Use a specific provider/model -graycode --provider openai --model gpt-4o +hawk --provider openai --model gpt-4o # Isolated worktree for changes -graycode --worktree "refactor module X" +hawk --worktree "refactor module X" # Non-interactive (headless) mode -graycode -p "Explain this codebase" +hawk -p "Explain this codebase" # Full auto mode -graycode exec --auto full "add error handling" +hawk exec --auto full "add error handling" # Dry-run mode (denies all tools) -graycode exec --autonomy dry-run "What would this do?" +hawk exec --autonomy dry-run "What would this do?" ``` --- ## Headless Mode -Run Graycode non-interactively for scripting, CI/CD, and automation: +Run Hawk non-interactively for scripting, CI/CD, and automation: ```bash -graycode -p "Your prompt here" +hawk -p "Your prompt here" ``` Output formats: @@ -205,15 +205,15 @@ Output formats: ## Project Rules (AGENTS.md) -Add per-project instructions by creating an `AGENTS.md` file in your repository. Graycode reads these files and injects their contents as a project-instructions message at the start of the conversation: +Add per-project instructions by creating an `AGENTS.md` file in your repository. Hawk reads these files and injects their contents as a project-instructions message at the start of the conversation: ``` -~/.graycode/AGENTS.md # Global rules (apply to all projects) +~/.hawk/AGENTS.md # Global rules (apply to all projects) /AGENTS.md # Repository-level rules /AGENTS.md # Directory-level rules (highest priority) ``` -Deeper files take precedence. Graycode also reads `CLAUDE.md` files for compatibility. +Deeper files take precedence. Hawk also reads `CLAUDE.md` files for compatibility. --- diff --git a/docs/user-guide/02-authentication.md b/docs/user-guide/02-authentication.md index 72190201b..a8de81e42 100644 --- a/docs/user-guide/02-authentication.md +++ b/docs/user-guide/02-authentication.md @@ -1,14 +1,14 @@ # Authentication -Graycode supports several authentication methods, including API key configuration through the TUI and multi-provider support via GraycodeRouter. +Hawk supports several authentication methods, including API key configuration through the TUI and multi-provider support via Eyrie. --- ## API Key Configuration -On first launch, Graycode opens the TUI where you can configure credentials. Press `/config` or `/autonomy` to open the configuration picker. Your API keys are stored in your OS keychain (macOS Keychain or Linux keyring), never in plain text or environment variables. +On first launch, Hawk opens the TUI where you can configure credentials. Press `/config` or `/autonomy` to open the configuration picker. Your API keys are stored in your OS keychain (macOS Keychain or Linux keyring), never in plain text or environment variables. -Graycode supports multiple providers: +Hawk supports multiple providers: | Provider | ID | Key | |----------|----|-----| @@ -24,10 +24,10 @@ Graycode supports multiple providers: ```bash # Verify credential status -graycode credentials status +hawk credentials status # Run the TUI and use /config to set keys interactively -graycode +hawk ``` ### Environment Variables (Fallback) @@ -36,7 +36,7 @@ For CI/CD or headless environments, you can set API keys as environment variable ```bash export XAI_API_KEY="xai-..." -graycode +hawk ``` Fireworks uses its OpenAI-compatible API. Set `FIREWORKS_API_KEY`; the default @@ -49,19 +49,19 @@ base URL is `https://api.fireworks.ai/inference/v1`. See the official ## Provider Configuration -Graycode uses GraycodeRouter for provider routing, health checks, and retry logic. To configure providers: +Hawk uses Eyrie for provider routing, health checks, and retry logic. To configure providers: ```bash # In the TUI, press /config to open provider settings -graycode +hawk # Or validate readiness -graycode path +hawk path ``` ### Deployment-Aware Routing -For deployment-aware routing, set in `.graycode/settings.json`: +For deployment-aware routing, set in `.hawk/settings.json`: ```json { @@ -72,10 +72,10 @@ For deployment-aware routing, set in `.graycode/settings.json`: Or export: ```bash -export GRAYCODE_DEPLOYMENT_ROUTING=true +export HAWK_DEPLOYMENT_ROUTING=true ``` -Graycode will route canonical model IDs through GraycodeRouter's deployment catalog. Refresh the catalog with: +Hawk will route canonical model IDs through Eyrie's deployment catalog. Refresh the catalog with: ``` /refresh-model-catalog @@ -90,7 +90,7 @@ Authenticate developers through your own Identity Provider (IdP) — such as Okt ### Configure via Settings ```json -// .graycode/settings.json +// .hawk/settings.json { "oidc": { "issuer": "https://acme.okta.com", @@ -99,7 +99,7 @@ Authenticate developers through your own Identity Provider (IdP) — such as Okt } ``` -Graycode discovers endpoints via `{issuer}/.well-known/openid-configuration`, opens the IdP login page, and stores tokens in the keychain. Tokens auto-refresh silently via the stored `refresh_token`. +Hawk discovers endpoints via `{issuer}/.well-known/openid-configuration`, opens the IdP login page, and stores tokens in the keychain. Tokens auto-refresh silently via the stored `refresh_token`. ### Required Scopes @@ -116,7 +116,7 @@ When browser-based login isn't possible — for example, on sandboxed VMs, CI ru ### How It Works -1. Graycode runs your command via `sh -c ""` +1. Hawk runs your command via `sh -c ""` 2. Your binary runs whatever auth flow it needs 3. **stdout** is captured and parsed as an access token 4. **stderr** carries human-readable output surfaced to the user @@ -139,7 +139,7 @@ JSON with optional refresh token: ### Configuration ```json -// .graycode/settings.json +// .hawk/settings.json { "auth_provider_command": "/usr/local/bin/my-auth-provider", "auth_provider_label": "Acme Corp" @@ -149,17 +149,17 @@ JSON with optional refresh token: Or via environment variables: ```bash -export GRAYCODE_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider" -export GRAYCODE_AUTH_PROVIDER_LABEL="Acme Corp" +export HAWK_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider" +export HAWK_AUTH_PROVIDER_LABEL="Acme Corp" ``` ### Token Refresh -When Graycode needs to refresh an expired token, it re-runs your binary with `GRAYCODE_AUTH_EXPIRED=1` set in the environment: +When Hawk needs to refresh an expired token, it re-runs your binary with `HAWK_AUTH_EXPIRED=1` set in the environment: ```bash #!/bin/sh -if [ "$GRAYCODE_AUTH_EXPIRED" = "1" ]; then +if [ "$HAWK_AUTH_EXPIRED" = "1" ]; then echo "Refreshing token..." >&2 TOKEN=$(my-company-auth --refresh --silent) else @@ -182,16 +182,16 @@ echo "{\"access_token\": \"$TOKEN\", \"expires_in\": 3600}" Check credential status at any time: ```bash -graycode credentials status +hawk credentials status ``` -This verifies keychain entries and validates GraycodeRouter's provider status. +This verifies keychain entries and validates Eyrie's provider status. --- ## Credential Precedence -Graycode resolves credentials in this order: +Hawk resolves credentials in this order: 1. **Per-model configuration** — set via `/config` or settings 2. **Keychain entry** — obtained through TUI configuration @@ -203,7 +203,7 @@ During a session, the active method handles all refreshes. ## Multi-Provider Support -Graycode works with any LLM provider through GraycodeRouter's adapter system: +Hawk works with any LLM provider through Eyrie's adapter system: | Provider | Status | |----------|--------| diff --git a/docs/user-guide/03-keyboard-shortcuts.md b/docs/user-guide/03-keyboard-shortcuts.md index 3ba2b2ea2..9ad63033e 100644 --- a/docs/user-guide/03-keyboard-shortcuts.md +++ b/docs/user-guide/03-keyboard-shortcuts.md @@ -1,12 +1,12 @@ # Keyboard Shortcuts -Reference for key bindings in the Graycode TUI. Bindings are built-in and cannot currently be remapped. +Reference for key bindings in the Hawk TUI. Bindings are built-in and cannot currently be remapped. --- ## Input Modes -Graycode has two input modes that control how you navigate the scrollback: +Hawk has two input modes that control how you navigate the scrollback: - **Simple mode** (default): Arrow keys for navigation, `Shift+Arrow` for turn navigation, `Space` to focus the prompt - **Vim mode** (opt-in): `j`/`k` for navigation, `H`/`L` for turn navigation, `h`/`l` for fold, `Tab` to focus the prompt @@ -17,7 +17,7 @@ Simple mode is active by default. To switch to Vim mode: /vim-mode ``` -Or set `vim_mode = true` under `[ui]` in `~/.graycode/settings.json`: +Or set `vim_mode = true` under `[ui]` in `~/.hawk/settings.json`: ```json { diff --git a/docs/user-guide/04-slash-commands.md b/docs/user-guide/04-slash-commands.md index 0d98d9552..0c5c85c59 100644 --- a/docs/user-guide/04-slash-commands.md +++ b/docs/user-guide/04-slash-commands.md @@ -204,7 +204,7 @@ Install a skill from a source. ``` /skills install go-review -graycode skills install go-review +hawk skills install go-review ``` ### `/skills audit` @@ -213,7 +213,7 @@ Security scan installed skills. ``` /skills audit -graycode skills audit +hawk skills audit ``` --- @@ -359,7 +359,7 @@ Quick ready-to-chat check. ### `/ecosystem` -Show ecosystem component status (GraycodeRouter, harrier, shrike). +Show ecosystem component status (Eyrie, harrier, shrike). ``` /ecosystem @@ -455,7 +455,7 @@ Toggle vim-style keybindings. ### `/refresh-model-catalog` -Fetch the latest deployment-aware model catalog from GraycodeRouter. +Fetch the latest deployment-aware model catalog from Eyrie. ``` /refresh-model-catalog diff --git a/docs/user-guide/05-configuration.md b/docs/user-guide/05-configuration.md index 942042d0f..253d01866 100644 --- a/docs/user-guide/05-configuration.md +++ b/docs/user-guide/05-configuration.md @@ -1,6 +1,6 @@ # Configuration -Graycode reads configuration from settings files, environment variables, and has defaults for all options. This document covers the common configuration options. +Hawk reads configuration from settings files, environment variables, and has defaults for all options. This document covers the common configuration options. --- @@ -11,18 +11,18 @@ Configuration is resolved in this order (highest priority first): 1. **CLI `--settings` JSON override** (`LoadSettingsWithOverride`) 2. **Per-command CLI flags** (e.g., `--provider`, `--model`) 3. **Environment variables** (only where explicitly read; there is no global env layer) -4. **Project settings** (`.graycode/settings.json`, repository-safe subset only — +4. **Project settings** (`.hawk/settings.json`, repository-safe subset only — `model`, `provider`, permissions, MCP servers and providers are stripped) -5. **User settings** (`~/.graycode/settings.json`) +5. **User settings** (`~/.hawk/settings.json`) 6. **Built-in defaults** --- ## Settings File -Location: `~/.graycode/settings.json` +Location: `~/.hawk/settings.json` -This is the main configuration file. Graycode writes to it when you save changes via `/config` or `/autonomy save`. +This is the main configuration file. Hawk writes to it when you save changes via `/config` or `/autonomy save`. ### Basic Settings @@ -53,7 +53,7 @@ This is the main configuration file. Graycode writes to it when you save changes - `operator` — full tool access for trusted operations - `autonomous` — no permission prompts -**Sandbox profiles** control permissions inside Graycode's mandatory Docker +**Sandbox profiles** control permissions inside Hawk's mandatory Docker execution boundary: - `off` — no additional policy restrictions - `workspace` — filesystem access limited to project directory @@ -112,34 +112,34 @@ Key environment variables for configuration. | Variable | Description | |----------|-------------| -| `GRAYCODE_Y0_FOLDER_TRUST` | Folder trust feature flag (default: `1`) | -| `GRAYCODE_Y0_MARKETPLACE` | Marketplace feature flag (default: `1`, set `0` to disable remote installs) | -| `GRAYCODE_DEPLOYMENT_ROUTING` | Not an environment variable: set `deployment_routing` in `settings.json` | +| `HAWK_Y0_FOLDER_TRUST` | Folder trust feature flag (default: `1`) | +| `HAWK_Y0_MARKETPLACE` | Marketplace feature flag (default: `1`, set `0` to disable remote installs) | +| `HAWK_DEPLOYMENT_ROUTING` | Not an environment variable: set `deployment_routing` in `settings.json` | ### Paths | Variable | Description | |----------|-------------| -| `GRAYCODE_HOME` | Harness home override used by identity only (default: `~/.graycode`); most config paths honor `GRAYCODE_CONFIG_DIR` / `GRAYCODE_STATE_DIR` / `GRAYCODE_CACHE_DIR` instead | +| `HAWK_HOME` | Harness home override used by identity only (default: `~/.hawk`); most config paths honor `HAWK_CONFIG_DIR` / `HAWK_STATE_DIR` / `HAWK_CACHE_DIR` instead | --- ## Project Configuration -Place configuration in `.graycode/` within your repository: +Place configuration in `.hawk/` within your repository: | File | Purpose | |------|---------| -| `.graycode/settings.json` | Project settings (autonomy, rules) | -| `.graycode/sandbox.toml` | Custom sandbox profiles | -| `.graycode/lsp.json` | LSP server configuration | +| `.hawk/settings.json` | Project settings (autonomy, rules) | +| `.hawk/sandbox.toml` | Custom sandbox profiles | +| `.hawk/lsp.json` | LSP server configuration | | `AGENTS.md` | Project instructions | --- ## Sandbox Profiles -Location: `~/.graycode/sandbox.toml` (user) or `.graycode/sandbox.toml` (project) +Location: `~/.hawk/sandbox.toml` (user) or `.hawk/sandbox.toml` (project) Define custom sandbox profiles: @@ -163,8 +163,8 @@ network = "deny" ## MCP Servers -Configure MCP servers in global `~/.graycode/settings.json` only — project -`.graycode/settings.json` cannot register MCP servers (stripped by +Configure MCP servers in global `~/.hawk/settings.json` only — project +`.hawk/settings.json` cannot register MCP servers (stripped by `projectSafeSettings`; project automation additionally requires folder trust, see below): @@ -200,7 +200,7 @@ Folder trust controls whether project automation (hooks, plugins, MCP, LSP) can ### Trust Store -Location: `~/.graycode/trusted_folders.toml` +Location: `~/.hawk/trusted_folders.toml` ```toml [[folders]] diff --git a/docs/user-guide/06-theming.md b/docs/user-guide/06-theming.md index e0d4acae8..cf1ede1b2 100644 --- a/docs/user-guide/06-theming.md +++ b/docs/user-guide/06-theming.md @@ -1,16 +1,16 @@ # Theming and Appearance Customization -Graycode draws all TUI colors from a central theme. You can switch themes while running, follow your operating system's light or dark appearance, and adjust scroll speed and compact mode through slash commands or settings. +Hawk draws all TUI colors from a central theme. You can switch themes while running, follow your operating system's light or dark appearance, and adjust scroll speed and compact mode through slash commands or settings. --- ## Available Themes -Graycode includes 17 built-in themes, plus an `auto` option that follows your system appearance: +Hawk includes 17 built-in themes, plus an `auto` option that follows your system appearance: | Theme | Description | |-------|-------------| -| **dark** | Neutral dark base with Graycode's Talon Gold accent. Default theme. | +| **dark** | Neutral dark base with Hawk's Talon Gold accent. Default theme. | | **dracula** | The Dracula color scheme with muted violet surfaces. | | **nord** | Arctic cold blue palette. | | **gruvbox** | Warm retro browns with olive-green accent. | @@ -37,7 +37,7 @@ Theme names are case-insensitive. ### In the TUI -Run the `/theme` slash command to open the theme picker. As you move through the list with the arrow keys, Graycode previews each theme in real time: +Run the `/theme` slash command to open the theme picker. As you move through the list with the arrow keys, Hawk previews each theme in real time: ``` /theme @@ -54,7 +54,7 @@ Submitting `/theme` on its own opens the picker. ### Via Settings -Set the theme in `~/.graycode/settings.json`: +Set the theme in `~/.hawk/settings.json`: ```json { @@ -66,7 +66,7 @@ Set the theme in `~/.graycode/settings.json`: ## Auto Theme (System Appearance) -Set `theme: "auto"` to have Graycode follow your operating system's light/dark appearance and switch themes automatically: +Set `theme: "auto"` to have Hawk follow your operating system's light/dark appearance and switch themes automatically: ```json { @@ -93,13 +93,13 @@ By default, dark mode maps to the **dark** theme and light mode maps to the **li | **Windows** | Reads the system personalization registry via PowerShell. | | **SSH / headless** | Defaults to dark theme when detection fails. | -Once running, Graycode checks for appearance changes when you explicitly switch themes (no continuous polling to avoid resource drain). +Once running, Hawk checks for appearance changes when you explicitly switch themes (no continuous polling to avoid resource drain). --- ## Color Support Detection -On startup, Graycode detects your terminal's color capability: +On startup, Hawk detects your terminal's color capability: | Level | Description | |-------|-------------| @@ -107,7 +107,7 @@ On startup, Graycode detects your terminal's color capability: | **256-color** | Indexed palette. Colors are mapped to the nearest index. | | **16-color** | ANSI names only. Colors map to the closest ANSI color. | -When `COLORTERM=truecolor` is set, Graycode uses truecolor mode. When `TERM` contains `256color`, it uses 256-color mode. When `NO_COLOR` is set, Graycode renders in monochrome. +When `COLORTERM=truecolor` is set, Hawk uses truecolor mode. When `TERM` contains `256color`, it uses 256-color mode. When `NO_COLOR` is set, Hawk renders in monochrome. --- @@ -192,7 +192,7 @@ View terminal configuration recommendations and current capabilities: /terminal-setup ``` -This shows detected color support, current settings, and tips for optimal Graycode experience. +This shows detected color support, current settings, and tips for optimal Hawk experience. --- diff --git a/docs/user-guide/07-mcp-servers.md b/docs/user-guide/07-mcp-servers.md index 1977c9422..45dae6e38 100644 --- a/docs/user-guide/07-mcp-servers.md +++ b/docs/user-guide/07-mcp-servers.md @@ -1,12 +1,12 @@ # MCP Servers -MCP (Model Context Protocol) servers extend Graycode with external tool integrations. They let Graycode interact with any service that implements the MCP standard. +MCP (Model Context Protocol) servers extend Hawk with external tool integrations. They let Hawk interact with any service that implements the MCP standard. --- ## What Are MCP Servers? -An MCP server is a process that exposes tools to Graycode over a standardized protocol. When you configure an MCP server, its tools become available to the model alongside Graycode's built-in tools. The model can discover and call these tools during a session. +An MCP server is a process that exposes tools to Hawk over a standardized protocol. When you configure an MCP server, its tools become available to the model alongside Hawk's built-in tools. The model can discover and call these tools during a session. For example, a GitHub MCP server might expose tools like `create_issue`, `list_pull_requests`, and `search_code`. A database server might expose `query`, `list_tables`, and `describe_schema`. @@ -16,11 +16,11 @@ See the [MCP specification](https://modelcontextprotocol.io) for protocol detail ## Configuration -MCP servers are configured in `.graycode/settings.json` under the `mcp_servers` key. +MCP servers are configured in `.hawk/settings.json` under the `mcp_servers` key. ### stdio Transport (Local Process) -Graycode spawns a local process and communicates over stdin/stdout: +Hawk spawns a local process and communicates over stdin/stdout: ```json { @@ -64,40 +64,40 @@ Manage MCP servers from the command line: ```bash # List configured MCP servers -graycode mcp list +hawk mcp list # Add a stdio server -graycode mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir +hawk mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir # Add a remote HTTP server -graycode mcp add linear --transport http https://mcp.linear.app/mcp +hawk mcp add linear --transport http https://mcp.linear.app/mcp # Remove a server -graycode mcp remove github +hawk mcp remove github # Diagnose server configuration -graycode mcp doctor -graycode mcp doctor +hawk mcp doctor +hawk mcp doctor ``` -Use `--scope project` to write to `.graycode/settings.json` in the current directory instead of the user config. +Use `--scope project` to write to `.hawk/settings.json` in the current directory instead of the user config. --- ## Project-Scoped MCP Servers -MCP servers can be configured per-project in `.graycode/settings.json`: +MCP servers can be configured per-project in `.hawk/settings.json`: ``` my-project/ - .graycode/ + .hawk/ settings.json src/ ... ``` ```json -// .graycode/settings.json +// .hawk/settings.json { "mcp_servers": { "linear": { @@ -125,7 +125,7 @@ MCP tools are namespaced with the server name to avoid collisions: ## Discovering and Using Tools -Graycode provides built-in tools to work with MCP servers: +Hawk provides built-in tools to work with MCP servers: - **`search_tool`** — Discover available integration tools across all enabled MCP servers - **`use_tool`** — Call an integration tool discovered via `search_tool` diff --git a/docs/user-guide/08-skills.md b/docs/user-guide/08-skills.md index 5e5c67da7..b5e14d201 100644 --- a/docs/user-guide/08-skills.md +++ b/docs/user-guide/08-skills.md @@ -1,26 +1,26 @@ # Skills -Skills are reusable prompt packages that extend Graycode with task-specific instructions. They let you capture a repeatable procedure once, instead of re-explaining it each session. +Skills are reusable prompt packages that extend Hawk with task-specific instructions. They let you capture a repeatable procedure once, instead of re-explaining it each session. --- ## What Are Skills? -A skill is a directory containing a `SKILL.md` file. Its markdown body tells Graycode how to handle a specific type of task: step-by-step instructions, conventions, and tool-usage patterns. +A skill is a directory containing a `SKILL.md` file. Its markdown body tells Hawk how to handle a specific type of task: step-by-step instructions, conventions, and tool-usage patterns. -Use a skill for a repeatable procedure that's too specific for AGENTS.md but too long to retype each time. Graycode activates a skill when it applies to your current task. +Use a skill for a repeatable procedure that's too specific for AGENTS.md but too long to retype each time. Hawk activates a skill when it applies to your current task. --- ## Skill Locations -Graycode discovers skills from these directories, in priority order: +Hawk discovers skills from these directories, in priority order: | Location | Scope | Priority | |----------|-------|----------| -| `.graycode/skills/`, `.graycode/commands/` | Local (CWD) | Highest | -| `/.graycode/skills/` | Repo | Medium | -| `~/.graycode/skills/` | User | Lowest | +| `.hawk/skills/`, `.hawk/commands/` | Local (CWD) | Highest | +| `/.hawk/skills/` | Repo | Medium | +| `~/.hawk/skills/` | User | Lowest | Higher-priority locations override skills with the same name. @@ -33,7 +33,7 @@ Higher-priority locations override skills with the same name. Each skill lives in its own directory with a `SKILL.md` file: ``` -~/.graycode/skills/ +~/.hawk/skills/ commit/ SKILL.md review-pr/ @@ -103,13 +103,13 @@ Pass arguments after the name: When names collide, use qualified forms: ``` -/local:commit # From ./.graycode/skills/ -/user:commit # From ~/.graycode/skills/ +/local:commit # From ./.hawk/skills/ +/user:commit # From ~/.hawk/skills/ ``` ### Automatic Invocation -Graycode can invoke a skill automatically when it recognizes a relevant task. Write specific descriptions in the `description` and `when-to-use` fields. +Hawk can invoke a skill automatically when it recognizes a relevant task. Write specific descriptions in the `description` and `when-to-use` fields. --- @@ -119,26 +119,26 @@ Manage skills from the command line: ```bash # Search the community registry -graycode skills search go +hawk skills search go # Install a skill from a source -graycode skills install go-review +hawk skills install go-review # Audit installed skills for security -graycode skills audit +hawk skills audit ``` --- ## Installing Skills -Graycode ships **no bundled skills** by default. Skills are installed on demand +Hawk ships **no bundled skills** by default. Skills are installed on demand from the separate `GrayCodeAI/starling` repo (or any GitHub repo): ```bash -graycode skills search # find a skill in the registry -graycode skills install [skill] # install after user approval -graycode skills audit # security-scan installed skills +hawk skills search # find a skill in the registry +hawk skills install [skill] # install after user approval +hawk skills audit # security-scan installed skills ``` Once installed, skills are discovered from the locations listed above. diff --git a/docs/user-guide/09-plugins.md b/docs/user-guide/09-plugins.md index e9169b213..773b065a2 100644 --- a/docs/user-guide/09-plugins.md +++ b/docs/user-guide/09-plugins.md @@ -20,12 +20,12 @@ For example, a `team-tools` plugin might include a deploy skill, a code-review a ## Plugin Locations -Graycode discovers plugins from these locations, in priority order: +Hawk discovers plugins from these locations, in priority order: | Location | Scope | Trust Required | |----------|-------|----------------| -| `.graycode/plugins/` | Project | Yes | -| `~/.graycode/plugins/` | User | No | +| `.hawk/plugins/` | Project | Yes | +| `~/.hawk/plugins/` | User | No | | `--plugin-dir` | Process | No | When two plugins share a name, the higher-priority location wins. @@ -38,21 +38,21 @@ Manage plugins from the command line: ```bash # List installed plugins -graycode plugins list +hawk plugins list # Install a plugin -graycode plugins install user/repo -graycode plugins install user/repo@v1.0 -graycode plugins install /path/to/local/plugin +hawk plugins install user/repo +hawk plugins install user/repo@v1.0 +hawk plugins install /path/to/local/plugin # Install with trust (required for project plugins) -graycode plugins install user/repo --trust +hawk plugins install user/repo --trust # Uninstall a plugin -graycode plugins uninstall my-plugin +hawk plugins uninstall my-plugin # Update plugins -graycode plugins update +hawk plugins update ``` --- @@ -63,15 +63,15 @@ Browse and install plugins from the community marketplace: ```bash # Search marketplace -graycode plugins search go +hawk plugins search go # Install from marketplace -graycode plugins install go-review --trust +hawk plugins install go-review --trust ``` ### Adding Marketplace Sources -Configure additional marketplaces in `~/.graycode/settings.json`: +Configure additional marketplaces in `~/.hawk/settings.json`: ```json { @@ -90,13 +90,13 @@ Configure additional marketplaces in `~/.graycode/settings.json`: ## Trust Model -- **User plugins** (`~/.graycode/plugins/`) — Trusted automatically -- **Project plugins** (`.graycode/plugins/`) — Require explicit trust before activation +- **User plugins** (`~/.hawk/plugins/`) — Trusted automatically +- **Project plugins** (`.hawk/plugins/`) — Require explicit trust before activation Trust is required because plugins can execute code via hooks and MCP servers. To trust a plugin: ```bash -graycode plugins install --trust +hawk plugins install --trust ``` --- diff --git a/docs/user-guide/10-hooks.md b/docs/user-guide/10-hooks.md index 0f503f7ee..8553ea89e 100644 --- a/docs/user-guide/10-hooks.md +++ b/docs/user-guide/10-hooks.md @@ -1,12 +1,12 @@ # Hooks -Hooks let you run code at key moments in a Graycode session. Use them to automate tasks, enforce safety checks, log activity, and integrate custom tools. +Hooks let you run code at key moments in a Hawk session. Use them to automate tasks, enforce safety checks, log activity, and integrate custom tools. --- ## What Are Hooks? -A hook is a shell command or HTTP endpoint that Graycode calls when a specific lifecycle event occurs. Hooks can: +A hook is a shell command or HTTP endpoint that Hawk calls when a specific lifecycle event occurs. Hooks can: - **Block actions** — A `PreToolUse` hook can deny a dangerous command before it runs - **React to events** — A `PostToolUse` hook can log every tool execution @@ -36,7 +36,7 @@ Only `PreToolUse` can block a tool call. All other events are passive. ## Hook Configuration -Create hooks in `.graycode/hooks/hooks.json`: +Create hooks in `.hawk/hooks/hooks.json`: ```json { @@ -100,8 +100,8 @@ Exit code `2` also signals denial. Any other exit code is fail-open (tool is all ## Trust Model -- **User hooks** (`~/.graycode/hooks/`) — Always trusted -- **Project hooks** (`.graycode/hooks/`) — Require folder trust +- **User hooks** (`~/.hawk/hooks/`) — Always trusted +- **Project hooks** (`.hawk/hooks/`) — Require folder trust Project hooks are blocked until you trust the folder: @@ -114,15 +114,15 @@ Project hooks are blocked until you trust the folder: ## Environment Variables -Graycode sets these variables for every hook: +Hawk sets these variables for every hook: | Variable | Description | |----------|-------------| -| `GRAYCODE_HOOK_EVENT` | Event name (e.g., `PreToolUse`) | -| `GRAYCODE_SESSION_ID` | Current session ID | -| `GRAYCODE_WORKSPACE_ROOT` | Project root path | -| `GRAYCODE_PLUGIN_ROOT` | Plugin directory (for plugin hooks) | -| `GRAYCODE_PLUGIN_DATA` | Plugin data directory | +| `HAWK_HOOK_EVENT` | Event name (e.g., `PreToolUse`) | +| `HAWK_SESSION_ID` | Current session ID | +| `HAWK_WORKSPACE_ROOT` | Project root path | +| `HAWK_PLUGIN_ROOT` | Plugin directory (for plugin hooks) | +| `HAWK_PLUGIN_DATA` | Plugin data directory | --- @@ -130,20 +130,20 @@ Graycode sets these variables for every hook: ```bash # List hooks -graycode hooks list +hawk hooks list # Trust project hooks -graycode hooks trust +hawk hooks trust # Untrust project hooks -graycode hooks untrust +hawk hooks untrust ``` --- ## Example: Safe Shell Guard -Create `.graycode/hooks/safety.json`: +Create `.hawk/hooks/safety.json`: ```json { diff --git a/docs/user-guide/11-custom-models.md b/docs/user-guide/11-custom-models.md index 57ced3219..f378e8d75 100644 --- a/docs/user-guide/11-custom-models.md +++ b/docs/user-guide/11-custom-models.md @@ -1,12 +1,12 @@ # Custom Models -Graycode connects to custom model endpoints through GraycodeRouter for alternative providers, self-hosted models, and overriding built-in settings. +Hawk connects to custom model endpoints through Eyrie for alternative providers, self-hosted models, and overriding built-in settings. --- ## Supported Providers -Graycode works with any LLM provider. Built-in support includes: +Hawk works with any LLM provider. Built-in support includes: | Provider | ID | Key | |----------|-----|-----| @@ -24,7 +24,7 @@ Graycode works with any LLM provider. Built-in support includes: ### CLI Flag ```bash -graycode -m gpt-4o -p "Hello" +hawk -m gpt-4o -p "Hello" ``` ### Slash Command @@ -39,7 +39,7 @@ In the TUI: ### Config Default ```json -// ~/.graycode/settings.json +// ~/.hawk/settings.json { "default_provider": "openai", "default_model": "gpt-4o" @@ -50,7 +50,7 @@ In the TUI: ## Configuring Custom Models -Add custom models in `~/.graycode/settings.json`: +Add custom models in `~/.hawk/settings.json`: ```json { @@ -73,7 +73,7 @@ Add custom models in `~/.graycode/settings.json`: ### Credential Resolution -Graycode resolves credentials in this order: +Hawk resolves credentials in this order: 1. Per-model `api_key` field 2. Environment variable (`env_key`) @@ -127,11 +127,11 @@ ollama pull llama-3.1-70b ## Deployment-Aware Routing -Enable deployment-aware routing to use GraycodeRouter's model catalog: +Enable deployment-aware routing to use Eyrie's model catalog: ```bash -export GRAYCODE_DEPLOYMENT_ROUTING=true -graycode +export HAWK_DEPLOYMENT_ROUTING=true +hawk ``` Or in settings: diff --git a/docs/user-guide/12-project-rules.md b/docs/user-guide/12-project-rules.md index 69736fb04..492b618c2 100644 --- a/docs/user-guide/12-project-rules.md +++ b/docs/user-guide/12-project-rules.md @@ -1,12 +1,12 @@ # Project Rules (AGENTS.md) -Project rules let you configure Graycode per project or directory. By placing an AGENTS.md file in your repository, you can set coding conventions, build instructions, and style guides that Graycode follows automatically. +Project rules let you configure Hawk per project or directory. By placing an AGENTS.md file in your repository, you can set coding conventions, build instructions, and style guides that Hawk follows automatically. --- ## Supported File Names -Graycode checks for these filenames in each directory (from repo root to CWD): +Hawk checks for these filenames in each directory (from repo root to CWD): - `AGENTS.md` - `Agents.md` @@ -14,26 +14,26 @@ Graycode checks for these filenames in each directory (from repo root to CWD): - `Claude.md` - `AGENT.md` -Graycode loads all matching files. Deeper files take precedence when instructions conflict. +Hawk loads all matching files. Deeper files take precedence when instructions conflict. --- ## Rules Directories -Graycode also scans for `*.md` files in rules directories: +Hawk also scans for `*.md` files in rules directories: | Location | Purpose | |----------|---------| -| `/.graycode/rules/` | Always scanned | +| `/.hawk/rules/` | Always scanned | | `/.claude/rules/` | Claude compatibility | --- ## How Discovery Works -Graycode scans rules in this order: +Hawk scans rules in this order: -1. **Global rules**: `~/.graycode/AGENTS.md` (applies to all projects) +1. **Global rules**: `~/.hawk/AGENTS.md` (applies to all projects) 2. **Repo rules**: Every directory from git root to CWD (inclusive) 3. **CWD-only**: If not in a git repo, only current directory @@ -49,7 +49,7 @@ my-monorepo/ AGENTS.md # "Use Express. Follow REST conventions." ``` -When Graycode runs in `packages/frontend/`, it loads all instructions. The frontend-specific rules appear later and take precedence. +When Hawk runs in `packages/frontend/`, it loads all instructions. The frontend-specific rules appear later and take precedence. --- @@ -108,7 +108,7 @@ my-monorepo/ For one-off rules without editing files: ```bash -graycode --rules "Always use TypeScript" -p "Implement feature" +hawk --rules "Always use TypeScript" -p "Implement feature" ``` --- diff --git a/docs/user-guide/13-memory.md b/docs/user-guide/13-memory.md index 09ebd387b..9d4bde3ab 100644 --- a/docs/user-guide/13-memory.md +++ b/docs/user-guide/13-memory.md @@ -1,12 +1,12 @@ # Memory -Memory lets Graycode recall facts, decisions, and patterns from earlier sessions. Graycode indexes saved information and searches it automatically, so new sessions can reuse relevant context. +Memory lets Hawk recall facts, decisions, and patterns from earlier sessions. Hawk indexes saved information and searches it automatically, so new sessions can reuse relevant context. --- ## What Is Memory? -Without memory, each Graycode session starts fresh. When you enable memory, Graycode can: +Without memory, each Hawk session starts fresh. When you enable memory, Hawk can: - Recall project conventions you explained before - Reuse debugging steps that worked @@ -30,13 +30,13 @@ Toggle memory in the TUI: ### CLI Flag ```bash -graycode --memory +hawk --memory ``` ### Settings ```json -// ~/.graycode/settings.json +// ~/.hawk/settings.json { "memory": { "enabled": true @@ -48,13 +48,13 @@ graycode --memory ## How Memory Is Stored -Memory is stored in harrier's graph database under `~/.graycode/harrier/`. +Memory is stored in harrier's graph database under `~/.hawk/harrier/`. | Location | Scope | Description | |----------|-------|-------------| -| `~/.graycode/harrier/global/` | Global | Cross-project memory | -| `~/.graycode/harrier/workspaces//` | Workspace | Project-specific memory | -| `~/.graycode/harrier/sessions/` | Sessions | Session logs and summaries | +| `~/.hawk/harrier/global/` | Global | Cross-project memory | +| `~/.hawk/harrier/workspaces//` | Workspace | Project-specific memory | +| `~/.hawk/harrier/sessions/` | Sessions | Session logs and summaries | The graph structure enables semantic search and relationship mapping between memories. @@ -64,17 +64,17 @@ The graph structure enables semantic search and relationship mapping between mem ### Remember -Ask Graycode to remember something, or use the slash command: +Ask Hawk to remember something, or use the slash command: ``` /remember always open PR links after pushing ``` -Graycode records entries as durable statements organized by topic. +Hawk records entries as durable statements organized by topic. ### Forget -Ask what Graycode should forget: +Ask what Hawk should forget: ``` /forget the snake_case convention @@ -84,13 +84,13 @@ Forget is best-effort. For guaranteed removal, use harrier's query tools directl ### Recall -Ask what Graycode remembers: +Ask what Hawk remembers: ``` /what do you remember about auth? ``` -Graycode searches across all memory sources and summarizes. +Hawk searches across all memory sources and summarizes. --- @@ -111,16 +111,16 @@ Or search directly: ### CLI Commands ```bash -graycode harrier # Open harrier UI -graycode harrier search # Search memory -graycode harrier stats # Show memory statistics +hawk harrier # Open harrier UI +hawk harrier search # Search memory +hawk harrier stats # Show memory statistics ``` --- ## First-Turn Injection -On the first turn of each session, Graycode automatically searches memory for content relevant to the current project and injects it as context. +On the first turn of each session, Hawk automatically searches memory for content relevant to the current project and injects it as context. Configure injection: @@ -136,7 +136,7 @@ Configure injection: ## Memory Search -Graycode searches memory automatically. Manual search: +Hawk searches memory automatically. Manual search: ``` Search harrier memory for "auth patterns" diff --git a/docs/user-guide/14-headless-mode.md b/docs/user-guide/14-headless-mode.md index 2e9ffbb5f..12ce192cd 100644 --- a/docs/user-guide/14-headless-mode.md +++ b/docs/user-guide/14-headless-mode.md @@ -1,6 +1,6 @@ # Headless Mode and Scripting -Headless mode runs Graycode non-interactively from the command line. It accepts a prompt, executes tools, and returns results — ideal for automation and CI/CD. +Headless mode runs Hawk non-interactively from the command line. It accepts a prompt, executes tools, and returns results — ideal for automation and CI/CD. --- @@ -9,10 +9,10 @@ Headless mode runs Graycode non-interactively from the command line. It accepts Pass a prompt to run headless: ```bash -graycode -p "Your prompt here" +hawk -p "Your prompt here" ``` -Graycode processes the prompt and prints the result to stdout. +Hawk processes the prompt and prints the result to stdout. --- @@ -23,7 +23,7 @@ Graycode processes the prompt and prints the result to stdout. Human-readable text: ```bash -graycode -p "Summarize this codebase" +hawk -p "Summarize this codebase" ``` ### json @@ -31,7 +31,7 @@ graycode -p "Summarize this codebase" Single JSON object after completion: ```bash -graycode -p "Summarize this codebase" --output-format json | jq -r '.response' +hawk -p "Summarize this codebase" --output-format json | jq -r '.response' ``` Output includes: @@ -44,7 +44,7 @@ Output includes: NDJSON events in real time: ```bash -graycode -p "Summarize" --output-format stream-json | jq -r 'select(.type=="content") | .content' +hawk -p "Summarize" --output-format stream-json | jq -r 'select(.type=="content") | .content' ``` Event types: @@ -59,20 +59,20 @@ Event types: ### Named Session -Each `graycode -p` creates a fresh session by default. To continue a session: +Each `hawk -p` creates a fresh session by default. To continue a session: ```bash # Get session ID -graycode -p "Initial prompt" --output-format json | jq -r '.sessionId' +hawk -p "Initial prompt" --output-format json | jq -r '.sessionId' # Resume the session -graycode -p "Follow-up" --resume +hawk -p "Follow-up" --resume ``` ### Continue Most Recent ```bash -graycode -p "Continue" --continue +hawk -p "Continue" --continue ``` --- @@ -83,10 +83,10 @@ Restrict available tools: ```bash # Allow only read tools -graycode -p "Explain this" --tools "Read,Grep,LS" +hawk -p "Explain this" --tools "Read,Grep,LS" # Deny specific tools -graycode -p "Review" --disallowed-tools "Bash,WebSearch" +hawk -p "Review" --disallowed-tools "Bash,WebSearch" ``` --- @@ -97,10 +97,10 @@ Control tool permissions: ```bash # Allow shell commands through the explicit tool policy flag -graycode -p "Build" --allowed-tools "Bash(git:*) Bash(npm:*)" +hawk -p "Build" --allowed-tools "Bash(git:*) Bash(npm:*)" # Deny dangerous commands -graycode -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" +hawk -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" ``` --- @@ -110,8 +110,8 @@ graycode -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" Use `--auto` for fully automated runs: ```bash -graycode -p "Format all files" --dangerously-skip-permissions -graycode exec --auto full "Add error handling" +hawk -p "Format all files" --dangerously-skip-permissions +hawk exec --auto full "Add error handling" ``` **Warning:** This grants full autonomy. Use only in trusted environments. @@ -124,14 +124,14 @@ graycode exec --auto full "Add error handling" ```bash #!/bin/bash -graycode -p "Review staged changes for bugs. Reply OK if fine." \ +hawk -p "Review staged changes for bugs. Reply OK if fine." \ --dangerously-skip-permissions --output-format json | jq -r '.response' | grep -q "^OK" || exit 1 ``` ### Code Review ```bash -graycode -p "Review PR for security issues" \ +hawk -p "Review PR for security issues" \ --output-format json --dangerously-skip-permissions | jq -r '.response' > review.md ``` @@ -139,7 +139,7 @@ graycode -p "Review PR for security issues" \ ```bash for file in src/*.go; do - graycode -p "Format $file" --auto + hawk -p "Format $file" --auto done ``` @@ -149,8 +149,8 @@ done ```bash export XAI_API_KEY="xai-..." # API key -export GRAYCODE_HOME="/path" # Custom config location -export GRAYCODE_LOG_FILE="/tmp/graycode.log" # Log file +export HAWK_HOME="/path" # Custom config location +export HAWK_LOG_FILE="/tmp/hawk.log" # Log file ``` --- diff --git a/docs/user-guide/15-agent-mode.md b/docs/user-guide/15-agent-mode.md index ee05882dd..c4133d8cf 100644 --- a/docs/user-guide/15-agent-mode.md +++ b/docs/user-guide/15-agent-mode.md @@ -1,6 +1,6 @@ # Agent Mode (ACP) and IDE Integration -Agent mode runs Graycode as an ACP (Agent Client Protocol) server for IDE integration and custom tooling. +Agent mode runs Hawk as an ACP (Agent Client Protocol) server for IDE integration and custom tooling. --- @@ -17,10 +17,10 @@ The [Agent Client Protocol (ACP)](https://agentclientprotocol.com) is a standard ## stdio Transport -Run Graycode as an ACP server over stdio: +Run Hawk as an ACP server over stdio: ```bash -graycode agent stdio +hawk agent stdio ``` Clients include: @@ -31,9 +31,9 @@ Clients include: ### Options ```bash -graycode agent --model gpt-4o stdio -graycode agent --auto stdio -graycode agent --agent-profile path/to/profile.yaml stdio +hawk agent --model gpt-4o stdio +hawk agent --auto stdio +hawk agent --agent-profile path/to/profile.yaml stdio ``` | Flag | Description | @@ -49,7 +49,7 @@ graycode agent --agent-profile path/to/profile.yaml stdio ### WebSocket Server ```bash -graycode agent serve --bind 127.0.0.1:2419 --secret +hawk agent serve --bind 127.0.0.1:2419 --secret ``` Connect clients over WebSocket using the secret token for authentication. @@ -81,7 +81,7 @@ ACP streams structured events with `sessionUpdate` types: ## Extension Methods -Graycode provides `x.ai/*` extension methods: +Hawk provides `x.ai/*` extension methods: | Category | Methods | |----------|---------| @@ -110,7 +110,7 @@ Graycode provides `x.ai/*` extension methods: import { spawn } from "child_process"; // Start ACP server -const proc = spawn("graycode", ["agent", "stdio"]); +const proc = spawn("hawk", ["agent", "stdio"]); // Initialize proc.stdin.write(JSON.stringify({ diff --git a/docs/user-guide/16-subagents.md b/docs/user-guide/16-subagents.md index 01026cb74..2d3605d30 100644 --- a/docs/user-guide/16-subagents.md +++ b/docs/user-guide/16-subagents.md @@ -8,7 +8,7 @@ Subagents are enabled by default. ## How Subagents Work -When Graycode needs to delegate work, it spawns a child session using the `Agent` tool. The child runs with: +When Hawk needs to delegate work, it spawns a child session using the `Agent` tool. The child runs with: - Its own context window (isolated from parent) - A toolset determined by its type and capability mode @@ -74,7 +74,7 @@ This creates an isolated git worktree, preventing conflicts with the parent sess ## Personas -Personas are behavioral overlays applied to subagents. Define them in `~/.graycode/settings.json`: +Personas are behavioral overlays applied to subagents. Define them in `~/.hawk/settings.json`: ```json { diff --git a/docs/user-guide/17-sessions.md b/docs/user-guide/17-sessions.md index 9c5670c5a..97b5d47f4 100644 --- a/docs/user-guide/17-sessions.md +++ b/docs/user-guide/17-sessions.md @@ -1,6 +1,6 @@ # Sessions -Graycode saves every conversation to disk automatically. Whether you work in the TUI, in headless mode, or over ACP, Graycode records the exchange as a session. +Hawk saves every conversation to disk automatically. Whether you work in the TUI, in headless mode, or over ACP, Hawk records the exchange as a session. --- @@ -14,7 +14,7 @@ A session is a persistent conversation with full history: - Token usage and turn counts - Subagent sessions -Sessions are identified by a unique session ID and stored under `~/.graycode/sessions/`. +Sessions are identified by a unique session ID and stored under `~/.hawk/sessions/`. --- @@ -36,7 +36,7 @@ This clears the current context and starts fresh. Alias: `/exit` -To leave the session but stay in Graycode: +To leave the session but stay in Hawk: ``` /home @@ -58,13 +58,13 @@ Opens a session picker. Select a session to resume. ```bash # Resume specific session -graycode --resume +hawk --resume # Continue most recent -graycode --continue +hawk --continue # New session with specific ID -graycode --session-id -p "prompt" +hawk --session-id -p "prompt" ``` --- @@ -122,10 +122,10 @@ Maintain context across headless calls: ```bash # Start and capture ID -ID=$(graycode -p "First" --output-format json | jq -r '.sessionId') +ID=$(hawk -p "First" --output-format json | jq -r '.sessionId') # Continue -graycode -p "Second" --resume "$ID" +hawk -p "Second" --resume "$ID" ``` --- diff --git a/docs/user-guide/18-sandbox.md b/docs/user-guide/18-sandbox.md index 3ad6a4bcb..9f9b2bdb4 100644 --- a/docs/user-guide/18-sandbox.md +++ b/docs/user-guide/18-sandbox.md @@ -1,6 +1,6 @@ # Sandbox -Sandbox mode restricts what Graycode and its spawned commands can access on your filesystem using OS-level kernel primitives. +Sandbox mode restricts what Hawk and its spawned commands can access on your filesystem using OS-level kernel primitives. --- @@ -8,13 +8,13 @@ Sandbox mode restricts what Graycode and its spawned commands can access on your ```bash # Workspace sandbox (recommended for development) -graycode --sandbox workspace +hawk --sandbox workspace # Read-only mode -graycode --sandbox read-only +hawk --sandbox read-only # Strict mode -graycode --sandbox strict +hawk --sandbox strict ``` --- @@ -24,15 +24,15 @@ graycode --sandbox strict | Profile | FS Read | FS Write | Child Network | Use Case | |---------|---------|----------|---------------|----------| | `off` | All | All | All | No restrictions | -| `workspace` | All | CWD + `~/.graycode/` + temp | Allowed | Normal development | -| `read-only` | All | `~/.graycode/` + temp | Blocked (Linux) | Exploration, reviews | -| `strict` | CWD + system | CWD + `~/.graycode/` + temp | Blocked (Linux) | Untrusted code | +| `workspace` | All | CWD + `~/.hawk/` + temp | Allowed | Normal development | +| `read-only` | All | `~/.hawk/` + temp | Blocked (Linux) | Exploration, reviews | +| `strict` | CWD + system | CWD + `~/.hawk/` + temp | Blocked (Linux) | Untrusted code | --- ## Custom Profiles -Create `.graycode/sandbox.toml`: +Create `.hawk/sandbox.toml`: ```toml [profiles.project] @@ -44,7 +44,7 @@ deny = ["**/.env", "**/*.pem"] Use with: ```bash -graycode --sandbox project +hawk --sandbox project ``` --- @@ -53,15 +53,15 @@ graycode --sandbox project ### workspace (recommended) -Read anywhere, write only to the project directory and Graycode's config. Allows network access for LLM calls and web search. +Read anywhere, write only to the project directory and Hawk's config. Allows network access for LLM calls and web search. ### read-only -Read anywhere, write only to `~/.graycode/` and temp directories. Good for code exploration without risk of modification. +Read anywhere, write only to `~/.hawk/` and temp directories. Good for code exploration without risk of modification. ### strict -Most restrictive. Read only CWD and essential system paths. Write only to CWD, `~/.graycode/`, and temp. +Most restrictive. Read only CWD and essential system paths. Write only to CWD, `~/.hawk/`, and temp. --- diff --git a/docs/user-guide/19-plan-mode.md b/docs/user-guide/19-plan-mode.md index fe089e2ed..39b8155e4 100644 --- a/docs/user-guide/19-plan-mode.md +++ b/docs/user-guide/19-plan-mode.md @@ -1,16 +1,16 @@ # Plan Mode -Plan mode is a structured planning phase where Graycode explores the codebase and designs an implementation before writing code. Use it when tasks have genuine ambiguity about the right approach. +Plan mode is a structured planning phase where Hawk explores the codebase and designs an implementation before writing code. Use it when tasks have genuine ambiguity about the right approach. --- ## What Plan Mode Does -When plan mode is active, Graycode: +When plan mode is active, Hawk: 1. Reads and searches the codebase to understand patterns 2. Designs an implementation approach -3. Writes the plan to a file under `.graycode/specs/` +3. Writes the plan to a file under `.hawk/specs/` 4. May ask clarifying questions via `/ask` 5. Calls `exit_plan_mode` to request approval @@ -45,7 +45,7 @@ Or press **Ctrl+Shift+P** to cycle modes. ## The Plan File -Plans are written to `.graycode/specs//plan.md`: +Plans are written to `.hawk/specs//plan.md`: - **Context** — Why the change is needed - **Approach** — Recommended implementation strategy @@ -56,7 +56,7 @@ Plans are written to `.graycode/specs//plan.md`: ## Plan Approval -When planning completes, Graycode opens a preview with action bar: +When planning completes, Hawk opens a preview with action bar: | Key | Action | |-----|--------| diff --git a/docs/user-guide/20-background-tasks.md b/docs/user-guide/20-background-tasks.md index d258c6bb5..c96e06285 100644 --- a/docs/user-guide/20-background-tasks.md +++ b/docs/user-guide/20-background-tasks.md @@ -1,6 +1,6 @@ # Background Tasks and Monitoring -Graycode runs long-lived processes without blocking the conversation. This covers background commands, `/loop`, and the `monitor` tool. +Hawk runs long-lived processes without blocking the conversation. This covers background commands, `/loop`, and the `monitor` tool. --- diff --git a/docs/user-guide/21-terminal-support.md b/docs/user-guide/21-terminal-support.md index de4b21dda..7ef8ee0ec 100644 --- a/docs/user-guide/21-terminal-support.md +++ b/docs/user-guide/21-terminal-support.md @@ -1,6 +1,6 @@ # Terminal Support and Troubleshooting -Graycode runs as a full-screen TUI powered by Bubble Tea. This covers terminal compatibility and common fixes. +Hawk runs as a full-screen TUI powered by Bubble Tea. This covers terminal compatibility and common fixes. --- @@ -24,7 +24,7 @@ set -as terminal-features ",*:RGB" ## Terminal Detection -Graycode detects these terminals: +Hawk detects these terminals: - Apple Terminal - iTerm2 @@ -61,7 +61,7 @@ Run `/terminal-setup` for diagnostics. **Cause**: Zellij, tmux control mode, or config. -**Fix**: Set in `~/.graycode/settings.json`: +**Fix**: Set in `~/.hawk/settings.json`: ```json { "terminal": { "alt_screen": "always" } } @@ -128,7 +128,7 @@ set -g set-clipboard on ## Diagnostics -Run in Graycode: +Run in Hawk: ``` /terminal-setup diff --git a/docs/user-guide/22-permissions-and-safety.md b/docs/user-guide/22-permissions-and-safety.md index 50662e3c6..c2b7437cd 100644 --- a/docs/user-guide/22-permissions-and-safety.md +++ b/docs/user-guide/22-permissions-and-safety.md @@ -1,6 +1,6 @@ # Permissions and Safety Controls -Graycode can read files, edit code, and run shell commands. The permission system controls what the agent is allowed to do. +Hawk can read files, edit code, and run shell commands. The permission system controls what the agent is allowed to do. --- diff --git a/docs/user-guide/23-dashboard.md b/docs/user-guide/23-dashboard.md index 91eab4f2a..c6f5aa092 100644 --- a/docs/user-guide/23-dashboard.md +++ b/docs/user-guide/23-dashboard.md @@ -1,6 +1,6 @@ # Dashboard and HUD -The Graycode dashboard provides system status and monitoring information. +The Hawk dashboard provides system status and monitoring information. --- @@ -9,7 +9,7 @@ The Graycode dashboard provides system status and monitoring information. In the TUI: ``` -/ecosystem # Ecosystem status (GraycodeRouter, harrier, shrike) +/ecosystem # Ecosystem status (Eyrie, harrier, shrike) /path # Developer path readiness /preflight # Quick health check ``` @@ -18,11 +18,11 @@ In the TUI: ## Ecosystem Status -Shows the status of all Graycode components: +Shows the status of all Hawk components: | Component | Status | |-----------|--------| -| GraycodeRouter (providers) | Ready / Error | +| Eyrie (providers) | Ready / Error | | harrier (memory) | Connected / Offline | | shrike (tokenizer) | Available | | swift (session capture) | Ready | @@ -34,7 +34,7 @@ Shows the status of all Graycode components: Check readiness to chat: ```bash -graycode path +hawk path ``` This verifies: diff --git a/docs/user-guide/24-monitoring-usage.md b/docs/user-guide/24-monitoring-usage.md index 8d8e4e511..40f3e29ba 100644 --- a/docs/user-guide/24-monitoring-usage.md +++ b/docs/user-guide/24-monitoring-usage.md @@ -1,6 +1,6 @@ # Monitoring and Usage -Graycode tracks token usage, costs, and session activity for monitoring and debugging. +Hawk tracks token usage, costs, and session activity for monitoring and debugging. --- @@ -11,8 +11,8 @@ Token usage is tracked per session and aggregated. ### View Usage ```bash -graycode usage -graycode doctor # Full health report +hawk usage +hawk doctor # Full health report ``` Usage shows: @@ -24,12 +24,12 @@ Usage shows: ## Telemetry -Graycode can send anonymous usage telemetry. +Hawk can send anonymous usage telemetry. ### Enable/Disable ```json -// ~/.graycode/settings.json +// ~/.hawk/settings.json { "telemetry": { "enabled": false @@ -40,7 +40,7 @@ Graycode can send anonymous usage telemetry. Or: ```bash -graycode --telemetry # Enable +hawk --telemetry # Enable haw --no-telemetry # Disable ``` @@ -77,7 +77,7 @@ Accessible via `/ecosystem` in TUI. ## Cloud Integration -Graycode Cloud provides managed usage tracking: +Hawk Cloud provides managed usage tracking: - Organization-level metrics - Budget alerts diff --git a/docs/user-guide/25-shell-completions.md b/docs/user-guide/25-shell-completions.md index 00aa97780..f40480c70 100644 --- a/docs/user-guide/25-shell-completions.md +++ b/docs/user-guide/25-shell-completions.md @@ -1,15 +1,15 @@ # Shell Completions -graycode ships completion scripts for **bash**, **zsh**, **fish**, and **PowerShell**, +hawk ships completion scripts for **bash**, **zsh**, **fish**, and **PowerShell**, plus a machine-readable **JSON** spec for IDE integration. ## Quick Install ```bash # Auto-install to the standard location for your shell and OS: -graycode completion install bash -graycode completion install zsh -graycode completion install fish +hawk completion install bash +hawk completion install zsh +hawk completion install fish ``` ## Manual Setup @@ -18,50 +18,50 @@ graycode completion install fish ```bash # Load for current session: -source <(graycode completion bash) +source <(hawk completion bash) # Persist (Linux): -graycode completion bash > ~/.local/share/bash-completion/completions/graycode +hawk completion bash > ~/.local/share/bash-completion/completions/hawk # Persist (macOS with Homebrew): -graycode completion bash > /opt/homebrew/etc/bash_completion.d/graycode +hawk completion bash > /opt/homebrew/etc/bash_completion.d/hawk ``` ### Zsh ```bash # Load for current session: -source <(graycode completion zsh) +source <(hawk completion zsh) # Persist: -graycode completion zsh > "${fpath[1]}/_graycode" +hawk completion zsh > "${fpath[1]}/_hawk" ``` ### Fish ```bash # Load for current session: -graycode completion fish | source +hawk completion fish | source # Persist: -graycode completion fish > ~/.config/fish/completions/graycode.fish +hawk completion fish > ~/.config/fish/completions/hawk.fish ``` ### PowerShell ```powershell # Load for current session: -graycode completion powershell | Out-String | Invoke-Expression +hawk completion powershell | Out-String | Invoke-Expression # Persist: add to your $PROFILE -graycode completion powershell > graycode.ps1 -. ./graycode.ps1 +hawk completion powershell > hawk.ps1 +. ./hawk.ps1 ``` ## JSON Spec (IDE Integration) ```bash -graycode completion json +hawk completion json ``` Prints a machine-readable command/flag spec that IDEs and editor plugins can @@ -69,10 +69,10 @@ consume for inline completions without shell integration. ## Install Paths -`graycode completion install` resolves the correct path automatically: +`hawk completion install` resolves the correct path automatically: | Shell | Linux | macOS (Homebrew) | |-------|-------|-------------------| -| bash | `~/.local/share/bash-completion/completions/graycode` | `/opt/homebrew/etc/bash_completion.d/graycode` | -| zsh | First `$fpath` entry (e.g. `/usr/local/share/zsh/site-functions/_graycode`) | Same | -| fish | `~/.config/fish/completions/graycode.fish` | Same | +| bash | `~/.local/share/bash-completion/completions/hawk` | `/opt/homebrew/etc/bash_completion.d/hawk` | +| zsh | First `$fpath` entry (e.g. `/usr/local/share/zsh/site-functions/_hawk`) | Same | +| fish | `~/.config/fish/completions/hawk.fish` | Same | diff --git a/docs/user-guide/26-learned-preferences.md b/docs/user-guide/26-learned-preferences.md index 2d03573c3..962ec8995 100644 --- a/docs/user-guide/26-learned-preferences.md +++ b/docs/user-guide/26-learned-preferences.md @@ -1,6 +1,6 @@ # Learned Preferences -Graycode can learn coding-style tendencies from feedback through its taste system. +Hawk can learn coding-style tendencies from feedback through its taste system. These preferences are advisory context, not policy. ## Policy Boundaries diff --git a/docs/user-guide/27-workflows.md b/docs/user-guide/27-workflows.md index 07ae36bb4..76e7a7a86 100644 --- a/docs/user-guide/27-workflows.md +++ b/docs/user-guide/27-workflows.md @@ -2,7 +2,7 @@ ## Interactive Input -- `/command` invokes a Graycode slash command. +- `/command` invokes a Hawk slash command. - `!command` runs a direct shell command when the current permission mode allows it. - `@path` adds file or directory context. - `Esc` interrupts the current operation. @@ -13,7 +13,7 @@ Use a bounded, machine-readable review in CI: ```bash -graycode review run HEAD --output-format json --max-turns 8 +hawk review run HEAD --output-format json --max-turns 8 ``` Review findings remain structured and severity-based. Provider retries, @@ -22,7 +22,7 @@ permissions, and tool timeouts are still enforced in headless mode. ## MCP-Backed Analysis 1. Establish project trust. -2. Merlin configured MCP servers with `graycode mcp`. +2. Merlin configured MCP servers with `hawk mcp`. 3. Run the review or scan command. 4. Check the persisted findings and event output. @@ -31,7 +31,7 @@ Do not bypass trust or permission controls to make an MCP tool convenient. ## Session Recovery Use `/session`, `/resume`, `/continue`, `/checkpoint`, and `/rewind` to recover -from interruptions. Graycode trims incomplete tool turns before a cancelled session +from interruptions. Hawk trims incomplete tool turns before a cancelled session is reused, preserving provider transcript invariants. ## Recording and Replay diff --git a/docs/user-guide/28-workflow-budgets.md b/docs/user-guide/28-workflow-budgets.md index 9ad6b4c5b..1f2ab15ef 100644 --- a/docs/user-guide/28-workflow-budgets.md +++ b/docs/user-guide/28-workflow-budgets.md @@ -1,6 +1,6 @@ # Workflow Budgets -Graycode exposes several independent limits. Configure the smallest useful scope +Hawk exposes several independent limits. Configure the smallest useful scope for automation and distinguish them when diagnosing termination. | Budget | Limits | Purpose | diff --git a/docs/versioning.md b/docs/versioning.md index 251ad8db2..049a3e8e3 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -12,7 +12,7 @@ repository that follows this layout. Adopted 2026-05-14. ## Pattern by repo type -### Go binaries (`graycode`, `harrier`, `swift`) +### Go binaries (`hawk`, `harrier`, `swift`) - `VERSION` at the repo root. - A version package (`internal/version` for binaries, or `main` itself) declares @@ -38,7 +38,7 @@ repository that follows this layout. Adopted 2026-05-14. the `VERSION` file because release-please bumps both atomically). - This is the Kubernetes / Helm / gh-cli pattern. -### Go libraries (`sparrow`, `graycode-router`, `kestrel`, `merlin`, `shrike`) +### Go libraries (`sparrow`, `eyrie`, `kestrel`, `merlin`, `shrike`) - `VERSION` at the repo root. - A `version.go` file co-located with `VERSION` uses `//go:embed` to read it at @@ -90,7 +90,7 @@ func init() { pattern = "^(?P[^\\s]+)" [tool.hatch.build.targets.wheel] - force-include = { "VERSION" = "graycode/VERSION" } + force-include = { "VERSION" = "hawk/VERSION" } ``` - `_version.py` reads the same `VERSION` file at runtime via `pathlib`, so `__version__` matches the package metadata both in source checkouts and in diff --git a/ecosystem.yaml b/ecosystem.yaml index 8dd0bea5b..b48e65d98 100644 --- a/ecosystem.yaml +++ b/ecosystem.yaml @@ -6,31 +6,31 @@ schema_version: 1 # file instead of carrying its own repo-name list. Only repositories that # exist are listed: removed siblings stay out until they are restored. contracts: - portable_graph_schema: graycode.graph/v1 + portable_graph_schema: hawk.graph/v1 cloud_graph_schema: graycode-cloud.graph/v1 usage_event: usage.recorded.v1 repositories: - - directory: graycode-cli - github_repo: graycode-cli - product_name: Graycode + - directory: hawk + github_repo: hawk + product_name: Hawk kind: product language: go - module: github.com/GrayCodeAI/graycode-cli + module: github.com/GrayCodeAI/hawk workspace: true - - directory: graycode-router - github_repo: graycode-router - product_name: Graycode Router + - directory: eyrie + github_repo: eyrie + product_name: Eyrie kind: engine language: go - module: github.com/GrayCodeAI/graycode-router + module: github.com/GrayCodeAI/eyrie workspace: true - facade: github.com/GrayCodeAI/graycode-router/engine + facade: github.com/GrayCodeAI/eyrie/engine - directory: graycode-skills github_repo: graycode-skills - product_name: Graycode Community Skills + product_name: Hawk Community Skills kind: extension language: python workspace: false diff --git a/examples/README.md b/examples/README.md index 6553d637f..bc809e223 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,26 +1,26 @@ -# Graycode Examples +# Hawk Examples -Graycode is an AI coding agent that understands your codebase. +Hawk is an AI coding agent that understands your codebase. ## Basic Usage ### Start a chat session ```bash -graycode chat +hawk chat > Explain how the authentication system works ``` ### Generate code ```bash -graycode "Add input validation to the user registration endpoint" +hawk "Add input validation to the user registration endpoint" ``` ### Review changes ```bash -graycode review +hawk review ``` ## Advanced Examples @@ -28,52 +28,52 @@ graycode review ### Use with skills ```bash -graycode --skill code-review "Review my latest changes" +hawk --skill code-review "Review my latest changes" ``` ### Analyze codebase ```bash -graycode analyze --depth full +hawk analyze --depth full ``` ### Fix issues ```bash -graycode fix --auto +hawk fix --auto ``` ### Headless agent in CI -Copy [graycode-ci-exec.yml](github/graycode-ci-exec.yml) to run `graycode exec --ephemeral --json` +Copy [hawk-ci-exec.yml](github/hawk-ci-exec.yml) to run `hawk exec --ephemeral --json` on pull requests (summarize diff, risk list, or your own prompt). Pin your -graycode install step and provider secrets before enabling the job. +hawk install step and provider secrets before enabling the job. ### Report CI delivery context -Copy [graycode-delivery-context.yml](github/graycode-delivery-context.yml) to your -repository to report GitHub Actions runs to Graycode Cloud. Create a dedicated, +Copy [hawk-delivery-context.yml](github/hawk-delivery-context.yml) to your +repository to report GitHub Actions runs to Hawk Cloud. Create a dedicated, revocable device token for CI and keep its endpoint, device ID, project ID, and token in GitHub Actions secrets. ## MCP Integration -Graycode can use MCP servers for extended capabilities: +Hawk can use MCP servers for extended capabilities: ```bash # With harrier for persistent memory harrier setup -graycode chat +hawk chat # With swift for session capture swift start -graycode "refactor the API layer" +hawk "refactor the API layer" swift stop ``` ## Configuration -Create `.graycode/config.json`: +Create `.hawk/config.json`: ```json { diff --git a/examples/github/graycode-delivery-context.yml b/examples/github/graycode-delivery-context.yml deleted file mode 100644 index fd01e0e66..000000000 --- a/examples/github/graycode-delivery-context.yml +++ /dev/null @@ -1,47 +0,0 @@ -# Graycode Delivery Context -# -# Copy to .github/workflows/graycode-delivery-context.yml. This workflow assumes -# `graycode` is available on PATH (install it using your team's normal release -# mechanism before this reporting step). -# -# Create a dedicated, revocable Graycode Cloud device token for CI. Do not reuse a -# developer's local device token. Store all three values as repository secrets. -name: Graycode Delivery Context - -on: - push: - branches: [main] - -permissions: - contents: read - -jobs: - report-delivery: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - # Install Graycode here using the release package your organization pins. - # The remaining steps are intentionally independent of the installer. - - name: Connect CI device to Graycode Cloud - run: | - graycode cloud connect \ - --endpoint "$GRAYCODE_CLOUD_URL" \ - --device-id "$GRAYCODE_CLOUD_DEVICE_ID" \ - --project-id "$GRAYCODE_CLOUD_PROJECT_ID" \ - --token "$GRAYCODE_CLOUD_DEVICE_TOKEN" - env: - GRAYCODE_CLOUD_URL: ${{ secrets.GRAYCODE_CLOUD_URL }} - GRAYCODE_CLOUD_DEVICE_ID: ${{ secrets.GRAYCODE_CLOUD_DEVICE_ID }} - GRAYCODE_CLOUD_PROJECT_ID: ${{ secrets.GRAYCODE_CLOUD_PROJECT_ID }} - GRAYCODE_CLOUD_DEVICE_TOKEN: ${{ secrets.GRAYCODE_CLOUD_DEVICE_TOKEN }} - - - name: Report successful CI run - if: ${{ success() }} - run: graycode cloud context --ci-status succeeded - - - name: Report failed CI run - if: ${{ failure() }} - run: graycode cloud context --ci-status failed diff --git a/examples/github/graycode-ci-exec.yml b/examples/github/hawk-ci-exec.yml similarity index 56% rename from examples/github/graycode-ci-exec.yml rename to examples/github/hawk-ci-exec.yml index 5e8deae4c..e15e35101 100644 --- a/examples/github/graycode-ci-exec.yml +++ b/examples/github/hawk-ci-exec.yml @@ -1,15 +1,15 @@ -# Graycode CI agent (headless) +# Hawk CI agent (headless) # -# Copy to .github/workflows/graycode-ci-exec.yml. +# Copy to .github/workflows/hawk-ci-exec.yml. # Runs a non-interactive agent turn with JSON output — suitable for # "fix failing tests", "summarize this PR", or similar CI jobs. # # Prerequisites: -# - graycode binary on PATH (pin a release or install from your org's package) +# - hawk binary on PATH (pin a release or install from your org's package) # - LLM credentials via secrets (never commit keys) -# - Docker available on the runner if your graycode build requires container isolation +# - Docker available on the runner if your hawk build requires container isolation # -name: Graycode CI Exec +name: Hawk CI Exec on: pull_request: @@ -26,27 +26,27 @@ permissions: pull-requests: read jobs: - graycode-exec: + hawk-exec: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - # Install graycode with your org's preferred mechanism, for example: + # Install hawk with your org's preferred mechanism, for example: # - curl -fsSL https://…/install.sh | sh - # - go install github.com/GrayCodeAI/graycode-cli/cmd/graycode@vX.Y.Z + # - go install github.com/GrayCodeAI/hawk/cmd/hawk@vX.Y.Z # - download a release asset into $GITHUB_PATH - - name: Ensure graycode is available + - name: Ensure hawk is available run: | - if ! command -v graycode >/dev/null 2>&1; then - echo "Install graycode before this step (see comments in this workflow)." + if ! command -v hawk >/dev/null 2>&1; then + echo "Install hawk before this step (see comments in this workflow)." exit 1 fi - graycode version || graycode --version || true + hawk version || hawk --version || true - name: Trust workspace (folder trust) - run: graycode trust add --reason "github-actions ci" || true + run: hawk trust add --reason "github-actions ci" || true - name: Run agent (ephemeral + JSON) id: agent @@ -54,20 +54,20 @@ jobs: # Map your provider key(s) here, e.g. ANTHROPIC_API_KEY / OPENAI_API_KEY. ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GRAYCODE_PROMPT: ${{ github.event.inputs.prompt || 'Summarize the changes in this PR (git log/diff) and list risks.' }} + HAWK_PROMPT: ${{ github.event.inputs.prompt || 'Summarize the changes in this PR (git log/diff) and list risks.' }} run: | set -euo pipefail - PROMPT="${GRAYCODE_PROMPT}" - graycode exec \ + PROMPT="${HAWK_PROMPT}" + hawk exec \ --ephemeral \ --json \ --auto full \ --max-turns 20 \ - "$PROMPT" | tee graycode-result.json + "$PROMPT" | tee hawk-result.json - name: Upload agent result if: always() uses: actions/upload-artifact@v4 with: - name: graycode-result - path: graycode-result.json + name: hawk-result + path: hawk-result.json diff --git a/examples/github/hawk-delivery-context.yml b/examples/github/hawk-delivery-context.yml new file mode 100644 index 000000000..343218d50 --- /dev/null +++ b/examples/github/hawk-delivery-context.yml @@ -0,0 +1,47 @@ +# Hawk Delivery Context +# +# Copy to .github/workflows/hawk-delivery-context.yml. This workflow assumes +# `hawk` is available on PATH (install it using your team's normal release +# mechanism before this reporting step). +# +# Create a dedicated, revocable Hawk Cloud device token for CI. Do not reuse a +# developer's local device token. Store all three values as repository secrets. +name: Hawk Delivery Context + +on: + push: + branches: [main] + +permissions: + contents: read + +jobs: + report-delivery: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + # Install Hawk here using the release package your organization pins. + # The remaining steps are intentionally independent of the installer. + - name: Connect CI device to Hawk Cloud + run: | + hawk cloud connect \ + --endpoint "$HAWK_CLOUD_URL" \ + --device-id "$HAWK_CLOUD_DEVICE_ID" \ + --project-id "$HAWK_CLOUD_PROJECT_ID" \ + --token "$HAWK_CLOUD_DEVICE_TOKEN" + env: + HAWK_CLOUD_URL: ${{ secrets.HAWK_CLOUD_URL }} + HAWK_CLOUD_DEVICE_ID: ${{ secrets.HAWK_CLOUD_DEVICE_ID }} + HAWK_CLOUD_PROJECT_ID: ${{ secrets.HAWK_CLOUD_PROJECT_ID }} + HAWK_CLOUD_DEVICE_TOKEN: ${{ secrets.HAWK_CLOUD_DEVICE_TOKEN }} + + - name: Report successful CI run + if: ${{ success() }} + run: hawk cloud context --ci-status succeeded + + - name: Report failed CI run + if: ${{ failure() }} + run: hawk cloud context --ci-status failed diff --git a/examples/github/graycode-issue-resolver.yml b/examples/github/hawk-issue-resolver.yml similarity index 55% rename from examples/github/graycode-issue-resolver.yml rename to examples/github/hawk-issue-resolver.yml index 922d24024..9a1724de0 100644 --- a/examples/github/graycode-issue-resolver.yml +++ b/examples/github/hawk-issue-resolver.yml @@ -1,16 +1,16 @@ -# Graycode Issue Resolver +# Hawk Issue Resolver # -# Copy this file to .github/workflows/graycode-issue-resolver.yml in your repository. +# Copy this file to .github/workflows/hawk-issue-resolver.yml in your repository. # -# It runs the Graycode agent in two scenarios: -# 1. Automation mode - when an issue is labeled `graycode-agent`, Graycode reads the +# It runs the Hawk agent in two scenarios: +# 1. Automation mode - when an issue is labeled `hawk-agent`, Hawk reads the # issue title + body and attempts to resolve it autonomously. -# 2. Interactive mode - when someone comments "@graycode ..." on an issue or PR, -# Graycode replies conversationally to the request. +# 2. Interactive mode - when someone comments "@hawk ..." on an issue or PR, +# Hawk replies conversationally to the request. # -# The mode is auto-detected by `graycode exec` from the GitHub event payload, so a +# The mode is auto-detected by `hawk exec` from the GitHub event payload, so a # single composite action handles both paths. -name: Graycode Issue Resolver +name: Hawk Issue Resolver on: issues: @@ -26,12 +26,12 @@ permissions: pull-requests: write jobs: - graycode: - # Run on the `graycode-agent` label, or on any comment mentioning @graycode. + hawk: + # Run on the `hawk-agent` label, or on any comment mentioning @hawk. if: >- - (github.event_name == 'issues' && github.event.label.name == 'graycode-agent') || - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@graycode')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@graycode')) + (github.event_name == 'issues' && github.event.label.name == 'hawk-agent') || + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@hawk')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@hawk')) runs-on: ubuntu-latest steps: - name: Checkout @@ -39,11 +39,11 @@ jobs: with: fetch-depth: 0 - - name: Run Graycode - id: graycode - uses: GrayCodeAI/graycode-cli/.github/actions/graycode@main + - name: Run Hawk + id: hawk + uses: GrayCodeAI/hawk/.github/actions/hawk@main with: - # Leave prompt empty so Graycode derives it from the triggering event. + # Leave prompt empty so Hawk derives it from the triggering event. # Set a "/skill-name args" prompt here to dispatch a skill instead. prompt: "" auto: semi @@ -52,12 +52,12 @@ jobs: # Provide your model provider credentials as repository secrets. ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - - name: Post Graycode response as a comment - if: ${{ steps.graycode.outputs.response != '' }} + - name: Post Hawk response as a comment + if: ${{ steps.hawk.outputs.response != '' }} uses: actions/github-script@v7 with: script: | - const body = `🦅 **Graycode**\n\n${{ toJSON(steps.graycode.outputs.response) }}`; + const body = `🦅 **Hawk**\n\n${{ toJSON(steps.hawk.outputs.response) }}`; const issueNumber = context.issue.number; if (issueNumber) { await github.rest.issues.createComment({ diff --git a/flake.nix b/flake.nix index 4b5c56af2..79207cb5b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,12 @@ { - description = "Graycode - AI coding agent powered by graycode-router"; + description = "Hawk - AI coding agent powered by eyrie"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; # GrayCodeAI sibling repos — the public Go proxy has stale v0.1.0 tags # (post-history-rewrite), so resolve them locally like the Dockerfile. - graycode-router = { url = "github:GrayCodeAI/graycode-router"; flake = false; }; + eyrie = { url = "github:GrayCodeAI/eyrie"; flake = false; }; merlin = { url = "github:GrayCodeAI/merlin"; flake = false; }; kestrel = { url = "github:GrayCodeAI/kestrel"; flake = false; }; shrike = { url = "github:GrayCodeAI/shrike"; flake = false; }; @@ -14,14 +14,14 @@ harrier = { url = "github:GrayCodeAI/harrier"; flake = false; }; }; - outputs = { self, nixpkgs, flake-utils, graycode-router, merlin, kestrel, shrike, swift, harrier }: + outputs = { self, nixpkgs, flake-utils, eyrie, merlin, kestrel, shrike, swift, harrier }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; siblings = { - "github.com/GrayCodeAI/graycode-router" = graycode-router; + "github.com/GrayCodeAI/eyrie" = eyrie; "github.com/GrayCodeAI/merlin" = merlin; "github.com/GrayCodeAI/kestrel" = kestrel; "github.com/GrayCodeAI/shrike" = shrike; @@ -53,8 +53,8 @@ ) siblings)} ''; - graycode = pkgs.buildGoModule rec { - pname = "graycode"; + hawk = pkgs.buildGoModule rec { + pname = "hawk"; version = "0.0.1"; src = ./.; @@ -96,7 +96,7 @@ meta = with lib; { description = "AI coding agent that reads, writes, and runs code in your terminal"; - homepage = "https://github.com/GrayCodeAI/graycode-cli"; + homepage = "https://github.com/GrayCodeAI/hawk"; license = licenses.mit; maintainers = [ ]; }; @@ -104,8 +104,8 @@ in { packages = { - default = graycode; - inherit graycode; + default = hawk; + inherit hawk; }; devShells.default = pkgs.mkShell { @@ -119,14 +119,14 @@ ]; shellHook = '' - echo "Graycode development shell" + echo "Hawk development shell" echo "Go version: $(go version)" ''; }; apps.default = { type = "app"; - program = "${graycode}/bin/graycode"; + program = "${hawk}/bin/hawk"; }; }); } diff --git a/go.mod b/go.mod index 60152f531..1cbb7407a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/GrayCodeAI/graycode-cli +module github.com/GrayCodeAI/hawk go 1.26.6 @@ -11,7 +11,7 @@ require ( charm.land/bubbles/v2 v2.1.1 charm.land/bubbletea/v2 v2.0.8 charm.land/lipgloss/v2 v2.0.5 - github.com/GrayCodeAI/graycode-router v0.0.1 + github.com/GrayCodeAI/eyrie v0.0.1 github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 github.com/GrayCodeAI/merlin v0.0.0-20260902154444-0f4b9f7326cb diff --git a/go.sum b/go.sum index e4b80bfce..0b7382cec 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8 github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/GrayCodeAI/graycode-router v0.0.1 h1:+yfIteR3uNRpzLlcBuQQkEOvMK5b7oG/B9rRtil1Q5I= -github.com/GrayCodeAI/graycode-router v0.0.1/go.mod h1:7G4UtNEv1l943s6yg+uBBqkUt5b9It2X3yOkh9dMGzE= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 h1:X5lmXWlBdPVyk7lMAX7M0FiRr9o16Sq7yIjfN3mnXGU= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7/go.mod h1:JBkYX6kUbWCr1fsK//hxQ4Ro1VvlxtB+bSMR0/N5RRY= github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 h1:MrmWb0coJ3Uaxr/FtDSH1FaL/lqs663QXUh8ds+w8Og= diff --git a/install.sh b/install.sh index 5af2d8a04..2649d5ee3 100755 --- a/install.sh +++ b/install.sh @@ -2,16 +2,16 @@ set -e # Versioned install target. -# By default the binary is installed under $GRAYCODE_HOME/bin (default ~/.graycode/bin). -# Override with GRAYCODE_HOME env var, or pass --prefix as the first flag. -GRAYCODE_HOME="${GRAYCODE_HOME:-$HOME/.graycode}" +# By default the binary is installed under $HAWK_HOME/bin (default ~/.hawk/bin). +# Override with HAWK_HOME env var, or pass --prefix as the first flag. +HAWK_HOME="${HAWK_HOME:-$HOME/.hawk}" if [ "$1" = "--prefix" ]; then - GRAYCODE_HOME="$2" + HAWK_HOME="$2" shift 2 fi -REPO="GrayCodeAI/graycode-cli" -BINARY="graycode" +REPO="GrayCodeAI/hawk" +BINARY="hawk" OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) @@ -39,7 +39,7 @@ fi ARCHIVE_NAME="${BINARY}_${LATEST}_${OS}_${ARCH}.${ARCHIVE_EXT}" URL="https://github.com/$REPO/releases/download/v${LATEST}/${ARCHIVE_NAME}" -echo "Downloading graycode v${LATEST} for ${OS}/${ARCH}..." +echo "Downloading hawk v${LATEST} for ${OS}/${ARCH}..." TMP=$(mktemp -d) ARCHIVE="$TMP/${ARCHIVE_NAME}" @@ -146,25 +146,25 @@ VERSION=$(printf '%s' "$LATEST" | sed 's/^v//') # falling back to checksum-only verification otherwise. Versioned install is a # prerequisite for safe in-place self-update tooling: once installs land at a # stable versioned path + symlink, a future updater can swap the link without -# ever replacing a binary a running graycode has mmap'd (same SIGKILL rationale). +# ever replacing a binary a running hawk has mmap'd (same SIGKILL rationale). # # Windows lacks reliable non-admin symlinks, so the launcher is a plain copy. -BINDIR="$GRAYCODE_HOME/bin" +BINDIR="$HAWK_HOME/bin" mkdir -p "$BINDIR" if [ "$OS" = "windows" ]; then - mv -f "$TMP/$BIN_NAME" "$BINDIR/graycode-$VERSION.exe" - cp -f "$BINDIR/graycode-$VERSION.exe" "$BINDIR/graycode.exe" + mv -f "$TMP/$BIN_NAME" "$BINDIR/hawk-$VERSION.exe" + cp -f "$BINDIR/hawk-$VERSION.exe" "$BINDIR/hawk.exe" echo "" - echo "Installed graycode v$VERSION to $BINDIR/graycode-$VERSION.exe" - echo "Linked launcher: $BINDIR/graycode.exe" + echo "Installed hawk v$VERSION to $BINDIR/hawk-$VERSION.exe" + echo "Linked launcher: $BINDIR/hawk.exe" else - mv -f "$TMP/$BIN_NAME" "$BINDIR/graycode-$VERSION" - ln -sf "graycode-$VERSION" "$BINDIR/graycode.tmp" \ - && mv -f "$BINDIR/graycode.tmp" "$BINDIR/graycode" + mv -f "$TMP/$BIN_NAME" "$BINDIR/hawk-$VERSION" + ln -sf "hawk-$VERSION" "$BINDIR/hawk.tmp" \ + && mv -f "$BINDIR/hawk.tmp" "$BINDIR/hawk" echo "" - echo "Installed graycode v$VERSION to $BINDIR/graycode-$VERSION (linked: $BINDIR/graycode)" + echo "Installed hawk v$VERSION to $BINDIR/hawk-$VERSION (linked: $BINDIR/hawk)" fi rm -rf "$TMP" @@ -172,5 +172,5 @@ echo "" echo "Add $BINDIR to your PATH if it is not already, e.g." echo " export PATH=\"\$PATH:$BINDIR\"" echo "" -echo "Restart any running graycode sessions to pick up the new binary — the old" +echo "Restart any running hawk sessions to pick up the new binary — the old" echo "process keeps running the previous version until it is restarted." diff --git a/internal/acp/acp_extra_test.go b/internal/acp/acp_extra_test.go index 7d4b5e141..706426ace 100644 --- a/internal/acp/acp_extra_test.go +++ b/internal/acp/acp_extra_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // --- Error factory for testing --- diff --git a/internal/acp/client_test.go b/internal/acp/client_test.go index 8997b0035..a128b6f24 100644 --- a/internal/acp/client_test.go +++ b/internal/acp/client_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // mockSession creates a minimal engine session for testing. diff --git a/internal/acp/content.go b/internal/acp/content.go index 8b1820d1e..ce2bd2db4 100644 --- a/internal/acp/content.go +++ b/internal/acp/content.go @@ -17,7 +17,7 @@ import ( "regexp" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/attachment" + "github.com/GrayCodeAI/hawk/internal/attachment" ) // Raster formats shared by ACP image blocks and the core attachment diff --git a/internal/acp/content_test.go b/internal/acp/content_test.go index 4e9de6ffd..1bf4bba22 100644 --- a/internal/acp/content_test.go +++ b/internal/acp/content_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/attachment" + "github.com/GrayCodeAI/hawk/internal/attachment" ) // pngFixture encodes a 4x3 NRGBA raster as PNG bytes. diff --git a/internal/acp/server.go b/internal/acp/server.go index a53301497..b3a227695 100644 --- a/internal/acp/server.go +++ b/internal/acp/server.go @@ -1,4 +1,4 @@ -// Package acp implements an Agent Client Protocol (ACP) server for graycode, exposing +// Package acp implements an Agent Client Protocol (ACP) server for hawk, exposing // the agent over newline-delimited JSON-RPC 2.0 on stdio so editors (e.g. Zed) // can drive it. It mirrors the framing of internal/mcp and the session-driving // pattern of internal/daemon. @@ -6,7 +6,7 @@ // Scope (first cut): the core agent-side methods (initialize, session/new, // session/prompt, session/cancel), streamed session/update notifications, and // client-routed tool approvals via session/request_permission. File reads/writes -// use graycode's local tools; client fs routing is intentionally out of scope. +// use hawk's local tools; client fs routing is intentionally out of scope. package acp import ( @@ -20,10 +20,10 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/attachment" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" - statussnapshot "github.com/GrayCodeAI/graycode-cli/internal/status" + "github.com/GrayCodeAI/hawk/internal/attachment" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" + statussnapshot "github.com/GrayCodeAI/hawk/internal/status" ) // ProtocolVersion is the ACP protocol version this server implements. @@ -186,8 +186,8 @@ func (s *Server) handle(ctx context.Context, msg rpcMessage) { "audio": false, }, }, - // Graycode control-plane metadata for IDE clients that want it. - "graycodeCapabilities": map[string]any{ + // Hawk control-plane metadata for IDE clients that want it. + "hawkCapabilities": map[string]any{ "workModes": []string{"plan", "act", "review"}, "isolation": []string{"dev", "workspace", "strict", "container"}, "folderTrust": true, @@ -357,8 +357,8 @@ func (s *Server) handleSessionNew(msg rpcMessage) { s.reply(msg.ID, map[string]any{ "sessionId": id, - // Graycode extensions (ignored by clients that only read sessionId). - "graycode": map[string]any{ + // Hawk extensions (ignored by clients that only read sessionId). + "hawk": map[string]any{ "workMode": string(sess.WorkMode()), "isolation": sess.Isolation().String(), "autoCommit": sess.AutoCommit(), diff --git a/internal/acp/server_test.go b/internal/acp/server_test.go index 3046f9d9f..178650947 100644 --- a/internal/acp/server_test.go +++ b/internal/acp/server_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/tool" ) // testFactory builds a session backed by the engine's canned mock chat client, @@ -134,7 +134,7 @@ func TestACP_ParseError(t *testing.T) { func TestACP_SessionLoad(t *testing.T) { tempDir := t.TempDir() - t.Setenv("GRAYCODE_SESSIONS_DIR", tempDir) + t.Setenv("HAWK_SESSIONS_DIR", tempDir) // Create and persist a session sessID := "acp-load-test-1" @@ -185,7 +185,7 @@ func TestACP_SessionLoad(t *testing.T) { func TestACP_SessionList(t *testing.T) { tempDir := t.TempDir() - t.Setenv("GRAYCODE_SESSIONS_DIR", tempDir) + t.Setenv("HAWK_SESSIONS_DIR", tempDir) prior := &session.Session{ ID: "acp-list-test-1", diff --git a/internal/appverify/appverify_test.go b/internal/appverify/appverify_test.go index 6e5a990e7..0428df7cf 100644 --- a/internal/appverify/appverify_test.go +++ b/internal/appverify/appverify_test.go @@ -116,7 +116,7 @@ func TestManifestRoundTripAndPriority(t *testing.T) { func TestLoadManifestCorruptIsError(t *testing.T) { root := writeProject(t, map[string]string{ - ".graycode/verify/environment.json": "{not json", + ".hawk/verify/environment.json": "{not json", }) if _, err := LoadManifest(root); err == nil { t.Fatal("expected error for corrupt manifest") diff --git a/internal/appverify/manifest.go b/internal/appverify/manifest.go index ee264e14e..576be01b2 100644 --- a/internal/appverify/manifest.go +++ b/internal/appverify/manifest.go @@ -6,13 +6,13 @@ import ( "os" "path/filepath" - "github.com/GrayCodeAI/graycode-cli/internal/safewrite" + "github.com/GrayCodeAI/hawk/internal/safewrite" ) // ManifestPath returns the location of the persisted verify environment -// manifest for a project: /.graycode/verify/environment.json. +// manifest for a project: /.hawk/verify/environment.json. func ManifestPath(root string) string { - return filepath.Join(root, ".graycode", "verify", "environment.json") + return filepath.Join(root, ".hawk", "verify", "environment.json") } // Manifest is the on-disk contract between recipe detection and execution. diff --git a/internal/appverify/prompt.go b/internal/appverify/prompt.go index c28827800..19b17fc58 100644 --- a/internal/appverify/prompt.go +++ b/internal/appverify/prompt.go @@ -7,7 +7,7 @@ import ( // EvidenceDir is the stable, workspace-relative directory for verification // artifacts. Stable paths let reports and downstream tooling rely on them. -const EvidenceDir = ".graycode/verify/artifacts" +const EvidenceDir = ".hawk/verify/artifacts" // BuildVerifyPrompt renders the phased QA-engineer prompt for the recipe. The // discipline it encodes (adopted from grok-cli) is that build/test passing diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 14a13183b..dda15e824 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -13,8 +13,8 @@ import ( "runtime" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/safewrite" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/safewrite" + "github.com/GrayCodeAI/hawk/internal/storage" ) // TokenStore manages authentication tokens. @@ -61,7 +61,7 @@ func (s *SecureStorage) Get(account string) (string, error) { if runtime.GOOS == "windows" { return s.getWindows(account) } - // Fallback to file-based storage for Linux (keyring handled by graycode-router layer) + // Fallback to file-based storage for Linux (keyring handled by eyrie layer) return s.getFile(account) } diff --git a/internal/auth/auth_extra_test.go b/internal/auth/auth_extra_test.go index a093f2723..ecb86d082 100644 --- a/internal/auth/auth_extra_test.go +++ b/internal/auth/auth_extra_test.go @@ -73,7 +73,7 @@ func TestNewSecureStorage(t *testing.T) { func TestSecureStorage_GetFile_NonExistent(t *testing.T) { // Override the config directory to a temp dir so .tokens definitely doesn't exist - t.Setenv("GRAYCODE_CONFIG_DIR", t.TempDir()) + t.Setenv("HAWK_CONFIG_DIR", t.TempDir()) ss := &SecureStorage{service: "test"} // getFile will fail because the file doesn't exist in the temp dir diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index cdb32061c..b020ad48e 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) func TestTokenStore(t *testing.T) { @@ -119,12 +119,12 @@ func TestGenerateNonce(t *testing.T) { func TestSecureStorage(t *testing.T) { t.Run("new secure storage", func(t *testing.T) { t.Parallel() - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") if ss == nil { t.Fatal("NewSecureStorage returned nil") } - if ss.service != "graycode-test" { - t.Errorf("service = %q, want %q", ss.service, "graycode-test") + if ss.service != "hawk-test" { + t.Errorf("service = %q, want %q", ss.service, "hawk-test") } }) @@ -132,7 +132,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") _, err := ss.getFile("nonexistent") if err == nil { t.Error("getFile() should return error for missing file") @@ -143,7 +143,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") if err := ss.setFile("anthropic", "sk-test-token"); err != nil { t.Fatalf("setFile() error = %v", err) } @@ -161,7 +161,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") if err := ss.setFile("provider", "old-token"); err != nil { t.Fatal(err) } @@ -182,7 +182,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") if err := ss.setFile("test", "secret"); err != nil { t.Fatal(err) } @@ -202,7 +202,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("graycode-test") + ss := NewSecureStorage("hawk-test") if err := ss.setFile("provider1", "token1"); err != nil { t.Fatal(err) } diff --git a/internal/auth/storage_extra_test.go b/internal/auth/storage_extra_test.go index 4c6efcc9e..b8b05d804 100644 --- a/internal/auth/storage_extra_test.go +++ b/internal/auth/storage_extra_test.go @@ -11,7 +11,7 @@ import ( func TestSecureStorage_GetSet_Fallback(t *testing.T) { // Override config dir to use a temp directory tmpDir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", tmpDir) + t.Setenv("HAWK_CONFIG_DIR", tmpDir) ss := &SecureStorage{service: "test-service"} @@ -34,7 +34,7 @@ func TestSecureStorage_GetSet_Fallback(t *testing.T) { // TestSecureStorage_GetSet_MultipleAccounts tests storing multiple accounts. func TestSecureStorage_GetSet_MultipleAccounts(t *testing.T) { tmpDir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", tmpDir) + t.Setenv("HAWK_CONFIG_DIR", tmpDir) ss := &SecureStorage{service: "test-service"} @@ -67,7 +67,7 @@ func TestSecureStorage_GetSet_MultipleAccounts(t *testing.T) { // TestSecureStorage_Get_NonExistent tests getting a token that doesn't exist. func TestSecureStorage_Get_NonExistent(t *testing.T) { tmpDir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", tmpDir) + t.Setenv("HAWK_CONFIG_DIR", tmpDir) ss := &SecureStorage{service: "test-service"} @@ -81,7 +81,7 @@ func TestSecureStorage_Get_NonExistent(t *testing.T) { // TestSecureStorage_Set_OverwritesExisting tests that Set overwrites existing tokens. func TestSecureStorage_Set_OverwritesExisting(t *testing.T) { tmpDir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", tmpDir) + t.Setenv("HAWK_CONFIG_DIR", tmpDir) ss := &SecureStorage{service: "test-service"} @@ -108,7 +108,7 @@ func TestSecureStorage_Set_OverwritesExisting(t *testing.T) { // TestSecureStorage_GetFile_CorruptJSON tests that corrupt JSON returns an error. func TestSecureStorage_GetFile_CorruptJSON(t *testing.T) { tmpDir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", tmpDir) + t.Setenv("HAWK_CONFIG_DIR", tmpDir) // Write corrupt JSON to the token file tokenFile := filepath.Join(tmpDir, ".tokens") diff --git a/internal/autoinit/autoinit.go b/internal/autoinit/autoinit.go index e4de2fd6b..9100e372f 100644 --- a/internal/autoinit/autoinit.go +++ b/internal/autoinit/autoinit.go @@ -1,13 +1,13 @@ // Package autoinit performs a one-time, automatic codebase-analysis pass the -// first time graycode runs in a project that has no context files (AGENTS.md / -// GRAYCODE.md / CLAUDE.md). It mirrors the behaviour of the `init-deep` skill but +// first time hawk runs in a project that has no context files (AGENTS.md / +// HAWK.md / CLAUDE.md). It mirrors the behaviour of the `init-deep` skill but // is gated so it runs at most once per project and can be disabled entirely. // // The package is intentionally additive and self-contained: it only inspects // the filesystem to decide whether to run, writes a marker file once a run has // been attempted, and delegates the actual analysis to a caller-supplied // runner. Callers (the cmd layer) wire the runner to whatever drives the -// init analysis (the init-deep skill / `graycode init`). When no runner is wired, +// init analysis (the init-deep skill / `hawk init`). When no runner is wired, // MaybeRun is a no-op beyond gating, so importing the package never changes // behaviour on its own. package autoinit @@ -18,8 +18,8 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/config" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/hawk/internal/storage" ) // markerName is the file written under the project's user-state directory once an @@ -30,12 +30,12 @@ const markerName = "auto-init.done" // disableEnv, when set to a truthy value ("1", "true", "yes", "on"), disables // auto-init globally. This is the kill switch for users/CI that never want the // behaviour. -const disableEnv = "GRAYCODE_DISABLE_AUTO_INIT" +const disableEnv = "HAWK_DISABLE_AUTO_INIT" // contextFiles are the project-level context files whose presence means the // project already has context and auto-init should be skipped. This matches -// the convention files recognized elsewhere in graycode. -var contextFiles = []string{"AGENTS.md", "GRAYCODE.md", "CLAUDE.md", "CONTEXT.md"} +// the convention files recognized elsewhere in hawk. +var contextFiles = []string{"AGENTS.md", "HAWK.md", "CLAUDE.md", "CONTEXT.md"} // Runner performs the actual codebase analysis for a project rooted at root. // It is supplied by the caller so this package carries no dependency on the @@ -93,7 +93,7 @@ func HasRun(root string) bool { // MaybeRun runs the auto-init analysis at most once for opts.Root, subject to // the gating rules: // -// 1. Disabled via GRAYCODE_DISABLE_AUTO_INIT -> skip. +// 1. Disabled via HAWK_DISABLE_AUTO_INIT -> skip. // 2. Marker file already present -> skip. // 3. Project already has a context file -> mark + skip (unless Force). // 4. Otherwise -> run, then mark. diff --git a/internal/autoinit/autoinit_test.go b/internal/autoinit/autoinit_test.go index 335507bd2..60d920222 100644 --- a/internal/autoinit/autoinit_test.go +++ b/internal/autoinit/autoinit_test.go @@ -97,7 +97,7 @@ func TestMaybeRun_Disabled(t *testing.T) { func TestMaybeRun_ForceIgnoresExistingContext(t *testing.T) { setTestStateDir(t) root := t.TempDir() - if err := os.WriteFile(filepath.Join(root, "GRAYCODE.md"), []byte("# x"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(root, "HAWK.md"), []byte("# x"), 0o644); err != nil { t.Fatal(err) } calls := 0 @@ -150,7 +150,7 @@ func TestMaybeRun_ReturnsMarkerWriteError(t *testing.T) { if err := os.WriteFile(stateFile, []byte("not a directory"), 0o644); err != nil { t.Fatal(err) } - t.Setenv("GRAYCODE_STATE_DIR", stateFile) + t.Setenv("HAWK_STATE_DIR", stateFile) calls := 0 run := func(context.Context, string) error { calls++ @@ -184,5 +184,5 @@ func TestHasContext(t *testing.T) { func setTestStateDir(t *testing.T) { t.Helper() - t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) } diff --git a/internal/bench/bench_test.go b/internal/bench/bench_test.go index 3e794a239..4f41a07b3 100644 --- a/internal/bench/bench_test.go +++ b/internal/bench/bench_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/types" ) // taskFixture is a minimal SWE-bench-style task: an instruction plus the set @@ -23,7 +23,7 @@ type taskFixture struct { // sWEbenchSmokeTasks is a small curated fixture set exercising the common // agent shapes: read+edit, planning, and a no-op task. Kept tiny so the bench // stays a compile+smoke gate (real SWE-bench evaluation uses real providers -// and is gated by GRAYCODE_BENCH_PROVIDER / GRAYCODE_BENCH_API_KEY, which is why this +// and is gated by HAWK_BENCH_PROVIDER / HAWK_BENCH_API_KEY, which is why this // test only runs when that env is set — see TestBenchmark_SWE_benchHeadless). var sWEbenchSmokeTasks = []taskFixture{ { @@ -43,15 +43,15 @@ var sWEbenchSmokeTasks = []taskFixture{ // so a benchmark can assert the agent attempted them. type stubChatClient struct { t testing.TB - events []types.GraycodeRouterStreamEvent + events []types.EyrieStreamEvent } -func (m *stubChatClient) Chat(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions) (*types.GraycodeRouterResponse, error) { - return &types.GraycodeRouterResponse{Content: "stub", FinishReason: "end_turn"}, nil +func (m *stubChatClient) Chat(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions) (*types.EyrieResponse, error) { + return &types.EyrieResponse{Content: "stub", FinishReason: "end_turn"}, nil } -func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { - ch := make(chan types.GraycodeRouterStreamEvent, len(m.events)+1) +func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { + ch := make(chan types.EyrieStreamEvent, len(m.events)+1) for _, e := range m.events { ch <- e } @@ -61,7 +61,7 @@ func (m *stubChatClient) StreamChatContinue(_ context.Context, _ []types.Graycod // runTask drives engine.Session.Stream for a single fixture and returns the // flattened event sequence + whether the loop terminated cleanly. -func runTask(t *testing.B, fix taskFixture, events []types.GraycodeRouterStreamEvent) (terminated bool, got []engine.StreamEvent) { +func runTask(t *testing.B, fix taskFixture, events []types.EyrieStreamEvent) (terminated bool, got []engine.StreamEvent) { svc := &stubChatClient{t: t, events: events} s := engine.NewSessionWithClient(svc, "bench", "bench-model", "bench system", nil, false) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -81,30 +81,30 @@ func runTask(t *testing.B, fix taskFixture, events []types.GraycodeRouterStreamE } // TestBenchmark_SWE_benchHeadless is a compile + smoke gate for the headless -// agent loop. It is skipped unless GRAYCODE_BENCH_HEADLESS=1 is set so it never +// agent loop. It is skipped unless HAWK_BENCH_HEADLESS=1 is set so it never // runs in CI by default (it uses a stub provider). Real provider-backed SWE -// harness execution lives in bench_suite.go and is invoked via `graycode bench`. +// harness execution lives in bench_suite.go and is invoked via `hawk bench`. func TestBenchmark_SWE_benchHeadless(t *testing.T) { - if v := os.Getenv("GRAYCODE_BENCH_HEADLESS"); v != "1" { + if v := os.Getenv("HAWK_BENCH_HEADLESS"); v != "1" { // TODO: track scheduling the headless benchmark smoke in CI. - t.Skip("set GRAYCODE_BENCH_HEADLESS=1 to run agent-loop benchmark smoke") + t.Skip("set HAWK_BENCH_HEADLESS=1 to run agent-loop benchmark smoke") } b := &testing.B{} for _, fix := range sWEbenchSmokeTasks { // The stub emits a content + done event, modelling a provider that // answers without tools (the no-tools path) or a read+answer path. - events := []types.GraycodeRouterStreamEvent{ + events := []types.EyrieStreamEvent{ {Type: "content", Content: "understood"}, } if len(fix.MustUse) > 0 { // Simulate a tool-use turn followed by completion. events = nil for _, name := range fix.MustUse { - events = append(events, types.GraycodeRouterStreamEvent{Type: "tool_use", Content: name}) + events = append(events, types.EyrieStreamEvent{Type: "tool_use", Content: name}) } - events = append(events, types.GraycodeRouterStreamEvent{Type: "content", Content: "done"}) + events = append(events, types.EyrieStreamEvent{Type: "content", Content: "done"}) } - events = append(events, types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) + events = append(events, types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) terminated, got := runTask(b, fix, events) if !terminated { diff --git a/internal/bench/suite.go b/internal/bench/suite.go index 329a6fc3a..07588127f 100644 --- a/internal/bench/suite.go +++ b/internal/bench/suite.go @@ -9,7 +9,7 @@ import ( "path/filepath" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // BenchmarkResult holds the result of a single benchmark run. @@ -41,9 +41,9 @@ func RunAll(projectDir string) (*BenchmarkSuite, error) { suite.Results = append(suite.Results, shrikeResults...) } - // Run graycode build benchmark - if graycodeResult, err := runGraycodeBuildBench(projectDir); err == nil { - suite.Results = append(suite.Results, graycodeResult) + // Run hawk build benchmark + if hawkResult, err := runHawkBuildBench(projectDir); err == nil { + suite.Results = append(suite.Results, hawkResult) } return suite, nil @@ -103,21 +103,21 @@ func runShrikeBench(projectDir string) ([]BenchmarkResult, error) { return []BenchmarkResult{result}, nil } -// runGraycodeBuildBench measures graycode build time. -func runGraycodeBuildBench(projectDir string) (BenchmarkResult, error) { - graycodeDir := filepath.Join(projectDir) - if _, err := os.Stat(filepath.Join(graycodeDir, "go.mod")); err != nil { - return BenchmarkResult{}, fmt.Errorf("graycode not found") +// runHawkBuildBench measures hawk build time. +func runHawkBuildBench(projectDir string) (BenchmarkResult, error) { + hawkDir := filepath.Join(projectDir) + if _, err := os.Stat(filepath.Join(hawkDir, "go.mod")); err != nil { + return BenchmarkResult{}, fmt.Errorf("hawk not found") } start := time.Now() cmd := exec.CommandContext(context.Background(), "go", "build", "-o", "/dev/null", ".") - cmd.Dir = graycodeDir + cmd.Dir = hawkDir err := cmd.Run() duration := time.Since(start) return BenchmarkResult{ - Name: "graycode/build", + Name: "hawk/build", Duration: duration, Score: float64(duration.Milliseconds()), Metric: "time", diff --git a/internal/bridge/kestrel/bridge.go b/internal/bridge/kestrel/bridge.go index cbecd27c8..dc4c94bf1 100644 --- a/internal/bridge/kestrel/bridge.go +++ b/internal/bridge/kestrel/bridge.go @@ -6,36 +6,36 @@ import ( "sync" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" - typescontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" - "github.com/GrayCodeAI/graycode-cli/internal/types" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + reviewcontracts "github.com/GrayCodeAI/hawk/internal/contracts/review" + typescontracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + "github.com/GrayCodeAI/hawk/internal/graphjournal" + "github.com/GrayCodeAI/hawk/internal/types" kestrelLib "github.com/GrayCodeAI/kestrel" kestrelgraph "github.com/GrayCodeAI/kestrel/graph" "github.com/GrayCodeAI/kestrel/qualitygraph" kestrelreview "github.com/GrayCodeAI/kestrel/review" ) -// GraycodeRouterAdapter implements kestrel's Provider interface using graycode's graycode-router client. -// It translates between kestrel.Message/kestrel.ChatOpts and Graycode runtime DTOs. -type GraycodeRouterAdapter struct { +// EyrieAdapter implements kestrel's Provider interface using hawk's eyrie client. +// It translates between kestrel.Message/kestrel.ChatOpts and Hawk runtime DTOs. +type EyrieAdapter struct { client types.ChatProvider provider string } -// NewGraycodeRouterAdapter creates an adapter that satisfies kestrel.Provider using -// the given graycode-router client and provider name (e.g. "anthropic", "openai"). -func NewGraycodeRouterAdapter(c types.ChatProvider, provider string) *GraycodeRouterAdapter { - return &GraycodeRouterAdapter{client: c, provider: provider} +// NewEyrieAdapter creates an adapter that satisfies kestrel.Provider using +// the given eyrie client and provider name (e.g. "anthropic", "openai"). +func NewEyrieAdapter(c types.ChatProvider, provider string) *EyrieAdapter { + return &EyrieAdapter{client: c, provider: provider} } -// Chat translates a kestrel LLM request into an graycode-router call and returns the +// Chat translates a kestrel LLM request into an eyrie call and returns the // result in kestrel's Response format. -func (a *GraycodeRouterAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, opts kestrelLib.ChatOpts) (*kestrelLib.Response, error) { - graycodeRouterMessages := make([]types.GraycodeRouterMessage, len(messages)) +func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, opts kestrelLib.ChatOpts) (*kestrelLib.Response, error) { + eyrieMessages := make([]types.EyrieMessage, len(messages)) for i, m := range messages { - graycodeRouterMessages[i] = types.GraycodeRouterMessage{ + eyrieMessages[i] = types.EyrieMessage{ Role: m.Role, Content: m.Content, } @@ -47,7 +47,7 @@ func (a *GraycodeRouterAdapter) Chat(ctx context.Context, messages []kestrelLib. temp = &t } - graycodeRouterOpts := types.ChatOptions{ + eyrieOpts := types.ChatOptions{ Provider: a.provider, Model: opts.Model, MaxTokens: opts.MaxTokens, @@ -55,7 +55,7 @@ func (a *GraycodeRouterAdapter) Chat(ctx context.Context, messages []kestrelLib. System: opts.System, } - resp, err := a.client.Chat(ctx, graycodeRouterMessages, graycodeRouterOpts) + resp, err := a.client.Chat(ctx, eyrieMessages, eyrieOpts) if err != nil { return nil, err } @@ -71,11 +71,11 @@ func (a *GraycodeRouterAdapter) Chat(ctx context.Context, messages []kestrelLib. }, nil } -// Bridge connects graycode to the kestrel code-review library. +// Bridge connects hawk to the kestrel code-review library. // If initialization fails, all operations degrade gracefully and return // empty results rather than errors. type Bridge struct { - adapter *GraycodeRouterAdapter + adapter *EyrieAdapter reviewer *kestrelLib.Reviewer opts []kestrelLib.Option mu sync.Mutex @@ -91,7 +91,7 @@ type GraphObservation struct { MaxFindings int } -// NewBridge creates a bridge to the kestrel library using the given Graycode +// NewBridge creates a bridge to the kestrel library using the given Hawk // transport client and provider name. Additional kestrel options (model, // concerns, etc.) are applied to all operations. func NewBridge(c types.ChatProvider, provider string, opts ...kestrelLib.Option) *Bridge { @@ -104,7 +104,7 @@ func (b *Bridge) init(c types.ChatProvider, provider string, opts ...kestrelLib. if c == nil { return } - b.adapter = NewGraycodeRouterAdapter(c, provider) + b.adapter = NewEyrieAdapter(c, provider) // Prepend the provider option so callers don't have to. b.opts = append([]kestrelLib.Option{kestrelLib.WithProvider(b.adapter)}, opts...) b.reviewer = kestrelLib.NewReviewer(b.opts...) @@ -227,7 +227,7 @@ func (b *Bridge) Improve(ctx context.Context, diff string) (*kestrelLib.ImproveR } // The following helpers convert Kestrel's vendored contract types into -// Graycode's contracts/* contract types (and the reverse for scope). The definitions +// Hawk's contracts/* contract types (and the reverse for scope). The definitions // are byte-identical, so conversion is a field-by-field copy at the boundary. func toKestrelScope(s graphcontracts.Scope) kestrelgraph.Scope { diff --git a/internal/bridge/kestrel/bridge_test.go b/internal/bridge/kestrel/bridge_test.go index f4486b27b..8e5679b45 100644 --- a/internal/bridge/kestrel/bridge_test.go +++ b/internal/bridge/kestrel/bridge_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + "github.com/GrayCodeAI/hawk/internal/graphjournal" kestrelLib "github.com/GrayCodeAI/kestrel" ) @@ -15,7 +15,7 @@ func TestReviewContractsObservedRecordsQualityGraph(t *testing.T) { if strings.Contains(kestrelLib.Version, "stub") { t.Skip("kestrel engine is the build-harness stub; skipping quality-graph integration test") } - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) bridge := &Bridge{} at := time.Date(2026, time.July, 25, 13, 0, 0, 0, time.UTC) result, err := bridge.ReviewContractsObserved( @@ -88,11 +88,11 @@ func TestBridge_ImproveNotReady(t *testing.T) { } } -func TestGraycodeRouterAdapter_Chat(t *testing.T) { - // Test that GraycodeRouterAdapter correctly translates kestrel messages. - // We can't easily test with a real graycode-router client, but we verify the adapter +func TestEyrieAdapter_Chat(t *testing.T) { + // Test that EyrieAdapter correctly translates kestrel messages. + // We can't easily test with a real eyrie client, but we verify the adapter // struct is properly constructed. - adapter := NewGraycodeRouterAdapter(nil, "openai") + adapter := NewEyrieAdapter(nil, "openai") if adapter.provider != "openai" { t.Fatalf("expected provider openai, got %s", adapter.provider) } diff --git a/internal/bridge/merlin/bridge.go b/internal/bridge/merlin/bridge.go index 9f2a9abc1..92e3e7f51 100644 --- a/internal/bridge/merlin/bridge.go +++ b/internal/bridge/merlin/bridge.go @@ -6,17 +6,17 @@ import ( "sync" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - typescontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - verifycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/verify" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + typescontracts "github.com/GrayCodeAI/hawk/internal/contracts/types" + verifycontracts "github.com/GrayCodeAI/hawk/internal/contracts/verify" + "github.com/GrayCodeAI/hawk/internal/graphjournal" merlinLib "github.com/GrayCodeAI/merlin" merlingraph "github.com/GrayCodeAI/merlin/graph" "github.com/GrayCodeAI/merlin/qualitygraph" merlinverify "github.com/GrayCodeAI/merlin/verify" ) -// Bridge connects graycode to the merlin site-auditing library. +// Bridge connects hawk to the merlin site-auditing library. // If initialization fails, all operations degrade gracefully and return // empty results rather than errors. type Bridge struct { @@ -25,7 +25,7 @@ type Bridge struct { ready bool } -// GraphObservation identifies an opt-in Graycode quality-graph journal record. +// GraphObservation identifies an opt-in Hawk quality-graph journal record. type GraphObservation struct { SessionID string ToolCallID string @@ -146,7 +146,7 @@ func (b *Bridge) RunContractsObserved( return toContractReport(contractReport), nil } -// The following helpers convert Merlin's vendored contract types into Graycode's +// The following helpers convert Merlin's vendored contract types into Hawk's // contracts/* contract types (and the reverse for scope). The definitions are // byte-identical, so conversion is a field-by-field copy at the boundary. diff --git a/internal/bridge/merlin/bridge_test.go b/internal/bridge/merlin/bridge_test.go index 351a71dc5..9a31adc92 100644 --- a/internal/bridge/merlin/bridge_test.go +++ b/internal/bridge/merlin/bridge_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + "github.com/GrayCodeAI/hawk/internal/graphjournal" merlinLib "github.com/GrayCodeAI/merlin" ) @@ -44,7 +44,7 @@ func TestRunContractsObservedRecordsQualityGraph(t *testing.T) { if strings.Contains(merlinLib.Version, "stub") { t.Skip("merlin engine is the build-harness stub; skipping quality-graph integration test") } - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) observedAt := time.Date(2026, time.July, 25, 12, 0, 0, 0, time.UTC) b := &Bridge{} diff --git a/internal/bridge/swift/lib_bridge.go b/internal/bridge/swift/lib_bridge.go index 8f3102d1f..d3bfac9b7 100644 --- a/internal/bridge/swift/lib_bridge.go +++ b/internal/bridge/swift/lib_bridge.go @@ -1,7 +1,7 @@ // Package swiftbridge provides a Go library bridge that wraps swift's -// functionality for use by graycode, replacing the subprocess-based approach. +// functionality for use by hawk, replacing the subprocess-based approach. // -// Since swift and graycode are separate Go modules, this package uses +// Since swift and hawk are separate Go modules, this package uses // interface-based decoupling. The bridge defines thin interfaces that // swift types satisfy, avoiding a direct go.mod dependency. package swiftbridge diff --git a/internal/catalogtest/install.go b/internal/catalogtest/install.go index 9ae5f25fa..d069d5e63 100644 --- a/internal/catalogtest/install.go +++ b/internal/catalogtest/install.go @@ -16,11 +16,11 @@ var ( globalPath string ) -// InstallGlobal writes the test catalog to a temp file and sets GRAYCODE_ROUTER_MODEL_CATALOG_PATH. +// InstallGlobal writes the test catalog to a temp file and sets EYRIE_MODEL_CATALOG_PATH. // Call from TestMain; returns cleanup to unset env. func InstallGlobal() (cleanup func()) { globalOnce.Do(func() { - dir, err := os.MkdirTemp("", "graycode-catalog-*") + dir, err := os.MkdirTemp("", "hawk-catalog-*") if err != nil { // In TestMain, we can't use t.Fatal, so log and exit. // This is a test helper, so panicking is acceptable but we'll use a clearer message. @@ -30,19 +30,19 @@ func InstallGlobal() (cleanup func()) { if err := os.WriteFile(globalPath, minimalCatalogJSON, 0o600); err != nil { panic("catalogtest: failed to write catalog: " + err.Error()) } - _ = os.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", globalPath) + _ = os.Setenv("EYRIE_MODEL_CATALOG_PATH", globalPath) }) return func() { - _ = os.Unsetenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH") + _ = os.Unsetenv("EYRIE_MODEL_CATALOG_PATH") } } -// Install sets GRAYCODE_ROUTER_MODEL_CATALOG_PATH for a single test (per-test temp file). +// Install sets EYRIE_MODEL_CATALOG_PATH for a single test (per-test temp file). func Install(t testing.TB) { t.Helper() path := filepath.Join(t.TempDir(), "model_catalog.json") if err := os.WriteFile(path, minimalCatalogJSON, 0o600); err != nil { t.Fatalf("catalogtest: failed to write catalog: %v", err) } - t.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", path) + t.Setenv("EYRIE_MODEL_CATALOG_PATH", path) } diff --git a/internal/circuitbreaker/circuitbreaker.go b/internal/circuitbreaker/circuitbreaker.go index ffdeda158..cd342e057 100644 --- a/internal/circuitbreaker/circuitbreaker.go +++ b/internal/circuitbreaker/circuitbreaker.go @@ -3,7 +3,7 @@ // OpenClaude's auto-compact circuit breaker: after a threshold of consecutive // failures, the breaker "opens" and skips the operation for a cooldown window, // then re-arms in a half-open state so a single success closes it and a single -// failure re-opens it. Graycode uses it to stop runaway auto-compaction (an +// failure re-opens it. Hawk uses it to stop runaway auto-compaction (an // irrecoverable prompt_too_long would otherwise retry thousands of times). package circuitbreaker diff --git a/internal/codegraph/algorithms_cgo_more.go b/internal/codegraph/algorithms_cgo_more.go index 9c2cac85f..bfadae73d 100644 --- a/internal/codegraph/algorithms_cgo_more.go +++ b/internal/codegraph/algorithms_cgo_more.go @@ -255,7 +255,7 @@ func (cg *CodeGraph) AnalyzeCoupling(topN int) ([]CouplingMetric, error) { } // CrossRepoQuery queries across multiple codegraph databases. -// Useful for finding relationships between graycode, graycode-router, shrike, harrier, etc. +// Useful for finding relationships between hawk, eyrie, shrike, harrier, etc. func CrossRepoQuery(repos []string, query string, limit int) (map[string][]Node, error) { results := make(map[string][]Node) @@ -278,7 +278,7 @@ func CrossRepoQuery(repos []string, query string, limit int) (map[string][]Node, } // CrossRepoImpact finds the impact of changing a symbol across multiple repos. -// If a symbol in graycode calls a symbol in graycode-router, this traces that cross-repo dependency. +// If a symbol in hawk calls a symbol in eyrie, this traces that cross-repo dependency. func CrossRepoImpact(repos []string, symbol string, maxDepth int) (map[string]*ImpactResult, error) { results := make(map[string]*ImpactResult) @@ -311,7 +311,7 @@ func CrossRepoImpact(repos []string, symbol string, maxDepth int) (map[string]*I } // FindCrossRepoCalls finds function calls that cross repo boundaries. -// For example, graycode calling graycode-router functions. +// For example, hawk calling eyrie functions. func FindCrossRepoCalls(repos []string) ([]CrossRepoCall, error) { type repoSymbol struct { repo string diff --git a/internal/config/catalog_api.go b/internal/config/catalog_api.go index c1e71a30a..af9f58316 100644 --- a/internal/config/catalog_api.go +++ b/internal/config/catalog_api.go @@ -5,8 +5,8 @@ import ( "sort" "strings" - gw "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - llm "github.com/GrayCodeAI/graycode-router/llm" + llm "github.com/GrayCodeAI/eyrie/llm" + gw "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) type GatewayStatus struct { @@ -22,19 +22,19 @@ type GatewayStatus struct { DNSHost string } -// IsCatalogCacheRequired reports whether an GraycodeRouter operation failed because +// IsCatalogCacheRequired reports whether an Eyrie operation failed because // the local model catalog has not been created yet. func IsCatalogCacheRequired(err error) bool { return gw.IsCatalogCacheRequired(err) } -// RegisteredProviderCount returns GraycodeRouter's canonical first-class provider count. +// RegisteredProviderCount returns Eyrie's canonical first-class provider count. func RegisteredProviderCount() int { return gw.RegisteredProviderCount() } func AllCatalogProviders() []string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil } @@ -57,7 +57,7 @@ func AllCatalogProviders() []string { } func AllSetupGateways() []string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil } @@ -122,7 +122,7 @@ func GatewaySupportsLiveDiscovery(providerID string) bool { } func ActiveGateway(ctx context.Context) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -137,7 +137,7 @@ func ActiveGateway(ctx context.Context) string { } func GatewayStatuses(ctx context.Context, activeProvider, activeModel string) []GatewayStatus { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil } @@ -163,7 +163,7 @@ func GatewayStatuses(ctx context.Context, activeProvider, activeModel string) [] } func GatewayForModel(modelID string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -171,7 +171,7 @@ func GatewayForModel(modelID string) string { } func ShouldClearSelectionAfterCredentialRemove(ctx context.Context, removedProvider string) bool { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return true } @@ -183,7 +183,7 @@ func ShouldClearSelectionAfterCredentialRemove(ctx context.Context, removedProvi } func ClearActiveSelection(ctx context.Context) error { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } @@ -191,7 +191,7 @@ func ClearActiveSelection(ctx context.Context) error { } func SyncSelectionWithCredentials(ctx context.Context) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return } @@ -213,7 +213,7 @@ func SyncSelectionWithCredentials(ctx context.Context) { } func DefaultModelForProvider(provider string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -221,7 +221,7 @@ func DefaultModelForProvider(provider string) string { } func DefaultModelForProviderWithSettings(settings Settings, provider string) string { - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return "" } @@ -248,7 +248,7 @@ func ModelIDsForProvider(provider string) ([]string, error) { } func CheapestModelForProvider(provider, fallback string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return fallback } @@ -256,7 +256,7 @@ func CheapestModelForProvider(provider, fallback string) string { } func ProviderOfModel(modelName string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -264,7 +264,7 @@ func ProviderOfModel(modelName string) string { } func ProviderOfModelWithSettings(settings Settings, modelName string) string { - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return "" } @@ -272,7 +272,7 @@ func ProviderOfModelWithSettings(settings Settings, modelName string) string { } func ExampleModelHints() (string, string) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "an Anthropic model", "an OpenAI model" } @@ -281,7 +281,7 @@ func ExampleModelHints() (string, string) { } func AllCanonicalModelIDs() []string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil } @@ -298,7 +298,7 @@ func AllCanonicalModelIDs() []string { } func ProviderIDForDeployment(deploymentID string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -311,7 +311,7 @@ func ProviderIDForDeployment(deploymentID string) string { } func PrimaryAPIKeyEnvForDeployment(deploymentID string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } @@ -324,7 +324,7 @@ func PrimaryAPIKeyEnvForDeployment(deploymentID string) string { } func engineGateway(providerID string) (gw.GatewayDefs, bool) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return gw.GatewayDefs{}, false } diff --git a/internal/config/catalog_compat_test.go b/internal/config/catalog_compat_test.go index b7f6ab5ae..a7909d1c9 100644 --- a/internal/config/catalog_compat_test.go +++ b/internal/config/catalog_compat_test.go @@ -4,12 +4,12 @@ import ( "context" "strings" - "github.com/GrayCodeAI/graycode-router/catalog" - graycoderouterconfig "github.com/GrayCodeAI/graycode-router/config" + "github.com/GrayCodeAI/eyrie/catalog" + eyrieconfig "github.com/GrayCodeAI/eyrie/config" ) // CompiledCatalogV1 is retained only for lower-level migration/security tests. -// Production Graycode code consumes engine DTOs exclusively. +// Production Hawk code consumes engine DTOs exclusively. func CompiledCatalogV1() *catalog.CompiledCatalog { compiled, err := catalog.LoadCatalog(context.Background(), catalog.LoadCatalogOptions{CachePath: catalog.DefaultCachePath()}) if err == nil && compiled != nil { @@ -20,7 +20,7 @@ func CompiledCatalogV1() *catalog.CompiledCatalog { return compiled } -func deploymentHasSecrets(deployment graycoderouterconfig.DeploymentConfig) bool { +func deploymentHasSecrets(deployment eyrieconfig.DeploymentConfig) bool { return strings.TrimSpace(deployment.APIKey) != "" || strings.TrimSpace(deployment.Token) != "" || strings.TrimSpace(deployment.SecretAccessKey) != "" || strings.TrimSpace(deployment.AccessKeyID) != "" || strings.TrimSpace(deployment.SessionToken) != "" diff --git a/internal/config/catalog_gateways_test.go b/internal/config/catalog_gateways_test.go index 32428a93e..5cf36e7d8 100644 --- a/internal/config/catalog_gateways_test.go +++ b/internal/config/catalog_gateways_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" - "github.com/GrayCodeAI/graycode-router/catalog" - "github.com/GrayCodeAI/graycode-router/credentials" + "github.com/GrayCodeAI/eyrie/catalog" + "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/hawk/internal/catalogtest" ) func TestIsCatalogCacheRequired(t *testing.T) { @@ -30,7 +30,7 @@ func TestAllSetupGateways_RegistryOnly(t *testing.T) { t.Fatalf("owner slug %q should not be a gateway", id) } } - // Required gateways that exist in the published graycode-router v0.1.3. + // Required gateways that exist in the published eyrie v0.1.3. want := map[string]bool{"azure": true, "bedrock": true, "gemini": true, "grok": true, "openrouter": true, "kimi": true, "vertex": true, "xiaomi_mimo_payg": true, "xiaomi_mimo_token_plan": true, "deepseek": true, "minimax_token_plan": true, "minimax_payg": true, "zai_payg": true, "zai_coding": true, "agnes": true} for id := range want { found := false @@ -44,12 +44,12 @@ func TestAllSetupGateways_RegistryOnly(t *testing.T) { t.Fatalf("missing setup gateway %q in %v", id, gws) } } - // Newly-added gateways (groq, poolside) present in the local GraycodeRouter checkout - // but not yet in a published graycode-router release — log if present, don't + // Newly-added gateways (groq, poolside) present in the local Eyrie checkout + // but not yet in a published eyrie release — log if present, don't // require them so the test passes with both GOWORK=on and GOWORK=off. for _, extra := range []string{"groq", "poolside"} { if containsString(gws, extra) { - t.Logf("extra gateway %q present (local graycode-router build)", extra) + t.Logf("extra gateway %q present (local eyrie build)", extra) } } if containsString(gws, "google") || containsString(gws, "xai") { @@ -140,7 +140,7 @@ func TestGatewayDisplayName(t *testing.T) { } } -func TestCachedModelCountForProvider_MatchesGraycodeRouterList(t *testing.T) { +func TestCachedModelCountForProvider_MatchesEyrieList(t *testing.T) { catalogtest.Install(t) compiled := CompiledCatalogV1() for _, gw := range AllSetupGateways() { diff --git a/internal/config/catalog_health.go b/internal/config/catalog_health.go index 3c13c5db3..87e3b5201 100644 --- a/internal/config/catalog_health.go +++ b/internal/config/catalog_health.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/theme" ) var ( @@ -18,7 +18,7 @@ var ( const catalogHealthCacheTTL = 15 * time.Second -// CatalogHealth summarizes the on-disk graycode-router model catalog for doctor / status output. +// CatalogHealth summarizes the on-disk eyrie model catalog for doctor / status output. type CatalogHealth struct { CachePath string `json:"cache_path"` Exists bool `json:"exists"` @@ -33,7 +33,7 @@ type CatalogHealth struct { Error string `json:"error,omitempty"` } -// CatalogHealthReport inspects ~/.graycode-router/model_catalog.json (or GRAYCODE_ROUTER_MODEL_CATALOG_PATH). +// CatalogHealthReport inspects ~/.eyrie/model_catalog.json (or EYRIE_MODEL_CATALOG_PATH). func CatalogHealthReport(ctx context.Context) CatalogHealth { path := CatalogCachePathForDisplay() catalogHealthMu.Lock() @@ -61,7 +61,7 @@ func InvalidateCatalogHealthCache() { } func catalogHealthReportUncached(ctx context.Context) CatalogHealth { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return CatalogHealth{Error: err.Error()} } @@ -73,15 +73,15 @@ func catalogHealthReportUncached(ctx context.Context) CatalogHealth { Source: status.Source, Error: status.Error, } if !h.Exists && h.Error == "" { - h.Error = "cache missing — graycode will discover automatically on start" + h.Error = "cache missing — hawk will discover automatically on start" } return h } -// FormatCatalogHealth returns human-readable catalog status for graycode doctor. +// FormatCatalogHealth returns human-readable catalog status for hawk doctor. func FormatCatalogHealth(h CatalogHealth) string { var b strings.Builder - b.WriteString(theme.Tint("Model catalog (graycode-router):", theme.ReportInfo) + "\n") + b.WriteString(theme.Tint("Model catalog (eyrie):", theme.ReportInfo) + "\n") b.WriteString(" " + theme.Tint("path:", theme.ReportMuted) + " " + theme.Tint(h.CachePath, theme.ReportInfo) + "\n") if h.Error != "" { b.WriteString(" " + theme.Tint("status:", theme.ReportMuted) + " " + theme.Tint(h.Error, theme.ReportError) + "\n") @@ -93,7 +93,7 @@ func FormatCatalogHealth(h CatalogHealth) string { } b.WriteString(" " + theme.Tint("models:", theme.ReportMuted) + " " + theme.Tint(fmt.Sprintf("%d", h.Models), theme.ReportInfo) + " " + theme.Tint("deployments:", theme.ReportMuted) + " " + theme.Tint(fmt.Sprintf("%d", h.Deployments), theme.ReportInfo) + " " + theme.Tint("offerings:", theme.ReportMuted) + " " + theme.Tint(fmt.Sprintf("%d", h.Offerings), theme.ReportInfo) + "\n") if h.Stale { - b.WriteString(" " + theme.Tint("stale:", theme.ReportMuted) + " " + theme.Tint("yes", theme.ReportWarn) + fmt.Sprintf(" (after %s) — graycode refreshes automatically on start\n", h.StaleAfter.UTC().Format(time.RFC3339))) + b.WriteString(" " + theme.Tint("stale:", theme.ReportMuted) + " " + theme.Tint("yes", theme.ReportWarn) + fmt.Sprintf(" (after %s) — hawk refreshes automatically on start\n", h.StaleAfter.UTC().Format(time.RFC3339))) } else if !h.StaleAfter.IsZero() { b.WriteString(" " + theme.Tint("stale:", theme.ReportMuted) + " " + theme.Tint("no", theme.ReportSuccess) + fmt.Sprintf(" (until %s)\n", h.StaleAfter.UTC().Format(time.RFC3339))) } @@ -108,7 +108,7 @@ func CatalogEmptyHint(ctx context.Context) string { if !HasConfiguredDeploymentCached(ctx) { return "run /config to paste an API key or set up Ollama (local, no key)" } - return "check network access, then graycode preflight or /config — graycode refreshes the catalog automatically" + return "check network access, then hawk preflight or /config — hawk refreshes the catalog automatically" } // EnsureCatalogAvailable returns an error when the production catalog cache is missing or empty. @@ -128,7 +128,7 @@ func EnsureCatalogAvailable(ctx context.Context) error { // CatalogCachePathForDisplay returns the path users should care about. func CatalogCachePathForDisplay() string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err == nil { return engine.StatePaths().Catalog } diff --git a/internal/config/catalog_health_test.go b/internal/config/catalog_health_test.go index a6e6215f4..48b638d6f 100644 --- a/internal/config/catalog_health_test.go +++ b/internal/config/catalog_health_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestCatalogEmptyHint_NoCredentials(t *testing.T) { @@ -43,7 +43,7 @@ func TestCatalogEmptyHint_WithCredentials(t *testing.T) { func TestEnsureCatalogAvailable_MissingCache(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", dir+"/missing.json") + t.Setenv("EYRIE_MODEL_CATALOG_PATH", dir+"/missing.json") err := EnsureCatalogAvailable(context.Background()) if err == nil || !strings.Contains(err.Error(), "/config") { @@ -53,7 +53,7 @@ func TestEnsureCatalogAvailable_MissingCache(t *testing.T) { func TestCatalogStatusLine_Empty(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", dir+"/missing.json") + t.Setenv("EYRIE_MODEL_CATALOG_PATH", dir+"/missing.json") line := CatalogStatusLine(context.Background()) if !strings.Contains(line, "missing") && !strings.Contains(line, "empty") { diff --git a/internal/config/catalog_startup.go b/internal/config/catalog_startup.go index f2c3d8856..506521a2e 100644 --- a/internal/config/catalog_startup.go +++ b/internal/config/catalog_startup.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/env" + "github.com/GrayCodeAI/hawk/internal/env" ) type gatewayModelCount struct { @@ -72,13 +72,13 @@ func CatalogStatusLine(ctx context.Context) string { return formatCatalogGatewayStatus("Catalog: ", rows, h.Models) } -// CatalogReady reports whether the graycode-router catalog cache exists and has models. +// CatalogReady reports whether the eyrie catalog cache exists and has models. func CatalogReady(ctx context.Context) bool { h := CatalogHealthReport(ctx) return h.Error == "" && h.Models > 0 && !h.Stale } -// CatalogStartupOptions controls automatic catalog refresh at graycode startup. +// CatalogStartupOptions controls automatic catalog refresh at hawk startup. type CatalogStartupOptions struct { ForceRefresh bool SkipAutoRefresh bool @@ -86,7 +86,7 @@ type CatalogStartupOptions struct { } // PrepareCatalogForSession ensures a usable, fresh catalog before chat/print. -// By default graycode auto-discovers when the cache is missing, empty, or stale. +// By default hawk auto-discovers when the cache is missing, empty, or stale. func PrepareCatalogForSession(ctx context.Context, out io.Writer, opts CatalogStartupOptions) error { h := CatalogHealthReport(ctx) if !catalogNeedsAutoRefresh(h, opts) { @@ -146,7 +146,7 @@ func catalogNeedsAutoRefresh(h CatalogHealth, opts CatalogStartupOptions) bool { return h.Stale } -// AutoRefreshCatalog runs graycode-router discover (remote + live APIs when keys are set). +// AutoRefreshCatalog runs eyrie discover (remote + live APIs when keys are set). func AutoRefreshCatalog(ctx context.Context, out io.Writer, verbose bool) error { if out != nil { if verbose { @@ -180,12 +180,12 @@ func AutoRefreshCatalog(ctx context.Context, out io.Writer, verbose bool) error // TryAutoRefreshCatalog refreshes once when the cache cannot be read (e.g. mid-session). func TryAutoRefreshCatalog(ctx context.Context) error { if !autoRefreshCatalogEnabled() { - return fmt.Errorf("automatic catalog refresh is disabled (GRAYCODE_AUTO_REFRESH_CATALOG=0)") + return fmt.Errorf("automatic catalog refresh is disabled (HAWK_AUTO_REFRESH_CATALOG=0)") } return AutoRefreshCatalog(ctx, nil, false) } -// RefreshCatalogAfterCredentials runs graycode-router discover after /config saves API keys. +// RefreshCatalogAfterCredentials runs eyrie discover after /config saves API keys. func RefreshCatalogAfterCredentials(ctx context.Context, out io.Writer) error { if !autoRefreshCatalogEnabled() { return nil @@ -209,7 +209,7 @@ func StartupCatalogPrefetch(ctx context.Context) { }() } -// DiscoverCatalogAfterSetup runs during optional graycode setup after API keys are saved. +// DiscoverCatalogAfterSetup runs during optional hawk setup after API keys are saved. func DiscoverCatalogAfterSetup(ctx context.Context, out io.Writer) { if out == nil { out = os.Stdout @@ -225,11 +225,11 @@ func catalogRefreshFailureHint(ctx context.Context) string { if !HasConfiguredDeployment(ctx) { return "No API keys in " + CredentialStoreName() + ". Run /config to paste a key or set up Ollama." } - return "Check network access and stored keys (" + CredentialStoreName() + "). Run graycode preflight or /config." + return "Check network access and stored keys (" + CredentialStoreName() + "). Run hawk preflight or /config." } func autoRefreshCatalogEnabled() bool { - switch strings.ToLower(strings.TrimSpace(env.Getenv("GRAYCODE_AUTO_REFRESH_CATALOG"))) { + switch strings.ToLower(strings.TrimSpace(env.Getenv("HAWK_AUTO_REFRESH_CATALOG"))) { case "0", "false", "no", "off": return false default: @@ -238,7 +238,7 @@ func autoRefreshCatalogEnabled() bool { } func catalogRefreshAlways() bool { - switch strings.ToLower(strings.TrimSpace(env.Getenv("GRAYCODE_CATALOG_REFRESH_ALWAYS"))) { + switch strings.ToLower(strings.TrimSpace(env.Getenv("HAWK_CATALOG_REFRESH_ALWAYS"))) { case "1", "true", "yes", "on": return true default: diff --git a/internal/config/catalog_startup_robust_test.go b/internal/config/catalog_startup_robust_test.go index 3cb8e3f5a..fa3863c38 100644 --- a/internal/config/catalog_startup_robust_test.go +++ b/internal/config/catalog_startup_robust_test.go @@ -6,16 +6,16 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" - graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/hawk/internal/catalogtest" + hawkconfig "github.com/GrayCodeAI/hawk/internal/config" ) func TestPrepareCatalogForSession_StaleCacheRefreshFailureContinues(t *testing.T) { catalogtest.Install(t) // Force stale so refresh is attempted; remote may fail offline — should not block if cache has models. - h := graycodeconfig.CatalogHealthReport(context.Background()) + h := hawkconfig.CatalogHealthReport(context.Background()) var buf bytes.Buffer - err := graycodeconfig.PrepareCatalogForSession(context.Background(), &buf, graycodeconfig.CatalogStartupOptions{ + err := hawkconfig.PrepareCatalogForSession(context.Background(), &buf, hawkconfig.CatalogStartupOptions{ ForceRefresh: true, }) // With ForceRefresh, remote may fail; if we had models before, we tolerate failure. @@ -27,8 +27,8 @@ func TestPrepareCatalogForSession_StaleCacheRefreshFailureContinues(t *testing.T func TestCatalogCachePathForDisplay_RespectsEnv(t *testing.T) { custom := filepath.Join(t.TempDir(), "custom.json") - t.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", custom) - if got := graycodeconfig.CatalogCachePathForDisplay(); got != custom { + t.Setenv("EYRIE_MODEL_CATALOG_PATH", custom) + if got := hawkconfig.CatalogCachePathForDisplay(); got != custom { t.Fatalf("path = %q want %q", got, custom) } } diff --git a/internal/config/catalog_startup_test.go b/internal/config/catalog_startup_test.go index 40ce0af78..6c98d153d 100644 --- a/internal/config/catalog_startup_test.go +++ b/internal/config/catalog_startup_test.go @@ -7,13 +7,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" + "github.com/GrayCodeAI/hawk/internal/catalogtest" ) func TestCatalogReady_MissingCache(t *testing.T) { InvalidateCatalogHealthCache() dir := t.TempDir() - t.Setenv("GRAYCODE_ROUTER_MODEL_CATALOG_PATH", filepath.Join(dir, "missing.json")) + t.Setenv("EYRIE_MODEL_CATALOG_PATH", filepath.Join(dir, "missing.json")) if CatalogReady(context.Background()) { t.Fatal("expected not ready without cache") } @@ -50,11 +50,11 @@ func TestCatalogNeedsAutoRefresh_Fresh(t *testing.T) { } func TestAutoRefreshCatalogEnabled(t *testing.T) { - t.Setenv("GRAYCODE_AUTO_REFRESH_CATALOG", "false") + t.Setenv("HAWK_AUTO_REFRESH_CATALOG", "false") if autoRefreshCatalogEnabled() { t.Fatal("expected disabled") } - t.Setenv("GRAYCODE_AUTO_REFRESH_CATALOG", "") + t.Setenv("HAWK_AUTO_REFRESH_CATALOG", "") if !autoRefreshCatalogEnabled() { t.Fatal("expected enabled by default") } diff --git a/internal/config/config.go b/internal/config/config.go index 630447282..31f26f557 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" ) // LoadAgentsMD reads AGENTS.md from the current directory or parents. diff --git a/internal/config/config_test.go b/internal/config/config_test.go index af036f884..6b56328d3 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestLoadAgentsMD(t *testing.T) { @@ -169,8 +169,8 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) configDir := filepath.Join(home, "config") - t.Setenv("GRAYCODE_CONFIG_DIR", configDir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) + t.Setenv("HAWK_CONFIG_DIR", configDir) + t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatal(err) } @@ -180,10 +180,10 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { settings := LoadSettings() if got := ActiveModel(context.TODO()); got != "openai/gpt-4o" { - t.Fatalf("expected global model in graycode-router, got %q (settings.model=%q)", got, settings.Model) + t.Fatalf("expected global model in eyrie, got %q (settings.model=%q)", got, settings.Model) } if settings.Model != "" { - t.Fatalf("model must not remain in graycode settings.json, got %q", settings.Model) + t.Fatalf("model must not remain in hawk settings.json, got %q", settings.Model) } if len(settings.AllowedTools) != 1 || settings.AllowedTools[0] != "Read" { t.Fatalf("expected global allowedTools, got %v", settings.AllowedTools) @@ -207,7 +207,7 @@ func TestSetGlobalSettingAndSettingValue(t *testing.T) { if err := SetGlobalSetting("maxBudgetUSD", "2.5"); err != nil { t.Fatal(err) } - // Graycode: API keys rejected from settings file + // Hawk: API keys rejected from settings file if err := SetGlobalSetting("apiKey.openai", "sk-test"); err == nil { t.Fatal("expected error setting api key in settings") } @@ -242,8 +242,8 @@ func TestLoadSettingsPreservesRejectedLegacySelection(t *testing.T) { home := t.TempDir() configDir := filepath.Join(home, "config") t.Setenv("HOME", home) - t.Setenv("GRAYCODE_CONFIG_DIR", configDir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", filepath.Join(home, "graycode-router")) + t.Setenv("HAWK_CONFIG_DIR", configDir) + t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) if err := os.MkdirAll(configDir, 0o700); err != nil { t.Fatal(err) } @@ -264,7 +264,7 @@ func TestLoadSettingsPreservesRejectedLegacySelection(t *testing.T) { t.Fatalf("rejected legacy selection was removed from disk: %s", data) } if got := ActiveModel(context.Background()); got != "" { - t.Fatalf("rejected legacy model reached GraycodeRouter state: %q", got) + t.Fatalf("rejected legacy model reached Eyrie state: %q", got) } } diff --git a/internal/config/credentials_store.go b/internal/config/credentials_store.go index f17d3f1d7..c314cce89 100644 --- a/internal/config/credentials_store.go +++ b/internal/config/credentials_store.go @@ -5,18 +5,18 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/theme" ) -// PersistAPIKey saves a provider API key via graycode-router (OS secret store). +// PersistAPIKey saves a provider API key via eyrie (OS secret store). func PersistAPIKey(ctx context.Context, envKey, secret string) error { secret = strings.TrimSpace(secret) envKey = strings.TrimSpace(envKey) if secret == "" || envKey == "" { return nil } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } @@ -27,7 +27,7 @@ func PersistAPIKey(ctx context.Context, envKey, secret string) error { return nil } -// CredentialInference is one graycode-router provider match for a pasted API key. +// CredentialInference is one eyrie provider match for a pasted API key. type CredentialInference struct { ProviderID string DeploymentID string @@ -35,7 +35,7 @@ type CredentialInference struct { DisplayName string } -// CredentialProviderOption is one graycode-router provider row for /config pickers. +// CredentialProviderOption is one eyrie provider row for /config pickers. type CredentialProviderOption struct { ProviderID string DeploymentID string @@ -46,7 +46,7 @@ type CredentialProviderOption struct { Rank int } -// CredentialResolveResult is graycode-router paste-key resolution (format check + full provider list; no prefix inference). +// CredentialResolveResult is eyrie paste-key resolution (format check + full provider list; no prefix inference). type CredentialResolveResult struct { FormatOK bool FormatError string @@ -54,9 +54,9 @@ type CredentialResolveResult struct { ProbeDisambiguationUsed bool } -// ResolveCredential validates format and lists all providers from graycode-router registry. +// ResolveCredential validates format and lists all providers from eyrie registry. func ResolveCredential(ctx context.Context, secret string) CredentialResolveResult { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return CredentialResolveResult{FormatError: err.Error()} } @@ -91,9 +91,9 @@ func InferenceFromOption(opt CredentialProviderOption) CredentialInference { } } -// SaveCredential validates, probes, and stores via graycode-router keychain. +// SaveCredential validates, probes, and stores via eyrie keychain. func SaveCredential(ctx context.Context, inference CredentialInference, secret string) error { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } @@ -106,7 +106,7 @@ func SaveCredential(ctx context.Context, inference CredentialInference, secret s // HasStoredCredentialForProvider reports whether the OS secret store has a key for this gateway. func HasStoredCredentialForProvider(ctx context.Context, providerID string) bool { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return false } @@ -117,7 +117,7 @@ func HasStoredCredentialForProvider(ctx context.Context, providerID string) bool // CredentialEnvironmentConflict reports a safe environment-vs-keychain // mismatch for a provider. It returns identifiers only, never secret values. func CredentialEnvironmentConflict(ctx context.Context, providerID string) (envVar string, conflict bool) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "", false } @@ -137,7 +137,7 @@ func ConfiguredCredentialProviders() []string { return configuredCredentialProvidersCached(context.Background()) } -// FormatCredentialCLIStatus returns graycode credentials status output (providers, not raw env names). +// FormatCredentialCLIStatus returns hawk credentials status output (providers, not raw env names). func FormatCredentialCLIStatus(ctx context.Context) string { if ctx == nil { ctx = context.Background() @@ -165,7 +165,7 @@ func RemoveStoredCredential(ctx context.Context, target string) ([]string, error if target == "" { return nil, fmt.Errorf("provider or env var name required") } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil, err } @@ -189,7 +189,7 @@ func MaskCredentialForProvider(ctx context.Context, provider string) string { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err == nil { status, statusErr := engine.CredentialStatus(ctx, provider) if statusErr == nil && status.Masked != "" { @@ -214,7 +214,7 @@ func maskCredentialSecret(secret string) string { // CredentialInferenceForProvider returns save metadata for a gateway chosen in /config. func CredentialInferenceForProvider(providerID string) (CredentialInference, error) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return CredentialInference{}, err } @@ -235,7 +235,7 @@ func LocalCredentialInference(providerID string) (CredentialInference, error) { return CredentialInferenceForProvider(providerID) } -// FormatConfigProviderError maps graycode-router setup errors to user-facing /config hints. +// FormatConfigProviderError maps eyrie setup errors to user-facing /config hints. func FormatConfigProviderError(providerID string, err error) string { if err == nil { return "" diff --git a/internal/config/credentials_store_test.go b/internal/config/credentials_store_test.go index 2f1067b98..d13b5b70c 100644 --- a/internal/config/credentials_store_test.go +++ b/internal/config/credentials_store_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestRemoveStoredCredential_ByProvider(t *testing.T) { diff --git a/internal/config/deployment.go b/internal/config/deployment.go index 2fa470ad5..2767002b6 100644 --- a/internal/config/deployment.go +++ b/internal/config/deployment.go @@ -1,7 +1,7 @@ package config -// DeploymentRoutingEnabled delegates deployment-routing policy ownership to GraycodeRouter runtime. +// DeploymentRoutingEnabled delegates deployment-routing policy ownership to Eyrie runtime. func DeploymentRoutingEnabled(s Settings) bool { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() return err == nil && engine.DeploymentRoutingEnabled(s.DeploymentRouting) } diff --git a/internal/config/deployment_status.go b/internal/config/deployment_status.go index a4c6a4eb4..620ae7bb9 100644 --- a/internal/config/deployment_status.go +++ b/internal/config/deployment_status.go @@ -10,9 +10,9 @@ func ResolveCanonicalModel(model string) string { return CanonicalModelID(context.Background(), strings.TrimSpace(model)) } -// DeploymentStatusReport returns graycode deployment routing diagnostics. +// DeploymentStatusReport returns hawk deployment routing diagnostics. func DeploymentStatusReport(ctx context.Context, activeModel string) (string, error) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "", err } @@ -20,16 +20,16 @@ func DeploymentStatusReport(ctx context.Context, activeModel string) (string, er } func DeploymentStatusReportWithSettings(ctx context.Context, settings Settings, activeModel string) (string, error) { - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return "", err } return engine.DeploymentStatus(ctx, activeModel) } -// RoutingPreviewJSON returns effective routing for a model (graycode-router routing JSON preview). +// RoutingPreviewJSON returns effective routing for a model (eyrie routing JSON preview). func RoutingPreviewJSON(ctx context.Context, model string) (string, error) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "", err } @@ -37,7 +37,7 @@ func RoutingPreviewJSON(ctx context.Context, model string) (string, error) { } func RoutingPreviewJSONWithSettings(ctx context.Context, settings Settings, model string) (string, error) { - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/deployments_ui_test.go b/internal/config/deployments_ui_test.go index c62f7b37b..77338e48c 100644 --- a/internal/config/deployments_ui_test.go +++ b/internal/config/deployments_ui_test.go @@ -3,7 +3,7 @@ package config import "testing" func TestDeploymentRoutingLabel(t *testing.T) { - t.Setenv("GRAYCODE_DEPLOYMENT_ROUTING", "") + t.Setenv("HAWK_DEPLOYMENT_ROUTING", "") enabled := true if DeploymentRoutingLabel(Settings{DeploymentRouting: &enabled}) != "on" { t.Fatal("expected on") diff --git a/internal/config/developer_path.go b/internal/config/developer_path.go index 7e59ffb9c..f05931ddd 100644 --- a/internal/config/developer_path.go +++ b/internal/config/developer_path.go @@ -8,15 +8,15 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/home" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/theme" - "github.com/GrayCodeAI/graycode-cli/internal/token" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/home" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/theme" + "github.com/GrayCodeAI/hawk/internal/token" + "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // PathCheckStatus is pass, warn, or fail for one readiness row. @@ -64,7 +64,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Setup", Name: "credentials", Status: PathFail, Detail: "No provider credentials configured", - FixHint: "Run graycode and /config to paste an API key (or configure Ollama)", + FixHint: "Run hawk and /config to paste an API key (or configure Ollama)", Blocking: true, }) } @@ -95,13 +95,13 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Setup", Name: "catalog", Status: PathWarn, Detail: "Catalog file present but empty", - FixHint: "Run graycode models refresh after adding credentials", + FixHint: "Run hawk models refresh after adding credentials", }) default: checks = append(checks, PathCheck{ Section: "Setup", Name: "catalog", Status: PathWarn, Detail: CatalogEmptyHint(ctx), - FixHint: "Add credentials then run graycode models refresh", + FixHint: "Add credentials then run hawk models refresh", }) } @@ -125,7 +125,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Security", Name: "provider.json", Status: PathFail, Detail: detail, - FixHint: "Run graycode start once (MigrateProviderSecrets) or remove secret fields manually", + FixHint: "Run hawk start once (MigrateProviderSecrets) or remove secret fields manually", Blocking: true, }) } else { @@ -140,20 +140,20 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Security", Name: "plaintext env", Status: PathWarn, Detail: "Plaintext credential files: " + strings.Join(paths, ", "), - FixHint: "Run graycode credentials migrate", + FixHint: "Run hawk credentials migrate", }) } else { checks = append(checks, PathCheck{ Section: "Security", Name: "plaintext env", Status: PathPass, - Detail: "No ~/.graycode/env or ~/.graycode/.env files", + Detail: "No ~/.hawk/env or ~/.hawk/.env files", Blocking: true, }) } - graycodeDir := home.MustDir() + hawkDir := home.MustDir() provPath := ProviderStateSecurityStatus().Path if provPath == "" { - provPath = filepath.Join(graycodeDir, ".graycode", "provider.json") + provPath = filepath.Join(hawkDir, ".hawk", "provider.json") } if reason := tool.IsSensitivePath(provPath); reason != "" { checks = append(checks, PathCheck{ @@ -194,7 +194,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { pre := EnginePreflightReport(ctx) if pre.Ready { checks = append(checks, PathCheck{ - Section: "Ecosystem", Name: "graycode-router", Status: PathPass, + Section: "Ecosystem", Name: "eyrie", Status: PathPass, Detail: "Preflight ready to chat", Blocking: true, }) @@ -207,8 +207,8 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { } } checks = append(checks, PathCheck{ - Section: "Ecosystem", Name: "graycode-router", Status: status, - Detail: "Preflight not ready — see graycode preflight", + Section: "Ecosystem", Name: "eyrie", Status: status, + Detail: "Preflight not ready — see hawk preflight", FixHint: "Complete /config (credentials + model)", Blocking: true, }) @@ -229,7 +229,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { }) } - sample := token.CountTokensFast("graycode developer path readiness") + sample := token.CountTokensFast("hawk developer path readiness") checks = append(checks, PathCheck{ Section: "Ecosystem", Name: "shrike", Status: PathPass, Detail: fmt.Sprintf("Embedded token/compress pipeline OK (sample=%d tokens)", sample), @@ -254,10 +254,10 @@ func anyBlockingFail(checks []PathCheck, section string) bool { func developerPathNextStep(r DeveloperPathReport, setup SetupState) string { if r.Ready { - return "Ready — run graycode and start chatting" + return "Ready — run hawk and start chatting" } if !setup.HasCredentials { - return "Run graycode → /config → paste API key (or Ollama local)" + return "Run hawk → /config → paste API key (or Ollama local)" } if !setup.HasModel { return "Run /config → pick a model from the catalog" @@ -265,7 +265,7 @@ func developerPathNextStep(r DeveloperPathReport, setup SetupState) string { if !r.SecureReady { return "Fix security items above (provider.json secrets, read guard)" } - return "Run graycode preflight for details, then /config if needed" + return "Run hawk preflight for details, then /config if needed" } // pathStatusColor maps a readiness status to a semantic report color. @@ -286,7 +286,7 @@ func pathStatusColor(s PathCheckStatus) color.Color { func FormatDeveloperPathReport(ctx context.Context) string { r := EvaluateDeveloperPath(ctx) var b strings.Builder - b.WriteString(theme.Tint("Developer path (graycode · graycode-router · shrike · harrier)", theme.ReportInfo) + "\n\n") + b.WriteString(theme.Tint("Developer path (hawk · eyrie · shrike · harrier)", theme.ReportInfo) + "\n\n") status := "NEEDS SETUP" statusColor := theme.ReportWarn @@ -322,7 +322,7 @@ func FormatDeveloperPathReport(ctx context.Context) string { } b.WriteString(theme.Tint("Next:", theme.ReportMuted) + " " + r.NextStep + "\n") - b.WriteString("\n" + theme.Tint("Docs: docs/DEVELOPER-PATH.md · docs/SECURITY-DEVELOPER.md · graycode doctor · graycode preflight", theme.ReportMuted) + "\n") + b.WriteString("\n" + theme.Tint("Docs: docs/DEVELOPER-PATH.md · docs/SECURITY-DEVELOPER.md · hawk doctor · hawk preflight", theme.ReportMuted) + "\n") return strings.TrimRight(b.String(), "\n") } @@ -348,12 +348,12 @@ func providerJSONHasSecretsOnDisk() (bool, string) { } func plaintextCredentialFilesPresent() (bool, []string) { - graycodeDir := filepath.Join(home.MustDir(), ".graycode") + hawkDir := filepath.Join(home.MustDir(), ".hawk") var paths []string for _, name := range []string{"env", ".env"} { - p := filepath.Join(graycodeDir, name) + p := filepath.Join(hawkDir, name) if _, err := os.Stat(p); err == nil { - paths = append(paths, "~/.graycode/"+name) + paths = append(paths, "~/.hawk/"+name) } } return len(paths) > 0, paths diff --git a/internal/config/developer_path_test.go b/internal/config/developer_path_test.go index ad300a140..a84c48c6d 100644 --- a/internal/config/developer_path_test.go +++ b/internal/config/developer_path_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestEvaluateDeveloperPath_FreshInstall(t *testing.T) { diff --git a/internal/config/ecosystem_report.go b/internal/config/ecosystem_report.go index 6b7fab747..b8e95f322 100644 --- a/internal/config/ecosystem_report.go +++ b/internal/config/ecosystem_report.go @@ -5,19 +5,19 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/theme" - "github.com/GrayCodeAI/graycode-cli/internal/token" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/theme" + "github.com/GrayCodeAI/hawk/internal/token" ) // EcosystemReport is the structured view of the ecosystem panel. type EcosystemReport struct { - Eyre EcosystemGraycodeRouter `json:"graycode-router"` - Harrier EcosystemHarrier `json:"harrier"` - Shrike EcosystemShrike `json:"shrike"` + Eyre EcosystemEyrie `json:"eyrie"` + Harrier EcosystemHarrier `json:"harrier"` + Shrike EcosystemShrike `json:"shrike"` } -type EcosystemGraycodeRouter struct { +type EcosystemEyrie struct { CatalogExists bool `json:"catalog_exists"` ModelCount int `json:"model_count,omitempty"` Ready bool `json:"ready"` @@ -40,7 +40,7 @@ type EcosystemShrike struct { func BuildEcosystemReport(ctx context.Context, provider, model string) EcosystemReport { var r EcosystemReport - // graycode-router + // eyrie cat := CatalogHealthReport(ctx) r.Eyre.CatalogExists = cat.Exists r.Eyre.ModelCount = cat.Models @@ -64,41 +64,41 @@ func BuildEcosystemReport(ctx context.Context, provider, model string) Ecosystem // shrike r.Shrike.Embedded = token.ShrikeAvailable() - r.Shrike.SampleTokens = token.CountTokensFast("graycode context compression pipeline") + r.Shrike.SampleTokens = token.CountTokensFast("hawk context compression pipeline") return r } -// FormatEcosystemPanel summarizes graycode-router, harrier, and shrike integration for doctor and status output. +// FormatEcosystemPanel summarizes eyrie, harrier, and shrike integration for doctor and status output. func FormatEcosystemPanel(ctx context.Context, provider, model string) string { var b strings.Builder - b.WriteString(theme.Tint("Ecosystem (graycode-router · harrier · shrike):", theme.ReportInfo) + "\n") + b.WriteString(theme.Tint("Ecosystem (eyrie · harrier · shrike):", theme.ReportInfo) + "\n") - // graycode-router — LLM provider layer + // eyrie — LLM provider layer cat := CatalogHealthReport(ctx) - graycodeRouterLine := " " + theme.Tint("graycode-router:", theme.ReportMuted) + " " + eyrieLine := " " + theme.Tint("eyrie:", theme.ReportMuted) + " " if cat.Exists { - graycodeRouterLine += theme.Tint(fmt.Sprintf("catalog %d models", cat.Models), theme.ReportInfo) + eyrieLine += theme.Tint(fmt.Sprintf("catalog %d models", cat.Models), theme.ReportInfo) } else { - graycodeRouterLine += theme.Tint("catalog missing (run graycode models refresh)", theme.ReportWarn) + eyrieLine += theme.Tint("catalog missing (run hawk models refresh)", theme.ReportWarn) } pre := EnginePreflightReport(ctx) if pre.Ready { - graycodeRouterLine += " · " + theme.Tint("locally ready", theme.ReportSuccess) + eyrieLine += " · " + theme.Tint("locally ready", theme.ReportSuccess) } else { - graycodeRouterLine += " · " + theme.Tint("setup incomplete", theme.ReportWarn) + eyrieLine += " · " + theme.Tint("setup incomplete", theme.ReportWarn) } if strings.TrimSpace(provider) != "" && provider != "auto" { - graycodeRouterLine += " · " + theme.Tint("provider "+provider, theme.ReportInfo) + eyrieLine += " · " + theme.Tint("provider "+provider, theme.ReportInfo) } if dep, err := EngineDeploymentSummary(ctx, model); err == nil { if dep.RoutingStages > 0 { - graycodeRouterLine += " · " + theme.Tint(fmt.Sprintf("routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages), theme.ReportInfo) + eyrieLine += " · " + theme.Tint(fmt.Sprintf("routing %s (%d stages)", dep.RoutingSource, dep.RoutingStages), theme.ReportInfo) } else { - graycodeRouterLine += " · " + theme.Tint("routing "+dep.RoutingSource, theme.ReportInfo) + eyrieLine += " · " + theme.Tint("routing "+dep.RoutingSource, theme.ReportInfo) } } - b.WriteString(graycodeRouterLine + "\n") + b.WriteString(eyrieLine + "\n") // harrier — persistent memory graph bridge := memory.NewHarrierBridge() @@ -111,7 +111,7 @@ func FormatEcosystemPanel(ctx context.Context, provider, model string) string { // shrike — token counting and context compression (embedded only when a // real shrike engine is linked; the build-harness stub reports 0 tokens). - sample := token.CountTokensFast("graycode context compression pipeline") + sample := token.CountTokensFast("hawk context compression pipeline") if token.ShrikeAvailable() { b.WriteString(" " + theme.Tint("shrike:", theme.ReportMuted) + " " + theme.Tint("embedded", theme.ReportInfo) + " · " + theme.Tint("token/compress pipeline OK", theme.ReportSuccess) + fmt.Sprintf(" (sample=%d tokens)", sample) + "\n") } else { diff --git a/internal/config/ecosystem_report_test.go b/internal/config/ecosystem_report_test.go index 2455a491c..09e8a6d1a 100644 --- a/internal/config/ecosystem_report_test.go +++ b/internal/config/ecosystem_report_test.go @@ -10,8 +10,8 @@ func TestFormatEcosystemPanel(t *testing.T) { t.Parallel() out := FormatEcosystemPanel(context.Background(), "anthropic", "claude-sonnet-4-20250514") for _, want := range []string{ - "Ecosystem (graycode-router · harrier · shrike):", - "graycode-router:", + "Ecosystem (eyrie · harrier · shrike):", + "eyrie:", "harrier:", "shrike:", } { diff --git a/internal/config/envmanager.go b/internal/config/envmanager.go index 981ae83d2..ce3290a7b 100644 --- a/internal/config/envmanager.go +++ b/internal/config/envmanager.go @@ -11,7 +11,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/env" + "github.com/GrayCodeAI/hawk/internal/env" ) // EnvVar represents a single environment variable with metadata. diff --git a/internal/config/envmanager_test.go b/internal/config/envmanager_test.go index fe3c9fc4c..3749c5196 100644 --- a/internal/config/envmanager_test.go +++ b/internal/config/envmanager_test.go @@ -174,8 +174,8 @@ func TestListForDisplay(t *testing.T) { Source: "env", Secret: true, } - em.Vars["GRAYCODE_MODEL"] = &EnvVar{ - Key: "GRAYCODE_MODEL", + em.Vars["HAWK_MODEL"] = &EnvVar{ + Key: "HAWK_MODEL", Value: "claude-sonnet-4-6", Source: ".env.local", Secret: false, @@ -195,8 +195,8 @@ func TestListForDisplay(t *testing.T) { if !strings.Contains(output, "from: env") { t.Fatal("output should show source") } - if !strings.Contains(output, "GRAYCODE_MODEL") { - t.Fatal("output should contain GRAYCODE_MODEL") + if !strings.Contains(output, "HAWK_MODEL") { + t.Fatal("output should contain HAWK_MODEL") } if !strings.Contains(output, "claude-sonnet-4-6") { t.Fatal("output should show non-secret value in full") diff --git a/internal/config/graycode_router_apply.go b/internal/config/eyrie_apply.go similarity index 69% rename from internal/config/graycode_router_apply.go rename to internal/config/eyrie_apply.go index f0ac5e5fc..6ed88efcf 100644 --- a/internal/config/graycode_router_apply.go +++ b/internal/config/eyrie_apply.go @@ -5,22 +5,22 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) -// ApplyCredentialsResult is Graycode's UI-safe view of an GraycodeRouter catalog/routing -// application. It intentionally excludes GraycodeRouter setup/config implementation +// ApplyCredentialsResult is Hawk's UI-safe view of an Eyrie catalog/routing +// application. It intentionally excludes Eyrie setup/config implementation // types from the product boundary. type ApplyCredentialsResult struct { Catalog gateway.CatalogSnapshot } -// ApplyGraycodeRouterCredentialsForProvider refreshes live models and writes sanitized +// ApplyEyrieCredentialsForProvider refreshes live models and writes sanitized // deployment routing after /config saves a key. -func ApplyGraycodeRouterCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { +func ApplyEyrieCredentialsForProvider(ctx context.Context, providerID string) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil, err } @@ -32,12 +32,12 @@ func ApplyGraycodeRouterCredentialsForProvider(ctx context.Context, providerID s return &ApplyCredentialsResult{Catalog: snapshot}, nil } -// ApplyGraycodeRouterCredentials refreshes all configured providers and writes +// ApplyEyrieCredentials refreshes all configured providers and writes // sanitized deployment routing. -func ApplyGraycodeRouterCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { +func ApplyEyrieCredentials(ctx context.Context) (*ApplyCredentialsResult, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return nil, err } @@ -53,7 +53,7 @@ func ApplyGraycodeRouterCredentials(ctx context.Context) (*ApplyCredentialsResul func RefreshGatewayCatalog(ctx context.Context, providerID string) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "", err } diff --git a/internal/config/graycode_router_engine.go b/internal/config/eyrie_engine.go similarity index 79% rename from internal/config/graycode_router_engine.go rename to internal/config/eyrie_engine.go index 96ca9ecbc..c480e9387 100644 --- a/internal/config/graycode_router_engine.go +++ b/internal/config/eyrie_engine.go @@ -3,7 +3,7 @@ package config import ( "context" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) type ( @@ -12,16 +12,16 @@ type ( EnginePreflightOptions = gateway.PreflightOptions ) -func newGraycodeRouterEngine() (*gateway.Gateway, error) { +func newEyrieEngine() (*gateway.Gateway, error) { return gateway.New(context.Background(), globalCustomProviders()) } -func NewGraycodeRouterEngine() (*gateway.Gateway, error) { return newGraycodeRouterEngine() } +func NewEyrieEngine() (*gateway.Gateway, error) { return newEyrieEngine() } -// NewGraycodeRouterEngineForSettings composes a fresh gateway for one effective -// Graycode settings snapshot. It performs no package-global registration and does +// NewEyrieEngineForSettings composes a fresh gateway for one effective +// Hawk settings snapshot. It performs no package-global registration and does // not mutate provider environment variables. -func NewGraycodeRouterEngineForSettings(settings Settings) (*gateway.Gateway, error) { +func NewEyrieEngineForSettings(settings Settings) (*gateway.Gateway, error) { return gateway.New(context.Background(), gatewayCustomGateways(settings.CustomProviders)) } @@ -84,7 +84,7 @@ func FormatEnginePreflight(report EnginePreflight) string { } func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return "", false } @@ -92,7 +92,7 @@ func EngineGatewayRegion(ctx context.Context, providerID string) (string, bool) } func SetEngineGatewayRegion(ctx context.Context, providerID, region string) error { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return err } @@ -100,7 +100,7 @@ func SetEngineGatewayRegion(ctx context.Context, providerID, region string) erro } func CanonicalModelID(ctx context.Context, modelID string) string { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return modelID } @@ -108,7 +108,7 @@ func CanonicalModelID(ctx context.Context, modelID string) string { } func HasCredentialEnv(ctx context.Context, envVar string) bool { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() return err == nil && gw.HasCredentialEnv(ctx, envVar) } @@ -117,15 +117,15 @@ func CredentialGuidance(providerID, secret string) string { } func ProviderStateSecurityStatus() gateway.ProviderStateSecurity { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { - return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "GraycodeRouter engine initialization failed"} + return gateway.ProviderStateSecurity{Error: err.Error(), Detail: "Eyrie engine initialization failed"} } return gw.ProviderStateSecurityStatus() } func MigrateEngineProviderSecrets() error { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return err } @@ -133,22 +133,22 @@ func MigrateEngineProviderSecrets() error { } func EngineDeploymentSummary(ctx context.Context, model string) (gateway.DeploymentSummary, error) { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return gateway.DeploymentSummary{}, err } return gw.DeploymentSummary(ctx, model) } -// newGraycodeRouterEngine is Graycode's default composition root for GraycodeRouter's stable host +// newEyrieEngine is Hawk's default composition root for Eyrie's stable host // facade. Command paths that support --settings must use -// NewGraycodeRouterEngineForSettings instead of relying on this global-settings default. +// NewEyrieEngineForSettings instead of relying on this global-settings default. -// ListEngineModels returns model-picker rows through GraycodeRouter's stable facade. +// ListEngineModels returns model-picker rows through Eyrie's stable facade. // EngineModel is an alias of gateway.ModelInfo, so model lists returned by the // gateway pass through without conversion. func ListEngineModels(ctx context.Context, providerID string, refresh bool) ([]EngineModel, error) { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return nil, err } @@ -156,7 +156,7 @@ func ListEngineModels(ctx context.Context, providerID string, refresh bool) ([]E } func ListEngineModelsWithSettings(ctx context.Context, settings Settings, providerID string, refresh bool) ([]EngineModel, error) { - gw, err := NewGraycodeRouterEngineForSettings(settings) + gw, err := NewEyrieEngineForSettings(settings) if err != nil { return nil, err } @@ -164,7 +164,7 @@ func ListEngineModelsWithSettings(ctx context.Context, settings Settings, provid } func ListLiveEngineModelsWithSettings(ctx context.Context, settings Settings, providerID string) ([]EngineModel, error) { - gw, err := NewGraycodeRouterEngineForSettings(settings) + gw, err := NewEyrieEngineForSettings(settings) if err != nil { return nil, err } @@ -172,7 +172,7 @@ func ListLiveEngineModelsWithSettings(ctx context.Context, settings Settings, pr } func ListPublicEngineModels(ctx context.Context, providerID string) ([]EngineModel, error) { - gw, err := newGraycodeRouterEngine() + gw, err := newEyrieEngine() if err != nil { return nil, err } diff --git a/internal/config/graycode_router_engine_test.go b/internal/config/eyrie_engine_test.go similarity index 83% rename from internal/config/graycode_router_engine_test.go rename to internal/config/eyrie_engine_test.go index e64de1011..4d7c79b7e 100644 --- a/internal/config/graycode_router_engine_test.go +++ b/internal/config/eyrie_engine_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) -func TestNewGraycodeRouterEngineForSettingsIsInvocationScoped(t *testing.T) { +func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { previousStore := gateway.DefaultStore() store := &gateway.MapStore{} gateway.SetDefaultStore(store) @@ -17,7 +17,7 @@ func TestNewGraycodeRouterEngineForSettingsIsInvocationScoped(t *testing.T) { Name: "private-gateway", BaseURL: "https://private.example.test/v1", APIKeyEnv: "PRIVATE_GATEWAY_API_KEY", Model: "private/model-v1", }}} - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { t.Fatal(err) } @@ -44,7 +44,7 @@ func TestNewGraycodeRouterEngineForSettingsIsInvocationScoped(t *testing.T) { t.Fatalf("custom credential surface = %+v, err=%v", status, err) } - if _, collisionErr := NewGraycodeRouterEngineForSettings(Settings{CustomProviders: []CustomProviderConfig{{ + if _, collisionErr := NewEyrieEngineForSettings(Settings{CustomProviders: []CustomProviderConfig{{ Name: "openai", BaseURL: "https://collision.example.test/v1", }}}); collisionErr == nil { t.Fatal("built-in custom gateway collision was accepted") @@ -53,7 +53,7 @@ func TestNewGraycodeRouterEngineForSettingsIsInvocationScoped(t *testing.T) { if err != nil || len(models) != 1 { t.Fatalf("independent invalid construction changed existing engine: %+v, err=%v", models, err) } - other, err := NewGraycodeRouterEngineForSettings(Settings{}) + other, err := NewEyrieEngineForSettings(Settings{}) if err != nil { t.Fatal(err) } diff --git a/internal/config/graycode_router_selection.go b/internal/config/eyrie_selection.go similarity index 75% rename from internal/config/graycode_router_selection.go rename to internal/config/eyrie_selection.go index 3271967c1..3cf28054c 100644 --- a/internal/config/graycode_router_selection.go +++ b/internal/config/eyrie_selection.go @@ -4,7 +4,7 @@ import ( "context" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) type ( @@ -13,9 +13,9 @@ type ( ) // EffectiveSelection resolves persisted selection and optional host overrides -// through GraycodeRouter's host-neutral engine contract. +// through Eyrie's host-neutral engine contract. func EffectiveSelection(ctx context.Context, opts SelectionOptions) Selection { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return Selection{} } @@ -23,60 +23,60 @@ func EffectiveSelection(ctx context.Context, opts SelectionOptions) Selection { } func EffectiveSelectionWithSettings(ctx context.Context, settings Settings, opts SelectionOptions) Selection { - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return Selection{} } return engine.EffectiveSelection(ctx, opts) } -// ActiveModel returns the selected model from graycode-router provider.json (not graycode settings). +// ActiveModel returns the selected model from eyrie provider.json (not hawk settings). func ActiveModel(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } return engine.ActiveSelection(ctx).Model } -// ActiveProvider returns the selected provider from graycode-router provider.json. +// ActiveProvider returns the selected provider from eyrie provider.json. func ActiveProvider(ctx context.Context) string { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } return engine.ActiveSelection(ctx).Provider } -// ActiveProviderID canonicalizes a host-facing provider/gateway id through GraycodeRouter runtime. +// ActiveProviderID canonicalizes a host-facing provider/gateway id through Eyrie runtime. func ActiveProviderID(provider string) string { return gateway.NormalizeProviderID(provider) } -// SetActiveModel persists model selection to graycode-router provider.json. +// SetActiveModel persists model selection to eyrie provider.json. func SetActiveModel(ctx context.Context, modelID string) error { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } return engine.SetActiveModel(ctx, modelID) } -// SetActiveProvider persists provider selection to graycode-router provider.json. +// SetActiveProvider persists provider selection to eyrie provider.json. func SetActiveProvider(ctx context.Context, provider string) error { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } @@ -90,14 +90,14 @@ func SetActiveSelection(ctx context.Context, provider, modelID string) error { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return err } return engine.SetSelection(ctx, provider, modelID) } -// migrateStoredModelProvider moves model/provider from ~/.graycode/settings.json into graycode-router once. +// migrateStoredModelProvider moves model/provider from ~/.hawk/settings.json into eyrie once. func migrateStoredModelProvider(s *Settings) { if s == nil { return @@ -109,7 +109,7 @@ func migrateStoredModelProvider(s *Settings) { activeProvider := strings.TrimSpace(ActiveProvider(ctx)) changed := false - // Existing GraycodeRouter state is authoritative. Otherwise migrate a stored pair + // Existing Eyrie state is authoritative. Otherwise migrate a stored pair // in one validated write so a rejected model cannot strand only the // provider in the destination or silently erase the user's source value. if activeModel != "" { diff --git a/internal/config/main_test.go b/internal/config/main_test.go index ec4fab174..e3567512a 100644 --- a/internal/config/main_test.go +++ b/internal/config/main_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/catalogtest" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestMain(m *testing.M) { diff --git a/internal/config/milestone_verify_test.go b/internal/config/milestone_verify_test.go index c4f56e1c5..7fbb1767d 100644 --- a/internal/config/milestone_verify_test.go +++ b/internal/config/milestone_verify_test.go @@ -8,23 +8,23 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-router/catalog" - graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" - "github.com/GrayCodeAI/graycode-router/credentials" + "github.com/GrayCodeAI/eyrie/catalog" + eyriecfg "github.com/GrayCodeAI/eyrie/config" + "github.com/GrayCodeAI/eyrie/credentials" ) -// isolateMilestoneTest uses a temp HOME and GRAYCODE_CONFIG_DIR so verification does not touch the user machine. +// isolateMilestoneTest uses a temp HOME and HAWK_CONFIG_DIR so verification does not touch the user machine. func isolateMilestoneTest(t *testing.T) string { t.Helper() home := t.TempDir() - graycodeDir := filepath.Join(home, ".graycode") - if err := os.MkdirAll(graycodeDir, 0o700); err != nil { + hawkDir := filepath.Join(home, ".hawk") + if err := os.MkdirAll(hawkDir, 0o700); err != nil { t.Fatal(err) } t.Setenv("HOME", home) - t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", graycodeDir) - return graycodeDir + t.Setenv("HAWK_CONFIG_DIR", hawkDir) + t.Setenv("EYRIE_CONFIG_DIR", hawkDir) + return hawkDir } func TestVerify_ProviderJSONOnDiskHasNoSecrets(t *testing.T) { @@ -34,23 +34,23 @@ func TestVerify_ProviderJSONOnDiskHasNoSecrets(t *testing.T) { t.Fatal("compiled catalog required") } env := map[string]string{"ANTHROPIC_API_KEY": "sk-ant-verify-test-key-1234567890"} - cfg := graycoderoutercfg.SyncProviderConfigFromCatalog(compiled, env) - path, err := graycoderoutercfg.GetProviderConfigPath() + cfg := eyriecfg.SyncProviderConfigFromCatalog(compiled, env) + path, err := eyriecfg.GetProviderConfigPath() if err != nil { t.Fatalf("GetProviderConfigPath: %v", err) } - if err := graycoderoutercfg.SaveProviderConfig(cfg, path); err != nil { + if err := eyriecfg.SaveProviderConfig(cfg, path); err != nil { t.Fatal(err) } assertProviderJSONFileHasNoSecrets(t, path) } func TestVerify_MigrateProviderSecretsStripsDisk(t *testing.T) { - graycodeDir := isolateMilestoneTest(t) + hawkDir := isolateMilestoneTest(t) store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) }) - path := filepath.Join(graycodeDir, "provider.json") + path := filepath.Join(hawkDir, "provider.json") secret := "sk-ant-migrate-verify-key-1234567890" raw := `{ "version": "1", @@ -84,7 +84,7 @@ func TestVerify_MigrateProviderSecretsStripsDisk(t *testing.T) { } func TestVerify_PersistAPIKeyDoesNotWriteProviderJSON(t *testing.T) { - graycodeDir := isolateMilestoneTest(t) + hawkDir := isolateMilestoneTest(t) credentials.SetDefaultStore(emptyCredentialStore{}) t.Cleanup(func() { credentials.SetDefaultStore(nil) }) @@ -92,7 +92,7 @@ func TestVerify_PersistAPIKeyDoesNotWriteProviderJSON(t *testing.T) { if err := PersistAPIKey(context.Background(), "ANTHROPIC_API_KEY", secret); err != nil { t.Fatal(err) } - path := filepath.Join(graycodeDir, "provider.json") + path := filepath.Join(hawkDir, "provider.json") if _, err := os.Stat(path); err == nil { data, _ := os.ReadFile(path) if strings.Contains(string(data), secret) { @@ -137,13 +137,13 @@ func TestVerify_EvaluateSetupFlow(t *testing.T) { t.Fatal("expected setup still needed until model selected") } - providerPath := filepath.Join(os.Getenv("HOME"), ".graycode", "provider.json") - cfg := &graycoderoutercfg.ProviderConfig{ + providerPath := filepath.Join(os.Getenv("HOME"), ".hawk", "provider.json") + cfg := &eyriecfg.ProviderConfig{ ActiveProvider: "anthropic", ActiveModel: "claude-sonnet-4-20250514", AnthropicModel: "claude-sonnet-4-20250514", } - if err := graycoderoutercfg.SaveProviderConfig(cfg, providerPath); err != nil { + if err := eyriecfg.SaveProviderConfig(cfg, providerPath); err != nil { t.Fatal(err) } st = EvaluateSetup(ctx) @@ -171,7 +171,7 @@ func assertProviderJSONFileHasNoSecrets(t *testing.T, path string) { t.Fatalf("provider.json at %s contains non-empty %s", path, needle) } } - var cfg graycoderoutercfg.ProviderConfig + var cfg eyriecfg.ProviderConfig if err := json.Unmarshal(data, &cfg); err != nil { t.Fatal(err) } diff --git a/internal/config/provider_filter.go b/internal/config/provider_filter.go index 08ba55e3f..183dd9651 100644 --- a/internal/config/provider_filter.go +++ b/internal/config/provider_filter.go @@ -4,13 +4,13 @@ import ( "context" ) -// DefaultModelProviderFilter picks which graycode-router provider to list models for when the UI -// has no explicit filter. Host prefs (settings) win; otherwise graycode-router routing/deployments decide. +// DefaultModelProviderFilter picks which eyrie provider to list models for when the UI +// has no explicit filter. Host prefs (settings) win; otherwise eyrie routing/deployments decide. func DefaultModelProviderFilter(ctx context.Context) string { if p := ActiveGateway(ctx); p != "" { return p } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "" } diff --git a/internal/config/sandbox_checklist.go b/internal/config/sandbox_checklist.go index cf9ddf315..ff584490a 100644 --- a/internal/config/sandbox_checklist.go +++ b/internal/config/sandbox_checklist.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/sandbox" ) // SandboxChecklistItem is one ordered step in the Docker onboarding checklist. @@ -20,7 +20,7 @@ type SandboxChecklistItem struct { // EvaluateSandboxChecklist returns the ordered Docker onboarding checklist // (daemon -> image cached -> registry reachable -> local build available), // emitted identically by path, preflight, and doctor. It is diagnostic only: -// it never mutates Docker state. graycode is fail-closed — there is no +// it never mutates Docker state. hawk is fail-closed — there is no // host-execution fallback (see docs/SECURITY-DEVELOPER.md). func EvaluateSandboxChecklist(ctx context.Context) []SandboxChecklistItem { if ctx == nil { @@ -50,7 +50,7 @@ func EvaluateSandboxChecklist(ctx context.Context) []SandboxChecklistItem { Status: statusFor(daemon && present), Detail: boolDetail(present, "Image present: "+img, - "Image not cached — Graycode pulls or builds it on first run"), + "Image not cached — Hawk pulls or builds it on first run"), FixCmd: "docker pull " + sandbox.DefaultSandboxImage(), }) diff --git a/internal/config/security_test.go b/internal/config/security_test.go index dc7173357..4fa8d21e6 100644 --- a/internal/config/security_test.go +++ b/internal/config/security_test.go @@ -87,8 +87,8 @@ func TestMergeSettings_AllowedToolsAppend(t *testing.T) { func TestValidateSettings_ValidConfig(t *testing.T) { // This test uses the global catalog test setup from main_test.go dir := t.TempDir() - t.Setenv("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", dir) + t.Setenv("HAWK_CONFIG_DIR", dir) + t.Setenv("EYRIE_CONFIG_DIR", dir) s := Settings{ MaxBudgetUSD: 10.0, } diff --git a/internal/config/settings.go b/internal/config/settings.go index 3cb5f4593..d9ab5dcc1 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -13,27 +13,27 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" - "github.com/GrayCodeAI/graycode-cli/internal/safewrite" - "github.com/GrayCodeAI/graycode-cli/internal/smartrouting" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/safewrite" + "github.com/GrayCodeAI/hawk/internal/smartrouting" + "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func fetchModelsViaRuntime(ctx context.Context, provider string) ([]EngineModel, error) { return ListEngineModels(ctx, provider, false) } -// Settings holds graycode configuration. -// Graycode: no API keys stored here. Secrets come from the OS secret store via graycode-router. +// Settings holds hawk configuration. +// Hawk: no API keys stored here. Secrets come from the OS secret store via eyrie. type Settings struct { // PolicySchemaVersion versions permission/autonomy/sandbox fields. Zero is // the legacy format and is migrated to CurrentPolicySchemaVersion on load. PolicySchemaVersion int `json:"policy_schema_version,omitempty"` - // Model and Provider are retained only for one-time migration into graycode-router provider.json. - // Graycode does not persist model/provider here; use SetActiveModel / SetActiveProvider. + // Model and Provider are retained only for one-time migration into eyrie provider.json. + // Hawk does not persist model/provider here; use SetActiveModel / SetActiveProvider. Model string `json:"model,omitempty"` Provider string `json:"provider,omitempty"` Theme string `json:"theme,omitempty"` @@ -106,7 +106,7 @@ func ToolPresetByName(name string) (ToolPreset, bool) { return p, ok } -// Attribution controls how graycode identifies itself in git commits. +// Attribution controls how hawk identifies itself in git commits. type Attribution = types.Attribution // CustomProviderConfig defines a user-specified OpenAI-compatible provider. @@ -191,7 +191,7 @@ func readSettingsFileCached(path string) ([]byte, error) { settingsCache.modTime.Equal(fi.ModTime()) && settingsCache.size == fi.Size() { return settingsCache.data, nil } - data, err := os.ReadFile(path) // #nosec G304 -- path is the graycode global settings path from internal storage config, not external input + data, err := os.ReadFile(path) // #nosec G304 -- path is the hawk global settings path from internal storage config, not external input if err == nil && statErr == nil { settingsCache.valid = true settingsCache.path = path @@ -213,7 +213,7 @@ func invalidateSettingsCache() { settingsCache.Unlock() } -// LoadGlobalSettings loads only Graycode's user config settings.json. +// LoadGlobalSettings loads only Hawk's user config settings.json. func LoadGlobalSettings() Settings { var s Settings path := globalSettingsPath() @@ -230,7 +230,7 @@ func LoadGlobalSettings() Settings { // LoadSettings loads settings from user config, overlaid with any // project-scoped settings discovered by walking up from the current working -// directory looking for .graycode/settings.json. +// directory looking for .hawk/settings.json. func LoadSettings() Settings { s := LoadGlobalSettings() if project := findProjectSettings(); project != nil { @@ -265,7 +265,7 @@ func projectSafeSettings(project Settings) Settings { } // findProjectSettings walks up from the current working directory looking for -// a .graycode/settings.json file. Returns nil if none is found. The nearest +// a .hawk/settings.json file. Returns nil if none is found. The nearest // ancestor wins (no recursive merge — a project settings file fully shadows // global settings for its fields). func findProjectSettings() *Settings { @@ -275,7 +275,7 @@ func findProjectSettings() *Settings { } dir := cwd for { - candidate := filepath.Join(dir, ".graycode", "settings.json") + candidate := filepath.Join(dir, ".hawk", "settings.json") if data, err := os.ReadFile(candidate); err == nil { // #nosec G304 -- path derived from cwd walk-up var s Settings if err := json.Unmarshal(data, &s); err == nil { @@ -443,7 +443,7 @@ func SaveGlobal(s Settings) error { // SettingValue returns a display-safe value for a supported setting key. func SettingValue(s Settings, key string) (string, bool) { normalized := normalizeSettingKey(key) - // Graycode: API key status comes from OS secret store, not settings file + // Hawk: API key status comes from OS secret store, not settings file if provider, ok := apiKeyProviderFromSettingKey(normalized); ok { return EnvKeyStatus(provider), true } @@ -510,12 +510,12 @@ func SettingValue(s Settings, key string) (string, bool) { } } -// SetGlobalSetting updates a supported scalar/list setting in Graycode user config. -// Graycode: API keys are NOT stored in settings.json. Use /config and the OS secret store. +// SetGlobalSetting updates a supported scalar/list setting in Hawk user config. +// Hawk: API keys are NOT stored in settings.json. Use /config and the OS secret store. func SetGlobalSetting(key, value string) error { s := LoadGlobalSettings() normalized := normalizeSettingKey(key) - // Graycode: reject API key persistence to disk + // Hawk: reject API key persistence to disk if _, ok := apiKeyProviderFromSettingKey(normalized); ok { return fmt.Errorf("API keys are not stored in settings.json. Save via /config (%s)", CredentialStoreName()) } @@ -689,7 +689,7 @@ func splitSettingList(value string) []string { func BoolPtr(b bool) *bool { return &b } // ───────────────────────────────────────────────────────────── -// Graycode: API keys from OS secret store only (no .env) +// Hawk: API keys from OS secret store only (no .env) // ───────────────────────────────────────────────────────────── // ProviderAPIKeyEnv returns the API key env var for a provider (registry first for setup gateways). @@ -702,7 +702,7 @@ func ProviderAPIKeyEnv(provider string) string { // EnvKeyStatus returns set, empty, or local from the OS credential store. func EnvKeyStatus(provider string) string { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return "empty" } @@ -738,7 +738,7 @@ func providerCredentialEnvAliases(provider string) []string { primary := strings.TrimSpace(ProviderAPIKeyEnv(provider)) seen := map[string]bool{} var out []string - engine, _ := newGraycodeRouterEngine() + engine, _ := newEyrieEngine() var aliases []string if engine != nil { aliases = engine.CredentialEnvKeys(provider) @@ -755,10 +755,10 @@ func providerCredentialEnvAliases(provider string) []string { } // ───────────────────────────────────────────────────────────── -// Live model catalog fetch from graycode-router +// Live model catalog fetch from eyrie // ───────────────────────────────────────────────────────────── -// FetchModelsForProvider returns models from the graycode-router catalog (dynamic; no graycode hardcoded lists). +// FetchModelsForProvider returns models from the eyrie catalog (dynamic; no hawk hardcoded lists). // RefreshModelCatalogV1 is the explicit network refresh boundary. func FetchModelsForProvider(provider string) ([]EngineModel, error) { provider = gateway.NormalizeProviderID(provider) @@ -776,7 +776,7 @@ func FetchModelsForProvider(provider string) ([]EngineModel, error) { if err != nil { return nil, err } - // Custom OpenAI-compatible providers: single model from settings, not graycode catalog data. + // Custom OpenAI-compatible providers: single model from settings, not hawk catalog data. for _, cp := range LoadSettings().CustomProviders { if gateway.NormalizeProviderID(cp.Name) != provider { continue @@ -788,7 +788,7 @@ func FetchModelsForProvider(provider string) ([]EngineModel, error) { }}, nil } } - return nil, fmt.Errorf("no models found for provider %s in graycode-router catalog (check API keys; graycode will refresh automatically on next start)", provider) + return nil, fmt.Errorf("no models found for provider %s in eyrie catalog (check API keys; hawk will refresh automatically on next start)", provider) } // FetchModelsForProviderWithSettings resolves cached models using one @@ -798,7 +798,7 @@ func FetchModelsForProviderWithSettings(ctx context.Context, settings Settings, if provider == "" { return nil, fmt.Errorf("no provider specified") } - engine, engineErr := NewGraycodeRouterEngineForSettings(settings) + engine, engineErr := NewEyrieEngineForSettings(settings) if engineErr != nil { return nil, engineErr } @@ -812,18 +812,18 @@ func FetchModelsForProviderWithSettings(ctx context.Context, settings Settings, if err != nil { return nil, err } - return nil, fmt.Errorf("no models found for provider %s in graycode-router catalog", provider) + return nil, fmt.Errorf("no models found for provider %s in eyrie catalog", provider) } func refreshModelCatalog(ctx context.Context, _ bool) (gateway.CatalogSnapshot, error) { - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err != nil { return gateway.CatalogSnapshot{}, err } return engine.RefreshCatalog(ctx, "") } -// RefreshModelCatalogV1 asks graycode-router to refresh the remote catalog and provider APIs using env API keys. +// RefreshModelCatalogV1 asks eyrie to refresh the remote catalog and provider APIs using env API keys. func RefreshModelCatalogV1(ctx context.Context) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() @@ -840,7 +840,7 @@ func RefreshModelCatalogV1(ctx context.Context) (string, error) { func RefreshModelCatalogV1WithSettings(ctx context.Context, settings Settings) (string, error) { ctx, cancel := context.WithTimeout(ctx, 90*time.Second) defer cancel() - engine, err := NewGraycodeRouterEngineForSettings(settings) + engine, err := NewEyrieEngineForSettings(settings) if err != nil { return "", err } diff --git a/internal/config/settings_status_test.go b/internal/config/settings_status_test.go index a4636e51d..2fc290e6f 100644 --- a/internal/config/settings_status_test.go +++ b/internal/config/settings_status_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) -func TestEnvKeyStatusUsesGraycodeRouterCredentialStatus(t *testing.T) { +func TestEnvKeyStatusUsesEyrieCredentialStatus(t *testing.T) { store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) }) diff --git a/internal/config/settings_test.go b/internal/config/settings_test.go index 280988b7f..99c5601ed 100644 --- a/internal/config/settings_test.go +++ b/internal/config/settings_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) func TestMergeSettings_ModelOverride(t *testing.T) { diff --git a/internal/config/setup_status.go b/internal/config/setup_status.go index c6594cc51..6bef8dea2 100644 --- a/internal/config/setup_status.go +++ b/internal/config/setup_status.go @@ -45,16 +45,16 @@ func evaluateSetupFrom(hasCreds, hasModel bool) SetupState { return st } -// HasConfiguredDeployment reports whether at least one graycode-router deployment has credentials. +// HasConfiguredDeployment reports whether at least one eyrie deployment has credentials. func HasConfiguredDeployment(ctx context.Context) bool { if ctx == nil { ctx = context.Background() } - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() return err == nil && engine.EffectiveSelection(ctx, SelectionOptions{}).HasConfiguredDeployment } -// HasSelectedModel reports whether graycode-router provider.json has a selected model. +// HasSelectedModel reports whether eyrie provider.json has a selected model. func HasSelectedModel() bool { return strings.TrimSpace(ActiveModel(context.Background())) != "" } diff --git a/internal/config/setup_status_test.go b/internal/config/setup_status_test.go index de7719ee8..3a5223c4f 100644 --- a/internal/config/setup_status_test.go +++ b/internal/config/setup_status_test.go @@ -5,9 +5,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" - "github.com/GrayCodeAI/graycode-router/catalog" - "github.com/GrayCodeAI/graycode-router/credentials" + "github.com/GrayCodeAI/eyrie/catalog" + "github.com/GrayCodeAI/eyrie/credentials" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestHasConfiguredDeployment_FromStore(t *testing.T) { diff --git a/internal/config/ui_cache.go b/internal/config/ui_cache.go index 8b5312238..bf013cddf 100644 --- a/internal/config/ui_cache.go +++ b/internal/config/ui_cache.go @@ -74,7 +74,7 @@ func gatewayCredentialSnapshot(ctx context.Context) map[string]gatewayCredential credSnapMu.Unlock() out := make(map[string]gatewayCredentialState) - engine, err := newGraycodeRouterEngine() + engine, err := newEyrieEngine() if err == nil { for _, gateway := range engine.Gateways(ctx) { out[gateway.ID] = gatewayCredentialState{ diff --git a/internal/config/validator.go b/internal/config/validator.go index 95b09c773..696d36e3a 100644 --- a/internal/config/validator.go +++ b/internal/config/validator.go @@ -43,9 +43,9 @@ func (r ValidationResult) Error() string { func ValidateSettings(s Settings) ValidationResult { var errors []ValidationError - // Provider names are delegated to GraycodeRouter. Do not hardcode/validate here. + // Provider names are delegated to Eyrie. Do not hardcode/validate here. - // Validate model selection (stored in graycode-router provider.json) + // Validate model selection (stored in eyrie provider.json) activeModel := strings.TrimSpace(s.Model) if activeModel == "" { activeModel = ActiveModel(context.Background()) @@ -62,7 +62,7 @@ func ValidateSettings(s Settings) ValidationResult { if activeProvider == "" { activeProvider = ActiveProvider(context.Background()) } - // Graycode: validate API key is in the OS secret store (not in settings) + // Hawk: validate API key is in the OS secret store (not in settings) if activeProvider != "" { envKey := ProviderAPIKeyEnv(activeProvider) if envKey != "" && EnvKeyStatus(activeProvider) != "set" { diff --git a/internal/config/validator_test.go b/internal/config/validator_test.go index 887ab321d..ab281a327 100644 --- a/internal/config/validator_test.go +++ b/internal/config/validator_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestValidateSettingsValid(t *testing.T) { @@ -25,7 +25,7 @@ func TestValidateSettingsValid(t *testing.T) { } } -func TestValidateSettingsProviderDelegatedToGraycodeRouter(t *testing.T) { +func TestValidateSettingsProviderDelegatedToEyrie(t *testing.T) { store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) }) @@ -33,7 +33,7 @@ func TestValidateSettingsProviderDelegatedToGraycodeRouter(t *testing.T) { s := Settings{Provider: "anthropic"} result := ValidateSettings(s) if result.Valid { - t.Fatal("expected invalid (missing env key for graycode-router provider)") + t.Fatal("expected invalid (missing env key for eyrie provider)") } } diff --git a/internal/config/xiaomi_setup_test.go b/internal/config/xiaomi_setup_test.go index d8b4b30db..300070753 100644 --- a/internal/config/xiaomi_setup_test.go +++ b/internal/config/xiaomi_setup_test.go @@ -4,27 +4,27 @@ import ( "os" "testing" - graycoderoutercfg "github.com/GrayCodeAI/graycode-router/config" + eyriecfg "github.com/GrayCodeAI/eyrie/config" ) func TestSetGatewayRegion_XiaomiClearsStaleBaseHost(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) - t.Setenv("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", dir) + t.Setenv("HAWK_CONFIG_DIR", dir) + t.Setenv("EYRIE_CONFIG_DIR", dir) t.Setenv("XIAOMI_MIMO_TOKEN_PLAN_BASE_URL", "https://caller-owned.example.test/v1") - cfg := &graycoderoutercfg.ProviderConfig{ + cfg := &eyriecfg.ProviderConfig{ Version: "1", XiaomiMimoTokenPlanRegion: "cn", XiaomiMimoTokenPlanBaseURL: "https://token-plan-cn.xiaomimimo.com/v1", } - if err := graycoderoutercfg.SaveProviderConfig(cfg, ""); err != nil { + if err := eyriecfg.SaveProviderConfig(cfg, ""); err != nil { t.Fatal(err) } if err := SetGatewayRegion(ProviderXiaomiTokenPlan, "sgp"); err != nil { t.Fatal(err) } - loaded := graycoderoutercfg.LoadProviderConfig("") + loaded := eyriecfg.LoadProviderConfig("") if loaded.XiaomiMimoTokenPlanRegion != "sgp" { t.Fatalf("region = %q", loaded.XiaomiMimoTokenPlanRegion) } @@ -36,13 +36,13 @@ func TestSetGatewayRegion_XiaomiClearsStaleBaseHost(t *testing.T) { func TestNeedsGatewayRegion_XiaomiInvalidAndMissing(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) - t.Setenv("GRAYCODE_CONFIG_DIR", dir) - t.Setenv("GRAYCODE_ROUTER_CONFIG_DIR", dir) + t.Setenv("HAWK_CONFIG_DIR", dir) + t.Setenv("EYRIE_CONFIG_DIR", dir) if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { t.Fatal("expected true when no config file") } - if err := graycoderoutercfg.SaveProviderConfig(&graycoderoutercfg.ProviderConfig{Version: "1", XiaomiMimoTokenPlanRegion: "tokyo"}, ""); err != nil { + if err := eyriecfg.SaveProviderConfig(&eyriecfg.ProviderConfig{Version: "1", XiaomiMimoTokenPlanRegion: "tokyo"}, ""); err != nil { t.Fatal(err) } if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { diff --git a/internal/container/lifecycle.go b/internal/container/lifecycle.go index 100043851..cb2d31ded 100644 --- a/internal/container/lifecycle.go +++ b/internal/container/lifecycle.go @@ -1,4 +1,4 @@ -// Package container provides Docker container lifecycle management for graycode's +// Package container provides Docker container lifecycle management for hawk's // sandboxed execution environments. It wraps the Docker CLI to start, stop, // inspect, and rebuild containers. package container diff --git a/internal/context/repomap/repomap.go b/internal/context/repomap/repomap.go index f19e4ecc7..4638166fc 100644 --- a/internal/context/repomap/repomap.go +++ b/internal/context/repomap/repomap.go @@ -1,12 +1,12 @@ // Package repomap is the prompt-injection shim that produces a token-budgeted -// repository overview for graycode's context layer. It builds an import/refer +// repository overview for hawk's context layer. It builds an import/refer // graph over the source files in root, ranks the nodes with a PageRank // pass, and renders the highest-ranked files (and their top symbols) as a // compact text block capped at Options.Budget tokens. // // # Relationship to internal/intelligence/repomap // -// graycode ships a second package, internal/intelligence/repomap, that exposes +// hawk ships a second package, internal/intelligence/repomap, that exposes // a much larger surface: call graphs, search, quality signals, API // scanning, incremental indexing, and so on. That package is the deep // analysis engine. THIS package is intentionally narrow: one entry point diff --git a/internal/context/repomap/scan.go b/internal/context/repomap/scan.go index ff217d587..1f9411f13 100644 --- a/internal/context/repomap/scan.go +++ b/internal/context/repomap/scan.go @@ -12,7 +12,7 @@ var skipDirs = map[string]struct{}{ ".git": {}, "node_modules": {}, "vendor": {}, - ".graycode": {}, + ".hawk": {}, "dist": {}, "build": {}, "target": {}, diff --git a/internal/context/rules.go b/internal/context/rules.go index e020fe10c..f22a5f02b 100644 --- a/internal/context/rules.go +++ b/internal/context/rules.go @@ -10,8 +10,8 @@ import ( "sort" "strings" - homepkg "github.com/GrayCodeAI/graycode-cli/internal/home" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + homepkg "github.com/GrayCodeAI/hawk/internal/home" + "github.com/GrayCodeAI/hawk/internal/storage" ) // managedSource is the synthetic source name for IT-managed (org policy) rules. @@ -23,10 +23,10 @@ const managedSource = "managed" func defaultManagedPaths() []string { switch runtime.GOOS { case "darwin": - return []string{"/Library/Application Support/Graycode/GRAYCODE.md"} + return []string{"/Library/Application Support/Hawk/HAWK.md"} default: // Linux and other unix-like systems. - return []string{"/etc/graycode/GRAYCODE.md"} + return []string{"/etc/hawk/HAWK.md"} } } @@ -55,7 +55,7 @@ var DefaultRuleSources = []RuleSource{ {".cursor/rules", 4, true}, {".github/instructions", 5, true}, {"AGENTS.md", 10, false}, - {"GRAYCODE.md", 11, false}, + {"HAWK.md", 11, false}, {"CLAUDE.md", 12, false}, {"CONTEXT.md", 13, false}, {".github/copilot-instructions.md", 14, false}, diff --git a/internal/context/rules_test.go b/internal/context/rules_test.go index 3c5a74e4b..fe171e38e 100644 --- a/internal/context/rules_test.go +++ b/internal/context/rules_test.go @@ -139,7 +139,7 @@ func TestRuleDiscoverer_SourcePriority(t *testing.T) { dir := t.TempDir() os.MkdirAll(filepath.Join(dir, ".agents", "rules"), 0o755) os.MkdirAll(filepath.Join(dir, ".claude", "rules"), 0o755) - os.WriteFile(filepath.Join(dir, ".agents", "rules", "a.md"), []byte("# Graycode"), 0o644) + os.WriteFile(filepath.Join(dir, ".agents", "rules", "a.md"), []byte("# Hawk"), 0o644) os.WriteFile(filepath.Join(dir, ".claude", "rules", "b.md"), []byte("# Claude"), 0o644) sub := filepath.Join(dir, "src") os.MkdirAll(sub, 0o755) @@ -150,16 +150,16 @@ func TestRuleDiscoverer_SourcePriority(t *testing.T) { rules := rd.Discover(target) // .agents/rules (priority 1) should come before .claude/rules (priority 3) - var graycodeIdx, claudeIdx int + var hawkIdx, claudeIdx int for i, r := range rules { if r.Source == ".agents/rules" { - graycodeIdx = i + hawkIdx = i } if r.Source == ".claude/rules" { claudeIdx = i } } - if graycodeIdx >= claudeIdx { + if hawkIdx >= claudeIdx { t.Error(".agents/rules should have higher precedence than .claude/rules") } } @@ -183,12 +183,12 @@ func TestRuleDiscoverer_EmptyProject(t *testing.T) { func TestRuleDiscoverer_ManagedTierPrecedence(t *testing.T) { dir := t.TempDir() // A project rule that would normally have top precedence. - os.WriteFile(filepath.Join(dir, "GRAYCODE.md"), []byte("# Project Policy"), 0o644) + os.WriteFile(filepath.Join(dir, "HAWK.md"), []byte("# Project Policy"), 0o644) target := filepath.Join(dir, "main.go") os.WriteFile(target, []byte("package main"), 0o644) // Stand in for the IT-managed policy file (default paths are system-level). - managed := filepath.Join(dir, "managed-GRAYCODE.md") + managed := filepath.Join(dir, "managed-HAWK.md") os.WriteFile(managed, []byte("# Org Policy"), 0o644) rd := NewRuleDiscoverer(dir) @@ -204,17 +204,17 @@ func TestRuleDiscoverer_ManagedTierPrecedence(t *testing.T) { if rules[0].Content != "# Org Policy" { t.Errorf("managed content mismatch: got %q", rules[0].Content) } - // Managed must outrank the project GRAYCODE.md regardless of project precedence. + // Managed must outrank the project HAWK.md regardless of project precedence. for i, r := range rules { - if r.Source == "GRAYCODE.md" && i == 0 { - t.Error("project GRAYCODE.md should not outrank managed tier") + if r.Source == "HAWK.md" && i == 0 { + t.Error("project HAWK.md should not outrank managed tier") } } } func TestRuleDiscoverer_ManagedTierMissing(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "GRAYCODE.md"), []byte("# Project"), 0o644) + os.WriteFile(filepath.Join(dir, "HAWK.md"), []byte("# Project"), 0o644) target := filepath.Join(dir, "main.go") os.WriteFile(target, []byte("package main"), 0o644) @@ -252,7 +252,7 @@ func TestStripHTMLComments(t *testing.T) { func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "GRAYCODE.md"), []byte("# Rules\n\nUse tabs."), 0o644) + os.WriteFile(filepath.Join(dir, "HAWK.md"), []byte("# Rules\n\nUse tabs."), 0o644) target := filepath.Join(dir, "main.go") os.WriteFile(target, []byte("package main"), 0o644) @@ -261,7 +261,7 @@ func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { found := false for _, r := range rules { - if r.Source == "GRAYCODE.md" { + if r.Source == "HAWK.md" { found = true if strings.Contains(r.Content, "internal note") { t.Errorf("HTML comment not stripped from loaded content: %q", r.Content) @@ -272,7 +272,7 @@ func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { } } if !found { - t.Fatal("GRAYCODE.md rule not loaded") + t.Fatal("HAWK.md rule not loaded") } } diff --git a/internal/context/walkup.go b/internal/context/walkup.go index b66638296..8838ca992 100644 --- a/internal/context/walkup.go +++ b/internal/context/walkup.go @@ -1,4 +1,4 @@ -// Package context provides hierarchical context discovery for graycode. +// Package context provides hierarchical context discovery for hawk. // It implements walk-up AGENTS.md discovery (pi-nested-agents-md pattern): // when an agent reads a file, the discoverer traverses upward collecting // convention files at each directory level with session-scoped deduplication. @@ -13,7 +13,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/hooks" + "github.com/GrayCodeAI/hawk/internal/hooks" ) const ( @@ -23,7 +23,7 @@ const ( // DefaultConventionFiles lists the files to discover at each directory level. var DefaultConventionFiles = []string{ - "AGENTS.md", "GRAYCODE.md", "CLAUDE.md", "CONTEXT.md", + "AGENTS.md", "HAWK.md", "CLAUDE.md", "CONTEXT.md", } // InjectionCache tracks which files have already been injected this session. diff --git a/internal/context/walkup_test.go b/internal/context/walkup_test.go index b9086cab1..f8c4a09be 100644 --- a/internal/context/walkup_test.go +++ b/internal/context/walkup_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/hooks" + "github.com/GrayCodeAI/hawk/internal/hooks" ) func TestWalkUpDiscoverer_FindsAgentsMd(t *testing.T) { diff --git a/internal/contracts/agent/hooks.go b/internal/contracts/agent/hooks.go index ff23bdc42..3ebc9ed40 100644 --- a/internal/contracts/agent/hooks.go +++ b/internal/contracts/agent/hooks.go @@ -1,13 +1,13 @@ // Vendored from github.com/GrayCodeAI/eagle/agent at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package agent import "strings" // Hook event names for lifecycle and tool gates. // -// Graycode may accept vendor aliases (Claude/Cursor); normalize to these -// canonical names at the boundary. Constants are shared so graycode, plugins, +// Hawk may accept vendor aliases (Claude/Cursor); normalize to these +// canonical names at the boundary. Constants are shared so hawk, plugins, // and SDKs agree on wire/event vocabulary without importing engines. const ( HookPreToolUse = "PreToolUse" @@ -24,7 +24,7 @@ const ( HookFailure = "Failure" ) -// VendorHookAliases maps common third-party hook names to canonical Graycode names. +// VendorHookAliases maps common third-party hook names to canonical Hawk names. // Keys are lower-case for case-insensitive lookup. var VendorHookAliases = map[string]string{ "pretooluse": HookPreToolUse, @@ -52,7 +52,7 @@ var VendorHookAliases = map[string]string{ "on_error": HookFailure, } -// CanonicalHookEvent returns the Graycode canonical event name for s, or "" if unknown. +// CanonicalHookEvent returns the Hawk canonical event name for s, or "" if unknown. func CanonicalHookEvent(s string) string { if s == "" { return "" diff --git a/internal/contracts/agent/spawn.go b/internal/contracts/agent/spawn.go index 47ba0d88a..6f4baac94 100644 --- a/internal/contracts/agent/spawn.go +++ b/internal/contracts/agent/spawn.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/agent at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. // Package agent defines shared DTOs for typed subagent spawn across graycode-eco. // // Stdlib only. No engine, CLI, or storage imports. diff --git a/internal/contracts/events/events.go b/internal/contracts/events/events.go index bb3599c22..a22b20461 100644 --- a/internal/contracts/events/events.go +++ b/internal/contracts/events/events.go @@ -1,10 +1,10 @@ // Vendored from github.com/GrayCodeAI/eagle/events at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package events import "time" -// ToolEvent represents a normalized tool event emitted by Graycode workflows. +// ToolEvent represents a normalized tool event emitted by Hawk workflows. type ToolEvent struct { ToolName string `json:"tool_name"` ToolInput map[string]interface{} `json:"tool_input,omitempty"` diff --git a/internal/contracts/graph/graph.go b/internal/contracts/graph/graph.go index 826c5eaf3..f9d5451e3 100644 --- a/internal/contracts/graph/graph.go +++ b/internal/contracts/graph/graph.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/graph at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. // Package graph defines the portable graph vocabulary shared across graycode-eco. // // The package contains data contracts only. Individual repositories retain diff --git a/internal/contracts/harness/harness.go b/internal/contracts/harness/harness.go index 080d86170..6692de3eb 100644 --- a/internal/contracts/harness/harness.go +++ b/internal/contracts/harness/harness.go @@ -1,11 +1,11 @@ // Vendored from github.com/GrayCodeAI/eagle/harness at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package harness import ( "time" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" ) // Dimension represents one of the 5 core Agent Work Loop dimensions. @@ -70,7 +70,7 @@ type AssetsDetected struct { KestrelBridge bool `json:"kestrel_bridge"` } -// Report is the neutral cross-repo contract for Graycode Agent Harness evaluations. +// Report is the neutral cross-repo contract for Hawk Agent Harness evaluations. type Report struct { TargetPath string `json:"target_path"` GeneratedAt time.Time `json:"generated_at"` diff --git a/internal/contracts/policy/policy.go b/internal/contracts/policy/policy.go index ebae6b4f3..6ce810c9c 100644 --- a/internal/contracts/policy/policy.go +++ b/internal/contracts/policy/policy.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/policy at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package policy import ( diff --git a/internal/contracts/review/review.go b/internal/contracts/review/review.go index c860fcdd9..3e060f6f9 100644 --- a/internal/contracts/review/review.go +++ b/internal/contracts/review/review.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/review at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package review import ( @@ -7,10 +7,10 @@ import ( "strings" "time" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" ) -// Finding is the neutral review finding contract shared across Graycode and review engines. +// Finding is the neutral review finding contract shared across Hawk and review engines. type Finding struct { Concern string `json:"concern"` Severity contracts.Severity `json:"severity"` diff --git a/internal/contracts/types/finding.go b/internal/contracts/types/finding.go index 5c1d17a46..1d1a326a3 100644 --- a/internal/contracts/types/finding.go +++ b/internal/contracts/types/finding.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/types at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package types import ( @@ -8,7 +8,7 @@ import ( "time" ) -// Finding represents a unified analysis concern sourced from Graycode support engines. +// Finding represents a unified analysis concern sourced from Hawk support engines. type Finding struct { ID string `json:"id"` Source string `json:"source"` diff --git a/internal/contracts/types/severity.go b/internal/contracts/types/severity.go index 32654bd0c..76813ba8f 100644 --- a/internal/contracts/types/severity.go +++ b/internal/contracts/types/severity.go @@ -1,5 +1,5 @@ // Vendored from github.com/GrayCodeAI/eagle/types at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package types import ( diff --git a/internal/contracts/verify/verify.go b/internal/contracts/verify/verify.go index d9269ec42..7acc406ca 100644 --- a/internal/contracts/verify/verify.go +++ b/internal/contracts/verify/verify.go @@ -1,14 +1,14 @@ // Vendored from github.com/GrayCodeAI/eagle/verify at v0.0.0-20260902153929-5877bed17503 (MIT, Copyright (c) 2026 GrayCode AI). -// The upstream repository no longer exists; this copy is owned by Graycode as its contract surface. +// The upstream repository no longer exists; this copy is owned by Hawk as its contract surface. package verify import ( "time" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/types" ) -// Finding is the neutral verification finding contract shared across Graycode and verification engines. +// Finding is the neutral verification finding contract shared across Hawk and verification engines. type Finding struct { Check string `json:"check"` Severity contracts.Severity `json:"severity"` diff --git a/internal/crash/crash.go b/internal/crash/crash.go index 2acefb196..ab67d9623 100644 --- a/internal/crash/crash.go +++ b/internal/crash/crash.go @@ -31,7 +31,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) const ( @@ -151,7 +151,7 @@ func WriteReport(recovered any, stack []byte) (string, error) { func formatReport(r CrashReport) string { var b strings.Builder - b.WriteString("graycode crash report\n") + b.WriteString("hawk crash report\n") fmt.Fprintf(&b, "timestamp: %s\n", r.Timestamp.Format(time.RFC3339Nano)) if r.Version != "" { fmt.Fprintf(&b, "version: %s\n", r.Version) diff --git a/internal/crash/crash_extra_test.go b/internal/crash/crash_extra_test.go index 989600e1c..23ecf8827 100644 --- a/internal/crash/crash_extra_test.go +++ b/internal/crash/crash_extra_test.go @@ -9,7 +9,7 @@ import ( "time" ) -const testEnvStateDir = "GRAYCODE_STATE_DIR" +const testEnvStateDir = "HAWK_STATE_DIR" // --- WriteReport tests --- @@ -112,7 +112,7 @@ func TestFormatReport_FullFields(t *testing.T) { } result := formatReport(r) - if !strings.Contains(result, "graycode crash report") { + if !strings.Contains(result, "hawk crash report") { t.Error("report should have header") } if !strings.Contains(result, "1.2.3") { diff --git a/internal/crash/crash_test.go b/internal/crash/crash_test.go index c5e999a99..62d1a7a00 100644 --- a/internal/crash/crash_test.go +++ b/internal/crash/crash_test.go @@ -5,14 +5,14 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/crash" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/crash" + "github.com/GrayCodeAI/hawk/internal/storage" ) // reportRoot returns the crash report dir by appending the subdir name to the // state dir, matching the layout crash.WriteReport uses. We go through the // public storage.StateDir so the test stays in-package-friendly and inherits -// the same GRAYCODE_STATE_DIR override production uses. +// the same HAWK_STATE_DIR override production uses. func reportRoot(t *testing.T) string { t.Helper() base := storage.StateDir() @@ -23,10 +23,10 @@ func reportRoot(t *testing.T) string { } func TestReportPath_InStateDir(t *testing.T) { - // GRAYCODE_STATE_DIR must be set before Install/crash writes anything; mirror + // HAWK_STATE_DIR must be set before Install/crash writes anything; mirror // the convention storage's own tests use. dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", dir) + t.Setenv("HAWK_STATE_DIR", dir) got, err := crash.WriteReport("boom", []byte("goroutine 1 [running]:\nmain.main()\n")) if err != nil { @@ -43,7 +43,7 @@ func TestReportPath_InStateDir(t *testing.T) { func TestInstall_DoesNotPanic(t *testing.T) { dir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", dir) + t.Setenv("HAWK_STATE_DIR", dir) // Install must be safe and idempotent. crash.Install() diff --git a/internal/crash/crash_unix.go b/internal/crash/crash_unix.go index d7f962852..f3bcd7c56 100644 --- a/internal/crash/crash_unix.go +++ b/internal/crash/crash_unix.go @@ -53,7 +53,7 @@ func dumpSignal(sig syscall.Signal, reason string) { timestamp := now().UTC().Format("20060102T150405.000Z") filename := fmt.Sprintf("crash-signal-%s-%s.txt", sig, timestamp) path := filepath.Join(dir, filename) - content := fmt.Sprintf("graycode signal report\nsignal: %s\nreason: %s\ntimestamp: %s\n\n%s\n", + content := fmt.Sprintf("hawk signal report\nsignal: %s\nreason: %s\ntimestamp: %s\n\n%s\n", sig, reason, timestamp, stacks) _ = os.WriteFile(path, []byte(content), 0o600) } diff --git a/internal/daemon/contract_parity_test.go b/internal/daemon/contract_parity_test.go index 85bd6b462..dc89e0a5a 100644 --- a/internal/daemon/contract_parity_test.go +++ b/internal/daemon/contract_parity_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // TestDaemon_GraphSync_ContractMatrix locks the /v1/graph/sync parity contract diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 2b8f19fec..38276ed36 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -22,12 +22,12 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/netutil" - "github.com/GrayCodeAI/graycode-cli/internal/observability/metrics" - "github.com/GrayCodeAI/graycode-cli/internal/securitylog" - graycodesession "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/netutil" + "github.com/GrayCodeAI/hawk/internal/observability/metrics" + "github.com/GrayCodeAI/hawk/internal/securitylog" + hawksession "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" ) const maxRequestBodyBytes = 1 << 20 @@ -74,10 +74,10 @@ func (e *InvalidChatRequestError) Unwrap() error { return e.Err } // version is set via SetVersion from main.go at startup. var version = "0.0.0" -// SetVersion propagates the canonical graycode version into the daemon. +// SetVersion propagates the canonical hawk version into the daemon. func SetVersion(v string) { version = v } -// Server is the graycode daemon HTTP server for programmatic/CI access. +// Server is the hawk daemon HTTP server for programmatic/CI access. type Server struct { addr string mux *http.ServeMux @@ -92,14 +92,14 @@ type Server struct { apiKey string gateways *gatewayManager - // readyFn reports whether GraycodeRouter's preflight/readiness dependencies are + // readyFn reports whether Eyrie's preflight/readiness dependencies are // initialized and the server can serve real traffic. It is consulted by // GET /v1/ready. A nil probe fails closed: a session factory alone does not - // prove that GraycodeRouter's catalog, credentials, and model selection are ready. + // prove that Eyrie's catalog, credentials, and model selection are ready. readyMu sync.RWMutex readyFn func() (bool, string) - // graphFactory projects a persisted Graycode session into the portable, + // graphFactory projects a persisted Hawk session into the portable, // privacy-safe execution graph. The composition root supplies the builder; // the daemon owns only HTTP validation, authentication, and encoding. graphMu sync.RWMutex @@ -343,7 +343,7 @@ func (s *Server) Start() (string, error) { s.gateways.Start(context.Background()) } - slog.Info("graycode daemon started", "addr", actualAddr) + slog.Info("hawk daemon started", "addr", actualAddr) return actualAddr, nil } @@ -382,7 +382,7 @@ func (s *Server) warnInsecureAuthConfig() { return } slog.Warn( - "graycode daemon started without API key authentication; only loopback access allowed", + "hawk daemon started without API key authentication; only loopback access allowed", "addr", s.addr, "hint", "Set Config.APIKey to enable authentication, or keep the default loopback bind.", ) @@ -441,7 +441,7 @@ func (s *Server) SetGraphFactory(factory GraphFactory) { s.graphMu.Unlock() } -// ready evaluates readiness using the installed GraycodeRouter probe. It fails closed +// ready evaluates readiness using the installed Eyrie probe. It fails closed // when the engine is absent or the composition root did not install a probe; // merely having a factory does not prove provider readiness. func (s *Server) ready() (bool, string) { @@ -454,7 +454,7 @@ func (s *Server) ready() (bool, string) { if s.newSession == nil { return false, "engine not configured" } - return false, "GraycodeRouter readiness probe not configured" + return false, "Eyrie readiness probe not configured" } func (s *Server) routes() { @@ -540,11 +540,11 @@ func (s *Server) cancelSession(sessionID string) bool { return true } -// maxConcurrentFromEnv reads GRAYCODE_DAEMON_MAX_CONCURRENT (clamped to >= 1) so +// maxConcurrentFromEnv reads HAWK_DAEMON_MAX_CONCURRENT (clamped to >= 1) so // operators can tune the global chat concurrency cap without a rebuild. func maxConcurrentFromEnv() int { n := defaultMaxConcurrentChat - if raw := os.Getenv("GRAYCODE_DAEMON_MAX_CONCURRENT"); raw != "" { + if raw := os.Getenv("HAWK_DAEMON_MAX_CONCURRENT"); raw != "" { if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 { n = parsed } @@ -706,12 +706,12 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) { lock.Lock() defer lock.Unlock() - var saved *graycodesession.Session + var saved *hawksession.Session if requestedID != "" { var loadErr error - saved, loadErr = graycodesession.Load(sessionID) + saved, loadErr = hawksession.Load(sessionID) if loadErr != nil { - if !errors.Is(loadErr, graycodesession.ErrNotFound) { + if !errors.Is(loadErr, hawksession.ErrNotFound) { slog.Error("load continuation session failed", "err", loadErr, "session_id", sessionID) writeJSON(w, http.StatusInternalServerError, ErrorResponse{ Error: "session load failed", @@ -783,7 +783,7 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) { return } if saved != nil { - sess.LoadMessages(graycodesession.ToRuntimeMessages(saved.Messages)) + sess.LoadMessages(hawksession.ToRuntimeMessages(saved.Messages)) if err := sess.ReplayJournal(saved.Events); err != nil { slog.Error("replay session event journal failed", "err", err, "session_id", sessionID) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "session event journal replay failed"}) @@ -807,7 +807,7 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) { } if requested > max { writeJSON(w, http.StatusBadRequest, ErrorResponse{ - Error: fmt.Sprintf("autonomy %q exceeds the daemon's configured maximum (%s); the daemon is non-interactive and cannot approve escalated permissions. Raise Config.MaxAutonomy (GRAYCODE_DAEMON_AUTONOMY) to allow it", requested.String(), max.String()), + Error: fmt.Sprintf("autonomy %q exceeds the daemon's configured maximum (%s); the daemon is non-interactive and cannot approve escalated permissions. Raise Config.MaxAutonomy (HAWK_DAEMON_AUTONOMY) to allow it", requested.String(), max.String()), Code: "autonomy_denied", }) return @@ -867,11 +867,11 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) { // streamSSE writes a streaming response as SSE events, observing client // disconnect via r.Context().Done() so the handler does not keep pushing // events to a dead connection. -func streamSSE(s *Server, w http.ResponseWriter, r *http.Request, events <-chan engine.StreamEvent, sessionID string, req ChatRequest, sess *engine.Session, saved *graycodesession.Session, start time.Time) { +func streamSSE(s *Server, w http.ResponseWriter, r *http.Request, events <-chan engine.StreamEvent, sessionID string, req ChatRequest, sess *engine.Session, saved *hawksession.Session, start time.Time) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") - w.Header().Set("X-Graycode-Session-ID", sessionID) + w.Header().Set("X-Hawk-Session-ID", sessionID) w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "DENY") flusher, _ := w.(http.Flusher) @@ -962,7 +962,7 @@ func (s *Server) abortStreamedSession(sessionID string) { } // writeJSONResponse accumulates events and writes a single JSON response. -func writeJSONResponse(s *Server, w http.ResponseWriter, events <-chan engine.StreamEvent, sessionID string, req ChatRequest, sess *engine.Session, saved *graycodesession.Session, start time.Time) { +func writeJSONResponse(s *Server, w http.ResponseWriter, events <-chan engine.StreamEvent, sessionID string, req ChatRequest, sess *engine.Session, saved *hawksession.Session, start time.Time) { var response strings.Builder var totalIn, totalOut, turns int @@ -985,7 +985,7 @@ func writeJSONResponse(s *Server, w http.ResponseWriter, events <-chan engine.St return } s.trackSession(sessionID, req, saved, start, turns) - w.Header().Set("X-Graycode-Session-ID", sessionID) + w.Header().Set("X-Hawk-Session-ID", sessionID) writeJSON(w, http.StatusOK, ChatResponse{ SessionID: sessionID, @@ -998,7 +998,7 @@ func writeJSONResponse(s *Server, w http.ResponseWriter, events <-chan engine.St } func validSessionID(id string) bool { - return graycodesession.ValidID(id) + return hawksession.ValidID(id) } // CancelRequest is the JSON body for POST /v1/cancel. @@ -1077,7 +1077,7 @@ func (s *Server) sessionLock(id string) *sync.Mutex { return &s.sessionLocks[hash%uint64(len(s.sessionLocks))] } -func persistDaemonSession(id string, req ChatRequest, sess *engine.Session, previous *graycodesession.Session, startedAt time.Time) error { +func persistDaemonSession(id string, req ChatRequest, sess *engine.Session, previous *hawksession.Session, startedAt time.Time) error { createdAt := startedAt name := "" if previous != nil { @@ -1092,20 +1092,20 @@ func persistDaemonSession(id string, req ChatRequest, sess *engine.Session, prev if j := sess.Persistence().Journal(); j != nil { j.AppendSessionTitle(name) } - return graycodesession.Save(&graycodesession.Session{ + return hawksession.Save(&hawksession.Session{ ID: id, Model: sess.Model(), Provider: sess.Provider(), Agent: req.Agent, CWD: req.CWD, Name: name, - Messages: graycodesession.FromRuntimeMessages(sess.RawMessages()), + Messages: hawksession.FromRuntimeMessages(sess.RawMessages()), Events: sess.JournalWire(), CreatedAt: createdAt, }) } -func (s *Server) trackSession(id string, req ChatRequest, previous *graycodesession.Session, startedAt time.Time, turns int) { +func (s *Server) trackSession(id string, req ChatRequest, previous *hawksession.Session, startedAt time.Time, turns int) { createdAt := startedAt if previous != nil && !previous.CreatedAt.IsZero() { createdAt = previous.CreatedAt diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go index 2265262ef..3dac3ca5d 100644 --- a/internal/daemon/daemon_test.go +++ b/internal/daemon/daemon_test.go @@ -14,10 +14,10 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func startTestDaemon(t *testing.T, srv *Server) string { @@ -94,7 +94,7 @@ func TestDaemon_Ready(t *testing.T) { wantReady: false, }, { - name: "engine wired without GraycodeRouter probe is not ready", + name: "engine wired without Eyrie probe is not ready", factory: factory, wantStatus: http.StatusServiceUnavailable, wantReady: false, @@ -238,7 +238,7 @@ func TestDaemon_RejectsUnknownFields(t *testing.T) { } func TestDaemon_Chat_WithEngine(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) factory := func(req ChatRequest) (*engine.Session, error) { sess := engine.NewSession("", "test-model", "you are helpful", nil) if err := sess.SetMaxTurns(1); err != nil { @@ -315,7 +315,7 @@ func postDaemonChat(t *testing.T, addr string, request ChatRequest, accept strin } func TestDaemon_ChatPersistsRetrievableSessionAndRequestMetadata(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) requestedCWD := t.TempDir() canonicalCWD, err := canonicalSessionCWD(requestedCWD) if err != nil { @@ -336,8 +336,8 @@ func TestDaemon_ChatPersistsRetrievableSessionAndRequestMetadata(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Fatalf("POST /v1/chat status = %d, want 200", resp.StatusCode) } - if chat.SessionID == "" || resp.Header.Get("X-Graycode-Session-ID") != chat.SessionID { - t.Fatalf("session ID response/header mismatch: body=%q header=%q", chat.SessionID, resp.Header.Get("X-Graycode-Session-ID")) + if chat.SessionID == "" || resp.Header.Get("X-Hawk-Session-ID") != chat.SessionID { + t.Fatalf("session ID response/header mismatch: body=%q header=%q", chat.SessionID, resp.Header.Get("X-Hawk-Session-ID")) } factoryReq := <-seen @@ -371,7 +371,7 @@ func TestDaemon_ChatPersistsRetrievableSessionAndRequestMetadata(t *testing.T) { } func TestDaemon_ChatContinuationReusesDurableSession(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) requestedCWD := t.TempDir() seen := make(chan ChatRequest, 2) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, daemonTestSessionFactory(seen)) @@ -419,7 +419,7 @@ func TestDaemon_ChatContinuationReusesDurableSession(t *testing.T) { } func TestDaemon_ChatRejectsMissingContinuation(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, daemonTestSessionFactory(nil)) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) @@ -432,7 +432,7 @@ func TestDaemon_ChatRejectsMissingContinuation(t *testing.T) { } func TestDaemon_ChatDoesNotMisreportCorruptContinuationAsMissing(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) if err := os.MkdirAll(storage.SessionsDir(), 0o750); err != nil { t.Fatal(err) } @@ -460,7 +460,7 @@ func TestDaemon_ChatDoesNotMisreportCorruptContinuationAsMissing(t *testing.T) { } func TestDaemon_ChatRejectsUnsafeSessionIDAndInvalidCWD(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, daemonTestSessionFactory(nil)) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) @@ -479,7 +479,7 @@ func TestDaemon_ChatRejectsUnsafeSessionIDAndInvalidCWD(t *testing.T) { } func TestDaemon_ChatSSEExposesRetrievableSessionID(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, daemonTestSessionFactory(nil)) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) @@ -490,7 +490,7 @@ func TestDaemon_ChatSSEExposesRetrievableSessionID(t *testing.T) { if err != nil { t.Fatalf("read SSE response: %v", err) } - id := resp.Header.Get("X-Graycode-Session-ID") + id := resp.Header.Get("X-Hawk-Session-ID") if resp.StatusCode != http.StatusOK || id == "" || !strings.Contains(string(body), `"session_id":"`+id+`"`) { t.Fatalf("SSE status=%d id=%q body=%q", resp.StatusCode, id, body) } diff --git a/internal/daemon/discord.go b/internal/daemon/discord.go index d051aa6f7..18e679803 100644 --- a/internal/daemon/discord.go +++ b/internal/daemon/discord.go @@ -11,7 +11,7 @@ import ( "github.com/bwmarrin/discordgo" ) -// DiscordGateway bridges graycode to Discord via the official Gateway (WebSocket), +// DiscordGateway bridges hawk to Discord via the official Gateway (WebSocket), // using bwmarrin/discordgo. Unlike a REST-poll bridge it receives message events // in real time — guild @mentions and direct messages — with reconnection, // heartbeats, and rate limiting handled by the library. Authorized prompts are @@ -21,7 +21,7 @@ type DiscordGateway struct { cfg DiscordConfig daemonAddr string apiKey string - client *http.Client // for forwardToGraycode + client *http.Client // for forwardToHawk auth *authorizer dispatch *asyncDispatcher @@ -140,7 +140,7 @@ func (g *DiscordGateway) handleMessage(ctx context.Context, senderID, channelID, if isPair, ok := g.auth.tryPair(senderID, text); isPair { if ok { - reply("Paired. You can now chat with graycode.") + reply("Paired. You can now chat with hawk.") } else { reply("Pairing failed: invalid code.") } @@ -151,7 +151,7 @@ func (g *DiscordGateway) handleMessage(ctx context.Context, senderID, channelID, return } - resp, err := forwardToGraycode(ctx, g.client, g.daemonAddr, g.apiKey, text) + resp, err := forwardToHawk(ctx, g.client, g.daemonAddr, g.apiKey, text) if err != nil { resp = fmt.Sprintf("Error: %v", err) } diff --git a/internal/daemon/e2e_test.go b/internal/daemon/e2e_test.go index 26946f01b..46b966573 100644 --- a/internal/daemon/e2e_test.go +++ b/internal/daemon/e2e_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/feature" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/feature" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // TestE2E_HealthEndpoint verifies the health endpoint returns the expected @@ -83,11 +83,11 @@ func TestE2E_MetricsEndpoint(t *testing.T) { if err != nil { t.Fatal(err) } - if !strings.Contains(string(body), "graycode_daemon_active_sessions") { - t.Error("expected graycode_daemon_active_sessions metric in output") + if !strings.Contains(string(body), "hawk_daemon_active_sessions") { + t.Error("expected hawk_daemon_active_sessions metric in output") } - if !strings.Contains(string(body), "graycode_daemon_uptime_seconds") { - t.Error("expected graycode_daemon_uptime_seconds metric in output") + if !strings.Contains(string(body), "hawk_daemon_uptime_seconds") { + t.Error("expected hawk_daemon_uptime_seconds metric in output") } // Test JSON format too. diff --git a/internal/daemon/gateway.go b/internal/daemon/gateway.go index b272f1e08..739537a1d 100644 --- a/internal/daemon/gateway.go +++ b/internal/daemon/gateway.go @@ -11,11 +11,11 @@ import ( "sync" ) -// forwardToGraycode posts a prompt to the daemon's /v1/chat endpoint and returns the +// forwardToHawk posts a prompt to the daemon's /v1/chat endpoint and returns the // assistant reply. daemonAddr must include a scheme (e.g. "http://127.0.0.1:4590"). // apiKey, when non-empty, is sent as a Bearer token. Shared by the Discord and // Slack gateways; Telegram keeps its own method for backwards compatibility. -func forwardToGraycode(ctx context.Context, client *http.Client, daemonAddr, apiKey, prompt string) (string, error) { +func forwardToHawk(ctx context.Context, client *http.Client, daemonAddr, apiKey, prompt string) (string, error) { payload, _ := json.Marshal(map[string]string{"prompt": prompt}) req, err := http.NewRequestWithContext(ctx, http.MethodPost, daemonAddr+"/v1/chat", strings.NewReader(string(payload))) if err != nil { @@ -41,7 +41,7 @@ func forwardToGraycode(ctx context.Context, client *http.Client, daemonAddr, api } // Gateway is a bidirectional messaging bridge between an external chat platform -// (Telegram, Discord, Slack, ...) and the graycode daemon. Implementations forward +// (Telegram, Discord, Slack, ...) and the hawk daemon. Implementations forward // inbound messages to the daemon's /v1/chat endpoint and relay the reply back. type Gateway interface { // Name returns a short identifier for the gateway (e.g. "telegram"). diff --git a/internal/daemon/gateway_test.go b/internal/daemon/gateway_test.go index 832bd2545..ffe340b7d 100644 --- a/internal/daemon/gateway_test.go +++ b/internal/daemon/gateway_test.go @@ -211,7 +211,7 @@ func TestAuthorizer_PairingAndAllowlist(t *testing.T) { } } -func TestForwardToGraycode(t *testing.T) { +func TestForwardToHawk(t *testing.T) { var gotAuth, gotPrompt string ts := newIPv4GatewayServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotAuth = r.Header.Get("Authorization") @@ -224,9 +224,9 @@ func TestForwardToGraycode(t *testing.T) { })) defer ts.Close() - reply, err := forwardToGraycode(context.Background(), ts.Client(), ts.URL, "key123", "ping") + reply, err := forwardToHawk(context.Background(), ts.Client(), ts.URL, "key123", "ping") if err != nil { - t.Fatalf("forwardToGraycode: %v", err) + t.Fatalf("forwardToHawk: %v", err) } if reply != "pong" { t.Errorf("reply=%q want pong", reply) @@ -367,22 +367,22 @@ func TestSlackGateway_RejectsBadSignature(t *testing.T) { } func TestDiscordGateway_HandleMessage_FlowsThroughAllowlist(t *testing.T) { - graycode := newIPv4GatewayServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "graycode-reply"}) + hawk := newIPv4GatewayServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) })) - defer graycode.Close() + defer hawk.Close() - g := newDiscordGateway(DiscordConfig{Token: "bot", PairingCode: "code"}, graycode.URL, "") + g := newDiscordGateway(DiscordConfig{Token: "bot", PairingCode: "code"}, hawk.URL, "") var sent []string send := func(s string) error { sent = append(sent, s); return nil } - // Unauthorized non-pair message -> "Unauthorized", no graycode call. + // Unauthorized non-pair message -> "Unauthorized", no hawk call. g.handleMessage(context.Background(), "u1", "C", "hello", send) // Wrong pairing code -> failure. g.handleMessage(context.Background(), "u1", "C", "/pair nope", send) // Correct pairing code -> paired. g.handleMessage(context.Background(), "u1", "C", "/pair code", send) - // Authorized -> forwarded to graycode. + // Authorized -> forwarded to hawk. g.handleMessage(context.Background(), "u1", "C", "do it", send) if len(sent) != 4 { @@ -397,8 +397,8 @@ func TestDiscordGateway_HandleMessage_FlowsThroughAllowlist(t *testing.T) { if !strings.Contains(sent[2], "Paired") { t.Errorf("sent[2]=%q want Paired", sent[2]) } - if sent[3] != "graycode-reply" { - t.Errorf("sent[3]=%q want graycode-reply", sent[3]) + if sent[3] != "hawk-reply" { + t.Errorf("sent[3]=%q want hawk-reply", sent[3]) } if !g.auth.allowed("u1") { t.Errorf("u1 should be allowed after pairing") diff --git a/internal/daemon/graph_ledger_sqlite_test.go b/internal/daemon/graph_ledger_sqlite_test.go index 95fd27087..dfb9b493f 100644 --- a/internal/daemon/graph_ledger_sqlite_test.go +++ b/internal/daemon/graph_ledger_sqlite_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestSQLiteGraphLedger_PersistsAcrossReopen(t *testing.T) { @@ -17,10 +17,10 @@ func TestSQLiteGraphLedger_PersistsAcrossReopen(t *testing.T) { SyncID: "sync-1", ProjectID: "proj", SessionID: "sess", - SchemaVersion: "graycode.graph/v1", + SchemaVersion: "hawk.graph/v1", Digest: "abc123", Facts: 4, - GraphJSON: `{"schema_version":"graycode.graph/v1","nodes":[]}`, + GraphJSON: `{"schema_version":"hawk.graph/v1","nodes":[]}`, ReceivedAt: time.Now().UTC(), } @@ -55,7 +55,7 @@ func TestSQLiteGraphLedger_PersistsAcrossReopen(t *testing.T) { if got.Digest != "abc123" || got.Facts != 4 || got.GraphJSON != rec.GraphJSON { t.Fatalf("retained record mismatch: %+v", got) } - if got.ProjectID != "proj" || got.SessionID != "sess" || got.SchemaVersion != "graycode.graph/v1" { + if got.ProjectID != "proj" || got.SessionID != "sess" || got.SchemaVersion != "hawk.graph/v1" { t.Fatalf("retained metadata mismatch: %+v", got) } if got.ReceivedAt.IsZero() { diff --git a/internal/daemon/h9_h10_test.go b/internal/daemon/h9_h10_test.go index 11b2b37c2..3782dba3d 100644 --- a/internal/daemon/h9_h10_test.go +++ b/internal/daemon/h9_h10_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // TestIPLimiter_BurstAndPerIPIsolation verifies the token bucket allows up to @@ -111,7 +111,7 @@ func TestDaemon_CancelEndpoint(t *testing.T) { // TestDaemon_GlobalConcurrencyCap verifies handleChat returns 503 when the // global concurrency semaphore is saturated (H9). func TestDaemon_GlobalConcurrencyCap(t *testing.T) { - t.Setenv("GRAYCODE_DAEMON_MAX_CONCURRENT", "1") + t.Setenv("HAWK_DAEMON_MAX_CONCURRENT", "1") srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, daemonTestSessionFactory(nil)) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) diff --git a/internal/daemon/middleware.go b/internal/daemon/middleware.go index ebda2e1b5..70a5b5a97 100644 --- a/internal/daemon/middleware.go +++ b/internal/daemon/middleware.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/feature" + "github.com/GrayCodeAI/hawk/internal/feature" ) // requestIDKey is the context key for the request ID. diff --git a/internal/daemon/middleware_test.go b/internal/daemon/middleware_test.go index 75ca86c11..2c29af36c 100644 --- a/internal/daemon/middleware_test.go +++ b/internal/daemon/middleware_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/feature" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/feature" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // --- Pure helper unit tests --- diff --git a/internal/daemon/routes_agent_status_test.go b/internal/daemon/routes_agent_status_test.go index ab5016cf2..e6c1d7a51 100644 --- a/internal/daemon/routes_agent_status_test.go +++ b/internal/daemon/routes_agent_status_test.go @@ -13,7 +13,7 @@ func TestAgentStatusStates(t *testing.T) { now := time.Now() // working: in-flight cancel registered. - s.sessions.Store("working-1", &Session{ID: "working-1", Agent: "graycode", Turns: 3, LastUsed: now}) + s.sessions.Store("working-1", &Session{ID: "working-1", Agent: "hawk", Turns: 3, LastUsed: now}) s.cancelMu.Lock() s.cancels["working-1"] = &cancelEntry{cancel: func() {}} s.cancelMu.Unlock() diff --git a/internal/daemon/routes_graph.go b/internal/daemon/routes_graph.go index 30c3550d0..2eee3f19f 100644 --- a/internal/daemon/routes_graph.go +++ b/internal/daemon/routes_graph.go @@ -9,8 +9,8 @@ import ( "time" "unicode" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/session" ) const ( @@ -26,7 +26,7 @@ type GraphRequest struct { GeneratedAt time.Time } -// GraphFactory projects one persisted session into Graycode's portable graph. +// GraphFactory projects one persisted session into Hawk's portable graph. type GraphFactory func(context.Context, GraphRequest) (executiongraph.Export, error) // handleGetSessionGraph handles GET /v1/sessions/{id}/graph. diff --git a/internal/daemon/routes_graph_read.go b/internal/daemon/routes_graph_read.go index 843e0bd93..c7d30d58b 100644 --- a/internal/daemon/routes_graph_read.go +++ b/internal/daemon/routes_graph_read.go @@ -17,7 +17,7 @@ import ( const ( defaultGraphReadLimit = 100 maxGraphReadLimit = 250 - graphReadSchemaVersion = "graycode-local.graph/v1" + graphReadSchemaVersion = "hawk-local.graph/v1" ) // GraphReadResponse is the JSON response for GET /v1/projects/:projectId/graph. diff --git a/internal/daemon/routes_graph_read_test.go b/internal/daemon/routes_graph_read_test.go index 5f693078d..12fdff547 100644 --- a/internal/daemon/routes_graph_read_test.go +++ b/internal/daemon/routes_graph_read_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/executiongraph" ) func getGraphRead(t *testing.T, addr, path string) *http.Response { @@ -49,8 +49,8 @@ func TestDaemon_GraphRead_Empty(t *testing.T) { t.Fatalf("expected 200, got %d", resp.StatusCode) } out := decodeGraphRead(t, resp) - if out.Graph.SchemaVersion != "graycode-local.graph/v1" { - t.Errorf("schema_version = %q, want graycode-local.graph/v1", out.Graph.SchemaVersion) + if out.Graph.SchemaVersion != "hawk-local.graph/v1" { + t.Errorf("schema_version = %q, want hawk-local.graph/v1", out.Graph.SchemaVersion) } if out.Graph.Scope["project_id"] != "proj" { t.Errorf("scope.project_id = %q, want proj", out.Graph.Scope["project_id"]) diff --git a/internal/daemon/routes_graph_sync.go b/internal/daemon/routes_graph_sync.go index f71579184..02ebb9387 100644 --- a/internal/daemon/routes_graph_sync.go +++ b/internal/daemon/routes_graph_sync.go @@ -12,12 +12,12 @@ import ( "strings" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + "github.com/GrayCodeAI/hawk/internal/executiongraph" ) // POST /v1/graph/sync lets a producer (a future ecosystem repo) push portable -// `*.graph/v1` facts into Graycode. It mirrors the cloud plane's /v1/graph/sync +// `*.graph/v1` facts into Hawk. It mirrors the cloud plane's /v1/graph/sync // contract so a producer can target either surface with the same payload. // The daemon is a localhost consumer surface: it validates the graph, rejects // malformed or non-portable facts, and acknowledges with an idempotency digest. diff --git a/internal/daemon/routes_graph_sync_test.go b/internal/daemon/routes_graph_sync_test.go index 8160ed4b1..41e28538e 100644 --- a/internal/daemon/routes_graph_sync_test.go +++ b/internal/daemon/routes_graph_sync_test.go @@ -11,9 +11,9 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func newGraphSyncTestServer(t *testing.T) string { diff --git a/internal/daemon/routes_graph_test.go b/internal/daemon/routes_graph_test.go index 9c20f25cc..3794ee624 100644 --- a/internal/daemon/routes_graph_test.go +++ b/internal/daemon/routes_graph_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/executiongraph" + "github.com/GrayCodeAI/hawk/internal/session" ) func TestGetSessionGraphProjectsBoundedRequest(t *testing.T) { @@ -27,7 +27,7 @@ func TestGetSessionGraphProjectsBoundedRequest(t *testing.T) { request := httptest.NewRequest( http.MethodGet, - "/v1/sessions/session-1/graph?repository=graycode&swift_checkpoint=012345abcdef&swift_checkpoint=fedcba987654", + "/v1/sessions/session-1/graph?repository=hawk&swift_checkpoint=012345abcdef&swift_checkpoint=fedcba987654", nil, ) request.Header.Set("Authorization", "Bearer secret") @@ -37,7 +37,7 @@ func TestGetSessionGraphProjectsBoundedRequest(t *testing.T) { if response.Code != http.StatusOK { t.Fatalf("status = %d, want 200: %s", response.Code, response.Body.String()) } - if captured.SessionID != "session-1" || captured.RepositoryID != "graycode" { + if captured.SessionID != "session-1" || captured.RepositoryID != "hawk" { t.Fatalf("captured request = %#v", captured) } wantCheckpoints := []string{"012345abcdef", "fedcba987654"} @@ -92,7 +92,7 @@ func TestGetSessionGraphRejectsInvalidInputBeforeFactory(t *testing.T) { }, { name: "repository control character", - url: "/v1/sessions/session-1/graph?repository=graycode%0Aother", + url: "/v1/sessions/session-1/graph?repository=hawk%0Aother", code: "invalid_repository", }, { diff --git a/internal/daemon/routes_lease.go b/internal/daemon/routes_lease.go index f4f18d029..60d60eb38 100644 --- a/internal/daemon/routes_lease.go +++ b/internal/daemon/routes_lease.go @@ -6,7 +6,7 @@ import ( "errors" "net/http" - "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/hawk/internal/session" ) // handleAcquireLease creates or refreshes a single-owner lease on a session, diff --git a/internal/daemon/routes_lease_test.go b/internal/daemon/routes_lease_test.go index 39e81a6db..6738c866f 100644 --- a/internal/daemon/routes_lease_test.go +++ b/internal/daemon/routes_lease_test.go @@ -6,8 +6,8 @@ import ( "net/http" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func httpDo(t *testing.T, method, url string) *http.Response { @@ -24,7 +24,7 @@ func httpDo(t *testing.T, method, url string) *http.Response { } func TestAcquireAndReleaseLease(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, nil) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) diff --git a/internal/daemon/routes_metrics.go b/internal/daemon/routes_metrics.go index fb10775f8..e25d35641 100644 --- a/internal/daemon/routes_metrics.go +++ b/internal/daemon/routes_metrics.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/observability/metrics" + "github.com/GrayCodeAI/hawk/internal/observability/metrics" ) // handleMetrics handles GET /v1/metrics. It exposes daemon-level metrics in @@ -76,16 +76,16 @@ func (s *Server) emitRuntimeMetrics(sb *strings.Builder) { return true }) - sb.WriteString("# TYPE graycode_daemon_active_sessions gauge\n") - sb.WriteString(fmt.Sprintf("graycode_daemon_active_sessions %d\n", activeSessions)) + sb.WriteString("# TYPE hawk_daemon_active_sessions gauge\n") + sb.WriteString(fmt.Sprintf("hawk_daemon_active_sessions %d\n", activeSessions)) // Concurrency slots used - sb.WriteString("# TYPE graycode_daemon_chat_concurrency_used gauge\n") - sb.WriteString(fmt.Sprintf("graycode_daemon_chat_concurrency_used %d\n", len(s.concurrencySem))) + sb.WriteString("# TYPE hawk_daemon_chat_concurrency_used gauge\n") + sb.WriteString(fmt.Sprintf("hawk_daemon_chat_concurrency_used %d\n", len(s.concurrencySem))) // Uptime - sb.WriteString("# TYPE graycode_daemon_uptime_seconds gauge\n") - sb.WriteString(fmt.Sprintf("graycode_daemon_uptime_seconds %.0f\n", time.Since(s.startedAt).Seconds())) + sb.WriteString("# TYPE hawk_daemon_uptime_seconds gauge\n") + sb.WriteString(fmt.Sprintf("hawk_daemon_uptime_seconds %.0f\n", time.Since(s.startedAt).Seconds())) } // sanitizeMetricName converts a dotted metric name to Prometheus naming diff --git a/internal/daemon/routes_review.go b/internal/daemon/routes_review.go index 96075e417..1a1874d48 100644 --- a/internal/daemon/routes_review.go +++ b/internal/daemon/routes_review.go @@ -19,7 +19,7 @@ const reviewArgMaxLen = 4096 var reviewArgCharset = regexp.MustCompile(`^[^\x00-\x1f\x7f]*$`) -// reviewSem bounds the number of concurrent `graycode review run` subprocesses +// reviewSem bounds the number of concurrent `hawk review run` subprocesses // spawned by POST /v1/review so an authenticated caller cannot exhaust CPU // or memory by firing unbounded review jobs. var reviewSem = make(chan struct{}, maxConcurrentReviews) @@ -91,7 +91,7 @@ func (s *Server) handleReview(w http.ResponseWriter, r *http.Request) { return } - // Trigger review asynchronously via graycode review run. + // Trigger review asynchronously via hawk review run. go func() { args := []string{"review", "run", req.SHA, "--background"} if req.Model != "" { @@ -100,7 +100,7 @@ func (s *Server) handleReview(w http.ResponseWriter, r *http.Request) { if req.Concerns != "" { args = append(args, "--concerns", req.Concerns) } - _ = exec.CommandContext(context.Background(), "graycode", args...).Run() // #nosec G204 -- binary is fixed "graycode"; args are validated (SHA regex, no "--" prefix, printable charset) + _ = exec.CommandContext(context.Background(), "hawk", args...).Run() // #nosec G204 -- binary is fixed "hawk"; args are validated (SHA regex, no "--" prefix, printable charset) }() resp := ReviewResponse{ @@ -112,8 +112,8 @@ func (s *Server) handleReview(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleReviewStatus(w http.ResponseWriter, _ *http.Request) { - // Run graycode review status and return output. - out, err := exec.CommandContext(context.Background(), "graycode", "review", "status").Output() + // Run hawk review status and return output. + out, err := exec.CommandContext(context.Background(), "hawk", "review", "status").Output() if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return diff --git a/internal/daemon/routes_review_test.go b/internal/daemon/routes_review_test.go index c136f7437..e63635685 100644 --- a/internal/daemon/routes_review_test.go +++ b/internal/daemon/routes_review_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // newReviewTestServer starts a daemon. RegisterReviewRoutes() is already @@ -119,7 +119,7 @@ func TestDaemon_ReviewStatus(t *testing.T) { } defer resp.Body.Close() - // `graycode review status` shells out to the graycode binary; in a sandboxed + // `hawk review status` shells out to the hawk binary; in a sandboxed // test environment that command may not resolve, so the handler's own // 500 branch is just as valid an outcome as a real 200 — both are // well-defined, deterministic behavior we can assert on. diff --git a/internal/daemon/routes_sessions.go b/internal/daemon/routes_sessions.go index 7205da072..40a72d216 100644 --- a/internal/daemon/routes_sessions.go +++ b/internal/daemon/routes_sessions.go @@ -8,8 +8,8 @@ import ( "path/filepath" "strconv" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" ) // handleGetSession handles GET /v1/sessions/{id} — get session detail. diff --git a/internal/daemon/routes_sessions_test.go b/internal/daemon/routes_sessions_test.go index 695f436c3..9b9fa8392 100644 --- a/internal/daemon/routes_sessions_test.go +++ b/internal/daemon/routes_sessions_test.go @@ -6,16 +6,16 @@ import ( "net/http" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" - contracts "github.com/GrayCodeAI/graycode-router/tools" + contracts "github.com/GrayCodeAI/eyrie/tools" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/testutil" ) // saveTestSession isolates session storage to a temp dir and persists a // session with the given messages, returning its ID. func saveTestSession(t *testing.T, id string, messages []session.Message) { t.Helper() - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := &session.Session{ ID: id, @@ -144,7 +144,7 @@ func TestDaemon_GetMessages_NoPaginationParams(t *testing.T) { } func TestDaemon_GetMessages_MissingSession(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, nil) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) @@ -190,7 +190,7 @@ func TestDaemon_DeleteSession_Success(t *testing.T) { } func TestDaemon_DeleteSession_InvalidID(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, nil) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) @@ -211,7 +211,7 @@ func TestDaemon_DeleteSession_InvalidID(t *testing.T) { } func TestDaemon_DeleteSession_NotFound(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, nil) addr := startTestDaemon(t, srv) defer srv.Stop(context.Background()) diff --git a/internal/daemon/routes_stats.go b/internal/daemon/routes_stats.go index b18ac0100..5adacfcc2 100644 --- a/internal/daemon/routes_stats.go +++ b/internal/daemon/routes_stats.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + analytics "github.com/GrayCodeAI/hawk/internal/observability" ) // handleStats handles GET /v1/stats — get aggregated usage statistics. diff --git a/internal/daemon/routes_stats_test.go b/internal/daemon/routes_stats_test.go index 6852e3290..6b154d7ed 100644 --- a/internal/daemon/routes_stats_test.go +++ b/internal/daemon/routes_stats_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + analytics "github.com/GrayCodeAI/hawk/internal/observability" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestDaemon_Stats_Aggregation(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) now := time.Now() traces := []*analytics.SessionTrace{ @@ -79,7 +79,7 @@ func TestDaemon_Stats_Aggregation(t *testing.T) { } func TestDaemon_Stats_DaysParam(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) old := &analytics.SessionTrace{ SessionID: "old", StartTime: time.Now().AddDate(0, 0, -10), Model: "m", MessageCount: 1, @@ -120,7 +120,7 @@ func TestDaemon_Stats_DaysParam(t *testing.T) { } func TestDaemon_Stats_Empty(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) srv := New(Config{Port: 0, Host: testutil.LoopbackHost}, nil) addr := startTestDaemon(t, srv) diff --git a/internal/daemon/routes_status.go b/internal/daemon/routes_status.go index bb96b15d9..9d435b15f 100644 --- a/internal/daemon/routes_status.go +++ b/internal/daemon/routes_status.go @@ -3,14 +3,14 @@ package daemon import ( "net/http" - "github.com/GrayCodeAI/graycode-cli/internal/status" + "github.com/GrayCodeAI/hawk/internal/status" ) // handleStatus returns a process-local, redacted daemon snapshot. It does not // initialize providers or MCP servers and is safe to call during startup. func (s *Server) handleStatus(w http.ResponseWriter, _ *http.Request) { snapshot := status.New() - snapshot.GraycodeVersion = version + snapshot.HawkVersion = version snapshot.Workspace = status.Workspace() if s.startedAt.IsZero() { snapshot.Recovery = "not_started" diff --git a/internal/daemon/slack.go b/internal/daemon/slack.go index eee0661c2..1ac7993d5 100644 --- a/internal/daemon/slack.go +++ b/internal/daemon/slack.go @@ -81,7 +81,7 @@ func newSlackGateway(cfg SlackConfig, daemonAddr, apiKey string, s *Server) *Sla func (g *SlackGateway) Name() string { return "slack" } // setDaemonURL implements daemonURLSetter. Slack forwards to the daemon via -// forwardToGraycode, so it needs the resolved address even though it replies via the +// forwardToHawk, so it needs the resolved address even though it replies via the // Slack Web API. func (g *SlackGateway) setDaemonURL(url string) { g.daemonAddr = url } @@ -167,7 +167,7 @@ func (g *SlackGateway) handleMention(ctx context.Context, ev slackEventInner) { if isPair, ok := g.auth.tryPair(ev.User, text); isPair { if ok { - g.reply(ctx, ev.Channel, threadTS, "Paired. You can now chat with graycode.") + g.reply(ctx, ev.Channel, threadTS, "Paired. You can now chat with hawk.") } else { g.reply(ctx, ev.Channel, threadTS, "Pairing failed: invalid code.") } @@ -178,7 +178,7 @@ func (g *SlackGateway) handleMention(ctx context.Context, ev slackEventInner) { return } - reply, err := forwardToGraycode(ctx, g.client, g.daemonAddr, g.apiKey, text) + reply, err := forwardToHawk(ctx, g.client, g.daemonAddr, g.apiKey, text) if err != nil { reply = fmt.Sprintf("Error: %v", err) } diff --git a/internal/daemon/telegram.go b/internal/daemon/telegram.go index b30fe7936..a816de47b 100644 --- a/internal/daemon/telegram.go +++ b/internal/daemon/telegram.go @@ -1,5 +1,5 @@ -// Package daemon provides a Telegram gateway for graycode. -// Allows users to interact with graycode via Telegram bot messages. +// Package daemon provides a Telegram gateway for hawk. +// Allows users to interact with hawk via Telegram bot messages. package daemon import ( @@ -15,13 +15,13 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/stt" + "github.com/GrayCodeAI/hawk/internal/stt" ) -// TelegramGateway connects graycode to a Telegram bot. +// TelegramGateway connects hawk to a Telegram bot. type TelegramGateway struct { Token string - DaemonAddr string // graycode daemon address to forward messages to + DaemonAddr string // hawk daemon address to forward messages to client *http.Client offset int @@ -188,7 +188,7 @@ func (tg *TelegramGateway) handleMessage(ctx context.Context, msg *TelegramMessa sender := telegramSenderID(msg) if isPair, ok := tg.auth.tryPair(sender, msg.Text); isPair { if ok { - tg.reply(ctx, msg.Chat.ID, "Paired. You can now chat with graycode.") + tg.reply(ctx, msg.Chat.ID, "Paired. You can now chat with hawk.") } else { tg.reply(ctx, msg.Chat.ID, "Pairing failed: invalid code.") } @@ -213,8 +213,8 @@ func (tg *TelegramGateway) handleMessage(ctx context.Context, msg *TelegramMessa } } - // Forward to graycode daemon - response, err := tg.forwardToGraycode(ctx, prompt) + // Forward to hawk daemon + response, err := tg.forwardToHawk(ctx, prompt) if err != nil { response = fmt.Sprintf("Error: %v", err) } @@ -311,7 +311,7 @@ func (tg *TelegramGateway) reply(ctx context.Context, chatID int64, text string) } } -func (tg *TelegramGateway) forwardToGraycode(ctx context.Context, prompt string) (string, error) { +func (tg *TelegramGateway) forwardToHawk(ctx context.Context, prompt string) (string, error) { // Use json.Marshal for safe JSON encoding instead of fmt.Sprintf // with %q, which does not handle all JSON edge cases (e.g., control // characters, surrogate pairs). @@ -337,7 +337,7 @@ func (tg *TelegramGateway) forwardToGraycode(ctx context.Context, prompt string) // Limit response body to 1 MiB to prevent memory exhaustion. body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { - slog.Warn("partial read in forwardToGraycode response", "error", err) + slog.Warn("partial read in forwardToHawk response", "error", err) } var chatResp struct { Response string `json:"response"` diff --git a/internal/daemon/telegram_test.go b/internal/daemon/telegram_test.go index 9d44c57f8..3697c050e 100644 --- a/internal/daemon/telegram_test.go +++ b/internal/daemon/telegram_test.go @@ -28,22 +28,22 @@ func newIPv4TelegramServer(t *testing.T, h http.Handler) *httptest.Server { return srv } -// telegramMockAPI captures sendMessage calls and serves a fake Telegram + graycode +// telegramMockAPI captures sendMessage calls and serves a fake Telegram + hawk // endpoint. The Telegram bot API base is hardcoded in telegram.go, so we instead // drive handleMessage directly and observe outbound sends via a mock daemon. func TestTelegram_HandleMessage_Authorization(t *testing.T) { - // Mock graycode daemon /v1/chat. - graycode := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "graycode-reply"}) + // Mock hawk daemon /v1/chat. + hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) })) - defer graycode.Close() + defer hawk.Close() // Mock Telegram sendMessage endpoint by intercepting via a custom transport. var mu sync.Mutex var sends []string tg := newTelegramGatewayFromConfig( TelegramConfig{Token: "tok", PairingCode: "open"}, - graycode.URL, "apikey", + hawk.URL, "apikey", ) tg.client = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { if strings.Contains(req.URL.Path, "/sendMessage") { @@ -53,7 +53,7 @@ func TestTelegram_HandleMessage_Authorization(t *testing.T) { mu.Unlock() return jsonResp(`{"ok":true}`), nil } - // forward-to-graycode goes to the real mock server; let it through. + // forward-to-hawk goes to the real mock server; let it through. return http.DefaultTransport.RoundTrip(req) })} @@ -64,13 +64,13 @@ func TestTelegram_HandleMessage_Authorization(t *testing.T) { return m } - // 1. Unauthorized non-pair message -> "Unauthorized" reply, no graycode call. + // 1. Unauthorized non-pair message -> "Unauthorized" reply, no hawk call. tg.handleMessage(context.Background(), mkMsg("hello")) // 2. Wrong pairing code -> failure reply. tg.handleMessage(context.Background(), mkMsg("/pair wrong")) // 3. Correct pairing code -> paired reply. tg.handleMessage(context.Background(), mkMsg("/pair open")) - // 4. Now authorized -> graycode reply forwarded. + // 4. Now authorized -> hawk reply forwarded. tg.handleMessage(context.Background(), mkMsg("do something")) mu.Lock() @@ -87,8 +87,8 @@ func TestTelegram_HandleMessage_Authorization(t *testing.T) { if !strings.Contains(sends[2], "Paired") { t.Errorf("send[2]=%q want Paired", sends[2]) } - if sends[3] != "graycode-reply" { - t.Errorf("send[3]=%q want graycode-reply", sends[3]) + if sends[3] != "hawk-reply" { + t.Errorf("send[3]=%q want hawk-reply", sends[3]) } if !tg.auth.allowed("alice") { t.Errorf("alice should be allowed after pairing") @@ -112,16 +112,16 @@ func TestTelegramSenderID(t *testing.T) { func TestTelegram_BareConstructorFailsClosed(t *testing.T) { // The bare constructor seeds an empty authorizer, so it must refuse all - // senders (no pairing code / allowlist) rather than forwarding to graycode. - graycodeCalled := false - graycode := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - graycodeCalled = true + // senders (no pairing code / allowlist) rather than forwarding to hawk. + hawkCalled := false + hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hawkCalled = true writeJSON(w, http.StatusOK, ChatResponse{Response: "ok"}) })) - defer graycode.Close() + defer hawk.Close() var sends []string - tg := NewTelegramGateway("tok", graycode.URL) + tg := NewTelegramGateway("tok", hawk.URL) tg.client = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { if strings.Contains(req.URL.Path, "/sendMessage") { _ = req.ParseForm() @@ -137,8 +137,8 @@ func TestTelegram_BareConstructorFailsClosed(t *testing.T) { if len(sends) != 1 || !strings.Contains(sends[0], "Unauthorized") { t.Fatalf("expected an Unauthorized reply, got %v", sends) } - if graycodeCalled { - t.Fatal("bare constructor must not forward unauthorized messages to graycode") + if hawkCalled { + t.Fatal("bare constructor must not forward unauthorized messages to hawk") } } @@ -166,14 +166,14 @@ func TestTelegramSafeFileName(t *testing.T) { } func TestTelegramVoiceWithoutTranscriberFallsBackToText(t *testing.T) { - graycode := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "graycode-reply"}) + hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) })) - defer graycode.Close() + defer hawk.Close() // No STT transcriber installed: a voice message must fall back to the // existing text path (transcribeAudio returns "" with no reply). - tg := newTelegramGatewayFromConfig(TelegramConfig{Token: "tok", AllowList: []string{"user"}}, graycode.URL, "k") + tg := newTelegramGatewayFromConfig(TelegramConfig{Token: "tok", AllowList: []string{"user"}}, hawk.URL, "k") tg.handleMessage(context.Background(), &TelegramMessage{ Text: "hello", Chat: struct { diff --git a/internal/deps/deps.go b/internal/deps/deps.go index fb1b128fc..7e594fc9f 100644 --- a/internal/deps/deps.go +++ b/internal/deps/deps.go @@ -1,4 +1,4 @@ -// Package deps documents the dependency injection pattern used across graycode. +// Package deps documents the dependency injection pattern used across hawk. // // # Pattern // diff --git a/internal/diffsandbox/sandbox.go b/internal/diffsandbox/sandbox.go index b2e09dd33..42485880c 100644 --- a/internal/diffsandbox/sandbox.go +++ b/internal/diffsandbox/sandbox.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/theme" + "github.com/GrayCodeAI/hawk/internal/theme" ) // ChangeType identifies the kind of file modification. diff --git a/internal/engine/adaptive_prompt.go b/internal/engine/adaptive_prompt.go index 2546d3578..4d54dcdf4 100644 --- a/internal/engine/adaptive_prompt.go +++ b/internal/engine/adaptive_prompt.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/textutil" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/textutil" ) // AdaptivePrompt adjusts system prompt sections based on user corrections. @@ -32,7 +32,7 @@ type PromptAdjustment struct { LastUsed time.Time `json:"last_used"` } -// NewAdaptivePrompt creates an adaptive prompt backed by Graycode user state. +// NewAdaptivePrompt creates an adaptive prompt backed by Hawk user state. func NewAdaptivePrompt() *AdaptivePrompt { ap := &AdaptivePrompt{ path: filepath.Join(storage.StateDir(), "adaptive_prompt.json"), diff --git a/internal/engine/adaptive_prompt_test.go b/internal/engine/adaptive_prompt_test.go index 2d0899552..bd0e8da3a 100644 --- a/internal/engine/adaptive_prompt_test.go +++ b/internal/engine/adaptive_prompt_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestAdaptivePrompt_New(t *testing.T) { diff --git a/internal/engine/adaptive_system_prompt.go b/internal/engine/adaptive_system_prompt.go index 24be93bd4..bc21fc519 100644 --- a/internal/engine/adaptive_system_prompt.go +++ b/internal/engine/adaptive_system_prompt.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // PromptBuildContext provides situational context for building a system prompt. @@ -266,12 +266,12 @@ func DiffPrompts(old, new string) string { return strings.TrimRight(result, "\n") } -// DefaultSections returns the built-in prompt sections that graycode uses. +// DefaultSections returns the built-in prompt sections that hawk uses. func DefaultSections(ctx PromptBuildContext) []PromptSection { sections := []PromptSection{ { Name: "identity", - Content: "You are graycode, an AI coding agent. You help developers write, debug, review, and refactor code. You operate inside the user's repository with access to tools for file manipulation, shell commands, and code search.", + Content: "You are hawk, an AI coding agent. You help developers write, debug, review, and refactor code. You operate inside the user's repository with access to tools for file manipulation, shell commands, and code search.", Priority: 1, }, { diff --git a/internal/engine/adaptive_system_prompt_test.go b/internal/engine/adaptive_system_prompt_test.go index a139cca9d..11a5fd876 100644 --- a/internal/engine/adaptive_system_prompt_test.go +++ b/internal/engine/adaptive_system_prompt_test.go @@ -5,7 +5,7 @@ import ( "sync" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) func TestNewSystemPromptBuilder(t *testing.T) { @@ -24,7 +24,7 @@ func TestNewSystemPromptBuilder(t *testing.T) { func TestAddSection(t *testing.T) { b := NewSystemPromptBuilder("", 5000) - b.AddSection(PromptSection{Name: "identity", Content: "You are graycode.", Priority: 1}) + b.AddSection(PromptSection{Name: "identity", Content: "You are hawk.", Priority: 1}) b.AddSection(PromptSection{Name: "safety", Content: "Be safe.", Priority: 1}) if len(b.Sections) != 2 { @@ -32,11 +32,11 @@ func TestAddSection(t *testing.T) { } // Adding same name replaces - b.AddSection(PromptSection{Name: "identity", Content: "You are graycode v2.", Priority: 1}) + b.AddSection(PromptSection{Name: "identity", Content: "You are hawk v2.", Priority: 1}) if len(b.Sections) != 2 { t.Fatalf("expected 2 sections after replace, got %d", len(b.Sections)) } - if b.Sections[0].Content != "You are graycode v2." { + if b.Sections[0].Content != "You are hawk v2." { t.Errorf("expected updated content, got %q", b.Sections[0].Content) } } @@ -302,7 +302,7 @@ func TestEstimateStringTokens(t *testing.T) { func TestFormatPrompt(t *testing.T) { sections := []PromptSection{ - {Name: "identity", Content: "You are graycode."}, + {Name: "identity", Content: "You are hawk."}, {Name: "safety", Content: "Be safe."}, } @@ -317,7 +317,7 @@ func TestFormatPrompt(t *testing.T) { if !strings.Contains(result, "## safety") { t.Error("expected safety section header") } - if !strings.Contains(result, "You are graycode.") { + if !strings.Contains(result, "You are hawk.") { t.Error("expected identity content") } if !strings.Contains(result, "Be safe.") { diff --git a/internal/engine/agent/background_agent.go b/internal/engine/agent/background_agent.go index 016eed4fa..a34dc537b 100644 --- a/internal/engine/agent/background_agent.go +++ b/internal/engine/agent/background_agent.go @@ -6,9 +6,9 @@ import ( "sync" "time" - agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" + agentcontracts "github.com/GrayCodeAI/hawk/internal/contracts/agent" - "github.com/GrayCodeAI/graycode-cli/internal/taskruntime" + "github.com/GrayCodeAI/hawk/internal/taskruntime" ) // BackgroundAgentPool manages async sub-agents that run in the background. diff --git a/internal/engine/agent_reexports.go b/internal/engine/agent_reexports.go index 784217868..a0903fa48 100644 --- a/internal/engine/agent_reexports.go +++ b/internal/engine/agent_reexports.go @@ -4,7 +4,7 @@ package engine import ( - "github.com/GrayCodeAI/graycode-cli/internal/engine/agent" + "github.com/GrayCodeAI/hawk/internal/engine/agent" ) type ( diff --git a/internal/engine/agent_resume_test.go b/internal/engine/agent_resume_test.go index 380d6c428..549ad438d 100644 --- a/internal/engine/agent_resume_test.go +++ b/internal/engine/agent_resume_test.go @@ -3,10 +3,10 @@ package engine import ( "testing" - agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + agentcontracts "github.com/GrayCodeAI/hawk/internal/contracts/agent" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestSubAgentResume_ReplaysTranscriptMessages(t *testing.T) { diff --git a/internal/engine/agent_session_tool.go b/internal/engine/agent_session_tool.go index 85f1f2626..fa2349220 100644 --- a/internal/engine/agent_session_tool.go +++ b/internal/engine/agent_session_tool.go @@ -7,16 +7,16 @@ import ( "strings" "time" - agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" + agentcontracts "github.com/GrayCodeAI/hawk/internal/contracts/agent" - engagent "github.com/GrayCodeAI/graycode-cli/internal/engine/agent" - "github.com/GrayCodeAI/graycode-cli/internal/eventlog" - "github.com/GrayCodeAI/graycode-cli/internal/gitworktree" - "github.com/GrayCodeAI/graycode-cli/internal/hooks" - "github.com/GrayCodeAI/graycode-cli/internal/prompts" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + engagent "github.com/GrayCodeAI/hawk/internal/engine/agent" + "github.com/GrayCodeAI/hawk/internal/eventlog" + "github.com/GrayCodeAI/hawk/internal/gitworktree" + "github.com/GrayCodeAI/hawk/internal/hooks" + "github.com/GrayCodeAI/hawk/internal/prompts" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/tool" ) // WireAgentTool sets up typed sub-agent spawning via SpawnController. @@ -55,7 +55,7 @@ func (s *Session) spawnSubAgentRequest(ctx context.Context, req agentcontracts.S Agent: string(mode), Depth: depth, Mode: "one-shot", - Provider: "graycode", + Provider: "hawk", Label: truncateSummary(norm.Prompt, 200), AgentProvider: s.ChatLLM().Provider(), AgentModel: s.ChatLLM().Model(), diff --git a/internal/engine/arc_test.go b/internal/engine/arc_test.go index 2322c00be..90a74418a 100644 --- a/internal/engine/arc_test.go +++ b/internal/engine/arc_test.go @@ -3,7 +3,7 @@ package engine import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/conversationarc" + "github.com/GrayCodeAI/hawk/internal/conversationarc" ) func TestSessionArcAccessors(t *testing.T) { diff --git a/internal/engine/architect.go b/internal/engine/architect.go index befcf6133..347d3c864 100644 --- a/internal/engine/architect.go +++ b/internal/engine/architect.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/planning" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/planning" + "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/types" ) // ArchitectConfig configures the two-model architect/editor pipeline. @@ -42,7 +42,7 @@ type PlanStep struct { } // Message is a lightweight chat message used by the architect pipeline. -// This avoids coupling to external message types from graycode-router. +// This avoids coupling to external message types from eyrie. type ArchitectMessage struct { Role string // "system", "user", "assistant" Content string @@ -133,7 +133,7 @@ type architectProvider struct { a *Architect } -func (p *architectProvider) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (p *architectProvider) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { archMsgs := make([]ArchitectMessage, len(messages)) for i, m := range messages { archMsgs[i] = ArchitectMessage{Role: m.Role, Content: m.Content} @@ -146,10 +146,10 @@ func (p *architectProvider) Chat(ctx context.Context, messages []types.GraycodeR if err != nil { return nil, err } - return &types.GraycodeRouterResponse{Content: out}, nil + return &types.EyrieResponse{Content: out}, nil } -func (p *architectProvider) StreamChat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (p *architectProvider) StreamChat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { return nil, nil } func (p *architectProvider) Ping(ctx context.Context) error { return nil } diff --git a/internal/engine/assumptions.go b/internal/engine/assumptions.go index 61a3cfb94..1c1f7bdab 100644 --- a/internal/engine/assumptions.go +++ b/internal/engine/assumptions.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // AssumptionStatus tracks whether an assumption has been verified. diff --git a/internal/engine/async/engine.go b/internal/engine/async/engine.go index 9ee719c40..4b0aa1330 100644 --- a/internal/engine/async/engine.go +++ b/internal/engine/async/engine.go @@ -8,7 +8,7 @@ import ( "github.com/google/uuid" - "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/hawk/internal/engine" ) // Engine wraps an engine.Session with queue-based async operation. diff --git a/internal/engine/async/engine_test.go b/internal/engine/async/engine_test.go index 3b14bc90a..909eca0a3 100644 --- a/internal/engine/async/engine_test.go +++ b/internal/engine/async/engine_test.go @@ -6,31 +6,31 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/types" ) // fakeClient is a scripted ChatClient for exercising the engine loop. type fakeClient struct { mu sync.Mutex - script []types.GraycodeRouterStreamEvent + script []types.EyrieStreamEvent streamStarted chan struct{} streamCtx context.Context } -func newFakeClient(events ...types.GraycodeRouterStreamEvent) *fakeClient { +func newFakeClient(events ...types.EyrieStreamEvent) *fakeClient { return &fakeClient{ script: events, streamStarted: make(chan struct{}), } } -func (f *fakeClient) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { - return &types.GraycodeRouterResponse{Content: "mock", FinishReason: "end_turn"}, nil +func (f *fakeClient) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { + return &types.EyrieResponse{Content: "mock", FinishReason: "end_turn"}, nil } -func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { +func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { f.mu.Lock() f.streamCtx = ctx f.mu.Unlock() @@ -40,7 +40,7 @@ func (f *fakeClient) StreamChatContinue(ctx context.Context, messages []types.Gr close(f.streamStarted) } - ch := make(chan types.GraycodeRouterStreamEvent, 16) + ch := make(chan types.EyrieStreamEvent, 16) go func() { defer close(ch) for _, evt := range f.script { @@ -63,11 +63,11 @@ type cancelTurns struct { call int } -func (c *cancelTurns) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { - return &types.GraycodeRouterResponse{Content: "mock", FinishReason: "end_turn"}, nil +func (c *cancelTurns) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { + return &types.EyrieResponse{Content: "mock", FinishReason: "end_turn"}, nil } -func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { +func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { select { case <-c.streamStarted: default: @@ -78,8 +78,8 @@ func (c *cancelTurns) StreamChatContinue(ctx context.Context, messages []types.G first := c.call == 1 c.mu.Unlock() - ch := make(chan types.GraycodeRouterStreamEvent, 4) - ch <- types.GraycodeRouterStreamEvent{Type: "content", Content: "thinking..."} + ch := make(chan types.EyrieStreamEvent, 4) + ch <- types.EyrieStreamEvent{Type: "content", Content: "thinking..."} if first { go func() { <-ctx.Done() @@ -118,9 +118,9 @@ func drainEvents(t *testing.T, ch <-chan *Event) []*Event { func TestEngineTurnEmitsTerminalDone(t *testing.T) { client := newFakeClient( - types.GraycodeRouterStreamEvent{Type: "content", Content: "hello"}, - types.GraycodeRouterStreamEvent{Type: "usage", Usage: &types.GraycodeRouterUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}}, - types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}, + types.EyrieStreamEvent{Type: "content", Content: "hello"}, + types.EyrieStreamEvent{Type: "usage", Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}}, + types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}, ) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -151,7 +151,7 @@ func TestEngineTurnEmitsTerminalDone(t *testing.T) { } func TestEngineStopThenStartProcessesAgain(t *testing.T) { - client := newFakeClient(types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) + client := newFakeClient(types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -211,7 +211,7 @@ func TestEngineCancelAbortsTurnAndKeepsRunning(t *testing.T) { func TestEngineStreamErrorEmitsErrorThenDone(t *testing.T) { client := newFakeClient( - types.GraycodeRouterStreamEvent{Type: "error", Error: "provider exploded"}, + types.EyrieStreamEvent{Type: "error", Error: "provider exploded"}, ) e := New(newTestSession(t, client)) e.Start(context.Background()) @@ -238,7 +238,7 @@ func TestEngineStreamErrorEmitsErrorThenDone(t *testing.T) { } func TestEngineReplyToReceivesTerminalEvent(t *testing.T) { - client := newFakeClient(types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn"}) + client := newFakeClient(types.EyrieStreamEvent{Type: "done", StopReason: "end_turn"}) e := New(newTestSession(t, client)) e.Start(context.Background()) defer e.Stop() diff --git a/internal/engine/auto_commit.go b/internal/engine/auto_commit.go index 481bdb820..1f813ccb6 100644 --- a/internal/engine/auto_commit.go +++ b/internal/engine/auto_commit.go @@ -81,7 +81,7 @@ func (ac *AutoCommitter) generateMessage(description string) string { if len(description) > 72 { description = description[:69] + "..." } - return "graycode: " + description + return "hawk: " + description } - return fmt.Sprintf("graycode: auto-commit %s", time.Now().Format("15:04:05")) + return fmt.Sprintf("hawk: auto-commit %s", time.Now().Format("15:04:05")) } diff --git a/internal/engine/auto_commit_wire_test.go b/internal/engine/auto_commit_wire_test.go index cae7685f9..0a4765ba0 100644 --- a/internal/engine/auto_commit_wire_test.go +++ b/internal/engine/auto_commit_wire_test.go @@ -7,8 +7,8 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/types" ) // autoCommitCaptureTool records ToolContext.AutoCommit for the wire path. diff --git a/internal/engine/background_runner.go b/internal/engine/background_runner.go index 135719fd7..23587b770 100644 --- a/internal/engine/background_runner.go +++ b/internal/engine/background_runner.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" + agentcontracts "github.com/GrayCodeAI/hawk/internal/contracts/agent" - "github.com/GrayCodeAI/graycode-cli/internal/taskruntime" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/taskruntime" + "github.com/GrayCodeAI/hawk/internal/tool" ) // BackgroundTask represents an async subagent task running in the background. diff --git a/internal/engine/branching/cascade.go b/internal/engine/branching/cascade.go index eade670f9..d6e5c0fd7 100644 --- a/internal/engine/branching/cascade.go +++ b/internal/engine/branching/cascade.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine/cost" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/engine/cost" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // CascadeRouter selects the optimal model for each request based on task complexity. @@ -211,7 +211,7 @@ func classifyPrompt(prompt string) string { } // modelForTask maps a task type to the appropriate model using configured roles -// and graycode-router catalog tier defaults. +// and eyrie catalog tier defaults. func (cr *CascadeRouter) modelForTask(taskType string, frugal bool) string { tier := routing.SuggestTierForTask(taskType) @@ -250,7 +250,7 @@ func (cr *CascadeRouter) modelForTask(taskType string, frugal bool) string { } } -// defaultFor returns the best model for a given cost tier via graycode-router catalog tier defaults. +// defaultFor returns the best model for a given cost tier via eyrie catalog tier defaults. func (cr *CascadeRouter) defaultFor(tier ModelTier) string { provider := "" if info, ok := routing.Find(cr.DefaultModel); ok { diff --git a/internal/engine/branching/cascade_test.go b/internal/engine/branching/cascade_test.go index c5ea314c9..5602674e9 100644 --- a/internal/engine/branching/cascade_test.go +++ b/internal/engine/branching/cascade_test.go @@ -3,12 +3,12 @@ package branching import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) const testProvider = "anthropic" -// testTierModels loads haiku/sonnet/opus model IDs from graycode-router's catalog (not hardcoded). +// testTierModels loads haiku/sonnet/opus model IDs from eyrie's catalog (not hardcoded). func testTierModels(t *testing.T, provider string) (haiku, sonnet, opus string) { t.Helper() haiku = routing.PreferredModelForTier(provider, routing.TierHaiku, "") diff --git a/internal/engine/branching/shadow.go b/internal/engine/branching/shadow.go index 32cb5fe18..fad7c38fa 100644 --- a/internal/engine/branching/shadow.go +++ b/internal/engine/branching/shadow.go @@ -28,7 +28,7 @@ type ShadowWorkspace struct { // NewShadowWorkspace creates a new temporary directory for shadow validation. func NewShadowWorkspace() (*ShadowWorkspace, error) { - dir, err := os.MkdirTemp("", "graycode-shadow-*") + dir, err := os.MkdirTemp("", "hawk-shadow-*") if err != nil { return nil, fmt.Errorf("shadow workspace: create temp dir: %w", err) } diff --git a/internal/engine/cache_gate.go b/internal/engine/cache_gate.go index 185df71dc..82ea78ed5 100644 --- a/internal/engine/cache_gate.go +++ b/internal/engine/cache_gate.go @@ -6,4 +6,4 @@ package engine // cache_planner.go (planCache / cacheDecision) implements this arithmetic: // segments the stable prefix, computes breakpoints, and enables caching only // when the expected reuse count beats the write premium. Full wire-format -// lowering and fleet-wide key-sharding remain graycode-router-side. +// lowering and fleet-wide key-sharding remain eyrie-side. diff --git a/internal/engine/cache_gate_test.go b/internal/engine/cache_gate_test.go index fcb52f624..96ff6ea95 100644 --- a/internal/engine/cache_gate_test.go +++ b/internal/engine/cache_gate_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestCacheDecisionNonAnthropicOff(t *testing.T) { @@ -31,7 +31,7 @@ func TestCacheDecisionLargePrefixOn(t *testing.T) { } func TestCacheDecisionCountsToolCatalog(t *testing.T) { - tools := []types.GraycodeRouterTool{ + tools := []types.EyrieTool{ {Name: "t1", Description: strings.Repeat("d", 9000), Parameters: map[string]interface{}{"type": "object"}}, } sys := "small" diff --git a/internal/engine/cache_planner.go b/internal/engine/cache_planner.go index be8c27083..f9640ace4 100644 --- a/internal/engine/cache_planner.go +++ b/internal/engine/cache_planner.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // Prompt-cache segment planning, extending the caveman-style break-even gate @@ -12,7 +12,7 @@ import ( // catalog) is split into cacheable segments, breakpoints are computed at the // last tool and last system boundaries, and the whole thing is enabled only // when the provider economics pay off for the expected reuse count. Full -// wire-format lowering and key sharding across a fleet remain graycode-router-side; +// wire-format lowering and key sharding across a fleet remain eyrie-side; // this is the deterministic client planner. // Anthropic 5m cache economics: uncached = 1.0x, cache write = 1.25x, @@ -52,7 +52,7 @@ type CachePlan struct { // planCache computes whether and how to cache the stable prefix for a call. // Deterministic and pure so it can be tested without a provider connection. -func planCache(provider, systemPrompt string, tools []types.GraycodeRouterTool, expectedReuse int) CachePlan { +func planCache(provider, systemPrompt string, tools []types.EyrieTool, expectedReuse int) CachePlan { if expectedReuse <= 0 { expectedReuse = cacheDefaultReuse } @@ -112,6 +112,6 @@ func planCache(provider, systemPrompt string, tools []types.GraycodeRouterTool, // cacheDecision reports whether to request provider-native caching, delegating // to the planner at the default reuse. Kept for backward compatibility. -func cacheDecision(provider, systemPrompt string, tools []types.GraycodeRouterTool) bool { +func cacheDecision(provider, systemPrompt string, tools []types.EyrieTool) bool { return planCache(provider, systemPrompt, tools, cacheDefaultReuse).Enabled } diff --git a/internal/engine/cache_planner_test.go b/internal/engine/cache_planner_test.go index 2efbf18c6..65316cedf 100644 --- a/internal/engine/cache_planner_test.go +++ b/internal/engine/cache_planner_test.go @@ -4,14 +4,14 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func bigSys(n int) string { return strings.Repeat("s", n) } func TestPlanCacheSegmentsAndBreakpoints(t *testing.T) { sys := bigSys(cacheMinPrefixBytes + 2048) - tools := []types.GraycodeRouterTool{{Name: "t", Description: strings.Repeat("d", 4096), Parameters: map[string]interface{}{"type": "object"}}} + tools := []types.EyrieTool{{Name: "t", Description: strings.Repeat("d", 4096), Parameters: map[string]interface{}{"type": "object"}}} p := planCache("anthropic", sys, tools, 2) if !p.Enabled { t.Fatalf("expected enabled, reason=%q", p.Reason) diff --git a/internal/engine/chat_provider.go b/internal/engine/chat_provider.go index 6a3c9874a..8e5af4b79 100644 --- a/internal/engine/chat_provider.go +++ b/internal/engine/chat_provider.go @@ -5,13 +5,13 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/hawk/internal/types" ) -// BuildChatProvider adapts Graycode's engine-backed session client to the smaller +// BuildChatProvider adapts Hawk's engine-backed session client to the smaller // provider contract used by host integrations such as Kestrel. Model resolution, -// credentials, routing, and transport remain owned by GraycodeRouter's engine facade. +// credentials, routing, and transport remain owned by Eyrie's engine facade. func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyProvider string) (types.ChatProvider, string, error) { client, provider, _, err := BuildChatClient(ctx, selection, legacyProvider) if err != nil { @@ -19,7 +19,7 @@ func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyP } provider = strings.TrimSpace(provider) if provider == "" { - return nil, "", fmt.Errorf("graycode-router transport: provider unavailable") + return nil, "", fmt.Errorf("eyrie transport: provider unavailable") } return &engineChatProvider{ client: client, @@ -28,7 +28,7 @@ func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyP }, provider, nil } -// engineChatProvider keeps compatibility-only provider behavior at Graycode's +// engineChatProvider keeps compatibility-only provider behavior at Hawk's // integration edge while all generation goes through the engine ChatClient. type engineChatProvider struct { client ChatClient @@ -38,7 +38,7 @@ type engineChatProvider struct { var _ types.ChatProvider = (*engineChatProvider)(nil) -func (p *engineChatProvider) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (p *engineChatProvider) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { if strings.TrimSpace(opts.Provider) == "" { opts.Provider = p.provider } @@ -48,7 +48,7 @@ func (p *engineChatProvider) Chat(ctx context.Context, messages []types.Graycode return p.client.Chat(ctx, messages, opts) } -func (p *engineChatProvider) StreamChat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.StreamResult, error) { +func (p *engineChatProvider) StreamChat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { if strings.TrimSpace(opts.Provider) == "" { opts.Provider = p.provider } diff --git a/internal/engine/chat_provider_test.go b/internal/engine/chat_provider_test.go index 7bf5fe02a..2fcfc67cd 100644 --- a/internal/engine/chat_provider_test.go +++ b/internal/engine/chat_provider_test.go @@ -5,8 +5,8 @@ import ( "errors" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/graycode-router/llm" + "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/hawk/internal/types" ) type recordingChatClient struct { @@ -14,14 +14,14 @@ type recordingChatClient struct { streamOptions types.ChatOptions } -func (c *recordingChatClient) Chat(_ context.Context, _ []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (c *recordingChatClient) Chat(_ context.Context, _ []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { c.chatOptions = opts - return &types.GraycodeRouterResponse{Content: "ok"}, nil + return &types.EyrieResponse{Content: "ok"}, nil } -func (c *recordingChatClient) StreamChatContinue(_ context.Context, _ []types.GraycodeRouterMessage, opts types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (c *recordingChatClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, opts types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.streamOptions = opts - events := make(chan types.GraycodeRouterStreamEvent) + events := make(chan types.EyrieStreamEvent) close(events) return llm.NewStreamResult(events, "", nil), nil } diff --git a/internal/engine/chat_replay.go b/internal/engine/chat_replay.go index 228b8e514..c0331a6cb 100644 --- a/internal/engine/chat_replay.go +++ b/internal/engine/chat_replay.go @@ -4,29 +4,29 @@ import ( "context" "os" - "github.com/GrayCodeAI/graycode-cli/internal/replaycache" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/replaycache" + "github.com/GrayCodeAI/hawk/internal/types" ) // replayCacheDirEnv opts a run into the disk-persisted replay cache: when set, // every non-streaming completion is looked up by its canonicalized request and // replayed from disk on a hit, giving deterministic, offline regression runs. // Unset (the default) leaves the chat path untouched. -const replayCacheDirEnv = "GRAYCODE_REPLAY_CACHE_DIR" +const replayCacheDirEnv = "HAWK_REPLAY_CACHE_DIR" // replayFingerprintEnv optionally folds an extra string (e.g. a fixture // version) into replay cache keys so whole suites can be invalidated at once. -const replayFingerprintEnv = "GRAYCODE_REPLAY_FINGERPRINT" +const replayFingerprintEnv = "HAWK_REPLAY_FINGERPRINT" // replayKey builds the cache key for one completion request. -func replayKey(opts types.ChatOptions, messages []types.GraycodeRouterMessage) string { +func replayKey(opts types.ChatOptions, messages []types.EyrieMessage) string { return replaycache.Key(replaycache.Fingerprint(os.Getenv(replayFingerprintEnv)), opts.Provider, opts.Model, messages, opts.MaxTokens) } // chatWithReplay wraps client.Chat with the replay cache when -// GRAYCODE_REPLAY_CACHE_DIR is set; otherwise it calls straight through. -func chatWithReplay(ctx context.Context, client ChatClient, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { +// HAWK_REPLAY_CACHE_DIR is set; otherwise it calls straight through. +func chatWithReplay(ctx context.Context, client ChatClient, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { dir := os.Getenv(replayCacheDirEnv) if dir == "" { return client.Chat(ctx, messages, opts) diff --git a/internal/engine/chat_replay_test.go b/internal/engine/chat_replay_test.go index 640466b10..308e6d366 100644 --- a/internal/engine/chat_replay_test.go +++ b/internal/engine/chat_replay_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestChatWithReplayDisabledPassesThrough(t *testing.T) { @@ -14,7 +14,7 @@ func TestChatWithReplayDisabledPassesThrough(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} resp, err := chatWithReplay(context.Background(), client, - []types.GraycodeRouterMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{Model: "m"}) + []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{Model: "m"}) if err != nil || resp.Content != "live" { t.Fatalf("resp=%v err=%v", resp, err) } @@ -29,7 +29,7 @@ func TestChatWithReplayCachesAndHits(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} - msgs := []types.GraycodeRouterMessage{{Role: "user", Content: "deterministic"}} + msgs := []types.EyrieMessage{{Role: "user", Content: "deterministic"}} opts := types.ChatOptions{Provider: "p", Model: "m", MaxTokens: 10} first, err := chatWithReplay(context.Background(), client, msgs, opts) @@ -57,7 +57,7 @@ func TestChatWithReplayFingerprintInvalidates(t *testing.T) { calls := 0 client := &countingClient{n: &calls, resp: "live"} - msgs := []types.GraycodeRouterMessage{{Role: "user", Content: "x"}} + msgs := []types.EyrieMessage{{Role: "user", Content: "x"}} opts := types.ChatOptions{Provider: "p", Model: "m"} if _, err := chatWithReplay(context.Background(), client, msgs, opts); err != nil { @@ -80,7 +80,7 @@ func TestChatWithReplayDoesNotCacheErrors(t *testing.T) { client := &countingClient{n: &calls, err: errors.New("boom")} for i := 0; i < 2; i++ { if _, err := chatWithReplay(context.Background(), client, - []types.GraycodeRouterMessage{{Role: "user", Content: "e"}}, types.ChatOptions{}); err == nil { + []types.EyrieMessage{{Role: "user", Content: "e"}}, types.ChatOptions{}); err == nil { t.Fatal("expected error to pass through") } } @@ -99,20 +99,20 @@ type countingClient struct { err error } -func (c *countingClient) Chat(context.Context, []types.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (c *countingClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { *c.n++ if c.err != nil { return nil, c.err } - return &types.GraycodeRouterResponse{Content: c.resp}, nil + return &types.EyrieResponse{Content: c.resp}, nil } -func (c *countingClient) StreamChatContinue(context.Context, []types.GraycodeRouterMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *countingClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { *c.n++ if c.err != nil { return nil, c.err } - ch := make(chan types.GraycodeRouterStreamEvent, 1) - ch <- types.GraycodeRouterStreamEvent{Type: "done"} + ch := make(chan types.EyrieStreamEvent, 1) + ch <- types.EyrieStreamEvent{Type: "done"} return &types.StreamResult{Events: ch}, nil } diff --git a/internal/engine/chat_service.go b/internal/engine/chat_service.go index 402dbaecb..25392ceeb 100644 --- a/internal/engine/chat_service.go +++ b/internal/engine/chat_service.go @@ -7,16 +7,16 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/observability/metrics" - "github.com/GrayCodeAI/graycode-cli/internal/resilience/ratelimit" - "github.com/GrayCodeAI/graycode-cli/internal/resilience/retry" - "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/graycode-router/engine" + "github.com/GrayCodeAI/eyrie/engine" + "github.com/GrayCodeAI/hawk/internal/observability/metrics" + "github.com/GrayCodeAI/hawk/internal/resilience/ratelimit" + "github.com/GrayCodeAI/hawk/internal/resilience/retry" + "github.com/GrayCodeAI/hawk/internal/types" ) // ChatService is the Session's view of the LLM transport. It owns the -// GraycodeRouter client, provider/model identity, and compatibility-only rate/retry -// controls. Production facade clients own resilience inside GraycodeRouter. The service +// Eyrie client, provider/model identity, and compatibility-only rate/retry +// controls. Production facade clients own resilience inside Eyrie. The service // is constructed once in NewSessionWithClient and consulted by // agentLoop every turn. // @@ -26,13 +26,13 @@ import ( // plan. type ChatService struct { mu sync.RWMutex - // client is the graycode-router transport. Always non-nil after construction. + // client is the eyrie transport. Always non-nil after construction. client ChatClient // provider / model are the active LLM identity. provider string model string // deploymentRouting is true when the client is catalog-backed - // (e.g. DeploymentRouter from graycode-router/runtime.ChatProvider). + // (e.g. DeploymentRouter from eyrie/runtime.ChatProvider). deploymentRouting bool // rateLimiter is the per-session token bucket. rateLimiter *ratelimit.Limiter @@ -43,7 +43,7 @@ type ChatService struct { // contCfg is the continuation config for StreamChatContinue. contCfg types.ContinuationConfig // outputSchema, when non-empty, requests a JSON-schema-constrained - // response. Plumbed into graycode-router's ChatOptions.ResponseFormat. + // response. Plumbed into eyrie's ChatOptions.ResponseFormat. outputSchema string // thinkingEnabled is the generic host preference for provider thinking / // reasoning toggles (Z.AI, LongCat, Agnes, …). nil leaves provider default. @@ -99,7 +99,7 @@ func NewChatService(client ChatClient, cfg ChatServiceConfig) *ChatService { } } -// Client returns the underlying graycode-router client. Exposed for callers (e.g. +// Client returns the underlying eyrie client. Exposed for callers (e.g. // background goroutines) that need to issue one-off LLM calls without // the agent-loop retry wrapper. func (c *ChatService) Client() ChatClient { @@ -185,7 +185,7 @@ func (c *ChatService) Reattach(client ChatClient, provider string) { // BuildOptions constructs a types.ChatOptions for an outgoing LLM call, // encoding all the knobs the agent loop needs (system prompt, model, // max tokens, tools, structured output, etc.). -func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens int, tools []types.GraycodeRouterTool) types.ChatOptions { +func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens int, tools []types.EyrieTool) types.ChatOptions { c.mu.RLock() provider := c.provider thinkingEnabled := c.thinkingEnabled @@ -207,25 +207,25 @@ func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens i if outputSchema != "" { opts.ResponseFormat = &types.ResponseFormat{Type: "json_schema", Schema: outputSchema} } - // Opt-in tool-catalog compression (GRAYCODE_TOOL_SHRINK=1): fail-open, so the + // Opt-in tool-catalog compression (HAWK_TOOL_SHRINK=1): fail-open, so the // returned tools equal the input whenever anything is off or drifts. - opts.Tools = shrinkGraycodeRouterTools(opts.Tools) + opts.Tools = shrinkEyrieTools(opts.Tools) return opts } // Stream issues a streaming LLM call with retry, rate-limit, and // emergency-compact. The returned *types.StreamResult's Events channel -// emits GraycodeRouterStreamEvent values; the caller must Close() the result +// emits EyrieStreamEvent values; the caller must Close() the result // when done. // // On context cancellation mid-call, returns the cancellation error wrapped // with whatever partial state the upstream had emitted (caller should // check ctx.Err()). // -// GraycodeRouter facade clients advertise that they manage provider resilience. For +// Eyrie facade clients advertise that they manage provider resilience. For // those clients this service records the product metric and delegates exactly -// once; injected legacy clients retain Graycode's compatibility retry/rate layer. -func (c *ChatService) Stream(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.StreamResult, error) { +// once; injected legacy clients retain Hawk's compatibility retry/rate layer. +func (c *ChatService) Stream(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.StreamResult, error) { c.mu.RLock() client := c.client rateLimiter := c.rateLimiter @@ -282,11 +282,11 @@ const ( // emergencyCompactWindow messages. This is a last-resort path (the normal // context governor in the agent loop does the real summarization); here we // only need the retry to succeed once. -func emergencyCompact(messages []types.GraycodeRouterMessage) []types.GraycodeRouterMessage { +func emergencyCompact(messages []types.EyrieMessage) []types.EyrieMessage { if len(messages) <= emergencyCompactMin { return messages } - out := make([]types.GraycodeRouterMessage, 0, emergencyCompactWindow+1) + out := make([]types.EyrieMessage, 0, emergencyCompactWindow+1) for _, m := range messages { if m.Role == "system" { out = append(out, m) @@ -304,7 +304,7 @@ func emergencyCompact(messages []types.GraycodeRouterMessage) []types.GraycodeRo // Chat issues a non-streaming LLM call. Used by background goroutines // (sleeptime consolidation, skill distillation) that don't need // incremental events. -func (c *ChatService) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (c *ChatService) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { client := c.Client() if client == nil { return nil, errors.New("chat service: no client configured") diff --git a/internal/engine/chat_service_test.go b/internal/engine/chat_service_test.go index 8952940fc..e37de4fad 100644 --- a/internal/engine/chat_service_test.go +++ b/internal/engine/chat_service_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/resilience/retry" - "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/graycode-router/llm" + "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/hawk/internal/resilience/retry" + "github.com/GrayCodeAI/hawk/internal/types" ) // TestChatService_BuildOptions checks that BuildOptions correctly @@ -19,8 +19,8 @@ func TestChatService_BuildOptions(t *testing.T) { Provider: "anthropic", Model: "claude-opus-4", }) - opts := svc.BuildOptions(strings.Repeat("you are graycode. ", cacheMinPrefixBytes/7+64), "claude-opus-4", 4096, nil) - opts.System = "you are graycode" // restore exact assertion target + opts := svc.BuildOptions(strings.Repeat("you are hawk. ", cacheMinPrefixBytes/7+64), "claude-opus-4", 4096, nil) + opts.System = "you are hawk" // restore exact assertion target if opts.Provider != "anthropic" { t.Errorf("expected provider=anthropic, got %q", opts.Provider) } @@ -35,7 +35,7 @@ func TestChatService_BuildOptions(t *testing.T) { if !opts.EnableCaching { t.Error("expected EnableCaching=true for anthropic with a prefix at/above break-even") } - if opts.System != "you are graycode" { + if opts.System != "you are hawk" { t.Errorf("expected system prompt to be set, got %q", opts.System) } } @@ -137,7 +137,7 @@ func TestChatService_ChatDelegatesToClient(t *testing.T) { }) resp, err := svc.Chat( context.Background(), - []types.GraycodeRouterMessage{{Role: "user", Content: "hi"}}, + []types.EyrieMessage{{Role: "user", Content: "hi"}}, svc.BuildOptions("sys", "claude-opus-4", 1024, nil), ) if err != nil { @@ -152,11 +152,11 @@ func TestChatService_ChatDelegatesToClient(t *testing.T) { // ChatService.Chat surfaces the underlying error unchanged. type errClient struct{ err error } -func (e *errClient) Chat(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (e *errClient) Chat(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions) (*types.EyrieResponse, error) { return nil, e.err } -func (e *errClient) StreamChatContinue(_ context.Context, _ []types.GraycodeRouterMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (e *errClient) StreamChatContinue(_ context.Context, _ []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { return nil, e.err } @@ -174,11 +174,11 @@ type resilienceManagingTestClient struct { calls int } -func (c *resilienceManagingTestClient) Chat(context.Context, []types.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (c *resilienceManagingTestClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { return nil, c.err } -func (c *resilienceManagingTestClient) StreamChatContinue(context.Context, []types.GraycodeRouterMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *resilienceManagingTestClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ return nil, c.err } @@ -187,7 +187,7 @@ func (c *resilienceManagingTestClient) ManagesResilience() bool { return true } func TestChatService_DoesNotDuplicateEngineResilience(t *testing.T) { client := &resilienceManagingTestClient{err: errors.New("routed transport failed")} svc := NewChatService(client, ChatServiceConfig{}) - _, err := svc.Stream(context.Background(), []types.GraycodeRouterMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) + _, err := svc.Stream(context.Background(), []types.EyrieMessage{{Role: "user", Content: "hi"}}, types.ChatOptions{}) if err == nil { t.Fatal("expected stream error") } @@ -200,16 +200,16 @@ type flakyLegacyStartClient struct { calls int } -func (*flakyLegacyStartClient) Chat(context.Context, []types.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (*flakyLegacyStartClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { return nil, nil } -func (c *flakyLegacyStartClient) StreamChatContinue(context.Context, []types.GraycodeRouterMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { +func (c *flakyLegacyStartClient) StreamChatContinue(context.Context, []types.EyrieMessage, types.ChatOptions, types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ if c.calls == 1 { return nil, errors.New("temporary transport failure") } - events := make(chan types.GraycodeRouterStreamEvent) + events := make(chan types.EyrieStreamEvent) close(events) return llm.NewStreamResult(events, "", nil), nil } @@ -243,21 +243,21 @@ func retryConfigForBoundaryTest() retry.Config { // received on the retry so tests can assert the emergency compact (H3). type overflowThenCaptureClient struct { calls int - seen []types.GraycodeRouterMessage + seen []types.EyrieMessage started bool } -func (*overflowThenCaptureClient) Chat(context.Context, []types.GraycodeRouterMessage, types.ChatOptions) (*types.GraycodeRouterResponse, error) { +func (*overflowThenCaptureClient) Chat(context.Context, []types.EyrieMessage, types.ChatOptions) (*types.EyrieResponse, error) { return nil, nil } -func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messages []types.GraycodeRouterMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { +func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messages []types.EyrieMessage, _ types.ChatOptions, _ types.ContinuationConfig) (*types.StreamResult, error) { c.calls++ if c.calls == 1 { return nil, errors.New("input too long: 120000 tokens exceeds the limit of 100000") } c.started = true - c.seen = append([]types.GraycodeRouterMessage(nil), messages...) + c.seen = append([]types.EyrieMessage(nil), messages...) return &types.StreamResult{}, nil } @@ -265,10 +265,10 @@ func (c *overflowThenCaptureClient) StreamChatContinue(_ context.Context, messag // context-overflow error, the retry sends a compacted (smaller) transcript // instead of re-sending the same overflowing messages. func TestChatService_EmergencyCompactTrimsBeforeRetry(t *testing.T) { - var messages []types.GraycodeRouterMessage - messages = append(messages, types.GraycodeRouterMessage{Role: "system", Content: "sys"}) + var messages []types.EyrieMessage + messages = append(messages, types.EyrieMessage{Role: "system", Content: "sys"}) for i := 0; i < 100; i++ { - messages = append(messages, types.GraycodeRouterMessage{Role: "user", Content: "message-" + itoaForTest(i)}) + messages = append(messages, types.EyrieMessage{Role: "user", Content: "message-" + itoaForTest(i)}) } client := &overflowThenCaptureClient{} diff --git a/internal/engine/client_interface.go b/internal/engine/client_interface.go index 9bdc9a44f..981ff8d7a 100644 --- a/internal/engine/client_interface.go +++ b/internal/engine/client_interface.go @@ -3,15 +3,15 @@ package engine import ( "context" - "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/graycode-router/llm" + "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/hawk/internal/types" ) // ChatClient abstracts the LLM client methods used by Session. -// The production implementation is the GraycodeRouter engine adapter; tests can inject a mock. +// The production implementation is the Eyrie engine adapter; tests can inject a mock. type ChatClient interface { - Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) - StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) + Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) + StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) } // resilienceManagingChatClient is an optional capability implemented by @@ -58,18 +58,18 @@ func NewMockClientForTest() ChatClient { type exportedMockClient struct{} -func (m *exportedMockClient) Chat(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions) (*types.GraycodeRouterResponse, error) { - return &types.GraycodeRouterResponse{ +func (m *exportedMockClient) Chat(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions) (*types.EyrieResponse, error) { + return &types.EyrieResponse{ Content: "mock test response", FinishReason: "end_turn", - Usage: &types.GraycodeRouterUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}, + Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}, }, nil } -func (m *exportedMockClient) StreamChatContinue(ctx context.Context, messages []types.GraycodeRouterMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { - ch := make(chan types.GraycodeRouterStreamEvent, 5) - ch <- types.GraycodeRouterStreamEvent{Type: "content", Content: "mock streamed response"} - ch <- types.GraycodeRouterStreamEvent{Type: "done", StopReason: "end_turn", Usage: &types.GraycodeRouterUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}} +func (m *exportedMockClient) StreamChatContinue(ctx context.Context, messages []types.EyrieMessage, opts types.ChatOptions, cfg types.ContinuationConfig) (*types.StreamResult, error) { + ch := make(chan types.EyrieStreamEvent, 5) + ch <- types.EyrieStreamEvent{Type: "content", Content: "mock streamed response"} + ch <- types.EyrieStreamEvent{Type: "done", StopReason: "end_turn", Usage: &types.EyrieUsage{PromptTokens: 10, CompletionTokens: 5, TotalTokens: 15}} close(ch) return &types.StreamResult{Events: ch}, nil } diff --git a/internal/engine/code/code_actions.go b/internal/engine/code/code_actions.go index 5320bd810..b39ff6803 100644 --- a/internal/engine/code/code_actions.go +++ b/internal/engine/code/code_actions.go @@ -9,7 +9,7 @@ import ( "sync" "text/template" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) type CodeAction struct { diff --git a/internal/engine/code_index_adapter.go b/internal/engine/code_index_adapter.go index 1151032bc..4e9d781de 100644 --- a/internal/engine/code_index_adapter.go +++ b/internal/engine/code_index_adapter.go @@ -1,8 +1,8 @@ package engine import ( - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" - "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" + "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" ) // harrierCodeIndexer adapts *memory.HarrierBridge to repomap.CodeIndexer. The two diff --git a/internal/engine/coding_soul.go b/internal/engine/coding_soul.go index 60f04e092..e11bc42f2 100644 --- a/internal/engine/coding_soul.go +++ b/internal/engine/coding_soul.go @@ -5,11 +5,11 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) // CodingSoul defines the persistent coding personality and style preferences. -// Loaded from Graycode user state. +// Loaded from Hawk user state. type CodingSoul struct { Style string // communication style Preferences string // coding preferences @@ -61,7 +61,7 @@ func (s *CodingSoul) ForPrompt() string { // InitSoulPrompt returns a prompt to generate an initial soul.md. func InitSoulPrompt() string { - return `Generate a coding soul profile for Graycode user state based on my coding patterns. Analyze my recent code and infer: + return `Generate a coding soul profile for Hawk user state based on my coding patterns. Analyze my recent code and infer: ## Style - How I communicate (terse vs verbose, formal vs casual) diff --git a/internal/engine/compact.go b/internal/engine/compact.go index a6f8bc136..fda3c884b 100644 --- a/internal/engine/compact.go +++ b/internal/engine/compact.go @@ -7,11 +7,11 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/engine/compact" + "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/types" - modelPkg "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + modelPkg "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // ShouldAutoCompact returns true if the conversation is approaching context limits. @@ -104,22 +104,22 @@ func (s *Session) smartCompactBody(ctx context.Context) { // segment before they leave the live context. Best-effort: a persistence // failure must never block or corrupt compaction itself. if sessionID := s.executionGraphSessionID(); sessionID != "" && len(compactedMsgs) > 0 { - detail, _ := compact.ParseCompactionDetail(os.Getenv("GRAYCODE_COMPACTION_SEGMENT_DETAIL")) + detail, _ := compact.ParseCompactionDetail(os.Getenv("HAWK_COMPACTION_SEGMENT_DETAIL")) if _, err := compact.WriteCompactionSegment(sessionID, compactedMsgs, detail); err != nil { slog.Debug("compaction segment persistence skipped", "error", err) } } tail := raw[len(raw)-keepEnd:] - keep := make([]types.GraycodeRouterMessage, 0, len(tail)+2) - keep = append(keep, types.GraycodeRouterMessage{ + keep := make([]types.EyrieMessage, 0, len(tail)+2) + keep = append(keep, types.EyrieMessage{ Role: "user", Content: "[Conversation summary]\n" + summary + "\n\n[Continue from the recent messages below.]", }) // Only emit the assistant ack when the tail starts with a user message; // two adjacent assistant messages are rejected by OpenAI-compat providers. if len(tail) == 0 || tail[0].Role != "assistant" { - keep = append(keep, types.GraycodeRouterMessage{ + keep = append(keep, types.EyrieMessage{ Role: "assistant", Content: "Understood. I have the context from the summary above. Continuing.", }) @@ -133,7 +133,7 @@ func (s *Session) smartCompactBody(ctx context.Context) { // window on very long transcripts. const summaryInputRuneCap = 32_000 -func (s *Session) generateSummary(ctx context.Context, raw []types.GraycodeRouterMessage) string { +func (s *Session) generateSummary(ctx context.Context, raw []types.EyrieMessage) string { // Incremental compaction: if a prior summary was persisted by an earlier // compaction, merge the NEW messages into it rather than re-summarizing the // entire conversation from scratch. This preserves already-captured context @@ -152,9 +152,9 @@ func (s *Session) generateSummary(ctx context.Context, raw []types.GraycodeRoute // Build a compact version of the conversation for summarization // using the structured compaction prompt from compact_prompt.go - var summaryMsgs []types.GraycodeRouterMessage + var summaryMsgs []types.EyrieMessage compactPrompt := BuildIncrementalCompactPrompt(prior) - summaryMsgs = append(summaryMsgs, types.GraycodeRouterMessage{ + summaryMsgs = append(summaryMsgs, types.EyrieMessage{ Role: "user", Content: compactPrompt + "\n\nConversation:\n", }) @@ -267,7 +267,7 @@ func CompressMessageContent(content string, maxTokens int) string { } // compactModel returns the cheapest available model for the current provider. -// Queries graycode-router's catalog at runtime — no hardcoded model names. +// Queries eyrie's catalog at runtime — no hardcoded model names. // Summarization doesn't need frontier reasoning, so the cheapest model suffices. func (s *Session) compactModel() string { provider := strings.ToLower(s.ChatLLM().Provider()) diff --git a/internal/engine/compact/aliases_extra_test.go b/internal/engine/compact/aliases_extra_test.go index 7049f46e6..49c8724b9 100644 --- a/internal/engine/compact/aliases_extra_test.go +++ b/internal/engine/compact/aliases_extra_test.go @@ -3,7 +3,7 @@ package compact import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestDefaultConfig(t *testing.T) { @@ -83,7 +83,7 @@ func TestCountClearableToolResults(t *testing.T) { func TestIsThinkingMessage(t *testing.T) { // Empty message - result := isThinkingMessage(types.GraycodeRouterMessage{}) + result := isThinkingMessage(types.EyrieMessage{}) if result { t.Error("expected false for empty message") } @@ -130,7 +130,7 @@ func TestReadSessionMemory_NonExistent(t *testing.T) { func TestIsCompactBoundary(t *testing.T) { // Empty message - result := IsCompactBoundary(types.GraycodeRouterMessage{}) + result := IsCompactBoundary(types.EyrieMessage{}) if result { t.Error("expected false for empty message") } diff --git a/internal/engine/compact/api.go b/internal/engine/compact/api.go index cc6d93da8..1605a39c1 100644 --- a/internal/engine/compact/api.go +++ b/internal/engine/compact/api.go @@ -1,9 +1,9 @@ package compact import ( - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/engine/token" ) type APICompactConfig struct { @@ -30,7 +30,7 @@ var mutatingTools = map[string]bool{ "NotebookEdit": true, } -func APICompactMessages(msgs []types.GraycodeRouterMessage, cfg APICompactConfig) []types.GraycodeRouterMessage { +func APICompactMessages(msgs []types.EyrieMessage, cfg APICompactConfig) []types.EyrieMessage { totalTokens := token.EstimateTokens(msgs) if totalTokens < cfg.TriggerTokens { return msgs @@ -41,7 +41,7 @@ func APICompactMessages(msgs []types.GraycodeRouterMessage, cfg APICompactConfig return msgs } - result := make([]types.GraycodeRouterMessage, len(msgs)) + result := make([]types.EyrieMessage, len(msgs)) copy(result, msgs) freed := 0 @@ -101,7 +101,7 @@ func APICompactMessages(msgs []types.GraycodeRouterMessage, cfg APICompactConfig return result } -func CountClearableToolResults(msgs []types.GraycodeRouterMessage) int { +func CountClearableToolResults(msgs []types.EyrieMessage) int { count := 0 for _, m := range msgs { if len(m.ToolResults) > 0 && m.ToolResults[0].Content != "[Old tool result content cleared]" { @@ -114,6 +114,6 @@ func CountClearableToolResults(msgs []types.GraycodeRouterMessage) int { return count } -func isThinkingMessage(m types.GraycodeRouterMessage) bool { +func isThinkingMessage(m types.EyrieMessage) bool { return len(m.Content) > 0 && m.Content[0] == '<' && len(m.ToolUse) == 0 } diff --git a/internal/engine/compact/files.go b/internal/engine/compact/files.go index 003b83ee9..a5b5641c6 100644 --- a/internal/engine/compact/files.go +++ b/internal/engine/compact/files.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) type FileTracker struct { @@ -35,7 +35,7 @@ func (ft *FileTracker) RecordModified(path string) { ft.ModifiedFiles[path]++ } -func (ft *FileTracker) ExtractFromMessages(messages []types.GraycodeRouterMessage) { +func (ft *FileTracker) ExtractFromMessages(messages []types.EyrieMessage) { for _, msg := range messages { if msg.Role != "assistant" { continue diff --git a/internal/engine/compact/files_test.go b/internal/engine/compact/files_test.go index 13e40d59e..421560a26 100644 --- a/internal/engine/compact/files_test.go +++ b/internal/engine/compact/files_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestFileTracker_NewFileTracker(t *testing.T) { @@ -67,7 +67,7 @@ func TestFileTracker_RecordModified_Empty(t *testing.T) { func TestFileTracker_ExtractFromMessages(t *testing.T) { t.Parallel() ft := NewFileTracker() - messages := []types.GraycodeRouterMessage{ + messages := []types.EyrieMessage{ { Role: "assistant", ToolUse: []types.ToolCall{ @@ -88,7 +88,7 @@ func TestFileTracker_ExtractFromMessages(t *testing.T) { func TestFileTracker_ExtractFromMessages_SkipNonAssistant(t *testing.T) { t.Parallel() ft := NewFileTracker() - messages := []types.GraycodeRouterMessage{ + messages := []types.EyrieMessage{ {Role: "user", ToolUse: []types.ToolCall{{Name: "Read", Arguments: map[string]interface{}{"path": "x.go"}}}}, } ft.ExtractFromMessages(messages) diff --git a/internal/engine/compact/incremental_test.go b/internal/engine/compact/incremental_test.go index b4b98172d..ffb89fe2a 100644 --- a/internal/engine/compact/incremental_test.go +++ b/internal/engine/compact/incremental_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestBuildIncrementalCompactPromptIncludesPriorSummary(t *testing.T) { @@ -32,7 +32,7 @@ func TestBuildIncrementalCompactPromptEmptyPriorFallsBack(t *testing.T) { } func TestExtractPriorSummaryFound(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "[Conversation summary]\n## Goal\n- build\n\n[Continue from the recent messages below.]"}, {Role: "assistant", Content: "ok"}, } @@ -43,7 +43,7 @@ func TestExtractPriorSummaryFound(t *testing.T) { } func TestExtractPriorSummaryNone(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hello"}, } if got := ExtractPriorSummary(msgs); got != "" { @@ -52,7 +52,7 @@ func TestExtractPriorSummaryNone(t *testing.T) { } func TestExtractPriorSummaryIgnoresNonSummaryFirst(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "[Session memory summary]\nstuff"}, {Role: "user", Content: "[Conversation summary]\n## Goal\n- x"}, } diff --git a/internal/engine/compact/micro.go b/internal/engine/compact/micro.go index 40593d24f..4b6cec735 100644 --- a/internal/engine/compact/micro.go +++ b/internal/engine/compact/micro.go @@ -3,7 +3,7 @@ package compact import ( "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) type MicroCompactConfig struct { @@ -25,7 +25,7 @@ type resultInfo struct { toolName string } -func MicrocompactMessages(msgs []types.GraycodeRouterMessage, cfg MicroCompactConfig) []types.GraycodeRouterMessage { +func MicrocompactMessages(msgs []types.EyrieMessage, cfg MicroCompactConfig) []types.EyrieMessage { var compactableResults []resultInfo for i, m := range msgs { if len(m.ToolResults) == 0 { @@ -47,7 +47,7 @@ func MicrocompactMessages(msgs []types.GraycodeRouterMessage, cfg MicroCompactCo clearSet[compactableResults[i].index] = true } - result := make([]types.GraycodeRouterMessage, len(msgs)) + result := make([]types.EyrieMessage, len(msgs)) copy(result, msgs) for idx := range clearSet { oldResults := result[idx].ToolResults @@ -59,7 +59,7 @@ func MicrocompactMessages(msgs []types.GraycodeRouterMessage, cfg MicroCompactCo IsError: tr.IsError, } } - result[idx] = types.GraycodeRouterMessage{ + result[idx] = types.EyrieMessage{ Role: result[idx].Role, ToolResults: newResults, } @@ -68,7 +68,7 @@ func MicrocompactMessages(msgs []types.GraycodeRouterMessage, cfg MicroCompactCo return result } -func ToolNameForResult(m types.GraycodeRouterMessage, msgs []types.GraycodeRouterMessage) string { +func ToolNameForResult(m types.EyrieMessage, msgs []types.EyrieMessage) string { if len(m.ToolResults) == 0 { return "" } @@ -83,7 +83,7 @@ func ToolNameForResult(m types.GraycodeRouterMessage, msgs []types.GraycodeRoute return "" } -func HasTimeGap(msgs []types.GraycodeRouterMessage, threshold time.Duration) bool { +func HasTimeGap(msgs []types.EyrieMessage, threshold time.Duration) bool { lastTextIdx := -1 for i := len(msgs) - 1; i >= 0; i-- { if HasTextContent(msgs[i]) && msgs[i].Role == "assistant" { diff --git a/internal/engine/compact/prompt.go b/internal/engine/compact/prompt.go index f21bfdd2b..6a5eefcff 100644 --- a/internal/engine/compact/prompt.go +++ b/internal/engine/compact/prompt.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) const noToolsPreamble = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools. @@ -121,14 +121,14 @@ func BuildIncrementalCompactPrompt(priorSummary string) string { fmt.Sprintf(incrementalUpdateTemplate, priorSummary) } -// PriorSummaryPrefix is the marker prefix graycode prepends to a persisted +// PriorSummaryPrefix is the marker prefix hawk prepends to a persisted // conversation summary message. const PriorSummaryPrefix = "[Conversation summary]" // ExtractPriorSummary extracts the previously generated summary text from the // first message of a conversation if one was persisted by an earlier // compaction. It returns "" when no prior summary is present. -func ExtractPriorSummary(msgs []types.GraycodeRouterMessage) string { +func ExtractPriorSummary(msgs []types.EyrieMessage) string { for _, m := range msgs { if m.Role != "user" { continue diff --git a/internal/engine/compact/session_memory.go b/internal/engine/compact/session_memory.go index e349d65ef..d3adafb49 100644 --- a/internal/engine/compact/session_memory.go +++ b/internal/engine/compact/session_memory.go @@ -5,10 +5,10 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/storage" ) type SessionMemoryConfig struct { @@ -25,7 +25,7 @@ func DefaultSessionMemoryConfig() SessionMemoryConfig { } } -func CalculateMessagesToKeepIndex(msgs []types.GraycodeRouterMessage, cfg SessionMemoryConfig) int { +func CalculateMessagesToKeepIndex(msgs []types.EyrieMessage, cfg SessionMemoryConfig) int { if len(msgs) == 0 { return 0 } @@ -55,8 +55,8 @@ func CalculateMessagesToKeepIndex(msgs []types.GraycodeRouterMessage, cfg Sessio return idx } -func FilterCompactBoundaries(msgs []types.GraycodeRouterMessage) []types.GraycodeRouterMessage { - result := make([]types.GraycodeRouterMessage, 0, len(msgs)) +func FilterCompactBoundaries(msgs []types.EyrieMessage) []types.EyrieMessage { + result := make([]types.EyrieMessage, 0, len(msgs)) for _, m := range msgs { if IsCompactBoundary(m) { continue @@ -66,7 +66,7 @@ func FilterCompactBoundaries(msgs []types.GraycodeRouterMessage) []types.Graycod return result } -func IsCompactBoundary(m types.GraycodeRouterMessage) bool { +func IsCompactBoundary(m types.EyrieMessage) bool { if m.Role != "user" { return false } diff --git a/internal/engine/compact/split_test.go b/internal/engine/compact/split_test.go index 86c258280..a95dfcc30 100644 --- a/internal/engine/compact/split_test.go +++ b/internal/engine/compact/split_test.go @@ -3,7 +3,7 @@ package compact import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // --------------------------------------------------------------------------- @@ -39,28 +39,28 @@ func TestIsCompactableTool_Empty(t *testing.T) { // --------------------------------------------------------------------------- func TestHasTextContent_WithContent(t *testing.T) { - msg := types.GraycodeRouterMessage{Role: "assistant", Content: "Hello world"} + msg := types.EyrieMessage{Role: "assistant", Content: "Hello world"} if !HasTextContent(msg) { t.Error("expected true for message with content") } } func TestHasTextContent_EmptyContent(t *testing.T) { - msg := types.GraycodeRouterMessage{Role: "assistant", Content: ""} + msg := types.EyrieMessage{Role: "assistant", Content: ""} if HasTextContent(msg) { t.Error("expected false for empty content") } } func TestHasTextContent_WhitespaceOnly(t *testing.T) { - msg := types.GraycodeRouterMessage{Role: "assistant", Content: " \n\t "} + msg := types.EyrieMessage{Role: "assistant", Content: " \n\t "} if HasTextContent(msg) { t.Error("expected false for whitespace-only content") } } func TestHasTextContent_ToolResultIgnored(t *testing.T) { - msg := types.GraycodeRouterMessage{ + msg := types.EyrieMessage{ Role: "user", Content: "has content", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "output"}}, @@ -71,7 +71,7 @@ func TestHasTextContent_ToolResultIgnored(t *testing.T) { } func TestHasTextContent_WithToolUse(t *testing.T) { - msg := types.GraycodeRouterMessage{ + msg := types.EyrieMessage{ Role: "assistant", Content: "Using tool", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}, @@ -134,7 +134,7 @@ func TestCompactResult_Fields(t *testing.T) { // --------------------------------------------------------------------------- func TestAdjustIndex_StartIndexZero(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "ok"}}}, } @@ -145,7 +145,7 @@ func TestAdjustIndex_StartIndexZero(t *testing.T) { } func TestAdjustIndex_StartBeyondRange(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hello"}, } got := AdjustIndexToPreserveAPIInvariants(msgs, 100) @@ -155,7 +155,7 @@ func TestAdjustIndex_StartBeyondRange(t *testing.T) { } func TestAdjustIndex_UnresolvedToolPair(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "do stuff"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "ok"}}}, diff --git a/internal/engine/compact/strategy.go b/internal/engine/compact/strategy.go index ff00189be..0e95a8adb 100644 --- a/internal/engine/compact/strategy.go +++ b/internal/engine/compact/strategy.go @@ -4,11 +4,11 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) type CompactResult struct { - Messages []types.GraycodeRouterMessage + Messages []types.EyrieMessage Summary string TokensBefore int TokensAfter int @@ -52,7 +52,7 @@ func IsCompactableTool(name string) bool { return compactableTools[name] } -func AdjustIndexToPreserveAPIInvariants(msgs []types.GraycodeRouterMessage, startIdx int) int { +func AdjustIndexToPreserveAPIInvariants(msgs []types.EyrieMessage, startIdx int) int { if startIdx <= 0 { return 0 } @@ -87,7 +87,7 @@ func AdjustIndexToPreserveAPIInvariants(msgs []types.GraycodeRouterMessage, star return idx } -func HasTextContent(m types.GraycodeRouterMessage) bool { +func HasTextContent(m types.EyrieMessage) bool { if len(m.ToolResults) > 0 { return false } diff --git a/internal/engine/compact/strategy_test.go b/internal/engine/compact/strategy_test.go index 34c7e1436..77d998ecf 100644 --- a/internal/engine/compact/strategy_test.go +++ b/internal/engine/compact/strategy_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/engine/token" ) func TestCompactEstimateTokens(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "Hello world"}, {Role: "assistant", Content: strings.Repeat("x", 400)}, } @@ -18,7 +18,7 @@ func TestCompactEstimateTokens(t *testing.T) { if tokens < 1 { t.Errorf("expected at least 1 token, got %d", tokens) } - shortMsgs := []types.GraycodeRouterMessage{ + shortMsgs := []types.EyrieMessage{ {Role: "user", Content: "hi"}, } shortTokens := token.EstimateTokens(shortMsgs) @@ -30,7 +30,7 @@ func TestCompactEstimateTokens(t *testing.T) { func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { tests := []struct { name string - msgs []types.GraycodeRouterMessage + msgs []types.EyrieMessage startIdx int wantIdx int }{ @@ -42,7 +42,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "no tool pairs", - msgs: []types.GraycodeRouterMessage{ + msgs: []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, {Role: "user", Content: "bye"}, @@ -52,7 +52,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "tool_result at startIdx - moves back past tool_use", - msgs: []types.GraycodeRouterMessage{ + msgs: []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "output"}}}, @@ -63,7 +63,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { }, { name: "at boundary already", - msgs: []types.GraycodeRouterMessage{ + msgs: []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "response"}, }, @@ -83,7 +83,7 @@ func TestAdjustIndexToPreserveAPIInvariants(t *testing.T) { } func TestMicrocompactMessages(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "read file.go"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Read"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "package main\nfunc main() {}"}}}, @@ -132,7 +132,7 @@ func TestMicrocompactMessages(t *testing.T) { } func TestAPICompactMessages(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Bash", Arguments: map[string]interface{}{"command": strings.Repeat("x", 1000)}}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: strings.Repeat("output ", 1000)}}}, @@ -158,7 +158,7 @@ func TestAPICompactMessages(t *testing.T) { } func TestAPICompactPreservesMutatingTools(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "edit file"}, {Role: "assistant", ToolUse: []types.ToolCall{{ID: "t1", Name: "Edit", Arguments: map[string]interface{}{"old_string": strings.Repeat("x", 1000), "new_string": "y"}}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: strings.Repeat("edited ", 500)}}}, @@ -180,7 +180,7 @@ func TestAPICompactPreservesMutatingTools(t *testing.T) { } func TestCalculateMessagesToKeepIndex(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: strings.Repeat("hello ", 100)}, {Role: "assistant", Content: strings.Repeat("response ", 100)}, {Role: "user", Content: strings.Repeat("follow up ", 100)}, @@ -205,7 +205,7 @@ func TestCalculateMessagesToKeepIndex(t *testing.T) { } func TestFilterCompactBoundaries(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "[Session memory summary]\nold stuff"}, {Role: "assistant", Content: "Understood."}, {Role: "user", Content: "real message"}, diff --git a/internal/engine/compact/transcript_segments.go b/internal/engine/compact/transcript_segments.go index 7adfb8ac6..db6ea471d 100644 --- a/internal/engine/compact/transcript_segments.go +++ b/internal/engine/compact/transcript_segments.go @@ -11,8 +11,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/types" ) // Compaction transcript segments, adopted from grok-build's @@ -80,7 +80,7 @@ func IndexPath(sessionID string) string { // RenderSegmentToMarkdown renders messages into a self-contained markdown // segment. Pure: no I/O. -func RenderSegmentToMarkdown(msgs []types.GraycodeRouterMessage, segIndex int, detail CompactionDetail) string { +func RenderSegmentToMarkdown(msgs []types.EyrieMessage, segIndex int, detail CompactionDetail) string { var b strings.Builder fmt.Fprintf(&b, "# Compaction segment %d\n\n", segIndex) fmt.Fprintf(&b, "Recorded: %s\n", time.Now().UTC().Format(time.RFC3339)) @@ -112,7 +112,7 @@ func RenderSegmentToMarkdown(msgs []types.GraycodeRouterMessage, segIndex int, d return b.String() } -func renderTurn(m types.GraycodeRouterMessage, detail CompactionDetail) string { +func renderTurn(m types.EyrieMessage, detail CompactionDetail) string { var b strings.Builder role := strings.ToUpper(m.Role) fmt.Fprintf(&b, "## %s\n", role) @@ -212,7 +212,7 @@ func NextSegmentIndex(sessionID string) int { // WriteCompactionSegment persists msgs as the next segment and appends an // INDEX.md row. It returns the written segment path. Callers should treat // errors as non-fatal: segment persistence must never block compaction. -func WriteCompactionSegment(sessionID string, msgs []types.GraycodeRouterMessage, detail CompactionDetail) (string, error) { +func WriteCompactionSegment(sessionID string, msgs []types.EyrieMessage, detail CompactionDetail) (string, error) { if sessionID == "" { return "", fmt.Errorf("compact: empty session id") } diff --git a/internal/engine/compact/transcript_segments_test.go b/internal/engine/compact/transcript_segments_test.go index 44c65a1df..c558a9df8 100644 --- a/internal/engine/compact/transcript_segments_test.go +++ b/internal/engine/compact/transcript_segments_test.go @@ -6,11 +6,11 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) -func segTestMessages() []types.GraycodeRouterMessage { - return []types.GraycodeRouterMessage{ +func segTestMessages() []types.EyrieMessage { + return []types.EyrieMessage{ {Role: "user", Content: "fix the flaky test in pkg/foo"}, { Role: "assistant", Content: "Looking into it.", @@ -74,7 +74,7 @@ func TestParseCompactionDetail(t *testing.T) { func TestWriteCompactionSegmentAndIndex(t *testing.T) { stateDir := t.TempDir() - t.Setenv("GRAYCODE_STATE_DIR", stateDir) + t.Setenv("HAWK_STATE_DIR", stateDir) sessionID := "seg-test-session" path, err := WriteCompactionSegment(sessionID, segTestMessages(), SegmentVerbose) if err != nil { diff --git a/internal/engine/compact_auto.go b/internal/engine/compact_auto.go index 6e3214d34..aea5e67fe 100644 --- a/internal/engine/compact_auto.go +++ b/internal/engine/compact_auto.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/circuitbreaker" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/circuitbreaker" + "github.com/GrayCodeAI/hawk/internal/types" ) // AutoCompactor orchestrates compaction with circuit breaker protection. @@ -173,7 +173,7 @@ type SmartCompactStrategy struct{} func (s *SmartCompactStrategy) Name() string { return "smart" } -func (s *SmartCompactStrategy) ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *SmartCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) > 20 } @@ -196,7 +196,7 @@ type TruncateStrategy struct{} func (s *TruncateStrategy) Name() string { return "truncate" } -func (s *TruncateStrategy) ShouldTrigger(_ []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *TruncateStrategy) ShouldTrigger(_ []types.EyrieMessage, tokenCount, threshold int) bool { return tokenCount >= threshold } diff --git a/internal/engine/compact_clear.go b/internal/engine/compact_clear.go index 7a4c8d26f..7d4fee3f5 100644 --- a/internal/engine/compact_clear.go +++ b/internal/engine/compact_clear.go @@ -4,7 +4,7 @@ import ( "context" "sort" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // Two-tier context management (adopted from herm): before compacting, "clear" @@ -28,7 +28,7 @@ const ( // recent clearKeepRecent tool-result messages intact, until estimatedTokens // drops below threshold. It returns the new messages and the estimated tokens // freed. Adopted from herm's clearOldToolResults. -func clearOldToolResults(msgs []types.GraycodeRouterMessage, estimatedTokens, threshold int) ([]types.GraycodeRouterMessage, int) { +func clearOldToolResults(msgs []types.EyrieMessage, estimatedTokens, threshold int) ([]types.EyrieMessage, int) { if threshold <= 0 || estimatedTokens <= 0 || len(msgs) == 0 { return msgs, 0 } @@ -94,7 +94,7 @@ func (s *Session) ClearOldToolResults(ctx context.Context) bool { } // toolResultBytes returns the total byte size of a message's tool results. -func toolResultBytes(m types.GraycodeRouterMessage) int { +func toolResultBytes(m types.EyrieMessage) int { total := 0 for _, tr := range m.ToolResults { total += len(tr.Content) @@ -104,7 +104,7 @@ func toolResultBytes(m types.GraycodeRouterMessage) int { // clearMessageToolResults replaces each non-empty tool-result content with the // placeholder and returns the estimated tokens freed (~4 bytes per token). -func clearMessageToolResults(m *types.GraycodeRouterMessage) int { +func clearMessageToolResults(m *types.EyrieMessage) int { freedBytes := 0 for i := range m.ToolResults { c := m.ToolResults[i].Content @@ -119,8 +119,8 @@ func clearMessageToolResults(m *types.GraycodeRouterMessage) int { // cloneToolResults copies msgs, deep-copying the ToolResults slices so callers // can mutate the copy without touching the persisted originals. -func cloneToolResults(msgs []types.GraycodeRouterMessage) []types.GraycodeRouterMessage { - out := make([]types.GraycodeRouterMessage, len(msgs)) +func cloneToolResults(msgs []types.EyrieMessage) []types.EyrieMessage { + out := make([]types.EyrieMessage, len(msgs)) for i, m := range msgs { out[i] = m if m.ToolResults != nil { diff --git a/internal/engine/compact_clear_test.go b/internal/engine/compact_clear_test.go index 36b825683..86b91cc38 100644 --- a/internal/engine/compact_clear_test.go +++ b/internal/engine/compact_clear_test.go @@ -3,15 +3,15 @@ package engine import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) -func mkMsg(role string, trs []types.ToolResult) types.GraycodeRouterMessage { - return types.GraycodeRouterMessage{Role: role, ToolResults: trs} +func mkMsg(role string, trs []types.ToolResult) types.EyrieMessage { + return types.EyrieMessage{Role: role, ToolResults: trs} } func TestClearOldToolResultsNoOpWhenBelowThreshold(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ mkMsg("user", []types.ToolResult{{Content: "aaaa"}}), } out, freed := clearOldToolResults(msgs, 100, 1000) @@ -22,7 +22,7 @@ func TestClearOldToolResultsNoOpWhenBelowThreshold(t *testing.T) { func TestClearOldToolResultsClearsBiggestFirst(t *testing.T) { // 6 tool-result messages; threshold low so clearing triggers; keep last 4. - msgs := make([]types.GraycodeRouterMessage, 6) + msgs := make([]types.EyrieMessage, 6) for i := range msgs { msgs[i] = mkMsg("user", []types.ToolResult{{Content: "12345678"}}) // 8 bytes } @@ -46,7 +46,7 @@ func TestClearOldToolResultsClearsBiggestFirst(t *testing.T) { func TestClearOldToolResultsStopsWhenUnderThreshold(t *testing.T) { // Huge contents, small threshold: it clears until under threshold, then stops. - msgs := make([]types.GraycodeRouterMessage, 6) + msgs := make([]types.EyrieMessage, 6) for i := range msgs { msgs[i] = mkMsg("user", []types.ToolResult{{Content: "12345678"}}) } @@ -63,7 +63,7 @@ func TestClearOldToolResultsStopsWhenUnderThreshold(t *testing.T) { } func TestClearOldToolResultsSkipsAlreadyCleared(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), mkMsg("user", []types.ToolResult{{Content: outputClearedPlaceholder}}), diff --git a/internal/engine/compact_micro_engine.go b/internal/engine/compact_micro_engine.go index a7877f65e..d57190567 100644 --- a/internal/engine/compact_micro_engine.go +++ b/internal/engine/compact_micro_engine.go @@ -4,16 +4,16 @@ import ( "context" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" + "github.com/GrayCodeAI/hawk/internal/engine/compact" ) type MicroCompactStrategy struct{} func (s *MicroCompactStrategy) Name() string { return "micro" } -func (s *MicroCompactStrategy) ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *MicroCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { if tokenCount < threshold/2 { return false } diff --git a/internal/engine/compact_provider_native.go b/internal/engine/compact_provider_native.go index 1f4ae67e1..bcdad611d 100644 --- a/internal/engine/compact_provider_native.go +++ b/internal/engine/compact_provider_native.go @@ -4,18 +4,18 @@ import ( "context" "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/graycode-router/llm" + "github.com/GrayCodeAI/eyrie/llm" + "github.com/GrayCodeAI/hawk/internal/types" ) // ProviderNativeCompactStrategy delegates provider-specific compaction to -// GraycodeRouter. Graycode owns when conversation state is compacted and how the resulting -// summary is inserted; GraycodeRouter owns credentials and provider transport details. +// Eyrie. Hawk owns when conversation state is compacted and how the resulting +// summary is inserted; Eyrie owns credentials and provider transport details. type ProviderNativeCompactStrategy struct{} func (s *ProviderNativeCompactStrategy) Name() string { return "provider_native" } -func (s *ProviderNativeCompactStrategy) ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *ProviderNativeCompactStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) >= 8 } @@ -49,8 +49,8 @@ func (s *ProviderNativeCompactStrategy) Compact(ctx context.Context, sess *Sessi if keepEnd > len(messagesBefore) { keepEnd = len(messagesBefore) } - tail := append([]types.GraycodeRouterMessage(nil), messagesBefore[len(messagesBefore)-keepEnd:]...) - messages := append([]types.GraycodeRouterMessage{{Role: "user", Content: FormatCompactSummary(summary)}}, tail...) + tail := append([]types.EyrieMessage(nil), messagesBefore[len(messagesBefore)-keepEnd:]...) + messages := append([]types.EyrieMessage{{Role: "user", Content: FormatCompactSummary(summary)}}, tail...) compact := &CompactResult{ Messages: messages, TokensBefore: tokensBefore, diff --git a/internal/engine/compact_reexports.go b/internal/engine/compact_reexports.go index bf1a8f96f..e964ba1bf 100644 --- a/internal/engine/compact_reexports.go +++ b/internal/engine/compact_reexports.go @@ -4,9 +4,9 @@ package engine import ( - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" + "github.com/GrayCodeAI/hawk/internal/engine/compact" ) type CompactVariant = compact.CompactVariant @@ -42,16 +42,16 @@ func BuildIncrementalCompactPrompt(priorSummary string) string { return compact.BuildIncrementalCompactPrompt(priorSummary) } -func ExtractPriorSummary(msgs []types.GraycodeRouterMessage) string { +func ExtractPriorSummary(msgs []types.EyrieMessage) string { return compact.ExtractPriorSummary(msgs) } -// PriorSummaryPrefix is the marker prefix graycode prepends to a persisted +// PriorSummaryPrefix is the marker prefix hawk prepends to a persisted // conversation summary message. const PriorSummaryPrefix = compact.PriorSummaryPrefix func IsCompactableTool(name string) bool { return compact.IsCompactableTool(name) } -func AdjustIndexToPreserveAPIInvariants(msgs []types.GraycodeRouterMessage, startIdx int) int { +func AdjustIndexToPreserveAPIInvariants(msgs []types.EyrieMessage, startIdx int) int { return compact.AdjustIndexToPreserveAPIInvariants(msgs, startIdx) } -func HasTextContent(m types.GraycodeRouterMessage) bool { return compact.HasTextContent(m) } +func HasTextContent(m types.EyrieMessage) bool { return compact.HasTextContent(m) } diff --git a/internal/engine/compact_regression_test.go b/internal/engine/compact_regression_test.go index cd9e1d448..3c274372b 100644 --- a/internal/engine/compact_regression_test.go +++ b/internal/engine/compact_regression_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // Regression tests for compaction fixes. Each test pins a specific bug so it @@ -24,18 +24,18 @@ func testSession(t *testing.T, window int) *Session { // toolPairMessages returns a transcript where an assistant tool_use is followed // by a user tool_result, embedded among filler messages. -func toolPairMessages(total int, toolUseAt int) []types.GraycodeRouterMessage { - msgs := make([]types.GraycodeRouterMessage, total) +func toolPairMessages(total int, toolUseAt int) []types.EyrieMessage { + msgs := make([]types.EyrieMessage, total) for i := range msgs { - msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: "filler"} + msgs[i] = types.EyrieMessage{Role: "user", Content: "filler"} } - msgs[toolUseAt] = types.GraycodeRouterMessage{ + msgs[toolUseAt] = types.EyrieMessage{ Role: "assistant", Content: "calling tool", ToolUse: []types.ToolCall{{ID: "tu-1", Name: "read_file", Arguments: map[string]any{"path": "a.go"}}}, } if toolUseAt+1 < total { - msgs[toolUseAt+1] = types.GraycodeRouterMessage{ + msgs[toolUseAt+1] = types.EyrieMessage{ Role: "user", Content: "tool responded", ToolResults: []types.ToolResult{ @@ -100,8 +100,8 @@ func TestSmartCompactAvoidsAdjacentAssistantRoles(t *testing.T) { // Build a long transcript whose kept tail starts with an assistant message // (conversation ended on a final assistant turn). msgs := makeMessages(40) - msgs[30] = types.GraycodeRouterMessage{Role: "assistant", Content: "tail starts with assistant"} - msgs[31] = types.GraycodeRouterMessage{Role: "user", Content: "next user turn"} + msgs[30] = types.EyrieMessage{Role: "assistant", Content: "tail starts with assistant"} + msgs[31] = types.EyrieMessage{Role: "user", Content: "next user turn"} s := testSession(t, 128000) s.Persistence().SetRawMessages(msgs) @@ -121,7 +121,7 @@ func TestSplitTurnCompactTruncatesOversizedMessageToSecondHalf(t *testing.T) { // and the retained message must contain exactly its second half. msgs := makeMessages(40) big := strings.Repeat("x", 20000) - msgs[35] = types.GraycodeRouterMessage{Role: "user", Content: big} + msgs[35] = types.EyrieMessage{Role: "user", Content: big} s := testSession(t, 128000) s.Persistence().SetRawMessages(msgs) @@ -156,11 +156,11 @@ func TestApplyCompactionPreservesConcurrentAppends(t *testing.T) { raw := s.Persistence().RawMessages() // snapshot (len 30) // Concurrent AddUser between snapshot and apply: concurrent := s.Persistence().RawMessages() - concurrent = append(concurrent, types.GraycodeRouterMessage{Role: "user", Content: "concurrent user turn"}) + concurrent = append(concurrent, types.EyrieMessage{Role: "user", Content: "concurrent user turn"}) s.Persistence().SetRawMessages(concurrent) // Compaction result computed from the stale snapshot (keeps only 20): - keep := append([]types.GraycodeRouterMessage(nil), raw[:20]...) + keep := append([]types.EyrieMessage(nil), raw[:20]...) s.Persistence().ApplyCompaction(keep, len(raw)) kept := s.Persistence().RawMessages() @@ -176,12 +176,12 @@ func TestShouldAutoCompactCountsToolTokens(t *testing.T) { // Content alone stays under the threshold; adding tool_result payloads // crosses it. ShouldAutoCompact must count the tool tokens. s := testSession(t, 2000) // threshold = 85% of 2000 = 1700 - msgs := make([]types.GraycodeRouterMessage, 40) + msgs := make([]types.EyrieMessage, 40) for i := range msgs { if i%2 == 0 { - msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: "short"} + msgs[i] = types.EyrieMessage{Role: "user", Content: "short"} } else { - msgs[i] = types.GraycodeRouterMessage{Role: "assistant", Content: "short reply"} + msgs[i] = types.EyrieMessage{Role: "assistant", Content: "short reply"} } } s.Persistence().SetRawMessages(msgs) @@ -204,9 +204,9 @@ func TestShouldAutoCompactCountsToolTokens(t *testing.T) { func TestGenerateSummaryInputCapped(t *testing.T) { // generateSummary must not feed an unbounded prompt even if the transcript // is huge (summaryInputRuneCap applies). - msgs := make([]types.GraycodeRouterMessage, 0, 500) + msgs := make([]types.EyrieMessage, 0, 500) for i := 0; i < 500; i++ { - msgs = append(msgs, types.GraycodeRouterMessage{Role: "user", Content: strings.Repeat("content ", 100)}) + msgs = append(msgs, types.EyrieMessage{Role: "user", Content: strings.Repeat("content ", 100)}) } s := testSession(t, 128000) summary := s.generateSummary(context.Background(), msgs) diff --git a/internal/engine/compact_relevance.go b/internal/engine/compact_relevance.go index 7cfcbde44..6e7ff84bd 100644 --- a/internal/engine/compact_relevance.go +++ b/internal/engine/compact_relevance.go @@ -4,8 +4,8 @@ import ( "context" "time" - "github.com/GrayCodeAI/graycode-cli/internal/relevanceprune" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/relevanceprune" + "github.com/GrayCodeAI/hawk/internal/types" ) // RelevancePruneStrategy prunes context by relevance: it scores older messages @@ -23,7 +23,7 @@ type RelevancePruneStrategy struct { func (s *RelevancePruneStrategy) Name() string { return "relevance" } -func (s *RelevancePruneStrategy) ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *RelevancePruneStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { return tokenCount >= threshold && len(msgs) >= 20 } @@ -43,7 +43,7 @@ func (s *RelevancePruneStrategy) Compact(ctx context.Context, sess *Session) (*C }, ) - out := toGraycodeRouterMessages(pruned) + out := toEyrieMessages(pruned) sess.Persistence().SetMessages(out) tokensAfter := EstimateTokens(sess.Persistence().RawMessages()) return &CompactResult{ @@ -55,7 +55,7 @@ func (s *RelevancePruneStrategy) Compact(ctx context.Context, sess *Session) (*C } // toPruneMessages adapts raw session messages to the relevance-prune shape. -func toPruneMessages(raw []types.GraycodeRouterMessage, now time.Time) []relevanceprune.Message { +func toPruneMessages(raw []types.EyrieMessage, now time.Time) []relevanceprune.Message { out := make([]relevanceprune.Message, len(raw)) for i, m := range raw { out[i] = relevanceprune.Message{ @@ -69,15 +69,15 @@ func toPruneMessages(raw []types.GraycodeRouterMessage, now time.Time) []relevan return out } -func toGraycodeRouterMessages(msgs []relevanceprune.Message) []types.GraycodeRouterMessage { - out := make([]types.GraycodeRouterMessage, len(msgs)) +func toEyrieMessages(msgs []relevanceprune.Message) []types.EyrieMessage { + out := make([]types.EyrieMessage, len(msgs)) for i, m := range msgs { - out[i] = types.GraycodeRouterMessage{Role: m.Role, Content: m.Content} + out[i] = types.EyrieMessage{Role: m.Role, Content: m.Content} } return out } -func hasErrorResult(m types.GraycodeRouterMessage) bool { +func hasErrorResult(m types.EyrieMessage) bool { for _, tr := range m.ToolResults { if tr.IsError { return true @@ -86,7 +86,7 @@ func hasErrorResult(m types.GraycodeRouterMessage) bool { return false } -func lastUserText(raw []types.GraycodeRouterMessage) string { +func lastUserText(raw []types.EyrieMessage) string { for i := len(raw) - 1; i >= 0; i-- { if raw[i].Role == "user" { return raw[i].Content diff --git a/internal/engine/compact_relevance_test.go b/internal/engine/compact_relevance_test.go index 917b7f2de..24295ba22 100644 --- a/internal/engine/compact_relevance_test.go +++ b/internal/engine/compact_relevance_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestRelevancePruneStrategy_ShouldTrigger(t *testing.T) { @@ -21,11 +21,11 @@ func TestRelevancePruneStrategy_ShouldTrigger(t *testing.T) { func TestRelevancePruneStrategy_Compact(t *testing.T) { sess := NewSessionWithClient(NewMockClientForTest(), "test", "test-model", "", nil, false) // A long transcript; last user message is the task context. - var msgs []types.GraycodeRouterMessage + var msgs []types.EyrieMessage for i := 0; i < 60; i++ { - msgs = append(msgs, types.GraycodeRouterMessage{Role: "user", Content: "unrelated filler content that has no shared keywords"}) + msgs = append(msgs, types.EyrieMessage{Role: "user", Content: "unrelated filler content that has no shared keywords"}) } - msgs = append(msgs, types.GraycodeRouterMessage{Role: "user", Content: "refactor the payment module and update all tests"}) + msgs = append(msgs, types.EyrieMessage{Role: "user", Content: "refactor the payment module and update all tests"}) sess.Persistence().SetRawMessages(msgs) s := &RelevancePruneStrategy{TargetTokens: 200} @@ -54,7 +54,7 @@ func TestRelevancePruneStrategy_Compact(t *testing.T) { } func TestToPruneMessagesAdapter(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hi"}, {Role: "assistant", Content: "ok", ToolUse: []types.ToolCall{{Name: "Bash"}}}, {Role: "user", Content: "err", ToolResults: []types.ToolResult{{IsError: true}}}, diff --git a/internal/engine/compact_session_memory_engine.go b/internal/engine/compact_session_memory_engine.go index e8223d217..2c48a58f8 100644 --- a/internal/engine/compact_session_memory_engine.go +++ b/internal/engine/compact_session_memory_engine.go @@ -6,16 +6,16 @@ import ( "os" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" + "github.com/GrayCodeAI/hawk/internal/engine/compact" ) type SessionMemoryStrategy struct{} func (s *SessionMemoryStrategy) Name() string { return "session_memory" } -func (s *SessionMemoryStrategy) ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool { +func (s *SessionMemoryStrategy) ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool { if tokenCount < threshold { return false } @@ -50,12 +50,12 @@ func (s *SessionMemoryStrategy) Compact(ctx context.Context, sess *Session) (*Co kept := messages[keepIdx:] kept = compact.FilterCompactBoundaries(kept) - result := make([]types.GraycodeRouterMessage, 0, len(kept)+2) - result = append(result, types.GraycodeRouterMessage{ + result := make([]types.EyrieMessage, 0, len(kept)+2) + result = append(result, types.EyrieMessage{ Role: "user", Content: "[Session memory summary]\n" + memContent + "\n\n[Continue from the recent messages below.]", }) - result = append(result, types.GraycodeRouterMessage{ + result = append(result, types.EyrieMessage{ Role: "assistant", Content: "Understood. I have the context from the session memory above. Continuing with the recent conversation.", }) diff --git a/internal/engine/compact_split.go b/internal/engine/compact_split.go index ae75cc29b..2c24bc232 100644 --- a/internal/engine/compact_split.go +++ b/internal/engine/compact_split.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // splitTurnCompact handles the edge case where a single turn's messages @@ -21,7 +21,7 @@ import ( // average token count of the tail, with a 2000-token floor to avoid false // positives. A single message exceeding this budget is treated as an // oversized turn. -func turnTokenBudget(tail []types.GraycodeRouterMessage) int { +func turnTokenBudget(tail []types.EyrieMessage) int { if len(tail) == 0 { return 0 } @@ -131,7 +131,7 @@ func (s *Session) splitTurnCompact(ctx context.Context) { // Reconstruct messages: summary + tail from oversized turn onward // Keep the second half of the oversized message + everything after // (its first half is covered by phase2Summary above). - remaining := append([]types.GraycodeRouterMessage(nil), raw[splitPoint:]...) + remaining := append([]types.EyrieMessage(nil), raw[splitPoint:]...) if len(remaining) > 0 { first := remaining[0] first.Content = secondHalfRunes(first.Content) @@ -141,8 +141,8 @@ func (s *Session) splitTurnCompact(ctx context.Context) { remaining[0] = first } - keep := make([]types.GraycodeRouterMessage, 0, len(remaining)+2) - keep = append(keep, types.GraycodeRouterMessage{ + keep := make([]types.EyrieMessage, 0, len(remaining)+2) + keep = append(keep, types.EyrieMessage{ Role: "user", Content: combined.String() + "\n\n[Continue from the recent messages below.]", }) @@ -150,7 +150,7 @@ func (s *Session) splitTurnCompact(ctx context.Context) { // message; adjacent assistant messages are rejected by OpenAI-compat // providers. if len(remaining) == 0 || remaining[0].Role != "assistant" { - keep = append(keep, types.GraycodeRouterMessage{ + keep = append(keep, types.EyrieMessage{ Role: "assistant", Content: "Understood. I have the context from the summary above. Continuing.", }) @@ -170,12 +170,12 @@ func secondHalfRunes(s string) string { } // generatePartialSummary generates an LLM summary for a subset of messages. -func (s *Session) generatePartialSummary(ctx context.Context, messages []types.GraycodeRouterMessage) string { +func (s *Session) generatePartialSummary(ctx context.Context, messages []types.EyrieMessage) string { if len(messages) == 0 { return "" } - var summaryMsgs []types.GraycodeRouterMessage + var summaryMsgs []types.EyrieMessage compactPrompt := BuildCompactPrompt(CompactPartial) var content strings.Builder content.WriteString(compactPrompt) @@ -191,7 +191,7 @@ func (s *Session) generatePartialSummary(ctx context.Context, messages []types.G } } - summaryMsgs = append(summaryMsgs, types.GraycodeRouterMessage{ + summaryMsgs = append(summaryMsgs, types.EyrieMessage{ Role: "user", Content: content.String(), }) @@ -215,7 +215,7 @@ func (s *Session) generatePartialSummary(ctx context.Context, messages []types.G } // summarizeOversizedTurn summarizes the content of a single oversized message. -func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.GraycodeRouterMessage) string { +func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.EyrieMessage) string { content := msg.Content if content == "" { // If it's a tool result, use that @@ -234,8 +234,8 @@ func (s *Session) summarizeOversizedTurn(ctx context.Context, msg types.Graycode halfLen = 4000 } - var summaryMsgs []types.GraycodeRouterMessage - summaryMsgs = append(summaryMsgs, types.GraycodeRouterMessage{ + var summaryMsgs []types.EyrieMessage + summaryMsgs = append(summaryMsgs, types.EyrieMessage{ Role: "user", Content: BuildCompactPrompt(CompactUpTo) + "\n\nSummarize the key information from this content prefix (the rest will be retained verbatim):\n\n" + msg.Role + ": " + truncateRunes(content, halfLen), diff --git a/internal/engine/compact_strategy_engine.go b/internal/engine/compact_strategy_engine.go index 4056cf909..ace86f8d5 100644 --- a/internal/engine/compact_strategy_engine.go +++ b/internal/engine/compact_strategy_engine.go @@ -3,12 +3,12 @@ package engine import ( "context" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) type CompactStrategy interface { Name() string - ShouldTrigger(msgs []types.GraycodeRouterMessage, tokenCount, threshold int) bool + ShouldTrigger(msgs []types.EyrieMessage, tokenCount, threshold int) bool Compact(ctx context.Context, s *Session) (*CompactResult, error) } @@ -31,7 +31,7 @@ func NewStrategyRegistry(config CompactConfig) *StrategyRegistry { return r } -func (r *StrategyRegistry) SelectStrategy(sess *Session, msgs []types.GraycodeRouterMessage, tokenCount int) CompactStrategy { +func (r *StrategyRegistry) SelectStrategy(sess *Session, msgs []types.EyrieMessage, tokenCount int) CompactStrategy { threshold := r.config.ContextWindowSize - r.config.AutoCompactBuffer - r.config.MaxOutputTokens for _, s := range r.strategies { if _, ok := s.(*ProviderNativeCompactStrategy); ok && (sess == nil || !sess.supportsNativeCompaction()) { diff --git a/internal/engine/compact_strategy_test.go b/internal/engine/compact_strategy_test.go index 4144c5e94..7b5c6f50f 100644 --- a/internal/engine/compact_strategy_test.go +++ b/internal/engine/compact_strategy_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestSessionMemoryStrategy_ShouldTrigger(t *testing.T) { @@ -100,14 +100,14 @@ func TestTurnTokenBudget(t *testing.T) { t.Errorf("turnTokenBudget(nil) = %d, want 0", got) } - small := []types.GraycodeRouterMessage{{Role: "user", Content: "short"}} + small := []types.EyrieMessage{{Role: "user", Content: "short"}} if got := turnTokenBudget(small); got != 2000 { t.Errorf("turnTokenBudget(small) = %d, want minimum floor 2000", got) } - big := make([]types.GraycodeRouterMessage, 10) + big := make([]types.EyrieMessage, 10) for i := range big { - big[i] = types.GraycodeRouterMessage{Role: "user", Content: strings.Repeat("x", 10000)} + big[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("x", 10000)} } avg := EstimateMessageTokens(big[0]) if avg*3 <= 2000 { @@ -118,13 +118,13 @@ func TestTurnTokenBudget(t *testing.T) { } } -func makeMessages(n int) []types.GraycodeRouterMessage { - msgs := make([]types.GraycodeRouterMessage, n) +func makeMessages(n int) []types.EyrieMessage { + msgs := make([]types.EyrieMessage, n) for i := range msgs { if i%2 == 0 { - msgs[i] = types.GraycodeRouterMessage{Role: "user", Content: strings.Repeat("message ", 50)} + msgs[i] = types.EyrieMessage{Role: "user", Content: strings.Repeat("message ", 50)} } else { - msgs[i] = types.GraycodeRouterMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} + msgs[i] = types.EyrieMessage{Role: "assistant", Content: strings.Repeat("response ", 50)} } } return msgs diff --git a/internal/engine/compression/cross_session.go b/internal/engine/compression/cross_session.go index 5b3f8a99e..0180fc633 100644 --- a/internal/engine/compression/cross_session.go +++ b/internal/engine/compression/cross_session.go @@ -10,8 +10,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/mathutil" - "github.com/GrayCodeAI/graycode-cli/internal/safewrite" + "github.com/GrayCodeAI/hawk/internal/mathutil" + "github.com/GrayCodeAI/hawk/internal/safewrite" ) // Insight represents a learned insight from a previous session. diff --git a/internal/engine/compression/session_timeline.go b/internal/engine/compression/session_timeline.go index 70ba85cea..bedea5767 100644 --- a/internal/engine/compression/session_timeline.go +++ b/internal/engine/compression/session_timeline.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // TimelineEvent represents a single event in a session timeline. diff --git a/internal/engine/compression/session_timeline_test.go b/internal/engine/compression/session_timeline_test.go index e1db2653a..5500cede5 100644 --- a/internal/engine/compression/session_timeline_test.go +++ b/internal/engine/compression/session_timeline_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewTimeline(t *testing.T) { diff --git a/internal/engine/context_compaction.go b/internal/engine/context_compaction.go index b5756b70a..97e510925 100644 --- a/internal/engine/context_compaction.go +++ b/internal/engine/context_compaction.go @@ -3,10 +3,10 @@ package engine import ( "path/filepath" - "github.com/GrayCodeAI/graycode-cli/internal/eventlog" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/storage" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/eventlog" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/hawk/internal/types" ) // CompactionEvent describes a completed context compaction. @@ -20,7 +20,7 @@ type CompactionEvent struct { // OnCompaction is invoked after compaction (TUI, logging, etc.). type OnCompaction func(CompactionEvent) -// PersistID is the on-disk session id under Graycode's user state sessions dir. +// PersistID is the on-disk session id under Hawk's user state sessions dir. // Set from the TUI when a chat session is created or resumed. func (s *Session) SetPersistID(id string) { if s == nil { @@ -134,7 +134,7 @@ func (s *Session) checkpointManager() *session.CheckpointManager { return p.CheckpointManager() } -func rawToSessionMessages(raw []types.GraycodeRouterMessage) []session.Message { +func rawToSessionMessages(raw []types.EyrieMessage) []session.Message { return session.FromRuntimeMessages(raw) } diff --git a/internal/engine/context_compaction_test.go b/internal/engine/context_compaction_test.go index 012c14c9c..bdb89ea50 100644 --- a/internal/engine/context_compaction_test.go +++ b/internal/engine/context_compaction_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" - "github.com/GrayCodeAI/graycode-router/credentials" - graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" + "github.com/GrayCodeAI/eyrie/credentials" + eyrieengine "github.com/GrayCodeAI/eyrie/engine" + "github.com/GrayCodeAI/hawk/internal/provider/gateway" ) func TestContextUsedTokens_PrefersAPI(t *testing.T) { @@ -21,21 +21,21 @@ func TestContextUsedTokens_PrefersAPI(t *testing.T) { } } -func TestNativeCompactionSupportUsesGraycodeRouterCredentialStore(t *testing.T) { +func TestNativeCompactionSupportUsesEyrieCredentialStore(t *testing.T) { ctx := context.Background() store := &credentials.MapStore{} - runtime, err := graycoderouterengine.New(graycoderouterengine.Options{SecretStore: store}) + runtime, err := eyrieengine.New(eyrieengine.Options{SecretStore: store}) if err != nil { t.Fatal(err) } s := NewSessionWithClient(gateway.NewFromEngine(runtime).ChatClient(), "anthropic", "claude-sonnet-4-6", "sys", nil, true) if s.supportsNativeCompaction() { - t.Fatal("expected no support before GraycodeRouter has a credential") + t.Fatal("expected no support before Eyrie has a credential") } if err := store.Set(ctx, credentials.AccountForEnv("ANTHROPIC_API_KEY"), "sk-test"); err != nil { t.Fatal(err) } if !s.supportsNativeCompaction() { - t.Fatal("expected support from GraycodeRouter's injected credential store") + t.Fatal("expected support from Eyrie's injected credential store") } } diff --git a/internal/engine/context_governor.go b/internal/engine/context_governor.go index 348a77ebd..017a9546d 100644 --- a/internal/engine/context_governor.go +++ b/internal/engine/context_governor.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/GrayCodeAI/graycode-cli/internal/engine/ctxmgr" - modelPkg "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/engine/ctxmgr" + modelPkg "github.com/GrayCodeAI/hawk/internal/provider/routing" ) const ( diff --git a/internal/engine/context_governor_test.go b/internal/engine/context_governor_test.go index abc84add3..596f4df44 100644 --- a/internal/engine/context_governor_test.go +++ b/internal/engine/context_governor_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestResolveModelContextWindow_Fallback(t *testing.T) { @@ -38,7 +38,7 @@ func TestMaybeSpillToolOutput_SmallUnchanged(t *testing.T) { } func TestMaybeSpillToolOutput_LargeSpills(t *testing.T) { - t.Setenv("GRAYCODE_CACHE_DIR", t.TempDir()) + t.Setenv("HAWK_CACHE_DIR", t.TempDir()) in := strings.Repeat("x", toolOutputSpillMinChars+100) got := maybeSpillToolOutput(in, "Bash", "call-1") if !strings.Contains(got, "/scratch/") { @@ -51,7 +51,7 @@ func TestMaybeSpillToolOutput_LargeSpills(t *testing.T) { func TestManageContextBeforeTurn_CollapseOnly(t *testing.T) { s := NewSession("", "test-model", "sys", nil) - s.Persistence().SetRawMessages([]types.GraycodeRouterMessage{ + s.Persistence().SetRawMessages([]types.EyrieMessage{ {Role: "user", ToolResults: []types.ToolResult{{Content: "err", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{Content: "err", IsError: true}}}, }) diff --git a/internal/engine/control/backtrack.go b/internal/engine/control/backtrack.go index a4fb8a0af..4fa78b0dd 100644 --- a/internal/engine/control/backtrack.go +++ b/internal/engine/control/backtrack.go @@ -4,17 +4,17 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // DecisionPoint captures a point in the conversation where the agent made a // choice that can potentially be rolled back if it leads to failure. type DecisionPoint struct { TurnIndex int - Description string // what was decided - Alternatives []string // other options available - Outcome string // "success", "failure", or "" (pending) - Messages []types.GraycodeRouterMessage // conversation state at this point + Description string // what was decided + Alternatives []string // other options available + Outcome string // "success", "failure", or "" (pending) + Messages []types.EyrieMessage // conversation state at this point } // BacktrackEngine records decision points during agent execution and provides @@ -36,9 +36,9 @@ func NewBacktrackEngine() *BacktrackEngine { // RecordDecision saves a decision point with the current conversation state. // If the number of recorded points exceeds the maximum, the oldest point is // removed. -func (be *BacktrackEngine) RecordDecision(turnIdx int, desc string, alternatives []string, msgs []types.GraycodeRouterMessage) { +func (be *BacktrackEngine) RecordDecision(turnIdx int, desc string, alternatives []string, msgs []types.EyrieMessage) { // Copy messages to avoid mutation from caller - snapshot := make([]types.GraycodeRouterMessage, len(msgs)) + snapshot := make([]types.EyrieMessage, len(msgs)) copy(snapshot, msgs) // Copy alternatives @@ -110,12 +110,12 @@ func (be *BacktrackEngine) GenerateRetryPrompt(dp *DecisionPoint) string { // RestoreState returns the conversation messages captured at the given decision // point, representing the state just before (not including) the failed // decision. This allows the agent to retry from that point. -func (be *BacktrackEngine) RestoreState(dp *DecisionPoint) []types.GraycodeRouterMessage { +func (be *BacktrackEngine) RestoreState(dp *DecisionPoint) []types.EyrieMessage { if dp == nil { return nil } // Return a copy to prevent mutation - restored := make([]types.GraycodeRouterMessage, len(dp.Messages)) + restored := make([]types.EyrieMessage, len(dp.Messages)) copy(restored, dp.Messages) return restored } diff --git a/internal/engine/control/backtrack_test.go b/internal/engine/control/backtrack_test.go index 6a71b3d33..f262363c1 100644 --- a/internal/engine/control/backtrack_test.go +++ b/internal/engine/control/backtrack_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestNewBacktrackEngine(t *testing.T) { @@ -23,7 +23,7 @@ func TestNewBacktrackEngine(t *testing.T) { func TestBacktrackEngine_RecordAndFind(t *testing.T) { be := NewBacktrackEngine() - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "Fix the bug"}, {Role: "assistant", Content: "I'll try approach A"}, } @@ -61,7 +61,7 @@ func TestBacktrackEngine_RecordAndFind(t *testing.T) { func TestBacktrackEngine_GenerateRetryPrompt(t *testing.T) { be := NewBacktrackEngine() - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "Refactor the function"}, } @@ -94,7 +94,7 @@ func TestBacktrackEngine_GenerateRetryPrompt(t *testing.T) { func TestBacktrackEngine_RestoreState(t *testing.T) { be := NewBacktrackEngine() - originalMsgs := []types.GraycodeRouterMessage{ + originalMsgs := []types.EyrieMessage{ {Role: "user", Content: "Step one"}, {Role: "assistant", Content: "Done with step one"}, {Role: "user", Content: "Step two"}, diff --git a/internal/engine/control/stall_detector.go b/internal/engine/control/stall_detector.go index 4224ae874..11ee29962 100644 --- a/internal/engine/control/stall_detector.go +++ b/internal/engine/control/stall_detector.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // StallEntry represents a single recorded tool invocation in the stall detection window. diff --git a/internal/engine/control/stall_detector_test.go b/internal/engine/control/stall_detector_test.go index e75bed227..b05c62855 100644 --- a/internal/engine/control/stall_detector_test.go +++ b/internal/engine/control/stall_detector_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewStallDetector(t *testing.T) { diff --git a/internal/engine/control_plane_test.go b/internal/engine/control_plane_test.go index 1ca443abc..4b5a06179 100644 --- a/internal/engine/control_plane_test.go +++ b/internal/engine/control_plane_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestParseIsolationProfile(t *testing.T) { @@ -71,19 +71,19 @@ func TestWorkModePlanFiltersToolsAndBash(t *testing.T) { } } -func TestLazyGraycodeRouterToolsAndPromote(t *testing.T) { +func TestLazyEyrieToolsAndPromote(t *testing.T) { reg := tool.NewRegistry(tool.FileReadTool{}, tool.ImpactTool{}) reg.EnableLazyModelSurface([]string{"Read"}) - graycodeRouter := reg.GraycodeRouterTools() - if len(graycodeRouter) != 1 || graycodeRouter[0].Name != "Read" { - t.Fatalf("GraycodeRouterTools = %#v, want only Read", graycodeRouter) + eyrie := reg.EyrieTools() + if len(eyrie) != 1 || eyrie[0].Name != "Read" { + t.Fatalf("EyrieTools = %#v, want only Read", eyrie) } if !reg.PromoteModelTool("Impact") { t.Fatal("promote Impact failed") } - graycodeRouter = reg.GraycodeRouterTools() - if len(graycodeRouter) != 2 { - t.Fatalf("after promote GraycodeRouterTools len = %d", len(graycodeRouter)) + eyrie = reg.EyrieTools() + if len(eyrie) != 2 { + t.Fatalf("after promote EyrieTools len = %d", len(eyrie)) } } diff --git a/internal/engine/control_reexports.go b/internal/engine/control_reexports.go index 0614917ae..9fdd82497 100644 --- a/internal/engine/control_reexports.go +++ b/internal/engine/control_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/graycode-cli/internal/engine/control" +import "github.com/GrayCodeAI/hawk/internal/engine/control" type ( LoopDetector = control.LoopDetector diff --git a/internal/engine/conversation_adapter.go b/internal/engine/conversation_adapter.go index bd5c2c2b3..2046361aa 100644 --- a/internal/engine/conversation_adapter.go +++ b/internal/engine/conversation_adapter.go @@ -28,8 +28,8 @@ type ConversationState struct { Summary string } -// ConversationManager manages a conversation lifecycle, bridging graycode-router's -// conversation management into graycode's chat session flow. It is safe for +// ConversationManager manages a conversation lifecycle, bridging eyrie's +// conversation management into hawk's chat session flow. It is safe for // concurrent use. type ConversationManager struct { mu sync.RWMutex diff --git a/internal/engine/cost/aliases.go b/internal/engine/cost/aliases.go index ec8e849e7..ca0f62bb3 100644 --- a/internal/engine/cost/aliases.go +++ b/internal/engine/cost/aliases.go @@ -1,3 +1,3 @@ // Package cost provides cost tracking, optimisation, and display -// for the graycode engine. See ../../docs/plans/engine-refactor-plan.md. +// for the hawk engine. See ../../docs/plans/engine-refactor-plan.md. package cost diff --git a/internal/engine/cost/cost_optimizer.go b/internal/engine/cost/cost_optimizer.go index c39a2604e..f14714bca 100644 --- a/internal/engine/cost/cost_optimizer.go +++ b/internal/engine/cost/cost_optimizer.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) type CostOptimizer struct { diff --git a/internal/engine/cost/cost_optimizer_test.go b/internal/engine/cost/cost_optimizer_test.go index a75a83121..260107c15 100644 --- a/internal/engine/cost/cost_optimizer_test.go +++ b/internal/engine/cost/cost_optimizer_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) const testProvider = "anthropic" diff --git a/internal/engine/cost/cost_table.go b/internal/engine/cost/cost_table.go index 408d4408f..9ca6791cc 100644 --- a/internal/engine/cost/cost_table.go +++ b/internal/engine/cost/cost_table.go @@ -4,7 +4,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // Tier-based default pricing when catalog data is unavailable. diff --git a/internal/engine/cost/cost_tracker.go b/internal/engine/cost/cost_tracker.go index bffcf2456..36fd45484 100644 --- a/internal/engine/cost/cost_tracker.go +++ b/internal/engine/cost/cost_tracker.go @@ -8,8 +8,8 @@ import ( "sync" "time" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + analytics "github.com/GrayCodeAI/hawk/internal/observability" + "github.com/GrayCodeAI/hawk/internal/storage" ) type CostTracker struct { diff --git a/internal/engine/cost/cost_tracker_test.go b/internal/engine/cost/cost_tracker_test.go index a9d3f510f..aca7ffdee 100644 --- a/internal/engine/cost/cost_tracker_test.go +++ b/internal/engine/cost/cost_tracker_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" - "github.com/GrayCodeAI/graycode-cli/internal/testutil" + analytics "github.com/GrayCodeAI/hawk/internal/observability" + "github.com/GrayCodeAI/hawk/internal/testutil" ) func TestCostTracker_NewAndRecord(t *testing.T) { diff --git a/internal/engine/cost_reexports.go b/internal/engine/cost_reexports.go index f395491a6..e0a1bab5d 100644 --- a/internal/engine/cost_reexports.go +++ b/internal/engine/cost_reexports.go @@ -1,8 +1,8 @@ package engine import ( - "github.com/GrayCodeAI/graycode-cli/internal/engine/cost" - analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + "github.com/GrayCodeAI/hawk/internal/engine/cost" + analytics "github.com/GrayCodeAI/hawk/internal/observability" ) type ( diff --git a/internal/engine/council.go b/internal/engine/council.go index 0f5399dc8..9fc3052ae 100644 --- a/internal/engine/council.go +++ b/internal/engine/council.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - routing "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + routing "github.com/GrayCodeAI/hawk/internal/provider/routing" ) // CouncilConfig controls the Karpathy LLM Council pattern. diff --git a/internal/engine/ctxmgr/context_budget.go b/internal/engine/ctxmgr/context_budget.go index 9e587a46b..b434d1955 100644 --- a/internal/engine/ctxmgr/context_budget.go +++ b/internal/engine/ctxmgr/context_budget.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/mathutil" + "github.com/GrayCodeAI/hawk/internal/mathutil" ) // ContextBudget allocates the model's context window across different content categories. diff --git a/internal/engine/ctxmgr/context_collapse.go b/internal/engine/ctxmgr/context_collapse.go index e4a4eba59..b7c139024 100644 --- a/internal/engine/ctxmgr/context_collapse.go +++ b/internal/engine/ctxmgr/context_collapse.go @@ -4,19 +4,19 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // CollapseRepeatedMessages finds and collapses similar consecutive messages to // save context tokens. It collapses: // - 3+ consecutive tool_results with similar content into a summary // - Repeated error messages into a count -func CollapseRepeatedMessages(msgs []types.GraycodeRouterMessage) []types.GraycodeRouterMessage { +func CollapseRepeatedMessages(msgs []types.EyrieMessage) []types.EyrieMessage { if len(msgs) < 3 { return msgs } - result := make([]types.GraycodeRouterMessage, 0, len(msgs)) + result := make([]types.EyrieMessage, 0, len(msgs)) i := 0 for i < len(msgs) { @@ -30,7 +30,7 @@ func CollapseRepeatedMessages(msgs []types.GraycodeRouterMessage) []types.Grayco runLen := j - i if runLen >= 2 { errText := extractErrorText(msgs[i]) - result = append(result, types.GraycodeRouterMessage{ + result = append(result, types.EyrieMessage{ Role: msgs[i].Role, Content: fmt.Sprintf("[Error repeated %d times: %s]", runLen, errText), ToolResults: msgs[i].ToolResults, @@ -52,7 +52,7 @@ func CollapseRepeatedMessages(msgs []types.GraycodeRouterMessage) []types.Grayco result = append(result, msgs[i]) toolName := toolResultSource(msgs[i]) collapsed := runLen - 2 - result = append(result, types.GraycodeRouterMessage{ + result = append(result, types.EyrieMessage{ Role: "user", Content: fmt.Sprintf("[Similar output from %s — %d results collapsed]", toolName, collapsed), ToolResults: []types.ToolResult{{ @@ -76,7 +76,7 @@ func CollapseRepeatedMessages(msgs []types.GraycodeRouterMessage) []types.Grayco // isSimilarToolResult checks whether two tool_result messages are similar // enough to collapse. Two results are similar if they come from the same tool // and their content shares the same first line or prefix (up to 100 chars). -func isSimilarToolResult(a, b types.GraycodeRouterMessage) bool { +func isSimilarToolResult(a, b types.EyrieMessage) bool { if len(a.ToolResults) == 0 || len(b.ToolResults) == 0 { return false } @@ -88,7 +88,7 @@ func isSimilarToolResult(a, b types.GraycodeRouterMessage) bool { } // toolResultSource extracts the tool name from a tool_result message. -func toolResultSource(msg types.GraycodeRouterMessage) string { +func toolResultSource(msg types.EyrieMessage) string { if len(msg.ToolResults) > 0 && msg.ToolResults[0].ToolUseID != "" { return msg.ToolResults[0].ToolUseID } @@ -108,7 +108,7 @@ func contentPrefix(s string, n int) string { } // isErrorMessage returns true if a message appears to be an error. -func isErrorMessage(msg types.GraycodeRouterMessage) bool { +func isErrorMessage(msg types.EyrieMessage) bool { if len(msg.ToolResults) > 0 && msg.ToolResults[0].IsError { return true } @@ -117,7 +117,7 @@ func isErrorMessage(msg types.GraycodeRouterMessage) bool { } // extractErrorText extracts the error text from an error message. -func extractErrorText(msg types.GraycodeRouterMessage) string { +func extractErrorText(msg types.EyrieMessage) string { if len(msg.ToolResults) > 0 && msg.ToolResults[0].IsError { return msg.ToolResults[0].Content } diff --git a/internal/engine/ctxmgr/context_collapse_test.go b/internal/engine/ctxmgr/context_collapse_test.go index cef35b3cc..812b21c93 100644 --- a/internal/engine/ctxmgr/context_collapse_test.go +++ b/internal/engine/ctxmgr/context_collapse_test.go @@ -3,11 +3,11 @@ package ctxmgr import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestCollapseRepeatedMessages_NoCollapse(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "assistant", Content: "hi"}, {Role: "user", Content: "bye"}, @@ -19,7 +19,7 @@ func TestCollapseRepeatedMessages_NoCollapse(t *testing.T) { } func TestCollapseRepeatedMessages_ToolResults(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\ndetails..."}}}, {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\nother details"}}}, {Role: "user", Content: "run tests", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "PASS: all tests passed\nmore details"}}}, @@ -33,7 +33,7 @@ func TestCollapseRepeatedMessages_ToolResults(t *testing.T) { } func TestCollapseRepeatedMessages_Errors(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t1", Content: "connection refused", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t2", Content: "connection refused", IsError: true}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "t3", Content: "connection refused", IsError: true}}}, @@ -48,7 +48,7 @@ func TestCollapseRepeatedMessages_Errors(t *testing.T) { } func TestCollapseRepeatedMessages_MixedContent(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hello"}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "ok\ndetails"}}}, {Role: "user", ToolResults: []types.ToolResult{{ToolUseID: "Bash", Content: "ok\nother"}}}, @@ -62,7 +62,7 @@ func TestCollapseRepeatedMessages_MixedContent(t *testing.T) { } func TestCollapseRepeatedMessages_ShortInput(t *testing.T) { - msgs := []types.GraycodeRouterMessage{ + msgs := []types.EyrieMessage{ {Role: "user", Content: "hi"}, } result := CollapseRepeatedMessages(msgs) diff --git a/internal/engine/ctxmgr/context_decay.go b/internal/engine/ctxmgr/context_decay.go index 5a3528d93..34eb60a53 100644 --- a/internal/engine/ctxmgr/context_decay.go +++ b/internal/engine/ctxmgr/context_decay.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // ContextDecay manages context entries with time-based importance decay. diff --git a/internal/engine/ctxmgr/context_decay_test.go b/internal/engine/ctxmgr/context_decay_test.go index 212385ce4..099554c69 100644 --- a/internal/engine/ctxmgr/context_decay_test.go +++ b/internal/engine/ctxmgr/context_decay_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewContextDecay(t *testing.T) { diff --git a/internal/engine/ctxmgr/context_viz.go b/internal/engine/ctxmgr/context_viz.go index 9f3f37ba7..c2ba19450 100644 --- a/internal/engine/ctxmgr/context_viz.go +++ b/internal/engine/ctxmgr/context_viz.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // ContextVisualizer provides a real-time view of context window usage, diff --git a/internal/engine/ctxmgr/context_viz_test.go b/internal/engine/ctxmgr/context_viz_test.go index 3ee689359..040de0145 100644 --- a/internal/engine/ctxmgr/context_viz_test.go +++ b/internal/engine/ctxmgr/context_viz_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestNewContextVisualizer(t *testing.T) { diff --git a/internal/engine/ctxmgr/incremental.go b/internal/engine/ctxmgr/incremental.go index a66bd4e1a..e6a0834cf 100644 --- a/internal/engine/ctxmgr/incremental.go +++ b/internal/engine/ctxmgr/incremental.go @@ -4,11 +4,11 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/graycode-cli/internal/systemcontext" + "github.com/GrayCodeAI/hawk/internal/systemcontext" ) // Section defines a dynamic, incrementally-rendered system-prompt section. It -// is the graycode-host integration of the systemcontext package: each section is a +// is the hawk-host integration of the systemcontext package: each section is a // typed context source whose value is loaded on demand, and only sections that // actually change are re-rendered as a mid-conversation update rather than // rebuilding the entire system prompt. @@ -146,7 +146,7 @@ func renderSection(header, value string) string { func keyScope(key string) string { // A section key like "memories" or "scope/name" maps to a valid // namespaced source key. - return "graycode" + return "hawk" } func keyName(key string) string { @@ -154,7 +154,7 @@ func keyName(key string) string { } // DefaultIncrementalSections returns a reasonable set of dynamic sections for -// a session, wired to the same loaders graycode already uses for each. It is a +// a session, wired to the same loaders hawk already uses for each. It is a // convenience for hosts that want to enable incremental context without // hand-assembling sections. Loaders may be nil-able wrappers; the returned // sections must be configured with their Load funcs by the caller. diff --git a/internal/engine/delegated_subagent_test.go b/internal/engine/delegated_subagent_test.go index 5401e3d82..c4c5c9cd8 100644 --- a/internal/engine/delegated_subagent_test.go +++ b/internal/engine/delegated_subagent_test.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/eventlog" - "github.com/GrayCodeAI/graycode-cli/internal/sandbox" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/eventlog" + "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/hawk/internal/tool" ) func TestSubAgentDelegatedPolicy_InheritsParentExplicitMode(t *testing.T) { diff --git a/internal/engine/diff/diff_staging.go b/internal/engine/diff/diff_staging.go index d4cd5d885..a477322f9 100644 --- a/internal/engine/diff/diff_staging.go +++ b/internal/engine/diff/diff_staging.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // StagingArea provides a local staging area for agent edits, allowing review diff --git a/internal/engine/diff_reexports.go b/internal/engine/diff_reexports.go index c18792927..d1bd3706b 100644 --- a/internal/engine/diff_reexports.go +++ b/internal/engine/diff_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/graycode-cli/internal/engine/diff" +import "github.com/GrayCodeAI/hawk/internal/engine/diff" // Types from diff sub-package. diff --git a/internal/engine/directive_scanner.go b/internal/engine/directive_scanner.go index b8d241ef7..703e51046 100644 --- a/internal/engine/directive_scanner.go +++ b/internal/engine/directive_scanner.go @@ -9,7 +9,7 @@ import ( "strings" ) -// Directive is a parsed graycode: comment from source code. +// Directive is a parsed hawk: comment from source code. type Directive struct { File string Line int @@ -17,9 +17,9 @@ type Directive struct { Context string } -var graycodeDirectivePattern = regexp.MustCompile(`(?i)(?://|#|--|/\*)\s*graycode:\s*(.+?)(?:\s*\*/)?$`) +var hawkDirectivePattern = regexp.MustCompile(`(?i)(?://|#|--|/\*)\s*hawk:\s*(.+?)(?:\s*\*/)?$`) -// ScanDirectives finds all `// graycode: ` comments in source files. +// ScanDirectives finds all `// hawk: ` comments in source files. func ScanDirectives(dir string) []Directive { var directives []Directive _ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { @@ -28,7 +28,7 @@ func ScanDirectives(dir string) []Directive { } if d.IsDir() { name := d.Name() - if name == ".git" || name == "node_modules" || name == "vendor" || name == ".graycode" { + if name == ".git" || name == "node_modules" || name == "vendor" || name == ".hawk" { return filepath.SkipDir } return nil @@ -45,7 +45,7 @@ func ScanDirectives(dir string) []Directive { } lines := strings.Split(string(data), "\n") for i, line := range lines { - matches := graycodeDirectivePattern.FindStringSubmatch(line) + matches := hawkDirectivePattern.FindStringSubmatch(line) if len(matches) > 1 { start := i - 3 if start < 0 { @@ -73,5 +73,5 @@ func DirectivePrompt(d Directive) string { return "File: " + d.File + " (line " + fmt.Sprintf("%d", d.Line) + ")\n" + "Directive: " + d.Command + "\n" + "Context:\n```\n" + d.Context + "\n```\n\n" + - "Implement what the graycode: comment asks for. Remove the graycode: comment after implementing." + "Implement what the hawk: comment asks for. Remove the hawk: comment after implementing." } diff --git a/internal/engine/elision.go b/internal/engine/elision.go index 665a67fda..11470af47 100644 --- a/internal/engine/elision.go +++ b/internal/engine/elision.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/engine/token" ) // elisionNotice computes a verified-facts suffix for a truncation marker from diff --git a/internal/engine/elision_test.go b/internal/engine/elision_test.go index 62e4c7955..4ed192e0d 100644 --- a/internal/engine/elision_test.go +++ b/internal/engine/elision_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/token" + "github.com/GrayCodeAI/hawk/internal/token" ) func TestElisionNoticeJSONRecords(t *testing.T) { diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 307780090..bd699affb 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -4,7 +4,7 @@ import ( "context" "os" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) const ( @@ -106,9 +106,9 @@ func (s *Session) compact(ctx context.Context) { return // nothing to compact } - keep := make([]types.GraycodeRouterMessage, 0, len(raw)-(cutEnd-cutStart)+1) + keep := make([]types.EyrieMessage, 0, len(raw)-(cutEnd-cutStart)+1) keep = append(keep, raw[:cutStart]...) - keep = append(keep, types.GraycodeRouterMessage{ + keep = append(keep, types.EyrieMessage{ Role: "user", Content: "[Earlier conversation compacted to save context.]", }) diff --git a/internal/engine/engine_full_loop_test.go b/internal/engine/engine_full_loop_test.go index 9e39825ac..e83d0b252 100644 --- a/internal/engine/engine_full_loop_test.go +++ b/internal/engine/engine_full_loop_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) func TestEngine_FullLoop_TextOnly(t *testing.T) { @@ -69,14 +69,14 @@ func TestEngine_FullLoop_MultiTurn(t *testing.T) { func TestEngine_FullLoop_ToolUse(t *testing.T) { mc := newMockClient( - &types.GraycodeRouterResponse{ + &types.EyrieResponse{ FinishReason: "tool_use", ToolCalls: []types.ToolCall{{ ID: "call_1", Name: "Read", Arguments: map[string]interface{}{"file_path": "/nonexistent"}, }}, - Usage: &types.GraycodeRouterUsage{PromptTokens: 50, CompletionTokens: 30}, + Usage: &types.EyrieUsage{PromptTokens: 50, CompletionTokens: 30}, }, mockTextResponse("I read the file and here's what I found."), ) diff --git a/internal/engine/engine_integration_test.go b/internal/engine/engine_integration_test.go index 5998f9eaf..419204fae 100644 --- a/internal/engine/engine_integration_test.go +++ b/internal/engine/engine_integration_test.go @@ -5,11 +5,11 @@ import ( "testing" "time" - contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/types" + contracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/types" - "github.com/GrayCodeAI/graycode-cli/internal/session" - "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/hawk/internal/tool" ) // ────────────────────────────────────────────────────────────────────────────── @@ -68,14 +68,14 @@ func TestIntegration_FullSessionFlow(t *testing.T) { // Simulate the engine receiving an assistant text reply + tool call + tool result // by manually building the message sequence that agentLoop would produce. - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.GraycodeRouterMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ Role: "assistant", Content: "Sure, let me check that file.", ToolUse: []types.ToolCall{ {ID: "tc-1", Name: "Bash", Arguments: map[string]interface{}{"command": "echo hello"}}, }, })) - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.GraycodeRouterMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ Role: "user", ToolResults: []types.ToolResult{{ ToolUseID: "tc-1", @@ -83,7 +83,7 @@ func TestIntegration_FullSessionFlow(t *testing.T) { IsError: false, }}, })) - sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.GraycodeRouterMessage{ + sess.Persistence().SetRawMessages(append(sess.Persistence().RawMessages(), types.EyrieMessage{ Role: "assistant", Content: "The command ran successfully and returned 'hello'.", })) diff --git a/internal/engine/engine_stage2_test_helpers.go b/internal/engine/engine_stage2_test_helpers.go index 95b4c554a..00ed92802 100644 --- a/internal/engine/engine_stage2_test_helpers.go +++ b/internal/engine/engine_stage2_test_helpers.go @@ -3,7 +3,7 @@ package engine import ( "testing" - "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/hawk/internal/provider/routing" ) const testProvider = "anthropic" diff --git a/internal/engine/engine_token_helpers.go b/internal/engine/engine_token_helpers.go index 657642647..5602c027b 100644 --- a/internal/engine/engine_token_helpers.go +++ b/internal/engine/engine_token_helpers.go @@ -3,10 +3,10 @@ package engine import ( "strings" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) -func isRecentToolHeavy(messages []types.GraycodeRouterMessage) bool { +func isRecentToolHeavy(messages []types.EyrieMessage) bool { const lookback = 3 toolTurns := 0 assistantSeen := 0 @@ -25,7 +25,7 @@ func isRecentToolHeavy(messages []types.GraycodeRouterMessage) bool { return assistantSeen >= lookback && toolTurns == assistantSeen } -func isTextQuestion(messages []types.GraycodeRouterMessage) bool { +func isTextQuestion(messages []types.EyrieMessage) bool { var lastUserMsg string for i := len(messages) - 1; i >= 0; i-- { msg := messages[i] @@ -53,7 +53,7 @@ func isTextQuestion(messages []types.GraycodeRouterMessage) bool { return strings.HasSuffix(strings.TrimSpace(lower), "?") } -func classifyPromptForBudget(messages []types.GraycodeRouterMessage) string { +func classifyPromptForBudget(messages []types.EyrieMessage) string { if isRecentToolHeavy(messages) { return "tool" } diff --git a/internal/engine/errs/aliases.go b/internal/engine/errs/aliases.go index 791745b8c..286f88746 100644 --- a/internal/engine/errs/aliases.go +++ b/internal/engine/errs/aliases.go @@ -1,5 +1,5 @@ // Package errs provides error context enrichment, grouping, learning, -// patterns, and recovery for the graycode engine. +// patterns, and recovery for the hawk engine. // // Named "errs" (not "error") to avoid shadowing the builtin error type. package errs diff --git a/internal/engine/errs/error_context.go b/internal/engine/errs/error_context.go index 25273e8f8..3e8ab0fbb 100644 --- a/internal/engine/errs/error_context.go +++ b/internal/engine/errs/error_context.go @@ -410,7 +410,7 @@ func NewErrorContext() *ErrorContext { }, } - ec.Patterns["graycode_old_str_not_found"] = &ErrorHelp{ + ec.Patterns["hawk_old_str_not_found"] = &ErrorHelp{ Pattern: regexp.MustCompile(`old_str not found`), Title: "Edit target string not found", Explanation: "The text specified in old_str does not exist in the file. The file may have been modified since it was last read, or the string may contain whitespace or encoding differences.", @@ -427,7 +427,7 @@ func NewErrorContext() *ErrorContext { AutoFix: "Re-read the target file and retry with the exact current content", } - ec.Patterns["graycode_file_too_large"] = &ErrorHelp{ + ec.Patterns["hawk_file_too_large"] = &ErrorHelp{ Pattern: regexp.MustCompile(`file too large`), Title: "File exceeds size limit", Explanation: "The file is too large to be processed in a single operation. This protects against accidentally loading very large files into memory.", @@ -444,7 +444,7 @@ func NewErrorContext() *ErrorContext { AutoFix: "Use head_tail or line-range reads to process the file in parts", } - ec.Patterns["graycode_budget_exceeded"] = &ErrorHelp{ + ec.Patterns["hawk_budget_exceeded"] = &ErrorHelp{ Pattern: regexp.MustCompile(`budget exceeded`), Title: "Token or cost budget exceeded", Explanation: "The session has consumed more tokens or cost than the configured budget allows. This is a safety limit to prevent runaway costs.", @@ -455,12 +455,12 @@ func NewErrorContext() *ErrorContext { "Check for loops that may be inflating token usage", }, Examples: []string{ - "graycode --budget 10.00 # set a higher budget", + "hawk --budget 10.00 # set a higher budget", "/compact # reduce context size", }, } - ec.Patterns["graycode_tool_not_found"] = &ErrorHelp{ + ec.Patterns["hawk_tool_not_found"] = &ErrorHelp{ Pattern: regexp.MustCompile(`(tool not found|unknown tool)`), Title: "Tool not found", Explanation: "The requested tool does not exist in the current tool registry. It may be misspelled or not available in this configuration.", @@ -475,7 +475,7 @@ func NewErrorContext() *ErrorContext { }, } - ec.Patterns["graycode_sandbox_violation"] = &ErrorHelp{ + ec.Patterns["hawk_sandbox_violation"] = &ErrorHelp{ Pattern: regexp.MustCompile(`(sandbox violation|operation not permitted by sandbox)`), Title: "Sandbox security violation", Explanation: "The operation was blocked by the sandbox security policy. The command attempted to access a resource outside the allowed scope.", @@ -483,7 +483,7 @@ func NewErrorContext() *ErrorContext { "Check which paths are allowed by the sandbox configuration", "Request permission for the specific operation", "Verify the file is within the project directory", - "Check sandbox settings in Graycode user settings", + "Check sandbox settings in Hawk user settings", }, Examples: []string{ "// Ensure operations target files within the project root", diff --git a/internal/engine/errs/error_context_test.go b/internal/engine/errs/error_context_test.go index 145fdfa68..1aeafee9a 100644 --- a/internal/engine/errs/error_context_test.go +++ b/internal/engine/errs/error_context_test.go @@ -277,7 +277,7 @@ func TestEnrich_SysConnectionRefused(t *testing.T) { } } -func TestEnrich_GraycodeOldStrNotFound(t *testing.T) { +func TestEnrich_HawkOldStrNotFound(t *testing.T) { ec := NewErrorContext() enriched := ec.Enrich("edit failed: old_str not found in file.go") if enriched == nil { @@ -291,7 +291,7 @@ func TestEnrich_GraycodeOldStrNotFound(t *testing.T) { } } -func TestEnrich_GraycodeFileTooLarge(t *testing.T) { +func TestEnrich_HawkFileTooLarge(t *testing.T) { ec := NewErrorContext() enriched := ec.Enrich("cannot read: file too large (50MB)") if enriched == nil { @@ -302,7 +302,7 @@ func TestEnrich_GraycodeFileTooLarge(t *testing.T) { } } -func TestEnrich_GraycodeBudgetExceeded(t *testing.T) { +func TestEnrich_HawkBudgetExceeded(t *testing.T) { ec := NewErrorContext() enriched := ec.Enrich("session terminated: budget exceeded ($5.00 limit)") if enriched == nil { diff --git a/internal/engine/errs/error_patterns.go b/internal/engine/errs/error_patterns.go index 5a1a96e3b..8b7a9bab0 100644 --- a/internal/engine/errs/error_patterns.go +++ b/internal/engine/errs/error_patterns.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/hawk/internal/storage" ) type ErrorPattern struct { diff --git a/internal/engine/errs_reexports.go b/internal/engine/errs_reexports.go index d45e8fc22..6ae27a910 100644 --- a/internal/engine/errs_reexports.go +++ b/internal/engine/errs_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/graycode-cli/internal/engine/errs" +import "github.com/GrayCodeAI/hawk/internal/engine/errs" type ( ErrorContext = errs.ErrorContext diff --git a/internal/engine/event_bus.go b/internal/engine/event_bus.go index 1ae9b34e2..260eef33c 100644 --- a/internal/engine/event_bus.go +++ b/internal/engine/event_bus.go @@ -24,7 +24,7 @@ type Event struct { Payload interface{} } -// EventBus is a lightweight pub/sub system for decoupling graycode components. +// EventBus is a lightweight pub/sub system for decoupling hawk components. type EventBus struct { mu sync.RWMutex subs map[EventType][]chan Event diff --git a/internal/engine/execution_graph_observations.go b/internal/engine/execution_graph_observations.go index adea312ee..b7e072964 100644 --- a/internal/engine/execution_graph_observations.go +++ b/internal/engine/execution_graph_observations.go @@ -7,13 +7,13 @@ import ( "strings" "time" - graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" - policycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" - "github.com/GrayCodeAI/graycode-cli/internal/engine/token" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" - "github.com/GrayCodeAI/graycode-cli/internal/types" - graycoderouterengine "github.com/GrayCodeAI/graycode-router/engine" - graycoderoutergraph "github.com/GrayCodeAI/graycode-router/graph" + eyrieengine "github.com/GrayCodeAI/eyrie/engine" + eyriegraph "github.com/GrayCodeAI/eyrie/graph" + graphcontracts "github.com/GrayCodeAI/hawk/internal/contracts/graph" + policycontracts "github.com/GrayCodeAI/hawk/internal/contracts/policy" + "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/hawk/internal/graphjournal" + "github.com/GrayCodeAI/hawk/internal/types" shrikegraph "github.com/GrayCodeAI/shrike/graph" ) @@ -30,10 +30,10 @@ func (s *Session) recordPolicyObservation(tc types.ToolCall, stage string, allow } verdict := policycontracts.Allow(reason) verdict.Rule = strings.TrimSpace(stage) - verdict.Source = "graycode." + strings.TrimSpace(stage) + verdict.Source = "hawk." + strings.TrimSpace(stage) if !allowed { verdict = policycontracts.Deny(reason, strings.TrimSpace(stage)) - verdict.Source = "graycode." + strings.TrimSpace(stage) + verdict.Source = "hawk." + strings.TrimSpace(stage) } if err := graphjournal.AppendPolicy(sessionID, tc.ID, stage, verdict, time.Now()); err != nil { s.Logger().Warn("graph observation append failed", map[string]interface{}{ @@ -120,7 +120,7 @@ func (s *Session) SessionID() string { } // ConfigureContextGraphObservation binds Harrier recall projections to this -// persisted Graycode session. It is safe to call before either side is configured. +// persisted Hawk session. It is safe to call before either side is configured. func (s *Session) ConfigureContextGraphObservation(repositoryDir string) { if s == nil || s.MemorySvc() == nil || s.MemorySvc().Harrier() == nil { return @@ -291,10 +291,10 @@ func (s *Session) shrikeUsageCanProceed() (bool, string) { return tracker.CanProceed() } -func (s *Session) recordGraycodeRouterOperationObservation( +func (s *Session) recordEyrieOperationObservation( provider, model, finishReason, content string, toolCallCount int, - usage *types.GraycodeRouterUsage, + usage *types.EyrieUsage, ) { sessionID := s.executionGraphSessionID() if sessionID == "" || usage == nil { @@ -310,19 +310,19 @@ func (s *Session) recordGraycodeRouterOperationObservation( } observedAt := time.Now().UTC() route := types.ResolvedRoute{Provider: provider, Model: model} - export, err := graycoderouterengine.BuildOperationsGraph(graycoderouterengine.OperationsGraphInput{ + export, err := eyrieengine.BuildOperationsGraph(eyrieengine.OperationsGraphInput{ Route: &route, Usage: usage, FinishReason: finishReason, Content: content, ToolCallCount: toolCallCount, ObservedAt: observedAt, - Scope: graycoderoutergraph.Scope{RepositoryID: repositoryID}, + Scope: eyriegraph.Scope{RepositoryID: repositoryID}, CorrelationID: sessionID, }) if err == nil { err = graphjournal.AppendRuntimeGraph( - sessionID, "", "model-generation", "graycode-router", + sessionID, "", "model-generation", "eyrie", toContractNodes(export.Nodes), toContractEdges(export.Edges), toContractEvents(export.Events), observedAt, ) } @@ -334,11 +334,11 @@ func (s *Session) recordGraycodeRouterOperationObservation( } } -// The following helpers convert GraycodeRouter's vendored graph contract types into -// Graycode's contracts/graph contract types. The definitions are byte-identical, so +// The following helpers convert Eyrie's vendored graph contract types into +// Hawk's contracts/graph contract types. The definitions are byte-identical, so // conversion is a field-by-field copy at the sibling boundary. -func toContractNodes(nodes []graycoderoutergraph.Node) []graphcontracts.Node { +func toContractNodes(nodes []eyriegraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { out[i] = toContractNode(n) @@ -346,7 +346,7 @@ func toContractNodes(nodes []graycoderoutergraph.Node) []graphcontracts.Node { return out } -func toContractNode(n graycoderoutergraph.Node) graphcontracts.Node { +func toContractNode(n eyriegraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), @@ -358,7 +358,7 @@ func toContractNode(n graycoderoutergraph.Node) graphcontracts.Node { } } -func toContractEdges(edges []graycoderoutergraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { out[i] = toContractEdge(e) @@ -366,7 +366,7 @@ func toContractEdges(edges []graycoderoutergraph.Edge) []graphcontracts.Edge { return out } -func toContractEdge(e graycoderoutergraph.Edge) graphcontracts.Edge { +func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), @@ -380,7 +380,7 @@ func toContractEdge(e graycoderoutergraph.Edge) graphcontracts.Edge { } } -func toContractEvents(events []graycoderoutergraph.Event) []graphcontracts.Event { +func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { out[i] = toContractEvent(ev) @@ -388,7 +388,7 @@ func toContractEvents(events []graycoderoutergraph.Event) []graphcontracts.Event return out } -func toContractEvent(ev graycoderoutergraph.Event) graphcontracts.Event { +func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), @@ -402,15 +402,15 @@ func toContractEvent(ev graycoderoutergraph.Event) graphcontracts.Event { } } -func toContractRef(r graycoderoutergraph.Ref) graphcontracts.Ref { +func toContractRef(r eyriegraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toContractScope(s graycoderoutergraph.Scope) graphcontracts.Scope { +func toContractScope(s eyriegraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toContractProvenance(p graycoderoutergraph.Provenance) graphcontracts.Provenance { +func toContractProvenance(p eyriegraph.Provenance) graphcontracts.Provenance { evidence := make([]graphcontracts.ArtifactRef, len(p.Evidence)) for i, a := range p.Evidence { evidence[i] = graphcontracts.ArtifactRef{URI: a.URI, Digest: a.Digest, MediaType: a.MediaType} diff --git a/internal/engine/execution_graph_observations_test.go b/internal/engine/execution_graph_observations_test.go index ed230ac44..af4170b34 100644 --- a/internal/engine/execution_graph_observations_test.go +++ b/internal/engine/execution_graph_observations_test.go @@ -6,10 +6,10 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" - "github.com/GrayCodeAI/graycode-cli/internal/token" - "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/graphjournal" + "github.com/GrayCodeAI/hawk/internal/token" + "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/hawk/internal/types" shrike "github.com/GrayCodeAI/shrike" ) @@ -26,7 +26,7 @@ func (graphVerifyTool) Execute(context.Context, json.RawMessage) (string, error) } func TestToolExecutionAutomaticallyRecordsPolicyAndVerification(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry(graphVerifyTool{})) sess.SetPersistID("graph-runtime-session") @@ -66,7 +66,7 @@ func TestShrikeCompressionObservationIsPrivacySafe(t *testing.T) { if !token.ShrikeAvailable() { t.Skip("shrike engine is the build-harness stub; skipping engine-dependent test") } - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-runtime-session") sess.recordShrikeCompressionObservation( @@ -96,7 +96,7 @@ func TestShrikeRedactionObservationIsPrivacySafe(t *testing.T) { if !token.ShrikeAvailable() { t.Skip("shrike engine is the build-harness stub; skipping engine-dependent test") } - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-redaction-session") sess.recordShrikeRedactionObservation( @@ -126,7 +126,7 @@ func TestShrikeUsageBudgetObservationTracksAndProjectsAuthoritativeUsage(t *test if !token.ShrikeAvailable() { t.Skip("shrike engine is the build-harness stub; skipping engine-dependent test") } - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-usage-session") if err := sess.SetMaxBudgetUSD(1); err != nil { @@ -234,19 +234,19 @@ func TestDrainAlertsSurfacesHourlyWarning(t *testing.T) { } } -func TestGraycodeRouterOperationObservationIsPrivacySafe(t *testing.T) { - t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) +func TestEyrieOperationObservationIsPrivacySafe(t *testing.T) { + t.Setenv("HAWK_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) - sess.SetPersistID("graycode-router-runtime-session") - sess.recordGraycodeRouterOperationObservation( + sess.SetPersistID("eyrie-runtime-session") + sess.recordEyrieOperationObservation( "private-provider", "private/model", "stop", "private generated content", 2, - &types.GraycodeRouterUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, + &types.EyrieUsage{PromptTokens: 100, CompletionTokens: 20, TotalTokens: 120}, ) - entries, err := graphjournal.Load("graycode-router-runtime-session") + entries, err := graphjournal.Load("eyrie-runtime-session") if err != nil { t.Fatalf("graphjournal.Load() error = %v", err) } @@ -259,7 +259,7 @@ func TestGraycodeRouterOperationObservationIsPrivacySafe(t *testing.T) { } for _, secret := range []string{"private-provider", "private/model", "private generated content"} { if strings.Contains(string(payload), secret) { - t.Fatalf("GraycodeRouter graph observation leaked %q", secret) + t.Fatalf("Eyrie graph observation leaked %q", secret) } } } diff --git a/internal/engine/experiment_loop.go b/internal/engine/experiment_loop.go index f611b1eba..651bb69ea 100644 --- a/internal/engine/experiment_loop.go +++ b/internal/engine/experiment_loop.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // gitRollbackTimeout bounds rollback git operations in the experiment loop. diff --git a/internal/engine/extract_targets_test.go b/internal/engine/extract_targets_test.go index 2e807fb5a..b0df1d255 100644 --- a/internal/engine/extract_targets_test.go +++ b/internal/engine/extract_targets_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - "github.com/GrayCodeAI/graycode-cli/internal/types" + "github.com/GrayCodeAI/hawk/internal/types" ) // fakeToolForSchema is a minimal tool.Tool implementation that returns a diff --git a/internal/engine/git/git_provider.go b/internal/engine/git/git_provider.go index c211d7730..29ee0b790 100644 --- a/internal/engine/git/git_provider.go +++ b/internal/engine/git/git_provider.go @@ -12,7 +12,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) // GitProvider integrates with GitHub/GitLab/Bitbucket APIs for issue management, diff --git a/internal/engine/git/git_provider_test.go b/internal/engine/git/git_provider_test.go index b60ba0b9b..0d132ca77 100644 --- a/internal/engine/git/git_provider_test.go +++ b/internal/engine/git/git_provider_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" + "github.com/GrayCodeAI/hawk/internal/ui/icons" ) func TestParsePRNumbersJSON(t *testing.T) { diff --git a/internal/engine/git/provider_extra_test.go b/internal/engine/git/provider_extra_test.go index ed2076229..e36fb6606 100644 --- a/internal/engine/git/provider_extra_test.go +++ b/internal/engine/git/provider_extra_test.go @@ -5,13 +5,13 @@ import ( ) func TestParseGHRepoView_Github(t *testing.T) { - jsonStr := `{"owner":{"login":"GrayCodeAI"},"name":"graycode-cli","url":"https://github.com/GrayCodeAI/graycode-cli"}` + jsonStr := `{"owner":{"login":"GrayCodeAI"},"name":"hawk","url":"https://github.com/GrayCodeAI/hawk"}` owner, repo, provider := parseGHRepoView(jsonStr) if owner != "GrayCodeAI" { t.Errorf("owner = %q, want %q", owner, "GrayCodeAI") } - if repo != "graycode-cli" { - t.Errorf("repo = %q, want %q", repo, "graycode-cli") + if repo != "hawk" { + t.Errorf("repo = %q, want %q", repo, "hawk") } if provider != "github" { t.Errorf("provider = %q, want %q", provider, "github") @@ -50,7 +50,7 @@ func TestParseGitConfig_Github(t *testing.T) { ignorecase = true precomposeunicode = true [remote "origin"] - url = git@github.com:GrayCodeAI/graycode-cli.git + url = git@github.com:GrayCodeAI/hawk.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] remote = origin @@ -59,8 +59,8 @@ func TestParseGitConfig_Github(t *testing.T) { if owner != "GrayCodeAI" { t.Errorf("owner = %q, want %q", owner, "GrayCodeAI") } - if repo != "graycode-cli" { - t.Errorf("repo = %q, want %q", repo, "graycode-cli") + if repo != "hawk" { + t.Errorf("repo = %q, want %q", repo, "hawk") } if provider != "github" { t.Errorf("provider = %q, want %q", provider, "github") diff --git a/internal/engine/git_safety.go b/internal/engine/git_safety.go index 16165acde..7df4780e7 100644 --- a/internal/engine/git_safety.go +++ b/internal/engine/git_safety.go @@ -25,7 +25,7 @@ type GitBranchInfo struct { Detached bool HasRepo bool Dirty bool - Suggested string // graycode/agent- when OnDefault + Suggested string // hawk/agent- when OnDefault } // InspectGitBranch reads branch and dirty state for repoDir ("" = cwd). @@ -62,7 +62,7 @@ func InspectGitBranch(repoDir string) GitBranchInfo { } info.OnDefault = !info.Detached && defaultBranchNames[info.Branch] if info.OnDefault { - info.Suggested = fmt.Sprintf("graycode/agent-%s", time.Now().Format("20060102-150405")) + info.Suggested = fmt.Sprintf("hawk/agent-%s", time.Now().Format("20060102-150405")) } st := exec.CommandContext(ctx, "git", "status", "--porcelain") @@ -73,7 +73,7 @@ func InspectGitBranch(repoDir string) GitBranchInfo { return info } -// EnsureAgentBranch creates and checks out a graycode/agent-* branch when currently +// EnsureAgentBranch creates and checks out a hawk/agent-* branch when currently // on a default branch. No-op if already on a feature branch or not a git repo. // Returns the branch name after the operation. func EnsureAgentBranch(repoDir string) (string, error) { @@ -89,11 +89,11 @@ func EnsureAgentBranch(repoDir string) (string, error) { } name := info.Suggested if name == "" { - name = fmt.Sprintf("graycode/agent-%d", time.Now().Unix()) + name = fmt.Sprintf("hawk/agent-%d", time.Now().Unix()) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // #nosec G204 -- fixed git subcommand; branch name is generated internally (graycode/agent-*) + // #nosec G204 -- fixed git subcommand; branch name is generated internally (hawk/agent-*) cmd := exec.CommandContext(ctx, "git", "checkout", "-b", name) cmd.Dir = info.RepoDir if out, err := cmd.CombinedOutput(); err != nil { diff --git a/internal/engine/git_safety_test.go b/internal/engine/git_safety_test.go index e722ee32f..8913cc694 100644 --- a/internal/engine/git_safety_test.go +++ b/internal/engine/git_safety_test.go @@ -53,7 +53,7 @@ func TestEnsureAgentBranch_FromMain(t *testing.T) { if err != nil { t.Fatal(err) } - if !strings.HasPrefix(name, "graycode/agent-") { + if !strings.HasPrefix(name, "hawk/agent-") { t.Fatalf("branch = %q", name) } info2 := InspectGitBranch(dir) diff --git a/internal/engine/goose_extras_test.go b/internal/engine/goose_extras_test.go index 59ff2e1ff..68fe65577 100644 --- a/internal/engine/goose_extras_test.go +++ b/internal/engine/goose_extras_test.go @@ -9,7 +9,7 @@ import ( func TestHintsLoader_LoadHints(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, ".graycodehints"), []byte("Use Go idioms\nPrefer table-driven tests"), 0o644) + os.WriteFile(filepath.Join(dir, ".hawkhints"), []byte("Use Go idioms\nPrefer table-driven tests"), 0o644) h := NewHintsLoader() hints := h.LoadHints(dir) @@ -29,7 +29,7 @@ func TestHintsLoader_LoadHints(t *testing.T) { func TestHintsLoader_Reset(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, ".graycodehints"), []byte("hint"), 0o644) + os.WriteFile(filepath.Join(dir, ".hawkhints"), []byte("hint"), 0o644) h := NewHintsLoader() h.LoadHints(dir) diff --git a/internal/engine/hints_loader.go b/internal/engine/hints_loader.go index 361a5188e..5974f58bb 100644 --- a/internal/engine/hints_loader.go +++ b/internal/engine/hints_loader.go @@ -6,8 +6,8 @@ import ( "strings" ) -// HintsFilenames are the files graycode auto-loads for project context. -var HintsFilenames = []string{".graycodehints", "AGENTS.md"} +// HintsFilenames are the files hawk auto-loads for project context. +var HintsFilenames = []string{".hawkhints", "AGENTS.md"} // HintsLoader discovers and loads project-specific hint files from the // working directory and subdirectories the agent explores. diff --git a/internal/engine/history/annotations.go b/internal/engine/history/annotations.go index 6003e7795..d8d397253 100644 --- a/internal/engine/history/annotations.go +++ b/internal/engine/history/annotations.go @@ -154,7 +154,7 @@ func (am *AnnotationManager) InjectAnnotations(file, content string) string { if a.Resolved { continue } - commentLine := fmt.Sprintf("%s [graycode:%s] %s", commentPrefix, a.Type, a.Content) + commentLine := fmt.Sprintf("%s [hawk:%s] %s", commentPrefix, a.Type, a.Content) idx := a.Line - 1 // convert 1-based to 0-based if idx < 0 { idx = 0 @@ -188,29 +188,29 @@ func annotationCommentPrefix(file string) string { } } -// graycodeAnnotationRe matches graycode annotation comment lines. -var graycodeAnnotationRe = regexp.MustCompile(`^\s*(//|#|/\*|` blocks in every AGENTS.md / CLAUDE.md — these are regenerated by the tool - - Go module paths `github.com/GrayCodeAI/{harrier,kestrel,merlin,shrike,swift,falcon}` — these are **live** dependencies in `graycode-cli/go.mod:15-18,55-57,172`, not dead names - - The `/products/` URL slugs in `graycode-platform/apps/web/lib/products.ts:9` -- **Workstream B is the only one that changes a deployed wire contract.** It requires a D1 migration and a coordinated worker deploy. Do not merge B's worker PR without applying migration `0024` first. - ---- - -## Workstream A — Skills Registry - -**Problem.** `graycode skills search` cannot work today, and `graycode skills install GrayCodeAI/graycode-skills ` cannot work either. The link is broken at three independent layers, and fixing only the URL restores nothing. - -1. **No public URL exists.** `graycode-skills/.github/workflows/publish-registry.yml:55-62` generates `registry.json`, signs it, then uploads it only as a GitHub Actions artifact (90-day retention). There is no release, no Pages, no R2, no commit-back. `registry.json` is gitignored at `graycode-skills/.gitignore:50`, so `raw.githubusercontent.com` returns 404 under both the old `starling` name and the new one. Verified: both URLs return HTTP 404; `GrayCodeAI/starling` 301-redirects to `GrayCodeAI/graycode-skills`; the repo has `has_pages=false` and its only release `v0.1.0` has zero assets. -2. **The JSON shapes do not match.** `tools/update_registry.py:165` emits a bare JSON array. `graycode-cli/internal/plugin/registry.go:73-77` parses an object `{version, updated_at, skills[]}`. `json.Unmarshal` of an array into that struct fails, so `FetchIndex` returns `invalid index` (`registry.go:136-137`) even with a working URL. The emitter also omits `repo`, which `auto_skill.go:175` needs to build the clone URL. -3. **Install cannot find the skills.** `registry.go:261-265` scans only `/*/SKILL.md` or `/skills/*/SKILL.md`. graycode-skills stores skills at `categories///SKILL.md` and has no `skills/` directory, so every install returns `skill not found`. - -**Decision (approved):** publish `registry.json` as an asset on a rolling GitHub Release. Stable URL, no git bloat, no new infrastructure. - -**Also in scope.** `graycode-cli/internal/plugin/marketplace.go:49` points at `plugins-registry.json`, a file that **nothing in any of the four repos generates**. It is a phantom default source and is removed here. - ---- - -### Task A1: Emit the registry shape graycode-cli actually parses - -**Files:** -- Modify: `graycode-skills/tools/update_registry.py:115-124` (add `repo`), `:160-166` (wrap in object) -- Modify: `graycode-skills/tools/registry_schema.py:30-58` (allow `repo`), `:84-87` (`REGISTRY_SCHEMA` becomes an object), `:217-230` (`load_and_validate` reads the object) -- Test: `graycode-skills/tests/test_update_registry.py` - -**Interfaces:** -- Produces: on-disk `registry.json` of the form `{"version": 1, "skills": [entry, ...]}` where each entry gains `"repo": "GrayCodeAI/graycode-skills"`. Task A3 relies on this shape. `build_registry()` still returns the bare `list[dict]` so `tools/skill_graph.py:13,268` is unaffected. - -> **Determinism matters.** `update_registry.py --check` compares generated text against the file on disk and is run in CI. Do **not** add a wall-clock `updated_at`; it would make `--check` fail on every run. The top-level `updated_at` is omitted entirely. `graycode-cli` never reads `idx.Version` or `idx.UpdatedAt` (verified: zero references outside tests), so an absent `updated_at` is harmless. - -- [ ] **Step 1: Write the failing tests** - -Append to `graycode-skills/tests/test_update_registry.py`: - -```python -class TestCanonicalRenderShape: - """registry.json must match the object shape graycode-cli parses.""" - - def test_render_wraps_entries_in_object(self): - from update_registry import render_registry - - doc = json.loads(render_registry([{"name": "a", "description": "d"}])) - assert isinstance(doc, dict), "top level must be an object, not an array" - assert doc["version"] == 1 - assert doc["skills"] == [{"name": "a", "description": "d"}] - - def test_render_omits_updated_at_for_determinism(self): - from update_registry import render_registry - - first = render_registry([{"name": "a"}]) - second = render_registry([{"name": "a"}]) - assert first == second - assert "updated_at" not in json.loads(first) - - def test_entries_carry_repo_slug(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch): - from update_registry import build_registry - - cat = tmp_path / "categories" / "python" / "demo-skill" - cat.mkdir(parents=True) - (cat / "SKILL.md").write_text( - "---\nname: demo-skill\ndescription: A demo skill\n---\n\nBody\n" - ) - monkeypatch.setattr("update_registry.REPO_ROOT", tmp_path) - monkeypatch.setattr("update_registry.CATEGORIES_DIR", tmp_path / "categories") - - entries = build_registry() - assert entries[0]["repo"] == "GrayCodeAI/graycode-skills" - - def test_schema_accepts_repo_field(self): - from registry_schema import validate_registry_entry - - errors = validate_registry_entry( - { - "name": "demo", - "description": "d", - "category": "python", - "tags": ["python"], - "path": "categories/python/demo", - "file_count": 1, - "has_scripts": False, - "repo": "GrayCodeAI/graycode-skills", - }, - path="demo", - ) - assert errors == [] -``` - -Ensure `import json` and `from pathlib import Path` are present at the top of the file. - -- [ ] **Step 2: Run the tests to verify they fail** - -```bash -cd graycode-skills && python -m pytest tests/test_update_registry.py -k CanonicalRenderShape -v -``` - -Expected: FAIL. `test_render_wraps_entries_in_object` fails with `assert isinstance(doc, dict)` because `render_registry` currently returns a JSON array. `test_entries_carry_repo_slug` fails with `KeyError: 'repo'`. `test_schema_accepts_repo_field` fails with an `additionalProperties` violation naming `repo`. - -- [ ] **Step 3: Add the `repo` slug to each entry** - -In `graycode-skills/tools/update_registry.py`, add a module constant next to the other module-level paths (near `REGISTRY_PATH`, around line 24): - -```python -# The GitHub slug every skill in this repo is installed from. graycode-cli -# builds its clone URL from this field (internal/plugin/auto_skill.go). -REGISTRY_REPO = "GrayCodeAI/graycode-skills" -``` - -Then extend the entry literal at line 115: - -```python - entry = { - "name": name, - "description": description, - "category": category_name, - "tags": tags, - "path": path, - "repo": REGISTRY_REPO, - "file_count": count_files(skill_dir), - "has_scripts": has_scripts_dir(skill_dir), - } -``` - -- [ ] **Step 4: Wrap the rendered document in the object shape** - -Replace `render_registry` in `graycode-skills/tools/update_registry.py`: - -```python -def render_registry(entries: list[dict]) -> str: - """Render registry entries in the canonical on-disk format. - - The top level is an object, not an array: graycode-cli parses - {version, updated_at, skills[]} (internal/plugin/registry.go). No - timestamp is emitted so that `--check` stays deterministic. - """ - document = {"version": 1, "skills": entries} - return json.dumps(document, indent=2, ensure_ascii=False) + "\n" -``` - -- [ ] **Step 5: Teach the schema about the new shape** - -In `graycode-skills/tools/registry_schema.py`, add `repo` to `REGISTRY_ENTRY_SCHEMA["properties"]` alongside the other optional fields (the block ending at line 78, before `"additionalProperties": False`): - -```python - "repo": { - "type": "string", - "description": "GitHub owner/repo slug the skill is installed from", - }, -``` - -Replace `REGISTRY_SCHEMA` (line 84): - -```python -REGISTRY_SCHEMA: dict[str, Any] = { - "type": "object", - "properties": { - "version": {"type": "integer"}, - "skills": {"type": "array", "items": REGISTRY_ENTRY_SCHEMA}, - }, - "required": ["version", "skills"], - "additionalProperties": False, -} -``` - -Then update `load_and_validate` (line 217) so it validates the `skills` array rather than the whole document as an array. Read the function first and adapt its existing error-collection style; the only change is that the list of entries is now `data["skills"]` instead of `data`, and a non-object top level is itself an error. - -- [ ] **Step 6: Run the tests to verify they pass** - -```bash -cd graycode-skills && python -m pytest tests/test_update_registry.py -v && python -m pytest -q -``` - -Expected: PASS, whole suite green. - -- [ ] **Step 7: Verify the real corpus still generates and validates** - -```bash -cd graycode-skills -python tools/update_registry.py -python -c "import json; d=json.load(open('registry.json')); print(type(d).__name__, d['version'], len(d['skills']), d['skills'][0]['repo'])" -python tools/update_registry.py --check && echo "CHECK CLEAN (deterministic)" -rm registry.json -``` - -Expected: `dict 1 12167 GrayCodeAI/graycode-skills`, then `CHECK CLEAN (deterministic)`. Delete the generated file; it stays gitignored. - -- [ ] **Step 8: Commit** - -```bash -git add tools/update_registry.py tools/registry_schema.py tests/test_update_registry.py -git commit -m "fix: emit registry.json in the object shape graycode-cli parses - -The generator emitted a bare JSON array while graycode-cli parses -{version, updated_at, skills[]}, so FetchIndex failed with 'invalid -index' regardless of URL. Entries now also carry the repo slug that -the installer needs to build a clone URL. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task A2: Publish the registry to a stable public URL - -**Files:** -- Modify: `graycode-skills/.github/workflows/publish-registry.yml:11-14` (add `permissions`), `:55-62` (add the release step after the artifact upload) - -**Interfaces:** -- Produces: `https://github.com/GrayCodeAI/graycode-skills/releases/latest/download/registry.json`. Task A3 hard-codes this URL. - -> **Why a rolling release and not `latest`.** `releases/latest/download/` resolves to the most recent **non-prerelease** release. The existing `v0.1.0` release is currently the latest, so the new rolling release must be created as a normal (non-draft, non-prerelease) release for that URL to resolve to it. Tagging it `registry-latest` and re-uploading with `--clobber` keeps exactly one moving target. - -- [ ] **Step 1: Grant the workflow permission to write releases** - -In `graycode-skills/.github/workflows/publish-registry.yml`, add a `permissions` block to the `build-and-publish` job, directly under `runs-on`: - -```yaml -jobs: - build-and-publish: - runs-on: ubuntu-latest - permissions: - contents: write - steps: -``` - -- [ ] **Step 2: Add the release-publishing step** - -Append to the end of the same file, after the existing `Upload registry artifacts` step: - -```yaml - - name: Publish registry to the rolling release - if: github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # One moving release holds the current registry. The Actions - # artifact above is retained separately for 90-day forensics. - if ! gh release view registry-latest >/dev/null 2>&1; then - gh release create registry-latest \ - --title "Skill registry (rolling)" \ - --notes "Generated registry.json for the current main. Updated automatically; do not delete." \ - --latest=false - fi - gh release upload registry-latest \ - registry.json registry-signature.json --clobber -``` - -`--latest=false` keeps the rolling release from displacing real version tags in the GitHub UI. The download URL used by the CLI in Task A3 addresses the tag directly, so it does not depend on which release is marked latest. - -- [ ] **Step 3: Validate the workflow file parses** - -```bash -cd graycode-skills && python -c "import yaml,sys; d=yaml.safe_load(open('.github/workflows/publish-registry.yml')); j=d['jobs']['build-and-publish']; print('permissions:', j['permissions']); print('steps:', [s['name'] for s in j['steps']])" -``` - -Expected: `permissions: {'contents': 'write'}` and a step list ending with `Publish registry to the rolling release`. - -- [ ] **Step 4: Commit** - -```bash -git add .github/workflows/publish-registry.yml -git commit -m "feat: publish registry.json to a rolling GitHub release - -The registry had no public URL: CI only uploaded it as a 90-day -Actions artifact and the file is gitignored, so every raw -githubusercontent URL 404d. A rolling registry-latest release gives -the CLI a stable download target without putting a 4.3 MB generated -file into git history. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - -- [ ] **Step 5: After merge, confirm the URL is live** - -```bash -curl -sIL https://github.com/GrayCodeAI/graycode-skills/releases/download/registry-latest/registry.json | grep -E '^HTTP' -``` - -Expected: a final `HTTP/2 200`. If the workflow has not run since merge, trigger it with `gh workflow run publish-registry.yml -R GrayCodeAI/graycode-skills` and re-check. **Task A3 cannot be verified end-to-end until this returns 200.** - ---- - -### Task A3: Point graycode-cli at the published registry and drop the phantom marketplace source - -**Files:** -- Modify: `graycode-cli/internal/plugin/registry.go:20` -- Modify: `graycode-cli/internal/plugin/marketplace.go:42-50` -- Test: `graycode-cli/internal/plugin/registry_test.go`, `graycode-cli/internal/plugin/marketplace_test.go` - -**Interfaces:** -- Consumes: the object shape from Task A1 and the URL from Task A2. -- Produces: `defaultIndexURL` pointing at the rolling release; `defaultMarketplaceSources()` returning an empty slice. - -> **Why the marketplace source is removed rather than repointed.** `plugins-registry.json` is generated by nothing in any of the four repos; the only reference anywhere is `marketplace.go:49`. `FetchAll` (`marketplace.go:111-132`) returns `(nil, nil)` when there are no sources and no errors, so removing the dead source turns a confusing fetch failure into a clean empty list. Users add real sources with `graycode plugin marketplace add`. - -- [ ] **Step 1: Write the failing tests** - -Append to `graycode-cli/internal/plugin/registry_test.go`: - -```go -func TestDefaultIndexURLIsPublished(t *testing.T) { - const want = "https://github.com/GrayCodeAI/graycode-skills/releases/download/registry-latest/registry.json" - if defaultIndexURL != want { - t.Fatalf("defaultIndexURL = %q, want %q", defaultIndexURL, want) - } - if strings.Contains(defaultIndexURL, "starling") { - t.Errorf("defaultIndexURL still references the renamed starling repo") - } -} - -func TestFetchIndexParsesGeneratedShape(t *testing.T) { - // Byte-for-byte the shape graycode-skills/tools/update_registry.py emits. - const generated = `{ - "version": 1, - "skills": [ - { - "name": "ab-test-setup", - "description": "Plan and design an A/B test", - "category": "testing", - "tags": ["testing"], - "path": "categories/testing/ab-test-setup", - "repo": "GrayCodeAI/graycode-skills", - "file_count": 1, - "has_scripts": false - } - ] -} -` - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write([]byte(generated)) - })) - defer srv.Close() - - rc := &RegistryClient{IndexURL: srv.URL, CacheDir: t.TempDir(), client: srv.Client()} - idx, err := rc.FetchIndex() - if err != nil { - t.Fatalf("FetchIndex: %v", err) - } - if len(idx.Skills) != 1 { - t.Fatalf("skills = %d, want 1", len(idx.Skills)) - } - if idx.Skills[0].Repo != "GrayCodeAI/graycode-skills" { - t.Errorf("Repo = %q, want the slug the installer clones from", idx.Skills[0].Repo) - } -} -``` - -Append to `graycode-cli/internal/plugin/marketplace_test.go`: - -```go -func TestNoPhantomDefaultMarketplaceSource(t *testing.T) { - for _, src := range DefaultMarketplaceSources() { - if strings.Contains(src.URL, "plugins-registry.json") { - t.Fatalf("default source %q points at plugins-registry.json, which nothing generates", src.Name) - } - } -} - -func TestFetchAllWithNoSourcesReturnsEmptyNotError(t *testing.T) { - mc := &MarketplaceClient{Sources: nil, CacheDir: t.TempDir()} - entries, err := mc.FetchAll() - if err != nil { - t.Fatalf("FetchAll with no sources returned error: %v", err) - } - if len(entries) != 0 { - t.Fatalf("entries = %d, want 0", len(entries)) - } -} -``` - -Ensure `net/http`, `net/http/httptest` and `strings` are imported in each file. `MarketplaceClient` construction must match the real struct; read `marketplace.go` and adjust the literal if the field set differs. - -- [ ] **Step 2: Run the tests to verify they fail** - -```bash -cd graycode-cli && go test ./internal/plugin/ -run 'TestDefaultIndexURLIsPublished|TestFetchIndexParsesGeneratedShape|TestNoPhantomDefaultMarketplaceSource|TestFetchAllWithNoSourcesReturnsEmptyNotError' -v -``` - -Expected: FAIL. The URL test reports the `starling` constant. The shape test passes only if the Go struct already matches, which it does, so it should pass once the URL is fixed; if it fails, the emitter in A1 drifted. The marketplace test reports the `plugins-registry.json` default. - -- [ ] **Step 3: Repoint the registry index URL** - -In `graycode-cli/internal/plugin/registry.go`, replace line 20: - -```go -// defaultIndexURL is the rolling release asset published by -// graycode-skills/.github/workflows/publish-registry.yml. The registry is a -// generated 4.3 MB artifact and is deliberately not committed to that repo, -// so a raw.githubusercontent.com URL cannot work. -const defaultIndexURL = "https://github.com/GrayCodeAI/graycode-skills/releases/download/registry-latest/registry.json" -``` - -- [ ] **Step 4: Remove the phantom marketplace source** - -In `graycode-cli/internal/plugin/marketplace.go`, replace the exported `DefaultMarketplaceSources` function at lines 43-52 (keep the name and the exported signature; `NewMarketplaceClient` and the `plugin marketplace` commands already call it): - -```go -// DefaultMarketplaceSources returns the built-in plugin index sources. -// -// There are none. No repository in the GrayCode ecosystem generates a -// plugins-registry.json, so shipping a built-in source only produced a 404 -// on every `graycode plugin marketplace list`. Users register real sources -// with `graycode plugin marketplace add `. -func DefaultMarketplaceSources() []MarketplaceSource { - return nil -} -``` - -Leave `loadUserMarketplaceSources` and `SaveUserSources` untouched. - -- [ ] **Step 5: Run the tests to verify they pass** - -```bash -cd graycode-cli && go test ./internal/plugin/ -v -``` - -Expected: PASS, whole package green. Pre-existing tests that use `"GrayCodeAI/starling"` as row **data** (`registry_test.go:20,21,167,192,250,276,285`, `skillslock_test.go:20`) are fixtures, not URL pins; leave them for Task D4. - -- [ ] **Step 6: Verify against the live registry** - -Only runnable once Task A2's URL returns 200. - -```bash -cd graycode-cli && go build -o /tmp/graycode ./cmd/graycode && /tmp/graycode skills search testing | head -20 -``` - -Expected: a list of matching skills. If it prints a registry error, re-check Step 5 of Task A2. - -- [ ] **Step 7: Commit** - -```bash -git add internal/plugin/registry.go internal/plugin/marketplace.go internal/plugin/registry_test.go internal/plugin/marketplace_test.go -git commit -m "fix: point the skill index at the published registry release - -The index URL referenced GrayCodeAI/starling, a repo renamed to -graycode-skills whose registry.json is generated and never committed, -so the URL 404d under either name. It now reads the rolling release -asset. The built-in marketplace source pointed at a -plugins-registry.json that nothing generates and is removed. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task A4: Make install discover skills at any repository layout - -**Files:** -- Modify: `graycode-cli/internal/plugin/registry.go:258-270` (the discovery block inside `Install`) -- Test: `graycode-cli/internal/plugin/registry_test.go` - -**Interfaces:** -- Consumes: nothing from earlier tasks; independently testable. -- Produces: `discoverSkillDirs(root string) (map[string]string, error)` mapping skill name to the directory containing its `SKILL.md`. `Install` iterates this map instead of `os.ReadDir(skillsRoot)`. - -> **Root cause, not the reported symptom.** The reported failure is "installing from graycode-skills says skill not found". The cause is that discovery hard-codes two layouts (`//` and `/skills//`). Every repo with any other layout fails the same way. One bounded walk fixes all of them, and is a smaller diff than adding a third special case. - -- [ ] **Step 1: Write the failing test** - -Append to `graycode-cli/internal/plugin/registry_test.go`: - -```go -func TestDiscoverSkillDirs(t *testing.T) { - tests := []struct { - name string - layout []string // SKILL.md paths relative to the repo root - want []string // expected skill names - }{ - { - name: "flat layout", - layout: []string{"go-review/SKILL.md"}, - want: []string{"go-review"}, - }, - { - name: "agentskills.io skills/ layout", - layout: []string{"skills/go-review/SKILL.md"}, - want: []string{"go-review"}, - }, - { - name: "graycode-skills categories layout", - layout: []string{"categories/go/go-review/SKILL.md", "categories/python/pandas/SKILL.md"}, - want: []string{"go-review", "pandas"}, - }, - { - name: "ignores vendored and dot directories", - layout: []string{"go-review/SKILL.md", ".git/hooks/SKILL.md", "node_modules/pkg/SKILL.md"}, - want: []string{"go-review"}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - root := t.TempDir() - for _, rel := range tc.layout { - full := filepath.Join(root, rel) - if err := os.MkdirAll(filepath.Dir(full), 0o750); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(full, []byte("---\nname: x\n---\n"), 0o600); err != nil { - t.Fatal(err) - } - } - - got, err := discoverSkillDirs(root) - if err != nil { - t.Fatalf("discoverSkillDirs: %v", err) - } - if len(got) != len(tc.want) { - t.Fatalf("found %d skills %v, want %d %v", len(got), keysOf(got), len(tc.want), tc.want) - } - for _, name := range tc.want { - dir, ok := got[name] - if !ok { - t.Errorf("missing skill %q; got %v", name, keysOf(got)) - continue - } - if _, err := os.Stat(filepath.Join(dir, "SKILL.md")); err != nil { - t.Errorf("skill %q maps to %q which has no SKILL.md", name, dir) - } - } - }) - } -} - -func keysOf(m map[string]string) []string { - out := make([]string, 0, len(m)) - for k := range m { - out = append(out, k) - } - sort.Strings(out) - return out -} -``` - -Ensure `os`, `path/filepath` and `sort` are imported. - -- [ ] **Step 2: Run the test to verify it fails** - -```bash -cd graycode-cli && go test ./internal/plugin/ -run TestDiscoverSkillDirs -v -``` - -Expected: FAIL to compile with `undefined: discoverSkillDirs`. - -- [ ] **Step 3: Implement bounded discovery** - -Add to `graycode-cli/internal/plugin/registry.go`: - -```go -// maxSkillSearchDepth bounds how deep discoverSkillDirs walks below the repo -// root. graycode-skills nests skills at categories///, which -// is depth 3; anything deeper is almost certainly test data or a vendored -// copy. -const maxSkillSearchDepth = 4 - -// discoverSkillDirs finds every directory under root containing a SKILL.md, -// keyed by the directory name. It replaces the previous two hard-coded -// layouts (// and /skills//) so repositories that -// group skills under a category directory are installable too. -// -// On a duplicate skill name the shallowest path wins; ties keep the first -// lexicographic match so the result is deterministic. -func discoverSkillDirs(root string) (map[string]string, error) { - found := map[string]string{} - depthOf := map[string]int{} - - err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - rel, relErr := filepath.Rel(root, path) - if relErr != nil { - return nil //nolint:nilerr // an unrelatable path is simply skipped - } - if d.IsDir() { - if path == root { - return nil - } - name := d.Name() - if strings.HasPrefix(name, ".") || name == "node_modules" || name == "vendor" { - return filepath.SkipDir - } - if len(strings.Split(filepath.ToSlash(rel), "/")) > maxSkillSearchDepth { - return filepath.SkipDir - } - return nil - } - if d.Name() != "SKILL.md" { - return nil - } - dir := filepath.Dir(path) - if dir == root { - return nil // a top-level SKILL.md documents the repo, not a skill - } - name := filepath.Base(dir) - depth := len(strings.Split(filepath.ToSlash(rel), "/")) - if prev, ok := found[name]; ok { - if depthOf[name] <= depth { - return nil - } - _ = prev - } - found[name] = dir - depthOf[name] = depth - return nil - }) - if err != nil { - return nil, fmt.Errorf("scan skills: %w", err) - } - return found, nil -} -``` - -Add `"io/fs"` to the imports. - -- [ ] **Step 4: Run the test to verify it passes** - -```bash -cd graycode-cli && go test ./internal/plugin/ -run TestDiscoverSkillDirs -v -``` - -Expected: PASS, all four subtests. - -- [ ] **Step 5: Wire it into `Install`** - -In `graycode-cli/internal/plugin/registry.go`, replace the discovery block at lines 258-270. Delete the `skillsRoot` computation and the `os.ReadDir(skillsRoot)` call, and drive the existing loop from the map instead: - -```go - // Discover skills in the cloned repo, whatever layout it uses. - discovered, err := discoverSkillDirs(tmpDir) - if err != nil { - return "", err - } - names := make([]string, 0, len(discovered)) - for name := range discovered { - names = append(names, name) - } - sort.Strings(names) -``` - -Then change the loop header from `for _, e := range entries {` to `for _, name := range names {`, delete the `if !e.IsDir() { continue }` guard and the `name := e.Name()` line, and change the `srcSkill` assignment to: - -```go - srcSkill := filepath.Join(discovered[name], "SKILL.md") -``` - -Leave the rest of the loop body, including the `skillName` filter, the trust checks, and the lockfile writes, exactly as they are. Add `"sort"` to the imports if it is not already present. - -> `// ponytail: whole-repo shallow clone. Installing one skill from graycode-skills clones ~127 MB of categories. Switch to git sparse-checkout of the skill's indexed path if install latency becomes a complaint.` - -Add that comment above the `git clone` call at line 247. - -- [ ] **Step 6: Run the full package and the boundary guards** - -```bash -cd graycode-cli && go test ./internal/plugin/ -v && gofumpt -l internal/plugin/ && make boundaries -``` - -Expected: package PASS, `gofumpt -l` prints nothing, all nine guards pass. - -- [ ] **Step 7: Verify a real install end-to-end** - -```bash -cd graycode-cli && go build -o /tmp/graycode ./cmd/graycode && /tmp/graycode skills install GrayCodeAI/graycode-skills ab-test-setup -``` - -Expected: reports the skill installed. Before this change it returned `skill "ab-test-setup" not found`. - -- [ ] **Step 8: Commit** - -```bash -git add internal/plugin/registry.go internal/plugin/registry_test.go -git commit -m "fix: discover skills at any repository layout on install - -Install scanned only //SKILL.md and -/skills//SKILL.md, so every repo grouping skills under a -category directory - graycode-skills included - reported 'skill not -found'. A bounded walk replaces both special cases. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -## Workstream B — Cloud Wire Contract - -**Problem.** Two field names drifted between graycode-cli and graycode-platform. Both schemas on the worker are `.strict()` zod objects, so both calls are rejected outright. - -| Call | CLI sends | Worker requires | Result | -|---|---|---|---| -| `POST /v1/auth/device/start` | `graycodeVersion` (`client.go:105`) | `hawkVersion` (`validation.ts:30`, `auth.ts:32`) | 400, `graycode cloud login` can never succeed | -| `POST /v1/usage` | `capability: "graycode"` (`exec.go:373`) | enum `[hawk, swift, shrike, harrier, merlin, kestrel]` (`contracts/v1.ts:8-15`) | 400, silently discarded because `RecordUsage` is fail-open (`client.go:176-179`) | - -**Decision (approved):** rename the wire to `graycode`. The CLI is already correct, so **graycode-cli needs no production change in this workstream** beyond one test fixture. All edits are in graycode-platform. - -**Deployment order is not optional.** Migration `0024` must be applied to D1 before the worker deploy, or every device read breaks on a missing column. - -> **Note on migration numbering.** `apps/worker/migrations/` already contains a collision: both `0022_graph_ledger.sql` and `0022_identity_ui.sql` exist. The next free prefix is `0024`, since `0023_usage_outbox.sql` is taken. Do not reuse `0022` or `0023`. - ---- - -### Task B1: Migrate the D1 schema and backfill legacy rows - -**Files:** -- Create: `graycode-platform/apps/worker/migrations/0024_graycode_capability_rename.sql` - -**Interfaces:** -- Produces: column `devices.graycode_version` (was `hawk_version`), column `cli_device_authorizations.graycode_version` (was `hawk_version`), and every `capability = 'hawk'` row rewritten to `'graycode'`. Tasks B2 and B3 read these names. - -- [ ] **Step 1: Write the migration** - -Create `graycode-platform/apps/worker/migrations/0024_graycode_capability_rename.sql`: - -```sql --- Rename the CLI wire fields from the legacy hawk product name to graycode. --- --- The CLI has always sent `graycodeVersion` and `capability: "graycode"` --- (graycode-cli internal/platform/cloud/client.go, cmd/exec.go). The worker's --- strict zod schemas required `hawkVersion` and rejected the `graycode` --- capability, so device login returned 400 and every usage event was dropped. --- The wire contract moves to the name the product actually has. --- --- SQLite supports RENAME COLUMN from 3.25; D1 is well past that. - -ALTER TABLE devices RENAME COLUMN hawk_version TO graycode_version; -ALTER TABLE cli_device_authorizations RENAME COLUMN hawk_version TO graycode_version; - --- Backfill rows written while the enum still said 'hawk'. The capability --- column is free TEXT, so historical rows would otherwise fail validation on --- any read path that re-parses them. -UPDATE usage_events SET capability = 'graycode' WHERE capability = 'hawk'; -UPDATE sessions SET capability = 'graycode' WHERE capability = 'hawk'; -``` - -- [ ] **Step 2: Verify the migration applies against a scratch database** - -```bash -cd graycode-platform/apps/worker -npx wrangler d1 migrations list graycode-cloud --local -npx wrangler d1 migrations apply graycode-cloud --local -npx wrangler d1 execute graycode-cloud --local --command "PRAGMA table_info(devices);" | grep -i version -``` - -Expected: the migration list shows `0024_graycode_capability_rename.sql` pending, apply succeeds, and the final command prints `graycode_version` with no `hawk_version` row. - -- [ ] **Step 3: Commit** - -```bash -git add apps/worker/migrations/0024_graycode_capability_rename.sql -git commit -m "feat: migrate device version and capability columns to graycode - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task B2: Rename the wire contract in the worker - -**Files:** -- Modify: `graycode-platform/apps/worker/contracts/v1.ts:8-16,23,55,124` -- Modify: `graycode-platform/apps/worker/src/domain/validation.ts:2,30,40` -- Modify: `graycode-platform/apps/worker/src/routes/auth.ts:32,48,56` -- Modify: `graycode-platform/apps/worker/src/routes/devices.ts:31,38,92` -- Modify: `graycode-platform/apps/worker/src/routes/sessions.ts:19` -- Modify: `graycode-platform/apps/worker/src/routes/organizations.ts:123` -- Modify: `graycode-platform/apps/worker/src/routes/enterprise.ts:596,748` -- Modify: `graycode-platform/apps/worker/src/auth/device-approve.ts:29,36,56,63` -- Test: `graycode-platform/apps/worker/test/{auth,devices,organizations,rate-limit,device-token,sessions,usage-sessions}.test.ts` - -**Interfaces:** -- Consumes: the column names from Task B1. -- Produces: `GRAYCODE_CAPABILITIES` (was `HAWK_CAPABILITIES`) with first element `'graycode'`; type `GraycodeCapability`; request field `graycodeVersion`. - -- [ ] **Step 1: Update the failing tests first** - -In `graycode-platform/apps/worker/test/auth.test.ts`, add a regression test that pins the CLI's actual request body: - -```ts -it('accepts the body graycode-cli actually sends', async () => { - const res = await app.request( - '/v1/auth/device/start', - { - method: 'POST', - headers: { 'content-type': 'application/json' }, - // Verbatim from graycode-cli internal/platform/cloud/client.go:105 - body: JSON.stringify({ - label: 'my-laptop', - platform: 'darwin', - graycodeVersion: '0.0.1', - }), - }, - env, - ) - expect(res.status).toBe(201) -}) -``` - -Match the surrounding tests' setup style for `app` and `env`; read the top of the file first. - -In `graycode-platform/apps/worker/test/usage-sessions.test.ts`, add: - -```ts -it('accepts the capability graycode-cli actually sends', async () => { - const res = await app.request( - '/v1/usage', - { - method: 'POST', - headers: { 'content-type': 'application/json', authorization: `Bearer ${deviceToken}` }, - // capability verbatim from graycode-cli cmd/exec.go:373 - body: JSON.stringify({ ...validUsageEvent, capability: 'graycode' }), - }, - env, - ) - expect(res.status).toBe(202) -}) -``` - -Reuse whatever fixture the neighbouring tests use in place of `validUsageEvent` and `deviceToken`. - -- [ ] **Step 2: Run the tests to verify they fail** - -```bash -cd graycode-platform/apps/worker && npx vitest run test/auth.test.ts test/usage-sessions.test.ts -``` - -Expected: FAIL. The device-start test gets 400 `Invalid device authorization request`. The usage test gets 400 `Invalid usage event`. - -- [ ] **Step 3: Rename the capability constant and type** - -In `graycode-platform/apps/worker/contracts/v1.ts`, replace lines 8-16: - -```ts -export const GRAYCODE_CAPABILITIES = [ - 'graycode', - 'swift', - 'shrike', - 'harrier', - 'merlin', - 'kestrel', -] as const -export type GraycodeCapability = (typeof GRAYCODE_CAPABILITIES)[number] -``` - -Update the two `capability: HawkCapability` fields at lines 23 and 55 to `GraycodeCapability`, and line 124 `hawkVersion: string` to `graycodeVersion: string`. Also reword the file's header comment (lines 1-7) from "Hawk Cloud" and "Hawk clients" to "Graycode Cloud" and "Graycode clients". - -- [ ] **Step 4: Update the validators** - -In `graycode-platform/apps/worker/src/domain/validation.ts`: line 2 imports `GRAYCODE_CAPABILITIES`, line 30 becomes `graycodeVersion: z.string().min(1).max(50),`, line 40 becomes `capability: z.enum(GRAYCODE_CAPABILITIES),`. - -In `graycode-platform/apps/worker/src/routes/auth.ts`: line 32 becomes `graycodeVersion: z.string().min(1).max(50),`; the INSERT at line 48 uses column `graycode_version`; the bound value at line 56 becomes `parsed.data.graycodeVersion`. - -In `graycode-platform/apps/worker/src/routes/sessions.ts`: line 19 becomes `capability: z.enum(GRAYCODE_CAPABILITIES),` with the matching import, replacing the inline literal array. - -- [ ] **Step 5: Update every SQL statement and its alias** - -Apply the same two mechanical substitutions in `src/routes/devices.ts:31,38,92`, `src/routes/organizations.ts:123`, `src/routes/enterprise.ts:596,748`, and `src/auth/device-approve.ts:29,36,56,63`: - -- column `hawk_version` becomes `graycode_version` -- SQL alias and TypeScript property `hawkVersion` becomes `graycodeVersion` - -- [ ] **Step 6: Update the remaining test fixtures** - -Replace `hawkVersion` with `graycodeVersion` at `test/auth.test.ts:116,125,317`, `test/organizations.test.ts:90`, `test/rate-limit.test.ts:153`, `test/devices.test.ts:77,106,118,135,153`. Replace `capability: 'hawk'` with `capability: 'graycode'` at `test/device-token.test.ts:166`, `test/sessions.test.ts:71,351`, `test/usage-sessions.test.ts:58,77,817`. - -```bash -cd graycode-platform/apps/worker -grep -rln 'hawkVersion' test/ | xargs sed -i '' 's/hawkVersion/graycodeVersion/g' -grep -rln "capability: 'hawk'" test/ | xargs sed -i '' "s/capability: 'hawk'/capability: 'graycode'/g" -grep -rn "hawkVersion\|capability: 'hawk'" test/ || echo "TEST FIXTURES CLEAN" -``` - -- [ ] **Step 7: Run the full worker suite** - -```bash -cd graycode-platform/apps/worker && npx tsc --noEmit && npx vitest run -``` - -Expected: typecheck clean, all tests PASS including the two added in Step 1. If `openapi-parity.test.ts` fails, that is expected until Task B4 updates the contract file; note it and continue. - -- [ ] **Step 8: Commit** - -```bash -git add apps/worker/contracts/v1.ts apps/worker/src apps/worker/test -git commit -m "feat!: rename the cloud wire contract from hawk to graycode - -Device login required hawkVersion while the CLI has always sent -graycodeVersion, and the capability enum rejected 'graycode', so -login returned 400 and every usage event was silently dropped by the -fail-open client. The wire now matches the product name. - -Requires migration 0024 to be applied before deploy. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task B3: Rename the capability enum in the BFF - -**Files:** -- Modify: `graycode-platform/apps/bff/src/routes/web-activity.ts:22,94` -- Modify: `graycode-platform/apps/bff/src/routes/web-dashboard.ts:15` - -**Interfaces:** -- Consumes: the capability values from Task B2. The BFF duplicates the enum as an inline literal rather than importing it. - -- [ ] **Step 1: Update both inline enums** - -In `graycode-platform/apps/bff/src/routes/web-activity.ts:94` and `web-dashboard.ts:15`, replace: - -```ts - tool: z.enum(['hawk', 'swift', 'shrike', 'harrier', 'merlin', 'kestrel']), -``` - -with: - -```ts - tool: z.enum(['graycode', 'swift', 'shrike', 'harrier', 'merlin', 'kestrel']), -``` - -Keep `.optional()` on the `web-activity.ts:94` occurrence. - -- [ ] **Step 2: Fix the user-facing achievement string** - -`web-activity.ts:22` reads `description: 'Completed your first hawk session'`. Change it to `'Completed your first graycode session'`. - -- [ ] **Step 3: Typecheck and test** - -```bash -cd graycode-platform/apps/bff && npx tsc --noEmit && npx vitest run -``` - -Expected: clean typecheck, tests PASS. - -- [ ] **Step 4: Commit** - -```bash -git add apps/bff/src/routes/web-activity.ts apps/bff/src/routes/web-dashboard.ts -git commit -m "feat: align the BFF capability enum with the graycode wire contract - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task B4: Update the OpenAPI contract and delete the stale third copy - -**Files:** -- Modify: `graycode-platform/api/graycode-cloud-openapi.yaml:3,1196,1230,1234,1322` -- Modify: `graycode-platform/apps/worker/contracts/openapi.yaml:1230,1234` (and the same title/enum lines) -- Delete: `graycode-platform/apps/worker/api/openapi.yaml` - -**Interfaces:** -- Consumes: the field names from Task B2. `test/openapi-parity.test.ts` reads `contracts/openapi.yaml` and must pass after this task. - -> **Three copies exist.** `api/graycode-cloud-openapi.yaml` and `apps/worker/contracts/openapi.yaml` are byte-identical. `apps/worker/api/openapi.yaml` is a stale 1371-line variant whose `servers.url` is the personal dev subdomain `https://graycode-cloud.lakshmanp230.workers.dev` (line 7). Nothing references it: no package script, no CI workflow, no turbo task. Deleting it removes both the drift and the personal subdomain in one step. - -- [ ] **Step 1: Confirm the stale copy is unreferenced before deleting** - -```bash -cd graycode-platform && grep -rn 'apps/worker/api/openapi\|api/openapi.yaml' --exclude-dir=node_modules --exclude-dir=.git . | grep -v '^./apps/worker/api/openapi.yaml' -``` - -Expected: no output. If anything references it, stop and repoint that reference at `apps/worker/contracts/openapi.yaml` instead of deleting. - -- [ ] **Step 2: Apply the renames to both live copies** - -```bash -cd graycode-platform -for f in api/graycode-cloud-openapi.yaml apps/worker/contracts/openapi.yaml; do - sed -i '' 's/hawkVersion/graycodeVersion/g' "$f" - sed -i '' 's/enum: \[hawk, swift/enum: [graycode, swift/' "$f" - sed -i '' 's/^ title: Hawk Cloud API/ title: Graycode Cloud API/' "$f" - sed -i '' 's/Control-plane API for Hawk and its related products/Control-plane API for Graycode and its related products/' "$f" - sed -i '' 's/Project-scoped hwc device token\./Project-scoped device token (`hwc_` prefix)./' "$f" -done -grep -n 'graycodeVersion\|enum: \[graycode\|title: Graycode Cloud' api/graycode-cloud-openapi.yaml -``` - -Expected: the rewritten lines print. The `hwc_` prefix itself stays; only its description changes. - -- [ ] **Step 3: Confirm the two live copies are still identical** - -```bash -cd graycode-platform && diff api/graycode-cloud-openapi.yaml apps/worker/contracts/openapi.yaml && echo "COPIES IN SYNC" -``` - -Expected: `COPIES IN SYNC`. - -- [ ] **Step 4: Delete the stale copy** - -```bash -cd graycode-platform && git rm apps/worker/api/openapi.yaml -``` - -- [ ] **Step 5: Run the parity test** - -```bash -cd graycode-platform/apps/worker && npx vitest run test/openapi-parity.test.ts -``` - -Expected: PASS. - -- [ ] **Step 6: Commit** - -```bash -git add api/graycode-cloud-openapi.yaml apps/worker/contracts/openapi.yaml -git commit -m "docs: rename the cloud contract to graycode and drop the stale copy - -apps/worker/api/openapi.yaml was an unreferenced 1371-line variant -still advertising a personal workers.dev subdomain as its server URL. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task B5: Align the CLI's cloud test fixture and document the endpoint - -**Files:** -- Modify: `graycode-cli/internal/platform/cloud/client_test.go:22` -- Modify: `graycode-cli/README.md` (the Portable Execution Graph section, around line 127) - -**Interfaces:** -- Consumes: the capability values from Task B2. No production CLI code changes; `client.go:105` and `exec.go:373` were already correct. - -> **A real gap this plan does not close.** `graycode cloud login` has no default endpoint: it requires `--endpoint` or `GRAYCODE_CLOUD_URL` (`cmd/cloud.go:41-46`). The worker's `wrangler.jsonc` declares no route or custom domain, so it is reachable only at its `workers.dev` address, and the contract's documented `servers.url` of `https://api.graycodeai.com` is the **BFF**, which requires a browser session cookie and returns 401 to a device token (`apps/bff/src/app.ts:54-58`). Choosing and provisioning a public hostname for the worker is an infrastructure decision outside this plan. Step 2 documents the requirement so users are not left guessing. - -- [ ] **Step 1: Fix the test fixture** - -In `graycode-cli/internal/platform/cloud/client_test.go:22`, change `Capability: "graycode"` — it is already correct and matches the renamed enum. Verify rather than edit: - -```bash -cd graycode-cli && grep -n 'Capability:' internal/platform/cloud/client_test.go -``` - -Expected: `Capability: "graycode"`. No edit needed. If it reads `"hawk"`, change it to `"graycode"`. - -- [ ] **Step 2: Document that the cloud endpoint must be supplied** - -In `graycode-cli/README.md`, directly under the `graycode cloud graph sync` code block, add: - -```markdown -Cloud commands require an endpoint. There is no default: pass `--endpoint` or -set `GRAYCODE_CLOUD_URL` to your Graycode Cloud worker URL before running -`graycode cloud login`. `https://api.graycodeai.com` is the browser BFF and -will reject a device token. -``` - -- [ ] **Step 3: Verify and commit** - -```bash -cd graycode-cli && go test ./internal/platform/cloud/ -v -git add internal/platform/cloud/client_test.go README.md -git commit -m "docs: state that graycode cloud requires an explicit endpoint - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - -- [ ] **Step 4: Deploy in the correct order** - -After the graycode-platform PRs merge: - -```bash -cd graycode-platform/apps/worker -npx wrangler d1 migrations apply graycode-cloud --remote # 0024 FIRST -cd ../.. && pnpm deploy:worker -pnpm deploy:bff -``` - -Expected: migration reports one statement batch applied, then both workers deploy. Deploying the worker before the migration breaks every device read. - ---- - -## Workstream C — Router Boundary Truth - -**Problem.** This workstream fixes documentation and two defective guards. **There is no import violation to repair: all nine boundary guards pass today.** - -What is actually wrong: - -1. **The documented boundary contradicts the enforced one.** `graycode-router/AGENTS.md:570-572` says graycode-cli must not assemble anything below `engine`. In reality all three enforcement layers deliberately allow four packages — `engine`, `llm`, `graph`, `tools` — and eight non-test files depend on that allowance (`internal/config/catalog_api.go:9`, `internal/engine/client_interface.go:7`, `internal/engine/compact_provider_native.go:8`, `internal/engine/execution_graph_observations.go:16`, `internal/provider/gateway/engine_client.go:16`, `internal/provider/gateway/gateway.go:15`, `internal/session/session.go:23`, `internal/types/client.go:6`). Six symbols they use have no `engine` equivalent at all: `ChatOptions`, `ContinuationConfig`, `StreamResult`/`NewStreamResult`, `ResponseFormat`, `ImageURLPart`, `InputAudioPart`, plus the `ModelClass*` constants and `GatewayRegionOption`. -2. **A guard that can never fail.** `graycode-cli/scripts/check-support-repo-coupling.sh:30-32` builds its regex from the peer list. With `graycode-router` the only engine in `ecosystem.yaml`, the peer list is empty and the pattern degenerates to `github\.com/GrayCodeAI/()(/|")`. Verified with `bash -x`. -3. **A guard missing from pre-push.** `lefthook.yml:115-125` runs three boundary scripts but omits `check-graycode-router-engine-boundary.sh` (verified: zero occurrences in the file). It runs in CI, so violations are caught late instead of before push. -4. **A dead exception.** Both Go AST tests carry a `internal/provider/gateway` → `credentials` exception (`internal/testaudit/audit_test.go:223-226`, `internal/testaudit/package_boundaries_test.go:61-65`). Zero non-test files import `graycode-router/credentials`. - -**Approach.** Document the four-package contract as the real boundary, make the vacuous guard honest, and add the missing hook. Do not attempt to collapse the CLI onto `engine`-only; six required symbols are unexported from the facade and widening `engine` is a graycode-router API change that belongs in its own plan. - ---- - -### Task C1: Make the documented boundary match the enforced one - -**Files:** -- Modify: `graycode-router/AGENTS.md:551,570-572` -- Modify: `graycode-router/README.md:48-53` (the Ecosystem Boundaries section) -- Modify: `graycode-cli/scripts/check-graycode-router-engine-boundary.sh:18-19` (comment only) - -**Interfaces:** -- Produces: one written contract naming exactly four allowed packages, cited by both repos. - -- [ ] **Step 1: State the real contract in graycode-router** - -In `graycode-router/README.md`, replace the Ecosystem Boundaries bullets at lines 48-53: - -```markdown -graycode-router is a Graycode support engine. Keep the dependency edge one-way. - -Hosts may import exactly four packages: - -| Package | Carries | -|---|---| -| `engine` | the stable host-facing facade | -| `llm` | host-facing DTOs and the `Provider` port that `engine` re-exports as aliases | -| `graph` | the portable execution-graph vocabulary | -| `tools` | tool-call and tool-result contracts | - -Everything else is engine-internal: `client`, `catalog`, `config`, -`credentials`, `router`, `runtime`, and their subpackages are not shared -contracts. Enforced by `graycode-cli/scripts/check-graycode-router-engine-boundary.sh` -and two Go AST tests in `graycode-cli/internal/testaudit/`. - -- do not import `graycode-cli/internal/*` -- do not import the removed legacy path `graycode/shared/types` -- do not import other engines (`harrier`, `shrike`, `swift`, `kestrel`, - `merlin`) — engines are peers, not dependencies -``` - -- [ ] **Step 2: Correct the AGENTS.md pitfall** - -In `graycode-router/AGENTS.md`, replace the first Common Pitfalls bullet (lines 570-572): - -```markdown -- `engine`, `llm`, `graph` and `tools` are the host contract surface. Graycode - must not assemble `client`, `catalog`, `config`, `credentials`, `router` or - `runtime`. Six symbols Graycode needs (`ChatOptions`, `ContinuationConfig`, - `StreamResult`, `ResponseFormat`, `ImageURLPart`, `InputAudioPart`) live in - `llm` with no `engine` alias; widening the facade to cover them is a - deliberate API change, not an incidental one. -``` - -- [ ] **Step 3: Correct the misleading guard comment** - -`graycode-cli/scripts/check-graycode-router-engine-boundary.sh:18-19` currently claims Graycode uses "the full vendored GraycodeRouter API surface", which overstates a four-package allowance. Replace both comment lines: - -```bash -# Host contract surface is exactly four packages: engine (facade), llm (DTOs -# and the Provider port), graph (portable graph vocabulary), tools (tool-call -# contracts). See graycode-router/README.md "Ecosystem Boundaries". -``` - -- [ ] **Step 4: Verify the guards still pass** - -```bash -cd graycode-cli && bash ./scripts/check-graycode-router-engine-boundary.sh && echo "EXIT=$?" -``` - -Expected: `graycode-router engine boundary passed (zero lower-level production imports)` then `EXIT=0`. - -- [ ] **Step 5: Commit (two repos, two commits)** - -```bash -cd graycode-router -git add README.md AGENTS.md -git commit -m "docs: document the four-package host contract surface - -AGENTS.md claimed engine-only while all three enforcement layers -deliberately allow engine, llm, graph and tools, and eight production -files depend on that allowance. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" - -cd ../graycode-cli -git add scripts/check-graycode-router-engine-boundary.sh -git commit -m "docs: correct the router boundary guard comment - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task C2: Repair the two defective guards - -**Files:** -- Modify: `graycode-cli/scripts/check-support-repo-coupling.sh:29-33` -- Modify: `graycode-cli/lefthook.yml:118-125` -- Modify: `graycode-cli/internal/testaudit/audit_test.go:223-226` -- Modify: `graycode-cli/internal/testaudit/package_boundaries_test.go:61-65` - -**Interfaces:** -- Produces: a peer guard that reports honestly when it has nothing to check, an engine-boundary check on pre-push, and both AST tests with the dead exception removed. - -- [ ] **Step 1: Make the vacuous guard honest** - -In `graycode-cli/scripts/check-support-repo-coupling.sh`, guard the empty-peer case before building the pattern. Insert immediately after the `pattern=` assignment at line 30: - -```bash - if [[ ${#peers[@]} -eq 0 ]]; then - # No sibling engines to check against. Building a regex from an empty - # peer list produced 'github\.com/GrayCodeAI/()(/|")', which matches - # nothing meaningful and made this guard silently unfailable. - echo "peer guard: ${repo} has no sibling engines to check" - continue - fi -``` - -Confirm `peers` is the array name and `continue` is inside the per-repo loop; read lines 20-40 first and adapt the variable names to what is actually there. - -- [ ] **Step 2: Verify the guard now reports rather than pretending** - -```bash -cd graycode-cli && bash ./scripts/check-support-repo-coupling.sh; echo "EXIT=$?" -``` - -Expected: `peer guard: graycode-router has no sibling engines to check` followed by the existing pass line, `EXIT=0`. - -- [ ] **Step 3: Add the missing pre-push hook** - -In `graycode-cli/lefthook.yml`, add a fourth boundary command alongside the existing three (after the `boundary-graycode-router-client` block ending at line 119): - -```yaml - boundary-graycode-router-engine: - run: bash scripts/check-graycode-router-engine-boundary.sh -``` - -- [ ] **Step 4: Verify lefthook parses and the hook is registered** - -```bash -cd graycode-cli && python3 -c "import yaml; d=yaml.safe_load(open('lefthook.yml')); print(sorted(d['pre-push']['commands']))" -``` - -Expected: the list includes `boundary-graycode-router-engine`. - -- [ ] **Step 5: Remove the dead credentials exception** - -Delete the exception block at `graycode-cli/internal/testaudit/audit_test.go:223-226`: - -```go - if strings.HasPrefix(rel, "internal/provider/gateway/") && path == graycodeRouterModule+"/credentials" { - continue - } -``` - -And at `graycode-cli/internal/testaudit/package_boundaries_test.go:61-65`, the equivalent block keyed on `filepath.ToSlash(filepath.Dir(relFile)) == "internal/provider/gateway"`. - -Verify nothing depended on them first: - -```bash -cd graycode-cli && grep -RInE --include='*.go' --exclude='*_test.go' 'graycode-router/credentials' . ; echo "exit=$? (1 means clean)" -``` - -Expected: no output, `exit=1`. - -- [ ] **Step 6: Run both AST tests and the whole guard set** - -```bash -cd graycode-cli && go test ./internal/testaudit/ -run 'TestNoDirectLowerGraycodeRouterImports|TestPackageDependencyGraph' -count=1 -v && make boundaries -``` - -Expected: both tests PASS, all guards pass. - -- [ ] **Step 7: Commit** - -```bash -git add scripts/check-support-repo-coupling.sh lefthook.yml internal/testaudit/audit_test.go internal/testaudit/package_boundaries_test.go -git commit -m "fix: repair two boundary guards that could not fail - -check-support-repo-coupling.sh built its regex from an empty peer list, -degenerating to a pattern matching nothing. The engine-boundary script -ran in CI but not on pre-push. Both AST tests carried a -gateway->credentials exception that no production file uses. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -## Workstream D — Naming Sweep - -**Scope (approved):** documentation, user-visible strings, and code comments. Roughly 230 sites. `graycode-cli` is already clean: it contains **zero** occurrences of `hawk` outside generated GitNexus blocks. Marketing copy under `graycode-platform/apps/web` is out of scope; it is dated content and product-line branding. - -Every load-bearing identifier listed in Global Constraints stays untouched. When in doubt, a name that appears in a wire payload, a database column, an environment variable, a filesystem path, a Cloudflare binding, a Go module path, or a URL route is load-bearing. - -**Two bonus defects folded in here:** -- A prior substring mass-rename corrupted prose in 15 places: `trace`→`swift` produced "Step-by-step execution swift", `inspect`→`merlin` produced "Merlin configured MCP servers", `tok`→`shrike` produced "ref-shrike". -- `graycode-skills` advertises "12,171+ skills" and "31 categories". The real counts are **12,167 skills** and **27 categories** (verified by `find … -name SKILL.md | wc -l` and `ls categories | wc -l`). - ---- - -### Task D1: graycode-skills — rebrand from starling and correct the counts - -**Files:** -- Modify: `README.md:1,3,7,16,19,22,25,99-102`, `CONTRIBUTING.md:1,3,82,175`, `AGENTS.md:2,7,9,44-48`, `SECURITY.md:1`, `CHANGELOG.md:3`, `api/openapi.yaml:3,5,9,13,15,17,38-39`, `docs/architecture.md:3,5,16,18,25,45`, `pyproject.toml:8`, `tools/init_skill.py:59`, `tools/sign_manifest.py:141`, `scripts/check-consumer-boundaries.sh:10` - -**Interfaces:** -- Produces: no code behavior change. `pyproject.toml` package `name = "starling"` at line 6 is **not** changed here; renaming a published package name is a release decision. - -- [ ] **Step 1: Correct the counts and the CLI name in the README** - -In `graycode-skills/README.md`: -- Line 1: `# hawk Community Skills` → `# Graycode Community Skills` -- Line 3: `[hawk](https://github.com/GrayCodeAI/hawk)` → `[Graycode](https://github.com/GrayCodeAI/graycode-cli)`, and `12,171+` → `12,167`, and `31 categories` → `27 categories` -- Line 7: `that hawk loads` → `that Graycode loads` -- Lines 16, 19, 25: `hawk skills list` → `graycode skills list`, `hawk skills search api-testing` → `graycode skills search api-testing`, `the hawk REPL` → `the graycode REPL` -- Line 22: `hawk skills install python-pandas` → `graycode skills install GrayCodeAI/graycode-skills python-pandas` - -> The install syntax genuinely differs: `graycode skills install` takes ` [skill-name]` (`graycode-cli/cmd/skills_cmd.go:74`), not a bare skill name. Documenting the bare form would keep the command broken even after Workstream A. - -- [ ] **Step 2: Fix the Ecosystem Boundaries block** - -`README.md:99-102` and `AGENTS.md:44-46` list **pre-rename engine names** that no longer exist: `yaad`, `tok`, `trace`, `sight`, `inspect`. Replace with the live names: - -```markdown -- `graycode-skills` extends Graycode through public skill and plugin surfaces. -- Do not reference support engine repos (`graycode-router`, `harrier`, `shrike`, - `swift`, `kestrel`, `merlin`) as direct dependencies. -- Do not reference `graycode-cli/internal/*` or the removed legacy path - `graycode/shared/types`. -- Skills should assume Graycode is the product boundary. -``` - -- [ ] **Step 3: Update the boundary guard's alternation to match** - -`graycode-skills/scripts/check-consumer-boundaries.sh` still forbids the pre-rename engine names. The pattern is inline inside the `grep -RInE` call at lines 7-12, not a variable. Replace the pattern argument on line 10: - -```bash - 'github\.com/GrayCodeAI/(graycode-router|harrier|shrike|swift|kestrel|merlin)(/|")|github\.com/GrayCodeAI/graycode-cli/(internal/|shared/types)' \ -``` - -Also update the two failure messages at lines 16 and 19 so they name Graycode rather than Hawk and graycode-skills rather than starling. - -Verify it still runs clean: - -```bash -cd graycode-skills && bash scripts/check-consumer-boundaries.sh; echo "EXIT=$?" -``` - -Expected: `EXIT=0`. This guard scans `README.md docs api tests tools .claude-plugin .codex-plugin .cursor-plugin`, so it will fail if Step 2's replacement text accidentally reintroduces a forbidden module path. - -- [ ] **Step 4: Sweep the remaining prose files** - -```bash -cd graycode-skills -for f in CONTRIBUTING.md AGENTS.md SECURITY.md CHANGELOG.md api/openapi.yaml docs/architecture.md pyproject.toml tools/init_skill.py tools/sign_manifest.py; do - sed -i '' -e 's/\bstarling\b/graycode-skills/g' -e 's/\bStarling\b/Graycode Skills/g' \ - -e 's/\bhawk-eco\b/graycode-eco/g' -e 's/\bHawk\b/Graycode/g' -e 's/\bhawk\b/graycode/g' "$f" -done -sed -i '' 's/12,171+/12,167/g; s/31 categories/27 categories/g' CONTRIBUTING.md -sed -i '' 's/^name = "graycode-skills"/name = "starling"/' pyproject.toml # restore the package name -grep -rn 'GrayCodeAI/graycode-skills' pyproject.toml || true -``` - -- [ ] **Step 5: Repair what the blunt sweep broke** - -The `sed` above also rewrites the GitNexus block in `AGENTS.md` and any load-bearing identifier. Restore them: - -```bash -cd graycode-skills -git diff --stat -git diff | grep -nE '^\+.*(AGENT_ENUM|progressive-disclosure|/graycode:|sync_marketplace)' || echo "no load-bearing identifiers touched" -``` - -Inspect `git diff` in full. Revert any hunk that changes a value inside a `` block, the `/hawk:` invoke prefix, the `AGENT_ENUM` values, or the `hawk-progressive-disclosure` marker. Those files (`tools/sync_marketplace.py`, `tools/validate_skill.py`, `tools/migrate_oversized_skills.py`) are not in the Step 4 loop, but verify nothing else drifted. - -- [ ] **Step 6: Verify the corpus still validates** - -```bash -cd graycode-skills && python -m pytest -q && python tools/validate_skill.py --all --warning-budget tools/validation_warning_budget.json && ruff check . -``` - -Expected: tests PASS, zero validation warnings, ruff clean. - -- [ ] **Step 7: Commit** - -```bash -git add -A -git commit -m "docs: rebrand from starling/hawk to graycode-skills/graycode - -Also corrects the advertised counts (12,167 skills across 27 -categories, not 12,171+ across 31), documents the real -'graycode skills install ' syntax, and replaces the -pre-rename engine names in the boundary docs and guard. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task D2: graycode-router — docs, user-facing strings, and comments - -**Files:** -- Modify: `README.md:33,39,42,48,50,52,53,177,305,309`, `AGENTS.md:18,52,58,144`, `CONTRIBUTING.md:4`, `SECURITY.md:10`, `docs/ARCHITECTURE.md:17`, `docs/architecture/HOST-ENGINE-BOUNDARY.md` (21 lines), `docs/guides/DYNAMIC-MODEL-DISCOVERY.md` (24 lines), `docs/guides/CREDENTIAL-SETUP-FLOW.md:1,3,38,53,72`, `docs/design/GRAYCODE-ROUTER-ENTERPRISE.md:61,78` -- Modify (user-facing strings): `catalog/v1.go:633`, `setup/status.go:135`, `runtime/preflight.go:48` -- Modify (comments): `llm/types.go`, `llm/provider.go` and ~140 further comment lines -- Modify: `scripts/test-config-flow.sh:44` -- Modify: `scripts/check-ecosystem-boundaries.sh:10` - -**Interfaces:** -- Produces: no behavior change. Three user-visible error strings change wording only. - -- [ ] **Step 1: Fix the three user-facing strings first** - -These are the only sweep items a user can actually see in the terminal. - -- `graycode-router/catalog/v1.go:633`: `run: hawk models refresh` → `run: graycode models refresh` -- `graycode-router/setup/status.go:135`: `hawk refreshes automatically; use \`hawk models refresh\`` → `graycode refreshes automatically; use \`graycode models refresh\`` -- `graycode-router/runtime/preflight.go:48`: `hawk will discover on /config` → `graycode will discover on /config` - -- [ ] **Step 2: Write a test that pins them** - -Create `graycode-router/setup/naming_test.go`: - -```go -package setup - -import ( - "os" - "path/filepath" - "strings" - "testing" -) - -// TestNoLegacyHostNameInUserFacingStrings guards the three call sites that -// print a command for the user to run. They named the old product. -func TestNoLegacyHostNameInUserFacingStrings(t *testing.T) { - files := []string{ - filepath.Join("..", "catalog", "v1.go"), - filepath.Join("..", "setup", "status.go"), - filepath.Join("..", "runtime", "preflight.go"), - } - for _, f := range files { - data, err := os.ReadFile(f) // #nosec G304 -- fixed test fixture paths - if err != nil { - t.Fatalf("read %s: %v", f, err) - } - for i, line := range strings.Split(string(data), "\n") { - if !strings.Contains(line, `"`) { - continue - } - if strings.Contains(line, "hawk models refresh") || strings.Contains(line, "hawk will discover") || strings.Contains(line, "hawk refreshes") { - t.Errorf("%s:%d prints the legacy host name to the user: %s", f, i+1, strings.TrimSpace(line)) - } - } - } -} -``` - -- [ ] **Step 3: Run it** - -```bash -cd graycode-router && go test ./setup/ -run TestNoLegacyHostNameInUserFacingStrings -v -``` - -Expected: PASS after Step 1. Revert one string temporarily to confirm the test can fail, then restore it. - -- [ ] **Step 4: Sweep the documentation** - -```bash -cd graycode-router -for f in README.md AGENTS.md CONTRIBUTING.md SECURITY.md docs/ARCHITECTURE.md \ - docs/architecture/HOST-ENGINE-BOUNDARY.md docs/guides/DYNAMIC-MODEL-DISCOVERY.md \ - docs/guides/CREDENTIAL-SETUP-FLOW.md docs/design/GRAYCODE-ROUTER-ENTERPRISE.md; do - sed -i '' -e 's|GrayCodeAI/hawk|GrayCodeAI/graycode-cli|g' -e 's/\bhawk-eco\b/graycode-eco/g' \ - -e 's/\bHawk\b/Graycode/g' -e 's/\bhawk\b/graycode/g' "$f" -done -grep -rn '\bhawk\b\|\bHawk\b' README.md AGENTS.md docs/ || echo "DOCS CLEAN" -``` - -- [ ] **Step 5: Fix the three doc facts the sweep cannot fix** - -- `README.md:50` says DTOs live in `eagle/llm`. The `eagle` module was removed and vendored. Change to: ``host-facing DTOs and the `Provider` port live in `llm/`; `engine/` re-exports them as aliases``. -- `SECURITY.md:10` and `CONTRIBUTING.md:4` link `VERSIONING.md` at the repo root of a repo that no longer exists. Point both at `https://github.com/GrayCodeAI/graycode-cli/blob/main/docs/versioning.md`. -- `docs/guides/CREDENTIAL-SETUP-FLOW.md:53` cites `hawk/cmd/chat_config_xiaomi.go`. Verify the current filename before writing a replacement: - -```bash -ls ../graycode-cli/cmd/ | grep -i 'chat_config' -``` - -Use whichever file actually exists; if none matches, delete the file reference rather than inventing one. - -- [ ] **Step 6: Remove the hard-coded personal path** - -`graycode-router/scripts/test-config-flow.sh:44` contains an absolute path into a previous working directory. Replace: - -```bash -cd "$(dirname "$0")/.." -``` - -- [ ] **Step 7: Extend the ecosystem guard to the current repo name** - -`graycode-router/scripts/check-ecosystem-boundaries.sh:10` forbids only `github.com/GrayCodeAI/hawk`, a module that no longer exists, while permitting the real host module. Widen the pattern, keeping the variable name `FORBIDDEN_HAWK` because it is read at lines 16 and 20: - -```bash -FORBIDDEN_HAWK='github\.com/GrayCodeAI/(hawk|graycode-cli)(/|")' -``` - -The comment above it at lines 7-9 also claims shared vocabulary "belongs in eagle", a module that was removed and vendored into `graycode-cli/internal/contracts`. Rewrite it: - -```bash -# GraycodeRouter is host-neutral: it must not depend on any Graycode package. -# Shared ecosystem vocabulary lives in graycode-cli/internal/contracts, which -# hosts vendor rather than import from here. -``` - -Verify: - -```bash -cd graycode-router && bash scripts/check-ecosystem-boundaries.sh; echo "EXIT=$?" -``` - -Expected: `EXIT=0`. - -- [ ] **Step 8: Sweep the Go comments** - -```bash -cd graycode-router -grep -rln '\bhawk\b\|\bHawk\b' --include='*.go' . | while read -r f; do - sed -i '' -e 's|// \(.*\)\bHawk\b|// \1Graycode|g' -e 's|// \(.*\)\bhawk\b|// \1graycode|g' "$f" -done -git diff --stat -``` - -Then inspect the diff and revert every hunk touching a **string literal** rather than a comment, and every load-bearing identifier: `HAWK_CONFIG_DIR` (`config/provider_env.go:402`, `config/category.go:122`, `engine/engine.go`), the `~/.hawk` paths (`credentials/migrate.go:14,88`), and `hawk_build` / `hawk_build_concise` (`tools/versioning.go:114,115`). - -```bash -git diff | grep -nE '^\+.*(HAWK_CONFIG_DIR|\.hawk|hawk_build)' && echo "REVERT THESE HUNKS" || echo "no load-bearing identifiers touched" -``` - -- [ ] **Step 9: Build, test, format** - -```bash -cd graycode-router && gofumpt -l . && go build ./... && go test ./... && go vet ./... -``` - -Expected: `gofumpt -l` prints nothing, build and vet clean, all tests PASS. - -- [ ] **Step 10: Commit** - -```bash -git add -A -git commit -m "docs: rename the host from hawk to graycode across docs and comments - -Also fixes three user-facing strings that told users to run -'hawk models refresh', corrects the removed eagle/llm reference, -repoints dead VERSIONING.md links, removes a hard-coded personal path -from test-config-flow.sh, and extends the ecosystem guard to the -current host module name. - -Keeps HAWK_CONFIG_DIR, ~/.hawk migration paths and hawk_build tool -namespaces: those are compatibility values, not prose. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task D3: graycode-platform — docs and the rename-corruption typos - -**Files:** -- Modify: `README.md:16,20,29,40,56,94,103`, `ARCHITECTURE.md:5,13,254,314`, `AGENTS.md:34,38,70`, `CLAUDE.md:31`, `CONTRIBUTING.md:4`, `SECURITY.md:10`, `CHANGELOG.md:3`, `docs/architecture.md:6`, `apps/worker/README.md:1,70,82`, `apps/bff/README.md:7`, `apps/worker/docs/{ENCRYPTION-KEY-ROTATION,DELIVERY-CONTEXT-FLOW,ARCHITECTURE-IMPLEMENTATION}.md`, `apps/worker/migrations/0003_cli_device_authorization.sql:1` (comment only), `apps/worker/src/app.ts:57`, `scripts/deploy.sh:24,26` - -**Interfaces:** -- Consumes: nothing. Runs independently of Workstream B, though B should land first so the docs describe the shipped contract. - -- [ ] **Step 1: Fix the three rename-corruption typos** - -A prior `trace`→`swift` substring rename corrupted generated GitNexus tables. In `AGENTS.md:70` and `CLAUDE.md:31`: - -```markdown -| `gitnexus://repo/graycode-platform/process/{name}` | Step-by-step execution trace | -``` - -Check the same corruption in the other three repos: - -```bash -cd /Users/lakshmanpatel/Desktop/OSS2026/graycode-eco -grep -rn 'execution swift' --include='*.md' --exclude-dir=node_modules --exclude-dir=.git . -``` - -Fix every hit to `execution trace`. - -- [ ] **Step 2: Fix the stale README warning** - -`graycode-platform/README.md:56` warns about `lakshmanp230.workers.dev` and names `apps/web/public/_headers`, `apps/web/dist/_headers` and `api/graycode-cloud-openapi.yaml`. Those three files are already clean; the only remaining occurrence was `apps/worker/api/openapi.yaml:7`, deleted in Task B4. Replace the whole warning block with an accurate one: - -```markdown -> ⚠️ The Graycode Cloud worker has no route or custom domain in -> `apps/worker/wrangler.jsonc`, so it is reachable only at its generated -> `workers.dev` address. CLI device traffic needs a stable hostname before -> launch; `api.graycodeai.com` is the browser BFF and rejects device tokens. -``` - -- [ ] **Step 3: Fix the architecture diagram's dead host** - -`ARCHITECTURE.md:314` renders a node labelled `graycode-api.workers.dev`, a host that does not exist. Replace with `api.graycodeai.com`. - -- [ ] **Step 4: Sweep the prose** - -```bash -cd graycode-platform -for f in README.md ARCHITECTURE.md AGENTS.md CONTRIBUTING.md SECURITY.md CHANGELOG.md \ - docs/architecture.md apps/worker/README.md apps/bff/README.md \ - apps/worker/docs/ENCRYPTION-KEY-ROTATION.md apps/worker/docs/DELIVERY-CONTEXT-FLOW.md \ - apps/worker/docs/ARCHITECTURE-IMPLEMENTATION.md; do - sed -i '' -e 's/Hawk Cloud/Graycode Cloud/g' -e 's|GrayCodeAI/hawk|GrayCodeAI/graycode-cli|g' \ - -e 's/\bhawk-eco\b/graycode-eco/g' -e 's/\bHawk\b/Graycode/g' -e 's/\bhawk\b/graycode/g' "$f" -done -``` - -- [ ] **Step 5: Restore the identifiers the sweep clobbered** - -```bash -cd graycode-platform -git diff | grep -nE '^\+.*(GraycodeCloudService|GRAYCODE_CLOUD"|graycode\.window_days|graycode\.sessions|graycode\.tokens)' && echo "REVERT THESE" || echo "clean" -``` - -`HawkCloudService` (RPC entrypoint), `HAWK_CLOUD` (service binding) and the `hawk.*` OTel metric names must survive. Revert any hunk that renamed them. In `apps/worker/README.md:70` the sentence should read: "The BFF calls Graycode Cloud through the named `HawkCloudService` RPC entrypoint." - -- [ ] **Step 6: Fix the login command name** - -`apps/worker/README.md:82` and `apps/worker/docs/ENCRYPTION-KEY-ROTATION.md:34` reference `hawk login`. The real command is `graycode cloud login` (`graycode-cli/cmd/cloud.go:35`). The Step 4 sweep produces `graycode login`, which is wrong. Correct both to `graycode cloud login`. - -```bash -cd graycode-platform && grep -rn 'graycode login' apps/ docs/ *.md -``` - -Expected after fixing: no output. - -- [ ] **Step 7: Fix the two remaining strings** - -- `apps/worker/src/app.ts:57`: `'Hawk Cloud request failed'` → `'Graycode Cloud request failed'` -- `scripts/deploy.sh:24,26`: `Deploying Hawk Cloud Worker` and `Hawk Cloud Worker deployed` → `Graycode Cloud Worker` -- `apps/worker/migrations/0003_cli_device_authorization.sql:1`: the comment mentioning `hawk login` → `graycode cloud login`. **Comment only. The filename and the column stay** — the column is renamed by migration `0024`, never by editing an applied migration. - -- [ ] **Step 8: Typecheck, test, format** - -```bash -cd graycode-platform && pnpm --filter @graycode/worker check && pnpm --filter @graycode/bff check -``` - -Expected: prettier clean, typecheck clean, all tests PASS. - -- [ ] **Step 9: Commit** - -```bash -git add -A -git commit -m "docs: rename Hawk Cloud to Graycode Cloud across platform docs - -Also corrects 'graycode login' to the real 'graycode cloud login' -command, replaces a stale workers.dev warning that named three -already-clean files, fixes a dead graycode-api.workers.dev node in the -architecture diagram, and repairs 'execution swift' typos left by an -earlier substring rename. - -Keeps HAWK_CLOUD, HawkCloudService and the hawk.* OTel metric names. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -### Task D4: graycode-cli — refresh the ecosystem inventory and drop starling fixtures - -**Files:** -- Modify: `graycode-cli/README.md` (the Ecosystem section, around lines 442-490) -- Modify: `graycode-cli/internal/plugin/registry_test.go:20,21,167,192,250,276,285` -- Modify: `graycode-cli/internal/plugin/skillslock_test.go:20` - -**Interfaces:** -- Consumes: the four-package contract from Task C1. - -> `graycode-cli` has zero `hawk` occurrences. The only stale names are `starling` in test fixtures and an ecosystem table that lists repos not present in `ecosystem.yaml`. - -- [ ] **Step 1: Replace the starling fixtures** - -```bash -cd graycode-cli -sed -i '' 's|GrayCodeAI/starling|GrayCodeAI/graycode-skills|g' internal/plugin/registry_test.go internal/plugin/skillslock_test.go -grep -rn 'starling' internal/ cmd/ || echo "CLI CLEAN" -``` - -- [ ] **Step 2: Correct the ecosystem section** - -`README.md:442-490` names `owl`, `falcon` and other repos as ecosystem members, but `ecosystem.yaml` lists exactly four: `graycode-cli`, `graycode-router`, `graycode-skills`, `graycode-platform`. Make the prose match the manifest, and extend the component table at the end of the section: - -```markdown -| Component | Repository | Purpose | -|---|---|---| -| **graycode** | This repo | AI coding agent | -| **graycode-router** | [GrayCodeAI/graycode-router](https://github.com/GrayCodeAI/graycode-router) | LLM provider runtime | -| **graycode-skills** | [GrayCodeAI/graycode-skills](https://github.com/GrayCodeAI/graycode-skills) | Community skill registry | -| **graycode-platform** | [GrayCodeAI/graycode-platform](https://github.com/GrayCodeAI/graycode-platform) | Web, BFF, and Graycode Cloud | - -`ecosystem.yaml` is the canonical inventory; tooling reads it rather than -carrying its own list. Support engines mounted through Go module -dependencies (`harrier`, `shrike`, `swift`, `kestrel`, `merlin`, `falcon`) -live in their own repositories and are not part of this workspace. -``` - -- [ ] **Step 3: Verify the manifest and the prose agree** - -```bash -cd graycode-cli && ./scripts/ecosystem-manifest.sh list && go test ./internal/plugin/ ./internal/testaudit/ -count=1 -``` - -Expected: the manifest lists exactly the four repos named in the table; tests PASS. - -- [ ] **Step 4: Commit** - -```bash -git add README.md internal/plugin/registry_test.go internal/plugin/skillslock_test.go -git commit -m "docs: align the ecosystem section with ecosystem.yaml - -Also replaces GrayCodeAI/starling test fixtures with the repo's -current name. - -Claude-Session: https://claude.ai/code/session_01MTUKadN91fcmuhxGWYVe2k" -``` - ---- - -## Execution Order - -The workstreams are independent, but two ordering constraints are real: - -1. **A1 → A2 → A3.** The CLI cannot be verified until the release URL is live. -2. **B1 → B2/B3/B4 → deploy.** The migration must be applied before the worker deploys. - -Recommended sequence, one PR per repo per workstream: - -| Order | Workstream | Repos | Blocking? | -|---|---|---|---| -| 1 | A (registry) | graycode-skills, then graycode-cli | A2 must merge and run before A3 verifies | -| 2 | B (wire contract) | graycode-platform, graycode-cli | migration before deploy | -| 3 | C (boundary truth) | graycode-router, graycode-cli | none | -| 4 | D (naming) | all four | run last so it sweeps the text A/B/C introduce | - ---- - -## Self-Review - -**Coverage.** Every finding from the scouting pass maps to a task: registry publishing (A2), registry shape (A1), registry consumption (A3), install discovery (A4), device-login field drift (B1-B2), usage capability drift (B1-B2), BFF enum (B3), triplicate OpenAPI plus personal subdomain (B4), missing cloud endpoint documented as a known gap (B5), documented-vs-enforced boundary (C1), vacuous peer guard (C2), missing pre-push hook (C2), dead credentials exception (C2), legacy naming (D1-D4), wrong skill and category counts (D1), rename-corruption typos (D3), hard-coded personal path (D2). - -**Deliberately out of scope, and why.** -- Widening the `engine` facade to re-export `ChatOptions`, `StreamResult`, `ResponseFormat`, `ContinuationConfig`, `ImageURLPart` and `InputAudioPart` so graycode-cli could be `engine`-only. That is a graycode-router public API change and deserves its own plan. -- Provisioning a public hostname for the Graycode Cloud worker. Infrastructure decision, flagged in B5. -- Renaming the `starling` Python package name in `pyproject.toml:6`. A published-name change is a release decision. -- Marketing copy under `apps/web`, including the blog post claiming 12,147 skills across 21 categories. Dated content; correcting it is an editorial call. -- `graycode-cli`'s own `docs/` tree, which mentions competitors and legacy names inside dated design documents. - -**Known risk.** Tasks D1, D2 and D3 use `sed` sweeps followed by a manual diff review. The review step is not optional: each sweep is capable of rewriting a load-bearing identifier, and each of those tasks carries an explicit revert-check step naming the identifiers at risk. If a sweep's diff exceeds what a reviewer can read carefully, split it per file rather than trusting the pattern. diff --git a/internal/config/ecosystem_report.go b/internal/config/ecosystem_report.go index b8e95f322..0f886e602 100644 --- a/internal/config/ecosystem_report.go +++ b/internal/config/ecosystem_report.go @@ -12,7 +12,7 @@ import ( // EcosystemReport is the structured view of the ecosystem panel. type EcosystemReport struct { - Eyre EcosystemEyrie `json:"eyrie"` + Eyrie EcosystemEyrie `json:"eyrie"` Harrier EcosystemHarrier `json:"harrier"` Shrike EcosystemShrike `json:"shrike"` } @@ -42,16 +42,16 @@ func BuildEcosystemReport(ctx context.Context, provider, model string) Ecosystem // eyrie cat := CatalogHealthReport(ctx) - r.Eyre.CatalogExists = cat.Exists - r.Eyre.ModelCount = cat.Models + r.Eyrie.CatalogExists = cat.Exists + r.Eyrie.ModelCount = cat.Models pre := EnginePreflightReport(ctx) - r.Eyre.Ready = pre.Ready + r.Eyrie.Ready = pre.Ready if strings.TrimSpace(provider) != "" && provider != "auto" { - r.Eyre.Provider = provider + r.Eyrie.Provider = provider } if dep, err := EngineDeploymentSummary(ctx, model); err == nil { - r.Eyre.RoutingSource = dep.RoutingSource - r.Eyre.RoutingStages = dep.RoutingStages + r.Eyrie.RoutingSource = dep.RoutingSource + r.Eyrie.RoutingStages = dep.RoutingStages } // harrier diff --git a/internal/storage/paths.go b/internal/storage/paths.go index 134748b66..adf5332b8 100644 --- a/internal/storage/paths.go +++ b/internal/storage/paths.go @@ -49,13 +49,12 @@ func SettingsPath() string { } func ProviderConfigPath() string { - // Eyrie owns provider routing state. Keep HAWK_CONFIG_DIR as the - // compatibility fallback, but let Eyrie's host-neutral override win when - // both variables are configured. + // Eyrie owns provider routing state and resolves it from EYRIE_CONFIG_DIR, + // defaulting to its own directory under the user config root. if dir := cleanEnvDir(envEyrieConfigDir); dir != "" { return filepath.Join(dir, "provider.json") } - return filepath.Join(ConfigDir(), "provider.json") + return filepath.Join(mustUserConfigDir(), "eyrie", "provider.json") } func SessionsDir() string { diff --git a/internal/storage/paths_extra_test.go b/internal/storage/paths_extra_test.go index 835104f36..52f4d6ebf 100644 --- a/internal/storage/paths_extra_test.go +++ b/internal/storage/paths_extra_test.go @@ -284,25 +284,11 @@ func TestProviderConfigPath_EyrieOverride(t *testing.T) { } } -func TestProviderConfigPath_HawkFallback(t *testing.T) { - hawkDir := filepath.Join(t.TempDir(), "hawk") - t.Setenv(envConfigDir, hawkDir) - t.Setenv(envEyrieConfigDir, "") - - got := ProviderConfigPath() - want := filepath.Join(hawkDir, "provider.json") - if got != want { - t.Errorf("ProviderConfigPath() = %q, want %q", got, want) - } -} - func TestProviderConfigPath_EyrieWhitespaceIgnored(t *testing.T) { - hawkDir := filepath.Join(t.TempDir(), "hawk") - t.Setenv(envConfigDir, hawkDir) t.Setenv(envEyrieConfigDir, " ") // whitespace should be treated as empty got := ProviderConfigPath() - want := filepath.Join(hawkDir, "provider.json") + want := filepath.Join(mustUserConfigDir(), "eyrie", "provider.json") if got != want { t.Errorf("ProviderConfigPath() = %q, want %q", got, want) } diff --git a/internal/storage/paths_test.go b/internal/storage/paths_test.go index 6185bc4b5..7f9a336ff 100644 --- a/internal/storage/paths_test.go +++ b/internal/storage/paths_test.go @@ -39,13 +39,12 @@ func TestProviderConfigPathUsesEyrieOverrideWithoutMovingHawkSettings(t *testing } } -func TestProviderConfigPathFallsBackToHawkOverride(t *testing.T) { - hawkDir := filepath.Join(t.TempDir(), "hawk") - t.Setenv(envConfigDir, hawkDir) +func TestProviderConfigPathDefaultsToEyrieDir(t *testing.T) { + t.Setenv(envConfigDir, filepath.Join(t.TempDir(), "hawk")) t.Setenv(envEyrieConfigDir, " ") - if got, want := ProviderConfigPath(), filepath.Join(hawkDir, "provider.json"); got != want { - t.Fatalf("ProviderConfigPath() = %q, want HAWK_CONFIG_DIR fallback %q", got, want) + if got, want := ProviderConfigPath(), filepath.Join(mustUserConfigDir(), "eyrie", "provider.json"); got != want { + t.Fatalf("ProviderConfigPath() = %q, want Eyrie default %q", got, want) } } diff --git a/internal/tool/safety.go b/internal/tool/safety.go index 628ef1cba..256dcc6c7 100644 --- a/internal/tool/safety.go +++ b/internal/tool/safety.go @@ -289,10 +289,6 @@ func IsSensitivePath(path string) string { } if cfgDir := strings.TrimSpace(env.Getenv("HAWK_CONFIG_DIR")); cfgDir != "" { - customProv := filepath.Join(cfgDir, "provider.json") - if matchesResolvedPath(clean, customProv) { - return "access to provider.json is blocked for security (API credentials)" - } customEnv := filepath.Join(cfgDir, "env") if matchesResolvedPath(clean, customEnv) { return "access to hawk env file is blocked for security (API keys)" @@ -389,7 +385,7 @@ func CommandReferencesSensitivePath(command string) string { command = expandCommandPathVariables(command) configuredPaths := []string{storage.ProviderConfigPath()} if cfgDir := strings.TrimSpace(env.Getenv("HAWK_CONFIG_DIR")); cfgDir != "" { - configuredPaths = append(configuredPaths, filepath.Join(cfgDir, "provider.json"), filepath.Join(cfgDir, "env"), filepath.Join(cfgDir, ".env")) + configuredPaths = append(configuredPaths, filepath.Join(cfgDir, "env"), filepath.Join(cfgDir, ".env")) } for _, candidate := range configuredPaths { if candidate != "" && strings.Contains(command, candidate) { diff --git a/internal/tool/safety_test.go b/internal/tool/safety_test.go index f2aa46515..8b6050230 100644 --- a/internal/tool/safety_test.go +++ b/internal/tool/safety_test.go @@ -263,13 +263,12 @@ func TestIsSensitivePath(t *testing.T) { func TestIsSensitivePath_HawkConfigDir(t *testing.T) { cfgDir := t.TempDir() t.Setenv("HAWK_CONFIG_DIR", cfgDir) - prov := filepath.Join(cfgDir, "provider.json") - if reason := IsSensitivePath(prov); reason == "" { - t.Fatalf("expected custom HAWK_CONFIG_DIR provider.json blocked, got empty") + if reason := IsSensitivePath(filepath.Join(cfgDir, "env")); reason == "" { + t.Fatalf("expected custom HAWK_CONFIG_DIR env file blocked, got empty") } } -func TestIsSensitivePath_EyrieConfigDirTakesPrecedence(t *testing.T) { +func TestIsSensitivePath_EyrieConfigDir(t *testing.T) { hawkDir := filepath.Join(t.TempDir(), "hawk") eyrieDir := filepath.Join(t.TempDir(), "eyrie") t.Setenv("HAWK_CONFIG_DIR", hawkDir) From 72c5da1a38fe2aec421d76f79222e28f1a4b98fd Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 11 Sep 2026 12:58:49 +0530 Subject: [PATCH 3/4] build(deps): require github.com/GrayCodeAI/eyrie v0.3.0 v0.3.0 is the first eyrie release whose go.mod declares github.com/GrayCodeAI/eyrie after the rename back from graycode-router. v0.0.1 declared the old path, which broke every CI job that resolves modules without the local go.work. Co-Authored-By: Claude Opus 5 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1cbb7407a..4ab35ae22 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( charm.land/bubbles/v2 v2.1.1 charm.land/bubbletea/v2 v2.0.8 charm.land/lipgloss/v2 v2.0.5 - github.com/GrayCodeAI/eyrie v0.0.1 + github.com/GrayCodeAI/eyrie v0.3.0 github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 github.com/GrayCodeAI/merlin v0.0.0-20260902154444-0f4b9f7326cb diff --git a/go.sum b/go.sum index 0b7382cec..402f399de 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8 github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/GrayCodeAI/eyrie v0.3.0 h1:jxd37JPCy1JGQ0r4rhZxer81EKluY1Mq6CEOu+7fJvc= +github.com/GrayCodeAI/eyrie v0.3.0/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 h1:X5lmXWlBdPVyk7lMAX7M0FiRr9o16Sq7yIjfN3mnXGU= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7/go.mod h1:JBkYX6kUbWCr1fsK//hxQ4Ro1VvlxtB+bSMR0/N5RRY= github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 h1:MrmWb0coJ3Uaxr/FtDSH1FaL/lqs663QXUh8ds+w8Og= From ea5d4633b1eb11dd460fee018bb51453ef3aaa4d Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 11 Sep 2026 13:28:49 +0530 Subject: [PATCH 4/4] build(deps): require github.com/GrayCodeAI/eyrie v0.6.0 eyrie v0.3.0 was already recorded in the Go module proxy and checksum database (May 2026, old module path github.com/hawk/eyrie), so every CI job that resolved it failed with a checksum mismatch. v0.6.0 is the rename-back release; its go.sum hashes match sum.golang.org. Co-Authored-By: Claude Opus 5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4ab35ae22..aadb6a0e0 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( charm.land/bubbles/v2 v2.1.1 charm.land/bubbletea/v2 v2.0.8 charm.land/lipgloss/v2 v2.0.5 - github.com/GrayCodeAI/eyrie v0.3.0 + github.com/GrayCodeAI/eyrie v0.6.0 github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 github.com/GrayCodeAI/merlin v0.0.0-20260902154444-0f4b9f7326cb diff --git a/go.sum b/go.sum index 402f399de..8deff4e35 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8 github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/GrayCodeAI/eyrie v0.3.0 h1:jxd37JPCy1JGQ0r4rhZxer81EKluY1Mq6CEOu+7fJvc= -github.com/GrayCodeAI/eyrie v0.3.0/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= +github.com/GrayCodeAI/eyrie v0.6.0 h1:RnbfwbspE38lPFwbdleOA3z1SpsjnrM3cSsgEaCIKVM= +github.com/GrayCodeAI/eyrie v0.6.0/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 h1:X5lmXWlBdPVyk7lMAX7M0FiRr9o16Sq7yIjfN3mnXGU= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7/go.mod h1:JBkYX6kUbWCr1fsK//hxQ4Ro1VvlxtB+bSMR0/N5RRY= github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 h1:MrmWb0coJ3Uaxr/FtDSH1FaL/lqs663QXUh8ds+w8Og=